Modul:DateTime: Unterschied zwischen den Versionen

Zur Navigation springen Zur Suche springen
(2015-03-16)
(2015-07-15)
Zeile 1: Zeile 1:
--[=[ 2015-03-16
local DateTime  -- Date and time utilities
Date and time utilities
local Serial    = "2015-07-15"
]=]
local Calc      = { }
 
 
 
-- local globals
local DateTime
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 17: Zeile 13:
local Nbsp      = mw.ustring.char( 160 )
local Nbsp      = mw.ustring.char( 160 )
local Tab        = mw.ustring.char( 9 )
local Tab        = mw.ustring.char( 9 )
local Frame
World.era = { en = { "BC", "AD" } }
World.era = { en = { "BC", "AD" } }
World.monthsAbbr = {  en = { n = 3 }  }
World.monthsAbbr = {  en = { n = 3 }  }
Zeile 116: Zeile 113:
     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 131: Zeile 128:




DateTime = function ( assign, alien )
local function frame()
     -- Create metatable (constructor)
    if not Frame then
        Frame = mw.getCurrentFrame()
    end
    return Frame
end -- frame()
 
 
 
local Signature = "__datetime"
local Metatable = {
    __index    = function ( self, access )
                    local r = self[ Signature ][ access ]
                    if r == nil then
                        if access == "serial" then
                            r = Serial
                        elseif access == "suite" then
                            r = "DateTime"
                        else
                            for k, v in pairs( Prototypes ) do
                                if k == access then
                                    r = Prototypes[ k ]
                                end
                            end -- for k, v
                        end
                    end
                    return r
                end,
    __newindex = function ( self, access, assign )
                    if type( access ) == "string" then
                        local data = self[ 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 )
                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
}
 
 
 
DateTime = function ( assign, alien, add )
     -- Create object (constructor)
     -- Parameter:
     -- Parameter:
     --    assign  -- string, with initial timestamp, or nil
     --    assign  -- string, with initial timestamp, or nil
Zeile 138: Zeile 196:
     --                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, as DateTime object
     --    table, as DateTime object
Zeile 143: Zeile 202:
     local r
     local r
     Private.foreign()
     Private.foreign()
     r = Private.factory( assign, alien )
     r = Private.factory( assign, alien, add )
     if type( r ) == "table" then
     if type( r ) == "table" then
        local meta = { }
         r = { [ Signature ] = r }
        local s    = "__datetime"
         setmetatable( r, Metatable )
        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
Zeile 190: Zeile 212:




Parser.digitsHeading = function ( analyse, alone, amount, add )
Calc.months = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
    -- 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:
     -- Parameter:
     --    analyse -- string to be scanned, starting with digit
     --    adjust -- table, with raw numbers
     --                digits only, else starting with exactly 4 digits
     local order  = { "mysec", "msec", "sec", "min", "hour",
     --     alone    -- true, if only digits
                    "dom", "month", "year" }
     --    amount  -- number of heading digits
    local ranges = { year  = { min = -999,
     --    add     -- table, to be extended
                              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
    for k, v in pairs( order ) do
        n = adjust[ v ]
        if n or move then
            range = ranges[ v ]
            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  n > 1  and
                      adjust.month >= 1  and
                      adjust.month <= 12 and
                      adjust.year > 1900 then
                        max = Calc.months[ adjust.month ]
                        if adjust.month == 2  and
                          ( adjust.year % 4 ~= 0  or
                            adjust.year % 400 == 0 ) then
                            max = 28
                        end
                        if n <= max then
                            max = false
                        end
                    end
                    if max then
                        m    = n % 30
                        move = ( n - m )  /  30
                        n    = 1 + m
                    end
                end
            end
            adjust[ v ] = n
        end
     end -- for k, v
end -- Calc.fair()
 
 
 
Calc.future = function ( add )
     -- Parse move interval
     -- Parameter:
     --    add   -- string, with GNU relative items
     -- Returns:
     -- Returns:
     --    table, extended if parsed
     --    table, with numeric components, or false
     --    false, if invalid text format
     local r, token
     local r = add
     local units = { year      = true,
     if alone then
                    month     = true,
        -- digits only
                    fortnight = { slot = "dom", mult = 14 },
        if amount <= 4 then
                    week      = { slot = "dom", mult = 7 },
            r.year = tonumber( analyse )
                    dom      = true,
        elseif n == 14 then
                    hour      = true,
            -- timestamp
                    min      = true,
            r.year  = tonumber( analyse:sub( 1, 4 ) )
                    sec      = true }
            r.month  = tonumber( analyse:sub( 5, 2 ) )
    local story = string.format( " %s ", add:lower() )
            r.dom    = tonumber( analyse:sub( 7, 2 ) )
                        :gsub( "%s+", " " )
            r.hour   = tonumber( analyse:sub( 9, 2 ) )
                        :gsub( " yesterday ", " -1 dom " )
            r.min    = tonumber( analyse:sub( 11, 2 ) )
                        :gsub( " tomorrow ",   " 1 dom " )
             r.sec    = tonumber( analyse:sub( 13, 2 ) )
                        :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
        r.year = tonumber( s )
end -- Calc.future()
        if sep == "-" then
 
            -- ISO
 
            s, sep, sx = sx:match( "^(%d%d)(-?)(.*)$" )
 
            if s then
Parser.digitsHeading = function ( analyse, alone, amount, add )
                r.month  = tonumber( s )
    -- String analysis, if digits only or at least 4 digits heading
                r.month2 = true
    -- Parameter:
                if sep == "-" then
    --    analyse  -- string to be scanned, starting with digit
                    s, sep, sx = sx:match( "^(%d%d?)([ T]?)(.*)$" )
    --                 digits only, else starting with exactly 4 digits
                    if s then
    --    alone    -- true, if only digits
                        r.dom = tonumber( s )
    --    amount  -- number of heading digits
                        if sep == "T" then
    --    add      -- table, to be extended
                            r.month2 = nil
    -- Returns:
                        else
    --     table, extended if parsed
                            r.dom2 = ( #s == 2 )
    --    false, if invalid text format
                        end
    local r = add
                        if sep then
    if alone then
                            r = Parser.time( sx, r, sep == "T" )
        -- digits only
                        end
        if amount <= 4 then
                    else
            r.year = tonumber( analyse )
                        r = false
        elseif n == 14 then
                    end
            -- timestamp
                elseif sx and sx ~= "" then
            r.year  = tonumber( analyse:sub(  1, 4 ) )
                    r = false
            r.month  = tonumber( analyse:sub(  5, 2 ) )
                end
            r.dom    = tonumber( analyse:sub7, 2 ) )
             else
             r.hour  = tonumber( analyse:sub( 9, 2 ) )
                r = false
             r.min    = tonumber( analyse:sub( 11, 2 ) )
            end
            r.sec    = tonumber( analyse:sub( 13, 2 ) )
        elseif sep:lower() == "w" then
             if sx then
                s = sx:match( "^(%d%d?)$" )
                if s then
                    r.week = tonumber( s )
                    if r.week < 1  or  r.week > 53 then
                        r = false
                    end
                else
                    r = false
                end
            else
                r = false
            end
         else
         else
             r = false
             r = false
         end
         end
     elseif amount == 8 then
     elseif amount == 4 then
        -- ISO compact
         local s, sep, sx = analyse:match( "^(%d+)([%-%.:Ww]?)(.*)$" )
         local s, sz = analyse:match( "^%d+T(%d+)([.+-]?%d*%a*)$" )
         r.year = tonumber( s )
         if s then
        if sep == "-" then
            local n = #s
            -- ISO
            if n == 2  or  n == 4  or  n == 6 then
            s, sep, sx = sx:match( "^(%d%d)(-?)(.*)$" )
                r.year = tonumber( analyse:sub(  1,  4 ) )
            if s then
                r.month = tonumber( analyse:sub(  5,  6 ) )
                r.month  = tonumber( s )
                r.dom  = tonumber( analyse:sub(  7,  8 ) )
                r.month2 = true
                r.hour  = tonumber( analyse:sub( 10, 11 ) )
                 if sep == "-" then
                if n > 2 then
                     s, sep, sx = sx:match( "^(%d%d?)([ T]?)(.*)$" )
                    r.min = tonumber( s:sub( 3, 4 ) )
                     if s then
                    if n == 6 then
                        r.dom = tonumber( s )
                        r.sec = tonumber( s:sub( 5, 6 ) )
                         if sep == "T" then
                    end
                             r.month2 = nil
                    n, s = sz:match( "^(%.%d+)([+-]?[%a%d]*)$" )
                    if n then
                        n      = n .. "00"
                        r.msec = tonumber( n:sub( 1, 3 ) )
                        sz    = s
                    end
                end
                 if sz ~= "" then
                     s, sz = sz:match( "^([+-]?)(%a*)$" )
                     if s == "" then
                         if sz:match( "^(%u)$" ) then
                             r.zone = sz
                         else
                         else
                             s = false
                             r.dom2 = ( #s == 2 )
                        end
                        if sep then
                            r = Parser.time( sx,  r,  sep == "T" )
                         end
                         end
                    elseif #s == 1 then
                        r.zone = s .. sz
                     else
                     else
                         s = false
                         r = false
                     end
                     end
                elseif sx and sx ~= "" then
                    r = false
                 end
                 end
             else
             else
                 s = false
                 r = false
             end
             end
        elseif sep:lower() == "w" then
            if sx then
                s = sx:match( "^(%d%d?)$" )
                if s then
                    r.week = tonumber( s )
                    if r.week < 1  or  r.week > 53 then
                        r = false
                    end
                else
                    r = false
                end
            else
                r = false
            end
        else
            r = false
        end
        if r then
            r.iso = true
         end
         end
    elseif amount == 8 then
        -- ISO compact
        local s, sz = analyse:match( "^%d+T(%d+)([.+-]?%d*%a*)$" )
         if s then
         if s then
             r = false
             local n = #s
        end
            if n == 2  or  n == 4  or  n == 6 then
    end
                r.year  = tonumber( analyse:sub( 1,  4 ) )
    return r
                r.month = tonumber( analyse:sub(  5,  6 ) )
end -- Parser.digitsHeading()
                r.dom   = tonumber( analyse:sub( 7, 8 ) )
 
                r.hour = tonumber( analyse:sub( 10, 11 ) )
 
                if n > 2 then
 
                    r.min = tonumber( s:sub( 3, 4 ) )
Parser.eraGermanEnglish = function ( analyse )
                    if n == 6 then
    -- String analysis, for German and English era
                        r.sec = tonumber( s:sub( 5, 6 ) )
    -- v. Chrv. u. Z.  n. Chr.  AD BC  A.D. B.C. B.C.E.
                    end
    -- Parameter:
                    n, s = sz:match( "^(%.%d+)([+-]?[%a%d]*)$" )
    --    analyse -- string
                    if n then
    -- Returns:
                        n      = n .. "00"
    --    1 -- table, with boolean era, if any
                        r.msec = tonumber( n:sub( 1, 3 ) )
    --    2 -- string, with era stripped off, if any
                        if #n >= 6 then
    local rO = { }
                            r.mysec = tonumber( n:sub( 4, 6 ) )
    local rS = analyse
                        end
    local s, switch = analyse:match( "^(.+) ([vn])%. ?Chr%.$" )
                        sz = s
    if switch then
                    end
        rS    = s
                end
        rO.bc = ( switch == "v" )
                if sz ~= "" then
    elseif analyse:find( " v%. ?u%. ?Z%.$" ) then
                    s, sz = sz:match( "^([+-]?)(%a*)$" )
        rS    = analyse:match( "^(.+) v%. ?u%. ?Z%.$" )
                    if s == "" then
        rO.bc = true
                        if sz:match( "^(%u)$" ) then
    elseif analyse:find( " B%.? ?C%.? ?E?%.?$" ) then
                            r.zone = sz
        rS    = analyse:match( "^(.+) B%.? ?C%.? ?E?%.?$" )
                        else
        rO.bc = true
                            s = false
    elseif analyse:find( "^A%.? ?D%.? " ) then
                        end
        rS    = analyse:match( "^A%.? ?D%.? (.*)$" )
                    elseif #s == 1 then
         rO.bc = false
                        r.zone = s .. sz
     end
                    else
     return rO, rS
                        s = false
end -- Parser.eraGermanEnglish()
                    end
                end
            else
                s = false
            end
        end
         if s then
            r = false
        end
     end
     return r
end -- Parser.digitsHeading()






Parser.european = function ( ahead, adhere, analyse, assign )
Parser.eraGermanEnglish = function ( analyse )
     -- String analysis, retrieve date style: DOM MONTH YEAR
     -- String analysis, for German and English era
    -- v. Chr.  v. u. Z.  n. Chr.  AD BC  A.D. B.C. B.C.E.
     -- Parameter:
     -- Parameter:
    --    ahead    -- string, with first digits, not more than 2
     --    analyse  -- string
    --    adhere  -- string, with first separator; not ":"
     --    analyse  -- string, remainder following adhere
    --    assign  -- table
     -- Returns:
     -- Returns:
     --    table, extended if parsed
     --    1  -- table, with boolean era, if any
     local r = assign
    --    2  -- string, with era stripped off, if any
     local s, s2, sx
    local rO = { }
     if adhere == "." or  adhere == ". " then
     local rS = analyse
         -- 23.12.2013
     local s, switch = analyse:match( "^(.+) ([vn])%. ?Chr%.$" )
         -- 23. Dezember 2013
     if switch then
         s, sx = analyse:match( "^(%d%d?)%.(.*)$" )
        rS    = s
         if s then
        rO.bc = ( switch == "v" )
            r = Parser.putDate( false, s, ahead, assign )
    elseif analyse:find( " v%. ?u%. ?Z%.$" ) then
            r = Parser.yearTime( sx, r )
         rS    = analyse:match( "^(.+) v%. ?u%. ?Z%.$" )
         else
         rO.bc = true
            s, sx = mw.ustring.match( analyse,
    elseif analyse:find( " B%.? ?C%.? ?E?%.?$" ) then
                                      "^ ?([%a&;]+%.?) ?(.*)$" )
         rS    = analyse:match( "^(.+) B%.? ?C%.? ?E?%.?$" )
            if s then
         rO.bc = true
                local n = Parser.monthNumber( s )
    elseif analyse:find( "^A%.? ?D%.? " ) then
                if n then
         rS    = analyse:match( "^A%.? ?D%.? (.*)$" )
                    r.month = n
        rO.bc = false
                    r.dom  = tonumber( ahead )
    end
                    r.dom2  = ( #ahead == 2 )
    return rO, rS
                    r      = Parser.yearTime( sx, r )
end -- Parser.eraGermanEnglish()
                else
 
                    r = false
 
                end
 
            else
Parser.european = function ( ahead, adhere, analyse, assign )
                r = false
    -- String analysis, retrieve date style: DOM MONTH YEAR
            end
    -- Parameter:
        end
    --    ahead   -- string, with first digits, not more than 2
     elseif adhere == " " then
    --    adhere  -- string, with first separator; not ":"
         -- 23 Dec 2013
    --    analyse  -- string, remainder following adhere
         s, sx = mw.ustring.match( analyse,
    --    assign  -- table
                                  "^([%a&;]+%.?) ?(.*)$" )
    -- Returns:
    --    table, extended if parsed
    local r = assign
    local s, s2, sx
     if adhere == "."  or  adhere == ". " then
         -- 23.12.2013
        -- 23. Dezember 2013
         s, sx = analyse:match( "^(%d%d?)%.(.*)$" )
         if s then
         if s then
             local n = Parser.monthNumber( s )
             r = Parser.putDate( false, s, ahead, assign )
             if n then
             r = Parser.yearTime( sx, r )
                r.month = n
                r.dom  = tonumber( ahead )
                r.dom2  = ( #ahead == 2 )
                r      = Parser.yearTime( sx, r )
            else
                r = false
            end
         else
         else
             r = false
             s, sx = mw.ustring.match( analyse,
                                      "^ ?([%a&;]+%.?) ?(.*)$" )
            if s then
                local n = Parser.monthNumber( s )
                if n then
                    r.month = n
                    r.dom  = tonumber( ahead )
                    r.dom2  = ( #ahead == 2 )
                    r      = Parser.yearTime( sx, r )
                else
                    r = false
                end
            else
                r = false
            end
         end
         end
     else
    elseif adhere == " " then
         r = false
        -- 23 Dec 2013
     end
        s, sx = mw.ustring.match( analyse,
     return r
                                  "^([%a&;]+%.?) ?(.*)$" )
end -- Parser.european()
        if s then
 
            local n = Parser.monthNumber( s )
            if n then
                r.month = n
                r.dom  = tonumber( ahead )
                r.dom2  = ( #ahead == 2 )
                r      = Parser.yearTime( sx, r )
            else
                r = false
            end
        else
            r = false
        end
     else
         r = false
     end
     return r
end -- Parser.european()
 




Zeile 461: Zeile 651:
         rO = false
         rO = false
     end
     end
     if not rO then
     if rO then
        rO.iso = true
    else
         rS = false
         rS = false
     end
     end
Zeile 640: Zeile 832:
                 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.057:




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.064:
     --                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.090:
             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.102:




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      -- string, with interval (PHP strtotime), or nil
     -- Returns:
     -- Returns:
     --    table, if parsed
     --    table, if parsed
Zeile 940: Zeile 1.143:
                     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
                 end
                 end
Zeile 954: Zeile 1.159:




Private.foreign = function ()
Private.flow = function ( at1, at2 )
     -- Retrieve localization submodule
    -- Compare two objects
     if not World.localization then
    -- 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 order = { "year", "month", "week", "dom",
                        "hour", "min", "sec", "msec", "mysec" }
        local life  = false
        local s, v1, v2
        for i = 1, 9 do
            s  = 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
                else
                    r = nil
                end
                if r ~= 0 then
                    if at1.bc and r then
                        r = r * -1
                    end
                    break    -- for i
                end
                life = true
            end
        end -- for i
    end
    return r
end -- Private.flow()
 
 
 
Private.foreign = function ()
     -- Retrieve localization submodule
     if not World.localization then
         local l, d = pcall( mw.loadData, "Module:DateTime/local" )
         local l, d = pcall( mw.loadData, "Module:DateTime/local" )
         if l then
         if l then
Zeile 980: Zeile 1.239:




Prototypes.fair = function ( self, access, assign )
Private.future = function ( add )
     -- Check formal validity of table
     -- Normalize move interval
     -- Parameter:
     -- Parameter:
     --    self    -- table, to be checked
     --    add  -- string or number, to be added
    --    access  -- string or nil, single item to be checked
    --    assign  -- single access value to be checked
     -- Returns:
     -- Returns:
     --    true, if valid;  false, if not
    --    string, with shift, or false/nil
     local r = ( type( self ) == "table" )
    local r
    if add then
        local s = type( add )
        if s == "string"  and  add:match( "^%s*[+-]?%d+%.?%d*%s*$" ) then
            r = tonumber( add )
            s = "number"
        else
            r = add
        end
        if s == "number" then
            if r == 0 then
                r = false
            else
                r = string.format( "%d second", r )
            end
        elseif s ~= "string" then
            r = false
        end
        if r then
            r = Calc.future( r )
        end
    end
    return r
end -- Private.future()
 
 
 
Prototypes.fair = function ( self, access, assign )
    -- Check formal validity of table
    -- Parameter:
    --    self    -- table, to be checked
    --    access  -- string or nil, single item to be checked
    --    assign  -- single access value to be checked
    -- Returns:
     --    true, if valid;  false, if not
     local r = ( type( self ) == "table" )
     if r then
     if r then
         local defs = { year  = { max = MaxYear },
         local defs = { year  = { max = MaxYear },
                       month = { min =  1,
                       month = { min =  1,
                                 max = 12 },
                                 max = 12 },
                      week  = { min =  1,
                                max = 53 },
                       dom  = { min =  1,
                       dom  = { min =  1,
                                 max = 31 },
                                 max = 31 },
Zeile 998: Zeile 1.292:
                       min  = { max = 59 },
                       min  = { max = 59 },
                       sec  = { max = 61 },
                       sec  = { max = 61 },
                       msec  = { max = 1000 }
                       msec  = { max = 999 },
                      mysec = { max = 999 }
         }
         }
        local months = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
         local fNum =
         local fNum =
             function ( k, v )
             function ( k, v )
Zeile 1.044: Zeile 1.338:
                         end
                         end
                         if m then
                         if m then
                             ret = ( d <= months[ m ] )
                             ret = ( d <= Calc.months[ m ] )
                             if ret then
                             if ret then
                                 local y
                                 local y
Zeile 1.105: Zeile 1.399:
             end
             end
         else
         else
             local order = { "bc", "year", "month", "dom",
             local order = { "bc", "year", "month", "week", "dom",
                             "hour", "min", "sec", "msec" }
                             "hour", "min", "sec", "msec", "mysec" }
             local life = false
             local life = false
             local leak = false
             local leak = false
             local s, v
             local s, v
             for i = 1, 8 do
             for i = 1, 10 do
                 s = order[ i ]
                 s = order[ i ]
                 v = self[ s ]
                 v = self[ s ]
Zeile 1.121: Zeile 1.415:
                         if not fNum( s, v ) then
                         if not fNum( s, v ) then
                             r = false
                             r = false
                             break
                             break   -- for i
                         end
                         end
                         life = true
                         life = true
                         leak = true
                         leak = true
                     end
                     end
                 else
                 elseif i == 3 then
                    if not self.week then
                        life = false
                    end
                elseif i ~= 4 then
                     life = false
                     life = false
                 end
                 end
             end -- for i
             end -- for i
            if self.week  and  ( self.month or self.dom ) then
                r = false
            end
         end
         end
     end
     end
Zeile 1.191: Zeile 1.492:




Prototypes.format = function ( self, ask, adapt )
Prototypes.flow = function ( self, another, assert )
     -- Format object as string
    -- 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 r
    if type( self ) == "table" then
        local d2
        if type( another ) == "table" then
            d2 = another
        else
            d2 = DateTime( another )
        end
        if type( d2 ) == "table" then
            r = Private.flow( self, d2 )
            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
        end
    end
    return r
end -- Prototypes.flow()
 
 
 
Prototypes.format = function ( self, ask, adapt )
     -- Format object as string
     -- Parameter:
     -- Parameter:
     --    self  -- table, with numbers etc.
     --    self  -- table, with numbers etc.
Zeile 1.198: Zeile 1.556:
     --    adapt  -- table, with options, or nil
     --    adapt  -- table, with options, or nil
     --              .lang    -- string, with particular language code
     --              .lang    -- string, with particular language code
    --              .london  -- true: UTC output; default: local
     --              .lonely  -- true: permit lonely hour
     --              .lonely  -- true: permit lonely hour
     -- Returns:
     -- Returns:
Zeile 1.249: Zeile 1.608:
                                               stamp, self.sec )
                                               stamp, self.sec )
                         if self.msec then
                         if self.msec then
                             stamp = string.format( "%s.%d",
                             stamp = string.format( "%s.%03d",
                                                   stamp, self.msec )
                                                   stamp, self.msec )
                            if self.mysec then
                                stamp = string.format( "%s%03d",
                                                      stamp,
                                                      self.mysec )
                            end
                         end
                         end
                     end
                     end
Zeile 1.349: Zeile 1.713:




World.templates.formatter = function ( assigned, ask, adapt )
Prototypes.future = function ( self, add )
     -- Retrieve format specification string
     -- Relative move by interval
     -- Parameter:
     -- Parameter:
     --    assigned -- table, with numbers etc.
     --    self -- table, to be used as base
     --    ask      -- string, format spec, or nil
     --    add  -- string or number, to be added
    --    adapt    -- table, with options
    --                  .lang    -- string, with particular language code
    --                  .lonely  -- true: permit lonely hour
     -- Returns:
     -- Returns:
     --    1  -- string
     --    table, with shift
     --     2  -- string or nil; append suffix (zone)
     local r, raw, rel, shift
     local r1, r2
     if type( self ) == "table" then
     if not ask  or  ask == "" then
        r     = self
         r1 = "c"
        shift = add       
     else
     elseif type( add ) == "table" then
         local template = World.templates[ ask ]
         r    = add
         r1 = ask
        shift = self       
        if not template then
    end
             local slang = ( adapt.lang or assigned.lang or World.slang )
     if r then
            local tmp  = World.templates[ slang ]
         raw = r[ Signature ]
            if tmp then
         rel = Private.future( shift )
                template = tmp[ ask ]
    end
             end
    if raw and rel then
        for k, v in pairs( rel ) do
             raw[ k ] = ( raw[ k ] or 0 )  +  v
        end -- for k, v
        Calc.fair( raw )
    end
    return r
end -- Prototypes.future()
 
 
 
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 type( template ) == "table" then
    end
             local low = ( ask == "ISO" or ask == "ISO-T" )
    if s2 then
            r1 = template.spec
         s2 = mw.text.trim( s2 )
            if assigned.year then
        if s2 == "" then
                if not assigned.dom then
             s2 = false
                    r1 = r1:gsub( "[ .%-]?[dDjlNwz][ .,%-]*", "" )
        end
                          :gsub( "^&#160;", "" )
    end
                    if not assigned.month then
    if s1 or s2 then
                        r1 = r1:gsub( "[ .%-]?[FmMnt][ .%-]*", "" )
        local l
                    end
        Frame = frame
                end
        l, r = pcall( Prototypes.flow,
             else
                      DateTime( s1 ), s2, action )
                r1 = r1:gsub( " ?[yY] ?", "" )
        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"
    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
        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
                 if not assigned.dom then
                     r1 = r1:gsub( "[ .]?[dDjlNwz][ .,%-]*", "" )
                    r1 = r1:gsub( "[ .%-]?[dDjlNwz][ .,%-]*", "" )
                             :gsub( "^&#160;", "" )
                          :gsub( "^&#160;", "" )
                    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( "^&#160;", "" )
                 end
                 end
             end
             end
Zeile 1.402: Zeile 1.831:
                             stamp = "H:i:s"
                             stamp = "H:i:s"
                             if assigned.msec then
                             if assigned.msec then
                                 stamp = string.format( "%s.%d",
                                 stamp = string.format( "%s.%03d",
                                                       stamp,
                                                       stamp,
                                                       assigned.msec )
                                                       assigned.msec )
                                if assigned.mysec then
                                    stamp = string.format( "%s.%03d",
                                                          stamp,
                                                        assigned.mysec )
                                end
                             end
                             end
                         end
                         end
Zeile 1.539: Zeile 1.973:


function p.test( args )
function p.test( args )
    local slang = args[ 3 ]
    local obj  = DateTime( args[ 1 ],  slang or "de",  args[ 4 ] )
     local r
     local r
    local obj = DateTime( args[ 1 ], "de" )
     if type( obj ) == "table" then
     if type( obj ) == "table" then
        local spec  = args[ 2 ]
         local opt
         local opt
        local spec  = args[ 2 ]
        local slang = args[ 3 ]
         if spec then
         if spec then
             spec = mw.text.trim( spec )
             spec = mw.text.trim( spec )
         end
         end
         if slang then
         if slang then
             opt = { lang = mw.text.trim( slang ) }
             opt = { lang = mw.text.trim( slang ) }
         end
         end
         r = obj:format( spec, opt )
         r = obj:format( spec, opt )
     else
     else
         r = ( args.noerror or "0" )
         r = ( args.noerror or "0" )
         if r == "0" then
         if r == "0" then
             r = fault( "Format nicht erkannt" )
             r = fault( "Format nicht erkannt" )
         else
         else
             r = ""
             r = ""
         end
         end
     end
     end
     return r
     return r
end -- p.test
end -- p.test
 
 
 
function p.failsafe( frame )
    local since = frame.args[ 1 ]
    local r
    if since then
        since = mw.text.trim( since )
        if since == "" then
          since = false
        end
    end
    if since then
        if since > Serial then
            r = ""
        else
            r = Serial
        end
    else
        r = Serial
    end
    return r
end -- p.failsafe
 
 
 
function p.format( frame )
    --    1  -- stamp
    --    2  -- spec
    --    3  -- slang
    --    4  -- shift
    local l, r
    local v = { frame.args[ 1 ],
                frame.args[ 2 ],
                frame.args[ 3 ],
                frame.args[ 4 ],
                noerror = frame.args.noerror }
    if not v[ 1 ]  or  v[ 1 ] == "now" then
        v[ 1 ] = frame:callParserFunction( "#timel", "c", v[ 4 ] )
        v[ 4 ] = false
    end
    Frame = frame
    l, r = pcall( p.test, v )
    if not l then
        r = fault( r )
    end
    return r
end -- p.format






function p.format( frame )
function p.lt( frame )
     local l, r
     return Templates.flow( frame, "lt" )
    local v = { frame.args[ 1 ],
end -- p.lt
                frame.args[ 2 ],
function p.le( 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
function p.eq( frame )
        v[ 1 ] = frame:callParserFunction( "#timel", "c" )
     return Templates.flow( frame, "eq" )
    end
end -- p.eq
    l, r = pcall( p.test, v )
function p.ne( frame )
     if not l then
    return Templates.flow( frame, "ne" )
        r = fault( r )
end -- p.ne
    end
function p.ge( frame )
     return r
     return Templates.flow( frame, "ge" )
end -- p.format
end -- p.ge
function p.gt( frame )
     return Templates.flow( frame, "gt" )
end -- p.gt