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