| rev | 
   line source | 
| 
jbe/bsw@0
 | 
     1 #!/usr/bin/env lua
 | 
| 
jbe/bsw@0
 | 
     2 
 | 
| 
jbe/bsw@0
 | 
     3 local _G             = _G
 | 
| 
jbe/bsw@0
 | 
     4 local _VERSION       = _VERSION
 | 
| 
jbe/bsw@0
 | 
     5 local assert         = assert
 | 
| 
jbe/bsw@0
 | 
     6 local error          = error
 | 
| 
jbe/bsw@0
 | 
     7 local getmetatable   = getmetatable
 | 
| 
jbe/bsw@0
 | 
     8 local ipairs         = ipairs
 | 
| 
jbe/bsw@0
 | 
     9 local next           = next
 | 
| 
jbe/bsw@0
 | 
    10 local pairs          = pairs
 | 
| 
jbe/bsw@0
 | 
    11 local print          = print
 | 
| 
jbe/bsw@0
 | 
    12 local rawequal       = rawequal
 | 
| 
jbe/bsw@0
 | 
    13 local rawget         = rawget
 | 
| 
jbe@64
 | 
    14 local rawlen         = rawlen
 | 
| 
jbe/bsw@0
 | 
    15 local rawset         = rawset
 | 
| 
jbe/bsw@0
 | 
    16 local select         = select
 | 
| 
jbe/bsw@0
 | 
    17 local setmetatable   = setmetatable
 | 
| 
jbe/bsw@0
 | 
    18 local tonumber       = tonumber
 | 
| 
jbe/bsw@0
 | 
    19 local tostring       = tostring
 | 
| 
jbe/bsw@0
 | 
    20 local type           = type
 | 
| 
jbe/bsw@0
 | 
    21 
 | 
| 
jbe/bsw@0
 | 
    22 local math      = math
 | 
| 
jbe/bsw@0
 | 
    23 local string    = string
 | 
| 
jbe/bsw@0
 | 
    24 local table     = table
 | 
| 
jbe/bsw@0
 | 
    25 
 | 
| 
jbe@64
 | 
    26 local _M = {}
 | 
| 
jbe@64
 | 
    27 if _ENV then
 | 
| 
jbe@64
 | 
    28   _ENV = _M
 | 
| 
jbe@64
 | 
    29 else
 | 
| 
jbe@64
 | 
    30   _G[...] = _M
 | 
| 
jbe@64
 | 
    31   setfenv(1, _M)
 | 
| 
jbe@64
 | 
    32 end
 | 
| 
jbe/bsw@0
 | 
    33 
 | 
| 
jbe/bsw@0
 | 
    34 
 | 
| 
jbe/bsw@0
 | 
    35 
 | 
| 
jbe/bsw@0
 | 
    36 ---------------------------------------
 | 
| 
jbe/bsw@0
 | 
    37 -- general functions and definitions --
 | 
| 
jbe/bsw@0
 | 
    38 ---------------------------------------
 | 
| 
jbe/bsw@0
 | 
    39 
 | 
| 
jbe/bsw@0
 | 
    40 --[[--
 | 
| 
jbe/bsw@0
 | 
    41 bool =            -- true, if value is an integer within resolution
 | 
| 
jbe/bsw@0
 | 
    42 atom.is_integer(
 | 
| 
jbe/bsw@0
 | 
    43   value           -- value to be tested
 | 
| 
jbe/bsw@0
 | 
    44 )
 | 
| 
jbe/bsw@0
 | 
    45 
 | 
| 
jbe/bsw@0
 | 
    46 This function returns true if the given object is an integer within resolution.
 | 
| 
jbe/bsw@0
 | 
    47 
 | 
| 
jbe/bsw@0
 | 
    48 --]]--
 | 
| 
jbe/bsw@0
 | 
    49 function is_integer(i)
 | 
| 
jbe/bsw@0
 | 
    50   return
 | 
| 
jbe/bsw@0
 | 
    51     type(i) == "number" and i % 1 == 0 and
 | 
| 
jbe/bsw@0
 | 
    52     (i + 1) - i == 1 and i - (i - 1) == 1
 | 
| 
jbe/bsw@0
 | 
    53 end
 | 
| 
jbe/bsw@0
 | 
    54 --//--
 | 
| 
jbe/bsw@0
 | 
    55 
 | 
| 
jbe/bsw@0
 | 
    56 --[[--
 | 
| 
jbe/bsw@0
 | 
    57 atom.not_a_number
 | 
| 
jbe/bsw@0
 | 
    58 
 | 
| 
jbe/bsw@0
 | 
    59 Value representing an invalid numeric result. Used for atom.integer.invalid and atom.number.invalid.
 | 
| 
jbe/bsw@0
 | 
    60 
 | 
| 
jbe/bsw@0
 | 
    61 --]]--
 | 
| 
jbe/bsw@0
 | 
    62 not_a_number = 0 / 0
 | 
| 
jbe/bsw@0
 | 
    63 --//--
 | 
| 
jbe/bsw@0
 | 
    64 
 | 
| 
jbe/bsw@0
 | 
    65 do
 | 
| 
jbe/bsw@0
 | 
    66 
 | 
| 
jbe/bsw@0
 | 
    67   local shadow = setmetatable({}, { __mode = "k" })
 | 
| 
jbe/bsw@0
 | 
    68 
 | 
| 
jbe/bsw@0
 | 
    69   local type_mt = { __index = {} }
 | 
| 
jbe/bsw@0
 | 
    70 
 | 
| 
jbe/bsw@0
 | 
    71   function type_mt:__call(...)
 | 
| 
jbe/bsw@0
 | 
    72     return self:new(...)
 | 
| 
jbe/bsw@0
 | 
    73   end
 | 
| 
jbe/bsw@0
 | 
    74 
 | 
| 
jbe/bsw@0
 | 
    75   function type_mt.__index:_create(data)
 | 
| 
jbe/bsw@0
 | 
    76     local value = setmetatable({}, self)
 | 
| 
jbe/bsw@0
 | 
    77     shadow[value] = data
 | 
| 
jbe/bsw@0
 | 
    78     return value
 | 
| 
jbe/bsw@0
 | 
    79   end
 | 
| 
jbe/bsw@0
 | 
    80 
 | 
| 
jbe/bsw@0
 | 
    81   local function write_prohibited()
 | 
| 
jbe/bsw@0
 | 
    82     error("Modification of an atom is prohibited.")
 | 
| 
jbe/bsw@0
 | 
    83   end
 | 
| 
jbe/bsw@0
 | 
    84 
 | 
| 
jbe/bsw@0
 | 
    85   -- returns a new type as a table, which serves also as metatable
 | 
| 
jbe/bsw@0
 | 
    86   function create_new_type(name)
 | 
| 
jbe/bsw@0
 | 
    87     local t = setmetatable(
 | 
| 
jbe/bsw@0
 | 
    88       { methods = {}, getters = {}, name = name },
 | 
| 
jbe/bsw@0
 | 
    89       type_mt
 | 
| 
jbe/bsw@0
 | 
    90     )
 | 
| 
jbe/bsw@0
 | 
    91     function t.__index(self, key)
 | 
| 
jbe/bsw@0
 | 
    92       local data = shadow[self]
 | 
| 
jbe/bsw@0
 | 
    93       local value = data[key]
 | 
| 
jbe/bsw@0
 | 
    94       if value ~= nil then return value end
 | 
| 
jbe/bsw@0
 | 
    95       local method = t.methods[key]
 | 
| 
jbe/bsw@0
 | 
    96       if method then return method end
 | 
| 
jbe/bsw@0
 | 
    97       local getter = t.getters[key]
 | 
| 
jbe/bsw@0
 | 
    98       if getter then return getter(self) end
 | 
| 
jbe/bsw@0
 | 
    99     end
 | 
| 
jbe/bsw@0
 | 
   100     t.__newindex = write_prohibited
 | 
| 
jbe/bsw@0
 | 
   101     return t
 | 
| 
jbe/bsw@0
 | 
   102   end
 | 
| 
jbe/bsw@0
 | 
   103 
 | 
| 
jbe/bsw@0
 | 
   104   --[[--
 | 
| 
jbe/bsw@0
 | 
   105   bool =          -- true, if 'value' is of type 't'
 | 
| 
jbe/bsw@0
 | 
   106   atom.has_type(
 | 
| 
jbe/bsw@0
 | 
   107     value,        -- any value
 | 
| 
jbe/bsw@0
 | 
   108     t             -- atom time, e.g. atom.date, or lua type, e.g. "string"
 | 
| 
jbe/bsw@0
 | 
   109   )
 | 
| 
jbe/bsw@0
 | 
   110 
 | 
| 
jbe/bsw@0
 | 
   111   This function checks, if a value is of a given type. The value may be an invalid value though, e.g. atom.date.invalid.
 | 
| 
jbe/bsw@0
 | 
   112 
 | 
| 
jbe/bsw@0
 | 
   113   --]]--
 | 
| 
jbe/bsw@0
 | 
   114   function has_type(value, t)
 | 
| 
jbe/bsw@0
 | 
   115     if t == nil then error("No type passed to has_type(...) function.") end
 | 
| 
jbe/bsw@0
 | 
   116     local lua_type = type(value)
 | 
| 
jbe/bsw@0
 | 
   117     return
 | 
| 
jbe/bsw@0
 | 
   118       lua_type == t or
 | 
| 
jbe/bsw@0
 | 
   119       getmetatable(value) == t or
 | 
| 
jbe/bsw@0
 | 
   120       (lua_type == "boolean" and t == _M.boolean) or
 | 
| 
jbe/bsw@0
 | 
   121       (lua_type == "string" and t == _M.string) or (
 | 
| 
jbe/bsw@0
 | 
   122         lua_type == "number" and
 | 
| 
jbe/bsw@0
 | 
   123         (t == _M.number or (
 | 
| 
jbe/bsw@0
 | 
   124           t == _M.integer and (
 | 
| 
jbe/bsw@0
 | 
   125             not (value <= 0 or value >= 0) or (
 | 
| 
jbe/bsw@0
 | 
   126               value % 1 == 0 and
 | 
| 
jbe/bsw@0
 | 
   127               (value + 1) - value == 1 and
 | 
| 
jbe/bsw@0
 | 
   128               value - (value - 1) == 1
 | 
| 
jbe/bsw@0
 | 
   129             )
 | 
| 
jbe/bsw@0
 | 
   130           )
 | 
| 
jbe/bsw@0
 | 
   131         ))
 | 
| 
jbe/bsw@0
 | 
   132       )
 | 
| 
jbe/bsw@0
 | 
   133   end
 | 
| 
jbe/bsw@0
 | 
   134   --//--
 | 
| 
jbe/bsw@0
 | 
   135 
 | 
| 
jbe/bsw@0
 | 
   136   --[[--
 | 
| 
jbe/bsw@0
 | 
   137   bool =          -- true, if 'value' is of type 't'
 | 
| 
jbe/bsw@0
 | 
   138   atom.is_valid(
 | 
| 
jbe/bsw@0
 | 
   139     value,        -- any value
 | 
| 
jbe/bsw@0
 | 
   140     t             -- atom time, e.g. atom.date, or lua type, e.g. "string"
 | 
| 
jbe/bsw@0
 | 
   141   )
 | 
| 
jbe/bsw@0
 | 
   142 
 | 
| 
jbe/bsw@0
 | 
   143   This function checks, if a value is valid. It optionally checks, if the value is of a given type.
 | 
| 
jbe/bsw@0
 | 
   144 
 | 
| 
jbe/bsw@0
 | 
   145   --]]--
 | 
| 
jbe/bsw@0
 | 
   146   function is_valid(value, t)
 | 
| 
jbe/bsw@0
 | 
   147     local lua_type = type(value)
 | 
| 
jbe/bsw@0
 | 
   148     if lua_type == "table" then
 | 
| 
jbe/bsw@0
 | 
   149       local mt = getmetatable(value)
 | 
| 
jbe/bsw@0
 | 
   150       if t then
 | 
| 
jbe/bsw@0
 | 
   151         return t == mt and not value.invalid
 | 
| 
jbe/bsw@0
 | 
   152       else
 | 
| 
jbe/bsw@0
 | 
   153         return (getmetatable(mt) == type_mt) and not value.invalid
 | 
| 
jbe/bsw@0
 | 
   154       end
 | 
| 
jbe/bsw@0
 | 
   155     elseif lua_type == "boolean" then
 | 
| 
jbe/bsw@0
 | 
   156       return not t or t == "boolean" or t == _M.boolean
 | 
| 
jbe/bsw@0
 | 
   157     elseif lua_type == "string" then
 | 
| 
jbe/bsw@0
 | 
   158       return not t or t == "string" or t == _M.string
 | 
| 
jbe/bsw@0
 | 
   159     elseif lua_type == "number" then
 | 
| 
jbe/bsw@0
 | 
   160       if t == _M.integer then
 | 
| 
jbe/bsw@0
 | 
   161         return
 | 
| 
jbe/bsw@0
 | 
   162           value % 1 == 0 and
 | 
| 
jbe/bsw@0
 | 
   163           (value + 1) - value == 1 and
 | 
| 
jbe/bsw@0
 | 
   164           value - (value - 1) == 1
 | 
| 
jbe/bsw@0
 | 
   165       else
 | 
| 
jbe/bsw@0
 | 
   166         return
 | 
| 
jbe/bsw@0
 | 
   167           (not t or t == "number" or t == _M.number) and
 | 
| 
jbe/bsw@0
 | 
   168           (value <= 0 or value >= 0)
 | 
| 
jbe/bsw@0
 | 
   169       end
 | 
| 
jbe/bsw@0
 | 
   170     else
 | 
| 
jbe/bsw@0
 | 
   171       return false
 | 
| 
jbe/bsw@0
 | 
   172     end
 | 
| 
jbe/bsw@0
 | 
   173   end
 | 
| 
jbe/bsw@0
 | 
   174   --//--
 | 
| 
jbe/bsw@0
 | 
   175 
 | 
| 
jbe/bsw@0
 | 
   176 end
 | 
| 
jbe/bsw@0
 | 
   177 
 | 
| 
jbe/bsw@0
 | 
   178 --[[--
 | 
| 
jbe/bsw@0
 | 
   179 string =    -- string representation to be passed to a load function
 | 
| 
jbe/bsw@0
 | 
   180 atom.dump(
 | 
| 
jbe/bsw@0
 | 
   181   value     -- value to be dumped
 | 
| 
jbe/bsw@0
 | 
   182 )
 | 
| 
jbe/bsw@0
 | 
   183 
 | 
| 
jbe/bsw@0
 | 
   184 This function returns a string representation of the given value.
 | 
| 
jbe/bsw@0
 | 
   185 
 | 
| 
jbe/bsw@0
 | 
   186 --]]--
 | 
| 
jbe/bsw@0
 | 
   187 function dump(obj)
 | 
| 
jbe/bsw@0
 | 
   188   if obj == nil then
 | 
| 
jbe/bsw@0
 | 
   189     return ""
 | 
| 
jbe/bsw@0
 | 
   190   else
 | 
| 
jbe/bsw@0
 | 
   191     return tostring(obj)
 | 
| 
jbe/bsw@0
 | 
   192   end
 | 
| 
jbe/bsw@0
 | 
   193 end
 | 
| 
jbe/bsw@0
 | 
   194 --//--
 | 
| 
jbe/bsw@0
 | 
   195 
 | 
| 
jbe/bsw@0
 | 
   196 
 | 
| 
jbe/bsw@0
 | 
   197 
 | 
| 
jbe/bsw@0
 | 
   198 -------------
 | 
| 
jbe/bsw@0
 | 
   199 -- boolean --
 | 
| 
jbe/bsw@0
 | 
   200 -------------
 | 
| 
jbe/bsw@0
 | 
   201 
 | 
| 
jbe/bsw@0
 | 
   202 boolean = { name = "boolean" }
 | 
| 
jbe/bsw@0
 | 
   203 
 | 
| 
jbe/bsw@0
 | 
   204 --[[--
 | 
| 
jbe/bsw@0
 | 
   205 bool =              -- true, false, or nil
 | 
| 
jbe/bsw@0
 | 
   206 atom.boolean:load(
 | 
| 
jbe/bsw@0
 | 
   207   string            -- string to be interpreted as boolean
 | 
| 
jbe/bsw@0
 | 
   208 )
 | 
| 
jbe/bsw@0
 | 
   209 
 | 
| 
jbe/bsw@0
 | 
   210 This method returns true or false or nil, depending on the input string.
 | 
| 
jbe/bsw@0
 | 
   211 
 | 
| 
jbe/bsw@0
 | 
   212 --]]--
 | 
| 
jbe/bsw@0
 | 
   213 function boolean:load(str)
 | 
| 
jbe@1
 | 
   214   if str == nil or str == "" then
 | 
| 
jbe@1
 | 
   215     return nil
 | 
| 
jbe@1
 | 
   216   elseif type(str) ~= "string" then
 | 
| 
jbe/bsw@0
 | 
   217     error("String expected")
 | 
| 
jbe/bsw@0
 | 
   218   elseif string.find(str, "^[TtYy1]") then
 | 
| 
jbe/bsw@0
 | 
   219     return true
 | 
| 
jbe/bsw@0
 | 
   220   elseif string.find(str, "^[FfNn0]") then
 | 
| 
jbe/bsw@0
 | 
   221     return false
 | 
| 
jbe/bsw@0
 | 
   222   else
 | 
| 
jbe/bsw@0
 | 
   223     return nil  -- we don't have an undefined bool
 | 
| 
jbe/bsw@0
 | 
   224   end
 | 
| 
jbe/bsw@0
 | 
   225 end
 | 
| 
jbe/bsw@0
 | 
   226 --//--
 | 
| 
jbe/bsw@0
 | 
   227 
 | 
| 
jbe/bsw@0
 | 
   228 
 | 
| 
jbe/bsw@0
 | 
   229 
 | 
| 
jbe/bsw@0
 | 
   230 ------------
 | 
| 
jbe/bsw@0
 | 
   231 -- string --
 | 
| 
jbe/bsw@0
 | 
   232 ------------
 | 
| 
jbe/bsw@0
 | 
   233 
 | 
| 
jbe/bsw@0
 | 
   234 _M.string = { name = "string" }
 | 
| 
jbe/bsw@0
 | 
   235 
 | 
| 
jbe/bsw@0
 | 
   236 --[[--
 | 
| 
jbe/bsw@0
 | 
   237 string =            -- the same string
 | 
| 
jbe/bsw@0
 | 
   238 atom.string:load(
 | 
| 
jbe/bsw@0
 | 
   239   string            -- a string
 | 
| 
jbe/bsw@0
 | 
   240 )
 | 
| 
jbe/bsw@0
 | 
   241 
 | 
| 
jbe/bsw@0
 | 
   242 This method returns the passed string, or throws an error, if the passed argument is not a string.
 | 
| 
jbe/bsw@0
 | 
   243 
 | 
| 
jbe/bsw@0
 | 
   244 --]]--
 | 
| 
jbe/bsw@0
 | 
   245 function _M.string:load(str)
 | 
| 
jbe@1
 | 
   246   if str == nil then
 | 
| 
jbe@1
 | 
   247     return nil
 | 
| 
jbe@1
 | 
   248   elseif type(str) ~= "string" then
 | 
| 
jbe/bsw@0
 | 
   249     error("String expected")
 | 
| 
jbe/bsw@0
 | 
   250   else
 | 
| 
jbe/bsw@0
 | 
   251     return str
 | 
| 
jbe/bsw@0
 | 
   252   end
 | 
| 
jbe/bsw@0
 | 
   253 end
 | 
| 
jbe/bsw@0
 | 
   254 --//--
 | 
| 
jbe/bsw@0
 | 
   255 
 | 
| 
jbe/bsw@0
 | 
   256 
 | 
| 
jbe/bsw@0
 | 
   257 
 | 
| 
jbe/bsw@0
 | 
   258 -------------
 | 
| 
jbe/bsw@0
 | 
   259 -- integer --
 | 
| 
jbe/bsw@0
 | 
   260 -------------
 | 
| 
jbe/bsw@0
 | 
   261 
 | 
| 
jbe/bsw@0
 | 
   262 integer = { name = "integer" }
 | 
| 
jbe/bsw@0
 | 
   263 
 | 
| 
jbe/bsw@0
 | 
   264 --[[--
 | 
| 
jbe/bsw@0
 | 
   265 int =              -- an integer or atom.integer.invalid (atom.not_a_number)
 | 
| 
jbe/bsw@0
 | 
   266 atom.integer:load(
 | 
| 
jbe/bsw@0
 | 
   267   string           -- a string representing an integer
 | 
| 
jbe/bsw@0
 | 
   268 )
 | 
| 
jbe/bsw@0
 | 
   269 
 | 
| 
jbe/bsw@0
 | 
   270 This method returns an integer represented by the given string. If the string doesn't represent a valid integer, then not-a-number is returned.
 | 
| 
jbe/bsw@0
 | 
   271 
 | 
| 
jbe/bsw@0
 | 
   272 --]]--
 | 
| 
jbe/bsw@0
 | 
   273 function integer:load(str)
 | 
| 
jbe@1
 | 
   274   if str == nil or str == "" then
 | 
| 
jbe@1
 | 
   275     return nil
 | 
| 
jbe@1
 | 
   276   elseif type(str) ~= "string" then
 | 
| 
jbe/bsw@0
 | 
   277     error("String expected")
 | 
| 
jbe/bsw@0
 | 
   278   else
 | 
| 
jbe/bsw@0
 | 
   279     local num = tonumber(str)
 | 
| 
jbe/bsw@0
 | 
   280     if is_integer(num) then return num else return not_a_number end
 | 
| 
jbe/bsw@0
 | 
   281   end
 | 
| 
jbe/bsw@0
 | 
   282 end
 | 
| 
jbe/bsw@0
 | 
   283 --//--
 | 
| 
jbe/bsw@0
 | 
   284 
 | 
| 
jbe/bsw@0
 | 
   285 --[[--
 | 
| 
jbe/bsw@0
 | 
   286 atom.integer.invalid
 | 
| 
jbe/bsw@0
 | 
   287 
 | 
| 
jbe/bsw@0
 | 
   288 This represents an invalid integer.
 | 
| 
jbe/bsw@0
 | 
   289 
 | 
| 
jbe/bsw@0
 | 
   290 --]]--
 | 
| 
jbe/bsw@0
 | 
   291 integer.invalid = not_a_number
 | 
| 
jbe/bsw@0
 | 
   292 --//
 | 
| 
jbe/bsw@0
 | 
   293 
 | 
| 
jbe/bsw@0
 | 
   294 
 | 
| 
jbe/bsw@0
 | 
   295 
 | 
| 
jbe/bsw@0
 | 
   296 ------------
 | 
| 
jbe/bsw@0
 | 
   297 -- number --
 | 
| 
jbe/bsw@0
 | 
   298 ------------
 | 
| 
jbe/bsw@0
 | 
   299 
 | 
| 
jbe/bsw@0
 | 
   300 number = create_new_type("number")
 | 
| 
jbe/bsw@0
 | 
   301 
 | 
| 
jbe/bsw@0
 | 
   302 --[[--
 | 
| 
jbe/bsw@0
 | 
   303 int =              -- a number or atom.number.invalid (atom.not_a_number)
 | 
| 
jbe/bsw@0
 | 
   304 atom.number:load(
 | 
| 
jbe/bsw@0
 | 
   305   string           -- a string representing a number
 | 
| 
jbe/bsw@0
 | 
   306 )
 | 
| 
jbe/bsw@0
 | 
   307 
 | 
| 
jbe/bsw@0
 | 
   308 This method returns a number represented by the given string. If the string doesn't represent a valid number, then not-a-number is returned.
 | 
| 
jbe/bsw@0
 | 
   309 
 | 
| 
jbe/bsw@0
 | 
   310 --]]--
 | 
| 
jbe/bsw@0
 | 
   311 function number:load(str)
 | 
| 
jbe@1
 | 
   312   if str == nil or str == "" then
 | 
| 
jbe@1
 | 
   313     return nil
 | 
| 
jbe@1
 | 
   314   elseif type(str) ~= "string" then
 | 
| 
jbe/bsw@0
 | 
   315     error("String expected")
 | 
| 
jbe/bsw@0
 | 
   316   else
 | 
| 
jbe/bsw@0
 | 
   317     return tonumber(str) or not_a_number
 | 
| 
jbe/bsw@0
 | 
   318   end
 | 
| 
jbe/bsw@0
 | 
   319 end
 | 
| 
jbe/bsw@0
 | 
   320 --//--
 | 
| 
jbe/bsw@0
 | 
   321 
 | 
| 
jbe/bsw@0
 | 
   322 --[[--
 | 
| 
jbe/bsw@0
 | 
   323 atom.number.invalid
 | 
| 
jbe/bsw@0
 | 
   324 
 | 
| 
jbe/bsw@0
 | 
   325 This represents an invalid number.
 | 
| 
jbe/bsw@0
 | 
   326 
 | 
| 
jbe/bsw@0
 | 
   327 --]]--
 | 
| 
jbe/bsw@0
 | 
   328 number.invalid = not_a_number
 | 
| 
jbe/bsw@0
 | 
   329 --//--
 | 
| 
jbe/bsw@0
 | 
   330 
 | 
| 
jbe/bsw@0
 | 
   331 
 | 
| 
jbe/bsw@0
 | 
   332 
 | 
| 
jbe/bsw@0
 | 
   333 --------------
 | 
| 
jbe/bsw@0
 | 
   334 -- fraction --
 | 
| 
jbe/bsw@0
 | 
   335 --------------
 | 
| 
jbe/bsw@0
 | 
   336 
 | 
| 
jbe/bsw@0
 | 
   337 fraction = create_new_type("fraction")
 | 
| 
jbe/bsw@0
 | 
   338 
 | 
| 
jbe/bsw@0
 | 
   339 --[[--
 | 
| 
jbe/bsw@0
 | 
   340 i =        -- the greatest common divisor (GCD) of all given natural numbers
 | 
| 
jbe/bsw@0
 | 
   341 atom.gcd(
 | 
| 
jbe/bsw@0
 | 
   342   a,      -- a natural number
 | 
| 
jbe/bsw@0
 | 
   343   b,      -- another natural number
 | 
| 
jbe/bsw@0
 | 
   344   ...     -- optionally more natural numbers
 | 
| 
jbe/bsw@0
 | 
   345 )
 | 
| 
jbe/bsw@0
 | 
   346 
 | 
| 
jbe/bsw@0
 | 
   347 This function returns the greatest common divisor (GCD) of two or more natural numbers.
 | 
| 
jbe/bsw@0
 | 
   348 
 | 
| 
jbe/bsw@0
 | 
   349 --]]--
 | 
| 
jbe/bsw@0
 | 
   350 function gcd(a, b, ...)
 | 
| 
jbe/bsw@0
 | 
   351   if a % 1 ~= 0 or a <= 0 then return 0 / 0 end
 | 
| 
jbe/bsw@0
 | 
   352   if b == nil then
 | 
| 
jbe/bsw@0
 | 
   353     return a
 | 
| 
jbe/bsw@0
 | 
   354   else
 | 
| 
jbe/bsw@0
 | 
   355     if b % 1 ~= 0 or b <= 0 then return 0 / 0 end
 | 
| 
jbe/bsw@0
 | 
   356     if ... == nil then
 | 
| 
jbe/bsw@0
 | 
   357       local k = 0
 | 
| 
jbe/bsw@0
 | 
   358       local t
 | 
| 
jbe/bsw@0
 | 
   359       while a % 2 == 0 and b % 2 == 0 do
 | 
| 
jbe/bsw@0
 | 
   360         a = a / 2; b = b / 2; k = k + 1
 | 
| 
jbe/bsw@0
 | 
   361       end
 | 
| 
jbe/bsw@0
 | 
   362       if a % 2 == 0 then t = a else t = -b end
 | 
| 
jbe/bsw@0
 | 
   363       while t ~= 0 do
 | 
| 
jbe/bsw@0
 | 
   364         while t % 2 == 0 do t = t / 2 end
 | 
| 
jbe/bsw@0
 | 
   365         if t > 0 then a = t else b = -t end
 | 
| 
jbe/bsw@0
 | 
   366         t = a - b
 | 
| 
jbe/bsw@0
 | 
   367       end
 | 
| 
jbe/bsw@0
 | 
   368       return a * 2 ^ k
 | 
| 
jbe/bsw@0
 | 
   369     else
 | 
| 
jbe/bsw@0
 | 
   370       return gcd(gcd(a, b), ...)
 | 
| 
jbe/bsw@0
 | 
   371     end
 | 
| 
jbe/bsw@0
 | 
   372   end
 | 
| 
jbe/bsw@0
 | 
   373 end
 | 
| 
jbe/bsw@0
 | 
   374 --//--
 | 
| 
jbe/bsw@0
 | 
   375 
 | 
| 
jbe/bsw@0
 | 
   376 --[[--
 | 
| 
jbe/bsw@0
 | 
   377 i =        --the least common multiple (LCD) of all given natural numbers
 | 
| 
jbe/bsw@0
 | 
   378 atom.lcm(
 | 
| 
jbe/bsw@0
 | 
   379   a,       -- a natural number
 | 
| 
jbe/bsw@0
 | 
   380   b,       -- another natural number
 | 
| 
jbe/bsw@0
 | 
   381   ...      -- optionally more natural numbers
 | 
| 
jbe/bsw@0
 | 
   382 )
 | 
| 
jbe/bsw@0
 | 
   383 
 | 
| 
jbe/bsw@0
 | 
   384 This function returns the least common multiple (LCD) of two or more natural numbers.
 | 
| 
jbe/bsw@0
 | 
   385 
 | 
| 
jbe/bsw@0
 | 
   386 --]]--
 | 
| 
jbe/bsw@0
 | 
   387 function lcm(a, b, ...)
 | 
| 
jbe/bsw@0
 | 
   388   if a % 1 ~= 0 or a <= 0 then return 0 / 0 end
 | 
| 
jbe/bsw@0
 | 
   389   if b == nil then
 | 
| 
jbe/bsw@0
 | 
   390     return a
 | 
| 
jbe/bsw@0
 | 
   391   else
 | 
| 
jbe/bsw@0
 | 
   392     if b % 1 ~= 0 or b <= 0 then return 0 / 0 end
 | 
| 
jbe/bsw@0
 | 
   393     if ... == nil then
 | 
| 
jbe/bsw@0
 | 
   394       return a * b / gcd(a, b)
 | 
| 
jbe/bsw@0
 | 
   395     else
 | 
| 
jbe/bsw@0
 | 
   396       return lcm(lcm(a, b), ...)
 | 
| 
jbe/bsw@0
 | 
   397     end
 | 
| 
jbe/bsw@0
 | 
   398   end
 | 
| 
jbe/bsw@0
 | 
   399 end
 | 
| 
jbe/bsw@0
 | 
   400 --//--
 | 
| 
jbe/bsw@0
 | 
   401 
 | 
| 
jbe/bsw@0
 | 
   402 --[[--
 | 
| 
jbe/bsw@0
 | 
   403 atom.fraction.invalid
 | 
| 
jbe/bsw@0
 | 
   404 
 | 
| 
jbe/bsw@0
 | 
   405 Value representing an invalid fraction.
 | 
| 
jbe/bsw@0
 | 
   406 
 | 
| 
jbe/bsw@0
 | 
   407 --]]--
 | 
| 
jbe/bsw@0
 | 
   408 fraction.invalid = fraction:_create{
 | 
| 
jbe/bsw@0
 | 
   409   numerator = not_a_number, denominator = not_a_number, invalid = true
 | 
| 
jbe/bsw@0
 | 
   410 }
 | 
| 
jbe/bsw@0
 | 
   411 --//--
 | 
| 
jbe/bsw@0
 | 
   412 
 | 
| 
jbe/bsw@0
 | 
   413 --[[--
 | 
| 
jbe/bsw@0
 | 
   414 frac =              -- fraction
 | 
| 
jbe/bsw@0
 | 
   415 atom.fraction:new(
 | 
| 
jbe/bsw@0
 | 
   416   numerator,        -- numerator
 | 
| 
jbe/bsw@0
 | 
   417   denominator       -- denominator
 | 
| 
jbe/bsw@0
 | 
   418 )
 | 
| 
jbe/bsw@0
 | 
   419 
 | 
| 
jbe/bsw@0
 | 
   420 This method creates a new fraction.
 | 
| 
jbe/bsw@0
 | 
   421 
 | 
| 
jbe/bsw@0
 | 
   422 --]]--
 | 
| 
jbe/bsw@0
 | 
   423 function fraction:new(numerator, denominator)
 | 
| 
jbe/bsw@0
 | 
   424   if not (
 | 
| 
jbe/bsw@0
 | 
   425     (numerator == nil   or type(numerator)   == "number") and
 | 
| 
jbe/bsw@0
 | 
   426     (denominator == nil or type(denominator) == "number")
 | 
| 
jbe/bsw@0
 | 
   427   ) then
 | 
| 
jbe/bsw@0
 | 
   428     error("Invalid arguments passed to fraction constructor.")
 | 
| 
jbe/bsw@0
 | 
   429   elseif
 | 
| 
jbe/bsw@0
 | 
   430     (not is_integer(numerator)) or
 | 
| 
jbe/bsw@0
 | 
   431     (denominator and (not is_integer(denominator)))
 | 
| 
jbe/bsw@0
 | 
   432   then
 | 
| 
jbe/bsw@0
 | 
   433     return fraction.invalid
 | 
| 
jbe/bsw@0
 | 
   434   elseif denominator then
 | 
| 
jbe/bsw@0
 | 
   435     if denominator == 0 then
 | 
| 
jbe/bsw@0
 | 
   436       return fraction.invalid
 | 
| 
jbe/bsw@0
 | 
   437     elseif numerator == 0 then
 | 
| 
jbe/bsw@0
 | 
   438       return fraction:_create{ numerator = 0, denominator = 1, float = 0 }
 | 
| 
jbe/bsw@0
 | 
   439     else
 | 
| 
jbe/bsw@0
 | 
   440       local d = gcd(math.abs(numerator), math.abs(denominator))
 | 
| 
jbe/bsw@0
 | 
   441       if denominator < 0 then d = -d end
 | 
| 
jbe/bsw@0
 | 
   442       local numerator2, denominator2 = numerator / d, denominator / d
 | 
| 
jbe/bsw@0
 | 
   443       return fraction:_create{
 | 
| 
jbe/bsw@0
 | 
   444         numerator   = numerator2,
 | 
| 
jbe/bsw@0
 | 
   445         denominator = denominator2,
 | 
| 
jbe/bsw@0
 | 
   446         float       = numerator2 / denominator2
 | 
| 
jbe/bsw@0
 | 
   447       }
 | 
| 
jbe/bsw@0
 | 
   448     end
 | 
| 
jbe/bsw@0
 | 
   449   else
 | 
| 
jbe/bsw@0
 | 
   450     return fraction:_create{
 | 
| 
jbe/bsw@0
 | 
   451       numerator = numerator, denominator = 1, float = numerator
 | 
| 
jbe/bsw@0
 | 
   452     }
 | 
| 
jbe/bsw@0
 | 
   453   end
 | 
| 
jbe/bsw@0
 | 
   454 end
 | 
| 
jbe/bsw@0
 | 
   455 --//--
 | 
| 
jbe/bsw@0
 | 
   456 
 | 
| 
jbe/bsw@0
 | 
   457 --[[--
 | 
| 
jbe/bsw@0
 | 
   458 frac =               -- fraction represented by the given string
 | 
| 
jbe/bsw@0
 | 
   459 atom.fraction:load(
 | 
| 
jbe/bsw@0
 | 
   460   string             -- string representation of a fraction
 | 
| 
jbe/bsw@0
 | 
   461 )
 | 
| 
jbe/bsw@0
 | 
   462 
 | 
| 
jbe/bsw@0
 | 
   463 This method returns a fraction represented by the given string.
 | 
| 
jbe/bsw@0
 | 
   464 
 | 
| 
jbe/bsw@0
 | 
   465 --]]--
 | 
| 
jbe/bsw@0
 | 
   466 function fraction:load(str)
 | 
| 
jbe@1
 | 
   467   if str == nil or str == "" then
 | 
| 
jbe/bsw@0
 | 
   468     return nil
 | 
| 
jbe@1
 | 
   469   elseif type(str) ~= "string" then
 | 
| 
jbe@1
 | 
   470     error("String expected")
 | 
| 
jbe/bsw@0
 | 
   471   else
 | 
| 
jbe/bsw@0
 | 
   472     local sign, int = string.match(str, "^(%-?)([0-9]+)$")
 | 
| 
jbe/bsw@0
 | 
   473     if sign == "" then return fraction:new(tonumber(int))
 | 
| 
jbe/bsw@0
 | 
   474     elseif sign == "-" then return fraction:new(- tonumber(int))
 | 
| 
jbe/bsw@0
 | 
   475     end
 | 
| 
jbe/bsw@0
 | 
   476     local sign, n, d = string.match(str, "^(%-?)([0-9]+)/([0-9]+)$")
 | 
| 
jbe/bsw@0
 | 
   477     if sign == "" then return fraction:new(tonumber(n), tonumber(d))
 | 
| 
jbe/bsw@0
 | 
   478     elseif sign == "-" then return fraction:new(- tonumber(n), tonumber(d))
 | 
| 
jbe/bsw@0
 | 
   479     end
 | 
| 
jbe/bsw@0
 | 
   480     return fraction.invalid
 | 
| 
jbe/bsw@0
 | 
   481   end
 | 
| 
jbe/bsw@0
 | 
   482 end
 | 
| 
jbe/bsw@0
 | 
   483 --//--
 | 
| 
jbe/bsw@0
 | 
   484 
 | 
| 
jbe/bsw@0
 | 
   485 function fraction:__tostring()
 | 
| 
jbe/bsw@0
 | 
   486   if self.invalid then
 | 
| 
jbe/bsw@0
 | 
   487     return "not_a_fraction"
 | 
| 
jbe/bsw@0
 | 
   488   else
 | 
| 
jbe/bsw@0
 | 
   489     return self.numerator .. "/" .. self.denominator
 | 
| 
jbe/bsw@0
 | 
   490   end
 | 
| 
jbe/bsw@0
 | 
   491 end
 | 
| 
jbe/bsw@0
 | 
   492 
 | 
| 
jbe/bsw@0
 | 
   493 function fraction.__eq(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   494   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
   495     return false
 | 
| 
jbe/bsw@0
 | 
   496   else
 | 
| 
jbe/bsw@0
 | 
   497     return
 | 
| 
jbe/bsw@0
 | 
   498       value1.numerator == value2.numerator and
 | 
| 
jbe/bsw@0
 | 
   499       value1.denominator == value2.denominator
 | 
| 
jbe/bsw@0
 | 
   500   end
 | 
| 
jbe/bsw@0
 | 
   501 end
 | 
| 
jbe/bsw@0
 | 
   502 
 | 
| 
jbe/bsw@0
 | 
   503 function fraction.__lt(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   504   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
   505     return false
 | 
| 
jbe/bsw@0
 | 
   506   else
 | 
| 
jbe/bsw@0
 | 
   507     return value1.float < value2.float
 | 
| 
jbe/bsw@0
 | 
   508   end
 | 
| 
jbe/bsw@0
 | 
   509 end
 | 
| 
jbe/bsw@0
 | 
   510 
 | 
| 
jbe/bsw@0
 | 
   511 function fraction.__le(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   512   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
   513     return false
 | 
| 
jbe/bsw@0
 | 
   514   else
 | 
| 
jbe/bsw@0
 | 
   515     return value1.float <= value2.float
 | 
| 
jbe/bsw@0
 | 
   516   end
 | 
| 
jbe/bsw@0
 | 
   517 end
 | 
| 
jbe/bsw@0
 | 
   518 
 | 
| 
jbe/bsw@0
 | 
   519 function fraction:__unm()
 | 
| 
jbe/bsw@0
 | 
   520   return fraction(-self.numerator, self.denominator)
 | 
| 
jbe/bsw@0
 | 
   521 end
 | 
| 
jbe/bsw@0
 | 
   522 
 | 
| 
jbe/bsw@0
 | 
   523 do
 | 
| 
jbe/bsw@0
 | 
   524 
 | 
| 
jbe/bsw@0
 | 
   525   local function extract(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   526     local n1, d1, n2, d2
 | 
| 
jbe/bsw@0
 | 
   527     if getmetatable(value1) == fraction then
 | 
| 
jbe/bsw@0
 | 
   528       n1 = value1.numerator
 | 
| 
jbe/bsw@0
 | 
   529       d1 = value1.denominator
 | 
| 
jbe/bsw@0
 | 
   530     elseif type(value1) == "number" then
 | 
| 
jbe/bsw@0
 | 
   531       n1 = value1
 | 
| 
jbe/bsw@0
 | 
   532       d1 = 1
 | 
| 
jbe/bsw@0
 | 
   533     else
 | 
| 
jbe/bsw@0
 | 
   534       error("Left operand of operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
   535     end
 | 
| 
jbe/bsw@0
 | 
   536     if getmetatable(value2) == fraction then
 | 
| 
jbe/bsw@0
 | 
   537       n2 = value2.numerator
 | 
| 
jbe/bsw@0
 | 
   538       d2 = value2.denominator
 | 
| 
jbe/bsw@0
 | 
   539     elseif type(value2) == "number" then
 | 
| 
jbe/bsw@0
 | 
   540       n2 = value2
 | 
| 
jbe/bsw@0
 | 
   541       d2 = 1
 | 
| 
jbe/bsw@0
 | 
   542     else
 | 
| 
jbe/bsw@0
 | 
   543       error("Right operand of operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
   544     end
 | 
| 
jbe/bsw@0
 | 
   545     return n1, d1, n2, d2
 | 
| 
jbe/bsw@0
 | 
   546   end
 | 
| 
jbe/bsw@0
 | 
   547 
 | 
| 
jbe/bsw@0
 | 
   548   function fraction.__add(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   549     local n1, d1, n2, d2 = extract(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   550     return fraction(n1 * d2 + n2 * d1, d1 * d2)
 | 
| 
jbe/bsw@0
 | 
   551   end
 | 
| 
jbe/bsw@0
 | 
   552 
 | 
| 
jbe/bsw@0
 | 
   553   function fraction.__sub(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   554     local n1, d1, n2, d2 = extract(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   555     return fraction(n1 * d2 - n2 * d1, d1 * d2)
 | 
| 
jbe/bsw@0
 | 
   556   end
 | 
| 
jbe/bsw@0
 | 
   557 
 | 
| 
jbe/bsw@0
 | 
   558   function fraction.__mul(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   559     local n1, d1, n2, d2 = extract(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   560     return fraction(n1 * n2, d1 * d2)
 | 
| 
jbe/bsw@0
 | 
   561   end
 | 
| 
jbe/bsw@0
 | 
   562 
 | 
| 
jbe/bsw@0
 | 
   563   function fraction.__div(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   564     local n1, d1, n2, d2 = extract(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   565     return fraction(n1 * d2, d1 * n2)
 | 
| 
jbe/bsw@0
 | 
   566   end
 | 
| 
jbe/bsw@0
 | 
   567 
 | 
| 
jbe/bsw@0
 | 
   568   function fraction.__pow(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   569     local n1, d1, n2, d2 = extract(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   570     local n1_abs = math.abs(n1)
 | 
| 
jbe/bsw@0
 | 
   571     local d1_abs = math.abs(d1)
 | 
| 
jbe/bsw@0
 | 
   572     local n2_abs = math.abs(n2)
 | 
| 
jbe/bsw@0
 | 
   573     local d2_abs = math.abs(d2)
 | 
| 
jbe/bsw@0
 | 
   574     local numerator, denominator
 | 
| 
jbe/bsw@0
 | 
   575     if d2_abs == 1 then
 | 
| 
jbe/bsw@0
 | 
   576       numerator = n1_abs
 | 
| 
jbe/bsw@0
 | 
   577       denominator = d1_abs
 | 
| 
jbe/bsw@0
 | 
   578     else
 | 
| 
jbe/bsw@0
 | 
   579       numerator = 0
 | 
| 
jbe/bsw@0
 | 
   580       while true do
 | 
| 
jbe/bsw@0
 | 
   581         local t = numerator ^ d2_abs
 | 
| 
jbe/bsw@0
 | 
   582         if t == n1_abs then break end
 | 
| 
jbe/bsw@0
 | 
   583         if not (t < n1_abs) then return value1.float / value2.float end
 | 
| 
jbe/bsw@0
 | 
   584         numerator = numerator + 1
 | 
| 
jbe/bsw@0
 | 
   585       end
 | 
| 
jbe/bsw@0
 | 
   586       denominator = 1
 | 
| 
jbe/bsw@0
 | 
   587       while true do
 | 
| 
jbe/bsw@0
 | 
   588         local t = denominator ^ d2_abs
 | 
| 
jbe/bsw@0
 | 
   589         if t == d1_abs then break end
 | 
| 
jbe/bsw@0
 | 
   590         if not (t < d1_abs) then return value1.float / value2.float end
 | 
| 
jbe/bsw@0
 | 
   591         denominator = denominator + 1
 | 
| 
jbe/bsw@0
 | 
   592       end
 | 
| 
jbe/bsw@0
 | 
   593     end
 | 
| 
jbe/bsw@0
 | 
   594     if n1 < 0 then
 | 
| 
jbe/bsw@0
 | 
   595       if d2_abs % 2 == 1 then
 | 
| 
jbe/bsw@0
 | 
   596         numerator = -numerator
 | 
| 
jbe/bsw@0
 | 
   597       else
 | 
| 
jbe/bsw@0
 | 
   598         return fraction.invalid
 | 
| 
jbe/bsw@0
 | 
   599       end
 | 
| 
jbe/bsw@0
 | 
   600     end
 | 
| 
jbe/bsw@0
 | 
   601     if n2 < 0 then
 | 
| 
jbe/bsw@0
 | 
   602       numerator, denominator = denominator, numerator
 | 
| 
jbe/bsw@0
 | 
   603     end
 | 
| 
jbe/bsw@0
 | 
   604     return fraction(numerator ^ n2_abs, denominator ^ n2_abs)
 | 
| 
jbe/bsw@0
 | 
   605   end
 | 
| 
jbe/bsw@0
 | 
   606 
 | 
| 
jbe/bsw@0
 | 
   607 end
 | 
| 
jbe/bsw@0
 | 
   608 
 | 
| 
jbe/bsw@0
 | 
   609 
 | 
| 
jbe/bsw@0
 | 
   610 
 | 
| 
jbe/bsw@0
 | 
   611 ----------
 | 
| 
jbe/bsw@0
 | 
   612 -- date --
 | 
| 
jbe/bsw@0
 | 
   613 ----------
 | 
| 
jbe/bsw@0
 | 
   614 
 | 
| 
jbe/bsw@0
 | 
   615 date = create_new_type("date")
 | 
| 
jbe/bsw@0
 | 
   616 
 | 
| 
jbe/bsw@0
 | 
   617 do
 | 
| 
jbe/bsw@0
 | 
   618   local c1 = 365             -- days of a non-leap year
 | 
| 
jbe/bsw@0
 | 
   619   local c4 = 4 * c1 + 1      -- days of a full 4 year cycle
 | 
| 
jbe/bsw@0
 | 
   620   local c100 = 25 * c4 - 1   -- days of a full 100 year cycle
 | 
| 
jbe/bsw@0
 | 
   621   local c400 = 4 * c100 + 1  -- days of a full 400 year cycle
 | 
| 
jbe/bsw@0
 | 
   622   local get_month_offset     -- function returning days elapsed within
 | 
| 
jbe/bsw@0
 | 
   623                              -- the given year until the given month
 | 
| 
jbe/bsw@0
 | 
   624                              -- (exclusive the given month)
 | 
| 
jbe/bsw@0
 | 
   625   do
 | 
| 
jbe/bsw@0
 | 
   626     local normal_month_offsets = {}
 | 
| 
jbe/bsw@0
 | 
   627     local normal_month_lengths = {
 | 
| 
jbe/bsw@0
 | 
   628       31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
 | 
| 
jbe/bsw@0
 | 
   629     }
 | 
| 
jbe/bsw@0
 | 
   630     local sum = 0
 | 
| 
jbe/bsw@0
 | 
   631     for i = 1, 12 do
 | 
| 
jbe/bsw@0
 | 
   632       normal_month_offsets[i] = sum
 | 
| 
jbe/bsw@0
 | 
   633       sum = sum + normal_month_lengths[i]
 | 
| 
jbe/bsw@0
 | 
   634     end
 | 
| 
jbe/bsw@0
 | 
   635     function get_month_offset(year, month)
 | 
| 
jbe/bsw@0
 | 
   636       if
 | 
| 
jbe/bsw@0
 | 
   637         (((year % 4 == 0) and not (year % 100 == 0)) or (year % 400 == 0))
 | 
| 
jbe/bsw@0
 | 
   638         and month > 2
 | 
| 
jbe/bsw@0
 | 
   639       then
 | 
| 
jbe/bsw@0
 | 
   640         return normal_month_offsets[month] + 1
 | 
| 
jbe/bsw@0
 | 
   641       else
 | 
| 
jbe/bsw@0
 | 
   642         return normal_month_offsets[month]
 | 
| 
jbe/bsw@0
 | 
   643       end
 | 
| 
jbe/bsw@0
 | 
   644     end
 | 
| 
jbe/bsw@0
 | 
   645   end
 | 
| 
jbe/bsw@0
 | 
   646 
 | 
| 
jbe/bsw@0
 | 
   647   --[[--
 | 
| 
jbe/bsw@0
 | 
   648   jd =                  -- days from January 1st 1970
 | 
| 
jbe/bsw@0
 | 
   649   atom.date.ymd_to_jd(
 | 
| 
jbe/bsw@0
 | 
   650     year,               -- year
 | 
| 
jbe/bsw@0
 | 
   651     month,              -- month from 1 to 12
 | 
| 
jbe/bsw@0
 | 
   652     day                 -- day from 1 to 31
 | 
| 
jbe/bsw@0
 | 
   653   )
 | 
| 
jbe/bsw@0
 | 
   654 
 | 
| 
jbe/bsw@0
 | 
   655   This function calculates the days from January 1st 1970 for a given year, month and day.
 | 
| 
jbe/bsw@0
 | 
   656 
 | 
| 
jbe/bsw@0
 | 
   657   --]]--
 | 
| 
jbe/bsw@0
 | 
   658   local offset = 0
 | 
| 
jbe/bsw@0
 | 
   659   function date.ymd_to_jd(year, month, day)
 | 
| 
jbe/bsw@0
 | 
   660     assert(is_integer(year), "Invalid year specified.")
 | 
| 
jbe/bsw@0
 | 
   661     assert(is_integer(month), "Invalid month specified.")
 | 
| 
jbe/bsw@0
 | 
   662     assert(is_integer(day), "Invalid day specified.")
 | 
| 
jbe/bsw@0
 | 
   663     local calc_year = year - 1
 | 
| 
jbe/bsw@0
 | 
   664     local n400 = math.floor(calc_year / 400)
 | 
| 
jbe/bsw@0
 | 
   665     local r400 = calc_year % 400
 | 
| 
jbe/bsw@0
 | 
   666     local n100 = math.floor(r400 / 100)
 | 
| 
jbe/bsw@0
 | 
   667     local r100 = r400 % 100
 | 
| 
jbe/bsw@0
 | 
   668     local n4 = math.floor(r100 / 4)
 | 
| 
jbe/bsw@0
 | 
   669     local n1 = r100 % 4
 | 
| 
jbe/bsw@0
 | 
   670     local jd = (
 | 
| 
jbe/bsw@0
 | 
   671       c400 * n400 + c100 * n100 + c4 * n4 + c1 * n1 +
 | 
| 
jbe/bsw@0
 | 
   672       get_month_offset(year, month) + (day - 1)
 | 
| 
jbe/bsw@0
 | 
   673     )
 | 
| 
jbe/bsw@0
 | 
   674     return jd - offset
 | 
| 
jbe/bsw@0
 | 
   675   end
 | 
| 
jbe/bsw@0
 | 
   676   offset = date.ymd_to_jd(1970, 1, 1)
 | 
| 
jbe/bsw@0
 | 
   677   --//--
 | 
| 
jbe/bsw@0
 | 
   678 
 | 
| 
jbe/bsw@0
 | 
   679   --[[--
 | 
| 
jbe/bsw@0
 | 
   680   year,                 -- year
 | 
| 
jbe/bsw@0
 | 
   681   month,                -- month from 1 to 12
 | 
| 
jbe/bsw@0
 | 
   682   day =                 -- day from 1 to 31
 | 
| 
jbe/bsw@0
 | 
   683   atom.date.jd_to_ymd(
 | 
| 
jbe/bsw@0
 | 
   684     jd,                 -- days from January 1st 1970
 | 
| 
jbe/bsw@0
 | 
   685   )
 | 
| 
jbe/bsw@0
 | 
   686 
 | 
| 
jbe/bsw@0
 | 
   687   Given the days from January 1st 1970 this function returns year, month and day.
 | 
| 
jbe/bsw@0
 | 
   688 
 | 
| 
jbe/bsw@0
 | 
   689   --]]--
 | 
| 
jbe/bsw@0
 | 
   690   function date.jd_to_ymd(jd)
 | 
| 
jbe/bsw@0
 | 
   691     assert(is_integer(jd), "Invalid julian date specified.")
 | 
| 
jbe/bsw@0
 | 
   692     local calc_jd = jd + offset
 | 
| 
jbe/bsw@0
 | 
   693     assert(is_integer(calc_jd), "Julian date is out of range.")
 | 
| 
jbe/bsw@0
 | 
   694     local n400 = math.floor(calc_jd / c400)
 | 
| 
jbe/bsw@0
 | 
   695     local r400 = calc_jd % c400
 | 
| 
jbe/bsw@0
 | 
   696     local n100 = math.floor(r400 / c100)
 | 
| 
jbe/bsw@0
 | 
   697     local r100 = r400 % c100
 | 
| 
jbe/bsw@0
 | 
   698     if n100 == 4 then n100, r100 = 3, c100 end
 | 
| 
jbe/bsw@0
 | 
   699     local n4 = math.floor(r100 / c4)
 | 
| 
jbe/bsw@0
 | 
   700     local r4 = r100 % c4
 | 
| 
jbe/bsw@0
 | 
   701     local n1 = math.floor(r4 / c1)
 | 
| 
jbe/bsw@0
 | 
   702     local r1 = r4 % c1
 | 
| 
jbe/bsw@0
 | 
   703     if n1 == 4 then n1, r1 = 3, c1 end
 | 
| 
jbe/bsw@0
 | 
   704     local year = 1 + 400 * n400 + 100 * n100 + 4 * n4 + n1
 | 
| 
jbe/bsw@0
 | 
   705     local month = 1 + math.floor(r1 / 31)
 | 
| 
jbe/bsw@0
 | 
   706     local month_offset = get_month_offset(year, month)
 | 
| 
jbe/bsw@0
 | 
   707     if month < 12 then
 | 
| 
jbe/bsw@0
 | 
   708       local next_month_offset = get_month_offset(year, month + 1)
 | 
| 
jbe/bsw@0
 | 
   709       if r1 >= next_month_offset then
 | 
| 
jbe/bsw@0
 | 
   710         month = month + 1
 | 
| 
jbe/bsw@0
 | 
   711         month_offset = next_month_offset
 | 
| 
jbe/bsw@0
 | 
   712       end
 | 
| 
jbe/bsw@0
 | 
   713     end
 | 
| 
jbe/bsw@0
 | 
   714     local day = 1 + r1 - month_offset
 | 
| 
jbe/bsw@0
 | 
   715     return year, month, day
 | 
| 
jbe/bsw@0
 | 
   716   end
 | 
| 
jbe/bsw@0
 | 
   717   --//--
 | 
| 
jbe/bsw@0
 | 
   718 end
 | 
| 
jbe/bsw@0
 | 
   719 
 | 
| 
jbe/bsw@0
 | 
   720 --[[--
 | 
| 
jbe/bsw@0
 | 
   721 atom.date.invalid
 | 
| 
jbe/bsw@0
 | 
   722 
 | 
| 
jbe/bsw@0
 | 
   723 Value representing an invalid date.
 | 
| 
jbe/bsw@0
 | 
   724 
 | 
| 
jbe/bsw@0
 | 
   725 --]]--
 | 
| 
jbe/bsw@0
 | 
   726 date.invalid = date:_create{
 | 
| 
jbe/bsw@0
 | 
   727   jd = not_a_number,
 | 
| 
jbe/bsw@0
 | 
   728   year = not_a_number, month = not_a_number, day = not_a_number,
 | 
| 
jbe/bsw@0
 | 
   729   invalid = true
 | 
| 
jbe/bsw@0
 | 
   730 }
 | 
| 
jbe/bsw@0
 | 
   731 --//--
 | 
| 
jbe/bsw@0
 | 
   732 
 | 
| 
jbe/bsw@0
 | 
   733 --[[--
 | 
| 
jbe/bsw@0
 | 
   734 d =                             -- date based on the given data
 | 
| 
jbe/bsw@0
 | 
   735 atom.date:new{
 | 
| 
jbe/bsw@0
 | 
   736   jd           = jd,            -- days since January 1st 1970
 | 
| 
jbe/bsw@0
 | 
   737   year         = year,          -- year
 | 
| 
jbe/bsw@0
 | 
   738   month        = month,         -- month from 1 to 12
 | 
| 
jbe/bsw@0
 | 
   739   day          = day,           -- day from 1 to 31
 | 
| 
jbe/bsw@0
 | 
   740   iso_weekyear = iso_weekyear,  -- year according to ISO 8601
 | 
| 
jbe/bsw@0
 | 
   741   iso_week     = iso_week,      -- week number according to ISO 8601
 | 
| 
jbe/bsw@0
 | 
   742   iso_weekday  = iso_weekday,   -- day of week from 1 for monday to 7 for sunday
 | 
| 
jbe/bsw@0
 | 
   743   us_weekyear  = us_weekyear,   -- year
 | 
| 
jbe/bsw@0
 | 
   744   us_week      = us_week,       -- week number according to US style counting
 | 
| 
jbe/bsw@0
 | 
   745   us_weekday   = us_weekday     -- day of week from 1 for sunday to 7 for saturday
 | 
| 
jbe/bsw@0
 | 
   746 }
 | 
| 
jbe/bsw@0
 | 
   747 
 | 
| 
jbe/bsw@0
 | 
   748 This method returns a new date value, based on given data.
 | 
| 
jbe/bsw@0
 | 
   749 
 | 
| 
jbe/bsw@0
 | 
   750 --]]--
 | 
| 
jbe/bsw@0
 | 
   751 function date:new(args)
 | 
| 
jbe/bsw@0
 | 
   752   local args = args
 | 
| 
jbe/bsw@0
 | 
   753   if type(args) == "number" then args = { jd = args } end
 | 
| 
jbe/bsw@0
 | 
   754   if type(args) == "table" then
 | 
| 
jbe/bsw@0
 | 
   755     local year, month, day = args.year, args.month, args.day
 | 
| 
jbe/bsw@0
 | 
   756     local jd = args.jd
 | 
| 
jbe/bsw@0
 | 
   757     local iso_weekyear = args.iso_weekyear
 | 
| 
jbe/bsw@0
 | 
   758     local iso_week     = args.iso_week
 | 
| 
jbe/bsw@0
 | 
   759     local iso_weekday  = args.iso_weekday
 | 
| 
jbe/bsw@0
 | 
   760     local us_week      = args.us_week
 | 
| 
jbe/bsw@0
 | 
   761     local us_weekday   = args.us_weekday
 | 
| 
jbe/bsw@0
 | 
   762     if
 | 
| 
jbe/bsw@0
 | 
   763       type(year)  == "number" and
 | 
| 
jbe/bsw@0
 | 
   764       type(month) == "number" and
 | 
| 
jbe/bsw@0
 | 
   765       type(day)   == "number"
 | 
| 
jbe/bsw@0
 | 
   766     then
 | 
| 
jbe/bsw@0
 | 
   767       if
 | 
| 
jbe/bsw@0
 | 
   768         is_integer(year)  and year >= 1  and year <= 9999 and
 | 
| 
jbe/bsw@0
 | 
   769         is_integer(month) and month >= 1 and month <= 12  and
 | 
| 
jbe/bsw@0
 | 
   770         is_integer(day)   and day >= 1   and day <= 31
 | 
| 
jbe/bsw@0
 | 
   771       then
 | 
| 
jbe/bsw@0
 | 
   772         return date:_create{
 | 
| 
jbe/bsw@0
 | 
   773           jd = date.ymd_to_jd(year, month, day),
 | 
| 
jbe/bsw@0
 | 
   774           year = year, month = month, day = day
 | 
| 
jbe/bsw@0
 | 
   775         }
 | 
| 
jbe/bsw@0
 | 
   776       else
 | 
| 
jbe/bsw@0
 | 
   777         return date.invalid
 | 
| 
jbe/bsw@0
 | 
   778       end
 | 
| 
jbe/bsw@0
 | 
   779     elseif type(jd) == "number" then
 | 
| 
jbe/bsw@0
 | 
   780       if is_integer(jd) and jd >= -719162 and jd <= 2932896 then
 | 
| 
jbe/bsw@0
 | 
   781         local year, month, day = date.jd_to_ymd(jd)
 | 
| 
jbe/bsw@0
 | 
   782         return date:_create{
 | 
| 
jbe/bsw@0
 | 
   783           jd = jd, year = year, month = month, day = day
 | 
| 
jbe/bsw@0
 | 
   784         }
 | 
| 
jbe/bsw@0
 | 
   785       else
 | 
| 
jbe/bsw@0
 | 
   786         return date.invalid
 | 
| 
jbe/bsw@0
 | 
   787       end
 | 
| 
jbe/bsw@0
 | 
   788     elseif
 | 
| 
jbe/bsw@0
 | 
   789       type(year)        == "number" and not iso_weekyear and
 | 
| 
jbe/bsw@0
 | 
   790       type(iso_week)    == "number" and
 | 
| 
jbe/bsw@0
 | 
   791       type(iso_weekday) == "number"
 | 
| 
jbe/bsw@0
 | 
   792     then
 | 
| 
jbe/bsw@0
 | 
   793       if
 | 
| 
jbe/bsw@0
 | 
   794         is_integer(year) and
 | 
| 
jbe/bsw@0
 | 
   795         is_integer(iso_week)     and iso_week >= 0 and iso_week <= 53 and
 | 
| 
jbe/bsw@0
 | 
   796         is_integer(iso_weekday)  and iso_weekday >= 1 and iso_weekday <= 7
 | 
| 
jbe/bsw@0
 | 
   797       then
 | 
| 
jbe/bsw@0
 | 
   798         local jan4 = date{ year = year, month = 1, day = 4 }
 | 
| 
jbe/bsw@0
 | 
   799         local reference = jan4 - jan4.iso_weekday - 7  -- Sun. of week -1
 | 
| 
jbe/bsw@0
 | 
   800         return date(reference + 7 * iso_week + iso_weekday)
 | 
| 
jbe/bsw@0
 | 
   801       else
 | 
| 
jbe/bsw@0
 | 
   802         return date.invalid
 | 
| 
jbe/bsw@0
 | 
   803       end
 | 
| 
jbe/bsw@0
 | 
   804     elseif
 | 
| 
jbe/bsw@0
 | 
   805       type(iso_weekyear) == "number" and not year and
 | 
| 
jbe/bsw@0
 | 
   806       type(iso_week)     == "number" and
 | 
| 
jbe/bsw@0
 | 
   807       type(iso_weekday)  == "number"
 | 
| 
jbe/bsw@0
 | 
   808     then
 | 
| 
jbe/bsw@0
 | 
   809       if
 | 
| 
jbe/bsw@0
 | 
   810         is_integer(iso_weekyear) and
 | 
| 
jbe/bsw@0
 | 
   811         is_integer(iso_week)     and iso_week >= 0 and iso_week <= 53 and
 | 
| 
jbe/bsw@0
 | 
   812         is_integer(iso_weekday)  and iso_weekday >= 1 and iso_weekday <= 7
 | 
| 
jbe/bsw@0
 | 
   813       then
 | 
| 
jbe/bsw@0
 | 
   814         local guessed = date{
 | 
| 
jbe/bsw@0
 | 
   815           year        = iso_weekyear,
 | 
| 
jbe/bsw@0
 | 
   816           iso_week    = iso_week,
 | 
| 
jbe/bsw@0
 | 
   817           iso_weekday = iso_weekday
 | 
| 
jbe/bsw@0
 | 
   818         }
 | 
| 
jbe/bsw@0
 | 
   819         if guessed.invalid or guessed.iso_weekyear == iso_weekyear then
 | 
| 
jbe/bsw@0
 | 
   820           return guessed
 | 
| 
jbe/bsw@0
 | 
   821         else
 | 
| 
jbe/bsw@0
 | 
   822           local year
 | 
| 
jbe/bsw@0
 | 
   823           if iso_week <= 1 then
 | 
| 
jbe/bsw@0
 | 
   824             year = iso_weekyear - 1
 | 
| 
jbe/bsw@0
 | 
   825           elseif iso_week >= 52 then
 | 
| 
jbe/bsw@0
 | 
   826             year = iso_weekyear + 1
 | 
| 
jbe/bsw@0
 | 
   827           else
 | 
| 
jbe/bsw@0
 | 
   828             error("Internal error in ISO week computation occured.")
 | 
| 
jbe/bsw@0
 | 
   829           end
 | 
| 
jbe/bsw@0
 | 
   830           return date{
 | 
| 
jbe/bsw@0
 | 
   831             year = year, iso_week = iso_week, iso_weekday = iso_weekday
 | 
| 
jbe/bsw@0
 | 
   832           }
 | 
| 
jbe/bsw@0
 | 
   833         end
 | 
| 
jbe/bsw@0
 | 
   834       else
 | 
| 
jbe/bsw@0
 | 
   835         return date.invalid
 | 
| 
jbe/bsw@0
 | 
   836       end
 | 
| 
jbe/bsw@0
 | 
   837     elseif
 | 
| 
jbe/bsw@0
 | 
   838       type(year) == "number" and
 | 
| 
jbe/bsw@0
 | 
   839       type(us_week)     == "number" and
 | 
| 
jbe/bsw@0
 | 
   840       type(us_weekday)  == "number"
 | 
| 
jbe/bsw@0
 | 
   841     then
 | 
| 
jbe/bsw@0
 | 
   842       if
 | 
| 
jbe/bsw@0
 | 
   843         is_integer(year) and
 | 
| 
jbe/bsw@0
 | 
   844         is_integer(us_week)     and us_week >= 0    and us_week <= 54   and
 | 
| 
jbe/bsw@0
 | 
   845         is_integer(us_weekday)  and us_weekday >= 1 and us_weekday <= 7
 | 
| 
jbe/bsw@0
 | 
   846       then
 | 
| 
jbe/bsw@0
 | 
   847         local jan1 = date{ year = year, month = 1, day = 1 }
 | 
| 
jbe/bsw@0
 | 
   848         local reference = jan1 - jan1.us_weekday - 7  -- Sat. of week -1
 | 
| 
jbe/bsw@0
 | 
   849         return date(reference + 7 * us_week + us_weekday)
 | 
| 
jbe/bsw@0
 | 
   850       else
 | 
| 
jbe/bsw@0
 | 
   851         return date.invalid
 | 
| 
jbe/bsw@0
 | 
   852       end
 | 
| 
jbe/bsw@0
 | 
   853     end
 | 
| 
jbe/bsw@0
 | 
   854   end
 | 
| 
jbe/bsw@0
 | 
   855   error("Illegal arguments passed to date constructor.")
 | 
| 
jbe/bsw@0
 | 
   856 end
 | 
| 
jbe/bsw@0
 | 
   857 --//--
 | 
| 
jbe/bsw@0
 | 
   858 
 | 
| 
jbe/bsw@0
 | 
   859 --[[--
 | 
| 
jbe/bsw@0
 | 
   860 atom.date:get_current()
 | 
| 
jbe/bsw@0
 | 
   861 
 | 
| 
jbe/bsw@0
 | 
   862 This function returns today's date.
 | 
| 
jbe/bsw@0
 | 
   863 
 | 
| 
jbe/bsw@0
 | 
   864 --]]--
 | 
| 
jbe/bsw@0
 | 
   865 function date:get_current()
 | 
| 
jbe/bsw@0
 | 
   866   local now = os.date("*t")
 | 
| 
jbe/bsw@0
 | 
   867   return date{
 | 
| 
jbe/bsw@0
 | 
   868     year = now.year, month = now.month, day = now.day
 | 
| 
jbe/bsw@0
 | 
   869   }
 | 
| 
jbe/bsw@0
 | 
   870 end
 | 
| 
jbe/bsw@0
 | 
   871 --//--
 | 
| 
jbe/bsw@0
 | 
   872 
 | 
| 
jbe/bsw@0
 | 
   873 --[[--
 | 
| 
jbe/bsw@0
 | 
   874 date =           -- date represented by the string
 | 
| 
jbe/bsw@0
 | 
   875 atom.date:load(
 | 
| 
jbe/bsw@0
 | 
   876   string         -- string representing a date
 | 
| 
jbe/bsw@0
 | 
   877 )
 | 
| 
jbe/bsw@0
 | 
   878 
 | 
| 
jbe/bsw@0
 | 
   879 This method returns a date represented by the given string.
 | 
| 
jbe/bsw@0
 | 
   880 
 | 
| 
jbe/bsw@0
 | 
   881 --]]--
 | 
| 
jbe/bsw@0
 | 
   882 function date:load(str)
 | 
| 
jbe@1
 | 
   883   if str == nil or str == "" then
 | 
| 
jbe/bsw@0
 | 
   884     return nil
 | 
| 
jbe@1
 | 
   885   elseif type(str) ~= "string" then
 | 
| 
jbe@1
 | 
   886     error("String expected")
 | 
| 
jbe/bsw@0
 | 
   887   else
 | 
| 
jbe/bsw@0
 | 
   888     local year, month, day = string.match(
 | 
| 
jbe/bsw@0
 | 
   889       str, "^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"
 | 
| 
jbe/bsw@0
 | 
   890     )
 | 
| 
jbe/bsw@0
 | 
   891     if year then
 | 
| 
jbe/bsw@0
 | 
   892       return date{
 | 
| 
jbe/bsw@0
 | 
   893         year = tonumber(year),
 | 
| 
jbe/bsw@0
 | 
   894         month = tonumber(month),
 | 
| 
jbe/bsw@0
 | 
   895         day = tonumber(day)
 | 
| 
jbe/bsw@0
 | 
   896       }
 | 
| 
jbe/bsw@0
 | 
   897     else
 | 
| 
jbe/bsw@0
 | 
   898       return date.invalid
 | 
| 
jbe/bsw@0
 | 
   899     end
 | 
| 
jbe/bsw@0
 | 
   900   end
 | 
| 
jbe/bsw@0
 | 
   901 end
 | 
| 
jbe/bsw@0
 | 
   902 --//--
 | 
| 
jbe/bsw@0
 | 
   903 
 | 
| 
jbe/bsw@0
 | 
   904 function date:__tostring()
 | 
| 
jbe/bsw@0
 | 
   905   if self.invalid then
 | 
| 
jbe/bsw@0
 | 
   906     return "invalid_date"
 | 
| 
jbe/bsw@0
 | 
   907   else
 | 
| 
jbe/bsw@0
 | 
   908     return string.format(
 | 
| 
jbe/bsw@0
 | 
   909       "%04i-%02i-%02i", self.year, self.month, self.day
 | 
| 
jbe/bsw@0
 | 
   910     )
 | 
| 
jbe/bsw@0
 | 
   911   end
 | 
| 
jbe/bsw@0
 | 
   912 end
 | 
| 
jbe/bsw@0
 | 
   913 
 | 
| 
jbe/bsw@0
 | 
   914 function date.getters:midnight()
 | 
| 
jbe/bsw@0
 | 
   915   return time{ year = self.year, month = self.month, day = self.day }
 | 
| 
jbe/bsw@0
 | 
   916 end
 | 
| 
jbe/bsw@0
 | 
   917 
 | 
| 
jbe/bsw@0
 | 
   918 function date.getters:midday()
 | 
| 
jbe/bsw@0
 | 
   919   return time{
 | 
| 
jbe/bsw@0
 | 
   920     year = self.year, month = self.month, day = self.day,
 | 
| 
jbe/bsw@0
 | 
   921     hour = 12
 | 
| 
jbe/bsw@0
 | 
   922   }
 | 
| 
jbe/bsw@0
 | 
   923 end
 | 
| 
jbe/bsw@0
 | 
   924 
 | 
| 
jbe/bsw@0
 | 
   925 function date.getters:iso_weekday()  -- 1 = Monday
 | 
| 
jbe/bsw@0
 | 
   926   return (self.jd + 3) % 7 + 1
 | 
| 
jbe/bsw@0
 | 
   927 end
 | 
| 
jbe/bsw@0
 | 
   928 
 | 
| 
jbe/bsw@0
 | 
   929 function date.getters:us_weekday()  -- 1 = Sunday
 | 
| 
jbe/bsw@0
 | 
   930   return (self.jd + 4) % 7 + 1
 | 
| 
jbe/bsw@0
 | 
   931 end
 | 
| 
jbe/bsw@0
 | 
   932 
 | 
| 
jbe/bsw@0
 | 
   933 function date.getters:iso_weekyear()  -- ISO week-numbering year
 | 
| 
jbe/bsw@0
 | 
   934   local year, month, day = self.year, self.month, self.day
 | 
| 
jbe/bsw@0
 | 
   935   local iso_weekday      = self.iso_weekday
 | 
| 
jbe/bsw@0
 | 
   936   if month == 1 then
 | 
| 
jbe/bsw@0
 | 
   937     if
 | 
| 
jbe/bsw@0
 | 
   938       (day == 3 and iso_weekday == 7) or
 | 
| 
jbe/bsw@0
 | 
   939       (day == 2 and iso_weekday >= 6) or
 | 
| 
jbe/bsw@0
 | 
   940       (day == 1 and iso_weekday >= 5)
 | 
| 
jbe/bsw@0
 | 
   941     then
 | 
| 
jbe/bsw@0
 | 
   942       return year - 1
 | 
| 
jbe/bsw@0
 | 
   943     end
 | 
| 
jbe/bsw@0
 | 
   944   elseif month == 12 then
 | 
| 
jbe/bsw@0
 | 
   945     if
 | 
| 
jbe/bsw@0
 | 
   946       (day == 29 and iso_weekday == 1) or
 | 
| 
jbe/bsw@0
 | 
   947       (day == 30 and iso_weekday <= 2) or
 | 
| 
jbe/bsw@0
 | 
   948       (day == 31 and iso_weekday <= 3)
 | 
| 
jbe/bsw@0
 | 
   949     then
 | 
| 
jbe/bsw@0
 | 
   950       return year + 1
 | 
| 
jbe/bsw@0
 | 
   951     end
 | 
| 
jbe/bsw@0
 | 
   952   end
 | 
| 
jbe/bsw@0
 | 
   953   return year
 | 
| 
jbe/bsw@0
 | 
   954 end
 | 
| 
jbe/bsw@0
 | 
   955 
 | 
| 
jbe/bsw@0
 | 
   956 function date.getters:iso_week()
 | 
| 
jbe/bsw@0
 | 
   957   local jan4 = date{ year = self.iso_weekyear, month = 1, day = 4 }
 | 
| 
jbe/bsw@0
 | 
   958   local reference = jan4.jd - jan4.iso_weekday - 6  -- monday of week 0
 | 
| 
jbe/bsw@0
 | 
   959   return math.floor((self.jd - reference) / 7)
 | 
| 
jbe/bsw@0
 | 
   960 end
 | 
| 
jbe/bsw@0
 | 
   961 
 | 
| 
jbe/bsw@0
 | 
   962 function date.getters:us_week()
 | 
| 
jbe/bsw@0
 | 
   963   local jan1 = date{ year = self.year, month = 1, day = 1 }
 | 
| 
jbe/bsw@0
 | 
   964   local reference = jan1.jd - jan1.us_weekday - 6  -- sunday of week 0
 | 
| 
jbe/bsw@0
 | 
   965   return math.floor((self.jd - reference) / 7)
 | 
| 
jbe/bsw@0
 | 
   966 end
 | 
| 
jbe/bsw@0
 | 
   967 
 | 
| 
jbe/bsw@0
 | 
   968 function date.__eq(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   969   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
   970     return false
 | 
| 
jbe/bsw@0
 | 
   971   else
 | 
| 
jbe/bsw@0
 | 
   972     return value1.jd == value2.jd
 | 
| 
jbe/bsw@0
 | 
   973   end
 | 
| 
jbe/bsw@0
 | 
   974 end
 | 
| 
jbe/bsw@0
 | 
   975 
 | 
| 
jbe/bsw@0
 | 
   976 function date.__lt(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   977   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
   978     return false
 | 
| 
jbe/bsw@0
 | 
   979   else
 | 
| 
jbe/bsw@0
 | 
   980     return value1.jd < value2.jd
 | 
| 
jbe/bsw@0
 | 
   981   end
 | 
| 
jbe/bsw@0
 | 
   982 end
 | 
| 
jbe/bsw@0
 | 
   983 
 | 
| 
jbe/bsw@0
 | 
   984 function date.__le(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   985   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
   986     return false
 | 
| 
jbe/bsw@0
 | 
   987   else
 | 
| 
jbe/bsw@0
 | 
   988     return value1.jd <= value2.jd
 | 
| 
jbe/bsw@0
 | 
   989   end
 | 
| 
jbe/bsw@0
 | 
   990 end
 | 
| 
jbe/bsw@0
 | 
   991 
 | 
| 
jbe/bsw@0
 | 
   992 function date.__add(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   993   if getmetatable(value1) == date then
 | 
| 
jbe/bsw@0
 | 
   994     if getmetatable(value2) == date then
 | 
| 
jbe/bsw@0
 | 
   995       error("Can not add two dates.")
 | 
| 
jbe/bsw@0
 | 
   996     elseif type(value2) == "number" then
 | 
| 
jbe/bsw@0
 | 
   997       return date(value1.jd + value2)
 | 
| 
jbe/bsw@0
 | 
   998     else
 | 
| 
jbe/bsw@0
 | 
   999       error("Right operand of '+' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1000     end
 | 
| 
jbe/bsw@0
 | 
  1001   elseif type(value1) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1002     if getmetatable(value2) == date then
 | 
| 
jbe/bsw@0
 | 
  1003       return date(value1 + value2.jd)
 | 
| 
jbe/bsw@0
 | 
  1004     else
 | 
| 
jbe/bsw@0
 | 
  1005       error("Assertion failed")
 | 
| 
jbe/bsw@0
 | 
  1006     end
 | 
| 
jbe/bsw@0
 | 
  1007   else
 | 
| 
jbe/bsw@0
 | 
  1008     error("Left operand of '+' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1009   end
 | 
| 
jbe/bsw@0
 | 
  1010 end
 | 
| 
jbe/bsw@0
 | 
  1011 
 | 
| 
jbe/bsw@0
 | 
  1012 function date.__sub(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1013   if not getmetatable(value1) == date then
 | 
| 
jbe/bsw@0
 | 
  1014     error("Left operand of '-' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1015   end
 | 
| 
jbe/bsw@0
 | 
  1016   if getmetatable(value2) == date then
 | 
| 
jbe/bsw@0
 | 
  1017     return value1.jd - value2.jd  -- TODO: transform to interval
 | 
| 
jbe/bsw@0
 | 
  1018   elseif type(value2) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1019     return date(value1.jd - value2)
 | 
| 
jbe/bsw@0
 | 
  1020   else
 | 
| 
jbe/bsw@0
 | 
  1021     error("Right operand of '-' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1022   end
 | 
| 
jbe/bsw@0
 | 
  1023 end
 | 
| 
jbe/bsw@0
 | 
  1024 
 | 
| 
jbe/bsw@0
 | 
  1025 
 | 
| 
jbe/bsw@0
 | 
  1026 
 | 
| 
jbe/bsw@0
 | 
  1027 ---------------
 | 
| 
jbe/bsw@0
 | 
  1028 -- timestamp --
 | 
| 
jbe/bsw@0
 | 
  1029 ---------------
 | 
| 
jbe/bsw@0
 | 
  1030 
 | 
| 
jbe/bsw@0
 | 
  1031 timestamp = create_new_type("timestamp")
 | 
| 
jbe/bsw@0
 | 
  1032 
 | 
| 
jbe/bsw@0
 | 
  1033 --[[--
 | 
| 
jbe/bsw@0
 | 
  1034 tsec =                          -- seconds since January 1st 1970 00:00
 | 
| 
jbe/bsw@0
 | 
  1035 atom.timestamp.ymdhms_to_tsec(
 | 
| 
jbe/bsw@0
 | 
  1036   year,                         -- year
 | 
| 
jbe/bsw@0
 | 
  1037   month,                        -- month from 1 to 12
 | 
| 
jbe/bsw@0
 | 
  1038   day,                          -- day from 1 to 31
 | 
| 
jbe/bsw@0
 | 
  1039   hour,                         -- hour from 0 to 23
 | 
| 
jbe/bsw@0
 | 
  1040   minute,                       -- minute from 0 to 59
 | 
| 
jbe/bsw@0
 | 
  1041   second                        -- second from 0 to 59
 | 
| 
jbe/bsw@0
 | 
  1042 )
 | 
| 
jbe/bsw@0
 | 
  1043 
 | 
| 
jbe/bsw@0
 | 
  1044 Given the year, month, day, hour, minute and second, this function returns the number of seconds since January 1st 1970 00:00.
 | 
| 
jbe/bsw@0
 | 
  1045 
 | 
| 
jbe/bsw@0
 | 
  1046 --]]--
 | 
| 
jbe/bsw@0
 | 
  1047 function timestamp.ymdhms_to_tsec(year, month, day, hour, minute, second)
 | 
| 
jbe/bsw@0
 | 
  1048   return
 | 
| 
jbe/bsw@0
 | 
  1049     86400 * date.ymd_to_jd(year, month, day) +
 | 
| 
jbe/bsw@0
 | 
  1050     3600 * hour + 60 * minute + second
 | 
| 
jbe/bsw@0
 | 
  1051 end
 | 
| 
jbe/bsw@0
 | 
  1052 --//--
 | 
| 
jbe/bsw@0
 | 
  1053 
 | 
| 
jbe/bsw@0
 | 
  1054 --[[--
 | 
| 
jbe/bsw@0
 | 
  1055 year,                      -- year
 | 
| 
jbe/bsw@0
 | 
  1056 month,                     -- month from 1 to 12
 | 
| 
jbe/bsw@0
 | 
  1057 day,                       -- day from 1 to 31
 | 
| 
jbe/bsw@0
 | 
  1058 hour,                      -- hour from 0 to 23
 | 
| 
jbe/bsw@0
 | 
  1059 minute,                    -- minute from 0 to 59
 | 
| 
jbe/bsw@0
 | 
  1060 second =                   -- second from 0 to 59
 | 
| 
jbe/bsw@0
 | 
  1061 atom.timestamp.tsec_to_ymdhms(
 | 
| 
jbe/bsw@0
 | 
  1062   tsec                     -- seconds since January 1st 1970 00:00
 | 
| 
jbe/bsw@0
 | 
  1063 )
 | 
| 
jbe/bsw@0
 | 
  1064 
 | 
| 
jbe/bsw@0
 | 
  1065 Given the seconds since January 1st 1970 00:00, this function returns the year, month, day, hour, minute and second.
 | 
| 
jbe/bsw@0
 | 
  1066 
 | 
| 
jbe/bsw@0
 | 
  1067 --]]--
 | 
| 
jbe/bsw@0
 | 
  1068 function timestamp.tsec_to_ymdhms(tsec)
 | 
| 
jbe/bsw@0
 | 
  1069   local jd   = math.floor(tsec / 86400)
 | 
| 
jbe/bsw@0
 | 
  1070   local dsec = tsec % 86400
 | 
| 
jbe/bsw@0
 | 
  1071   local year, month, day = date.jd_to_ymd(jd)
 | 
| 
jbe/bsw@0
 | 
  1072   local hour   = math.floor(dsec / 3600)
 | 
| 
jbe/bsw@0
 | 
  1073   local minute = math.floor((dsec % 3600) / 60)
 | 
| 
jbe/bsw@0
 | 
  1074   local second = dsec % 60
 | 
| 
jbe/bsw@0
 | 
  1075   return year, month, day, hour, minute, second
 | 
| 
jbe/bsw@0
 | 
  1076 end
 | 
| 
jbe/bsw@0
 | 
  1077 --//--
 | 
| 
jbe/bsw@0
 | 
  1078 
 | 
| 
jbe/bsw@0
 | 
  1079 --[[--
 | 
| 
jbe/bsw@0
 | 
  1080 timestamp.invalid
 | 
| 
jbe/bsw@0
 | 
  1081 
 | 
| 
jbe/bsw@0
 | 
  1082 Value representing an invalid timestamp.
 | 
| 
jbe/bsw@0
 | 
  1083 
 | 
| 
jbe/bsw@0
 | 
  1084 --]]--
 | 
| 
jbe/bsw@0
 | 
  1085 timestamp.invalid = timestamp:_create{
 | 
| 
jbe/bsw@0
 | 
  1086   tsec = not_a_number,
 | 
| 
jbe/bsw@0
 | 
  1087   year = not_a_number, month = not_a_number, day = not_a_number,
 | 
| 
jbe/bsw@0
 | 
  1088   hour = not_a_number, minute = not_a_number, second = not_a_number,
 | 
| 
jbe/bsw@0
 | 
  1089   invalid = true
 | 
| 
jbe/bsw@0
 | 
  1090 }
 | 
| 
jbe/bsw@0
 | 
  1091 --//--
 | 
| 
jbe/bsw@0
 | 
  1092 
 | 
| 
jbe/bsw@0
 | 
  1093 --[[--
 | 
| 
jbe/bsw@0
 | 
  1094 ts =                 -- timestamp based on given data
 | 
| 
jbe/bsw@0
 | 
  1095 atom.timestamp:new{
 | 
| 
jbe/bsw@0
 | 
  1096   tsec   = tsec,     -- seconds since January 1st 1970 00:00
 | 
| 
jbe/bsw@0
 | 
  1097   year   = year,     -- year
 | 
| 
jbe/bsw@0
 | 
  1098   month  = month,    -- month from 1 to 12
 | 
| 
jbe/bsw@0
 | 
  1099   day    = day,      -- day from 1 to 31
 | 
| 
jbe/bsw@0
 | 
  1100   hour   = hour,     -- hour from 0 to 23
 | 
| 
jbe/bsw@0
 | 
  1101   minute = minute,   -- minute from 0 to 59
 | 
| 
jbe/bsw@0
 | 
  1102   second = second    -- second from 0 to 59
 | 
| 
jbe/bsw@0
 | 
  1103 }
 | 
| 
jbe/bsw@0
 | 
  1104 
 | 
| 
jbe/bsw@0
 | 
  1105 This method returns a new timestamp value, based on given data.
 | 
| 
jbe/bsw@0
 | 
  1106 
 | 
| 
jbe/bsw@0
 | 
  1107 --]]--
 | 
| 
jbe/bsw@0
 | 
  1108 function timestamp:new(args)
 | 
| 
jbe/bsw@0
 | 
  1109   local args = args
 | 
| 
jbe/bsw@0
 | 
  1110   if type(args) == "number" then args = { tsec = args } end
 | 
| 
jbe/bsw@0
 | 
  1111   if type(args) == "table" then
 | 
| 
jbe/bsw@0
 | 
  1112     if not args.second then
 | 
| 
jbe/bsw@0
 | 
  1113       args.second = 0
 | 
| 
jbe/bsw@0
 | 
  1114       if not args.minute then
 | 
| 
jbe/bsw@0
 | 
  1115         args.minute = 0
 | 
| 
jbe/bsw@0
 | 
  1116         if not args.hour then
 | 
| 
jbe/bsw@0
 | 
  1117           args.hour = 0
 | 
| 
jbe/bsw@0
 | 
  1118         end
 | 
| 
jbe/bsw@0
 | 
  1119       end
 | 
| 
jbe/bsw@0
 | 
  1120     end
 | 
| 
jbe/bsw@0
 | 
  1121     if
 | 
| 
jbe/bsw@0
 | 
  1122       type(args.year)   == "number" and
 | 
| 
jbe/bsw@0
 | 
  1123       type(args.month)  == "number" and
 | 
| 
jbe/bsw@0
 | 
  1124       type(args.day)    == "number" and
 | 
| 
jbe/bsw@0
 | 
  1125       type(args.hour)   == "number" and
 | 
| 
jbe/bsw@0
 | 
  1126       type(args.minute) == "number" and
 | 
| 
jbe/bsw@0
 | 
  1127       type(args.second) == "number"
 | 
| 
jbe/bsw@0
 | 
  1128     then
 | 
| 
jbe/bsw@0
 | 
  1129       if
 | 
| 
jbe/bsw@0
 | 
  1130         is_integer(args.year) and
 | 
| 
jbe/bsw@0
 | 
  1131         args.year >= 1 and args.year <= 9999 and
 | 
| 
jbe/bsw@0
 | 
  1132         is_integer(args.month) and
 | 
| 
jbe/bsw@0
 | 
  1133         args.month >= 1 and args.month <= 12 and
 | 
| 
jbe/bsw@0
 | 
  1134         is_integer(args.day) and
 | 
| 
jbe/bsw@0
 | 
  1135         args.day >= 1 and args.day <= 31 and
 | 
| 
jbe/bsw@0
 | 
  1136         is_integer(args.hour) and
 | 
| 
jbe/bsw@0
 | 
  1137         args.hour >= 0 and args.hour <= 23 and
 | 
| 
jbe/bsw@0
 | 
  1138         is_integer(args.minute) and
 | 
| 
jbe/bsw@0
 | 
  1139         args.minute >= 0 and args.minute <= 59 and
 | 
| 
jbe/bsw@0
 | 
  1140         is_integer(args.second) and
 | 
| 
jbe/bsw@0
 | 
  1141         args.second >= 0 and args.second <= 59
 | 
| 
jbe/bsw@0
 | 
  1142       then
 | 
| 
jbe/bsw@0
 | 
  1143         return timestamp:_create{
 | 
| 
jbe/bsw@0
 | 
  1144           tsec = timestamp.ymdhms_to_tsec(
 | 
| 
jbe/bsw@0
 | 
  1145             args.year, args.month, args.day,
 | 
| 
jbe/bsw@0
 | 
  1146             args.hour, args.minute, args.second
 | 
| 
jbe/bsw@0
 | 
  1147           ),
 | 
| 
jbe/bsw@0
 | 
  1148           year   = args.year,
 | 
| 
jbe/bsw@0
 | 
  1149           month  = args.month,
 | 
| 
jbe/bsw@0
 | 
  1150           day    = args.day,
 | 
| 
jbe/bsw@0
 | 
  1151           hour   = args.hour,
 | 
| 
jbe/bsw@0
 | 
  1152           minute = args.minute,
 | 
| 
jbe/bsw@0
 | 
  1153           second = args.second
 | 
| 
jbe/bsw@0
 | 
  1154         }
 | 
| 
jbe/bsw@0
 | 
  1155       else
 | 
| 
jbe/bsw@0
 | 
  1156         return timestamp.invalid
 | 
| 
jbe/bsw@0
 | 
  1157       end
 | 
| 
jbe/bsw@0
 | 
  1158     elseif type(args.tsec) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1159       if
 | 
| 
jbe/bsw@0
 | 
  1160         is_integer(args.tsec) and
 | 
| 
jbe/bsw@0
 | 
  1161         args.tsec >= -62135596800 and args.tsec <= 253402300799
 | 
| 
jbe/bsw@0
 | 
  1162       then
 | 
| 
jbe/bsw@0
 | 
  1163         local year, month, day, hour, minute, second =
 | 
| 
jbe/bsw@0
 | 
  1164           timestamp.tsec_to_ymdhms(args.tsec)
 | 
| 
jbe/bsw@0
 | 
  1165         return timestamp:_create{
 | 
| 
jbe/bsw@0
 | 
  1166           tsec = args.tsec,
 | 
| 
jbe/bsw@0
 | 
  1167           year = year, month = month, day = day,
 | 
| 
jbe/bsw@0
 | 
  1168           hour = hour, minute = minute, second = second
 | 
| 
jbe/bsw@0
 | 
  1169         }
 | 
| 
jbe/bsw@0
 | 
  1170       else
 | 
| 
jbe/bsw@0
 | 
  1171         return timestamp.invalid
 | 
| 
jbe/bsw@0
 | 
  1172       end
 | 
| 
jbe/bsw@0
 | 
  1173     end
 | 
| 
jbe/bsw@0
 | 
  1174   end
 | 
| 
jbe/bsw@0
 | 
  1175   error("Invalid arguments passed to timestamp constructor.")
 | 
| 
jbe/bsw@0
 | 
  1176 end
 | 
| 
jbe/bsw@0
 | 
  1177 --//--
 | 
| 
jbe/bsw@0
 | 
  1178 
 | 
| 
jbe/bsw@0
 | 
  1179 --[[--
 | 
| 
jbe/bsw@0
 | 
  1180 ts =                          -- current date/time as timestamp
 | 
| 
jbe/bsw@0
 | 
  1181 atom.timestamp:get_current()
 | 
| 
jbe/bsw@0
 | 
  1182 
 | 
| 
jbe/bsw@0
 | 
  1183 This function returns the current date and time as timestamp.
 | 
| 
jbe/bsw@0
 | 
  1184 
 | 
| 
jbe/bsw@0
 | 
  1185 --]]--
 | 
| 
jbe/bsw@0
 | 
  1186 function timestamp:get_current()
 | 
| 
jbe/bsw@0
 | 
  1187   local now = os.date("*t")
 | 
| 
jbe/bsw@0
 | 
  1188   return timestamp{
 | 
| 
jbe/bsw@0
 | 
  1189     year = now.year, month = now.month, day = now.day,
 | 
| 
jbe/bsw@0
 | 
  1190     hour = now.hour, minute = now.min, second = now.sec
 | 
| 
jbe/bsw@0
 | 
  1191   }
 | 
| 
jbe/bsw@0
 | 
  1192 end
 | 
| 
jbe/bsw@0
 | 
  1193 --//--
 | 
| 
jbe/bsw@0
 | 
  1194 
 | 
| 
jbe/bsw@0
 | 
  1195 --[[--
 | 
| 
jbe/bsw@0
 | 
  1196 ts =             -- timestamp represented by the string
 | 
| 
jbe/bsw@0
 | 
  1197 atom.timestamp:load(
 | 
| 
jbe/bsw@0
 | 
  1198   string         -- string representing a timestamp
 | 
| 
jbe/bsw@0
 | 
  1199 )
 | 
| 
jbe/bsw@0
 | 
  1200 
 | 
| 
jbe/bsw@0
 | 
  1201 This method returns a timestamp represented by the given string.
 | 
| 
jbe/bsw@0
 | 
  1202 
 | 
| 
jbe/bsw@0
 | 
  1203 --]]--
 | 
| 
jbe/bsw@0
 | 
  1204 function timestamp:load(str)
 | 
| 
jbe@1
 | 
  1205   if str == nil or str == "" then
 | 
| 
jbe/bsw@0
 | 
  1206     return nil
 | 
| 
jbe@1
 | 
  1207   elseif type(str) ~= "string" then
 | 
| 
jbe@1
 | 
  1208     error("String expected")
 | 
| 
jbe/bsw@0
 | 
  1209   else
 | 
| 
jbe/bsw@0
 | 
  1210     local year, month, day, hour, minute, second = string.match(
 | 
| 
jbe/bsw@0
 | 
  1211       str,
 | 
| 
jbe/bsw@0
 | 
  1212       "^([0-9][0-9][0-9][0-9])%-([0-9][0-9])%-([0-9][0-9]) ([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$"
 | 
| 
jbe/bsw@0
 | 
  1213     )
 | 
| 
jbe/bsw@0
 | 
  1214     if year then
 | 
| 
jbe/bsw@0
 | 
  1215       return timestamp{
 | 
| 
jbe/bsw@0
 | 
  1216         year   = tonumber(year),
 | 
| 
jbe/bsw@0
 | 
  1217         month  = tonumber(month),
 | 
| 
jbe/bsw@0
 | 
  1218         day    = tonumber(day),
 | 
| 
jbe/bsw@0
 | 
  1219         hour   = tonumber(hour),
 | 
| 
jbe/bsw@0
 | 
  1220         minute = tonumber(minute),
 | 
| 
jbe/bsw@0
 | 
  1221         second = tonumber(second)
 | 
| 
jbe/bsw@0
 | 
  1222       }
 | 
| 
jbe/bsw@0
 | 
  1223     else
 | 
| 
jbe/bsw@0
 | 
  1224       return timestamp.invalid
 | 
| 
jbe/bsw@0
 | 
  1225     end
 | 
| 
jbe/bsw@0
 | 
  1226   end
 | 
| 
jbe/bsw@0
 | 
  1227 end
 | 
| 
jbe/bsw@0
 | 
  1228 
 | 
| 
jbe/bsw@0
 | 
  1229 function timestamp:__tostring()
 | 
| 
jbe/bsw@0
 | 
  1230   if self.invalid then
 | 
| 
jbe/bsw@0
 | 
  1231     return "invalid_timestamp"
 | 
| 
jbe/bsw@0
 | 
  1232   else
 | 
| 
jbe/bsw@0
 | 
  1233     return string.format(
 | 
| 
jbe/bsw@0
 | 
  1234       "%04i-%02i-%02i %02i:%02i:%02i",
 | 
| 
jbe/bsw@0
 | 
  1235       self.year, self.month, self.day, self.hour, self.minute, self.second
 | 
| 
jbe/bsw@0
 | 
  1236     )
 | 
| 
jbe/bsw@0
 | 
  1237   end
 | 
| 
jbe/bsw@0
 | 
  1238 end
 | 
| 
jbe/bsw@0
 | 
  1239 
 | 
| 
jbe/bsw@0
 | 
  1240 function timestamp.getters:date()
 | 
| 
jbe/bsw@0
 | 
  1241   return date{ year = self.year, month = self.month, day = self.day }
 | 
| 
jbe/bsw@0
 | 
  1242 end
 | 
| 
jbe/bsw@0
 | 
  1243 
 | 
| 
jbe/bsw@0
 | 
  1244 function timestamp.getters:time()
 | 
| 
jbe/bsw@0
 | 
  1245   return time{
 | 
| 
jbe/bsw@0
 | 
  1246     hour = self.hour,
 | 
| 
jbe/bsw@0
 | 
  1247     minute = self.minute,
 | 
| 
jbe/bsw@0
 | 
  1248     second = self.second
 | 
| 
jbe/bsw@0
 | 
  1249   }
 | 
| 
jbe/bsw@0
 | 
  1250 end
 | 
| 
jbe/bsw@0
 | 
  1251 
 | 
| 
jbe/bsw@0
 | 
  1252 function timestamp.__eq(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1253   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
  1254     return false
 | 
| 
jbe/bsw@0
 | 
  1255   else
 | 
| 
jbe/bsw@0
 | 
  1256     return value1.tsec == value2.tsec
 | 
| 
jbe/bsw@0
 | 
  1257   end
 | 
| 
jbe/bsw@0
 | 
  1258 end
 | 
| 
jbe/bsw@0
 | 
  1259 
 | 
| 
jbe/bsw@0
 | 
  1260 function timestamp.__lt(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1261   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
  1262     return false
 | 
| 
jbe/bsw@0
 | 
  1263   else
 | 
| 
jbe/bsw@0
 | 
  1264     return value1.tsec < value2.tsec
 | 
| 
jbe/bsw@0
 | 
  1265   end
 | 
| 
jbe/bsw@0
 | 
  1266 end
 | 
| 
jbe/bsw@0
 | 
  1267 
 | 
| 
jbe/bsw@0
 | 
  1268 function timestamp.__le(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1269   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
  1270     return false
 | 
| 
jbe/bsw@0
 | 
  1271   else
 | 
| 
jbe/bsw@0
 | 
  1272     return value1.tsec <= value2.tsec
 | 
| 
jbe/bsw@0
 | 
  1273   end
 | 
| 
jbe/bsw@0
 | 
  1274 end
 | 
| 
jbe/bsw@0
 | 
  1275 
 | 
| 
jbe/bsw@0
 | 
  1276 function timestamp.__add(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1277   if getmetatable(value1) == timestamp then
 | 
| 
jbe/bsw@0
 | 
  1278     if getmetatable(value2) == timestamp then
 | 
| 
jbe/bsw@0
 | 
  1279       error("Can not add two timestamps.")
 | 
| 
jbe/bsw@0
 | 
  1280     elseif type(value2) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1281       return timestamp(value1.tsec + value2)
 | 
| 
jbe/bsw@0
 | 
  1282     else
 | 
| 
jbe/bsw@0
 | 
  1283       error("Right operand of '+' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1284     end
 | 
| 
jbe/bsw@0
 | 
  1285   elseif type(value1) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1286     if getmetatable(value2) == timestamp then
 | 
| 
jbe/bsw@0
 | 
  1287       return timestamp(value1 + value2.tsec)
 | 
| 
jbe/bsw@0
 | 
  1288     else
 | 
| 
jbe/bsw@0
 | 
  1289       error("Assertion failed")
 | 
| 
jbe/bsw@0
 | 
  1290     end
 | 
| 
jbe/bsw@0
 | 
  1291   else
 | 
| 
jbe/bsw@0
 | 
  1292     error("Left operand of '+' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1293   end
 | 
| 
jbe/bsw@0
 | 
  1294 end
 | 
| 
jbe/bsw@0
 | 
  1295 
 | 
| 
jbe/bsw@0
 | 
  1296 function timestamp.__sub(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1297   if not getmetatable(value1) == timestamp then
 | 
| 
jbe/bsw@0
 | 
  1298     error("Left operand of '-' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1299   end
 | 
| 
jbe/bsw@0
 | 
  1300   if getmetatable(value2) == timestamp then
 | 
| 
jbe/bsw@0
 | 
  1301     return value1.tsec - value2.tsec  -- TODO: transform to interval
 | 
| 
jbe/bsw@0
 | 
  1302   elseif type(value2) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1303     return timestamp(value1.tsec - value2)
 | 
| 
jbe/bsw@0
 | 
  1304   else
 | 
| 
jbe/bsw@0
 | 
  1305     error("Right operand of '-' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1306   end
 | 
| 
jbe/bsw@0
 | 
  1307 end
 | 
| 
jbe/bsw@0
 | 
  1308 
 | 
| 
jbe/bsw@0
 | 
  1309 
 | 
| 
jbe/bsw@0
 | 
  1310 
 | 
| 
jbe/bsw@0
 | 
  1311 ----------
 | 
| 
jbe/bsw@0
 | 
  1312 -- time --
 | 
| 
jbe/bsw@0
 | 
  1313 ----------
 | 
| 
jbe/bsw@0
 | 
  1314 
 | 
| 
jbe/bsw@0
 | 
  1315 time = create_new_type("time")
 | 
| 
jbe/bsw@0
 | 
  1316 
 | 
| 
jbe/bsw@0
 | 
  1317 function time.hms_to_dsec(hour, minute, second)
 | 
| 
jbe/bsw@0
 | 
  1318   return 3600 * hour + 60 * minute + second
 | 
| 
jbe/bsw@0
 | 
  1319 end
 | 
| 
jbe/bsw@0
 | 
  1320 
 | 
| 
jbe/bsw@0
 | 
  1321 function time.dsec_to_hms(dsec)
 | 
| 
jbe/bsw@0
 | 
  1322   local hour   = math.floor(dsec / 3600)
 | 
| 
jbe/bsw@0
 | 
  1323   local minute = math.floor((dsec % 3600) / 60)
 | 
| 
jbe/bsw@0
 | 
  1324   local second = dsec % 60
 | 
| 
jbe/bsw@0
 | 
  1325   return hour, minute, second
 | 
| 
jbe/bsw@0
 | 
  1326 end
 | 
| 
jbe/bsw@0
 | 
  1327 
 | 
| 
jbe/bsw@0
 | 
  1328 --[[--
 | 
| 
jbe/bsw@0
 | 
  1329 atom.time.invalid
 | 
| 
jbe/bsw@0
 | 
  1330 
 | 
| 
jbe/bsw@0
 | 
  1331 Value representing an invalid time of day.
 | 
| 
jbe/bsw@0
 | 
  1332 
 | 
| 
jbe/bsw@0
 | 
  1333 --]]--
 | 
| 
jbe/bsw@0
 | 
  1334 time.invalid = time:_create{
 | 
| 
jbe/bsw@0
 | 
  1335   dsec = not_a_number,
 | 
| 
jbe/bsw@0
 | 
  1336   hour = not_a_number, minute = not_a_number, second = not_a_number,
 | 
| 
jbe/bsw@0
 | 
  1337   invalid = true
 | 
| 
jbe/bsw@0
 | 
  1338 }
 | 
| 
jbe/bsw@0
 | 
  1339 --//--
 | 
| 
jbe/bsw@0
 | 
  1340 
 | 
| 
jbe/bsw@0
 | 
  1341 --[[--
 | 
| 
jbe/bsw@0
 | 
  1342 t =                 -- time based on given data
 | 
| 
jbe/bsw@0
 | 
  1343 atom.time:new{
 | 
| 
jbe/bsw@0
 | 
  1344   dsec = dsec,      -- seconds since 00:00:00
 | 
| 
jbe/bsw@0
 | 
  1345   hour = hour,      -- hour from 0 to 23
 | 
| 
jbe/bsw@0
 | 
  1346   minute = minute,  -- minute from 0 to 59
 | 
| 
jbe/bsw@0
 | 
  1347   second = second   -- second from 0 to 59
 | 
| 
jbe/bsw@0
 | 
  1348 }
 | 
| 
jbe/bsw@0
 | 
  1349 
 | 
| 
jbe/bsw@0
 | 
  1350 This method returns a new time value, based on given data.
 | 
| 
jbe/bsw@0
 | 
  1351 
 | 
| 
jbe/bsw@0
 | 
  1352 --]]--
 | 
| 
jbe/bsw@0
 | 
  1353 function time:new(args)
 | 
| 
jbe/bsw@0
 | 
  1354   local args = args
 | 
| 
jbe/bsw@0
 | 
  1355   if type(args) == "number" then args = { dsec = args } end
 | 
| 
jbe/bsw@0
 | 
  1356   if type(args) == "table" then
 | 
| 
jbe/bsw@0
 | 
  1357     if not args.second then
 | 
| 
jbe/bsw@0
 | 
  1358       args.second = 0
 | 
| 
jbe/bsw@0
 | 
  1359       if not args.minute then
 | 
| 
jbe/bsw@0
 | 
  1360         args.minute = 0
 | 
| 
jbe/bsw@0
 | 
  1361       end
 | 
| 
jbe/bsw@0
 | 
  1362     end
 | 
| 
jbe/bsw@0
 | 
  1363     if
 | 
| 
jbe/bsw@0
 | 
  1364       type(args.hour)   == "number" and
 | 
| 
jbe/bsw@0
 | 
  1365       type(args.minute) == "number" and
 | 
| 
jbe/bsw@0
 | 
  1366       type(args.second) == "number"
 | 
| 
jbe/bsw@0
 | 
  1367     then
 | 
| 
jbe/bsw@0
 | 
  1368       if
 | 
| 
jbe/bsw@0
 | 
  1369         is_integer(args.hour) and
 | 
| 
jbe/bsw@0
 | 
  1370         args.hour >= 0 and args.hour <= 23 and
 | 
| 
jbe/bsw@0
 | 
  1371         is_integer(args.minute) and
 | 
| 
jbe/bsw@0
 | 
  1372         args.minute >= 0 and args.minute <= 59 and
 | 
| 
jbe/bsw@0
 | 
  1373         is_integer(args.second) and
 | 
| 
jbe/bsw@0
 | 
  1374         args.second >= 0 and args.second <= 59
 | 
| 
jbe/bsw@0
 | 
  1375       then
 | 
| 
jbe/bsw@0
 | 
  1376         return time:_create{
 | 
| 
jbe/bsw@0
 | 
  1377           dsec = time.hms_to_dsec(args.hour, args.minute, args.second),
 | 
| 
jbe/bsw@0
 | 
  1378           hour   = args.hour,
 | 
| 
jbe/bsw@0
 | 
  1379           minute = args.minute,
 | 
| 
jbe/bsw@0
 | 
  1380           second = args.second
 | 
| 
jbe/bsw@0
 | 
  1381         }
 | 
| 
jbe/bsw@0
 | 
  1382       else
 | 
| 
jbe/bsw@0
 | 
  1383         return time.invalid
 | 
| 
jbe/bsw@0
 | 
  1384       end
 | 
| 
jbe/bsw@0
 | 
  1385     elseif type(args.dsec) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1386       if
 | 
| 
jbe/bsw@0
 | 
  1387         is_integer(args.dsec) and
 | 
| 
jbe/bsw@0
 | 
  1388         args.dsec >= 0 and args.dsec <= 86399
 | 
| 
jbe/bsw@0
 | 
  1389       then
 | 
| 
jbe/bsw@0
 | 
  1390         local hour, minute, second =
 | 
| 
jbe/bsw@0
 | 
  1391           time.dsec_to_hms(args.dsec)
 | 
| 
jbe/bsw@0
 | 
  1392         return time:_create{
 | 
| 
jbe/bsw@0
 | 
  1393           dsec = args.dsec,
 | 
| 
jbe/bsw@0
 | 
  1394           hour = hour, minute = minute, second = second
 | 
| 
jbe/bsw@0
 | 
  1395         }
 | 
| 
jbe/bsw@0
 | 
  1396       else
 | 
| 
jbe/bsw@0
 | 
  1397         return time.invalid
 | 
| 
jbe/bsw@0
 | 
  1398       end
 | 
| 
jbe/bsw@0
 | 
  1399     end
 | 
| 
jbe/bsw@0
 | 
  1400   end
 | 
| 
jbe/bsw@0
 | 
  1401   error("Invalid arguments passed to time constructor.")
 | 
| 
jbe/bsw@0
 | 
  1402 end
 | 
| 
jbe/bsw@0
 | 
  1403 --//--
 | 
| 
jbe/bsw@0
 | 
  1404 
 | 
| 
jbe/bsw@0
 | 
  1405 --[[--
 | 
| 
jbe/bsw@0
 | 
  1406 t =                      -- current time of day
 | 
| 
jbe/bsw@0
 | 
  1407 atom.time:get_current()
 | 
| 
jbe/bsw@0
 | 
  1408 
 | 
| 
jbe/bsw@0
 | 
  1409 This method returns the current time of the day.
 | 
| 
jbe/bsw@0
 | 
  1410 
 | 
| 
jbe/bsw@0
 | 
  1411 --]]--
 | 
| 
jbe/bsw@0
 | 
  1412 function time:get_current()
 | 
| 
jbe/bsw@0
 | 
  1413   local now = os.date("*t")
 | 
| 
jbe/bsw@0
 | 
  1414   return time{ hour = now.hour, minute = now.min, second = now.sec }
 | 
| 
jbe/bsw@0
 | 
  1415 end
 | 
| 
jbe/bsw@0
 | 
  1416 --//--
 | 
| 
jbe/bsw@0
 | 
  1417 
 | 
| 
jbe/bsw@0
 | 
  1418 --[[--
 | 
| 
jbe/bsw@0
 | 
  1419 t =              -- time represented by the string
 | 
| 
jbe/bsw@0
 | 
  1420 atom.time:load(
 | 
| 
jbe/bsw@0
 | 
  1421   string         -- string representing a time of day
 | 
| 
jbe/bsw@0
 | 
  1422 )
 | 
| 
jbe/bsw@0
 | 
  1423 
 | 
| 
jbe/bsw@0
 | 
  1424 This method returns a time represented by the given string.
 | 
| 
jbe/bsw@0
 | 
  1425 
 | 
| 
jbe/bsw@0
 | 
  1426 --]]--
 | 
| 
jbe/bsw@0
 | 
  1427 function time:load(str)
 | 
| 
jbe@1
 | 
  1428   if str == nil or str == "" then
 | 
| 
jbe/bsw@0
 | 
  1429     return nil
 | 
| 
jbe@1
 | 
  1430   elseif type(str) ~= "string" then
 | 
| 
jbe@1
 | 
  1431     error("String expected")
 | 
| 
jbe/bsw@0
 | 
  1432   else
 | 
| 
jbe/bsw@0
 | 
  1433     local hour, minute, second = string.match(
 | 
| 
jbe/bsw@0
 | 
  1434       str,
 | 
| 
jbe/bsw@0
 | 
  1435       "^([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$"
 | 
| 
jbe/bsw@0
 | 
  1436     )
 | 
| 
jbe/bsw@0
 | 
  1437     if hour then
 | 
| 
jbe/bsw@0
 | 
  1438       return time{
 | 
| 
jbe/bsw@0
 | 
  1439         hour   = tonumber(hour),
 | 
| 
jbe/bsw@0
 | 
  1440         minute = tonumber(minute),
 | 
| 
jbe/bsw@0
 | 
  1441         second = tonumber(second)
 | 
| 
jbe/bsw@0
 | 
  1442       }
 | 
| 
jbe/bsw@0
 | 
  1443     else
 | 
| 
jbe/bsw@0
 | 
  1444       return time.invalid
 | 
| 
jbe/bsw@0
 | 
  1445     end
 | 
| 
jbe/bsw@0
 | 
  1446   end
 | 
| 
jbe/bsw@0
 | 
  1447 end
 | 
| 
jbe/bsw@0
 | 
  1448 --//--
 | 
| 
jbe/bsw@0
 | 
  1449 
 | 
| 
jbe/bsw@0
 | 
  1450 function time:__tostring()
 | 
| 
jbe/bsw@0
 | 
  1451   if self.invalid then
 | 
| 
jbe/bsw@0
 | 
  1452     return "invalid_time"
 | 
| 
jbe/bsw@0
 | 
  1453   else
 | 
| 
jbe/bsw@0
 | 
  1454     return string.format(
 | 
| 
jbe/bsw@0
 | 
  1455       "%02i:%02i:%02i",
 | 
| 
jbe/bsw@0
 | 
  1456       self.hour, self.minute, self.second
 | 
| 
jbe/bsw@0
 | 
  1457     )
 | 
| 
jbe/bsw@0
 | 
  1458   end
 | 
| 
jbe/bsw@0
 | 
  1459 end
 | 
| 
jbe/bsw@0
 | 
  1460 
 | 
| 
jbe/bsw@0
 | 
  1461 function time.__eq(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1462   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
  1463     return false
 | 
| 
jbe/bsw@0
 | 
  1464   else
 | 
| 
jbe/bsw@0
 | 
  1465     return value1.dsec == value2.dsec
 | 
| 
jbe/bsw@0
 | 
  1466   end
 | 
| 
jbe/bsw@0
 | 
  1467 end
 | 
| 
jbe/bsw@0
 | 
  1468 
 | 
| 
jbe/bsw@0
 | 
  1469 function time.__lt(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1470   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
  1471     return false
 | 
| 
jbe/bsw@0
 | 
  1472   else
 | 
| 
jbe/bsw@0
 | 
  1473     return value1.dsec < value2.dsec
 | 
| 
jbe/bsw@0
 | 
  1474   end
 | 
| 
jbe/bsw@0
 | 
  1475 end
 | 
| 
jbe/bsw@0
 | 
  1476 
 | 
| 
jbe/bsw@0
 | 
  1477 function time.__le(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1478   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
  1479     return false
 | 
| 
jbe/bsw@0
 | 
  1480   else
 | 
| 
jbe/bsw@0
 | 
  1481     return value1.dsec <= value2.dsec
 | 
| 
jbe/bsw@0
 | 
  1482   end
 | 
| 
jbe/bsw@0
 | 
  1483 end
 | 
| 
jbe/bsw@0
 | 
  1484 
 | 
| 
jbe/bsw@0
 | 
  1485 function time.__add(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1486   if getmetatable(value1) == time then
 | 
| 
jbe/bsw@0
 | 
  1487     if getmetatable(value2) == time then
 | 
| 
jbe/bsw@0
 | 
  1488       error("Can not add two times.")
 | 
| 
jbe/bsw@0
 | 
  1489     elseif type(value2) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1490       return time((value1.dsec + value2) % 86400)
 | 
| 
jbe/bsw@0
 | 
  1491     else
 | 
| 
jbe/bsw@0
 | 
  1492       error("Right operand of '+' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1493     end
 | 
| 
jbe/bsw@0
 | 
  1494   elseif type(value1) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1495     if getmetatable(value2) == time then
 | 
| 
jbe/bsw@0
 | 
  1496       return time((value1 + value2.dsec) % 86400)
 | 
| 
jbe/bsw@0
 | 
  1497     else
 | 
| 
jbe/bsw@0
 | 
  1498       error("Assertion failed")
 | 
| 
jbe/bsw@0
 | 
  1499     end
 | 
| 
jbe/bsw@0
 | 
  1500   else
 | 
| 
jbe/bsw@0
 | 
  1501     error("Left operand of '+' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1502   end
 | 
| 
jbe/bsw@0
 | 
  1503 end
 | 
| 
jbe/bsw@0
 | 
  1504 
 | 
| 
jbe/bsw@0
 | 
  1505 function time.__sub(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1506   if not getmetatable(value1) == time then
 | 
| 
jbe/bsw@0
 | 
  1507     error("Left operand of '-' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1508   end
 | 
| 
jbe/bsw@0
 | 
  1509   if getmetatable(value2) == time then
 | 
| 
jbe/bsw@0
 | 
  1510     return value1.dsec - value2.dsec  -- TODO: transform to interval
 | 
| 
jbe/bsw@0
 | 
  1511   elseif type(value2) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1512     return time((value1.dsec - value2) % 86400)
 | 
| 
jbe/bsw@0
 | 
  1513   else
 | 
| 
jbe/bsw@0
 | 
  1514     error("Right operand of '-' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1515   end
 | 
| 
jbe/bsw@0
 | 
  1516 end
 | 
| 
jbe/bsw@0
 | 
  1517 
 | 
| 
jbe/bsw@0
 | 
  1518 function time.__concat(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1519   local mt1, mt2 = getmetatable(value1), getmetatable(value2)
 | 
| 
jbe/bsw@0
 | 
  1520   if mt1 == date and mt2 == time then
 | 
| 
jbe/bsw@0
 | 
  1521     return timestamp{
 | 
| 
jbe/bsw@0
 | 
  1522       year = value1.year, month = value1.month, day = value1.day,
 | 
| 
jbe/bsw@0
 | 
  1523       hour = value2.hour, minute = value2.minute, second = value2.second
 | 
| 
jbe/bsw@0
 | 
  1524     }
 | 
| 
jbe/bsw@0
 | 
  1525   elseif mt1 == time and mt2 == date then
 | 
| 
jbe/bsw@0
 | 
  1526     return timestamp{
 | 
| 
jbe/bsw@0
 | 
  1527       year = value2.year, month = value2.month, day = value2.day,
 | 
| 
jbe/bsw@0
 | 
  1528       hour = value1.hour, minute = value1.minute, second = value1.second
 | 
| 
jbe/bsw@0
 | 
  1529     }
 | 
| 
jbe/bsw@0
 | 
  1530   elseif mt1 == time then
 | 
| 
jbe/bsw@0
 | 
  1531     error("Right operand of '..' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1532   elseif mt2 == time then
 | 
| 
jbe/bsw@0
 | 
  1533     error("Left operand of '..' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1534   else
 | 
| 
jbe/bsw@0
 | 
  1535     error("Assertion failed")
 | 
| 
jbe/bsw@0
 | 
  1536   end
 | 
| 
jbe/bsw@0
 | 
  1537 end
 | 
| 
jbe/bsw@0
 | 
  1538 
 | 
| 
jbe@64
 | 
  1539 
 | 
| 
jbe@64
 | 
  1540 
 | 
| 
jbe@64
 | 
  1541 return _M
 |