| 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@196
 | 
   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@531
 | 
   773         local jd = date.ymd_to_jd(year, month, day)
 | 
| 
jbe@531
 | 
   774         local year2, month2, day2 = date.jd_to_ymd(jd)
 | 
| 
jbe@531
 | 
   775         if year == year2 and month == month2 and day == day2 then
 | 
| 
jbe@531
 | 
   776           return date:_create{
 | 
| 
jbe@531
 | 
   777             jd = date.ymd_to_jd(year, month, day),
 | 
| 
jbe@531
 | 
   778             year = year2, month = month2, day = day2
 | 
| 
jbe@531
 | 
   779           }
 | 
| 
jbe@531
 | 
   780         else
 | 
| 
jbe@531
 | 
   781           return date.invalid
 | 
| 
jbe@531
 | 
   782         end
 | 
| 
jbe/bsw@0
 | 
   783       else
 | 
| 
jbe/bsw@0
 | 
   784         return date.invalid
 | 
| 
jbe/bsw@0
 | 
   785       end
 | 
| 
jbe/bsw@0
 | 
   786     elseif type(jd) == "number" then
 | 
| 
jbe/bsw@0
 | 
   787       if is_integer(jd) and jd >= -719162 and jd <= 2932896 then
 | 
| 
jbe/bsw@0
 | 
   788         local year, month, day = date.jd_to_ymd(jd)
 | 
| 
jbe/bsw@0
 | 
   789         return date:_create{
 | 
| 
jbe/bsw@0
 | 
   790           jd = jd, year = year, month = month, day = day
 | 
| 
jbe/bsw@0
 | 
   791         }
 | 
| 
jbe/bsw@0
 | 
   792       else
 | 
| 
jbe/bsw@0
 | 
   793         return date.invalid
 | 
| 
jbe/bsw@0
 | 
   794       end
 | 
| 
jbe/bsw@0
 | 
   795     elseif
 | 
| 
jbe/bsw@0
 | 
   796       type(year)        == "number" and not iso_weekyear and
 | 
| 
jbe/bsw@0
 | 
   797       type(iso_week)    == "number" and
 | 
| 
jbe/bsw@0
 | 
   798       type(iso_weekday) == "number"
 | 
| 
jbe/bsw@0
 | 
   799     then
 | 
| 
jbe/bsw@0
 | 
   800       if
 | 
| 
jbe/bsw@0
 | 
   801         is_integer(year) and
 | 
| 
jbe/bsw@0
 | 
   802         is_integer(iso_week)     and iso_week >= 0 and iso_week <= 53 and
 | 
| 
jbe/bsw@0
 | 
   803         is_integer(iso_weekday)  and iso_weekday >= 1 and iso_weekday <= 7
 | 
| 
jbe/bsw@0
 | 
   804       then
 | 
| 
jbe/bsw@0
 | 
   805         local jan4 = date{ year = year, month = 1, day = 4 }
 | 
| 
jbe/bsw@0
 | 
   806         local reference = jan4 - jan4.iso_weekday - 7  -- Sun. of week -1
 | 
| 
jbe/bsw@0
 | 
   807         return date(reference + 7 * iso_week + iso_weekday)
 | 
| 
jbe/bsw@0
 | 
   808       else
 | 
| 
jbe/bsw@0
 | 
   809         return date.invalid
 | 
| 
jbe/bsw@0
 | 
   810       end
 | 
| 
jbe/bsw@0
 | 
   811     elseif
 | 
| 
jbe/bsw@0
 | 
   812       type(iso_weekyear) == "number" and not year and
 | 
| 
jbe/bsw@0
 | 
   813       type(iso_week)     == "number" and
 | 
| 
jbe/bsw@0
 | 
   814       type(iso_weekday)  == "number"
 | 
| 
jbe/bsw@0
 | 
   815     then
 | 
| 
jbe/bsw@0
 | 
   816       if
 | 
| 
jbe/bsw@0
 | 
   817         is_integer(iso_weekyear) and
 | 
| 
jbe/bsw@0
 | 
   818         is_integer(iso_week)     and iso_week >= 0 and iso_week <= 53 and
 | 
| 
jbe/bsw@0
 | 
   819         is_integer(iso_weekday)  and iso_weekday >= 1 and iso_weekday <= 7
 | 
| 
jbe/bsw@0
 | 
   820       then
 | 
| 
jbe/bsw@0
 | 
   821         local guessed = date{
 | 
| 
jbe/bsw@0
 | 
   822           year        = iso_weekyear,
 | 
| 
jbe/bsw@0
 | 
   823           iso_week    = iso_week,
 | 
| 
jbe/bsw@0
 | 
   824           iso_weekday = iso_weekday
 | 
| 
jbe/bsw@0
 | 
   825         }
 | 
| 
jbe/bsw@0
 | 
   826         if guessed.invalid or guessed.iso_weekyear == iso_weekyear then
 | 
| 
jbe/bsw@0
 | 
   827           return guessed
 | 
| 
jbe/bsw@0
 | 
   828         else
 | 
| 
jbe/bsw@0
 | 
   829           local year
 | 
| 
jbe/bsw@0
 | 
   830           if iso_week <= 1 then
 | 
| 
jbe/bsw@0
 | 
   831             year = iso_weekyear - 1
 | 
| 
jbe/bsw@0
 | 
   832           elseif iso_week >= 52 then
 | 
| 
jbe/bsw@0
 | 
   833             year = iso_weekyear + 1
 | 
| 
jbe/bsw@0
 | 
   834           else
 | 
| 
jbe/bsw@0
 | 
   835             error("Internal error in ISO week computation occured.")
 | 
| 
jbe/bsw@0
 | 
   836           end
 | 
| 
jbe/bsw@0
 | 
   837           return date{
 | 
| 
jbe/bsw@0
 | 
   838             year = year, iso_week = iso_week, iso_weekday = iso_weekday
 | 
| 
jbe/bsw@0
 | 
   839           }
 | 
| 
jbe/bsw@0
 | 
   840         end
 | 
| 
jbe/bsw@0
 | 
   841       else
 | 
| 
jbe/bsw@0
 | 
   842         return date.invalid
 | 
| 
jbe/bsw@0
 | 
   843       end
 | 
| 
jbe/bsw@0
 | 
   844     elseif
 | 
| 
jbe/bsw@0
 | 
   845       type(year) == "number" and
 | 
| 
jbe/bsw@0
 | 
   846       type(us_week)     == "number" and
 | 
| 
jbe/bsw@0
 | 
   847       type(us_weekday)  == "number"
 | 
| 
jbe/bsw@0
 | 
   848     then
 | 
| 
jbe/bsw@0
 | 
   849       if
 | 
| 
jbe/bsw@0
 | 
   850         is_integer(year) and
 | 
| 
jbe/bsw@0
 | 
   851         is_integer(us_week)     and us_week >= 0    and us_week <= 54   and
 | 
| 
jbe/bsw@0
 | 
   852         is_integer(us_weekday)  and us_weekday >= 1 and us_weekday <= 7
 | 
| 
jbe/bsw@0
 | 
   853       then
 | 
| 
jbe/bsw@0
 | 
   854         local jan1 = date{ year = year, month = 1, day = 1 }
 | 
| 
jbe/bsw@0
 | 
   855         local reference = jan1 - jan1.us_weekday - 7  -- Sat. of week -1
 | 
| 
jbe/bsw@0
 | 
   856         return date(reference + 7 * us_week + us_weekday)
 | 
| 
jbe/bsw@0
 | 
   857       else
 | 
| 
jbe/bsw@0
 | 
   858         return date.invalid
 | 
| 
jbe/bsw@0
 | 
   859       end
 | 
| 
jbe/bsw@0
 | 
   860     end
 | 
| 
jbe/bsw@0
 | 
   861   end
 | 
| 
jbe/bsw@0
 | 
   862   error("Illegal arguments passed to date constructor.")
 | 
| 
jbe/bsw@0
 | 
   863 end
 | 
| 
jbe/bsw@0
 | 
   864 --//--
 | 
| 
jbe/bsw@0
 | 
   865 
 | 
| 
jbe/bsw@0
 | 
   866 --[[--
 | 
| 
jbe/bsw@0
 | 
   867 atom.date:get_current()
 | 
| 
jbe/bsw@0
 | 
   868 
 | 
| 
jbe/bsw@0
 | 
   869 This function returns today's date.
 | 
| 
jbe/bsw@0
 | 
   870 
 | 
| 
jbe/bsw@0
 | 
   871 --]]--
 | 
| 
jbe/bsw@0
 | 
   872 function date:get_current()
 | 
| 
jbe/bsw@0
 | 
   873   local now = os.date("*t")
 | 
| 
jbe/bsw@0
 | 
   874   return date{
 | 
| 
jbe/bsw@0
 | 
   875     year = now.year, month = now.month, day = now.day
 | 
| 
jbe/bsw@0
 | 
   876   }
 | 
| 
jbe/bsw@0
 | 
   877 end
 | 
| 
jbe/bsw@0
 | 
   878 --//--
 | 
| 
jbe/bsw@0
 | 
   879 
 | 
| 
jbe/bsw@0
 | 
   880 --[[--
 | 
| 
jbe/bsw@0
 | 
   881 date =           -- date represented by the string
 | 
| 
jbe/bsw@0
 | 
   882 atom.date:load(
 | 
| 
jbe/bsw@0
 | 
   883   string         -- string representing a date
 | 
| 
jbe/bsw@0
 | 
   884 )
 | 
| 
jbe/bsw@0
 | 
   885 
 | 
| 
jbe/bsw@0
 | 
   886 This method returns a date represented by the given string.
 | 
| 
jbe/bsw@0
 | 
   887 
 | 
| 
jbe/bsw@0
 | 
   888 --]]--
 | 
| 
jbe/bsw@0
 | 
   889 function date:load(str)
 | 
| 
jbe@1
 | 
   890   if str == nil or str == "" then
 | 
| 
jbe/bsw@0
 | 
   891     return nil
 | 
| 
jbe@1
 | 
   892   elseif type(str) ~= "string" then
 | 
| 
jbe@1
 | 
   893     error("String expected")
 | 
| 
jbe/bsw@0
 | 
   894   else
 | 
| 
jbe/bsw@0
 | 
   895     local year, month, day = string.match(
 | 
| 
jbe/bsw@0
 | 
   896       str, "^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"
 | 
| 
jbe/bsw@0
 | 
   897     )
 | 
| 
jbe/bsw@0
 | 
   898     if year then
 | 
| 
jbe/bsw@0
 | 
   899       return date{
 | 
| 
jbe/bsw@0
 | 
   900         year = tonumber(year),
 | 
| 
jbe/bsw@0
 | 
   901         month = tonumber(month),
 | 
| 
jbe/bsw@0
 | 
   902         day = tonumber(day)
 | 
| 
jbe/bsw@0
 | 
   903       }
 | 
| 
jbe/bsw@0
 | 
   904     else
 | 
| 
jbe/bsw@0
 | 
   905       return date.invalid
 | 
| 
jbe/bsw@0
 | 
   906     end
 | 
| 
jbe/bsw@0
 | 
   907   end
 | 
| 
jbe/bsw@0
 | 
   908 end
 | 
| 
jbe/bsw@0
 | 
   909 --//--
 | 
| 
jbe/bsw@0
 | 
   910 
 | 
| 
jbe/bsw@0
 | 
   911 function date:__tostring()
 | 
| 
jbe/bsw@0
 | 
   912   if self.invalid then
 | 
| 
jbe/bsw@0
 | 
   913     return "invalid_date"
 | 
| 
jbe/bsw@0
 | 
   914   else
 | 
| 
jbe/bsw@0
 | 
   915     return string.format(
 | 
| 
jbe/bsw@0
 | 
   916       "%04i-%02i-%02i", self.year, self.month, self.day
 | 
| 
jbe/bsw@0
 | 
   917     )
 | 
| 
jbe/bsw@0
 | 
   918   end
 | 
| 
jbe/bsw@0
 | 
   919 end
 | 
| 
jbe/bsw@0
 | 
   920 
 | 
| 
jbe/bsw@0
 | 
   921 function date.getters:midnight()
 | 
| 
jbe@90
 | 
   922   return timestamp{ year = self.year, month = self.month, day = self.day }
 | 
| 
jbe/bsw@0
 | 
   923 end
 | 
| 
jbe/bsw@0
 | 
   924 
 | 
| 
jbe/bsw@0
 | 
   925 function date.getters:midday()
 | 
| 
jbe@90
 | 
   926   return timestamp{
 | 
| 
jbe/bsw@0
 | 
   927     year = self.year, month = self.month, day = self.day,
 | 
| 
jbe/bsw@0
 | 
   928     hour = 12
 | 
| 
jbe/bsw@0
 | 
   929   }
 | 
| 
jbe/bsw@0
 | 
   930 end
 | 
| 
jbe/bsw@0
 | 
   931 
 | 
| 
jbe/bsw@0
 | 
   932 function date.getters:iso_weekday()  -- 1 = Monday
 | 
| 
jbe/bsw@0
 | 
   933   return (self.jd + 3) % 7 + 1
 | 
| 
jbe/bsw@0
 | 
   934 end
 | 
| 
jbe/bsw@0
 | 
   935 
 | 
| 
jbe/bsw@0
 | 
   936 function date.getters:us_weekday()  -- 1 = Sunday
 | 
| 
jbe/bsw@0
 | 
   937   return (self.jd + 4) % 7 + 1
 | 
| 
jbe/bsw@0
 | 
   938 end
 | 
| 
jbe/bsw@0
 | 
   939 
 | 
| 
jbe/bsw@0
 | 
   940 function date.getters:iso_weekyear()  -- ISO week-numbering year
 | 
| 
jbe/bsw@0
 | 
   941   local year, month, day = self.year, self.month, self.day
 | 
| 
jbe/bsw@0
 | 
   942   local iso_weekday      = self.iso_weekday
 | 
| 
jbe/bsw@0
 | 
   943   if month == 1 then
 | 
| 
jbe/bsw@0
 | 
   944     if
 | 
| 
jbe/bsw@0
 | 
   945       (day == 3 and iso_weekday == 7) or
 | 
| 
jbe/bsw@0
 | 
   946       (day == 2 and iso_weekday >= 6) or
 | 
| 
jbe/bsw@0
 | 
   947       (day == 1 and iso_weekday >= 5)
 | 
| 
jbe/bsw@0
 | 
   948     then
 | 
| 
jbe/bsw@0
 | 
   949       return year - 1
 | 
| 
jbe/bsw@0
 | 
   950     end
 | 
| 
jbe/bsw@0
 | 
   951   elseif month == 12 then
 | 
| 
jbe/bsw@0
 | 
   952     if
 | 
| 
jbe/bsw@0
 | 
   953       (day == 29 and iso_weekday == 1) or
 | 
| 
jbe/bsw@0
 | 
   954       (day == 30 and iso_weekday <= 2) or
 | 
| 
jbe/bsw@0
 | 
   955       (day == 31 and iso_weekday <= 3)
 | 
| 
jbe/bsw@0
 | 
   956     then
 | 
| 
jbe/bsw@0
 | 
   957       return year + 1
 | 
| 
jbe/bsw@0
 | 
   958     end
 | 
| 
jbe/bsw@0
 | 
   959   end
 | 
| 
jbe/bsw@0
 | 
   960   return year
 | 
| 
jbe/bsw@0
 | 
   961 end
 | 
| 
jbe/bsw@0
 | 
   962 
 | 
| 
jbe/bsw@0
 | 
   963 function date.getters:iso_week()
 | 
| 
jbe/bsw@0
 | 
   964   local jan4 = date{ year = self.iso_weekyear, month = 1, day = 4 }
 | 
| 
jbe/bsw@0
 | 
   965   local reference = jan4.jd - jan4.iso_weekday - 6  -- monday 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.getters:us_week()
 | 
| 
jbe/bsw@0
 | 
   970   local jan1 = date{ year = self.year, month = 1, day = 1 }
 | 
| 
jbe/bsw@0
 | 
   971   local reference = jan1.jd - jan1.us_weekday - 6  -- sunday of week 0
 | 
| 
jbe/bsw@0
 | 
   972   return math.floor((self.jd - reference) / 7)
 | 
| 
jbe/bsw@0
 | 
   973 end
 | 
| 
jbe/bsw@0
 | 
   974 
 | 
| 
jbe/bsw@0
 | 
   975 function date.__eq(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   976   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
   977     return false
 | 
| 
jbe/bsw@0
 | 
   978   else
 | 
| 
jbe/bsw@0
 | 
   979     return value1.jd == value2.jd
 | 
| 
jbe/bsw@0
 | 
   980   end
 | 
| 
jbe/bsw@0
 | 
   981 end
 | 
| 
jbe/bsw@0
 | 
   982 
 | 
| 
jbe/bsw@0
 | 
   983 function date.__lt(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   984   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
   985     return false
 | 
| 
jbe/bsw@0
 | 
   986   else
 | 
| 
jbe/bsw@0
 | 
   987     return value1.jd < value2.jd
 | 
| 
jbe/bsw@0
 | 
   988   end
 | 
| 
jbe/bsw@0
 | 
   989 end
 | 
| 
jbe/bsw@0
 | 
   990 
 | 
| 
jbe/bsw@0
 | 
   991 function date.__le(value1, value2)
 | 
| 
jbe/bsw@0
 | 
   992   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
   993     return false
 | 
| 
jbe/bsw@0
 | 
   994   else
 | 
| 
jbe/bsw@0
 | 
   995     return value1.jd <= value2.jd
 | 
| 
jbe/bsw@0
 | 
   996   end
 | 
| 
jbe/bsw@0
 | 
   997 end
 | 
| 
jbe/bsw@0
 | 
   998 
 | 
| 
jbe/bsw@0
 | 
   999 function date.__add(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1000   if getmetatable(value1) == date then
 | 
| 
jbe/bsw@0
 | 
  1001     if getmetatable(value2) == date then
 | 
| 
jbe/bsw@0
 | 
  1002       error("Can not add two dates.")
 | 
| 
jbe/bsw@0
 | 
  1003     elseif type(value2) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1004       return date(value1.jd + value2)
 | 
| 
jbe/bsw@0
 | 
  1005     else
 | 
| 
jbe/bsw@0
 | 
  1006       error("Right operand of '+' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1007     end
 | 
| 
jbe/bsw@0
 | 
  1008   elseif type(value1) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1009     if getmetatable(value2) == date then
 | 
| 
jbe/bsw@0
 | 
  1010       return date(value1 + value2.jd)
 | 
| 
jbe/bsw@0
 | 
  1011     else
 | 
| 
jbe/bsw@0
 | 
  1012       error("Assertion failed")
 | 
| 
jbe/bsw@0
 | 
  1013     end
 | 
| 
jbe/bsw@0
 | 
  1014   else
 | 
| 
jbe/bsw@0
 | 
  1015     error("Left operand of '+' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1016   end
 | 
| 
jbe/bsw@0
 | 
  1017 end
 | 
| 
jbe/bsw@0
 | 
  1018 
 | 
| 
jbe/bsw@0
 | 
  1019 function date.__sub(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1020   if not getmetatable(value1) == date then
 | 
| 
jbe/bsw@0
 | 
  1021     error("Left operand of '-' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1022   end
 | 
| 
jbe/bsw@0
 | 
  1023   if getmetatable(value2) == date then
 | 
| 
jbe@107
 | 
  1024     return value1.jd - value2.jd  -- TODO: transform to interval
 | 
| 
jbe/bsw@0
 | 
  1025   elseif type(value2) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1026     return date(value1.jd - value2)
 | 
| 
jbe/bsw@0
 | 
  1027   else
 | 
| 
jbe/bsw@0
 | 
  1028     error("Right operand of '-' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1029   end
 | 
| 
jbe/bsw@0
 | 
  1030 end
 | 
| 
jbe/bsw@0
 | 
  1031 
 | 
| 
jbe/bsw@0
 | 
  1032 
 | 
| 
jbe/bsw@0
 | 
  1033 
 | 
| 
jbe/bsw@0
 | 
  1034 ---------------
 | 
| 
jbe/bsw@0
 | 
  1035 -- timestamp --
 | 
| 
jbe/bsw@0
 | 
  1036 ---------------
 | 
| 
jbe/bsw@0
 | 
  1037 
 | 
| 
jbe/bsw@0
 | 
  1038 timestamp = create_new_type("timestamp")
 | 
| 
jbe/bsw@0
 | 
  1039 
 | 
| 
jbe/bsw@0
 | 
  1040 --[[--
 | 
| 
jbe/bsw@0
 | 
  1041 tsec =                          -- seconds since January 1st 1970 00:00
 | 
| 
jbe/bsw@0
 | 
  1042 atom.timestamp.ymdhms_to_tsec(
 | 
| 
jbe/bsw@0
 | 
  1043   year,                         -- year
 | 
| 
jbe/bsw@0
 | 
  1044   month,                        -- month from 1 to 12
 | 
| 
jbe/bsw@0
 | 
  1045   day,                          -- day from 1 to 31
 | 
| 
jbe/bsw@0
 | 
  1046   hour,                         -- hour from 0 to 23
 | 
| 
jbe/bsw@0
 | 
  1047   minute,                       -- minute from 0 to 59
 | 
| 
jbe/bsw@0
 | 
  1048   second                        -- second from 0 to 59
 | 
| 
jbe/bsw@0
 | 
  1049 )
 | 
| 
jbe/bsw@0
 | 
  1050 
 | 
| 
jbe/bsw@0
 | 
  1051 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
 | 
  1052 
 | 
| 
jbe/bsw@0
 | 
  1053 --]]--
 | 
| 
jbe/bsw@0
 | 
  1054 function timestamp.ymdhms_to_tsec(year, month, day, hour, minute, second)
 | 
| 
jbe/bsw@0
 | 
  1055   return
 | 
| 
jbe/bsw@0
 | 
  1056     86400 * date.ymd_to_jd(year, month, day) +
 | 
| 
jbe/bsw@0
 | 
  1057     3600 * hour + 60 * minute + second
 | 
| 
jbe/bsw@0
 | 
  1058 end
 | 
| 
jbe/bsw@0
 | 
  1059 --//--
 | 
| 
jbe/bsw@0
 | 
  1060 
 | 
| 
jbe/bsw@0
 | 
  1061 --[[--
 | 
| 
jbe/bsw@0
 | 
  1062 year,                      -- year
 | 
| 
jbe/bsw@0
 | 
  1063 month,                     -- month from 1 to 12
 | 
| 
jbe/bsw@0
 | 
  1064 day,                       -- day from 1 to 31
 | 
| 
jbe/bsw@0
 | 
  1065 hour,                      -- hour from 0 to 23
 | 
| 
jbe/bsw@0
 | 
  1066 minute,                    -- minute from 0 to 59
 | 
| 
jbe/bsw@0
 | 
  1067 second =                   -- second from 0 to 59
 | 
| 
jbe/bsw@0
 | 
  1068 atom.timestamp.tsec_to_ymdhms(
 | 
| 
jbe/bsw@0
 | 
  1069   tsec                     -- seconds since January 1st 1970 00:00
 | 
| 
jbe/bsw@0
 | 
  1070 )
 | 
| 
jbe/bsw@0
 | 
  1071 
 | 
| 
jbe/bsw@0
 | 
  1072 Given the seconds since January 1st 1970 00:00, this function returns the year, month, day, hour, minute and second.
 | 
| 
jbe/bsw@0
 | 
  1073 
 | 
| 
jbe/bsw@0
 | 
  1074 --]]--
 | 
| 
jbe/bsw@0
 | 
  1075 function timestamp.tsec_to_ymdhms(tsec)
 | 
| 
jbe/bsw@0
 | 
  1076   local jd   = math.floor(tsec / 86400)
 | 
| 
jbe/bsw@0
 | 
  1077   local dsec = tsec % 86400
 | 
| 
jbe/bsw@0
 | 
  1078   local year, month, day = date.jd_to_ymd(jd)
 | 
| 
jbe/bsw@0
 | 
  1079   local hour   = math.floor(dsec / 3600)
 | 
| 
jbe/bsw@0
 | 
  1080   local minute = math.floor((dsec % 3600) / 60)
 | 
| 
jbe/bsw@0
 | 
  1081   local second = dsec % 60
 | 
| 
jbe/bsw@0
 | 
  1082   return year, month, day, hour, minute, second
 | 
| 
jbe/bsw@0
 | 
  1083 end
 | 
| 
jbe/bsw@0
 | 
  1084 --//--
 | 
| 
jbe/bsw@0
 | 
  1085 
 | 
| 
jbe/bsw@0
 | 
  1086 --[[--
 | 
| 
jbe@320
 | 
  1087 atom.timestamp.invalid
 | 
| 
jbe/bsw@0
 | 
  1088 
 | 
| 
jbe/bsw@0
 | 
  1089 Value representing an invalid timestamp.
 | 
| 
jbe/bsw@0
 | 
  1090 
 | 
| 
jbe/bsw@0
 | 
  1091 --]]--
 | 
| 
jbe/bsw@0
 | 
  1092 timestamp.invalid = timestamp:_create{
 | 
| 
jbe/bsw@0
 | 
  1093   tsec = not_a_number,
 | 
| 
jbe/bsw@0
 | 
  1094   year = not_a_number, month = not_a_number, day = not_a_number,
 | 
| 
jbe/bsw@0
 | 
  1095   hour = not_a_number, minute = not_a_number, second = not_a_number,
 | 
| 
jbe/bsw@0
 | 
  1096   invalid = true
 | 
| 
jbe/bsw@0
 | 
  1097 }
 | 
| 
jbe/bsw@0
 | 
  1098 --//--
 | 
| 
jbe/bsw@0
 | 
  1099 
 | 
| 
jbe/bsw@0
 | 
  1100 --[[--
 | 
| 
jbe/bsw@0
 | 
  1101 ts =                 -- timestamp based on given data
 | 
| 
jbe/bsw@0
 | 
  1102 atom.timestamp:new{
 | 
| 
jbe/bsw@0
 | 
  1103   tsec   = tsec,     -- seconds since January 1st 1970 00:00
 | 
| 
jbe/bsw@0
 | 
  1104   year   = year,     -- year
 | 
| 
jbe/bsw@0
 | 
  1105   month  = month,    -- month from 1 to 12
 | 
| 
jbe/bsw@0
 | 
  1106   day    = day,      -- day from 1 to 31
 | 
| 
jbe/bsw@0
 | 
  1107   hour   = hour,     -- hour from 0 to 23
 | 
| 
jbe/bsw@0
 | 
  1108   minute = minute,   -- minute from 0 to 59
 | 
| 
jbe/bsw@0
 | 
  1109   second = second    -- second from 0 to 59
 | 
| 
jbe/bsw@0
 | 
  1110 }
 | 
| 
jbe/bsw@0
 | 
  1111 
 | 
| 
jbe/bsw@0
 | 
  1112 This method returns a new timestamp value, based on given data.
 | 
| 
jbe/bsw@0
 | 
  1113 
 | 
| 
jbe/bsw@0
 | 
  1114 --]]--
 | 
| 
jbe/bsw@0
 | 
  1115 function timestamp:new(args)
 | 
| 
jbe/bsw@0
 | 
  1116   local args = args
 | 
| 
jbe/bsw@0
 | 
  1117   if type(args) == "number" then args = { tsec = args } end
 | 
| 
jbe/bsw@0
 | 
  1118   if type(args) == "table" then
 | 
| 
jbe/bsw@0
 | 
  1119     if not args.second then
 | 
| 
jbe/bsw@0
 | 
  1120       args.second = 0
 | 
| 
jbe/bsw@0
 | 
  1121       if not args.minute then
 | 
| 
jbe/bsw@0
 | 
  1122         args.minute = 0
 | 
| 
jbe/bsw@0
 | 
  1123         if not args.hour then
 | 
| 
jbe/bsw@0
 | 
  1124           args.hour = 0
 | 
| 
jbe/bsw@0
 | 
  1125         end
 | 
| 
jbe/bsw@0
 | 
  1126       end
 | 
| 
jbe/bsw@0
 | 
  1127     end
 | 
| 
jbe/bsw@0
 | 
  1128     if
 | 
| 
jbe/bsw@0
 | 
  1129       type(args.year)   == "number" and
 | 
| 
jbe/bsw@0
 | 
  1130       type(args.month)  == "number" and
 | 
| 
jbe/bsw@0
 | 
  1131       type(args.day)    == "number" and
 | 
| 
jbe/bsw@0
 | 
  1132       type(args.hour)   == "number" and
 | 
| 
jbe/bsw@0
 | 
  1133       type(args.minute) == "number" and
 | 
| 
jbe/bsw@0
 | 
  1134       type(args.second) == "number"
 | 
| 
jbe/bsw@0
 | 
  1135     then
 | 
| 
jbe/bsw@0
 | 
  1136       if
 | 
| 
jbe/bsw@0
 | 
  1137         is_integer(args.year) and
 | 
| 
jbe/bsw@0
 | 
  1138         args.year >= 1 and args.year <= 9999 and
 | 
| 
jbe/bsw@0
 | 
  1139         is_integer(args.month) and
 | 
| 
jbe/bsw@0
 | 
  1140         args.month >= 1 and args.month <= 12 and
 | 
| 
jbe/bsw@0
 | 
  1141         is_integer(args.day) and
 | 
| 
jbe/bsw@0
 | 
  1142         args.day >= 1 and args.day <= 31 and
 | 
| 
jbe/bsw@0
 | 
  1143         is_integer(args.hour) and
 | 
| 
jbe/bsw@0
 | 
  1144         args.hour >= 0 and args.hour <= 23 and
 | 
| 
jbe/bsw@0
 | 
  1145         is_integer(args.minute) and
 | 
| 
jbe/bsw@0
 | 
  1146         args.minute >= 0 and args.minute <= 59 and
 | 
| 
jbe/bsw@0
 | 
  1147         is_integer(args.second) and
 | 
| 
jbe/bsw@0
 | 
  1148         args.second >= 0 and args.second <= 59
 | 
| 
jbe/bsw@0
 | 
  1149       then
 | 
| 
jbe/bsw@0
 | 
  1150         return timestamp:_create{
 | 
| 
jbe/bsw@0
 | 
  1151           tsec = timestamp.ymdhms_to_tsec(
 | 
| 
jbe/bsw@0
 | 
  1152             args.year, args.month, args.day,
 | 
| 
jbe/bsw@0
 | 
  1153             args.hour, args.minute, args.second
 | 
| 
jbe/bsw@0
 | 
  1154           ),
 | 
| 
jbe/bsw@0
 | 
  1155           year   = args.year,
 | 
| 
jbe/bsw@0
 | 
  1156           month  = args.month,
 | 
| 
jbe/bsw@0
 | 
  1157           day    = args.day,
 | 
| 
jbe/bsw@0
 | 
  1158           hour   = args.hour,
 | 
| 
jbe/bsw@0
 | 
  1159           minute = args.minute,
 | 
| 
jbe/bsw@0
 | 
  1160           second = args.second
 | 
| 
jbe/bsw@0
 | 
  1161         }
 | 
| 
jbe/bsw@0
 | 
  1162       else
 | 
| 
jbe/bsw@0
 | 
  1163         return timestamp.invalid
 | 
| 
jbe/bsw@0
 | 
  1164       end
 | 
| 
jbe/bsw@0
 | 
  1165     elseif type(args.tsec) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1166       if
 | 
| 
jbe/bsw@0
 | 
  1167         is_integer(args.tsec) and
 | 
| 
jbe/bsw@0
 | 
  1168         args.tsec >= -62135596800 and args.tsec <= 253402300799
 | 
| 
jbe/bsw@0
 | 
  1169       then
 | 
| 
jbe/bsw@0
 | 
  1170         local year, month, day, hour, minute, second =
 | 
| 
jbe/bsw@0
 | 
  1171           timestamp.tsec_to_ymdhms(args.tsec)
 | 
| 
jbe/bsw@0
 | 
  1172         return timestamp:_create{
 | 
| 
jbe/bsw@0
 | 
  1173           tsec = args.tsec,
 | 
| 
jbe/bsw@0
 | 
  1174           year = year, month = month, day = day,
 | 
| 
jbe/bsw@0
 | 
  1175           hour = hour, minute = minute, second = second
 | 
| 
jbe/bsw@0
 | 
  1176         }
 | 
| 
jbe/bsw@0
 | 
  1177       else
 | 
| 
jbe/bsw@0
 | 
  1178         return timestamp.invalid
 | 
| 
jbe/bsw@0
 | 
  1179       end
 | 
| 
jbe/bsw@0
 | 
  1180     end
 | 
| 
jbe/bsw@0
 | 
  1181   end
 | 
| 
jbe/bsw@0
 | 
  1182   error("Invalid arguments passed to timestamp constructor.")
 | 
| 
jbe/bsw@0
 | 
  1183 end
 | 
| 
jbe/bsw@0
 | 
  1184 --//--
 | 
| 
jbe/bsw@0
 | 
  1185 
 | 
| 
jbe/bsw@0
 | 
  1186 --[[--
 | 
| 
jbe/bsw@0
 | 
  1187 ts =                          -- current date/time as timestamp
 | 
| 
jbe/bsw@0
 | 
  1188 atom.timestamp:get_current()
 | 
| 
jbe/bsw@0
 | 
  1189 
 | 
| 
jbe/bsw@0
 | 
  1190 This function returns the current date and time as timestamp.
 | 
| 
jbe/bsw@0
 | 
  1191 
 | 
| 
jbe/bsw@0
 | 
  1192 --]]--
 | 
| 
jbe/bsw@0
 | 
  1193 function timestamp:get_current()
 | 
| 
jbe/bsw@0
 | 
  1194   local now = os.date("*t")
 | 
| 
jbe/bsw@0
 | 
  1195   return timestamp{
 | 
| 
jbe/bsw@0
 | 
  1196     year = now.year, month = now.month, day = now.day,
 | 
| 
jbe/bsw@0
 | 
  1197     hour = now.hour, minute = now.min, second = now.sec
 | 
| 
jbe/bsw@0
 | 
  1198   }
 | 
| 
jbe/bsw@0
 | 
  1199 end
 | 
| 
jbe/bsw@0
 | 
  1200 --//--
 | 
| 
jbe/bsw@0
 | 
  1201 
 | 
| 
jbe/bsw@0
 | 
  1202 --[[--
 | 
| 
jbe/bsw@0
 | 
  1203 ts =             -- timestamp represented by the string
 | 
| 
jbe/bsw@0
 | 
  1204 atom.timestamp:load(
 | 
| 
jbe/bsw@0
 | 
  1205   string         -- string representing a timestamp
 | 
| 
jbe/bsw@0
 | 
  1206 )
 | 
| 
jbe/bsw@0
 | 
  1207 
 | 
| 
jbe/bsw@0
 | 
  1208 This method returns a timestamp represented by the given string.
 | 
| 
jbe/bsw@0
 | 
  1209 
 | 
| 
jbe/bsw@0
 | 
  1210 --]]--
 | 
| 
jbe/bsw@0
 | 
  1211 function timestamp:load(str)
 | 
| 
jbe@1
 | 
  1212   if str == nil or str == "" then
 | 
| 
jbe/bsw@0
 | 
  1213     return nil
 | 
| 
jbe@1
 | 
  1214   elseif type(str) ~= "string" then
 | 
| 
jbe@1
 | 
  1215     error("String expected")
 | 
| 
jbe/bsw@0
 | 
  1216   else
 | 
| 
jbe/bsw@0
 | 
  1217     local year, month, day, hour, minute, second = string.match(
 | 
| 
jbe/bsw@0
 | 
  1218       str,
 | 
| 
jbe/bsw@0
 | 
  1219       "^([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
 | 
  1220     )
 | 
| 
jbe/bsw@0
 | 
  1221     if year then
 | 
| 
jbe/bsw@0
 | 
  1222       return timestamp{
 | 
| 
jbe/bsw@0
 | 
  1223         year   = tonumber(year),
 | 
| 
jbe/bsw@0
 | 
  1224         month  = tonumber(month),
 | 
| 
jbe/bsw@0
 | 
  1225         day    = tonumber(day),
 | 
| 
jbe/bsw@0
 | 
  1226         hour   = tonumber(hour),
 | 
| 
jbe/bsw@0
 | 
  1227         minute = tonumber(minute),
 | 
| 
jbe/bsw@0
 | 
  1228         second = tonumber(second)
 | 
| 
jbe/bsw@0
 | 
  1229       }
 | 
| 
jbe/bsw@0
 | 
  1230     else
 | 
| 
jbe/bsw@0
 | 
  1231       return timestamp.invalid
 | 
| 
jbe/bsw@0
 | 
  1232     end
 | 
| 
jbe/bsw@0
 | 
  1233   end
 | 
| 
jbe/bsw@0
 | 
  1234 end
 | 
| 
jbe/bsw@0
 | 
  1235 
 | 
| 
jbe/bsw@0
 | 
  1236 function timestamp:__tostring()
 | 
| 
jbe/bsw@0
 | 
  1237   if self.invalid then
 | 
| 
jbe/bsw@0
 | 
  1238     return "invalid_timestamp"
 | 
| 
jbe/bsw@0
 | 
  1239   else
 | 
| 
jbe/bsw@0
 | 
  1240     return string.format(
 | 
| 
jbe/bsw@0
 | 
  1241       "%04i-%02i-%02i %02i:%02i:%02i",
 | 
| 
jbe/bsw@0
 | 
  1242       self.year, self.month, self.day, self.hour, self.minute, self.second
 | 
| 
jbe/bsw@0
 | 
  1243     )
 | 
| 
jbe/bsw@0
 | 
  1244   end
 | 
| 
jbe/bsw@0
 | 
  1245 end
 | 
| 
jbe/bsw@0
 | 
  1246 
 | 
| 
jbe/bsw@0
 | 
  1247 function timestamp.getters:date()
 | 
| 
jbe/bsw@0
 | 
  1248   return date{ year = self.year, month = self.month, day = self.day }
 | 
| 
jbe/bsw@0
 | 
  1249 end
 | 
| 
jbe/bsw@0
 | 
  1250 
 | 
| 
jbe/bsw@0
 | 
  1251 function timestamp.getters:time()
 | 
| 
jbe/bsw@0
 | 
  1252   return time{
 | 
| 
jbe/bsw@0
 | 
  1253     hour = self.hour,
 | 
| 
jbe/bsw@0
 | 
  1254     minute = self.minute,
 | 
| 
jbe/bsw@0
 | 
  1255     second = self.second
 | 
| 
jbe/bsw@0
 | 
  1256   }
 | 
| 
jbe/bsw@0
 | 
  1257 end
 | 
| 
jbe/bsw@0
 | 
  1258 
 | 
| 
jbe/bsw@0
 | 
  1259 function timestamp.__eq(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1260   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
  1261     return false
 | 
| 
jbe/bsw@0
 | 
  1262   else
 | 
| 
jbe/bsw@0
 | 
  1263     return value1.tsec == value2.tsec
 | 
| 
jbe/bsw@0
 | 
  1264   end
 | 
| 
jbe/bsw@0
 | 
  1265 end
 | 
| 
jbe/bsw@0
 | 
  1266 
 | 
| 
jbe/bsw@0
 | 
  1267 function timestamp.__lt(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1268   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
  1269     return false
 | 
| 
jbe/bsw@0
 | 
  1270   else
 | 
| 
jbe/bsw@0
 | 
  1271     return value1.tsec < value2.tsec
 | 
| 
jbe/bsw@0
 | 
  1272   end
 | 
| 
jbe/bsw@0
 | 
  1273 end
 | 
| 
jbe/bsw@0
 | 
  1274 
 | 
| 
jbe/bsw@0
 | 
  1275 function timestamp.__le(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1276   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
  1277     return false
 | 
| 
jbe/bsw@0
 | 
  1278   else
 | 
| 
jbe/bsw@0
 | 
  1279     return value1.tsec <= value2.tsec
 | 
| 
jbe/bsw@0
 | 
  1280   end
 | 
| 
jbe/bsw@0
 | 
  1281 end
 | 
| 
jbe/bsw@0
 | 
  1282 
 | 
| 
jbe/bsw@0
 | 
  1283 function timestamp.__add(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1284   if getmetatable(value1) == timestamp then
 | 
| 
jbe/bsw@0
 | 
  1285     if getmetatable(value2) == timestamp then
 | 
| 
jbe/bsw@0
 | 
  1286       error("Can not add two timestamps.")
 | 
| 
jbe/bsw@0
 | 
  1287     elseif type(value2) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1288       return timestamp(value1.tsec + value2)
 | 
| 
jbe/bsw@0
 | 
  1289     else
 | 
| 
jbe/bsw@0
 | 
  1290       error("Right operand of '+' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1291     end
 | 
| 
jbe/bsw@0
 | 
  1292   elseif type(value1) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1293     if getmetatable(value2) == timestamp then
 | 
| 
jbe/bsw@0
 | 
  1294       return timestamp(value1 + value2.tsec)
 | 
| 
jbe/bsw@0
 | 
  1295     else
 | 
| 
jbe/bsw@0
 | 
  1296       error("Assertion failed")
 | 
| 
jbe/bsw@0
 | 
  1297     end
 | 
| 
jbe/bsw@0
 | 
  1298   else
 | 
| 
jbe/bsw@0
 | 
  1299     error("Left operand of '+' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1300   end
 | 
| 
jbe/bsw@0
 | 
  1301 end
 | 
| 
jbe/bsw@0
 | 
  1302 
 | 
| 
jbe/bsw@0
 | 
  1303 function timestamp.__sub(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1304   if not getmetatable(value1) == timestamp then
 | 
| 
jbe/bsw@0
 | 
  1305     error("Left operand of '-' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1306   end
 | 
| 
jbe/bsw@0
 | 
  1307   if getmetatable(value2) == timestamp then
 | 
| 
jbe@107
 | 
  1308     return value1.tsec - value2.tsec  -- TODO: transform to interval
 | 
| 
jbe/bsw@0
 | 
  1309   elseif type(value2) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1310     return timestamp(value1.tsec - value2)
 | 
| 
jbe/bsw@0
 | 
  1311   else
 | 
| 
jbe/bsw@0
 | 
  1312     error("Right operand of '-' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1313   end
 | 
| 
jbe/bsw@0
 | 
  1314 end
 | 
| 
jbe/bsw@0
 | 
  1315 
 | 
| 
jbe/bsw@0
 | 
  1316 
 | 
| 
jbe/bsw@0
 | 
  1317 
 | 
| 
jbe/bsw@0
 | 
  1318 ----------
 | 
| 
jbe/bsw@0
 | 
  1319 -- time --
 | 
| 
jbe/bsw@0
 | 
  1320 ----------
 | 
| 
jbe/bsw@0
 | 
  1321 
 | 
| 
jbe/bsw@0
 | 
  1322 time = create_new_type("time")
 | 
| 
jbe/bsw@0
 | 
  1323 
 | 
| 
jbe/bsw@0
 | 
  1324 function time.hms_to_dsec(hour, minute, second)
 | 
| 
jbe/bsw@0
 | 
  1325   return 3600 * hour + 60 * minute + second
 | 
| 
jbe/bsw@0
 | 
  1326 end
 | 
| 
jbe/bsw@0
 | 
  1327 
 | 
| 
jbe/bsw@0
 | 
  1328 function time.dsec_to_hms(dsec)
 | 
| 
jbe/bsw@0
 | 
  1329   local hour   = math.floor(dsec / 3600)
 | 
| 
jbe/bsw@0
 | 
  1330   local minute = math.floor((dsec % 3600) / 60)
 | 
| 
jbe/bsw@0
 | 
  1331   local second = dsec % 60
 | 
| 
jbe/bsw@0
 | 
  1332   return hour, minute, second
 | 
| 
jbe/bsw@0
 | 
  1333 end
 | 
| 
jbe/bsw@0
 | 
  1334 
 | 
| 
jbe/bsw@0
 | 
  1335 --[[--
 | 
| 
jbe/bsw@0
 | 
  1336 atom.time.invalid
 | 
| 
jbe/bsw@0
 | 
  1337 
 | 
| 
jbe/bsw@0
 | 
  1338 Value representing an invalid time of day.
 | 
| 
jbe/bsw@0
 | 
  1339 
 | 
| 
jbe/bsw@0
 | 
  1340 --]]--
 | 
| 
jbe/bsw@0
 | 
  1341 time.invalid = time:_create{
 | 
| 
jbe/bsw@0
 | 
  1342   dsec = not_a_number,
 | 
| 
jbe/bsw@0
 | 
  1343   hour = not_a_number, minute = not_a_number, second = not_a_number,
 | 
| 
jbe/bsw@0
 | 
  1344   invalid = true
 | 
| 
jbe/bsw@0
 | 
  1345 }
 | 
| 
jbe/bsw@0
 | 
  1346 --//--
 | 
| 
jbe/bsw@0
 | 
  1347 
 | 
| 
jbe/bsw@0
 | 
  1348 --[[--
 | 
| 
jbe/bsw@0
 | 
  1349 t =                 -- time based on given data
 | 
| 
jbe/bsw@0
 | 
  1350 atom.time:new{
 | 
| 
jbe/bsw@0
 | 
  1351   dsec = dsec,      -- seconds since 00:00:00
 | 
| 
jbe/bsw@0
 | 
  1352   hour = hour,      -- hour from 0 to 23
 | 
| 
jbe/bsw@0
 | 
  1353   minute = minute,  -- minute from 0 to 59
 | 
| 
jbe/bsw@0
 | 
  1354   second = second   -- second from 0 to 59
 | 
| 
jbe/bsw@0
 | 
  1355 }
 | 
| 
jbe/bsw@0
 | 
  1356 
 | 
| 
jbe/bsw@0
 | 
  1357 This method returns a new time value, based on given data.
 | 
| 
jbe/bsw@0
 | 
  1358 
 | 
| 
jbe/bsw@0
 | 
  1359 --]]--
 | 
| 
jbe/bsw@0
 | 
  1360 function time:new(args)
 | 
| 
jbe/bsw@0
 | 
  1361   local args = args
 | 
| 
jbe/bsw@0
 | 
  1362   if type(args) == "number" then args = { dsec = args } end
 | 
| 
jbe/bsw@0
 | 
  1363   if type(args) == "table" then
 | 
| 
jbe/bsw@0
 | 
  1364     if not args.second then
 | 
| 
jbe/bsw@0
 | 
  1365       args.second = 0
 | 
| 
jbe/bsw@0
 | 
  1366       if not args.minute then
 | 
| 
jbe/bsw@0
 | 
  1367         args.minute = 0
 | 
| 
jbe/bsw@0
 | 
  1368       end
 | 
| 
jbe/bsw@0
 | 
  1369     end
 | 
| 
jbe/bsw@0
 | 
  1370     if
 | 
| 
jbe/bsw@0
 | 
  1371       type(args.hour)   == "number" and
 | 
| 
jbe/bsw@0
 | 
  1372       type(args.minute) == "number" and
 | 
| 
jbe/bsw@0
 | 
  1373       type(args.second) == "number"
 | 
| 
jbe/bsw@0
 | 
  1374     then
 | 
| 
jbe/bsw@0
 | 
  1375       if
 | 
| 
jbe/bsw@0
 | 
  1376         is_integer(args.hour) and
 | 
| 
jbe/bsw@0
 | 
  1377         args.hour >= 0 and args.hour <= 23 and
 | 
| 
jbe/bsw@0
 | 
  1378         is_integer(args.minute) and
 | 
| 
jbe/bsw@0
 | 
  1379         args.minute >= 0 and args.minute <= 59 and
 | 
| 
jbe/bsw@0
 | 
  1380         is_integer(args.second) and
 | 
| 
jbe/bsw@0
 | 
  1381         args.second >= 0 and args.second <= 59
 | 
| 
jbe/bsw@0
 | 
  1382       then
 | 
| 
jbe/bsw@0
 | 
  1383         return time:_create{
 | 
| 
jbe/bsw@0
 | 
  1384           dsec = time.hms_to_dsec(args.hour, args.minute, args.second),
 | 
| 
jbe/bsw@0
 | 
  1385           hour   = args.hour,
 | 
| 
jbe/bsw@0
 | 
  1386           minute = args.minute,
 | 
| 
jbe/bsw@0
 | 
  1387           second = args.second
 | 
| 
jbe/bsw@0
 | 
  1388         }
 | 
| 
jbe/bsw@0
 | 
  1389       else
 | 
| 
jbe/bsw@0
 | 
  1390         return time.invalid
 | 
| 
jbe/bsw@0
 | 
  1391       end
 | 
| 
jbe/bsw@0
 | 
  1392     elseif type(args.dsec) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1393       if
 | 
| 
jbe/bsw@0
 | 
  1394         is_integer(args.dsec) and
 | 
| 
jbe/bsw@0
 | 
  1395         args.dsec >= 0 and args.dsec <= 86399
 | 
| 
jbe/bsw@0
 | 
  1396       then
 | 
| 
jbe/bsw@0
 | 
  1397         local hour, minute, second =
 | 
| 
jbe/bsw@0
 | 
  1398           time.dsec_to_hms(args.dsec)
 | 
| 
jbe/bsw@0
 | 
  1399         return time:_create{
 | 
| 
jbe/bsw@0
 | 
  1400           dsec = args.dsec,
 | 
| 
jbe/bsw@0
 | 
  1401           hour = hour, minute = minute, second = second
 | 
| 
jbe/bsw@0
 | 
  1402         }
 | 
| 
jbe/bsw@0
 | 
  1403       else
 | 
| 
jbe/bsw@0
 | 
  1404         return time.invalid
 | 
| 
jbe/bsw@0
 | 
  1405       end
 | 
| 
jbe/bsw@0
 | 
  1406     end
 | 
| 
jbe/bsw@0
 | 
  1407   end
 | 
| 
jbe/bsw@0
 | 
  1408   error("Invalid arguments passed to time constructor.")
 | 
| 
jbe/bsw@0
 | 
  1409 end
 | 
| 
jbe/bsw@0
 | 
  1410 --//--
 | 
| 
jbe/bsw@0
 | 
  1411 
 | 
| 
jbe/bsw@0
 | 
  1412 --[[--
 | 
| 
jbe/bsw@0
 | 
  1413 t =                      -- current time of day
 | 
| 
jbe/bsw@0
 | 
  1414 atom.time:get_current()
 | 
| 
jbe/bsw@0
 | 
  1415 
 | 
| 
jbe/bsw@0
 | 
  1416 This method returns the current time of the day.
 | 
| 
jbe/bsw@0
 | 
  1417 
 | 
| 
jbe/bsw@0
 | 
  1418 --]]--
 | 
| 
jbe/bsw@0
 | 
  1419 function time:get_current()
 | 
| 
jbe/bsw@0
 | 
  1420   local now = os.date("*t")
 | 
| 
jbe/bsw@0
 | 
  1421   return time{ hour = now.hour, minute = now.min, second = now.sec }
 | 
| 
jbe/bsw@0
 | 
  1422 end
 | 
| 
jbe/bsw@0
 | 
  1423 --//--
 | 
| 
jbe/bsw@0
 | 
  1424 
 | 
| 
jbe/bsw@0
 | 
  1425 --[[--
 | 
| 
jbe/bsw@0
 | 
  1426 t =              -- time represented by the string
 | 
| 
jbe/bsw@0
 | 
  1427 atom.time:load(
 | 
| 
jbe/bsw@0
 | 
  1428   string         -- string representing a time of day
 | 
| 
jbe/bsw@0
 | 
  1429 )
 | 
| 
jbe/bsw@0
 | 
  1430 
 | 
| 
jbe/bsw@0
 | 
  1431 This method returns a time represented by the given string.
 | 
| 
jbe/bsw@0
 | 
  1432 
 | 
| 
jbe/bsw@0
 | 
  1433 --]]--
 | 
| 
jbe/bsw@0
 | 
  1434 function time:load(str)
 | 
| 
jbe@1
 | 
  1435   if str == nil or str == "" then
 | 
| 
jbe/bsw@0
 | 
  1436     return nil
 | 
| 
jbe@1
 | 
  1437   elseif type(str) ~= "string" then
 | 
| 
jbe@1
 | 
  1438     error("String expected")
 | 
| 
jbe/bsw@0
 | 
  1439   else
 | 
| 
jbe/bsw@0
 | 
  1440     local hour, minute, second = string.match(
 | 
| 
jbe/bsw@0
 | 
  1441       str,
 | 
| 
jbe/bsw@0
 | 
  1442       "^([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$"
 | 
| 
jbe/bsw@0
 | 
  1443     )
 | 
| 
jbe/bsw@0
 | 
  1444     if hour then
 | 
| 
jbe/bsw@0
 | 
  1445       return time{
 | 
| 
jbe/bsw@0
 | 
  1446         hour   = tonumber(hour),
 | 
| 
jbe/bsw@0
 | 
  1447         minute = tonumber(minute),
 | 
| 
jbe/bsw@0
 | 
  1448         second = tonumber(second)
 | 
| 
jbe/bsw@0
 | 
  1449       }
 | 
| 
jbe/bsw@0
 | 
  1450     else
 | 
| 
jbe/bsw@0
 | 
  1451       return time.invalid
 | 
| 
jbe/bsw@0
 | 
  1452     end
 | 
| 
jbe/bsw@0
 | 
  1453   end
 | 
| 
jbe/bsw@0
 | 
  1454 end
 | 
| 
jbe/bsw@0
 | 
  1455 --//--
 | 
| 
jbe/bsw@0
 | 
  1456 
 | 
| 
jbe/bsw@0
 | 
  1457 function time:__tostring()
 | 
| 
jbe/bsw@0
 | 
  1458   if self.invalid then
 | 
| 
jbe/bsw@0
 | 
  1459     return "invalid_time"
 | 
| 
jbe/bsw@0
 | 
  1460   else
 | 
| 
jbe/bsw@0
 | 
  1461     return string.format(
 | 
| 
jbe/bsw@0
 | 
  1462       "%02i:%02i:%02i",
 | 
| 
jbe/bsw@0
 | 
  1463       self.hour, self.minute, self.second
 | 
| 
jbe/bsw@0
 | 
  1464     )
 | 
| 
jbe/bsw@0
 | 
  1465   end
 | 
| 
jbe/bsw@0
 | 
  1466 end
 | 
| 
jbe/bsw@0
 | 
  1467 
 | 
| 
jbe/bsw@0
 | 
  1468 function time.__eq(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1469   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
  1470     return false
 | 
| 
jbe/bsw@0
 | 
  1471   else
 | 
| 
jbe/bsw@0
 | 
  1472     return value1.dsec == value2.dsec
 | 
| 
jbe/bsw@0
 | 
  1473   end
 | 
| 
jbe/bsw@0
 | 
  1474 end
 | 
| 
jbe/bsw@0
 | 
  1475 
 | 
| 
jbe/bsw@0
 | 
  1476 function time.__lt(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1477   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
  1478     return false
 | 
| 
jbe/bsw@0
 | 
  1479   else
 | 
| 
jbe/bsw@0
 | 
  1480     return value1.dsec < value2.dsec
 | 
| 
jbe/bsw@0
 | 
  1481   end
 | 
| 
jbe/bsw@0
 | 
  1482 end
 | 
| 
jbe/bsw@0
 | 
  1483 
 | 
| 
jbe/bsw@0
 | 
  1484 function time.__le(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1485   if value1.invalid or value2.invalid then
 | 
| 
jbe/bsw@0
 | 
  1486     return false
 | 
| 
jbe/bsw@0
 | 
  1487   else
 | 
| 
jbe/bsw@0
 | 
  1488     return value1.dsec <= value2.dsec
 | 
| 
jbe/bsw@0
 | 
  1489   end
 | 
| 
jbe/bsw@0
 | 
  1490 end
 | 
| 
jbe/bsw@0
 | 
  1491 
 | 
| 
jbe/bsw@0
 | 
  1492 function time.__add(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1493   if getmetatable(value1) == time then
 | 
| 
jbe/bsw@0
 | 
  1494     if getmetatable(value2) == time then
 | 
| 
jbe/bsw@0
 | 
  1495       error("Can not add two times.")
 | 
| 
jbe/bsw@0
 | 
  1496     elseif type(value2) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1497       return time((value1.dsec + value2) % 86400)
 | 
| 
jbe/bsw@0
 | 
  1498     else
 | 
| 
jbe/bsw@0
 | 
  1499       error("Right operand of '+' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1500     end
 | 
| 
jbe/bsw@0
 | 
  1501   elseif type(value1) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1502     if getmetatable(value2) == time then
 | 
| 
jbe/bsw@0
 | 
  1503       return time((value1 + value2.dsec) % 86400)
 | 
| 
jbe/bsw@0
 | 
  1504     else
 | 
| 
jbe/bsw@0
 | 
  1505       error("Assertion failed")
 | 
| 
jbe/bsw@0
 | 
  1506     end
 | 
| 
jbe/bsw@0
 | 
  1507   else
 | 
| 
jbe/bsw@0
 | 
  1508     error("Left operand of '+' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1509   end
 | 
| 
jbe/bsw@0
 | 
  1510 end
 | 
| 
jbe/bsw@0
 | 
  1511 
 | 
| 
jbe/bsw@0
 | 
  1512 function time.__sub(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1513   if not getmetatable(value1) == time then
 | 
| 
jbe/bsw@0
 | 
  1514     error("Left operand of '-' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1515   end
 | 
| 
jbe/bsw@0
 | 
  1516   if getmetatable(value2) == time then
 | 
| 
jbe@107
 | 
  1517     return value1.dsec - value2.dsec  -- TODO: transform to interval
 | 
| 
jbe/bsw@0
 | 
  1518   elseif type(value2) == "number" then
 | 
| 
jbe/bsw@0
 | 
  1519     return time((value1.dsec - value2) % 86400)
 | 
| 
jbe/bsw@0
 | 
  1520   else
 | 
| 
jbe/bsw@0
 | 
  1521     error("Right operand of '-' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1522   end
 | 
| 
jbe/bsw@0
 | 
  1523 end
 | 
| 
jbe/bsw@0
 | 
  1524 
 | 
| 
jbe/bsw@0
 | 
  1525 function time.__concat(value1, value2)
 | 
| 
jbe/bsw@0
 | 
  1526   local mt1, mt2 = getmetatable(value1), getmetatable(value2)
 | 
| 
jbe/bsw@0
 | 
  1527   if mt1 == date and mt2 == time then
 | 
| 
jbe/bsw@0
 | 
  1528     return timestamp{
 | 
| 
jbe/bsw@0
 | 
  1529       year = value1.year, month = value1.month, day = value1.day,
 | 
| 
jbe/bsw@0
 | 
  1530       hour = value2.hour, minute = value2.minute, second = value2.second
 | 
| 
jbe/bsw@0
 | 
  1531     }
 | 
| 
jbe/bsw@0
 | 
  1532   elseif mt1 == time and mt2 == date then
 | 
| 
jbe/bsw@0
 | 
  1533     return timestamp{
 | 
| 
jbe/bsw@0
 | 
  1534       year = value2.year, month = value2.month, day = value2.day,
 | 
| 
jbe/bsw@0
 | 
  1535       hour = value1.hour, minute = value1.minute, second = value1.second
 | 
| 
jbe/bsw@0
 | 
  1536     }
 | 
| 
jbe/bsw@0
 | 
  1537   elseif mt1 == time then
 | 
| 
jbe/bsw@0
 | 
  1538     error("Right operand of '..' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1539   elseif mt2 == time then
 | 
| 
jbe/bsw@0
 | 
  1540     error("Left operand of '..' operator has wrong type.")
 | 
| 
jbe/bsw@0
 | 
  1541   else
 | 
| 
jbe/bsw@0
 | 
  1542     error("Assertion failed")
 | 
| 
jbe/bsw@0
 | 
  1543   end
 | 
| 
jbe/bsw@0
 | 
  1544 end
 | 
| 
jbe/bsw@0
 | 
  1545 
 | 
| 
jbe@64
 | 
  1546 
 | 
| 
jbe@64
 | 
  1547 
 | 
| 
jbe@64
 | 
  1548 return _M
 |