Zeile 1: |
Zeile 1: |
− | --[=[ 2015-03-16 | + | local DateTime = { serial = "2020-08-30", |
− | Date and time utilities | + | suite = "DateTime", |
− | ]=]
| + | item = 20652535 } |
− | | + | -- Date and time objects |
− | | + | local Failsafe = DateTime |
− | | + | local GlobalMod = DateTime |
− | -- local globals
| + | local Calc = { } |
− | local DateTime | + | local Meta = { } |
| local Parser = { } | | local Parser = { } |
| local Private = { } | | local Private = { } |
| local Prototypes = { } | | local Prototypes = { } |
| + | local Templates = { } |
| local World = { slang = "en", | | local World = { slang = "en", |
| monthsLong = { }, | | monthsLong = { }, |
Zeile 15: |
Zeile 16: |
| months4 = { } } | | months4 = { } } |
| local MaxYear = 2099 | | local MaxYear = 2099 |
− | local Nbsp = mw.ustring.char( 160 ) | + | local Frame |
− | local Tab = mw.ustring.char( 9 )
| + | DateTime.char = { nbsp = mw.ustring.char( 160 ), |
| + | tab = mw.ustring.char( 9 ) } |
| World.era = { en = { "BC", "AD" } } | | World.era = { en = { "BC", "AD" } } |
| World.monthsAbbr = { en = { n = 3 } } | | World.monthsAbbr = { en = { n = 3 } } |
Zeile 105: |
Zeile 107: |
| HST = -1000 -- Hawaiian Standard Time | | HST = -1000 -- Hawaiian Standard Time |
| } | | } |
| + | |
| + | |
| + | |
| + | local foreignModule = function ( access, advanced, append, alt, alert ) |
| + | -- Fetch global module |
| + | -- Precondition: |
| + | -- access -- string, with name of base module |
| + | -- advanced -- true, for require(); else mw.loadData() |
| + | -- append -- string, with subpage part, if any; or false |
| + | -- alt -- number, of wikidata item of root; or false |
| + | -- alert -- true, for throwing error on data problem |
| + | -- Postcondition: |
| + | -- Returns whatever, probably table |
| + | -- 2020-01-01 |
| + | local storage = access |
| + | local fun, lucky, r |
| + | if advanced then |
| + | fun = require |
| + | else |
| + | fun = mw.loadData |
| + | end |
| + | if append then |
| + | storage = string.format( "%s/%s", storage, append ) |
| + | end |
| + | lucky, r = pcall( fun, "Module:" .. storage ) |
| + | if not lucky then |
| + | local suited |
| + | GlobalMod.globalModules = GlobalMod.globalModules or { } |
| + | suited = GlobalMod.globalModules[ access ] |
| + | if not suited and |
| + | type( alt ) == "number" and |
| + | alt > 0 then |
| + | suited = string.format( "Q%d", alt ) |
| + | suited = mw.wikibase.getSitelink( suited ) |
| + | GlobalMod.globalModules[ access ] = suited or true |
| + | end |
| + | if type( suited ) == "string" then |
| + | storage = suited |
| + | if append then |
| + | storage = string.format( "%s/%s", storage, append ) |
| + | end |
| + | lucky, r = pcall( fun, storage ) |
| + | end |
| + | if not lucky and alert then |
| + | error( "Missing or invalid page: " .. storage ) |
| + | end |
| + | end |
| + | return r |
| + | end -- foreignModule() |
| | | |
| | | |
Zeile 116: |
Zeile 167: |
| return mw.ustring.upper( mw.ustring.sub( a, 1, 1 ) ) | | return mw.ustring.upper( mw.ustring.sub( a, 1, 1 ) ) |
| .. mw.ustring.lower( mw.ustring.sub( a, 2 ) ) | | .. mw.ustring.lower( mw.ustring.sub( a, 2 ) ) |
− | end -- fault() | + | end -- capitalize() |
| | | |
| | | |
Zeile 126: |
Zeile 177: |
| -- Returns: | | -- Returns: |
| -- string, HTML span | | -- string, HTML span |
− | return string.format( "<span class=\"error\">%s</span>", a ) | + | local e = mw.html.create( "span" ) |
| + | :addClass( "error" ) |
| + | :wikitext( a ) |
| + | return tostring( e ) |
| end -- fault() | | end -- fault() |
| | | |
| | | |
| | | |
− | DateTime = function ( assign, alien ) | + | local function frame() |
− | -- Create metatable (constructor) | + | if not Frame then |
| + | Frame = mw.getCurrentFrame() |
| + | end |
| + | return Frame |
| + | end -- frame() |
| + | |
| + | |
| + | |
| + | Meta.localized = false |
| + | Meta.serial = DateTime.serial |
| + | Meta.signature = "__datetime" |
| + | Meta.suite = "{DateTime}" |
| + | Meta.components = { lang = "string", |
| + | bc = "boolean", |
| + | year = "number", |
| + | month = "number", |
| + | week = "number", |
| + | dom = "number", |
| + | hour = "number", |
| + | min = "number", |
| + | sec = "number", |
| + | msec = "number", |
| + | mysec = "number", |
| + | zone = false, |
| + | leap = "boolean", |
| + | jul = "boolean" } |
| + | Meta.order = { "bc", "year", "month", "week", "dom", |
| + | "hour", "min", "sec", "msec", "mysec" } |
| + | Meta.tableI = { -- instance metatable |
| + | __index = function ( self, access ) |
| + | local r = self[ Meta.signature ][ access ] |
| + | if r == nil then |
| + | if access == "serial" then |
| + | r = Meta.serial |
| + | elseif access == "suite" then |
| + | r = "DateTime" |
| + | else |
| + | r = Prototypes[ access ] |
| + | end |
| + | end |
| + | return r |
| + | end, |
| + | __newindex = function ( self, access, assign ) |
| + | if type( access ) == "string" then |
| + | local data = self[ Meta.signature ] |
| + | if assign == nil then |
| + | local val = data[ access ] |
| + | data[ access ] = nil |
| + | if not Prototypes.fair( data ) then |
| + | data[ access ] = val |
| + | end |
| + | elseif Prototypes.fair( data, |
| + | access, |
| + | assign ) then |
| + | data[ access ] = assign |
| + | end |
| + | end |
| + | return |
| + | end, |
| + | __add = function ( op1, op2 ) |
| + | return Prototypes.future( op1, op2, true ) |
| + | end, |
| + | __eq = function ( op1, op2 ) |
| + | return Prototypes.flow( op1, op2, "eq" ) |
| + | end, |
| + | __lt = function ( op1, op2 ) |
| + | return Prototypes.flow( op1, op2, "lt" ) |
| + | end, |
| + | __le = function ( op1, op2 ) |
| + | return Prototypes.flow( op1, op2, "le" ) |
| + | end, |
| + | __tostring = function ( e ) |
| + | return Prototypes.tostring( e ) |
| + | end, |
| + | __call = function ( func, ... ) |
| + | return Meta.fiat( ... ) |
| + | end |
| + | } -- Meta.tableI |
| + | Meta.tableL = { -- library metatable |
| + | __index = function ( self, access ) |
| + | local r |
| + | if access == "serial" then |
| + | r = Meta.serial |
| + | elseif access == "suite" then |
| + | r = Meta.suite |
| + | end |
| + | return r |
| + | end, |
| + | __newindex = function () |
| + | return |
| + | end, |
| + | __tostring = function () |
| + | return Meta.suite |
| + | end, |
| + | __call = function ( func, ... ) |
| + | return Meta.fiat( ... ) |
| + | end |
| + | } -- Meta.tableL |
| + | Meta.fiat = function ( assign, alien, add ) |
| + | -- Create instance object (constructor) |
| -- Parameter: | | -- Parameter: |
| -- assign -- string, with initial timestamp, or nil | | -- assign -- string, with initial timestamp, or nil |
| -- nil -- now | | -- nil -- now |
| -- false -- empty object | | -- false -- empty object |
| + | -- table -- clone this object, or copy from raw |
| + | -- ignore remaining parameters |
| -- alien -- string, with language code, or nil | | -- alien -- string, with language code, or nil |
| + | -- add -- string, with interval (PHP strtotime), or nil |
| -- Returns: | | -- Returns: |
| -- table, as DateTime object | | -- table, as DateTime object |
Zeile 143: |
Zeile 299: |
| local r | | local r |
| Private.foreign() | | Private.foreign() |
− | r = Private.factory( assign, alien ) | + | if type( assign ) == "table" then |
| + | if assign.suite == Meta.suite and |
| + | getmetatable( assign ) == Meta.tableI then |
| + | r = assign[ Meta.signature ] |
| + | else |
| + | r = Private.from( assign ) |
| + | end |
| + | else |
| + | r = Private.factory( assign, alien, add ) |
| + | end |
| if type( r ) == "table" then | | if type( r ) == "table" then |
− | local meta = { } | + | r = { [ Meta.signature ] = r } |
− | local s = "__datetime"
| + | setmetatable( r, Meta.tableI ) |
− | meta.__index = function( self, access )
| |
− | return self[ s ][ access ]
| |
− | end
| |
− | meta.__newindex = function( self, access, assign )
| |
− | if type( access ) == "string" then
| |
− | local data = self[ s ]
| |
− | if assign == nil then
| |
− | local val = data[ access ]
| |
− | data[ access ] = nil
| |
− | if not Prototypes.fair( data ) then
| |
− | data[ access ] = val
| |
− | end
| |
− | elseif Prototypes.fair( data,
| |
− | access,
| |
− | assign ) then
| |
− | data[ access ] = assign
| |
− | end
| |
− | end
| |
− | return
| |
− | end
| |
− | r = { [ s ] = r }
| |
− | r.fair = function ( ... )
| |
− | return Prototypes.fair( ... )
| |
− | end
| |
− | r.figure = function ( ... )
| |
− | return Prototypes.figure( ... )
| |
− | end
| |
− | r.first = function ( ... )
| |
− | return Prototypes.first( ... )
| |
− | end
| |
− | r.format = function ( ... )
| |
− | return Prototypes.format( ... )
| |
− | end
| |
− | r.full = function ( ... )
| |
− | return Prototypes.full( ... )
| |
− | end
| |
− | setmetatable( r, meta ) | |
| end | | end |
| return r | | return r |
− | end -- DateTime() | + | end -- Meta.fiat() |
| + | setmetatable( DateTime, Meta.tableL ) |
| + | DateTime.serial = nil |
| + | |
| | | |
| + | Calc.months = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } |
| | | |
| | | |
− | Parser.digitsHeading = function ( analyse, alone, amount, add )
| + | |
− | -- String analysis, if digits only or at least 4 digits heading | + | -- Calc.fast = function ( at ) |
| + | -- -- Quick scan of full ISO stamp |
| + | -- -- Parameter: |
| + | -- -- apply -- string, ISO |
| + | -- -- Returns: |
| + | -- -- table, with numeric components |
| + | -- local r = { } |
| + | -- r.year = tonumber( at:sub( 1, 4 ) ) |
| + | -- r.month = tonumber( at:sub( 6, 2 ) ) |
| + | -- r.dom = tonumber( at:sub( 9, 2 ) ) |
| + | -- r.hour = tonumber( at:sub( 12, 2 ) ) |
| + | -- r.min = tonumber( at:sub( 14, 2 ) ) |
| + | -- r.sec = tonumber( at:sub( 17, 2 ) ) |
| + | -- if at:sub( 19, 1 ) == "." then |
| + | -- r.msec = tonumber( at:sub( 20, 3 ) ) |
| + | -- if #at > 22 then |
| + | -- r.mysec = tonumber( at:sub( 23, 3 ) ) |
| + | -- end |
| + | -- end |
| + | -- return r |
| + | -- end -- Calc.fast() |
| + | |
| + | |
| + | |
| + | Calc.fair = function ( adjust ) |
| + | -- Normalize numeric components |
| + | -- Parameter: |
| + | -- adjust -- table, with raw numbers |
| + | local ranges = { year = { min = -999, |
| + | max = 9999 }, |
| + | month = { min = 1, |
| + | max = 12, |
| + | mod = 12 }, |
| + | dom = { min = 1, |
| + | max = 28 }, |
| + | hour = { mod = 24 }, |
| + | min = { mod = 60 }, |
| + | sec = { mod = 60 }, |
| + | msec = { mod = 1000 }, |
| + | mysec = { mod = 1000 } } |
| + | local m, max, min, move, n, range, s |
| + | for i = 10, 2, -1 do |
| + | s = Meta.order[ i ] |
| + | n = adjust[ s ] |
| + | if n or move then |
| + | range = ranges[ s ] |
| + | if range then |
| + | min = range.min or 0 |
| + | max = range.max or ( range.mod - 1 ) |
| + | if move then |
| + | n = ( n or 0 ) + move |
| + | move = false |
| + | end |
| + | if n < min or n > max then |
| + | if range.mod then |
| + | m = n % range.mod |
| + | move = ( n - m ) / range.mod |
| + | n = min + m |
| + | else -- dom |
| + | if adjust.month and adjust.year and |
| + | adjust.month >= 1 and |
| + | adjust.month <= 12 and |
| + | adjust.year > 1900 then |
| + | if n > 0 then |
| + | max = Calc.final( adjust ) |
| + | while n > max do |
| + | n = n - max |
| + | if adjust.month < 12 then |
| + | adjust.month = adjust.month + 1 |
| + | else |
| + | adjust.month = 1 |
| + | adjust.year = adjust.year + 1 |
| + | end |
| + | max = Calc.final( adjust ) |
| + | end -- while n <= max |
| + | else |
| + | while n < 1 do |
| + | if adjust.month == 1 then |
| + | adjust.month = 12 |
| + | adjust.year = adjust.year - 1 |
| + | else |
| + | adjust.month = adjust.month - 1 |
| + | end |
| + | max = Calc.final( adjust ) |
| + | n = n + max |
| + | end -- while n < 1 |
| + | end |
| + | end |
| + | end |
| + | end |
| + | adjust[ s ] = n |
| + | end |
| + | end |
| + | end -- for i |
| + | end -- Calc.fair() |
| + | |
| + | |
| + | |
| + | Calc.final = function ( adjust ) |
| + | -- Retrieve number of days in particular month |
| -- Parameter: | | -- Parameter: |
− | -- analyse -- string to be scanned, starting with digit | + | -- adjust -- table, with date specification |
− | -- digits only, else starting with exactly 4 digits
| |
− | -- alone -- true, if only digits
| |
− | -- amount -- number of heading digits
| |
− | -- add -- table, to be extended
| |
| -- Returns: | | -- Returns: |
− | -- table, extended if parsed | + | -- number, of days in month |
− | -- false, if invalid text format
| + | local r = Calc.months[ adjust.month ] |
− | local r = add | + | if adjust.month == 2 and |
− | if alone then | + | ( adjust.year % 4 ~= 0 or |
− | -- digits only | + | adjust.year % 400 == 0 ) then |
− | if amount <= 4 then
| + | r = 28 |
− | r.year = tonumber( analyse )
| + | end |
− | elseif n == 14 then
| + | return r |
− | -- timestamp
| + | end -- Calc.final() |
− | r.year = tonumber( analyse:sub( 1, 4 ) )
| + | |
− | r.month = tonumber( analyse:sub( 5, 2 ) )
| + | |
− | r.dom = tonumber( analyse:sub( 7, 2 ) )
| + | |
− | r.hour = tonumber( analyse:sub( 9, 2 ) )
| + | Calc.future = function ( add ) |
− | r.min = tonumber( analyse:sub( 11, 2 ) )
| + | -- Parse move interval |
− | r.sec = tonumber( analyse:sub( 13, 2 ) ) | + | -- Parameter: |
| + | -- add -- string, with GNU relative items |
| + | -- Returns: |
| + | -- table, with numeric components, or false |
| + | local r, token |
| + | local units = { year = true, |
| + | month = true, |
| + | fortnight = { slot = "dom", mult = 14 }, |
| + | week = { slot = "dom", mult = 7 }, |
| + | dom = true, |
| + | hour = true, |
| + | min = true, |
| + | sec = true } |
| + | local story = string.format( " %s ", add:lower() ) |
| + | :gsub( "%s+", " " ) |
| + | :gsub( " yesterday ", " -1 dom " ) |
| + | :gsub( " tomorrow ", " 1 dom " ) |
| + | :gsub( "(%l)s ", "%1 " ) |
| + | :gsub( " day ", " dom " ) |
| + | :gsub( " minute ", " min " ) |
| + | :gsub( " second ", " sec " ) |
| + | local feed = function () |
| + | local slice |
| + | token, slice = story:match( "^( (%S+)) " ) |
| + | return slice |
| + | end |
| + | local fed = function () |
| + | story = story:sub( #token + 1 ) |
| + | end |
| + | local m, n, s, u |
| + | while true do |
| + | s = feed() |
| + | if s then |
| + | n = 1 |
| + | if s:match( "^[+-]?%d+$" ) then |
| + | n = tonumber( s ) |
| + | fed() |
| + | s = feed() |
| + | end |
| + | if s then |
| + | u = units[ s ] |
| + | end |
| + | if s and u then |
| + | fed() |
| + | if u ~= true then |
| + | s = u.slot |
| + | n = n * u.mult |
| + | end |
| + | if feed() == "ago" then |
| + | if n > 0 then |
| + | n = - n |
| + | end |
| + | fed() |
| + | end |
| + | r = r or { } |
| + | r[ s ] = ( r[ s ] or 0 ) + n |
| + | else |
| + | r = false |
| + | break -- while true |
| + | end |
| else | | else |
− | r = false | + | break -- while true |
| end | | end |
− | elseif amount == 4 then | + | end -- while true |
− | local s, sep, sx = analyse:match( "^(%d+)([%-%.:Ww]?)(.*)$" ) | + | return r |
| + | end -- Calc.future() |
| + | |
| + | |
| + | |
| + | Parser.digitsHeading = function ( analyse, alone, amount, add ) |
| + | -- String analysis, if digits only or at least 4 digits heading |
| + | -- Parameter: |
| + | -- analyse -- string to be scanned, starting with digit |
| + | -- digits only, else starting with exactly 4 digits |
| + | -- alone -- true, if only digits |
| + | -- amount -- number of heading digits |
| + | -- add -- table, to be extended |
| + | -- Returns: |
| + | -- table, extended if parsed |
| + | -- false, if invalid text format |
| + | local r = add |
| + | if alone then |
| + | -- digits only |
| + | if amount <= 4 then |
| + | r.year = tonumber( analyse ) |
| + | elseif amount == 14 then |
| + | -- timestamp |
| + | r.year = tonumber( analyse:sub( 1, 4 ) ) |
| + | r.month = tonumber( analyse:sub( 5, 6 ) ) |
| + | r.dom = tonumber( analyse:sub( 7, 8 ) ) |
| + | r.hour = tonumber( analyse:sub( 9, 10 ) ) |
| + | r.min = tonumber( analyse:sub( 11, 12 ) ) |
| + | r.sec = tonumber( analyse:sub( 13, 14 ) ) |
| + | else |
| + | r = false |
| + | end |
| + | elseif amount == 4 then |
| + | local s, sep, sx = analyse:match( "^(%d+)([%-%.:Ww]?)(.*)$" ) |
| r.year = tonumber( s ) | | r.year = tonumber( s ) |
| if sep == "-" then | | if sep == "-" then |
Zeile 263: |
Zeile 582: |
| else | | else |
| r = false | | r = false |
| + | end |
| + | if r then |
| + | r.iso = true |
| end | | end |
| elseif amount == 8 then | | elseif amount == 8 then |
Zeile 283: |
Zeile 605: |
| n = n .. "00" | | n = n .. "00" |
| r.msec = tonumber( n:sub( 1, 3 ) ) | | r.msec = tonumber( n:sub( 1, 3 ) ) |
− | sz = s | + | if #n >= 6 then |
| + | r.mysec = tonumber( n:sub( 4, 6 ) ) |
| + | end |
| + | sz = s |
| end | | end |
| end | | end |
Zeile 461: |
Zeile 786: |
| rO = false | | rO = false |
| end | | end |
− | if not rO then | + | if rO then |
| + | rO.iso = true |
| + | else |
| rS = false | | rS = false |
| end | | end |
Zeile 500: |
Zeile 827: |
| n = #s2 | | n = #s2 |
| if n <= 2 and #s3 == 4 then | | if n <= 2 and #s3 == 4 then |
− | rO.dom = tonumber( n ) | + | rO.dom = tonumber( s2 ) |
| rO.year = tonumber( s3 ) | | rO.year = tonumber( s3 ) |
| rO.dom2 = ( n == 2 ) | | rO.dom2 = ( n == 2 ) |
Zeile 640: |
Zeile 967: |
| s = sx:match( "^%.(%d+)$" ) | | s = sx:match( "^%.(%d+)$" ) |
| if s then | | if s then |
− | r.msec = tonumber( s ) | + | s = s .. "00" |
| + | r.msec = tonumber( s:sub( 1, 3 ) ) |
| + | if #s >= 6 then |
| + | r.mysec = tonumber( s:sub( 4, 6 ) ) |
| + | end |
| else | | else |
| r = false | | r = false |
Zeile 861: |
Zeile 1.192: |
| | | |
| | | |
− | Private.factory = function ( assign, alien ) | + | Private.factory = function ( assign, alien, add ) |
| -- Create DateTime table (constructor) | | -- Create DateTime table (constructor) |
| -- Parameter: | | -- Parameter: |
Zeile 868: |
Zeile 1.199: |
| -- false -- empty object | | -- false -- empty object |
| -- alien -- string, with language code, or nil | | -- alien -- string, with language code, or nil |
| + | -- add -- string, with interval (PHP strtotime), or nil |
| -- Returns: | | -- Returns: |
| -- table, for DateTime object | | -- table, for DateTime object |
| -- string or false, if failed | | -- string or false, if failed |
| local l = true | | local l = true |
− | local r = false
| |
| local slang = mw.text.trim( alien or World.slang or "en" ) | | local slang = mw.text.trim( alien or World.slang or "en" ) |
| + | local r |
| if assign == false then | | if assign == false then |
| r = { } | | r = { } |
| else | | else |
| local stamp = ( assign or "now" ) | | local stamp = ( assign or "now" ) |
| + | local shift |
| + | if add then |
| + | shift = Private.future( add ) |
| + | end |
| + | r = false |
| if stamp == "now" then | | if stamp == "now" then |
− | stamp = mw.getCurrentFrame():callParserFunction( "#timel", | + | stamp = frame():callParserFunction( "#timel", "c", shift ) |
− | "c" )
| + | shift = false |
| else | | else |
| local seconds = stamp:match( "^#(%d+)$" ) | | local seconds = stamp:match( "^#(%d+)$" ) |
Zeile 888: |
Zeile 1.225: |
| end | | end |
| end | | end |
− | l, r = pcall( Private.fetch, stamp, slang ) | + | l, r = pcall( Private.fetch, stamp, slang, shift ) |
| end | | end |
| if l and type( r ) == "table" then | | if l and type( r ) == "table" then |
Zeile 900: |
Zeile 1.237: |
| | | |
| | | |
− | Private.fetch = function ( analyse, alien ) | + | Private.fetch = function ( analyse, alien, add ) |
| -- Retrieve object from string | | -- Retrieve object from string |
| -- Parameter: | | -- Parameter: |
| -- analyse -- string to be interpreted | | -- analyse -- string to be interpreted |
| -- alien -- string with language code, or nil | | -- alien -- string with language code, or nil |
| + | -- add -- table, with interval, or nil |
| -- Returns: | | -- Returns: |
| -- table, if parsed | | -- table, if parsed |
Zeile 911: |
Zeile 1.249: |
| local r | | local r |
| if type( analyse ) == "string" then | | if type( analyse ) == "string" then |
| + | local strip = mw.ustring.char( 0x5B, 0x200E, 0x200F, 0x5D ) |
| r = analyse:gsub( " ", " " ) | | r = analyse:gsub( " ", " " ) |
| :gsub( " ", " " ) | | :gsub( " ", " " ) |
| :gsub( "&#x[aA]0;", " " ) | | :gsub( "&#x[aA]0;", " " ) |
| :gsub( " ", " " ) | | :gsub( " ", " " ) |
− | :gsub( Nbsp, " " ) | + | :gsub( DateTime.char.nbsp, " " ) |
− | :gsub( Tab, " " ) | + | :gsub( DateTime.char.tab, " " ) |
| :gsub( " +", " " ) | | :gsub( " +", " " ) |
| :gsub( "%[%[", "" ) | | :gsub( "%[%[", "" ) |
| :gsub( "%]%]", "" ) | | :gsub( "%]%]", "" ) |
| + | :gsub( strip, "" ) |
| r = mw.text.trim( r ) | | r = mw.text.trim( r ) |
| if r == "" then | | if r == "" then |
| r = { } | | r = { } |
| else | | else |
− | local slang = ( alien or "" ) | + | local slang = ( alien or "" ) |
| + | local parser = { en = "GermanEnglish", |
| + | de = "GermanEnglish", |
| + | frr = "GermanEnglish", |
| + | nds = "GermanEnglish" } |
| + | local suitable |
| if slang == "" then | | if slang == "" then |
| slang = "en" | | slang = "en" |
Zeile 933: |
Zeile 1.278: |
| end | | end |
| end | | end |
− | slang = slang:lower() | + | slang = slang:lower() |
− | if slang == "en" or slang == "de" then | + | suitable = parser[ slang ] |
| + | if suitable then |
| local l | | local l |
− | l, r = pcall( Parser.GermanEnglish, r ) | + | l, r = pcall( Parser[ suitable ], r ) |
| if l and r then | | if l and r then |
| if not Prototypes.fair( r ) then | | if not Prototypes.fair( r ) then |
| r = false | | r = false |
| + | elseif add then |
| + | r = Prototypes.future( r, add ) |
| end | | end |
| + | else |
| + | r = "invalid format" |
| end | | end |
| else | | else |
− | r = "unknown language" | + | r = "unknown language: " .. slang |
| end | | end |
| end | | end |
Zeile 954: |
Zeile 1.304: |
| | | |
| | | |
− | Private.foreign = function () | + | Private.field = function ( at, ask, adapt, atleast ) |
− | -- Retrieve localization submodule | + | -- Format object as string |
− | if not World.localization then | + | -- Parameter: |
− | local l, d = pcall( mw.loadData, "Module:DateTime/local" )
| + | -- at -- DateTime |
− | if l then | + | -- ask -- string, with format spec, or nil |
− | local wk
| + | -- adapt -- table, with options, or nil |
− | if d.slang then | + | -- .lang -- string, with particular language code |
− | World.slang = d.slang | + | -- .london -- true: UTC output; default: local |
− | end
| + | -- .lonely -- true: permit lonely hour |
− | for k, v in pairs( d ) do
| + | -- atleast -- string, with default value, or nil |
− | wk = World[ k ]
| + | -- Returns: |
− | if wk and wk.en then | + | -- string, or false, if invalid, or number for julian date |
− | for subk, subv in pairs( v ) do | + | local r, spec |
− | wk[ subk ] = subv
| + | if type( ask ) == "string" then |
− | end -- for k, v
| + | if ask:sub( 1, 1 ) == "$" then |
| + | if ask:sub( 1, 11 ) == "$JulianDate" then |
| + | local luxury = ( ask:sub( -2 ) == ",$" ) |
| + | if ask:sub( 1, 14 ) == "$JulianDateJul" then |
| + | at.legacy = true |
| + | elseif ask:sub( 1, 15 ) == "$JulianDateGreg" then |
| else | | else |
− | World[ k ] = v | + | at.legacy = Private.former( at ) |
| end | | end |
− | end -- for k, v | + | r = Private.fixed( at, luxury ) |
| + | elseif ask:sub( 1, 11 ) == "$JulianCal$" then |
| + | adapt.legacy = true |
| + | spec = ask:sub( 12 ) |
| + | elseif ask:sub( 1, 3 ) == "$\"$" then |
| + | r = ask:sub( 4 ) |
| + | else |
| + | spec = ask |
| + | end |
| + | else |
| + | spec = ask |
| end | | end |
− | World.localization = true | + | else |
| + | spec = false |
| end | | end |
− | end -- Private.foreign() | + | if not r then |
| + | r = Private.format( at, spec, adapt ) |
| + | end |
| + | return r or atleast |
| + | end -- Private.field() |
| | | |
| | | |
| | | |
− | Prototypes.fair = function ( self, access, assign )
| + | Private.fixed = function ( at, advanced ) |
− | -- Check formal validity of table | + | -- Compute julian date |
| -- Parameter: | | -- Parameter: |
− | -- self -- table, to be checked | + | -- at -- DateTime |
− | -- access -- string or nil, single item to be checked | + | -- .legacy -- true: at is in Julian calendar |
− | -- assign -- single access value to be checked | + | -- advanced -- true: format long number |
| -- Returns: | | -- Returns: |
− | -- true, if valid; false, if not | + | -- number, or string |
− | local r = ( type( self ) == "table" ) | + | local mM, mMY, mY, nY, r |
− | if r then | + | if at.year then |
− | local defs = { year = { max = MaxYear }, | + | mY = at.year * 12 |
− | month = { min = 1,
| + | else -- actually invalid |
− | max = 12 },
| + | mY = 0 |
− | dom = { min = 1,
| + | end |
− | max = 31 },
| + | if at.month then |
− | hour = { max = 23 },
| + | mMY = at.month |
− | min = { max = 59 },
| + | else |
− | sec = { max = 61 },
| + | mMY = 1 |
− | msec = { max = 1000 }
| + | end |
− | }
| + | mMY = mMY + 57609 |
− | local months = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } | + | if at.dom then |
− | local fNum = | + | r = at.dom |
− | function ( k, v ) | + | else |
− | local ret = true
| + | r = 1 |
− | local dk = defs[ k ]
| + | end |
− | if dk then
| + | mM = ( mY + mMY ) * 0.08333333333 -- divided by 12 months |
− | if type( dk.max ) == "number" then
| + | nY = math.floor( mM - 1 ) |
− | ret = ( type( v ) == "number" )
| + | r = math.floor( nY * 365.25 ) |
− | if ret then
| + | + math.floor( ( mMY%12 + 4 ) * 30.6 ) |
− | local min
| + | + r |
− | if dk.min then
| + | if at.legacy then |
− | min = dk.min
| + | r = r - 32205.5 |
− | else
| + | else |
− | min = 0
| + | r = r - math.floor( nY * 0.01 ) -- no leap day in century |
− | end
| + | + math.floor( nY * 0.0025 ) -- but every 400 years |
− | ret = ( v >= min and v <= dk.max
| + | - 32167.5 |
− | and math.floor( v ) == v )
| + | end |
− | if ret and dk.f then
| + | if at.hour then -- divided by 24 hours per day |
− | ret = dk.f( v )
| + | r = r + at.hour * 0.0416666666666667 |
− | end
| + | else |
− | end | + | r = r + 0.5 |
| + | end |
| + | if at.min then -- divided by 1440 minutes per day |
| + | r = r + at.min * 0.000694444444 |
| + | end |
| + | if at.sec then -- divided by 86400 seconds per day |
| + | r = r + at.min * 0.00001157407407 |
| + | end |
| + | if at.bc then |
| + | r = 3442406 - r |
| + | if at.legacy then |
| + | r = r + 3 |
| + | end |
| + | end |
| + | if advanced then |
| + | local slang = ( at.lang or World.slang ) |
| + | local o = mw.language.new( slang ) |
| + | r = o:formatNum( r ) |
| + | end |
| + | return r |
| + | end -- Private.fixed() |
| + | |
| + | |
| + | |
| + | Private.flow = function ( at1, at2 ) |
| + | -- Compare two objects |
| + | -- Parameter: |
| + | -- at1 -- DateTime |
| + | -- at2 -- DateTime |
| + | -- Returns: |
| + | -- -1, 0, 1 or nil if not comparable |
| + | local r = 0 |
| + | if at1.bc or at2.bc and at1.bc ~= at2.bc then |
| + | if at1.bc then |
| + | r = -1 |
| + | else |
| + | r = 1 |
| + | end |
| + | else |
| + | local life = false |
| + | local s, v1, v2 |
| + | for i = 2, 10 do |
| + | s = Meta.order[ i ] |
| + | v1 = at1[ s ] |
| + | v2 = at2[ s ] |
| + | if v1 or v2 then |
| + | if v1 and v2 then |
| + | if v1 < v2 then |
| + | r = -1 |
| + | elseif v1 > v2 then |
| + | r = 1 |
| + | end |
| + | elseif life then |
| + | if v2 then |
| + | r = -1 |
| + | else |
| + | r = 1 |
| end | | end |
| + | else |
| + | r = nil |
| end | | end |
− | return ret | + | if r ~= 0 then |
− | end -- fNum()
| + | if at1.bc and r then |
− | defs.dom.f =
| + | r = r * -1 |
− | function ()
| + | end |
− | local ret
| + | break -- for i |
− | local d
| |
− | if access == "dom" then
| |
− | d = assign
| |
− | else
| |
− | d = self.dom | |
| end | | end |
− | if d then | + | life = true |
− | ret = ( d <= 28 )
| + | end |
− | if not ret then
| + | end -- for i |
− | local m
| + | end |
− | if access == "month" then
| + | return r |
− | m = assign
| + | end -- Private.flow() |
− | else
| + | |
− | m = self.month
| + | |
− | end
| + | |
− | if m then
| + | Private.foreign = function () |
− | ret = ( d <= months[ m ] )
| + | -- Retrieve localization submodule |
− | if ret then
| + | if not Meta.localized then |
− | local y
| + | local d = foreignModule( DateTime.suite, |
− | if access == "year" then
| + | false, |
− | y = assign
| + | "local", |
− | else
| + | DateTime.item ) |
− | y = self.year
| + | if type( d ) == "table" then |
− | end
| + | local wk |
− | if d == 29 and m == 2 and y then
| + | if d.slang then |
− | if y % 4 ~= 0 or y % 400 == 0 then
| + | Meta.suite = string.format( "%s %s", |
− | ret = false
| + | Meta.suite, d.slang ) |
− | end
| + | World.slang = d.slang |
− | end
| + | end |
− | end
| + | for k, v in pairs( d ) do |
− | end | + | wk = World[ k ] |
− | end | + | if wk and wk.en then |
| + | for subk, subv in pairs( v ) do |
| + | wk[ subk ] = subv |
| + | end -- for k, v |
| else | | else |
− | ret = true | + | World[ k ] = v |
| end | | end |
− | return ret
| + | end -- for k, v |
− | end -- defs.dom.f()
| + | end |
− | defs.sec.f = | + | Meta.localized = true |
− | function () | + | end |
− | local ret | + | end -- Private.foreign() |
− | local second | + | |
− | if access == "sec" then | + | |
− | second = assign
| + | |
− | else | + | Private.format = function ( at, ask, adapt ) |
− | second = self.sec | + | -- Format object as string |
| + | -- Parameter: |
| + | -- at -- table, with numbers etc. |
| + | -- ask -- string, format spec, or nil |
| + | -- adapt -- table, with options, or nil |
| + | -- .lang -- string, with particular language code |
| + | -- .london -- true: UTC output; default: local |
| + | -- .lonely -- true: permit lonely hour |
| + | -- Returns: |
| + | -- string, or not |
| + | local slang = at.lang or "en" |
| + | local opts = { lang = slang } |
| + | local babel, r |
| + | if type( adapt ) == "table" then |
| + | if type( adapt.lang ) == "string" then |
| + | local i = adapt.lang:find( "-", 3, true ) |
| + | if i then |
| + | slang = adapt.lang:lower() |
| + | opts.lang = slang:sub( 1, i - 1 ) |
| + | else |
| + | opts.lang = adapt.lang:lower() |
| + | end |
| + | end |
| + | opts.london = adapt.london |
| + | opts.lonely = adapt.lonely |
| + | end |
| + | babel = mw.language.new( opts.lang:lower() ) |
| + | if babel then |
| + | local shift, show, stamp, suffix, limit4, locally |
| + | if at.month then |
| + | stamp = World.monthsLong.en[ at.month ] |
| + | if at.year then |
| + | stamp = string.format( "%s %04d", stamp, at.year ) |
| + | end |
| + | if at.dom then |
| + | stamp = string.format( "%d %s", at.dom, stamp ) |
| + | end |
| + | if ask and ask:find( "Mon4", 1, true ) then |
| + | local mon4 = World.months4[ opts.lang:lower() ] |
| + | if mon4 and mon4[ at.month ] then |
| + | limit4 = true |
| end | | end |
− | if second then | + | end |
− | ret = ( second <= 59 ) | + | elseif at.year then |
− | if not ret and self.leap then | + | stamp = string.format( "%04d", at.year ) |
− | ret = true | + | end |
| + | if at.hour then |
| + | if stamp then |
| + | stamp = stamp .. " " |
| + | else |
| + | stamp = "" |
| + | end |
| + | stamp = string.format( "%s%02d:", stamp, at.hour ) |
| + | if at.min then |
| + | stamp = string.format( "%s%02d", stamp, at.min ) |
| + | if at.sec then |
| + | stamp = string.format( "%s:%02d", |
| + | stamp, at.sec ) |
| + | if at.msec then |
| + | stamp = string.format( "%s.%03d", |
| + | stamp, at.msec ) |
| + | if at.mysec then |
| + | stamp = string.format( "%s%03d", |
| + | stamp, |
| + | at.mysec ) |
| + | end |
| end | | end |
| end | | end |
− | return ret | + | else |
− | end -- defs.sec.f() | + | stamp = stamp .. "00" |
− | if access or assign then | + | end |
− | r = ( type( access ) == "string" ) | + | if at.zone then |
− | if r then | + | stamp = stamp .. World.zones.formatter( at, "+-" ) |
− | local def = defs[ access ] | + | end |
− | if def then | + | end |
− | r = fNum( access, assign ) | + | show, suffix = World.templates.formatter( at, ask, opts ) |
− | if r then | + | if limit4 then |
− | if def == "dom" or | + | show = show:gsub( "M", "F" ) |
− | def == "month" or
| + | end |
− | def == "year" then
| + | if type( opts.london ) == "boolean" then |
− | r = defs.dom.f() | + | locally = not opts.london |
| + | else |
| + | locally = true |
| + | end |
| + | r = babel:formatDate( show, stamp, locally ) |
| + | r = r:gsub( " $", "" ) |
| + | if at.year and at.year < 1000 then |
| + | r = r:gsub( string.format( "%04d", at.year ), |
| + | tostring( at.year ) ) |
| + | end |
| + | if at.month then |
| + | local bucket, m, suite, x |
| + | if show:find( "F", 1, true ) then |
| + | suite = "monthsLong" |
| + | elseif show:find( "M", 1, true ) then |
| + | suite = "monthsAbbr" |
| + | end |
| + | bucket = World[ suite ] |
| + | if bucket then |
| + | m = bucket[ opts.lang:lower() ] |
| + | if slang then |
| + | x = bucket[ slang:lower() ] |
| + | end |
| + | if m then |
| + | local base = m[ at.month ] |
| + | local ex |
| + | if x then |
| + | ex = x[ at.month ] |
| + | end |
| + | if suite == "monthsAbbr" then |
| + | local stop |
| + | if ex then |
| + | stop = x.suffix |
| + | base = ex |
| + | else |
| + | stop = m.suffix |
| + | end |
| + | if base and stop then |
| + | local shift, std |
| + | std = string.format( "%s%%%s", |
| + | base[ 1 ], stop ) |
| + | shift = string.format( "%s%s", |
| + | base[ 2 ], stop ) |
| + | r = mw.ustring.gsub( r, std, shift ) |
| + | end |
| + | elseif suite == "monthsLong" then |
| + | if base and ex then |
| + | r = mw.ustring.gsub( r, base, ex ) |
| end | | end |
| end | | end |
− | elseif access == "lang" then
| |
− | r = ( type( assign ) == "string" )
| |
− | if r then
| |
− | r = assign:match( "^%l%l%l?-?%a*$" )
| |
− | end
| |
− | elseif access == "london" then
| |
− | r = ( type( assign ) == "boolean" )
| |
| end | | end |
| end | | end |
− | else | + | end |
− | local order = { "bc", "year", "month", "dom",
| + | if suffix then |
− | "hour", "min", "sec", "msec" }
| + | r = r .. suffix |
− | local life = false
| |
− | local leak = false
| |
− | local s, v
| |
− | for i = 1, 8 do
| |
− | s = order[ i ]
| |
− | v = self[ s ]
| |
− | if v then
| |
− | if not life and leak then
| |
− | -- gap detected
| |
− | r = false
| |
− | break
| |
− | else
| |
− | if not fNum( s, v ) then
| |
− | r = false
| |
− | break
| |
− | end
| |
− | life = true
| |
− | leak = true
| |
− | end
| |
− | else
| |
− | life = false
| |
− | end
| |
− | end -- for i
| |
| end | | end |
| end | | end |
| return r | | return r |
− | end -- Prototypes.fair() | + | end -- Private.format() |
| | | |
| | | |
| | | |
− | Prototypes.figure = function ( self, assign )
| + | Private.former = function ( at ) |
− | -- Assign month by name | + | -- Analyze whether Julian calendar |
| -- Parameter: | | -- Parameter: |
− | -- self -- table, to be filled | + | -- at -- table, to be evaluated |
− | -- assign -- string, with month name | + | -- Returns: |
| + | -- true, i |
| + | local r |
| + | if at.year then |
| + | if at.year < 1582 then |
| + | r = true |
| + | elseif at.year == 1582 then |
| + | if at.month then |
| + | if at.month < 10 then |
| + | r = true |
| + | elseif at.month == 10 then |
| + | r = ( at.dom <= 15 ) |
| + | end |
| + | end |
| + | end |
| + | end |
| + | return r |
| + | end -- Private.former() |
| + | |
| + | |
| + | |
| + | Private.from = function ( attempt ) |
| + | -- Create valid raw table from arbitrary table |
| + | -- Parameter: |
| + | -- attempt -- table, to be evaluated |
| -- Returns: | | -- Returns: |
− | -- number 1...12, if valid; false, if not | + | -- table, with valid components, or nil |
− | local r = false | + | local data = { } |
− | if type( self ) == "table" and type( assign ) == "string" then | + | local r |
− | r = Parser.monthNumber( assign ) | + | for k, v in pairs( Meta.components ) do |
− | if r then | + | if v then |
− | self.month = r | + | v = ( type( attempt[ k ] ) == v ) |
| + | else |
| + | v = true |
| + | end |
| + | if v then |
| + | data[ k ] = attempt[ k ] |
| end | | end |
| + | end -- for k, v |
| + | if Prototypes.fair( data ) then |
| + | r = data |
| end | | end |
| return r | | return r |
− | end -- Prototypes.figure() | + | end -- Private.from() |
| | | |
| | | |
| | | |
− | Prototypes.first = function ( self )
| + | Private.future = function ( add ) |
− | -- Retrieve abbreviated month name in current language | + | -- Normalize move interval |
| -- Parameter: | | -- Parameter: |
− | -- self -- table, to be evaluated | + | -- add -- string or number, to be added |
| -- Returns: | | -- Returns: |
− | -- string, if defined; false, if not | + | -- table, with shift, or false/nil |
| local r | | local r |
− | if type( self ) == "table" and self.month then | + | if add then |
− | local slang = ( self.lang or World.slang ) | + | local s = type( add ) |
− | r = World.monthsLong[ slang ] | + | if s == "string" and add:match( "^%s*[+-]?%d+%.?%d*%s*$" ) then |
| + | r = tonumber( add ) |
| + | s = "number" |
| + | end |
| + | if s == "number" then |
| + | if r == 0 then |
| + | r = false |
| + | else |
| + | r = string.format( "%d second", r or add ) |
| + | end |
| + | elseif s == "string" then |
| + | r = add |
| + | else |
| + | r = false |
| + | end |
| if r then | | if r then |
− | local brief = World.monthsAbbr[ slang ] | + | r = Calc.future( r ) |
− | r = r[ self.month ]
| |
− | if brief then
| |
− | local ex = brief[ self.month ]
| |
− | local s = brief.suffix
| |
− | if ex then
| |
− | r = ex[ 2 ]
| |
− | else
| |
− | local n = brief.n or 3
| |
− | r = mw.ustring.sub( r, 1, n )
| |
− | end
| |
− | if s then
| |
− | r = r .. s
| |
− | end
| |
− | end
| |
| end | | end |
− | else
| |
− | r = false
| |
| end | | end |
| return r | | return r |
− | end -- Prototypes.first() | + | end -- Private.future() |
| | | |
| | | |
| | | |
− | Prototypes.format = function ( self, ask, adapt ) | + | Prototypes.clone = function ( self ) |
− | -- Format object as string | + | -- Clone object |
| -- Parameter: | | -- Parameter: |
− | -- self -- table, with numbers etc. | + | -- self -- table, with object, to be cloned |
− | -- ask -- string, format spec, or nil
| |
− | -- adapt -- table, with options, or nil
| |
− | -- .lang -- string, with particular language code
| |
− | -- .lonely -- true: permit lonely hour
| |
| -- Returns: | | -- Returns: |
− | -- string, or false, if invalid | + | -- table, with object |
− | local r = false | + | local r = { [ Meta.signature ] = self[ Meta.signature ] } |
− | if type( self ) == "table" then | + | setmetatable( r, Meta.tableI ) |
− | local opts = { lang = self.lang } | + | return r |
− | local babel, slang | + | end -- Prototypes.clone() |
− | if type( adapt ) == "table" then | + | |
− | if type( adapt.lang ) == "string" then | + | |
− | local i = adapt.lang:find( "-", 3, true )
| + | |
− | if i then
| + | Prototypes.failsafe = function ( self, atleast ) |
− | slang = adapt.lang
| + | -- Retrieve versioning and check for compliance |
− | opts.lang = slang:sub( 1, i - 1 )
| + | -- Precondition: |
− | else
| + | -- self -- table, or not, with DateTime object, unused |
− | opts.lang = adapt.lang
| + | -- atleast -- string, with required version |
| + | -- or "wikidata" or "~" or "@" or false |
| + | -- Postcondition: |
| + | -- Returns string -- with queried version/item, also if problem |
| + | -- false -- if appropriate |
| + | -- 2020-08-17 |
| + | local since = atleast |
| + | local last = ( since == "~" ) |
| + | local linked = ( since == "@" ) |
| + | local link = ( since == "item" ) |
| + | local r |
| + | if last or link or linked or since == "wikidata" then |
| + | local item = Meta.item |
| + | since = false |
| + | if type( item ) == "number" and item > 0 then |
| + | local suited = string.format( "Q%d", item ) |
| + | if linkedlink then |
| + | r = suited |
| + | else |
| + | local entity = mw.wikibase.getEntity( suited ) |
| + | if type( entity ) == "table" then |
| + | local seek = Failsafe.serialProperty or "P348" |
| + | local vsn = entity:formatPropertyValues( seek ) |
| + | if type( vsn ) == "table" and |
| + | type( vsn.value ) == "string" and |
| + | vsn.value ~= "" then |
| + | if last and |
| + | vsn.value == ( Meta.serial or |
| + | DateTime.serial ) then |
| + | r = false |
| + | elseif linked then |
| + | if mw.title.getCurrentTitle().prefixedText |
| + | == mw.wikibase.getSitelink( suited ) then |
| + | r = false |
| + | else |
| + | r = suited |
| + | end |
| + | else |
| + | r = vsn.value |
| + | end |
| + | if last then |
| + | r = false |
| + | else |
| + | r = vsn.value |
| + | end |
| + | end |
| end | | end |
| end | | end |
− | opts.lang = opts.lang:lower()
| |
− | opts.london = adapt.london
| |
− | opts.lonely = adapt.lonely
| |
| end | | end |
− | babel = mw.language.new( opts.lang )
| + | end |
− | if babel then
| + | if type( r ) == "nil" then |
− | local shift, show, stamp, suffix, limit4, locally
| + | if not since or since <= Meta.serial then |
− | if self.month then
| + | r = Meta.serial |
− | stamp = World.monthsLong.en[ self.month ]
| + | else |
− | if self.year then
| + | r = false |
− | stamp = string.format( "%s %04d", stamp, self.year )
| + | end |
− | end
| + | end |
− | if self.dom then
| + | return r |
− | stamp = string.format( "%d %s", self.dom, stamp )
| + | end -- Prototypes.failsafe() |
− | end
| + | |
− | if ask and ask:find( "Mon4" ) then
| + | |
− | local mon4 = World.months4[ opts.lang ]
| + | |
− | if mon4 then
| + | Prototypes.fair = function ( self, access, assign ) |
− | if mon4[ self.month ] then
| + | -- Check formal validity of table |
− | limit4 = true
| + | -- Parameter: |
− | end
| + | -- self -- table, to be checked |
− | end
| + | -- access -- string or nil, single item to be checked |
− | end
| + | -- assign -- single access value to be checked |
− | elseif self.year then
| |
− | stamp = string.format( "%04d", self.year )
| |
− | end
| |
− | if self.hour then
| |
− | stamp = string.format( "%s %02d:", stamp, self.hour )
| |
− | if self.min then
| |
− | stamp = string.format( "%s%02d", stamp, self.min )
| |
− | if self.sec then
| |
− | stamp = string.format( "%s:%02d",
| |
− | stamp, self.sec )
| |
− | if self.msec then
| |
− | stamp = string.format( "%s.%d",
| |
− | stamp, self.msec )
| |
− | end
| |
− | end
| |
− | else
| |
− | stamp = stamp .. "00"
| |
− | end
| |
− | if self.zone then
| |
− | stamp = stamp .. World.zones.formatter( self, "+-" )
| |
− | end
| |
− | end
| |
− | show, suffix = World.templates.formatter( self, ask, opts )
| |
− | if limit4 then
| |
− | show = show:gsub( "M", "F" )
| |
− | end
| |
− | if type( opts.london ) == "boolean" then
| |
− | locally = not opts.london
| |
− | else
| |
− | locally = true
| |
− | end
| |
− | r = babel:formatDate( show, stamp, locally ) | |
− | r = r:gsub( " $", "" )
| |
− | if self.year and self.year < 1000 then
| |
− | r = r:gsub( string.format( "%04d", self.year ),
| |
− | tostring( self.year ) )
| |
− | end
| |
− | if self.month then
| |
− | local bucket, m, suite, x
| |
− | if show:find( "F" ) then
| |
− | suite = "monthsLong"
| |
− | elseif show:find( "M" ) then
| |
− | suite = "monthsAbbr"
| |
− | end
| |
− | bucket = World[ suite ]
| |
− | if bucket then
| |
− | m = bucket[ opts.lang ]
| |
− | if slang then
| |
− | x = bucket[ slang ]
| |
− | end
| |
− | if m then
| |
− | local base = m[ self.month ]
| |
− | local ex
| |
− | if x then
| |
− | ex = x[ self.month ]
| |
− | end
| |
− | if suite == "monthsAbbr" then
| |
− | local stop
| |
− | if ex then
| |
− | stop = x.suffix
| |
− | base = ex
| |
− | else
| |
− | stop = m.suffix
| |
− | end
| |
− | if base and stop then
| |
− | local shift, std
| |
− | std = string.format( "%s%%%s",
| |
− | base[ 1 ], stop )
| |
− | shift = string.format( "%s%s",
| |
− | base[ 2 ], stop )
| |
− | r = mw.ustring.gsub( r, std, shift )
| |
− | end
| |
− | elseif suite == "monthsLong" then
| |
− | if base and ex then
| |
− | r = mw.ustring.gsub( r, base, ex )
| |
− | end
| |
− | end
| |
− | end
| |
− | end
| |
− | end
| |
− | if suffix then | |
− | r = r .. suffix
| |
− | end
| |
− | end | |
− | end | |
− | return r | |
− | end -- Prototypes.format() | |
− | | |
− | | |
− | | |
− | Prototypes.full = function ( self ) | |
− | -- Retrieve month name in current language | |
− | -- Parameter: | |
− | -- self -- table, to be evaluated | |
| -- Returns: | | -- Returns: |
− | -- string, if defined; false, if not | + | -- true, if valid; false, if not |
− | local r | + | local r = ( type( self ) == "table" ) |
− | if type( self ) == "table" and self.month then
| + | if r then |
− | local slang = ( self.lang or World.slang ) | + | local defs = { year = { max = MaxYear }, |
− | r = World.monthsLong[ slang ]
| + | month = { min = 1, |
− | if r then
| + | max = 12 }, |
− | r = r[ self.month ]
| + | week = { min = 1, |
− | end
| + | max = 53 }, |
− | else
| + | dom = { min = 1, |
− | r = false
| + | max = 31 }, |
− | end
| + | hour = { max = 23 }, |
− | return r
| + | min = { max = 59 }, |
− | end -- Prototypes.full()
| + | sec = { max = 61 }, |
− | | + | msec = { max = 999 }, |
− | | + | mysec = { max = 999 } |
− | | + | } |
− | World.templates.formatter = function ( assigned, ask, adapt )
| + | local fNum = |
− | -- Retrieve format specification string
| + | function ( k, v ) |
− | -- Parameter:
| + | local ret = true |
− | -- assigned -- table, with numbers etc.
| + | local dk = defs[ k ] |
− | -- ask -- string, format spec, or nil
| + | if dk then |
− | -- adapt -- table, with options
| + | if type( dk.max ) == "number" then |
− | -- .lang -- string, with particular language code
| + | ret = ( type( v ) == "number" ) |
− | -- .lonely -- true: permit lonely hour
| + | if ret then |
− | -- Returns:
| + | local min |
− | -- 1 -- string
| + | if dk.min then |
− | -- 2 -- string or nil; append suffix (zone)
| + | min = dk.min |
− | local r1, r2
| + | else |
− | if not ask or ask == "" then
| + | min = 0 |
− | r1 = "c"
| + | end |
− | else
| + | ret = ( v >= min and v <= dk.max |
− | local template = World.templates[ ask ] | + | and math.floor( v ) == v ) |
− | r1 = ask
| + | if ret and dk.f then |
− | if not template then
| + | ret = dk.f( v ) |
− | local slang = ( adapt.lang or assigned.lang or World.slang ) | + | end |
− | local tmp = World.templates[ slang ]
| + | end |
− | if tmp then
| |
− | template = tmp[ ask ] | |
− | end
| |
− | end
| |
− | if type( template ) == "table" then
| |
− | local low = ( ask == "ISO" or ask == "ISO-T" )
| |
− | r1 = template.spec
| |
− | if assigned.year then
| |
− | if not assigned.dom then
| |
− | r1 = r1:gsub( "[ .%-]?[dDjlNwz][ .,%-]*", "" )
| |
− | :gsub( "^ ", "" )
| |
− | if not assigned.month then
| |
− | r1 = r1:gsub( "[ .%-]?[FmMnt][ .%-]*", "" )
| |
| end | | end |
| end | | end |
− | else | + | return ret |
− | r1 = r1:gsub( " ?[yY] ?", "" )
| + | end -- fNum() |
− | if not assigned.dom then
| + | if self.bc then |
− | r1 = r1:gsub( "[ .]?[dDjlNwz][ .,%-]*", "" )
| + | defs.year.max = 999999 |
− | :gsub( "^ ", "" )
| + | end |
| + | defs.dom.f = |
| + | function () |
| + | local ret |
| + | local d |
| + | if access == "dom" then |
| + | d = assign |
| + | else |
| + | d = self.dom |
| end | | end |
− | end
| + | if d then |
− | if template.lift and
| + | ret = ( d <= 28 ) |
− | (assigned.dom or
| + | if not ret then |
− | not (assigned.month or assigned.year or assigned.bc)
| + | local m |
− | ) then
| + | if access == "month" then |
− | local stamp = false
| + | m = assign |
− | if assigned.hour then
| + | else |
− | if assigned.min then
| + | m = self.month |
− | stamp = "H:i" | + | end |
− | if assigned.sec then | + | if m then |
− | stamp = "H:i:s" | + | ret = ( d <= Calc.months[ m ] ) |
− | if assigned.msec then | + | if ret then |
− | stamp = string.format( "%s.%d", | + | local y |
− | stamp,
| + | if access == "year" then |
− | assigned.msec )
| + | y = assign |
| + | else |
| + | y = self.year |
| + | end |
| + | if d == 29 and m == 2 and y then |
| + | if y % 4 ~= 0 or |
| + | ( y % 100 == 0 and |
| + | y % 400 ~= 0 ) then |
| + | ret = false |
| + | end |
| + | end |
| end | | end |
| end | | end |
− | elseif adapt.lonely then
| |
− | stamp = "H"
| |
| end | | end |
| + | else |
| + | ret = true |
| end | | end |
− | if low or ask:find( "hh:mm:ss" ) then | + | return ret |
− | if stamp then | + | end -- defs.dom.f() |
− | r1 = string.format( "%s %s", r1, stamp )
| + | defs.sec.f = |
− | end
| + | function () |
| + | local ret |
| + | local second |
| + | if access == "sec" then |
| + | second = assign |
| + | else |
| + | second = self.sec |
| end | | end |
− | if stamp then | + | if second then |
− | if low or template.long then | + | ret = ( second <= 59 ) |
− | local scheme
| + | if not ret and self.leap then |
− | if template.long then
| + | ret = true |
− | scheme = mw.language.getContentLanguage()
| |
− | scheme = scheme.code
| |
− | end
| |
− | r2 = World.zones.formatter( assigned, scheme ) | |
| end | | end |
| end | | end |
− | end | + | return ret |
− | if type ( assigned.bc ) == "boolean" then | + | end -- defs.sec.f() |
− | local eras = World.era[ adapt.lang ] or World.era.en | + | if access or assign then |
− | local i | + | r = ( type( access ) == "string" ) |
− | if not r2 then | + | if r then |
− | r2 = "" | + | local def = defs[ access ] |
| + | if def then |
| + | r = fNum( access, assign ) |
| + | if r then |
| + | if def == "dom" or |
| + | def == "month" or |
| + | def == "year" then |
| + | r = defs.dom.f() |
| + | end |
| + | end |
| + | elseif access == "lang" then |
| + | r = ( type( assign ) == "string" ) |
| + | if r then |
| + | r = assign:match( "^%l%l%l?-?%a*$" ) |
| + | end |
| + | elseif access == "london" then |
| + | r = ( type( assign ) == "boolean" ) |
| end | | end |
− | if assigned.bc then | + | end |
− | i = 1 | + | else |
− | else | + | local life = false |
− | i = 2 | + | local leak = false |
| + | local s, v |
| + | for i = 1, 10 do |
| + | s = Meta.order[ i ] |
| + | v = self[ s ] |
| + | if v then |
| + | if not life and leak then |
| + | -- gap detected |
| + | r = false |
| + | break |
| + | else |
| + | if not fNum( s, v ) then |
| + | r = false |
| + | break -- for i |
| + | end |
| + | life = true |
| + | leak = true |
| + | end |
| + | elseif i == 3 then |
| + | if not self.week then |
| + | life = false |
| + | end |
| + | elseif i ~= 4 then |
| + | life = false |
| end | | end |
− | r2 = string.format( "%s %s", r2, eras[ i ] )
| + | end -- for i |
| + | if self.week and ( self.month or self.dom ) then |
| + | r = false |
| end | | end |
| end | | end |
| end | | end |
− | return r1, r2 | + | return r |
− | end -- World.templates.formatter() | + | end -- Prototypes.fair() |
| | | |
| | | |
| | | |
− | World.zones.formatter = function ( assigned, align )
| + | Prototypes.figure = function ( self, assign ) |
− | -- Retrieve time zone specification string | + | -- Assign month by name |
| -- Parameter: | | -- Parameter: |
− | -- assigned -- table, with numbers etc. | + | -- self -- table, to be filled |
− | -- .zone should be available
| + | -- assign -- string, with month name |
− | -- align -- string, format spec, or nil | |
− | -- nil, false, "+-" -- +/- 0000
| |
− | -- "Z" -- single letter
| |
− | -- "UTC" -- "UTC", if appropriate
| |
− | -- "de" -- try localized
| |
| -- Returns: | | -- Returns: |
− | -- string | + | -- number 1...12, if valid; false, if not |
− | local r = "" | + | local r = false |
− | local move = 0 | + | if type( self ) == "table" and type( assign ) == "string" then |
− | if assigned.zone then | + | r = Parser.monthNumber( assign ) |
− | local s = type( assigned.zone ) | + | if r then |
− | if s == "string" then | + | self.month = r |
− | s = assigned.zone:upper() | + | end |
− | if #s == 1 then | + | end |
− | -- "YXWVUTSRQPONZABCDEFGHIKLM" | + | return r |
− | move = World.zones[ "!" ]:find( s ) | + | end -- Prototypes.figure() |
− | if move then | + | |
− | move = ( move - 13 ) * 100 | + | |
− | assigned.zone = move
| + | |
| + | Prototypes.first = function ( self ) |
| + | -- Retrieve abbreviated month name in current language |
| + | -- Parameter: |
| + | -- self -- table, to be evaluated |
| + | -- Returns: |
| + | -- string, if defined; false, if not |
| + | local r |
| + | if type( self ) == "table" and self.month then |
| + | local slang = ( self.lang or World.slang ) |
| + | r = World.monthsLong[ slang ] |
| + | if r then |
| + | local brief = World.monthsAbbr[ slang ] |
| + | r = r[ self.month ] |
| + | if brief then |
| + | local ex = brief[ self.month ] |
| + | local s = brief.suffix |
| + | if ex then |
| + | r = ex[ 2 ] |
| else | | else |
− | assigned.zone = false | + | local n = brief.n or 3 |
| + | r = mw.ustring.sub( r, 1, n ) |
| end | | end |
− | else
| + | if s then |
− | local code = World.zones[ s ] | + | r = r .. s |
− | if not code then
| |
− | local slang = ( assigned.lang or
| |
− | World.slang )
| |
− | local tmp = World.zones[ slang ]
| |
− | if tmp then
| |
− | code = tmp[ s ]
| |
− | end
| |
− | end
| |
− | if code then
| |
− | move = code
| |
− | assigned.zone = move
| |
| end | | end |
| end | | end |
− | elseif s == "number" then
| |
− | move = assigned.zone
| |
| end | | end |
| + | else |
| + | r = false |
| end | | end |
− | if move then | + | return r |
− | local spec = "+-" | + | end -- Prototypes.first() |
− | if align then | + | |
− | if align == "Z" then | + | |
− | if move % 100 == 0 then | + | |
− | r = World.zones[ "!" ]:sub( move / 100 + 13, 1 ) | + | Prototypes.fix = function ( self ) |
− | spec = false | + | -- Adapt this object to local time if no explicit zone given |
| + | -- Parameter: |
| + | -- self -- table, with numbers etc. |
| + | if type( self ) == "table" and |
| + | not self.zone then |
| + | local seconds = Prototypes.format( self, "Z" ) |
| + | Prototypes.future( self, - tonumber( seconds ) ) |
| + | end |
| + | end -- Prototypes.fix() |
| + | |
| + | |
| + | |
| + | Prototypes.flow = function ( self, another, assert ) |
| + | -- Compare this object with another timestamp |
| + | -- Parameter: |
| + | -- self -- table, with numbers etc. |
| + | -- another -- DateTime or string or nil (now) |
| + | -- assert -- nil, or string with operator |
| + | -- "lt", "le", "eq", "ne", "ge", "gt", |
| + | -- "<", "<=", "==", "~=", "<>", ">=", "=>", ">" |
| + | -- Returns: |
| + | -- if assert: true or false |
| + | -- else: -1, 0, 1 |
| + | -- nil if invalid |
| + | local base, other, r |
| + | if type( self ) == "table" then |
| + | base = self |
| + | other = another |
| + | elseif type( another ) == "table" then |
| + | base = another |
| + | other = self |
| + | end |
| + | if base then |
| + | if type( other ) ~= "table" then |
| + | other = Meta.fiat( other ) |
| + | end |
| + | if type( other ) == "table" then |
| + | r = Private.flow( base, other ) |
| + | if r and type( assert ) == "string" then |
| + | local trsl = { lt = "<", |
| + | ["<"] = "<", |
| + | le = "<=", |
| + | ["<="] = "<=", |
| + | eq = "=", |
| + | ["=="] = "=", |
| + | ne = "<>", |
| + | ["<>"] = "<>", |
| + | ["~="] = "<>", |
| + | ge = ">=", |
| + | [">="] = ">=", |
| + | ["=>"] = ">=", |
| + | gt = ">", |
| + | [">"] = ">" } |
| + | local same = trsl[ assert:lower() ] |
| + | if same then |
| + | local s = "=" |
| + | if r < 0 then |
| + | s = "<" |
| + | elseif r > 0 then |
| + | s = ">" |
| + | end |
| + | r = ( same:find( s, 1, true ) ~= nil ) |
| + | else |
| + | r = nil |
| end | | end |
− | elseif align ~= "+-" then
| + | end |
− | if move == 0 then
| + | end |
− | r = " UTC"
| + | end |
− | spec = false
| + | return r |
− | else
| + | end -- Prototypes.flow() |
− | local part = World.zones[ align ]
| |
− | if part then
| |
− | for k, v in pairs( part ) do
| |
− | if v == move then
| |
− | r = string.format( " (%s)", k )
| |
− | spec = false
| |
− | break
| |
− | end
| |
− | end -- for k, v
| |
− | end
| |
− | end
| |
− | end | |
− | end
| |
− | if spec == "+-" then
| |
− | if move < 0 then
| |
− | spec = "%4.4d"
| |
− | else
| |
− | spec = "+%4.4d"
| |
− | end
| |
− | r = string.format( spec, move )
| |
− | r = string.format( "%s:%s",
| |
− | r:sub( 1, 3), r:sub( 4 ) )
| |
− | end | |
− | end | |
− | return r | |
− | end -- World.zones.formatter() | |
| | | |
| | | |
| | | |
− | -- Export | + | Prototypes.format = function ( self, ask, adapt ) |
− | local p = { } | + | -- Format object as string |
| + | -- Parameter: |
| + | -- self -- table, with numbers etc. |
| + | -- ask -- string, format spec, or nil |
| + | -- table, with multiple formats |
| + | -- string may contain multiple formats joined by "|||" |
| + | -- adapt -- table, with options, or nil |
| + | -- .lang -- string, with particular language code |
| + | -- .london -- true: UTC output; default: local |
| + | -- .lonely -- true: permit lonely hour |
| + | -- Returns: |
| + | -- string, or false, if invalid, or number for julian date |
| + | local r |
| + | if type( self ) == "table" then |
| + | local s = type( ask ) |
| + | local poly |
| + | if s == "string" and ask:find( "|||", 1, true ) then |
| + | poly = mw.text.split( ask, "|||" ) |
| + | elseif s == "table" then |
| + | poly = ask |
| + | end |
| + | if poly then |
| + | r = "" |
| + | for i = 1, #poly do |
| + | r = r .. Private.field( self, poly[ i ], adapt ) |
| + | end -- for i |
| + | else |
| + | r = Private.field( self, ask, adapt ) |
| + | end |
| + | end |
| + | return r or false |
| + | end -- Prototypes.format() |
| + | |
| + | |
| | | |
− | function p.test( args ) | + | Prototypes.full = function ( self ) |
− | local r | + | -- Retrieve month name in current language |
− | local obj = DateTime( args[ 1 ], "de" ) | + | -- Parameter: |
− | if type( obj ) == "table" then | + | -- self -- table, to be evaluated |
| + | -- Returns: |
| + | -- string, if defined; false, if not |
| + | local r |
| + | if type( self ) == "table" and self.month then |
| + | local slang = ( self.lang or World.slang ) |
| + | r = World.monthsLong[ slang ] |
| + | if r then |
| + | r = r[ self.month ] |
| + | end |
| + | else |
| + | r = false |
| + | end |
| + | return r |
| + | end -- Prototypes.full() |
| + | |
| + | |
| + | |
| + | Prototypes.future = function ( self, add, allocate ) |
| + | -- Relative move by interval |
| + | -- Parameter: |
| + | -- self -- table, to be used as base |
| + | -- add -- string or number, to be added |
| + | -- allocate -- true, if a clone shall be returned |
| + | -- Returns: |
| + | -- table, with shift |
| + | local r, raw, rel, shift |
| + | if type( self ) == "table" then |
| + | r = self |
| + | shift = add |
| + | elseif type( add ) == "table" then |
| + | r = add |
| + | shift = self |
| + | end |
| + | if r then |
| + | if r[ Meta.signature ] then |
| + | raw = r[ Meta.signature ] |
| + | else |
| + | raw = r |
| + | end |
| + | if type( shift ) == "table" then |
| + | rel = shift |
| + | else |
| + | rel = Private.future( shift ) |
| + | end |
| + | end |
| + | if raw and rel then |
| + | if allocate then |
| + | r = Prototypes.clone( r ) |
| + | raw = r[ Meta.signature ] |
| + | end |
| + | for k, v in pairs( rel ) do |
| + | raw[ k ] = ( raw[ k ] or 0 ) + v |
| + | end -- for k, v |
| + | Calc.fair( raw ) |
| + | r[ Meta.signature ] = raw |
| + | end |
| + | return r |
| + | end -- Prototypes.future() |
| + | |
| + | |
| + | |
| + | Prototypes.tostring = function ( self ) |
| + | -- Stringify yourself |
| + | -- Parameter: |
| + | -- self -- table, to be stringified |
| + | -- Returns: |
| + | -- string |
| + | local dels = { false, "", "-", "-", "", "", ":", ":", ".", "" } |
| + | local wids = { false, 4, 2, 2, 2, 2, 2, 2, 3, 3 } |
| + | local s = "" |
| + | local n, r, spec |
| + | local f = function ( a ) |
| + | n = self[ Meta.order[ a ] ] |
| + | s = s .. dels[ a ] |
| + | if n then |
| + | spec = string.format( "%%s%%0%dd", wids[ a ] ) |
| + | s = string.format( spec, s, n ) |
| + | end |
| + | end -- f() |
| + | for i = 2, 5 do |
| + | f( i ) |
| + | end -- for i |
| + | r = s |
| + | s = "" |
| + | for i = 6, 10 do |
| + | f( i ) |
| + | end -- for i |
| + | if s == "::." then |
| + | r = r:gsub( "%-+$", "" ) |
| + | else |
| + | if r == "--" then |
| + | r = s |
| + | else |
| + | r = string.format( "%sT%s", r, s ) |
| + | end |
| + | end |
| + | r = r:gsub( "%.$", "" ) |
| + | if self.bc then |
| + | if self.year then |
| + | r = "-" .. r |
| + | else |
| + | r = r .. " BC" |
| + | end |
| + | end |
| + | return r |
| + | end -- Prototypes.tostring() |
| + | |
| + | |
| + | |
| + | Prototypes.valueOf = function ( self ) |
| + | -- Returns yourselves primitive value (primitive table) |
| + | -- Parameter: |
| + | -- self -- table, to be dumped |
| + | -- Returns: |
| + | -- table, or false |
| + | local r |
| + | if type( self ) == "table" then |
| + | r = self[ Meta.signature ] |
| + | end |
| + | return r or false |
| + | end -- Prototypes.valueOf() |
| + | |
| + | |
| + | |
| + | Templates.flow = function ( frame, action ) |
| + | -- Comparison invokation |
| + | -- Parameter: |
| + | -- frame -- object |
| + | -- Returns: |
| + | -- string, either "" or "1" |
| + | local r |
| + | local s1 = frame.args[ 1 ] |
| + | local s2 = frame.args[ 2 ] |
| + | if s1 then |
| + | s1 = mw.text.trim( s1 ) |
| + | if s1 == "" then |
| + | s1 = false |
| + | end |
| + | end |
| + | if s2 then |
| + | s2 = mw.text.trim( s2 ) |
| + | if s2 == "" then |
| + | s2 = false |
| + | end |
| + | end |
| + | if s1 or s2 then |
| + | local l |
| + | Frame = frame |
| + | l, r = pcall( Prototypes.flow, |
| + | Meta.fiat( s1 ), s2, action ) |
| + | if r == true then |
| + | r = "1" |
| + | end |
| + | end |
| + | return r or "" |
| + | end -- Templates.flow() |
| + | |
| + | |
| + | |
| + | World.templates.formatter = function ( assigned, ask, adapt ) |
| + | -- Retrieve format specification string |
| + | -- Parameter: |
| + | -- assigned -- table, with numbers etc. |
| + | -- ask -- string, format spec, or nil |
| + | -- adapt -- table, with options |
| + | -- .lang -- string, with particular language code |
| + | -- .lonely -- true: permit lonely hour |
| + | -- Returns: |
| + | -- 1 -- string |
| + | -- 2 -- string or nil; append suffix (zone) |
| + | local r1, r2 |
| + | if not ask or ask == "" then |
| + | r1 = "c" |
| + | elseif ask == "*" then |
| + | if World.present then |
| + | if assigned.hour then |
| + | if assigned.dom or assigned.month or assigned.year then |
| + | if World.present.both and |
| + | World.present.date and |
| + | World.present.time then |
| + | r1 = World.present.both |
| + | :gsub( "$date", World.present.date ) |
| + | :gsub( "$time", World.present.time ) |
| + | else |
| + | r1 = World.present.date |
| + | end |
| + | end |
| + | r1 = r1 or World.present.time |
| + | else |
| + | r1 = World.present.date |
| + | end |
| + | end |
| + | r1 = r1 or "c" |
| + | else |
| + | local template = World.templates[ ask ] |
| + | r1 = ask |
| + | if not template then |
| + | local slang = ( adapt.lang or assigned.lang or World.slang ) |
| + | local tmp = World.templates[ slang ] |
| + | if tmp then |
| + | template = tmp[ ask ] |
| + | end |
| + | if not template then |
| + | local i = slang:find( "-", 3, true ) |
| + | if i then |
| + | slang = slang:sub( 1, i - 1 ):lower() |
| + | tmp = World.templates[ slang ] |
| + | if tmp then |
| + | template = tmp[ ask ] |
| + | end |
| + | end |
| + | end |
| + | end |
| + | if type( template ) == "table" then |
| + | local low = ( ask == "ISO" or ask == "ISO-T" ) |
| + | r1 = template.spec |
| + | if assigned.year then |
| + | if not assigned.dom then |
| + | r1 = r1:gsub( "[ .%-]?[dDjlNwz][ .,%-]*", "" ) |
| + | :gsub( "^ ", "" ) |
| + | if not assigned.month then |
| + | r1 = r1:gsub( "[ .%-]?[FmMnt][ .%-]*", "" ) |
| + | end |
| + | end |
| + | else |
| + | r1 = r1:gsub( " ?[yY] ?", "" ) |
| + | if not assigned.dom then |
| + | r1 = r1:gsub( "[ .]?[dDjlNwz][ .,%-]*", "" ) |
| + | :gsub( "^ ", "" ) |
| + | end |
| + | end |
| + | if template.lift and |
| + | ( assigned.dom or |
| + | not ( assigned.month or assigned.year or assigned.bc ) |
| + | ) then |
| + | local stamp = false |
| + | if assigned.hour then |
| + | if assigned.min then |
| + | stamp = "H:i" |
| + | if assigned.sec then |
| + | stamp = "H:i:s" |
| + | if assigned.msec then |
| + | stamp = string.format( "%s.%03d", |
| + | stamp, |
| + | assigned.msec ) |
| + | if assigned.mysec then |
| + | stamp = string.format( "%s.%03d", |
| + | stamp, |
| + | assigned.mysec ) |
| + | end |
| + | end |
| + | end |
| + | elseif adapt.lonely then |
| + | stamp = "H" |
| + | end |
| + | end |
| + | if low or ask:find( "hh:mm:ss", 1, true ) then |
| + | if stamp then |
| + | r1 = string.format( "%s %s", r1, stamp ) |
| + | end |
| + | elseif ask:find( "hh:mm", 1, true ) and |
| + | stamp and |
| + | #stamp > 3 then |
| + | r1 = string.format( "%s H:i", r1 ) |
| + | end |
| + | if stamp then |
| + | if low or template.long then |
| + | local scheme |
| + | if template.long then |
| + | scheme = mw.language.getContentLanguage() |
| + | scheme = scheme.code |
| + | end |
| + | r2 = World.zones.formatter( assigned, scheme ) |
| + | end |
| + | end |
| + | end |
| + | if type ( assigned.bc ) == "boolean" then |
| + | local eras = World.era[ adapt.lang ] or World.era.en |
| + | local i |
| + | if not r2 then |
| + | r2 = "" |
| + | end |
| + | if assigned.bc then |
| + | i = 1 |
| + | else |
| + | i = 2 |
| + | end |
| + | r2 = string.format( "%s %s", r2, eras[ i ] ) |
| + | end |
| + | end |
| + | end |
| + | return r1, r2 |
| + | end -- World.templates.formatter() |
| + | |
| + | |
| + | |
| + | World.zones.formatter = function ( assigned, align ) |
| + | -- Retrieve time zone specification string |
| + | -- Parameter: |
| + | -- assigned -- table, with numbers etc. |
| + | -- .zone should be available |
| + | -- align -- string, format spec, or nil |
| + | -- nil, false, "+-" -- +/- 0000 |
| + | -- "Z" -- single letter |
| + | -- "UTC" -- "UTC", if appropriate |
| + | -- "de" -- try localized |
| + | -- Returns: |
| + | -- string |
| + | local r = "" |
| + | local move = 0 |
| + | if assigned.zone then |
| + | local s = type( assigned.zone ) |
| + | if s == "string" then |
| + | s = assigned.zone:upper() |
| + | if #s == 1 then |
| + | -- "YXWVUTSRQPONZABCDEFGHIKLM" |
| + | move = World.zones[ "!" ]:find( s, 1, true ) |
| + | if move then |
| + | move = ( move - 13 ) * 100 |
| + | assigned.zone = move |
| + | else |
| + | assigned.zone = false |
| + | end |
| + | else |
| + | local code = World.zones[ s ] |
| + | if not code then |
| + | local slang = ( assigned.lang or |
| + | World.slang ) |
| + | local tmp = World.zones[ slang ] |
| + | if tmp then |
| + | code = tmp[ s ] |
| + | end |
| + | if not code and |
| + | slang ~= "en" and |
| + | World.zones.en then |
| + | code = World.zones.en[ s ] |
| + | end |
| + | end |
| + | if code then |
| + | move = code |
| + | assigned.zone = move |
| + | end |
| + | end |
| + | elseif s == "number" then |
| + | move = assigned.zone |
| + | end |
| + | end |
| + | if move then |
| + | local spec = "+-" |
| + | if align then |
| + | if align == "Z" then |
| + | if move % 100 == 0 then |
| + | r = World.zones[ "!" ]:sub( move / 100 + 13, 1 ) |
| + | spec = false |
| + | end |
| + | elseif align ~= "+-" then |
| + | if move == 0 then |
| + | r = " UTC" |
| + | spec = false |
| + | else |
| + | local part = World.zones[ align ] |
| + | if part then |
| + | for k, v in pairs( part ) do |
| + | if v == move then |
| + | r = string.format( " (%s)", k ) |
| + | spec = false |
| + | break |
| + | end |
| + | end -- for k, v |
| + | end |
| + | end |
| + | end |
| + | end |
| + | if spec == "+-" then |
| + | if move < 0 then |
| + | spec = "%4.4d" |
| + | else |
| + | spec = "+%4.4d" |
| + | end |
| + | r = string.format( spec, move ) |
| + | r = string.format( "%s:%s", |
| + | r:sub( 1, 3), r:sub( 4 ) ) |
| + | end |
| + | end |
| + | return r |
| + | end -- World.zones.formatter() |
| + | |
| + | |
| + | |
| + | -- Export |
| + | local p = { } |
| + | |
| + | p.test = function ( args, alien ) |
| + | local slang = args.lang or alien |
| + | local obj = Meta.fiat( args[ 1 ], false, args.shift ) |
| + | local r |
| + | if type( obj ) == "table" then |
| + | local spec = args[ 2 ] |
| local opt | | local opt |
− | local spec = args[ 2 ]
| + | if spec then |
− | local slang = args[ 3 ]
| + | spec = mw.text.trim( spec ) |
− | if spec then | + | end |
− | spec = mw.text.trim( spec ) | + | if slang then |
− | end | + | opt = { lang = mw.text.trim( slang ) } |
− | if slang then | + | end |
− | opt = { lang = mw.text.trim( slang ) } | + | r = obj:format( spec, opt ) |
− | end | + | else |
− | r = obj:format( spec, opt ) | + | r = ( args.noerror or "0" ) |
− | else | + | if r == "0" then |
− | r = ( args.noerror or "0" ) | + | r = fault( "Format invalid" ) |
− | if r == "0" then | + | else |
− | r = fault( "Format nicht erkannt" ) | + | r = "" |
− | else | + | end |
− | r = "" | + | if args.errCat then |
− | end | + | local cats = mw.text.split( args.errCat, "%s*|%s*" ) |
− | end | + | for i = 1, #cats do |
− | return r | + | r = string.format( "%s[[Category:%s]]", r, cats[ i ] ) |
− | end -- p.test | + | end -- for i |
| + | end |
| + | end |
| + | return r |
| + | end -- p.test |
| + | |
| + | |
| + | |
| + | p.failsafe = function ( frame ) |
| + | local s = type( frame ) |
| + | local r, since |
| + | if s == "table" then |
| + | since = frame.args[ 1 ] |
| + | elseif s == "string" then |
| + | since = mw.text.trim( since ) |
| + | if since == "" then |
| + | since = false |
| + | end |
| + | end |
| + | return Prototypes.failsafe( false, since ) or "" |
| + | end -- p.failsafe |
| + | |
| + | |
| + | |
| + | p.format = function ( frame ) |
| + | -- 1 -- stamp |
| + | -- 2 -- spec |
| + | -- lang |
| + | -- shift |
| + | -- noerror |
| + | local l, r |
| + | local v = { frame.args[ 1 ], |
| + | frame.args[ 2 ], |
| + | shift = frame.args.shift, |
| + | noerror = frame.args.noerror, |
| + | errCat = frame.args.errCat } |
| + | if not v[ 1 ] or v[ 1 ] == "now" then |
| + | v[ 1 ] = frame:callParserFunction( "#timel", "c", v.shift ) |
| + | v.shift = false |
| + | end |
| + | Frame = frame |
| + | l, r = pcall( p.test, v, frame.args[ 3 ] or frame.args.lang ) |
| + | if not l then |
| + | r = fault( r ) |
| + | end |
| + | return r |
| + | end -- p.format |
| | | |
| | | |
| | | |
− | function p.format( frame )
| + | p.lt = function ( frame ) |
− | local l, r | + | return Templates.flow( frame, "lt" ) |
− | local v = { frame.args[ 1 ],
| + | end -- p.lt |
− | frame.args[ 2 ],
| + | p.le = function ( frame ) |
− | frame.args[ 3 ],
| + | return Templates.flow( frame, "le" ) |
− | noerror = frame.args.noerror }
| + | end -- p.le |
− | if not v[ 1 ] or v[ 1 ] == "now" then | + | p.eq = function ( frame ) |
− | v[ 1 ] = frame:callParserFunction( "#timel", "c" )
| + | return Templates.flow( frame, "eq" ) |
− | end
| + | end -- p.eq |
− | l, r = pcall( p.test, v )
| + | p.ne = function ( frame ) |
− | if not l then
| + | return Templates.flow( frame, "ne" ) |
− | r = fault( r )
| + | end -- p.ne |
− | end
| + | p.ge = function ( frame ) |
− | return r | + | return Templates.flow( frame, "ge" ) |
− | end -- p.format | + | end -- p.ge |
| + | p.gt = function ( frame ) |
| + | return Templates.flow( frame, "gt" ) |
| + | end -- p.gt |
| | | |
| | | |
| | | |
− | p.DateTime = function ( ... ) | + | p.DateTime = function () |
− | return DateTime( ... ) | + | return DateTime |
| end -- p.DateTime | | end -- p.DateTime |
| | | |
| return p | | return p |