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@320
|
1081 atom.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
|