webmcp

annotate libraries/atom/atom.lua @ 270:aedd13009ddc

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

Impressum / About Us