Modul:DateTime: Unterschied zwischen den Versionen
Zur Navigation springen
Zur Suche springen
Diese Vorlage(n) wurde(n) fast unverändert von der deutschsprachigen Wikipedia übernommen. Es wurden nur geringfügige technische, stilistische und organisatorische Anpassungen ans ÖsterreichWiki durchgeführt.
(update) |
(updates) |
||
Zeile 1: | Zeile 1: | ||
--[=[ 2014-02- | --[=[ 2014-02-04 | ||
Date and time utilities | Date and time utilities | ||
]=] | ]=] | ||
Zeile 8: | Zeile 8: | ||
local DateTime | local DateTime | ||
local Parser = { } | local Parser = { } | ||
local Private = { } | |||
local Prototypes = { } | local Prototypes = { } | ||
local World = { slang = "en", | local World = { slang = "en", | ||
Zeile 120: | Zeile 121: | ||
-- string or false, if failed | -- string or false, if failed | ||
local r | local r | ||
Private.foreign() | |||
r = | r = Private.factory( assign, alien ) | ||
if type( r ) == "table" then | if type( r ) == "table" then | ||
local meta = { } | local meta = { } | ||
Zeile 146: | Zeile 147: | ||
end | end | ||
r = { [ s ] = r } | r = { [ s ] = r } | ||
r.fair = function ( | r.fair = function ( ... ) | ||
return Prototypes.fair( | return Prototypes.fair( ... ) | ||
end | end | ||
r.format = function ( ... ) | r.format = function ( ... ) | ||
return Prototypes.format( | return Prototypes.format( ... ) | ||
end | end | ||
setmetatable( r, meta ) | setmetatable( r, meta ) | ||
Zeile 742: | Zeile 743: | ||
Private.factory = function ( assign, alien ) | |||
-- Create DateTime table (constructor) | -- Create DateTime table (constructor) | ||
-- Parameter: | -- Parameter: | ||
Zeile 763: | Zeile 764: | ||
"c" ) | "c" ) | ||
end | end | ||
l, r = pcall( | l, r = pcall( Private.fetch, stamp, slang ) | ||
end | end | ||
if l and type( r ) == "table" then | if l and type( r ) == "table" then | ||
Zeile 771: | Zeile 772: | ||
end | end | ||
return r | return r | ||
end -- Prototypes. | end -- Private.factory() | ||
Private.fetch = function ( analyse, alien ) | |||
-- Retrieve object from string | |||
-- Parameter: | |||
-- analyse -- string to be interpreted | |||
-- alien -- string with language code, or nil | |||
-- Returns: | |||
-- table, if parsed | |||
-- false, if invalid text format | |||
-- string, if serious error (args) | |||
local r | |||
if type( analyse ) == "string" then | |||
r = analyse:gsub( " ", " " ) | |||
:gsub( " ", " " ) | |||
:gsub( " ", " " ) | |||
:gsub( Nbsp, " " ) | |||
:gsub( Tab, " " ) | |||
:gsub( " +", " " ) | |||
r = mw.text.trim( r ) | |||
if r == "" then | |||
r = { } | |||
else | |||
local slang = ( alien or "" ) | |||
if slang == "" then | |||
slang = "en" | |||
else | |||
local s = slang:match( "^(%a+)%-" ) | |||
if s then | |||
slang = s | |||
end | |||
end | |||
slang = slang:lower() | |||
if slang == "en" or slang == "de" then | |||
local l | |||
l, r = pcall( Parser.GermanEnglish, r ) | |||
if l and r then | |||
if not Prototypes.fair( r ) then | |||
r = false | |||
end | |||
end | |||
else | |||
r = "unknown language" | |||
end | |||
end | |||
else | |||
r = "bad type" | |||
end | |||
return r | |||
end -- Private.fetch() | |||
Private.foreign = function () | |||
-- Retrieve localization submodule | |||
if not World.localization then | |||
local l, d = pcall( mw.loadData, "Module:DateTime/local" ) | |||
if l then | |||
if d.slang then | |||
World.slang = d.slang | |||
end | |||
for k, v in pairs( d ) do | |||
if World[ k ].en then | |||
local part = World[ k ] | |||
for subk, subv in pairs( v ) do | |||
part[ subk ] = subv | |||
end -- for k, v | |||
else | |||
World[ k ] = v | |||
end | |||
end -- for k, v | |||
end | |||
World.localization = true | |||
end | |||
end -- Private.foreign() | |||
Zeile 915: | Zeile 992: | ||
return r | return r | ||
end -- Prototypes.fair() | end -- Prototypes.fair() | ||
Zeile 1.245: | Zeile 1.246: | ||
local p = { } | local p = { } | ||
function p. | function p.test( args ) | ||
local r = ( | local r | ||
if | local o = DateTime( args[ 1 ], "de" ) | ||
if type( o ) == "table" then | |||
local spec = args[ 2 ] | |||
local slang = args[ 3 ] | |||
local spec = | |||
local slang = | |||
if spec then | if spec then | ||
spec = trim( spec ) | spec = mw.text.trim( spec ) | ||
end | end | ||
if slang then | if slang then | ||
slang = trim( slang ) | slang = mw.text.trim( slang ) | ||
end | end | ||
r = o:format( spec, slang ) | |||
else | |||
r = ( args.noerror or "0" ) | |||
if r == "0" then | |||
r = fault( "Format nicht erkannt" ) | |||
else | else | ||
r = "" | |||
end | end | ||
end | |||
r = | return r | ||
end -- test | |||
function p.format( frame ) | |||
local l, r | |||
local v = { frame.args[ 1 ], | |||
frame.args[ 2 ], | |||
frame.args[ 3 ], | |||
noerror = frame.args.noerror } | |||
if not v[ 1 ] or v[ 1 ] == "now" then | |||
v[ 1 ] = frame:callParserFunction( "#timel", "c" ) | |||
end | |||
l, r = pcall( p.test, v ) | |||
if not l then | |||
r = fault( r ) | |||
end | end | ||
return r | return r | ||
end -- format | end -- format | ||
p.DateTime = function ( ... ) | p.DateTime = function ( ... ) |
Version vom 5. Februar 2014, 08:54 Uhr
--[=[ 2014-02-04
Date and time utilities
]=]
-- local globals
local DateTime
local Parser = { }
local Private = { }
local Prototypes = { }
local World = { slang = "en",
monthsLong = { },
monthsParse = { } }
local Nbsp = mw.ustring.char( 160 )
local Tab = mw.ustring.char( 9 )
World.era = { en = { "BC", "AD" } }
World.monthsAbbr = { en = { n = 3 } }
World.monthsLong.en = { "January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
}
World.monthsParse.en = { [ "Apr" ] = 4,
[ "Aug" ] = 8,
[ "Dec" ] = 12,
[ "Feb" ] = 2,
[ "Jan" ] = 1,
[ "Jul" ] = 7,
[ "Jun" ] = 6,
[ "Mar" ] = 3,
[ "May" ] = 5,
[ "Nov" ] = 11,
[ "Oct" ] = 10,
[ "Sep" ] = 9
}
World.templates = { [ "ISO" ] =
{ spec = "Y-m-d",
lift = true },
[ "ISO-T" ] =
{ spec = "c" },
[ "timestamp" ] =
{ spec = "YmdHis" }
}
World.templates.en = { }
World.zones = {
[ "!" ] = "YXWVUTSRQPONZABCDEFGHIKLM",
UTC = 0,
GMT = 0 -- Greenwich Mean Time
}
World.zones.en = {
BST = 100, -- British Summer Time
IST = 100, -- Irish Summer Time
WET = 0, -- Western Europe Time
WEST = 100, -- Western Europe Summer Time
CET = 100, -- Central Europe Time
CEST = 200, -- Central Europe Summer Time
EET = 200, -- Eastern Europe Time
EEST = 300, -- Eastern Europe Summer Time
MSK = 300, -- Moscow Time
MSD = 400, -- Moscow Summer Time
NST = -330, -- Newfoundland Standard Time
NDT = -230, -- Newfoundland Daylight Time
AST = -400, -- Atlantic Standard Time
ADT = -300, -- Atlantic Daylight Time
EST = -500, -- Eastern Standard Time
EDT = -400, -- Eastern Daylight Saving Time
CST = -600, -- Central Standard Time
CDT = -500, -- Central Daylight Saving Time
MST = -700, -- Mountain Standard Time
MDT = -600, -- Mountain Daylight Saving Time
PST = -800, -- Pacific Standard Time
PDT = -700, -- Pacific Daylight Saving Time
AKST = -900, -- Alaska Standard Time
AKDT = -800, -- Alaska Standard Daylight Saving Time
HST = -1000 -- Hawaiian Standard Time
}
local function capitalize( a )
-- Upcase first character, downcase anything else
-- Parameter:
-- a -- string
-- Returns:
-- string
return mw.ustring.upper( mw.ustring.sub( a, 1, 1 ) )
.. mw.ustring.lower( mw.ustring.sub( a, 2 ) )
end -- fault()
local function fault( a )
-- Format error message by class=error
-- Parameter:
-- a -- string, error message
-- Returns:
-- string, HTML span
return string.format( "<span class=\"error\">%s</span>", a )
end -- fault()
DateTime = function ( assign, alien )
-- Create metatable (constructor)
-- Parameter:
-- assign -- string, with initial timestamp, or nil
-- nil -- now
-- false -- empty object
-- alien -- string, with language code, or nil
-- Returns:
-- table, as DateTime object
-- string or false, if failed
local r
Private.foreign()
r = Private.factory( assign, alien )
if type( r ) == "table" then
local meta = { }
local s = "__datetime"
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.format = function ( ... )
return Prototypes.format( ... )
end
setmetatable( r, meta )
end
return r
end -- DateTime()
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 n == 14 then
-- timestamp
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 ) )
r.min = tonumber( analyse:sub( 11, 2 ) )
r.sec = tonumber( analyse:sub( 13, 2 ) )
else
r = false
end
elseif amount == 4 then
local s, sep, sx = analyse:match( "^(%d+)([%-%.:w]?)(.*)$" )
r.year = tonumber( s )
if sep == "-" then
-- ISO
s, sep, sx = sx:match( "^(%d%d)(-?)(.*)$" )
if s then
r.month = tonumber( s )
r.month2 = true
if sep == "-" then
s, sep, sx = sx:match( "^(%d%d?)([ T]?)(.*)$" )
if s then
r.dom = tonumber( s )
if sep == "T" then
r.month2 = nil
else
r.dom2 = ( #s == 2 )
end
if sep then
r = Parser.time( sx, r, sep == "T" )
end
else
r = false
end
elseif sx and sx ~= "" then
r = false
end
else
r = false
end
elseif sep:lower() == "w" then
if s then
s = s:match( "^(%d%d?)$" )
if s then
r.week = tonumber( s )
else
r = false
end
else
r = false
end
else
r = false
end
elseif amount == 8 then
-- ISO compact
local s, sz = analyse:match( "^%d+T(%d+)([.+-]?%d*%a*)$" )
if s then
local n = #s
if n == 2 or n == 4 or n == 6 then
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( 10, 11 ) )
if n > 2 then
r.min = tonumber( s:sub( 3, 4 ) )
if n == 6 then
r.sec = tonumber( s:sub( 5, 6 ) )
end
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
s = false
end
elseif #s == 1 then
r.zone = s .. sz
else
s = false
end
end
else
s = false
end
end
if s then
r = false
end
end
return r
end -- Parser.digitsHeading()
Parser.eraGermanEnglish = function ( analyse )
-- String analysis, for German and English era
-- v. Chr. v. u. Z. n. Chr. AD BC A.D. B.C. B.C.E.
-- Parameter:
-- analyse -- string
-- Returns:
-- 1 -- table, with boolean era, if any
-- 2 -- string, with era stripped off, if any
local rO = { }
local rS = analyse
local s, switch = analyse:match( "^(.+) ([vn])%. ?Chr%.$" )
if switch then
rS = s
rO.bc = ( switch == "v" )
elseif analyse:find( " v%. ?u%. ?Z%.$" ) then
rS = analyse:match( "^(.+) v%. ?u%. ?Z%.$" )
rO.bc = true
elseif analyse:find( " B%.? ?C%.? ?E?%.?$" ) then
rS = analyse:match( "^(.+) B%.? ?C%.? ?E?%.?$" )
rO.bc = true
elseif analyse:find( "^A%.? ?D%.? " ) then
rS = analyse:match( "^A%.? ?D%.? (.*)$" )
rO.bc = false
end
return rO, rS
end -- Parser.eraGermanEnglish()
Parser.european = function ( ahead, adhere, analyse, assign )
-- String analysis, retrieve date style: DOM MONTH YEAR
-- Parameter:
-- ahead -- string, with first digits, not more than 2
-- adhere -- string, with first separator; not ":"
-- analyse -- string, remainder following adhere
-- assign -- table
-- 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
r = Parser.putDate( false, s, ahead, assign )
r = Parser.yearTime( sx, r )
else
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
elseif adhere == " " then
-- 23 Dec 2013
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
else
r = false
end
return r
end -- Parser.european()
Parser.monthHeading = function ( analyse, assign )
-- String analysis, retrieve month heading date (US only)
-- Parameter:
-- analyse -- string, with heading word
-- assign -- table
-- Returns:
-- 1 -- table, extended if parsed
-- 2 -- stripped string, or false, if invalid text format
local rO = assign
local rS = analyse
local s, sep = mw.ustring.match( analyse, "^([%a&;]+%.?)([^%a%.]?)" )
if s then
-- might begin with month name "December 23, 2013"
local n = Parser.monthNumber( s )
if n then
rO.month = n
if sep == "" then
rS = ""
else
local s2, s3
n = mw.ustring.len( s ) + 1
s = mw.ustring.sub( analyse, n )
s2 = s:match( "^ (%d%d%d?%d?)$" )
if s2 then
rO.year = tonumber( s2 )
rS = ""
else
s2, s3, rS = s:match( "^ (%d+), (%d+)( ?.*)$" )
if s2 and s3 then
n = #s2
if n <= 2 and #s3 == 4 then
rO.dom = tonumber( n )
rO.year = tonumber( s3 )
rO.dom2 = ( n == 2 )
else
rO = false
end
else
rO = false
end
end
end
else
rO = false
end
else
rO = false
end
if not rO then
rS = false
end
return rO, rS
end -- Parser.monthHeading()
Parser.monthNumber = function ( analyse )
-- String analysis, retrieve month number
-- Parameter:
-- analyse -- string, with month name including any period
-- Returns:
-- number, 1...12 if found
-- false or nil, if not detected
local r = false
local s = mw.ustring.match( analyse, "^([%a&;]+)%.?$" )
if s then
local given
s = capitalize( s )
for k, v in pairs( World.monthsLong ) do
given = World.monthsParse[ k ]
if given then
r = given[ s ]
end
if not r then
given = World.monthsLong[ k ]
for i = 1, 12 do
if given[ i ] == s then
r = i
break
end
end -- for i
end
if r then
break
end
end -- for k, v
end
return r
end -- Parser.monthNumber()
Parser.putDate = function ( aYear, aMonth, aDom, assign )
-- Store date strings
-- Parameter:
-- aYear -- string, with year, or false
-- aMonth -- string, with numeric month
-- aDom -- string, with day of month
-- assign -- table
-- Returns:
-- table, extended
local r = assign
if aYear then
r.year = tonumber( aYear )
end
r.month = tonumber( aMonth )
r.dom = tonumber( aDom )
r.month2 = ( #aMonth == 2 )
r.dom2 = ( #aDom == 2 )
return r
end -- Parser.putDate()
Parser.time = function ( analyse, assign, adjusted )
-- String analysis, retrieve time components
-- Parameter:
-- analyse -- string, with time part
-- assign -- table
-- adjusted -- true: fixed length of 2 digits expected
-- Returns:
-- table, extended if parsed
-- false, if invalid text format
local r = assign
if analyse ~= "" then
local s, sx = analyse:match( "^(%d+)(:?.*)$" )
if s then
local n = #s
if n <= 2 then
r.hour = tonumber( s )
if not adjusted then
r.hour2 = ( n == 2 )
end
else
sx = false
r = false
end
if sx then
s, sx = sx:match( "^:(%d+)(:?(.*))$" )
if s then
if #s == 2 then
r.min = tonumber( s )
else
sx = false
r = false
end
if sx then
local sep
local scan = "^([:,] ?)(%d+)(.*)$"
sep, s, sx = sx:match( scan )
if sep == ":" then
if #s == 2 then
r.sec = tonumber( s )
end
elseif sep == ", " then
r = Parser.wikiDate( s .. sx, r )
sx = false
else
r = false
end
end
else
r = false
end
end
if sx and sx ~= "" then
s = sx:match( "^%.(%d+)$" )
if s then
r.msec = tonumber( s )
else
r = false
end
end
else
r = false
end
end
return r
end -- Parser.time()
Parser.wikiDate = function ( analyse, assign )
-- String analysis, for date after wiki ~~~~~ signature time
-- dmy 10:28, 30. Dez. 2013
-- ymd 10:28, 2013 Dez. 30
-- Parameter:
-- analyse -- string
-- assign -- table
-- Returns:
-- table, extended if parsed
-- false, if invalid text format
local r
local s = analyse:match( "^(2%d%d%d) " )
local sx
if s then
-- ymd "10:28, 2013 Dez. 30"
local n = false
r = assign
r.year = tonumber( s )
s = analyse:sub( 6 )
s, sx = mw.ustring.match( analyse:sub( 6 ),
"^([%a&;]+)%.? (%d%d?)$" )
if s then
n = Parser.monthNumber( s )
if n then
r.month = n
end
end
if n then
r.dom = tonumber( sx )
r.dom2 = ( #sx == 2 )
else
r = false
end
else
-- dmy "10:28, 30. Dez. 2013"
local sep
s, sep, sx = analyse:match( "^(%d%d?)(%.? ?)(%a.+)$" )
if s then
r = Parser.european( s, sep, sx, assign )
else
r = false
end
end
return r
end -- Parser.wikiDate()
Parser.yearTime = function ( analyse, assign )
-- String analysis, for possible year and possible time
-- Parameter:
-- analyse -- string, starting with year
-- assign -- table
-- Returns:
-- table, extended if parsed
-- false, if invalid text format
local r = assign
local n = #analyse
if n > 0 then
local s, sx
if n == 4 then
if analyse:match( "^%d%d%d%d$" ) then
s = analyse
sx = false
end
else
s = analyse:match( "^(%d%d%d%d)[ ,]" )
if s then
sx = analyse:sub( 5 )
end
end
if s then
r.year = tonumber( s )
if sx then
s, sx = sx:match( "^(,? ?)(%d.*)$" )
if #s >= 1 then
r = Parser.time( sx, r )
end
end
else
r = false
end
end
return r
end -- Parser.yearTime()
Parser.zone = function ( analyse, assign )
-- String analysis, for time zone
-- +/-nn +/-nnnn (AAAa)
-- Parameter:
-- analyse -- string
-- assign -- table
-- Returns:
-- 1 -- table, with number or string zone, if any, or false
-- 2 -- string, with zone stripped off, if any
local rO = assign
local rS = analyse
local s, sign, shift, sub
s = "^(.+)([+-])([01]%d):?(%d?%d?)$"
s, sign, shift, sub = analyse:match( s )
if sign then
if s:find( ":%d%d *$" ) then
if sub then
if #sub == 2 then
rO.zone = tonumber( shift .. sub )
else
rO = false
end
else
rO.zone = tonumber( shift ) * 100
end
if rO then
if sign == "-" then
rO.zone = - rO.zone
end
rS = mw.text.trim( s )
end
end
elseif analyse:find( "%(.*%)$" ) then
s, shift = analyse:match( "^(.+)%((%a%a%a%a?)%)$" )
if shift then
rO.zone = shift:upper()
rS = mw.text.trim( s )
else
rO = false
end
else
s, shift = analyse:match( "^(.+%d) ?(%a+)$" )
if shift then
local n = #shift
if n == 1 then
rO.zone = shift:upper()
elseif n == 3 then
if shift == "UTC" or shift == "GMT" then
rO.zone = 0
end
end
if rO.zone then
rS = s
else
rO = false
end
end
end
return rO, rS
end -- Parser.zone()
Parser.GermanEnglish = function ( analyse )
-- String analysis, for German and English formats
-- Parameter:
-- analyse -- string, with date or time or parts of it
-- Returns:
-- table, if parsed
-- false, if invalid text format
local r, s = Parser.eraGermanEnglish( analyse )
r, s = Parser.zone( s, r )
if r then
local start, sep, sx = s:match( "^(%d+)([ %-%.:WwT]?)(.*)$" )
if start then
-- begins with one or more digits (ASCII)
local n = #start
local lazy = ( start == s and
( n >=4 or type( r.bc == "boolean" ) ) )
if n == 4 or n == 8 or lazy then
r = Parser.digitsHeading( s, lazy, n, r )
elseif n <= 2 then
if sep == ":" then
r, s = Parser.time( s, r )
elseif sep == "" then
r = false
else
r = Parser.european( start, sep, sx, r )
end
else
r = false
end
else
r, s = Parser.monthHeading( s, r )
if r and s ~= "" then
r = Parser.time( s, r )
end
end
end
return r
end -- Parser.GermanEnglish()
Private.factory = function ( assign, alien )
-- Create DateTime table (constructor)
-- Parameter:
-- assign -- string, with initial timestamp, or nil
-- nil -- now
-- false -- empty object
-- alien -- string, with language code, or nil
-- Returns:
-- table, for DateTime object
-- string or false, if failed
local l = true
local r = false
local slang = mw.text.trim( alien or World.slang or "en" )
if assign == false then
r = { }
else
local stamp = ( assign or "now" )
if stamp == "now" then
stamp = mw.getCurrentFrame():callParserFunction( "#timel",
"c" )
end
l, r = pcall( Private.fetch, stamp, slang )
end
if l and type( r ) == "table" then
if slang ~= "" then
r.lang = slang
end
end
return r
end -- Private.factory()
Private.fetch = function ( analyse, alien )
-- Retrieve object from string
-- Parameter:
-- analyse -- string to be interpreted
-- alien -- string with language code, or nil
-- Returns:
-- table, if parsed
-- false, if invalid text format
-- string, if serious error (args)
local r
if type( analyse ) == "string" then
r = analyse:gsub( " ", " " )
:gsub( " ", " " )
:gsub( " ", " " )
:gsub( Nbsp, " " )
:gsub( Tab, " " )
:gsub( " +", " " )
r = mw.text.trim( r )
if r == "" then
r = { }
else
local slang = ( alien or "" )
if slang == "" then
slang = "en"
else
local s = slang:match( "^(%a+)%-" )
if s then
slang = s
end
end
slang = slang:lower()
if slang == "en" or slang == "de" then
local l
l, r = pcall( Parser.GermanEnglish, r )
if l and r then
if not Prototypes.fair( r ) then
r = false
end
end
else
r = "unknown language"
end
end
else
r = "bad type"
end
return r
end -- Private.fetch()
Private.foreign = function ()
-- Retrieve localization submodule
if not World.localization then
local l, d = pcall( mw.loadData, "Module:DateTime/local" )
if l then
if d.slang then
World.slang = d.slang
end
for k, v in pairs( d ) do
if World[ k ].en then
local part = World[ k ]
for subk, subv in pairs( v ) do
part[ subk ] = subv
end -- for k, v
else
World[ k ] = v
end
end -- for k, v
end
World.localization = true
end
end -- Private.foreign()
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
local defs = { year = { max = 2099 },
month = { max = 12 },
dom = { max = 31 },
hour = { max = 23 },
min = { max = 59 },
sec = { max = 61 },
msec = { max = 1000 }
}
local months = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
local fNum =
function ( k, v )
local ret = true
local dk = defs[ k ]
if dk then
local mx = dk.max
if type( mx ) == "number" then
ret = ( type( v ) == "number" )
if ret then
ret = ( v >= 0 and v <= mx
and math.floor( v ) == v )
if ret and dk.f then
ret = dk.f( v )
end
end
end
end
return ret
end -- fNum()
defs.dom.f =
function ()
local ret
local d
if access == "dom" then
d = assign
else
d = self.dom
end
if d then
ret = ( d <= 28 )
if not ret then
local m
if access == "month" then
m = assign
else
m = self.month
end
if m then
ret = ( d <= months[ m ] )
if ret then
local y
if access == "year" then
y = assign
else
y = self.year
end
if d == 29 and m == 2 and y then
if y % 4 ~= 0 or y % 400 == 0 then
ret = false
end
end
end
end
end
else
ret = true
end
return ret
end -- defs.dom.f()
defs.sec.f =
function ()
local ret
local second
if access == "sec" then
second = assign
else
second = self.sec
end
if second then
ret = ( second <= 59 )
if not ret and self.leap then
ret = true
end
end
return ret
end -- defs.sec.f()
if access or assign then
r = ( type( access ) == "string" )
if r then
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
end
end
else
local order = { "bc", "year", "month", "dom",
"hour", "min", "sec", "msec" }
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
return r
end -- Prototypes.fair()
Prototypes.format = function ( self, ask, alien )
-- Format object as string
-- Parameter:
-- self -- table, with numbers etc.
-- ask -- string, format spec, or nil
-- alien -- string, with language code, or nil
-- Returns:
-- string, or false, if invalid
local r = false
if type( self ) == "table" then
local slang = ( alien or self.lang )
local babel = mw.language.new( slang )
if babel then
local show
local stamp
local suffix
local locally
if self.month then
stamp = World.monthsLong.en[ self.month ]
if self.year then
stamp = string.format( "%s %04d", stamp, self.year )
end
if self.dom then
stamp = string.format( "%d %s", self.dom, stamp )
end
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, alien )
if self.locally or alien then
locally = true
else
locally = false
end
r = babel:formatDate( show, stamp, locally )
if self.year and self.year < 1000 then
r = r:gsub( string.format( "%04d", self.year ),
tostring( self.year ) )
end
if suffix then
r = r .. suffix
end
end
end
return r
end -- Prototypes.format()
World.templates.formatter = function ( assigned, ask, alien )
-- Retrieve format specification string
-- Parameter:
-- assigned -- table, with numbers etc.
-- ask -- string, format spec, or nil
-- alien -- string, with language code, or nil
-- 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 = ( alien or assigned.lang or World.slang )
local tmp = World.templates[ slang ]
if tmp then
template = tmp[ ask ]
end
end
if type( template ) == "table" then
r1 = template.spec
if assigned.year then
if not assigned.dom then
r1 = r1:gsub( "[ .]?[jJ][ .,%-]*", "" )
:gsub( "^ ", "" )
if not assigned.month then
r1 = r1:gsub( "[ .%-]?[fFmM][ .%-]*", "" )
end
end
else
r1 = r1:gsub( " ?[yY] ?", "" )
if not assigned.dom then
r1 = r1:gsub( "[ .]?[jJ][ .,%-]*", "" )
:gsub( "^ ", "" )
end
end
if template.lift then
local spec = "T. Monat JJJJ hh:mm:ss Zone"
local stamp = false
local low = ( ask == "ISO" or ask == "ISO-T" )
if ask ~= spec then
spec = false
end
if assigned.hour and assigned.min then
stamp = "H:i"
if assigned.sec then
stamp = "H:i:s"
if assigned.msec then
stamp = string.format( "%s.%d",
stamp, assigned.msec )
end
end
end
if low or spec then
if stamp then
r1 = string.format( "%s %s", r1, stamp )
end
end
if stamp then
local dewiki = ( ask == "dewiki" or spec )
if low or dewiki then
local scheme
if dewiki then
scheme = "de"
end
r2 = World.zones.formatter( assigned, scheme )
end
end
end
if type ( assigned.bc ) == "boolean" then
local eras = World.era[ alien ] 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 )
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
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 = { }
function p.test( args )
local r
local o = DateTime( args[ 1 ], "de" )
if type( o ) == "table" then
local spec = args[ 2 ]
local slang = args[ 3 ]
if spec then
spec = mw.text.trim( spec )
end
if slang then
slang = mw.text.trim( slang )
end
r = o:format( spec, slang )
else
r = ( args.noerror or "0" )
if r == "0" then
r = fault( "Format nicht erkannt" )
else
r = ""
end
end
return r
end -- test
function p.format( frame )
local l, r
local v = { frame.args[ 1 ],
frame.args[ 2 ],
frame.args[ 3 ],
noerror = frame.args.noerror }
if not v[ 1 ] or v[ 1 ] == "now" then
v[ 1 ] = frame:callParserFunction( "#timel", "c" )
end
l, r = pcall( p.test, v )
if not l then
r = fault( r )
end
return r
end -- format
p.DateTime = function ( ... )
return DateTime( ... )
end -- p.DateTime
return p