webmcp

annotate libraries/atom/atom.lua @ 531:b19a6b4f61f3

Reject nonexistent dates in atom.date:new{...}
author jbe
date Mon Jan 15 20:13:32 2018 +0100 (2018-01-15)
parents e3e2a03f75b2
children
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

Impressum / About Us