Module:D'ni Time
From Guild of Archivists
Documentation for this module may be created at Module:D'ni Time/doc
-- THIS IS OBVIOUSLY NOT DONE! DO NOT TOUCH! -Vee
local p = {}
local args = {}
local origArgs
local root
-- Declare important constants
local refDniHahr = 9647
local msPerHahr = 31556925216
local prorahnteePerHahr = 10 * 29 * 5 * 25 * 25 * 25
local msPerProrahn = msPerHahr / prorahnteePerHahr
local refProrahnteePerHahr = 9647 * 290 * 5 * 25 * 25 * 25
local leapSecTimeStamps = {2272060800, 2287785600, 2303683200, 2335219200, 2366755200, 2398291200, 2429913600, 2461449600, 2492985600, 2524521600, 2571782400, 2603318400, 2634854400, 2698012800, 2776982400, 2840140800, 2871676800, 2918937600, 2950473600, 2982009600, 3029443200, 3076704000, 3124137600, 3345062400, 3439756800, 3550089600, 3644697600, 3692217600}
local LeapSecOffset = 10
local function ConvertLeapSecTimeStamps()
-- convert LeapSecTimeStamps from NTP epoch (number of seconds since 1900-01-01 00:00:00)
-- to Unix standard (number of milliseconds since 1970-01-01 00:00:00)
delta = os.time({year=1900, month=1, day=1, hour=0, min=0})
for key,value in pairs(leapSecTimeStamps) do
leapSecTimeStamps[key] = value + delta
end
end
local function AdjustForLeapSeconds(timestamp)
-- Adjust timestamp for leap seconds
leapsecs = 0
for key,value in pairs(leapSecTimeStamps) do
if timestamp >= value then
leapsecs = leapsecs + 1
end
end
if leapsecs > 0 then
leapsecs = leapsecs + (LeapSecOffset - 1)
end
return timestamp + leapsecs
end
local function DejustForLeapSeconds(timestamp)
-- Adjust timestamp for leap seconds the other way
leapsecs = 0
for key,value in pairs(leapSecTimeStamps) do
if timestamp >= value then
leapsecs = leapsecs + 1
end
end
if leapsecs > 0 then
leapsecs = leapsecs + (LeapSecOffset - 1)
end
return timestamp - leapsecs
end
local function makeSurfaceTimeStamp(timeStampTable)
-- convert a UTC date-time to a adjusted timestamp
temp = os.time(timeStampTable)
return AdjustForLeapSeconds(temp)
end
-- Run conversion of leapseconds and conversion of reference Timestamp here,
-- since these require the above functions to be already declared
ConvertLeapSecTimeStamps()
local refTimeStamp = makeSurfaceTimeStamp({year=1991, month=4, day=21, hour=16, min=54})
local function adjust(dniTime)
-- Ensure DniTime values are within bounds
while (dniTime['prorahn'] > 25) do
dniTime['prorahn'] = dniTime['prorahn'] - 25
dniTime['gorahn'] = dniTime['gorahn'] + 1
end
while (dniTime['prorahn'] < 0) do
dniTime['prorahn'] = dniTime['prorahn'] + 25
dniTime['gorahn'] = dniTime['gorahn'] - 1
end
while (dniTime['gorahn'] > 25) do
dniTime['gorahn'] = dniTime['gorahn'] - 25;
dniTime['tahvo'] = dniTime['tahvo'] + 1;
end
while (dniTime['gorahn'] < 0) do
dniTime['gorahn'] = dniTime['gorahn'] + 25
dniTime['tahvo'] = dniTime['tahvo'] - 1
end
while (dniTime['tahvo'] > 25) do
dniTime['tahvo'] = dniTime['tahvo'] - 25
dniTime['gartahvo'] = dniTime['gartahvo'] + 1
end
while (dniTime['tahvo'] < 0) do
dniTime['tahvo'] = dniTime['tahvo'] + 25
dniTime['gartahvo'] = dniTime['gartahvo'] - 1
end
while (dniTime['gartahvo'] > 5) do
dniTime['gartahvo'] = dniTime['gartahvo'] - 5
dniTime['yahr'] = dniTime['yahr'] + 1
end
while (dniTime['gartahvo'] < 0) do
dniTime['gartahvo'] = dniTime['gartahvo'] + 5
dniTime['yahr'] = dniTime['yahr'] - 1
end
while (dniTime['yahr'] > 29) do
dniTime['yahr'] = dniTime['yahr'] - 29
dniTime['vailee'] = dniTime['vailee'] + 1
end
while (dniTime['yahr'] < 0) do
dniTime['yahr'] = dniTime['yahr'] + 29
dniTime['vailee'] = dniTime['vailee'] - 1
end
while (dniTime['vailee'] > 9) do
dniTime['vailee'] = dniTime['vailee'] - 10
dniTime['hahr'] = dniTime['hahr'] + 1;
end
while (dniTime['vailee'] < 0) do
dniTime['vailee'] = dniTime['vailee'] + 10
dniTime['hahr'] = dniTime['hahr'] - 1
end
return dniTime
end
local function surface2DniTime(surfaceTimeStamp)
-- Given a surfaceTimestamp
-- Return a DniTime (table)
dniTime = {}
delta = (surfaceTimeStamp - refTimeStamp) * 1000
dniTime['hahr'] = math.floor(delta / msPerHahr)
delta = delta - (dniTime['hahr'] * msPerHahr)
delta = delta * (prorahnteePerHahr / msPerHahr)
dniTime['vailee'] = math.floor(delta / (29 * 5 * 25 * 25 * 25))
delta = delta - (dniTime['vailee'] * (29 * 5 * 25 * 25 * 25))
dniTime['yahr'] = math.floor(delta / (5 * 25 * 25 * 25))
delta = delta - (dniTime['yahr'] * (5 * 25 * 25 * 25))
dniTime['gartahvo'] = math.floor(delta / (25 * 25 * 25))
delta = delta - (dniTime['gartahvo'] * (25 * 25 * 25))
dniTime['tahvo'] = math.floor(delta / (25 * 25))
delta = delta - (dniTime['tahvo'] * (25 * 25))
dniTime['gorahn'] = math.floor(delta / 25)
delta = delta - (dniTime['gorahn'] * 25)
dniTime['prorahn'] = math.floor(delta)
dniTime['hahr'] = dniTime['hahr'] + refDniHahr
dniTime['yahr'] = dniTime['yahr'] + 1
return adjust(dniTime)
end
local function dni2SurfaceTime(dniTime)
-- Given a DniTime (table)
-- Return a surfacteTimestamp
dniTime = adjust(dniTime)
dTimeInProrahntee = dniTime['prorahn'] + (dniTime['gorahn'] * 25) + (dniTime['tahvo'] * 25 * 25) + (dniTime['gartahvo'] * 25 * 25 * 25) + ((dniTime['yahr'] - 1) * 5 * 25 * 25 * 25) + ((dniTime['vailee']) * 29 * 5 * 25 * 25 * 25) + (dniTime['hahr'] * 290 * 5 * 25 * 25 * 25)
dTimeDelta = refProrahnteePerHahr - dTimeInProrahntee
dTimeDelta = dTimeDelta * msPerProrahn
dTimeDelta = (refTimeStamp * 1000) - dTimeDelta
surfaceTimestamp = DejustForLeapSeconds(dTimeDelta/1000)
return os.date("*t",surfaceTimestamp)
end
-- ==Display Functions==
local function getVaileeName(vailee, useDniFont)
-- Return name of vailee given number (zero-indexed)
-- If useDniFont is true, return it in Dnifont text
useDniFont = useDniFont or false
vaileetee = {"Leefo","Leebro","Leesahn","Leetar","Leevot","Leevofo","Leevobro","Leevosahn","Leevotar","Leenovoo"}
vaileetee_dnifont = {"lEfo","lEbro","lEsan","lEtar","lEvot","lEvofo","lEvobro","lEvosan","lEvotar","lEnovU"}
if(useDniFont) then
return vaileetee_dnifont[vailee+1]
else
return vaileetee[vailee+1]
end
end
local function displayDniTime(dniTime)
-- Return dniTime (table) as string of table
return '{'..'hahr = '..dniTime['hahr']..', vailee = '..dniTime['vailee']..', vaileeName = '..getVaileeName(dniTime['vailee'])..', yahr = '..dniTime['yahr']..', gartahvo = '..dniTime['gartahvo']..', tahvo = '..dniTime['tahvo']..', gorahn = '..dniTime['gorahn']..', prorahn = '..dniTime['prorahn']..'}'
end
local function displaySurfaceTime(surface)
-- Return surfaceTime (table) as string of table
return '{'..'year = '..surface['year']..', month = '..surface['month']..', day = '..surface['day']..', hour = '..surface['hour']..', min = '..surface['min']..', sec = '..surface['sec']..'}'
end
local function formatSurfaceTime(surfaceTime,format)
format = format or 'c'
lang = mw.getContentLanguage()
return lang:formatDate(format,tostring(surfaceTime['year'])..'/'..tostring(surfaceTime['month'])..'/'..tostring(surfaceTime['day'])..' '..tostring(surfaceTime['hour'])..':'..tostring(surfaceTime['min'])..':'..tostring(surfaceTime['sec']))
end
local function formatDniTime(dniTime,format)
-- Given a DniTime (table), returns formatted string.
-- format is a string with patterns for parts of the date, like standard dates
-- Patterns in capitals padded with zeroes
-- HHHH, hhhh = hahr (4-digits, Uppercase includes +/-)
-- hh = last two digits of the hahr
-- XX = 'era' (BE or AE)
-- VVVV = vailee by name
-- VV,vv = vailee (1 indexed)
-- YY, yy = yahr
-- GG, gg = gahrotahvo
-- TT, tt = tahvo
-- RR, rr = gorahnn
-- PP, pp = prorahn
format = format or "VVVV yy HHHH gg:tt:rr:pp"
result = format
if (dniTime['hahr'] < 0) then era = "BE" else era = "AE" end
result = string.gsub(result,"HHHH", string.format("%04d",tostring(dniTime['hahr'])))
result = string.gsub(result,"hhhh", math.abs(dniTime['hahr']))
result = string.gsub(result,"hh", string.sub(dniTime['hahr'],-2))
result = string.gsub(result,"YY", string.format("%02d",tostring(dniTime['yahr'])))
result = string.gsub(result,"yy", dniTime['yahr'])
result = string.gsub(result,"VVVV", getVaileeName(dniTime['vailee']))
result = string.gsub(result,"VV", string.format("%02d",tostring(dniTime['vailee']+1)))
result = string.gsub(result,"vv", dniTime['vailee'] + 1)
result = string.gsub(result,"GG", string.format("%02d",tostring(dniTime['gartahvo'])))
result = string.gsub(result,"gg", dniTime['gartahvo'])
result = string.gsub(result,"TT", string.format("%02d",tostring(dniTime['tahvo'])))
result = string.gsub(result,"tt", dniTime['tahvo'])
result = string.gsub(result,"RR", string.format("%02d",tostring(dniTime['gorahn'])))
result = string.gsub(result,"rr", dniTime['gorahn'])
result = string.gsub(result,"PP", string.format("%02d",tostring(dniTime['prorahn'])))
result = string.gsub(result,"pp", dniTime['prorahn'])
result = string.gsub(result,"XX", era)
return result
end
-- =============================================================================
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
function p.test(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
-- Parse the data parameters
lang = mw.getContentLanguage()
output = os.time({year=1900, month=1, day=1, hour=0, min=0}) .. "<br/>" .. refTimeStamp .."<br/>" .. displaySurfaceTime(os.date("*t",refTimeStamp))
output = output .. "<br/>" .. displaySurfaceTime(os.date("*t",63100800)) .. "<br/>"
output = output .. displayDniTime(surface2DniTime(makeSurfaceTimeStamp({year=2020, month=7, day=29, hour=16, min=0}))).."<br/>"..formatDniTime(surface2DniTime(makeSurfaceTimeStamp({year=2020, month=7, day=29, hour=16, min=0}))).."---<br/>"
output = output .. formatSurfaceTime(dni2SurfaceTime({hahr = 9676, vailee = 2, yahr = 22, gartahvo = 1, tahvo = 18, gorahn = 21, prorahn = 24})).."<br/>---"
output = output .. lang:formatDate('c',os.date("%c",os.time({year=2020, month=7, day=29, hour=16, min=0})))
return output
end
return p