webmcp

annotate libraries/atom/atom.lua @ 0:9fdfb27f8e67

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

Impressum / About Us