Module:D'ni Time/dni

From Guild of Archivists

Documentation for this module may be created at Module:D'ni Time/dni/doc

dnitools = require("Module:D'ni Tools")

local p = {}

local args = {}
local origArgs
local root

local vaileetee = {"Leefo","Leebro","Leesahn","Leetar","Leevot","Leevofo","Leevobro","Leevosahn","Leevotar","Leenovoo"}
local vaileetee_dnifont = {"lEfo","lEbro","lEsan","lEtar","lEvot","lEvofo","lEvobro","lEvosan","lEvotar","lEnovU"}

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

    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']..', gahrtahvo = '..dniTime['gahrtahvo']..', tahvo = '..dniTime['tahvo']..', gorahn = '..dniTime['gorahn']..', prorahn = '..dniTime['prorahn']..'}'

end

-- Much of this code derived from https://help.interfaceware.com/code/details/dateparse-lua

-- Create index table for vailee
if true then
   local function index_by_name(array)
      local dict = {}
      for i,name in pairs(array) do
         name = name:lower()
         dict[name] = i - 1
      end
      return dict
   end
   vailee_by_name = index_by_name(vaileetee)
end

-- Given string s, return name of vailee
local function lookup_vailee(s)
   local vailee = vailee_by_name[s:lower()]
   if not vailee then error('expected vailee, got "'..s..'"') end
   return vailee
end

local function fix_hahr(BE,DE)
   return function(s,d)
             if s:upper() == BE then
                d.hahr = - math.abs(d.hahr)
             elseif s:upper() == DE then
                d.hahr = math.abs(d.hahr)
             else
                error('expected '..BE..' or '..DE..', got "'..s..'"')
             end
          end
end

--Just returns a number (serves as 'default' for function table)
local function parseint(s)
   return tonumber(s)
end
 
-- Possible codes in Dni format string, with associated patterns and functions. 
-- The default function is parseint(), since most values are just integers, exactly 
-- as we need them.
local fmt_details = {
   hhhh = { '%d%d%d%d', 'hahr' };
   v    = { '%d+',    'vailee' };
   vv   = { '%d%d',   'vailee' };
   vvvv = { '%a+', 'vailee', lookup_vailee }; 
   y    = { '%d+',  'yahr' };
   yy   = { '%d%d', 'yahr' };
   g    = { '%d+',  'gahrtahvo' };
   gg   = { '%d%d', 'gahrtahvo' };
   t    = { '%d+',  'tahvo' };
   tt   = { '%d%d', 'tahvo' };
   r    = { '%d+',  'gorahn' };
   rr   = { '%d%d', 'gorahn' };
   p    = { '%d+',  'prorahn' };
   pp   = { '%d%d', 'prorahn' };
   xx   = { '%a%a', 'BE or DE', fix_hahr('BE','DE') };
   [' '] = { '%s*', 'whitespace' }; -- Allow omission.
   [','] = { '%s*,?', 'a comma' };  -- Allow omission and leading whitespace.
   w     = { '%a+', 'a word' };     -- Value ignored.
   n     = { '%d+', 'a number' };   -- Value ignored.
}
 
-- Splits one part of a format string off; returns that and the rest.
local function split_fmt(fmt)
   local c = fmt:match('^(%a)')
   if c then
      return fmt:match('^('..c..'+)(.*)')
   elseif #fmt > 0 then
      return fmt:sub(1,1), fmt:sub(2)
   end
end
 

local function parseDniDateStr(s, fmt)
-- Parses the string, s, according to the format, fmt.

   fmt = fmt or "hhhh vvvv y g:t:r:p"

   local matched, d = '', {hahr=1,yahr=1,vailee=0,gahrtahvo=0,tahvo=0,gorahn=0,prorahn=0} --Sets default
   local function fail(what, pattern) --Error msg function
      if pattern then what = what..' ('..pattern..')' end
      if matched ~= '' then what = what..' after "'..matched..'"' end
      error('expected '..what..', got "'..s..'"')
   end
   while fmt ~= '' do
      local head_fmt, rest_fmt = split_fmt(fmt)
      local pattern, field, fun = unpack(fmt_details[head_fmt] or {})
      local part, rest
      if pattern then
         part, rest = s:match('^('..pattern..')(.*)')
         if not part then fail(field,head_fmt) end
         d[field] = (fun or parseint)(part,d)
         matched = matched .. part
      elseif head_fmt:find('^%a') then
         error('unknown date/time pattern: '..head_fmt)
      elseif s:sub(1,#head_fmt) ~= head_fmt then
         fail('"'..head_fmt..'"')
      else
         matched = matched .. s:sub(1,#head_fmt)
         rest = s:sub(#head_fmt + 1)
      end
      s, fmt = rest, rest_fmt
   end
   if s ~= '' then fail('nothing') end
   return d
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 DE)
-- VVVV = vailee by name
-- VV,vv = vailee (1 indexed)
-- YY, yy = yahr
-- GG, gg = gahrtahvo
-- TT, tt = tahvo
-- RR, rr = gorahnn
-- PP, pp = prorahn

   format = format or "HHHH XX VVVV yy gg:tt:rr:pp"

   result = format

   if (dniTime['hahr'] < 0) then 
       era = "BE"
       sign = "-" 
   else 
       era = "DE"
       sign = "" 
   end

result = string.gsub(result,"%a+", {
                               ["dHHHH"] = dnitools._num2dnifont(math.abs(dniTime['hahr']),4),
                               ["HHHH"] = string.format("%04d",tostring(math.abs(dniTime['hahr']))),
                               ["dhhhh"] = dnitools._num2dnifont(math.abs(dniTime['hahr'])),
                               ["hhhh"] = math.abs(dniTime['hahr']),
                               ["dhh"] = dnitools._num2dnifont(string.sub(dniTime['hahr'],-2)),
                               ["hh"] = string.sub(dniTime['hahr'],-2),
                               ["dYY"] = dnitools._num2dnifont(dniTime['yahr'],2),
                               ["YY"] = string.format("%02d",tostring(dniTime['yahr'])),
                               ["dyy"] = dnitools._num2dnifont(dniTime['yahr']),
                               ["yy"] = dniTime['yahr'],
                               ["dVVVV"] = getVaileeName(dniTime['vailee'],true),
                               ["VVVV"] = getVaileeName(dniTime['vailee']),
                               ["VV"] = string.format("%02d",tostring(dniTime['vailee']+1)),
                               ["dvv"] = dnitools._num2dnifont(dniTime['vailee'] + 1),
                               ["vv"] = dniTime['vailee'] + 1,
                               ["GG"] = string.format("%02d",tostring(dniTime['gahrtahvo'])),
                               ["dgg"] = dnitools._num2dnifont(dniTime['gahrtahvo']),
                               ["gg"] = dniTime['gahrtahvo'],
                               ["TT"] = string.format("%02d",tostring(dniTime['tahvo'])),
                               ["dtt"] = dnitools._num2dnifont(dniTime['tahvo']),
                               ["tt"] = dniTime['tahvo'],
                               ["RR"] = string.format("%02d",tostring(dniTime['gorahn'])),
                               ["drr"] = dnitools._num2dnifont(dniTime['gorahn']),
                               ["rr"] = dniTime['gorahn'],
                               ["PP"] = string.format("%02d",tostring(dniTime['prorahn'])),
                               ["pp"] = dniTime['prorahn'],
                               ["dpp"] = dnitools._num2dnifont(dniTime['prorahn']),
                               ["XX"] = era,
                               ["X"] = sign,
})

   return result

end

-- Public API

function p.parseDniDateStr(s, fmt)
   return parseDniDateStr(s, fmt)
end

function p.formatDniTime(dniTime,format)
   return formatDniTime(dniTime,format)
end

function p.displayDniTime(dniTime)
   return displayDniTime(dniTime)
end

return p