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