コンテンツにスキップ

英文维基 | 中文维基 | 日文维基 | 草榴社区

利用者:Burthsceh/サンドボックス/モジュール:TimestampFormatter

local d = {

   31,
   28,
   31,
   30,
   31,
   30,
   31,
   31,
   30,
   31,
   30,
   31,

} local era = {

   ["明治"] = 1868,
   ["大正"] = 1912,
   ["昭和"] = 1926,
   ["平成"] = 1989,

} local months = {

   [""] = nil,
   -- roman
   i = 1,
   ii = 2,
   iii = 3,
   iiii = 4,
   iiv = 3,
   iv = 4,
   v = 5,
   vi = 6,
   vii = 7,
   viii = 8,
   viiii = 9,
   iix = 8,
   ix = 9,
   x = 10,
   xi = 11,
   xii = 12,
   -- en
   jan = 1,
   january = 1,
   feb = 2,
   february = 2,
   mar = 3,
   march = 3,
   apr = 4,
   april = 4,
   may = 5,
   jun = 6,
   june = 6,
   jul = 7,
   july = 7,
   aug = 8,
   august = 8,
   sep = 9,
   sept = 9,
   september = 9,
   oct = 10,
   october = 10,
   nov = 11,
   november = 11,
   dec = 12,
   december = 12,
   -- fr
   ["janvier"] = 1,
   ["février"] = 2,
   ["mars"] = 3,
   ["avril"] = 4,
   ["mai"] = 5,
   ["juin"] = 6,
   ["juillet"] = 7,
   ["août"] = 8,
   ["septembre"] = 9,
   ["octobre"] = 10,
   ["novembre"] = 11,
   ["décembre"] = 12,

} local ustr = mw.ustring local usub = ustr.sub local f = {} local p = {} local q = {} local t = {} local v = {}


function p.formatter(frame)

   local timestamp = frame.args[1];
   local format = frame.args[2] or "c";
   local formatter = frame.args.formatter or "pf";
   local validation = frame.args.validation;
   local debug = frame.args.debug;
   return q.timestamp(timestamp, format, formatter, validation, debug)

end


function q.timestamp(timestamp, format, formatter, validation, debug)

   local ts = t.generate(timestamp)
   t.parse(ts)
   if ts.invalid then
       return q.error(ts, debug) .. q.categorize(ts)
   end
   if validation then
       v.validate(ts)
       if ts.invalid then
           return q.error(ts, debug) .. q.categorize(ts)
       end
   end
   return f[formatter](ts, format) .. q.categorize(ts)

end

function q.error(ts, debug)

   local ret = " 0 then
           ret = ret .. " | warning: " .. table.concat(ts.warnings, " ")
       end
   end
   ret = ret .. "\">" .. ts.original .. ""
   return ret

end

function q.categorize(ts)

   local ret = ""
   if ts.invalid then
       ret = ""
   end
   if #ts.warnings > 0 then
       ret = ret .. "[[Category:テンプレートを正しく使用していないページ/TimestampFormatter/"
           .. table.concat(ts.warnings,
           "]]"
   end
   return ret

end


function t.generate(timestamp)

   local ts = {
       era = nil,
       year = nil,
       month = nil,
       day = nil,
       yearofweek = nil,
       week = nil,
       dayofweek = nil,
       dayofyear = nil,
       hour = nil,
       minute = nil,
       second = nil,
       frac = nil,
       unixtime = false,
       timezone = nil,
       called = {},
       next = "initial",
       position = 1,
       invalid = false,
       debug = "",
       warnings = {},
   }
   ts.original = timestamp
   ts.trimmed = mw.text.trim(timestamp)
   ts.char = mw.text.split(ts.trimmed, "")
   ts.length = #ts.char
   ts.size = #ts.trimmed
   return ts

end

function t.parse(ts)

   repeat
       table.insert(ts.called, ts.next)
       t[ts.next](ts)
   until ts.next == nil or ts.length < ts.position

end

function t.initial(ts)

   if ts.length < 4 then
       ts.next = nil
       ts.invalid = true
       return
   end
   local c1 = ts.char[1]
   local c2 = ts.char[2]
   if "0" <= c1 and c1 <= "9" then
       ts.next = "numeric"
   elseif c1 == "+" or c1 == "-" then
       ts.next = "拡大"
   elseif c1 == "@" then
       ts.next = "unixtime"
   elseif ("A" <= c1 and c1 <= "Z") or ("a" <= c1 and c1 <= "z") then
       ts.next = "alphabet"
   elseif c1 == "平" and c2 == "成" then
       ts.era = "平成"
       ts.next = "日時元号"
       ts.position = 3
   elseif c1 == "昭" and c2 == "和" then
       ts.era = "昭和"
       ts.next = "日時元号"
       ts.position = 3
   elseif c1 == "明" and c2 == "治" then
       ts.era = "明治"
       ts.next = "日時元号"
       ts.position = 3
   elseif c1 == "大" and c2 == "正" then
       ts.era = "大正"
       ts.next = "日時元号"
       ts.position = 3
   else
       ts.next = nil
       ts.invalid = true
   end

end

t.unixtime = function (ts)

   if string.match(ts.trimmed, "^@%-?[0-9]+$") then
       ts.unixtime = true
   else
       ts.invalid = true
   end
   ts.next = nil

end

t.numeric = function (ts)

   local numend = 1
   while numend <= ts.length and "0" <= ts.char[numend] and ts.char[numend] <= "9" do
       numend = numend + 1
   end
   numend = numend - 1
   if numend == ts.length then
       ts.next = nil
       if numend <= 8 then  -- 4 <= numend
           ts.year = tonumber(usub(ts.trimmed, 1, 4))
           if numend == 5 then
               ts.month = tonumber(ts.char[5])
           elseif numend == 6 then
               ts.month = tonumber(ts.char[5] .. ts.char[6])
           elseif numend == 7 then
               ts.dayofyear = tonumber(usub(ts.trimmed, 5, 7))
           elseif numend == 8 then
               ts.month = tonumber(ts.char[5] .. ts.char[6])
               ts.day = tonumber(ts.char[7] .. ts.char[8])
           end
       elseif numend == 14 then
           ts.year = tonumber(usub(ts.trimmed, 1, 4))
           ts.month = tonumber(ts.char[5] .. ts.char[6])
           ts.day = tonumber(ts.char[7] .. ts.char[8])
           ts.hour = tonumber(ts.char[9] .. ts.char[10])
           ts.minute = tonumber(ts.char[11] .. ts.char[12])
           ts.second = tonumber(ts.char[13] .. ts.char[14])
       elseif numend == 13 then
           ts.year = tonumber(usub(ts.trimmed, 1, 4))
           ts.dayofyear = tonumber(usub(ts.trimmed, 5, 7))
           ts.hour = tonumber(ts.char[8] .. ts.char[9])
           ts.minute = tonumber(ts.char[10] .. ts.char[11])
           ts.second = tonumber(ts.char[12] .. ts.char[13])
       else
           ts.invalid = true
       end
   else
       if numend == 4 then
           if ts.length < 6 then
               ts.next = nil
               ts.invalid = true
           end
           local c5 = ts.char[5]
           local c6 = ts.char[6]
           if c5 == "-" then
               if "0" <= c6 and c6 <= "9" then
                   ts.next = "拡張"
               elseif c6 == "W" then
                   ts.yearofweek = tonumber(usub(ts.trimmed, 1, 4))
                   ts.next = "拡大W"
                   ts.position = 6
               else
                   ts.next = "YYYYMonth"
               end
           elseif c5 == "年" or c5 == "(" or c5 == "(" then
               ts.next = "日時数字"
           elseif c5 == "/" then
               ts.next = "slash"
           elseif c5 == "W" then
               if string.match(ts.trimmed,
                   "^@[0-9][0-9][0-9][0-9]W[0-5][0-9][1-7][0-2][0-9][0-5][0-9][0-5][0-9]$") then
                   ts.yearofweek = tonumber(usub(ts.trimmed, 1, 4))
                   ts.week = tonumber(ts.char[6] .. ts.char[7])
                   ts.dayofweek = tonumber(ts.char[8])
                   ts.hour = tonumber(ts.char[9] .. ts.char[10])
                   ts.minute = tonumber(ts.char[11] .. ts.char[12])
                   ts.second = tonumber(ts.char[13] .. ts.char[14])
                   ts.next = nil
               else
                   ts.next = "基本W"
               end
           else
               ts.next = "YYYYMonth"
           end
       elseif numend == 1 then
           ts.next = "dMonthYYYY"
           ts.position = 1
       elseif numend == 2 then
           ts.position = 2
           if ts.length == 26 and ts.size == 26 then
               ts.next = "Common Log Format"
           else
               ts.next = "dMonthYYYY"
           end
       elseif numend == 7 then
           ts.year = tonumber(usub(ts.trimmed, 1, 4))
           ts.dayofyear = tonumber(usub(ts.trimmed, 5, 7))
           ts.next = "基本time"
           ts.position = 8
       elseif numend == 8 then
           ts.year = tonumber(usub(ts.trimmed, 1, 4))
           ts.month = tonumber(ts.char[5] .. ts.char[6])
           ts.day = tonumber(ts.char[7] .. ts.char[8])
           ts.next = "基本time"
           ts.position = 9
       else
           ts.next = nil
           ts.invalid = true
       end
   end

end

t.alphabet = function(ts)

   ts.next = "Monthd?YYYY"

end

-- [ISO 8601[

t["基本W"] = function (ts)

   ts.yearofweek = tonumber(usub(ts.trimmed, 1, 4))
   local p = ts.position
   if ts.length ~= ts.size or ts.length < 7
       or ts.char[6] < "0" or "9" < ts.char[6]
       or ts.char[7] < "0" or "9" < ts.char[7] then
       ts.next = nil
       ts.invalid = true
   end
   ts.week = tonumber(ts.char[p] .. ts.char[p+1])
   if ts.length == 7 then
       ts.next = nil
   elseif "1" <= ts.char[8] and ts.char[8] <= "7" then
       ts.dayofweek = tonumber(ts.char[8])
       ts.position = 9
       ts.next = "基本time"
   else
       ts.next = nil
       ts.invalid = true
   end

end

t["基本time"] = function (ts)

   local p = ts.position
   if ts.length ~= ts.size or ts.length < p + 2 or ts.char[p] ~= "T" then
       ts.next = nil
       ts.invalid = true
       return
   end
   local numend = 1
   while p + numend <= ts.length and "0" <= ts.char[p+numend] and ts.char[p+numend] <= "9" do
       numend = numend + 1
   end
   numend = numend - 1
   if not (numend == 6 or numend == 4 or numend == 2) then
       ts.next = nil
       ts.invalid = true
       return
   end
   ts.next = "基本残り"
   ts.hour = tonumber(ts.char[p+1] .. ts.char[p+2])
   if numend == 2 then
       ts.position = p + 3
       return
   end
   ts.minute = tonumber(ts.char[p+3] .. ts.char[p+4])
   if numend == 4 then
       ts.position = p + 5
   else
       ts.second = tonumber(ts.char[p+5] .. ts.char[p+6])
       ts.position = p + 7
   end

end

t["基本残り"] = function (ts)

   local p = ts.position
   if ts.char[p] == "." or ts.char[p] == "," then
       repeat
           p = p + 1
       until p <= ts.length and "0" <= ts.char[p] and ts.char[p] <= "9"
       ts.frac = tonumber("0." .. usub(ts.trimmed, ts.position, p - 1))
       if ts.minute == nil then
           local t = ts.frac * 60
           ts.minute = math.floor(t)
           ts.frac = t - ts.minute
       end
       if ts.second == nil then
           local t = ts.frac * 60
           ts.second = math.floor(t)
           ts.frac = t - ts.second
       end
       if ts.length == p - 1 then
           ts.next = nil
           return
       end
   end
   if ts.char[p] == "+" or ts.char[p] == "-" then
       if ts.length == p + 2
           and ts.char[p+1] == "0" or ts.char[p+1] == "1"
           and "0" <= ts.char[p+2] and ts.char[p+2] <= "9" then
           ts.timezone = usub(ts.trimmed, p, p + 2)
       elseif ts.length == p + 4
           and ts.char[p+1] == "0" or ts.char[p+1] == "1"
           and "0" <= ts.char[p+2] and ts.char[p+2] <= "9"
           and "0" <= ts.char[p+3] and ts.char[p+3] <= "5"
           and "0" <= ts.char[p+4] and ts.char[p+4] <= "9" then
           ts.timezone = usub(ts.trimmed, p, p + 4)
       else
           ts.invalid = true
       end
   elseif ts.char[p] == "Z" then
       ts.timezone = "Z"
   else
       ts.invalid = true
   end
   ts.next = nil

end

t["拡張"] = function (ts)

   ts.year = tonumber(usub(ts.trimmed, 1, 4))
   if ts.length ~= ts.size then
       ts.next = nil
       ts.invalid = true
       return
   elseif ts.length == 6 then
       ts.month = tonumber(ts.char[6])
       ts.next = nil
       return
   elseif ts.char[7] == "-" then
       ts.next = "拡張独自M"
       return
   elseif ts.char[7] < "0" or "9" < ts.char[7] then
       ts.next = nil
       ts.invalid = true
       return
   end
   if ts.length == 7 then
       ts.month = tonumber(ts.char[6] .. ts.char[7])
       ts.next = nil
   elseif ts.length == 8 then
       ts.next = "拡大DDD"
       ts.position = 6
   elseif ts.char[8] == "-" then
       ts.next = "拡張MM"
   else
       ts.next = "拡大DDD"
       ts.position = 6
   end

end

t["拡張MM"] = function (ts)

   ts.month = tonumber(ts.char[6] .. ts.char[7])
   if ts.length == 9 then
       ts.next = nil
       if "0" <= ts.char[9] and ts.char[9] <= "9" then
           ts.day = tonumber(ts.char[9])
       else
           ts.invalid = true
       end
   elseif "0" <= ts.char[9] and ts.char[9] <= "9" then
       if "0" <= ts.char[10] and ts.char[10] <= "9" then
           ts.day = tonumber(ts.char[9] .. ts.char[10])
           if ts.length == 10 then
               ts.next = nil
           elseif ts.char[11] == "T" then
               ts.next = "拡張time"
               ts.position = 12
           elseif ts.char[11] == " " then
               ts.next = "拡張time"
               ts.debug = "独自"
               ts.position = 12
           else
               ts.next = nil
               ts.invalid = true
           end
       else
           ts.day = tonumber(ts.char[9])
           if ts.length == 10 then
               ts.next = nil
               ts.invalid = true
           elseif ts.char[10] == " " or ts.char[10] == "T" then
               ts.next = "拡張time"
               ts.debug = "独自"
               ts.position = 11
           else
               ts.next = nil
               ts.invalid = true
           end
       end
   end

end

t["拡張独自M"] = function (ts)

   if ts.length < 8 or ts.char[8] < "0" or "9" < ts.char[8] then
       ts.next = nil
       ts.invalid = true
       return
   end
   ts.month = tonumber(ts.char[6])
   if ts.length == 8 then
       ts.day = tonumber(ts.char[8])
       ts.next = nil
   elseif ts.char[9] == " " or ts.char[9] == "T" then
       ts.day = tonumber(ts.char[8])
       ts.next = "拡張time"
       ts.debug = "独自"
       ts.position = 10
   elseif "0" <= ts.char[9] and ts.char[9] <= "9" then
       ts.day = tonumber(ts.char[8] .. ts.char[9])
       if ts.length == 9 then
           ts.next = nil
       elseif ts.char[10] == " " or ts.char[10] == "T" then
           ts.next = "拡張time"
           ts.debug = "独自"
           ts.position = 11
       else
           ts.next = nil
           ts.invalid = true
       end
   end

end

t["拡張time"] = function (ts)

   local p = ts.position
   if ts.length < p + 3 or ts.char[p] < "0" or "9" < ts.char[p] then
       ts.next = nil
       ts.debug = "0"
       ts.invalid = true
       return
   end
   if "0" <= ts.char[p+1] and ts.char[p+1] <= "9" then
       ts.hour = tonumber(ts.char[p] .. ts.char[p+1])
       if ts.char[p+2] ~= ":" then
           ts.next = nil
           ts.invalid = true
           ts.debug = "1"
           return
       end
       p = p + 3
   elseif ts.char[p+1] == ":" then
       ts.hour = tonumber(ts.char[p])
       p = p + 2
   else
       ts.next = nil
       ts.invalid = true
       ts.debug = "2"
       return
   end
   if p + 1 <= ts.length and "0" <= ts.char[p] and ts.char[p] <= "9"
       and "0" <= ts.char[p+1] and ts.char[p+1] <= "9" then
       ts.minute = tonumber(ts.char[p] .. ts.char[p+1])
   else
       ts.next = nil
       ts.invalid = true
       ts.debug = "3"
       return
   end
   p = p + 2
   if p + 2 <= ts.length and ts.char[p] == ":"
       and "0" <= ts.char[p+1] and ts.char[p+1] <= "9"
       and "0" <= ts.char[p+2] and ts.char[p+2] <= "9" then
       ts.second = tonumber(ts.char[p+1] .. ts.char[p+2])
       p = p + 3
   end
   if ts.debug ~= "独自" and (p == 17 or p == 20) then
       ts.next = "拡大残り"
   else
       ts.next = "timezone"
   end
   ts.position = p

end

-- 年拡大 t["拡大"] = function (ts)

   if ts.length ~= ts.size then
       ts.next = nil
       ts.invalid = true
       return
   end
   local yearend = 2
   while yearend <= ts.length and "0" <= ts.char[yearend] and ts.char[yearend] <= "9" do
       yearend = yearend + 1
   end
   if yearend < 6 then
       ts.next = nil
       ts.invalid = true
       ts.debug = "1:" .. yearend
       return
   end
   ts.position = yearend + 1
   yearend = yearend - 1
   if ts.length == yearend then
       ts.year = tonumber(usub(ts.trimmed, 1, yearend))
       ts.next = nil
       ts.debug = "3"
   elseif ts.length < yearend + 3 or ts.char[yearend+1] ~= "-" then
       ts.next = nil
       ts.invalid = true
       ts.debug = "4"
   elseif ts.char[yearend+2] == "W" then
       ts.yearofweek = tonumber(usub(ts.trimmed, 1, yearend))
       ts.next = "拡大W"
   else
       ts.year = tonumber(usub(ts.trimmed, 1, yearend))
       if yearend + 4 <= ts.length
           and "0" <= ts.char[yearend+4] and ts.char[yearend+4] <= "9" then
           ts.next = "拡大DDD"
       else
           ts.next = "拡大MMDD"
       end
   end

end

t["拡大DDD"] = function (ts)

   local dddstart = ts.position
   if "0" <= ts.char[dddstart] and ts.char[dddstart] <= "9"
       and "0" <= ts.char[dddstart+1] and ts.char[dddstart+1] <= "9"
       and "0" <= ts.char[dddstart+2] and ts.char[dddstart+2] <= "9" then
       ts.dayofyear = tonumber(usub(ts.trimmed, dddstart, dddstart + 2))
       ts.position = dddstart + 3
       ts.next = "拡大time"
   else
       ts.next = nil
       ts.invalid = true
   end

end

t["拡大MMDD"] = function (ts)

   local p = ts.position
   if "0" <= ts.char[p] and ts.char[p] <= "9"
       and "0" <= ts.char[p+1] and ts.char[p+1] <= "9" then
       ts.month = tonumber(ts.char[p] .. ts.char[p+1])
   else
       ts.next = nil
       ts.invalid = true
       return
   end
   p = p + 2
   if ts.length < p then
       ts.next = nil
   elseif ts.char[p] ~= "-" or ts.length < p + 1 then
       ts.next = nil
       ts.invalid = true
   else
       ts.day = tonumber(ts.char[p+1] .. ts.char[p+2])
       ts.position = p + 3
       ts.next = "拡大time"
   end

end

t["拡大W"] = function (ts)

   local p = ts.position + 1
   if p + 1 <= ts.length
       and "0" <= ts.char[p] and ts.char[p] <= "9"
       and "0" <= ts.char[p+1] and ts.char[p+1] <= "9" then
       ts.week = tonumber(ts.char[p] .. ts.char[p+1])
   else
       ts.next = nil
       ts.invalid = true
       return
   end
   p = p + 2
   if ts.length < p then
       ts.next = nil
   elseif ts.char[p] ~= "-" or ts.length < p + 1 then
       ts.next = nil
       ts.invalid = true
   elseif "1" <= ts.char[p+1] and ts.char[p+1] <= "7" then
       ts.dayofweek = tonumber(ts.char[p+1])
       ts.position = p + 2
       ts.next = "拡大time"
   else
       ts.next = nil
       ts.invalid = true
   end

end

t["拡大time"] = function (ts)

   local p = ts.position
   if ts.char[p] ~= "T" or ts.length < p + 5 or ts.char[p+3] ~= ":" then
       ts.next = nil
       ts.invalid = true
       ts.debug = "0: " .. p
       return
   end
   p = p + 1
   if "0" <= ts.char[p] and ts.char[p] <= "9"
       and "0" <= ts.char[p+1] and ts.char[p+1] <= "9" then
       ts.hour = tonumber(ts.char[p] .. ts.char[p+1])
   else
       ts.next = nil
       ts.invalid = true
       ts.debug = "1"
       return
   end
   p = p + 3
   if "0" <= ts.char[p] and ts.char[p] <= "9"
       and "0" <= ts.char[p+1] and ts.char[p+1] <= "9" then
       ts.minute = tonumber(ts.char[p] .. ts.char[p+1])
   else
       ts.next = nil
       ts.invalid = true
       ts.debug = "2"
       return
   end
   p = p + 2
   if ts.length < p then
       ts.next = nil
       ts.debug = "3"
   elseif p + 2 <= ts.length and ts.char[p] == ":"
       and "0" <= ts.char[p+1] and ts.char[p+1] <= "9"
       and "0" <= ts.char[p+2] and ts.char[p+2] <= "9" then
       ts.second = tonumber(ts.char[p+1] .. ts.char[p+2])
       p = p + 3
       ts.debug = "4"
   else
       ts.debug = "5"
   end
   ts.next = "拡大残り"
   ts.position = p

end

t["拡大残り"] = function (ts)

   local p = ts.position
   if ts.char[p] == "." or ts.char[p] == "," then
       repeat
           p = p + 1
       until p <= ts.length and "0" <= ts.char[p] and ts.char[p] <= "9"
       ts.frac = tonumber("0." .. usub(ts.trimmed, ts.position, p - 1))
       if ts.second == nil then
           local t = ts.frac * 60
           ts.second = math.floor(t)
           ts.frac = t - ts.second
       end
       if ts.length == p - 1 then
           ts.next = nil
           return
       end
   end
   if ts.char[p] == "+" or ts.char[p] == "-" then
       if ts.length == p + 2
           and ts.char[p+1] == "0" or ts.char[p+1] == "1"
           and "0" <= ts.char[p+2] and ts.char[p+2] <= "9" then
           ts.timezone = usub(ts.trimmed, p, p + 2)
       elseif ts.length == p + 5
           and ts.char[p+1] == "0" or ts.char[p+1] == "1"
           and "0" <= ts.char[p+2] and ts.char[p+2] <= "9"
           and ts.char[p+3] == ":"
           and "0" <= ts.char[p+4] and ts.char[p+4] <= "5"
           and "0" <= ts.char[p+5] and ts.char[p+5] <= "9" then
           ts.timezone = usub(ts.trimmed, p, p + 5)
       else
           ts.invalid = true
       end
   elseif ts.char[p] == "Z" then
       ts.timezone = "Z"
   else
       ts.invalid = true
   end
   ts.next = nil

end

-- ]ISO 8601]

-- [en[

--ymd t.YYYYMonth = function (ts)

   ts.year = tonumber(usub(ts.trimmed, 1, 4))
   local t = ustr.match(usub(ts.trimmed, 5), "^[\t\n\f\r ,-.]*(.-)$")
   local month = ustr.lower(ustr.match(t, "^%a+") or "")
   ts.month = months[month]
   if ts.month == nil then
       ts.next = nil
       ts.invalid = true
       return
   end
   t = ustr.match(string.sub(t, #month + 1), "^[\t\n\f\r ,-.]*(.-)$")
   if #t == 0 then
       ts.next = nil
       return
   end
   ts.day = string.match(t, "^[0-9]+")
   if ts.day == nil then
       ts.next = nil
       ts.invalid = true
       return
   end
   t = ustr.match(string.sub(t, #ts.day + 1), "^[nrst]?[dht]?[\t\n\f\r ,-.at]*(.-)$")
   ts.day = tonumber(ts.day)
   if #t == 0 then
       ts.next = nil
   else
       ts.next = "time"
       ts.position = ts.length - ustr.len(t) + 1
   end

end

--dmy t.dMonthYYYY = function (ts)

   local p = ts.position
   ts.day = tonumber(usub(ts.trimmed, 1, p))
   local t = ustr.match(usub(ts.trimmed, p + 1), "^[nrst]?[dht]?[\t\n\f\r ,-.]*(.-)$")
   local month = ustr.lower(ustr.match(t, "^%a+") or "")
   ts.month = months[month]
   if ts.month == nil then
       ts.next = nil
       ts.invalid = true
       return
   end
   t = ustr.match(string.sub(t, #month + 1), "^[\t\n\f\r ,-.]*(.-)$")
   if #t == 0 then
       ts.next = nil
       ts.invalid = true
       return
   end
   ts.year = string.match(t, "^[0-9]+")
   if ts.year == nil then
       ts.next = nil
       ts.invalid = true
       return
   end
   t = ustr.match(string.sub(t, #ts.year + 1), "^[\t\n\f\r ,-.at]*(.-)$")
   ts.year = tonumber(ts.year)
   if #t == 0 then
       ts.next = nil
   else
       ts.next = "time"
       ts.position = ts.length - ustr.len(t) + 1
   end

end

--mdy t["Monthd?YYYY"] = function (ts)

   local a = ustr.lower(ustr.match(ts.trimmed, "^%a+") or "")
   ts.month = months[a]
   if ts.month == nil then
       ts.next = nil
       ts.invalid = true
       return
   end
   local t = ustr.match(string.sub(ts.trimmed, #a + 1), "^[\t\n\f\r ,-.]*(.-)$")
   if #t == 0 then
       ts.next = nil
       ts.invalid = true
       return
   end
   a = string.match(t, "^[0-9]+")
   if a == nil or #a == 3 then
       ts.next = nil
       ts.invalid = true
       return
   elseif #a > 3 then
       ts.year = tonumber(a)
       ts.next = nil
       return
   end
   ts.day = tonumber(a)
   t = ustr.match(usub(t, #a + 1), "^[nrst]?[dht]?[\t\n\f\r ,-.]*(.-)$")
   if #t == 0 then
       ts.next = nil
       ts.invalid = true
       return
   end
   ts.year = string.match(t, "^[0-9]+")
   if ts.year == nil then
       ts.next = nil
       ts.invalid = true
       return
   end
   t = ustr.match(string.sub(t, #ts.year + 1), "^[\t\n\f\r ,-.at]*(.-)$")
   ts.year = tonumber(ts.year)
   if #t == 0 then
       ts.next = nil
   else
       ts.next = "time"
       ts.position = ts.length - ustr.len(t) + 1
   end

end

t.time = function (ts)

   local p = ts.position
   if ts.length < p + 3 or ts.char[p] < "0" or "9" < ts.char[p] then
       ts.next = nil
       ts.invalid = true
       ts.debug = "0"
       return
   end
   if "0" <= ts.char[p+1] and ts.char[p+1] <= "9" then
       ts.hour = tonumber(ts.char[p] .. ts.char[p+1])
       if ts.char[p+2] ~= ":" then
           ts.next = nil
           ts.invalid = true
           ts.debug = "1"
           return
       end
       p = p + 3
   elseif ts.char[p+1] == ":" then
       ts.hour = tonumber(ts.char[p])
       p = p + 2
   else
       ts.next = nil
       ts.invalid = true
       ts.debug = "2"
       return
   end
   if ts.length >= p + 1 and "0" <= ts.char[p] and ts.char[p] <= "9"
       and "0" <= ts.char[p+1] and ts.char[p+1] <= "9" then
       ts.minute = tonumber(ts.char[p] .. ts.char[p+1])
   else
       ts.next = nil
       ts.invalid = true
       ts.debug = "3"
       return
   end
   p = p + 2
   if p + 2 <= ts.length then
       if ts.char[p] == ":"
           and "0" <= ts.char[p+1] and ts.char[p+1] <= "9"
           and "0" <= ts.char[p+2] and ts.char[p+2] <= "9" then
           ts.second = tonumber(ts.char[p+1] .. ts.char[p+2])
           p = p + 3
       end
       local ap = ustr.match(usub(ts.trimmed, p), "^[\t\n\f\r ,-.]*([APap]).?[Mm].?")
       if ap then
           p = p + ustr.len(ustr.match(usub(ts.trimmed, p), "^[\t\n\f\r ,-.]*[APap].?[Mm].?"))
           if ap == "P" or ap == "p" then
               ts.hour = ts.hour + 12
           end
       end
   end
   ts.next = "timezone"
   ts.position = p

end

-- ]en]

t.slash = function (ts)

   if ts.length ~= ts.size or ts.length < 6 or ts.char[6] < "0" or "9" < ts.char[6] then
       ts.next = "日時数字"
       return
   end
   ts.year = tonumber(usub(ts.trimmed, 1, 4))
   if ts.length == 6 then
       ts.month = tonumber(ts.char[6])
       ts.next = nil
       return
   end
   local p = 9
   if "0" <= ts.char[7] or ts.char[7] <= "9" then
       ts.month = tonumber(ts.char[6] .. ts.char[7])
       if ts.length == 7 then
           ts.next = nil
           return
       elseif ts.char[8] ~= "/" then
           ts.next = nil
           ts.invalid = true
       end
   elseif ts.char[7] == "/" then
       ts.month = tonumber(ts.char[6])
       p = 8
   else
       ts.next = nil
       ts.invalid = true
       return
   end
   if ts.length < p or ts.char[p] < "0" or "9" < ts.char[p] then
       ts.next = nil
       ts.invalid = true
   elseif ts.length == p then
       ts.day = tonumber(ts.char[p])
       ts.next = nil
   elseif "0" <= ts.char[p+1] or ts.char[p+1] <= "9" then
       ts.day = tonumber(ts.char[p] .. ts.char[p+1])
       if ts.length == p + 1 then
           ts.next = nil
       elseif ts.length < p + 6 then
           ts.next = nil
           ts.invalid = true
       elseif ts.char[p+2] == " " then
           ts.next = "time"
           ts.position = p + 3
       end
   end

end

t["Common Log Format"] = function (ts)

   if not string.match(ts.trimmed,
       "^[0-3][0-9]/[ADFJMNOS][aceopu][bcglnprtvy]/[0-9][0-9][0-9][0-9]:[0-2][0-9]:[0-5][0-9]:[0-6][0-9] [%+%-][01][0-9][0-5][0-9]$") then
       ts.next = "dMonthYYYY"
       return
   end
   local Mon = {
       Jan = 1,
       Feb = 2,
       Mar = 3,
       Apr = 4,
       May = 5,
       Jun = 6,
       Jul = 7,
       Aug = 8,
       Sep = 9,
       Oct = 10,
       Nov = 11,
       Dec = 12,
   }
   ts.year = tonumber(usub(ts.trimmed, 8, 11))
   ts.month = Mon[usub(ts.trimmed, 4, 6)]
   if ts.month == nil then
       ts.year = nil
       ts.next = "dMonthYYYY"
       return
   end
   ts.day = tonumber(ts.char[1] .. ts.char[2])
   ts.hour = tonumber(ts.char[13] .. ts.char[14])
   ts.minute = tonumber(ts.char[16] .. ts.char[17])
   ts.second = tonumber(ts.char[19] .. ts.char[20])
   ts.timezone = usub(ts.trimmed, 21, 26)
   ts.next = nil

end

t.timezone = function (ts)

   ts.next = nil
   local t = mw.text.trim(usub(ts.trimmed, ts.position), "\t\n\f\r ()")
   if #t ~= ustr.len(t) then
       ts.invalid = true
   elseif string.match(t, "^[A-Za-z]+$") then
       if 1 <= #t and #t <= 6 then
           ts.timezone = t
       else
           ts.invalid = true
       end
   elseif string.match(t, "^[%+%-][01][0-9]$") then
       ts.timezone = t
   elseif string.match(t, "^[%+%-][01][0-9]:?[0-5][0-9]$") then
       ts.timezone = t
   elseif string.match(t, "^GMT[%+%-][01][0-9]$") then
       ts.timezone = t
   elseif string.match(t, "^GMT[%+%-][01][0-9]:?[0-5][0-9]$") then
       ts.timezone = t
   elseif string.match(t, "^UTC[%+%-][01][0-9]$") then
       ts.timezone = string.sub(t, 4)
   elseif string.match(t, "^UTC[%+%-][01][0-9]:?[0-5][0-9]$") then
       ts.timezone = string.sub(t, 4)
   else
       local s, e = string.find(t, "^[A-Z][a-z]+")
       if s then
           s = string.sub(t, e + 1)
           e = string.sub(t, 1, e)
           for a in string.gmatch(s, "[_/][A-Z][a-z]+") do
               e = e .. a
           end
           if t == e then
               ts.timezone = t
           else
               ts.invalid = true
           end
       else
           ts.invalid = true
       end
   end

end

-- [ja[

local tokennumber = {

   ["0"] = "number",
   ["1"] = "number",
   ["2"] = "number",
   ["3"] = "number",
   ["4"] = "number",
   ["5"] = "number",
   ["6"] = "number",
   ["7"] = "number",
   ["8"] = "number",
   ["9"] = "number",

} local tokenpunc = {

   ["\t"] = "punc",
   ["\n"] = "punc",
   ["\f"] = "punc",
   ["\r"] = "punc",
   [" "] = "punc",
   [","] = "punc",
   ["-"] = "punc",
   ["/"] = "punc",
   [" "] = "punc",
   ["、"] = "punc",
   ["。"] = "punc",
   [","] = "punc",
   ["."] = "punc",

} local token1 = {

   ["("] = "open",
   ["("] = "open",
   [")"] = "close",
   [")"] = "close",
   ["元"] = "元",
   ["年"] = "年",
   ["月"] = "月",
   ["日"] = "日",
   ["時"] = "時",
   ["分"] = "分",
   ["秒"] = "秒",

} local token2 = {

   ["午前"] = "午",
   ["午後"] = "午",
   ["日曜"] = "曜日",
   ["月曜"] = "曜日",
   ["火曜"] = "曜日",
   ["水曜"] = "曜日",
   ["木曜"] = "曜日",
   ["金曜"] = "曜日",
   ["土曜"] = "曜日",
   ["明治"] = "元号",
   ["大正"] = "元号",
   ["昭和"] = "元号",
   ["平成"] = "元号",

} local token3 = {

   ["(日)"] = "曜日",
   ["(月)"] = "曜日",
   ["(火)"] = "曜日",
   ["(水)"] = "曜日",
   ["(木)"] = "曜日",
   ["(金)"] = "曜日",
   ["(土)"] = "曜日",
   ["(日)"] = "曜日",
   ["(月)"] = "曜日",
   ["(火)"] = "曜日",
   ["(水)"] = "曜日",
   ["(木)"] = "曜日",
   ["(金)"] = "曜日",
   ["(土)"] = "曜日",
   ["日曜日"] = "曜日",
   ["月曜日"] = "曜日",
   ["火曜日"] = "曜日",
   ["水曜日"] = "曜日",
   ["木曜日"] = "曜日",
   ["金曜日"] = "曜日",
   ["土曜日"] = "曜日",

} local dayofweek = {

   ["(日)"] = 1,
   ["(月)"] = 2,
   ["(火)"] = 3,
   ["(水)"] = 4,
   ["(木)"] = 5,
   ["(金)"] = 6,
   ["(土)"] = 7,
   ["(日)"] = 1,
   ["(月)"] = 2,
   ["(火)"] = 3,
   ["(水)"] = 4,
   ["(木)"] = 5,
   ["(金)"] = 6,
   ["(土)"] = 7,
   ["日曜"] = 1,
   ["月曜"] = 2,
   ["火曜"] = 3,
   ["水曜"] = 4,
   ["木曜"] = 5,
   ["金曜"] = 6,
   ["土曜"] = 7,
   ["日曜日"] = 1,
   ["月曜日"] = 2,
   ["火曜日"] = 3,
   ["水曜日"] = 4,
   ["木曜日"] = 5,
   ["金曜日"] = 6,
   ["土曜日"] = 7,
   -- zh
   ["(一)"] = 2,
   ["(二)"] = 3,
   ["(三)"] = 4,
   ["(四)"] = 5,
   ["(五)"] = 6,
   ["(六)"] = 7,
   ["(七)"] = 1, --
   ["(天)"] = 1,
   ["(一)"] = 2,
   ["(二)"] = 3,
   ["(三)"] = 4,
   ["(四)"] = 5,
   ["(五)"] = 6,
   ["(六)"] = 7,
   ["(七)"] = 1, --
   ["(天)"] = 1,
   ["星期一"] = 2,
   ["星期二"] = 3,
   ["星期三"] = 4,
   ["星期四"] = 5,
   ["星期五"] = 6,
   ["星期六"] = 7,
   ["星期七"] = 1, --
   ["星期天"] = 1,
   ["星期日"] = 1,
   ["礼拜一"] = 2,
   ["礼拜二"] = 3,
   ["礼拜三"] = 4,
   ["礼拜四"] = 5,
   ["礼拜五"] = 6,
   ["礼拜六"] = 7,
   ["礼拜七"] = 1, --
   ["礼拜天"] = 1,
   ["礼拜日"] = 1,
   ["礼拝一"] = 2, --
   ["礼拝二"] = 3, --
   ["礼拝三"] = 4, --
   ["礼拝四"] = 5, --
   ["礼拝五"] = 6, --
   ["礼拝六"] = 7, --
   ["礼拝七"] = 1, --
   ["礼拝天"] = 1, --
   ["礼拝日"] = 1, --

}

t["日時数字"] = function (ts)

   local state = {
       {
           "number|year",
           {
               call = "元号",
               "open",
               "元号",
               "number",
               "close",
           },
           "年",
           {
               call = "元号",
               "open",
               "元号",
               "number",
               "年",
               "close",
           },
       },
       {
           "number|month",
           "月",
       },
       {
           "number|day",
           "日",
           {
               call = "曜日",
               "曜日",
           },
       },
       {
           {
               call = "午",
               "午",
           },
           "number|hour",
           "時",
           {
               call = "分秒",--[[
               "number|minute",
               "分",
               {
                   "number|second",
                   "秒",
               },]]
           },
       },
   }
   local token1 = {
       ["("] = "open",
       ["("] = "open",
       [")"] = "close",
       [")"] = "close",
       ["元"] = "元",
       ["年"] = "年",
       ["月"] = "月",
       ["日"] = "日",
       ["号"] = "日", -- zh
       ["時"] = "時",
       ["分"] = "分",
       ["秒"] = "秒",
   }
   local token3 = {
       ["(日)"] = "曜日",
       ["(月)"] = "曜日",
       ["(火)"] = "曜日",
       ["(水)"] = "曜日",
       ["(木)"] = "曜日",
       ["(金)"] = "曜日",
       ["(土)"] = "曜日",
       ["(日)"] = "曜日",
       ["(月)"] = "曜日",
       ["(火)"] = "曜日",
       ["(水)"] = "曜日",
       ["(木)"] = "曜日",
       ["(金)"] = "曜日",
       ["(土)"] = "曜日",
       ["日曜日"] = "曜日",
       ["月曜日"] = "曜日",
       ["火曜日"] = "曜日",
       ["水曜日"] = "曜日",
       ["木曜日"] = "曜日",
       ["金曜日"] = "曜日",
       ["土曜日"] = "曜日",
       -- zh
       ["(一)"] = "曜日",
       ["(二)"] = "曜日",
       ["(三)"] = "曜日",
       ["(四)"] = "曜日",
       ["(五)"] = "曜日",
       ["(六)"] = "曜日",
       ["(七)"] = "曜日",
       ["(天)"] = "曜日",
       ["(一)"] = "曜日",
       ["(二)"] = "曜日",
       ["(三)"] = "曜日",
       ["(四)"] = "曜日",
       ["(五)"] = "曜日",
       ["(六)"] = "曜日",
       ["(七)"] = "曜日",
       ["(天)"] = "曜日",
       ["星期一"] = "曜日",
       ["星期二"] = "曜日",
       ["星期三"] = "曜日",
       ["星期四"] = "曜日",
       ["星期五"] = "曜日",
       ["星期六"] = "曜日",
       ["星期七"] = "曜日",
       ["星期天"] = "曜日",
       ["星期日"] = "曜日",
       ["礼拜一"] = "曜日",
       ["礼拜二"] = "曜日",
       ["礼拜三"] = "曜日",
       ["礼拜四"] = "曜日",
       ["礼拜五"] = "曜日",
       ["礼拜六"] = "曜日",
       ["礼拜七"] = "曜日",
       ["礼拜天"] = "曜日",
       ["礼拜日"] = "曜日",
       ["礼拝一"] = "曜日",
       ["礼拝二"] = "曜日",
       ["礼拝三"] = "曜日",
       ["礼拝四"] = "曜日",
       ["礼拝五"] = "曜日",
       ["礼拝六"] = "曜日",
       ["礼拝七"] = "曜日",
       ["礼拝天"] = "曜日",
       ["礼拝日"] = "曜日",
   }
   local str = ts.char
   local point = 0
   function scan()
       local val = ""
       local type = ""
       if ts.length <= point then
           return "end", nil
       end
       while point < ts.length and type do
           point = point + 1
           type = tokenpunc[str[point]]
       end
       if point + 2 <= ts.length then
           val = str[point] .. str[point+1] .. str[point+2]
           type = token3[val]
           if type then
               point = point + 2
               return type, val
           end
        end
        if point + 1 <= ts.length then
           val = str[point] .. str[point+1]
           type = token2[val]
           if type then
               point = point + 1
               return type, val
           end
       end
       if point <= ts.length then
           val = str[point]
           type = token1[val]
           if type then
               return type, val
           end
       end
       type = tokennumber[str[point]]
       if type == nil then
           return "error", nil
       end
       val = str[point]
       while point < ts.length do
           point = point + 1
           type = tokennumber[str[point]]
           if type == nil then
               point = point - 1
               return "number", tonumber(val)
           end
           val = val .. str[point]
       end
       return "end", nil
   end
   local u = {}
   u["元号"] = function (v2)
       local p = point
       local t, v, e
       for i3, v3 in ipairs(v2) do
           t, v = scan()
           if v3 == t then
               if t == "元号" then
                   e = v
               elseif t == "number" then
                   if e == "明治" then
                       if ts.year > 1872 then
                           if v + 1867 ~= ts.year then
                               table.insert(ts.warnings, "年不一致")
                               return false
                           end
                       end
                   elseif v + era[e] - 1 ~= ts.year then
                       table.insert(ts.warnings, "年不一致")
                       return false
                   end
               end
           elseif v3 == "number" and t == "元" then
               if e ~= "明治" and era[e] ~= ts.year then
                   table.insert(ts.warnings, "年不一致")
                   return false
               end
           elseif v3 == "open" then
               point = p
               return true
           else
               ts.debug = v3 .. t .. point
               return false
           end
       end
       return true
   end
   u["曜日"] = function (v2)
       local p = point
       local t, v = scan()
       if t == "曜日" then
           ts.dayofweek = dayofweek[v]
       else
           point = p
       end
       return true
   end
   local pm = false
   u["午"] = function (v2)
       local p = point
       local t, v = scan()
       if t == "午" then
           if v == "午後" then
               pm = true
           end
       else
           point = p
       end
       return true
   end
   u["分秒"] = function (v2)
       local p = point
       local t, v = scan()
       if t == "number" then
           ts.minute = v
       else
           point = p
           ts.next = "timezone"
           if pm then
               ts.hour = ts.hour + 12
           end
           ts.position = p + 1
           return nil
       end
       t, v = scan()
       if t ~= "分" then
           point = p
           ts.next = "timezone"
           if pm then
               ts.hour = ts.hour + 12
           end
           ts.position = p + 1
           return nil
       end
       p = point
       t, v = scan()
       if t == "number" then
           ts.second = v
       else
           point = p
           ts.next = "timezone"
           if pm then
               ts.hour = ts.hour + 12
           end
           ts.position = p + 1
           return nil
       end
       t, v = scan()
       if t ~= "秒" then
           point = p
           ts.next = "timezone"
           if pm then
               ts.hour = ts.hour + 12
           end
           ts.position = p + 1
           return nil
       end
       return true
   end
   if not ustr.match(ts.trimmed, "時") then
       table.remove(state)
   end
   local t, v
   for i1, v1 in ipairs(state) do
       for i2, v2 in ipairs(v1) do
           if type(v2) == "table" then
               local z = u[v2.call](v2)
               if z == false then
                   ts.next = nil
                   ts.invalid = true
                   return
               elseif z == nil then
                   return
               end
           else
               local v21, v22 = unpack(mw.text.split(v2, "|", true))
               t, v = scan()
               if v21 == t then
                   if v22 then
                       ts[v22] = v
                   end
               elseif t == "end" and v21 == "number" then
                   ts.next = nil
                   if pm then
                       ts.hour = ts.hour + 12
                   end
                   return
               else
                   ts.next = nil
                   ts.invalid = true
                   ts.debug = v2 .. t .. point .. tostring(v) .. v21
                   return
               end
           end
       end
   end
   if ustr.match(ts.trimmed, "時") then
       if pm then
           ts.hour = ts.hour + 12
       end
       ts.next = "timezone"
       ts.position = point + 1
   else
       ts.next = "time"
       ts.position = point + 1 + ustr.len(ustr.match(usub(ts.trimmed, point + 1), "^[\t\n\f\r ,-.]*"))
   end

end

t["日時元号"] = function (ts)

   ts.position = 3
   if ustr.match(ts.trimmed, "[0-9]") then
       ts.next = "日時元号数字"
   elseif ustr.match(ts.trimmed, "[0-9]") then
       local s = ustr.gsub(ts.trimmed, "0", "0")
       s = ustr.gsub(s, "1", "1")
       s = ustr.gsub(s, "2", "2")
       s = ustr.gsub(s, "3", "3")
       s = ustr.gsub(s, "4", "4")
       s = ustr.gsub(s, "5", "5")
       s = ustr.gsub(s, "6", "6")
       s = ustr.gsub(s, "7", "7")
       s = ustr.gsub(s, "8", "8")
       ts.trimmed = ustr.gsub(s, "9", "9")
       ts.char = mw.text.split(ts.trimmed, "")
       ts.next = "日時元号数字"
   elseif ustr.match(ts.trimmed, "[零〇]") then
       local s = ustr.gsub(ts.trimmed, "零", "〇")
       s = ustr.gsub(s, "[弌壱壹]", "一")
       s = ustr.gsub(s, "[弍弐貮貳贰]", "二")
       s = ustr.gsub(s, "[弎参參叁]", "三")
       s = ustr.gsub(s, "肆", "四")
       s = ustr.gsub(s, "伍", "五")
       s = ustr.gsub(s, "[陸陆]", "六")
       s = ustr.gsub(s, "[柒漆質]", "七")
       s = ustr.gsub(s, "捌", "八")
       ts.trimmed = ustr.gsub(s, "玖", "九")
       ts.char = mw.text.split(ts.trimmed, "")
       ts.next = "日時元号漢字位取り"
   elseif ustr.match(ts.trimmed, "[十拾廿念百陌佰千阡仟万萬]") then
       local s = ustr.gsub(ts.trimmed, "[弌壱壹]", "一")
       s = ustr.gsub(s, "[弍弐貮貳贰]", "二")
       s = ustr.gsub(s, "[弎参參叁]", "三")
       s = ustr.gsub(s, "肆", "四")
       s = ustr.gsub(s, "伍", "五")
       s = ustr.gsub(s, "[陸陆]", "六")
       s = ustr.gsub(s, "[柒漆質]", "七")
       s = ustr.gsub(s, "捌", "八")
       ts.trimmed = ustr.gsub(s, "玖", "九")
       s = ustr.gsub(s, "拾", "十")
       s = ustr.gsub(s, "廿", "二十")
       s = ustr.gsub(s, "念", "三十")
       ts.trimmed = ustr.gsub(s, "[陌佰]", "百")
       ts.trimmed = ustr.gsub(s, "[阡仟]", "千")
       s = ustr.gsub(s, "萬", "万")
       ts.char = mw.text.split(ts.trimmed, "")
       ts.next = "日時元号漢字命数"
   else
       local s = ustr.gsub(ts.trimmed, "零", "〇")
       s = ustr.gsub(s, "[弌壱壹]", "一")
       s = ustr.gsub(s, "[弍弐貮貳贰]", "二")
       s = ustr.gsub(s, "[弎参參叁]", "三")
       s = ustr.gsub(s, "肆", "四")
       s = ustr.gsub(s, "伍", "五")
       s = ustr.gsub(s, "[陸陆]", "六")
       s = ustr.gsub(s, "[柒漆質]", "七")
       s = ustr.gsub(s, "捌", "八")
       ts.trimmed = ustr.gsub(s, "玖", "九")
       ts.char = mw.text.split(ts.trimmed, "")
       ts.next = "日時元号漢字位取り"
   end

end

local state = {

   {
       "number|year",
       {
           call = "年",
           "open",
           "number",
           "close",
       },
       "年",
       {
           call = "年",
           "open",
           "number",
           "年",
           "close",
       },
   },
   {
       "number|month",
       "月",
   },
   {
       "number|day",
       "日",
       {
           call = "曜日",
           "曜日",
       },
   },
   {
       {
           call = "午",
           "午",
       },
       "number|hour",
       "時",
       {
           call = "分秒",
       },
   },

}

t["日時元号数字"] = function (ts)

   local str = ts.char
   local point = ts.position - 1
   function scan()
       local val = ""
       local type = ""
       if ts.length <= point then
           return "end", nil
       end
       while point < ts.length and type do
           point = point + 1
           type = tokenpunc[str[point]]
       end
       if point + 2 <= ts.length then
           val = str[point] .. str[point+1] .. str[point+2]
           type = token3[val]
           if type then
               point = point + 2
               return type, val
           end
        end
        if point + 1 <= ts.length then
           val = str[point] .. str[point+1]
           type = token2[val]
           if type then
               point = point + 1
               return type, val
           end
       end
       if point <= ts.length then
           val = str[point]
           type = token1[val]
           if type then
               return type, val
           end
       end
       type = tokennumber[str[point]]
       if type == nil then
           return "error", nil
       end
       val = str[point]
       while point < ts.length do
           point = point + 1
           type = tokennumber[str[point]]
           if type == nil then
               point = point - 1
               return "number", tonumber(val)
           end
           val = val .. str[point]
       end
       return "end", nil
   end
   local u = {}
   u["年"] = function (v2)
       local p = point
       local t, v
       for i3, v3 in ipairs(v2) do
           t, v = scan()
           if v3 == t then
               if t == "number" then
                   if e == "明治" then
                       if ts.year > 1872 then
                           if ts.year + 1867 ~= v then
                               table.insert(ts.warnings, "年不一致")
                               return false
                           end
                       end
                   elseif ts.year + era[ts.era] - 1 ~= v then
                       table.insert(ts.warnings, "年不一致")
                       return false
                   end
               end
           elseif v3 == "open" then
               point = p
               return true
           else
               ts.debug = v3 .. t .. point
               return false
           end
       end
       return true
   end
   u["曜日"] = function (v2)
       local p = point
       local t, v = scan()
       if t == "曜日" then
           ts.dayofweek = dayofweek[v]
       else
           point = p
       end
       return true
   end
   local pm = false
   u["午"] = function (v2)
       local p = point
       local t, v = scan()
       if t == "午" then
           if v == "午後" then
               pm = true
           end
       else
           point = p
       end
       return true
   end
   u["分秒"] = function (v2)
       local p = point
       local t, v = scan()
       if t == "number" then
           ts.minute = v
       else
           point = p
           ts.next = "timezone"
           if pm then
               ts.hour = ts.hour + 12
           end
           ts.position = p + 1
           return nil
       end
       t, v = scan()
       if t ~= "分" then
           point = p
           ts.next = "timezone"
           if pm then
               ts.hour = ts.hour + 12
           end
           ts.position = p + 1
           return nil
       end
       p = point
       t, v = scan()
       if t == "number" then
           ts.second = v
       else
           point = p
           ts.next = "timezone"
           if pm then
               ts.hour = ts.hour + 12
           end
           ts.position = p + 1
           return nil
       end
       t, v = scan()
       if t ~= "秒" then
           point = p
           ts.next = "timezone"
           if pm then
               ts.hour = ts.hour + 12
           end
           ts.position = p + 1
           return nil
       end
       return true
   end
   if not ustr.match(ts.trimmed, "時") then
       table.remove(state)
   end
   local t, v
   for i1, v1 in ipairs(state) do
       for i2, v2 in ipairs(v1) do
           if type(v2) == "table" then
               local z = u[v2.call](v2)
               if z == false then
                   ts.next = nil
                   ts.invalid = true
                   return
               elseif z == nil then
                   return
               end
           else
               local v21, v22 = unpack(mw.text.split(v2, "|", true))
               t, v = scan()
               if v21 == t then
                   if v22 then
                       if v22 == "year" and v == 1 then
                           ts.next = nil
                           ts.invalid = true
                           table.insert(ts.warning, "元号1年")
                       end
                       ts[v22] = v
                   end
               elseif v2 == "number|year" and t == "元" then
                   ts.year = 1
               elseif t == "end" and v21 == "number" then
                   ts.next = nil
                   if pm then
                       ts.hour = ts.hour + 12
                   end
                   return
               else
                   ts.next = nil
                   ts.invalid = true
                   ts.debug = v2 .. t .. point .. tostring(v) .. v21
                   return
               end
           end
       end
   end
   if ustr.match(ts.trimmed, "時") then
       if pm then
           ts.hour = ts.hour + 12
       end
       ts.next = "timezone"
       ts.position = point + 1
   else
       ts.next = "time"
       ts.position = point + 1 + ustr.len(ustr.match(usub(ts.trimmed, point + 1), "^[\t\n\f\r ,-.]*"))
   end

end

t["日時元号漢字位取り"] = function (ts)

   local tokennumber = {
       ["〇"] = "number",
       ["一"] = "number",
       ["二"] = "number",
       ["三"] = "number",
       ["四"] = "number",
       ["五"] = "number",
       ["六"] = "number",
       ["七"] = "number",
       ["八"] = "number",
       ["九"] = "number",
   }
   local n = {
       ["〇"] = "0",
       ["一"] = "1",
       ["二"] = "2",
       ["三"] = "3",
       ["四"] = "4",
       ["五"] = "5",
       ["六"] = "6",
       ["七"] = "7",
       ["八"] = "8",
       ["九"] = "9",
   }
   local str = ts.char
   local point = ts.position - 1
   function scan()
       local val = ""
       local type = ""
       if ts.length <= point then
           return "end", nil
       end
       while point < ts.length and type do
           point = point + 1
           type = tokenpunc[str[point]]
       end
       if point + 2 <= ts.length then
           val = str[point] .. str[point+1] .. str[point+2]
           type = token3[val]
           if type then
               point = point + 2
               return type, val
           end
        end
        if point + 1 <= ts.length then
           val = str[point] .. str[point+1]
           type = token2[val]
           if type then
               point = point + 1
               return type, val
           end
       end
       if point <= ts.length then
           val = str[point]
           type = token1[val]
           if type then
               return type, val
           end
       end
       type = tokennumber[str[point]]
       if type == nil then
           return "error", nil
       end
       val = n[str[point]]
       while point < ts.length do
           point = point + 1
           type = tokennumber[str[point]]
           if type == nil then
               point = point - 1
               return "number", tonumber(val)
           end
           val = val .. n[str[point]]
       end
       return "end", nil
   end
   local u = {}
   u["年"] = function (v2)
       local p = point
       local t, v
       for i3, v3 in ipairs(v2) do
           t, v = scan()
           if v3 == t then
               if t == "number" then
                   if e == "明治" then
                       if ts.year > 1872 then
                           if ts.year + 1867 ~= v then
                               table.insert(ts.warnings, "年不一致")
                               return false
                           end
                       end
                   elseif ts.year + era[ts.era] - 1 ~= v then
                       table.insert(ts.warnings, "年不一致")
                       return false
                   end
               end
           elseif v3 == "open" then
               point = p
               return true
           else
               ts.debug = v3 .. t .. point
               return false
           end
       end
       return true
   end
   u["曜日"] = function (v2)
       local p = point
       local t, v = scan()
       if t == "曜日" then
           ts.dayofweek = dayofweek[v]
       else
           point = p
       end
       return true
   end
   local pm = false
   u["午"] = function (v2)
       local p = point
       local t, v = scan()
       if t == "午" then
           if v == "午後" then
               pm = true
           end
       else
           point = p
       end
       return true
   end
   u["分秒"] = function (v2)
       local p = point
       local t, v = scan()
       if t == "number" then
           ts.minute = v
       else
           point = p
           ts.next = nil--"timezone"
           if pm then
               ts.hour = ts.hour + 12
           end
           ts.position = p + 1
           return nil
       end
       t, v = scan()
       if t ~= "分" then
           point = p
           ts.next = nil--"timezone"
           if pm then
               ts.hour = ts.hour + 12
           end
           ts.position = p + 1
           return nil
       end
       p = point
       t, v = scan()
       if t == "number" then
           ts.second = v
       else
           point = p
           ts.next = nil--"timezone"
           if pm then
               ts.hour = ts.hour + 12
           end
           ts.position = p + 1
           return nil
       end
       t, v = scan()
       if t ~= "秒" then
           point = p
           ts.next = nil--"timezone"
           if pm then
               ts.hour = ts.hour + 12
           end
           ts.position = p + 1
           return nil
       end
       return true
   end
   if not ustr.match(ts.trimmed, "時") then
       table.remove(state)
   end
   local t, v
   for i1, v1 in ipairs(state) do
       for i2, v2 in ipairs(v1) do
           if type(v2) == "table" then
               local z = u[v2.call](v2)
               if z == false then
                   ts.next = nil
                   ts.invalid = true
                   return
               elseif z == nil then
                   return
               end
           else
               local v21, v22 = unpack(mw.text.split(v2, "|", true))
               t, v = scan()
               if v21 == t then
                   if v22 then
                       if v22 == "year" and v == 1 then
                           ts.next = nil
                           ts.invalid = true
                           table.insert(ts.warning, "元号1年")
                       end
                       ts[v22] = v
                   end
               elseif v2 == "number|year" and t == "元" then
                   ts.year = 1
               elseif t == "end" and v21 == "number" then
                   ts.next = nil
                   if pm then
                       ts.hour = ts.hour + 12
                   end
                   return
               else
                   ts.next = nil
                   ts.invalid = true
                   ts.debug = v2 .. t .. point .. tostring(v) .. v21
                   return
               end
           end
       end
   end
   if ustr.match(ts.trimmed, "時") and pm then
       ts.hour = ts.hour + 12
   end
   ts.next = nil

end

t["日時元号漢字命数"] = function (ts)

   local tokennumber = {
       ["一"] = "number",
       ["二"] = "number",
       ["三"] = "number",
       ["四"] = "number",
       ["五"] = "number",
       ["六"] = "number",
       ["七"] = "number",
       ["八"] = "number",
       ["九"] = "number",
       ["十"] = "数詞",
       ["百"] = "数詞",
       ["千"] = "数詞",
       ["万"] = "数詞",
   }
   local n = {
       ["一"] = "1",
       ["二"] = "2",
       ["三"] = "3",
       ["四"] = "4",
       ["五"] = "5",
       ["六"] = "6",
       ["七"] = "7",
       ["八"] = "8",
       ["九"] = "9",
       ["十"] = 10,
       ["百"] = 100,
       ["千"] = 1000,
   }
   local str = ts.char
   local point = ts.position - 1
   function scan()
       local val = ""
       local type = ""
       if ts.length <= point then
           return "end", nil
       end
       while point < ts.length and type do
           point = point + 1
           type = tokenpunc[str[point]]
       end
       if point + 2 <= ts.length then
           val = str[point] .. str[point+1] .. str[point+2]
           type = token3[val]
           if type then
               point = point + 2
               return type, val
           end
        end
        if point + 1 <= ts.length then
           val = str[point] .. str[point+1]
           type = token2[val]
           if type then
               point = point + 1
               return type, val
           end
       end
       if point <= ts.length then
           val = str[point]
           type = token1[val]
           if type then
               return type, val
           end
       end
       local a = nil
       local b = 0
       local c = 0
       local d = 0
       type = tokennumber[str[point]]
       if type == "number" then
           a = n[str[point]]
       elseif type == nil or str[point] == "万" then
           return "error", nil
       else
           b = n[str[point]]
       end
       while point < ts.length do
           point = point + 1
           type = tokennumber[str[point]]
           if type == nil then
               point = point - 1
               if a then
                   b = b + a
               end
               return "number", c * 10000 + b
           elseif type == "number" then
               if a then
                   return "error", nil
               else
                   a = n[str[point]]
               end
           elseif str[point] == "万" then
               if c == 0 and b ~= 0 then
                   if a then
                       b = b + a
                       a = nil
                   end
                   c = b
                   b = 0
               else
                   return "error", nil
               end
           else
               if a then
                   b = b + a * n[str[point]]
                   a = nil
               else
                   b = b + n[str[point]]
               end
           end
       end
       return "end", nil
   end
   local u = {}
   u["年"] = function (v2)
       local p = point
       local t, v
       for i3, v3 in ipairs(v2) do
           t, v = scan()
           if v3 == t then
               if t == "number" then
                   if e == "明治" then
                       if ts.year > 1872 then
                           if ts.year + 1867 ~= v then
                               table.insert(ts.warnings, "年不一致")
                               return false
                           end
                       end
                   elseif ts.year + era[ts.era] - 1 ~= v then
                       table.insert(ts.warnings, "年不一致")
                       return false
                   end
               end
           elseif v3 == "open" then
               point = p
               return true
           else
               ts.debug = v3 .. t .. point
               return false
           end
       end
       return true
   end
   u["曜日"] = function (v2)
       local p = point
       local t, v = scan()
       if t == "曜日" then
           ts.dayofweek = dayofweek[v]
       else
           point = p
       end
       return true
   end
   local pm = false
   u["午"] = function (v2)
       local p = point
       local t, v = scan()
       if t == "午" then
           if v == "午後" then
               pm = true
           end
       else
           point = p
       end
       return true
   end
   u["分秒"] = function (v2)
       local p = point
       local t, v = scan()
       if t == "number" then
           ts.minute = v
       else
           point = p
           ts.next = nil--"timezone"
           if pm then
               ts.hour = ts.hour + 12
           end
           ts.position = p + 1
           return nil
       end
       t, v = scan()
       if t ~= "分" then
           point = p
           ts.next = nil--"timezone"
           if pm then
               ts.hour = ts.hour + 12
           end
           ts.position = p + 1
           return nil
       end
       p = point
       t, v = scan()
       if t == "number" then
           ts.second = v
       else
           point = p
           ts.next = nil--"timezone"
           if pm then
               ts.hour = ts.hour + 12
           end
           ts.position = p + 1
           return nil
       end
       t, v = scan()
       if t ~= "秒" then
           point = p
           ts.next = nil--"timezone"
           if pm then
               ts.hour = ts.hour + 12
           end
           ts.position = p + 1
           return nil
       end
       return true
   end
   if not ustr.match(ts.trimmed, "時") then
       table.remove(state)
   end
   local t, v
   for i1, v1 in ipairs(state) do
       for i2, v2 in ipairs(v1) do
           if type(v2) == "table" then
               local z = u[v2.call](v2)
               if z == false then
                   ts.next = nil
                   ts.invalid = true
                   return
               elseif z == nil then
                   return
               end
           else
               local v21, v22 = unpack(mw.text.split(v2, "|", true))
               t, v = scan()
               if v21 == t then
                   if v22 then
                       if v22 == "year" and v == 1 then
                           ts.next = nil
                           ts.invalid = true
                           table.insert(ts.warning, "元号1年")
                       end
                       ts[v22] = v
                   end
               elseif v2 == "number|year" and t == "元" then
                   ts.year = 1
               elseif t == "end" and v21 == "number" then
                   ts.next = nil
                   if pm then
                       ts.hour = ts.hour + 12
                   end
                   return
               else
                   ts.next = nil
                   ts.invalid = true
                   ts.debug = v2 .. t .. point .. tostring(v) .. v21
                   return
               end
           end
       end
   end
   if ustr.match(ts.trimmed, "時") and pm then
       ts.hour = ts.hour + 12
   end
   ts.next = nil

end

-- ]ja]


function v.validate(ts)

   if ts.year and ts.year > 1900 then
       if ts.month then
           if ts.month == 1 or (3 <= ts.month and ts.month <= 12) then
               if ts.day and (ts.day < 1 or d[ts.month] < ts.day) then
                   table.insert(ts.warnings, "範囲外/day")
               end
           elseif ts.month == 2 then
               if ts.day then
                   if ts.year % 4 == 0 and (ts.year % 100 ~= 0 or ts.year % 400 == 0) then
                       if ts.day < 1 or 29 < ts.day then
                           table.insert(ts.warnings, "範囲外/day")
                       end
                   else
                       if ts.day < 1 or 28 < ts.day then
                           table.insert(ts.warnings, "範囲外/day")
                       end
                   end
               end
           else
               table.insert(ts.warnings, "範囲外/month")
           end
       elseif ts.dayofyear then
           if ts.year % 4 == 0 and (ts.year % 100 ~= 0 or ts.year % 400 == 0) then
               if ts.dayofyear < 1 or 366 < ts.dayofyear then
                   table.insert(ts.warnings, "範囲外/dayofyear")
               end
           else
               if ts.dayofyear < 1 or 365 < ts.dayofyear then
                   table.insert(ts.warnings, "範囲外/dayofyear")
               end
           end
       end
   end
   if ts.dayofweek and (ts.dayofweek < 1 or 7 < ts.dayofweek) then
       table.insert(ts.warnings, "範囲外/dayofweek")
   end
   if ts.hour and (ts.hour < 0 or 23 < ts.hour) then
       table.insert(ts.warnings, "範囲外/hour")
   end
   if ts.minute and (ts.minute < 0 or 59 < ts.minute) then
       table.insert(ts.warnings, "範囲外/minute")
   end
   if ts.second and (ts.second < 0 or 60 < ts.second) then
       table.insert(ts.warnings, "範囲外/second")
   end

end


function f.pf(ts, format)

   local lang = mw.language.getContentLanguage()
   if ts.unixtime then
       return lang:formatDate(format, ts.trimmed)
   end
   if ts.hour == nil then
       ts.hour = 0
   end
   if ts.minute == nil then
       ts.minute = 0
   end
   if ts.second == nil then
       ts.second = 0
   end
   local date = ""
   if ts.dayofyear then
       date = string.format("%04u%03uT%02u%02u%02u",
           ts.year, ts.dayofyear, ts.hour, ts.minute, ts.second)
   elseif ts.week then
       if ts.dayofweek == nil then
           ts.dayofweek = 1
       end
       date = string.format("%04uW%02u%1uT%02u%02u%02u",
           ts.yearofweek, ts.week, ts.dayofweek, ts.hour, ts.minute, ts.second)
   else
       if ts.era then
           ts.year = ts.year + era[ts.era] - 1
       end
       if ts.month == nil then
           ts.month = 1
       end
       if ts.day == nil then
           ts.day = 1
       end
       date = string.format("%04u%02u%02uT%02u%02u%02u",
           ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second)
   end
   local issuccess, ret = pcall(lang.formatDate, lang, format, date)
   return ret

end

function f.test(ts)

   if ts.unixtime then
       return ts.trimmed
   end
   if ts.hour == nil then
       ts.hour = 0
   end
   if ts.minute == nil then
       ts.minute = 0
   end
   if ts.second == nil then
       ts.second = 0
   end
   if ts.dayofyear then
       return string.format("%04u%03uT%02u%02u%02u",
           ts.year, ts.dayofyear, ts.hour, ts.minute, ts.second)
   elseif ts.week then
       if ts.dayofweek == nil then
           ts.dayofweek = 1
       end
       return string.format("%04uW%02u%1uT%02u%02u%02u",
           ts.yearofweek, ts.week, ts.dayofweek, ts.hour, ts.minute, ts.second)
   end
   if ts.era then
       ts.year = ts.year + era[ts.era] - 1
   end
   if ts.month == nil then
       ts.month = 1
   end
   if ts.day == nil then
       ts.day = 1
   end
   return string.format("%04u%02u%02uT%02u%02u%02u",
       ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second)

end

return p