webmcp

annotate libraries/atom/atom.lua @ 64:3d43a5cf17c1

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

Impressum / About Us