Name | Executed | Routines | % | Executed | Lines | % | Unexecuted |
/home/matt/eu/rds/include/std/datetime.e | 29 | 29 | 100.00% | 288 | 317 | 90.85% | 29 |
Routine | Executed | Lines | Unexecuted | |
julianDate() | 13 | 21 | 61.90% | 8 |
add() | 30 | 34 | 88.24% | 4 |
format() | 70 | 74 | 94.59% | 4 |
julianDayOfYear() | 12 | 16 | 75.00% | 4 |
julianDay() | 8 | 11 | 72.73% | 3 |
parse() | 46 | 49 | 93.88% | 3 |
daysInMonth() | 5 | 6 | 83.33% | 1 |
daysInYear() | 3 | 4 | 75.00% | 1 |
isLeap() | 5 | 6 | 83.33% | 1 |
datetime() | 21 | 21 | 100.00% | 0 |
datetimeToSeconds() | 2 | 2 | 100.00% | 0 |
days_in_month() | 2 | 2 | 100.00% | 0 |
days_in_year() | 2 | 2 | 100.00% | 0 |
diff() | 2 | 2 | 100.00% | 0 |
from_date() | 2 | 2 | 100.00% | 0 |
from_unix() | 2 | 2 | 100.00% | 0 |
gmtime() | 12 | 12 | 100.00% | 0 |
is_leap_year() | 2 | 2 | 100.00% | 0 |
new() | 5 | 5 | 100.00% | 0 |
new_time() | 2 | 2 | 100.00% | 0 |
now() | 2 | 2 | 100.00% | 0 |
now_gmt() | 3 | 3 | 100.00% | 0 |
secondsToDateTime() | 8 | 8 | 100.00% | 0 |
subtract() | 2 | 2 | 100.00% | 0 |
time() | 3 | 3 | 100.00% | 0 |
to_unix() | 2 | 2 | 100.00% | 0 |
tolower() | 2 | 2 | 100.00% | 0 |
weeks_day() | 2 | 2 | 100.00% | 0 |
years_day() | 2 | 2 | 100.00% | 0 |
# | Executed | |
1 | -- (c) Copyright - See License.txt | |
2 | -- | |
3 | --**** | |
4 | -- == Date/Time | |
5 | -- | |
6 | -- < | |
7 | ||
8 | -- | |
9 | namespace datetime | |
10 | ||
11 | ||
12 | include std/machine.e | |
13 | include std/dll.e | |
14 | include std/sequence.e | |
15 | include std/get.e | |
16 | include std/error.e | |
17 | include std/types.e | |
18 | ||
19 | 101 | ifdef LINUX then |
20 | 101 | constant gmtime_ = define_c_func(open_dll(""), "gmtime", {C_POINTER}, C_POINTER) |
21 | 101 | constant time_ = define_c_func(open_dll(""), "time", {C_POINTER}, C_INT) |
22 | elsifdef FREEBSD or SUNOS or OPENBSD then | |
23 | constant gmtime_ = define_c_func(open_dll("libc.so"), "gmtime", {C_POINTER}, C_POINTER) | |
24 | constant time_ = define_c_func(open_dll("libc.so"), "time", {C_POINTER}, C_INT) | |
25 | elsifdef OSX then | |
26 | constant gmtime_ = define_c_func(open_dll("libc.dylib"), "gmtime", {C_POINTER}, C_POINTER) | |
27 | constant time_ = define_c_func(open_dll("libc.dylib"), "time", {C_POINTER}, C_INT) | |
28 | elsifdef WIN32 then | |
29 | constant gmtime_ = define_c_func(open_dll("msvcrt.dll"), "gmtime", {C_POINTER}, C_POINTER) | |
30 | constant time_ = define_c_proc(open_dll("kernel32.dll"), "GetSystemTimeAsFileTime", {C_POINTER}) | |
31 | end ifdef | |
32 | ||
33 | 101 | enum TM_SEC, TM_MIN, TM_HOUR, TM_MDAY, TM_MON, TM_YEAR --, TM_WDAY, TM_YDAY, TM_ISDST |
34 | ||
35 | 5 | |
36 | 5 | ifdef WIN32 then |
37 | atom ptra, valhi, vallow, deltahi, deltalow | |
38 | deltahi = 27111902 | |
39 | deltalow = 3577643008 | |
40 | ptra = allocate(8) | |
41 | c_proc(time_, {ptra}) | |
42 | vallow = peek4u(ptra) | |
43 | valhi = peek4u(ptra+4) | |
44 | free(ptra) | |
45 | vallow -= deltalow | |
46 | valhi -= deltahi | |
47 | if vallow < 0 then | |
48 | vallow += power(2, 32) | |
49 | valhi -= 1 | |
50 | end if | |
51 | return floor(((valhi * power(2,32)) + vallow) / 10000000) | |
52 | elsedef | |
53 | 5 | return c_func(time_, {NULL}) |
54 | end ifdef | |
55 | end function | |
56 | ||
57 | 5 | |
58 | sequence ret | |
59 | atom timep, tm_p | |
60 | integer n | |
61 | ||
62 | 5 | timep = allocate(4) |
63 | 5 | poke4(timep, time) |
64 | ||
65 | 5 | tm_p = c_func(gmtime_, {timep}) |
66 | ||
67 | 5 | free(timep) |
68 | ||
69 | 5 | ret = repeat(0, 9) |
70 | 5 | n = 0 |
71 | ||
72 | 5 | for i = 1 to 9 do |
73 | 45 | ret[i] = peek4s(tm_p+n) |
74 | 45 | n = n + 4 |
75 | 45 | end for |
76 | ||
77 | 5 | return ret |
78 | end function | |
79 | ||
80 | constant | |
81 | 101 | Gregorian_Reformation = 1752, |
82 | 101 | Gregorian_Reformation00 = 1700, |
83 | 101 | DaysPerMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, |
84 | 101 | EPOCH_1970 = 62135856000, |
85 | 101 | DayLengthInSeconds = 86400 |
86 | ||
87 | -- Helpers ------------------------------------------------------------------ | |
88 | ||
89 | 24 | |
90 | 24 | return x + (x >= 'A' and x <= 'Z') * ('a' - 'A') |
91 | end function | |
92 | ||
93 | -- Date Handling ------------------------------------------------------------ | |
94 | ||
95 | 89 | |
96 | sequence ly | |
97 | ||
98 | 89 | ly = (remainder(year, {4, 100, 400, 3200, 80000})=0) |
99 | ||
100 | 89 | if not ly[1] then return 0 end if |
101 | ||
102 | 71 | if year <= Gregorian_Reformation then |
103 | 0 | return 1 -- ly[1] can't possibly be 0 here so set shortcut as '1'. |
104 | else | |
105 | 71 | return ly[1] - ly[2] + ly[3] - ly[4] + ly[5] |
106 | end if | |
107 | end function | |
108 | ||
109 | 481 | |
110 | 481 | if year = Gregorian_Reformation and month = 9 then |
111 | 0 | return 19 |
112 | 481 | elsif month != 2 then |
113 | 406 | return DaysPerMonth[month] |
114 | else | |
115 | 75 | return DaysPerMonth[month] + isLeap(year) |
116 | end if | |
117 | end function | |
118 | ||
119 | 10 | |
120 | 10 | if year = Gregorian_Reformation then |
121 | 0 | return 355 |
122 | end if | |
123 | 10 | return 365 + isLeap(year) |
124 | end function | |
125 | ||
126 | -- Functions using the new data-types | |
127 | ||
128 | 44 | |
129 | integer year, month, day | |
130 | integer d | |
131 | ||
132 | 44 | year = ymd[1] |
133 | 44 | month = ymd[2] |
134 | 44 | day = ymd[3] |
135 | ||
136 | 44 | if month = 1 then return day end if |
137 | ||
138 | 35 | d = 0 |
139 | 35 | for i = 1 to month - 1 do |
140 | 107 | d += daysInMonth(year, i) |
141 | 107 | end for |
142 | ||
143 | 35 | d += day |
144 | ||
145 | 35 | if year = Gregorian_Reformation and month = 9 then |
146 | 0 | if day > 13 then |
147 | 0 | d -= 11 |
148 | 0 | elsif day > 2 then |
149 | 0 | return 0 |
150 | end if | |
151 | end if | |
152 | ||
153 | 35 | return d |
154 | end function | |
155 | ||
156 | 41 | |
157 | integer year | |
158 | integer j, greg00 | |
159 | ||
160 | 41 | year = ymd[1] |
161 | 41 | j = julianDayOfYear(ymd) |
162 | ||
163 | 41 | year -= 1 |
164 | 41 | greg00 = year - Gregorian_Reformation00 |
165 | ||
166 | 41 | j += ( |
167 | 365 * year | |
168 | + floor(year/4) | |
169 | + (greg00 > 0) | |
170 | * ( | |
171 | - floor(greg00/100) | |
172 | + floor(greg00/400+.25) | |
173 | ) | |
174 | - 11 * (year >= Gregorian_Reformation) | |
175 | ) | |
176 | ||
177 | 41 | if year >= 3200 then |
178 | 0 | j -= floor(year/ 3200) |
179 | 0 | if year >= 80000 then |
180 | 0 | j += floor(year/80000) |
181 | end if | |
182 | end if | |
183 | ||
184 | 41 | return j |
185 | end function | |
186 | ||
187 | 8 | |
188 | integer year, doy | |
189 | ||
190 | -- Take a guesstimate at the year -- this is usually v.close | |
191 | 8 | if j >= 0 then |
192 | 8 | year = floor(j / (12 * 30.43687604)) + 1 |
193 | else | |
194 | 0 | year = -floor(-j / 365.25) + 1 |
195 | end if | |
196 | ||
197 | -- Calculate the day in the guessed year | |
198 | 8 | doy = j - (julianDay({year, 1, 1}) - 1) -- = j - last day of prev year |
199 | ||
200 | -- Correct any errors | |
201 | ||
202 | -- The guesstimate is usually so close that these whiles could probably | |
203 | -- be made into ifs, but I haven't checked all possible dates yet... ;) | |
204 | ||
205 | 8 | while doy <= 0 do -- we guessed too high for the year |
206 | 0 | year -= 1 |
207 | 0 | doy += daysInYear(year) |
208 | 0 | end while |
209 | ||
210 | 8 | while doy > daysInYear(year) do -- we guessed too low |
211 | 0 | doy -= daysInYear(year) |
212 | 0 | year += 1 |
213 | 0 | end while |
214 | ||
215 | -- guess month | |
216 | 8 | if doy <= daysInMonth(year, 1) then |
217 | 1 | return {year, 1, doy} |
218 | end if | |
219 | 7 | for month = 2 to 12 do |
220 | 24 | doy -= daysInMonth(year, month-1) |
221 | 24 | if doy <= daysInMonth(year, month) then |
222 | 7 | return {year, month, doy} |
223 | end if | |
224 | 17 | end for |
225 | ||
226 | -- Skip to the next year on overflow | |
227 | -- The alternative is a crash, listed below | |
228 | 0 | return {year+1, 1, doy-31} |
229 | end function | |
230 | ||
231 | -- Conversions to and from seconds | |
232 | ||
233 | 11 | |
234 | 11 | return julianDay(dt) * DayLengthInSeconds + (dt[4] * 60 + dt[5]) * 60 + dt[6] |
235 | end function | |
236 | ||
237 | 8 | |
238 | integer days, minutes, hours | |
239 | ||
240 | 8 | days = floor(seconds / DayLengthInSeconds) |
241 | 8 | seconds = remainder(seconds, DayLengthInSeconds) |
242 | ||
243 | 8 | hours = floor( seconds / 3600 ) |
244 | 8 | seconds -= hours * 3600 |
245 | ||
246 | 8 | minutes = floor( seconds / 60 ) |
247 | 8 | seconds -= minutes* 60 |
248 | 8 | return julianDate(days) & {hours, minutes, seconds} |
249 | end function | |
250 | ||
251 | --**** | |
252 | -- === Localized Variables | |
253 | ||
254 | --** | |
255 | -- Names of the months | |
256 | ||
257 | 101 | public sequence month_names = { "January", "February", "March", |
258 | "April", "May", "June", | |
259 | "July", "August", "September", | |
260 | "October", "November", "December" } | |
261 | ||
262 | --** | |
263 | -- Abbreviations of month names | |
264 | ||
265 | 101 | public sequence month_abbrs = { "Jan", "Feb", "Mar", |
266 | "Apr", "May", "Jun", | |
267 | "Jul", "Aug", "Sep", | |
268 | "Oct", "Nov", "Dec" } | |
269 | ||
270 | --** | |
271 | -- Names of the days | |
272 | ||
273 | 101 | public sequence day_names = { "Sunday", "Monday", |
274 | "Tuesday", "Wednesday", | |
275 | "Thursday", "Friday", | |
276 | "Saturday" } | |
277 | ||
278 | --** | |
279 | -- Abbreviations of day names | |
280 | ||
281 | 101 | public sequence day_abbrs = { "Sun", "Mon", |
282 | "Tue", "Wed", | |
283 | "Thu", "Fri", | |
284 | "Sat" } | |
285 | ||
286 | --** | |
287 | -- AM/PM | |
288 | ||
289 | 101 | public sequence ampm = { "AM", "PM" } |
290 | ||
291 | --**** | |
292 | -- === Constants | |
293 | -- | |
294 | ||
295 | --** | |
296 | -- Accessors | |
297 | -- * ##YEAR## | |
298 | -- * ##MONTH## | |
299 | -- * ##DAY## | |
300 | -- * ##HOUR## | |
301 | -- * ##MINUTE## | |
302 | -- * ##SECOND## | |
303 | ||
304 | 101 | public enum YEAR, MONTH, DAY, HOUR, MINUTE, SECOND |
305 | ||
306 | --** | |
307 | -- Intervals | |
308 | -- * ##YEARS## | |
309 | -- * ##MONTHS## | |
310 | -- * ##WEEKS## | |
311 | -- * ##DAYS## | |
312 | -- * ##HOURS## | |
313 | -- * ##MINUTES## | |
314 | -- * ##SECONDS## | |
315 | -- * ##DATE## | |
316 | ||
317 | 101 | public enum YEARS, MONTHS, WEEKS, DAYS, HOURS, MINUTES, SECONDS, DATE |
318 | ||
319 | --**** | |
320 | -- === Types | |
321 | ||
322 | --** | |
323 | -- datetime type | |
324 | -- | |
325 | -- Parameters: | |
326 | -- # ##obj## : any object, so no crash takes place. | |
327 | -- | |
328 | -- Comments: | |
329 | -- A datetime type consists of a sequence of length 6 in the form | |
330 | -- ##{year, month, day_of_month, hour, minute, second}##. Checks are made to guarantee | |
331 | -- those values are in range. | |
332 | -- | |
333 | -- Note: | |
334 | -- All components must be integers except | |
335 | -- seconds, as those can also be floating point values. | |
336 | ||
337 | 306 | |
338 | 306 | if atom(o) then return 0 end if |
339 | ||
340 | 306 | if length(o) != 6 then return 0 end if |
341 | ||
342 | 306 | if not integer(o[YEAR]) then return 0 end if |
343 | ||
344 | 306 | if not integer(o[MONTH]) then return 0 end if |
345 | ||
346 | 306 | if not integer(o[DAY]) then return 0 end if |
347 | ||
348 | 306 | if not integer(o[HOUR]) then return 0 end if |
349 | ||
350 | 306 | if not integer(o[MINUTE]) then return 0 end if |
351 | ||
352 | 306 | if not atom(o[SECOND]) then return 0 end if |
353 | ||
354 | 306 | if not equal(o[1..3], {0,0,0}) then |
355 | -- Special case of all zeros is allowed; used when the data is a time only. | |
356 | 303 | if o[MONTH] < 1 then return 0 end if |
357 | ||
358 | 303 | if o[MONTH] > 12 then return 0 end if |
359 | ||
360 | 303 | if o[DAY] < 1 then return 0 end if |
361 | ||
362 | 303 | if o[DAY] > daysInMonth(o[YEAR],o[MONTH]) then return 0 end if |
363 | end if | |
364 | ||
365 | 306 | if o[HOUR] < 0 then return 0 end if |
366 | ||
367 | 306 | if o[HOUR] > 23 then return 0 end if |
368 | ||
369 | 306 | if o[MINUTE] < 0 then return 0 end if |
370 | ||
371 | 306 | if o[MINUTE] > 59 then return 0 end if |
372 | ||
373 | 306 | if o[SECOND] < 0 then return 0 end if |
374 | ||
375 | 306 | if o[SECOND] >= 60 then return 0 end if |
376 | ||
377 | 306 | return 1 |
378 | end type | |
379 | ||
380 | --**** | |
381 | -- === Routines | |
382 | ||
383 | --**** | |
384 | -- Signature: | |
385 | -- | |
386 | -- | |
387 | -- Description: | |
388 | -- Return the number of seconds since some fixed point in the past. | |
389 | -- | |
390 | -- Returns: | |
391 | -- An **atom**, which represents an absolute number of seconds. | |
392 | -- | |
393 | -- Comments: | |
394 | -- Take the difference between two readings of ##time##(), to measure, for example, how long | |
395 | -- a section of code takes to execute. | |
396 | -- | |
397 | -- On some machines, ##time##() can return a negative number. However, you can still use the | |
398 | -- difference in calls to ##time##() to measure elapsed time. | |
399 | -- | |
400 | -- Example 1: | |
401 | -- | |
402 | -- constant ITERATIONS = 1000000 | |
403 | -- integer p | |
404 | -- atom t0, loop_overhead | |
405 | -- | |
406 | -- t0 = time() | |
407 | -- for i = 1 to ITERATIONS do | |
408 | -- -- time an empty loop | |
409 | -- end for | |
410 | -- loop_overhead = time() - t0 | |
411 | -- | |
412 | -- t0 = time() | |
413 | -- for i = 1 to ITERATIONS do | |
414 | -- p = power(2, 20) | |
415 | -- end for | |
416 | -- ? (time() - t0 - loop_overhead)/ITERATIONS | |
417 | -- -- calculates time (in seconds) for one call to power | |
418 | -- | |
419 | -- | |
420 | -- See Also: | |
421 | -- [[:date]], [[:now]] | |
422 | ||
423 | --**** | |
424 | -- Signature: | |
425 | -- | |
426 | -- | |
427 | -- Description: | |
428 | -- Return a sequence with information on the current date. | |
429 | -- | |
430 | -- Returns: | |
431 | -- A **sequence** of length 8, laid out as follows: | |
432 | -- # year ~-- since 1900 | |
433 | -- # month ~-- January = 1 | |
434 | -- # day ~-- day of month, starting at 1 | |
435 | -- # hour ~-- 0 to 23 | |
436 | -- # minute ~-- 0 to 59 | |
437 | -- # second ~-- 0 to 59 | |
438 | -- # day of the week ~-- Sunday = 1 | |
439 | -- # day of the year ~-- January 1st = 1 | |
440 | -- | |
441 | -- Comments: | |
442 | -- The value returned for the year is actually the number of years since 1900 (not the last 2 digits of the year). | |
443 | -- In the year 2000 this value was 100. In 2001 it was 101, etc. | |
444 | -- | |
445 | -- Example 1: | |
446 | -- | |
447 | -- | |
448 | -- now = date() | |
449 | -- -- now has: {95,3,24,23,47,38,6,83} | |
450 | -- -- i.e. Friday March 24, 1995 at 11:47:38pm, day 83 of the year | |
451 | -- | |
452 | -- | |
453 | -- See Also: | |
454 | -- [[:time]], [[:now]] | |
455 | ||
456 | --** | |
457 | -- Convert a sequence formatted according to the built-in ##date##() function to a valid datetime | |
458 | -- sequence. | |
459 | -- | |
460 | -- Parameters: | |
461 | -- # ##src## : a sequence which date() might have returned | |
462 | -- | |
463 | -- Returns: | |
464 | -- A **sequence**, more precisely a **datetime** corresponding to the same moment in time. | |
465 | -- | |
466 | -- Example 1: | |
467 | -- | |
468 | -- d = from_date(date()) | |
469 | -- -- d is the current date and time | |
470 | -- | |
471 | -- | |
472 | -- See Also: | |
473 | -- [[:date]], [[:from_unix]], [[:now]], [[:new]] | |
474 | ||
475 | 5 | |
476 | 5 | return {src[YEAR]+1900, src[MONTH], src[DAY], src[HOUR], src[MINUTE], src[SECOND]} |
477 | end function | |
478 | ||
479 | --** | |
480 | -- Create a new datetime value initialized with the current date and time | |
481 | -- | |
482 | -- Returns: | |
483 | -- A **sequence**, more precisely a **datetime** corresponding to the current moment in time. | |
484 | -- | |
485 | -- Example 1: | |
486 | -- | |
487 | -- dt = now() | |
488 | -- -- dt is the current date and time | |
489 | -- | |
490 | -- | |
491 | -- See Also: | |
492 | -- [[:from_date]], [[:from_unix]], [[:new]], [[:new_time]], [[:now_gmt]] | |
493 | ||
494 | 4 | |
495 | 4 | return from_date(date()) |
496 | end function | |
497 | ||
498 | --** | |
499 | -- Create a new datetime value that falls into the Greenwich Mean Time (GMT) timezone. | |
500 | -- This function will return a datetime that is GMT, no matter what timezone the system | |
501 | -- is running under. | |
502 | -- | |
503 | -- Example 1: | |
504 | -- | |
505 | -- dt = now_gmt() | |
506 | -- -- If local time was July 16th, 2008 at 10:34pm CST | |
507 | -- -- dt would be July 17th, 2008 at 03:34pm GMT | |
508 | -- | |
509 | -- | |
510 | -- See Also: | |
511 | -- [[:now]] | |
512 | ||
513 | 5 | |
514 | 5 | sequence t1 = gmtime(time()) |
515 | ||
516 | 5 | return {t1[TM_YEAR]+1900, t1[TM_MON]+1, t1[TM_MDAY], t1[TM_HOUR], t1[TM_MIN], t1[TM_SEC]} |
517 | end function | |
518 | ||
519 | --** | |
520 | -- Create a new datetime value. | |
521 | -- | |
522 | -- !! TODO: test default parameter usage | |
523 | -- | |
524 | -- Parameters: | |
525 | -- # ##year## ~-- the full year. | |
526 | -- # ##month## ~-- the month (1-12). | |
527 | -- # ##day## ~-- the day of the month (1-31). | |
528 | -- # ##hour## ~-- the hour (0-23) (defaults to 0) | |
529 | -- # ##minute## ~-- the minute (0-59) (defaults to 0) | |
530 | -- # ##second## ~-- the second (0-59) (defaults to 0) | |
531 | -- | |
532 | -- Example 1: | |
533 | -- | |
534 | -- dt = new(2010, 1, 1, 0, 0, 0) | |
535 | -- -- dt is Jan 1st, 2010 | |
536 | -- | |
537 | -- | |
538 | -- See Also: | |
539 | -- [[:from_date]], [[:from_unix]], [[:now]], [[:new_time]] | |
540 | ||
541 | 13 | |
542 | integer hour=0, integer minute=0, atom second=0) | |
543 | datetime d | |
544 | 13 | d = {year, month, day, hour, minute, second} |
545 | 13 | if equal(d, {0,0,0,0,0,0}) then |
546 | 1 | return now() |
547 | else | |
548 | 12 | return d |
549 | end if | |
550 | end function | |
551 | ||
552 | --** | |
553 | -- Create a new datetime value with a date of zeros. | |
554 | -- | |
555 | -- !! TODO: test | |
556 | -- | |
557 | -- Parameters: | |
558 | -- # ##hour## : is the hour (0-23) | |
559 | -- # ##minute## : is the minute (0-59) | |
560 | -- # ##second## : is the second (0-59) | |
561 | -- | |
562 | -- Example 1: | |
563 | -- | |
564 | -- dt = new_time(10, 30, 55) | |
565 | -- dt is 10:30:55 AM | |
566 | -- | |
567 | -- | |
568 | -- See Also: | |
569 | -- [[:from_date]], [[:from_unix]], [[:now]], [[:new]] | |
570 | ||
571 | 1 | |
572 | 1 | return new(0, 0, 0, hour, minute, second) |
573 | end function | |
574 | ||
575 | --** | |
576 | -- Get the day of week of the datetime dt. | |
577 | -- | |
578 | -- Parameters: | |
579 | -- # ##dt## : a datetime to be queried. | |
580 | -- | |
581 | -- Returns: | |
582 | -- An **integer**, between 1 (Sunday) and 7 (Saturday). | |
583 | -- | |
584 | -- Example 1: | |
585 | -- | |
586 | -- d = new(2008, 5, 2, 0, 0, 0) | |
587 | -- day = weeks_day(d) -- day is 6 because May 2, 2008 is a Friday. | |
588 | -- | |
589 | ||
590 | 22 | |
591 | 22 | return remainder(julianDay(dt)-1+4094, 7) + 1 |
592 | end function | |
593 | ||
594 | --** | |
595 | -- Get the Julian day of year of the supplied date. | |
596 | -- | |
597 | -- Parameters: | |
598 | -- # ##dt## : a datetime to be queried. | |
599 | -- | |
600 | -- Returns: | |
601 | -- An **integer**, between 1 and 366. | |
602 | -- | |
603 | -- Comments: | |
604 | -- For dates earlier than 1800, this routine may give inaccurate results if the date | |
605 | -- applies to a country other than United Kingdom or a former colony thereof. The change from | |
606 | -- Julian to Gregorian calendar took place much earlier in some other European countries. | |
607 | -- | |
608 | -- Example 1: | |
609 | -- | |
610 | -- d = new(2008, 5, 2, 0, 0, 0) | |
611 | -- day = years_day(d) -- day is 123 | |
612 | -- | |
613 | ||
614 | 2 | |
615 | 2 | return julianDayOfYear({dt[YEAR], dt[MONTH], dt[DAY]}) |
616 | end function | |
617 | ||
618 | --** | |
619 | -- Determine if ##dt## falls within leap year. | |
620 | -- | |
621 | -- Parameters: | |
622 | -- # ##dt## : a datetime to be queried. | |
623 | -- | |
624 | -- Returns: | |
625 | -- An **integer**, of 1 if leap year, otherwise 0. | |
626 | -- | |
627 | -- Example 1: | |
628 | -- | |
629 | -- d = new(2008, 1, 1, 0, 0, 0) | |
630 | -- ? is_leap_year(d) -- prints 1 | |
631 | -- d = new(2005, 1, 1, 0, 0, 0) | |
632 | -- ? is_leap_year(d) -- prints 0 | |
633 | -- | |
634 | -- | |
635 | -- See Also: | |
636 | -- [[:days_in_month]] | |
637 | ||
638 | 2 | |
639 | 2 | return isLeap(dt[YEAR]) |
640 | end function | |
641 | ||
642 | --** | |
643 | -- Return the number of days in the month of ##dt##. | |
644 | -- | |
645 | -- This takes into account leap year. | |
646 | -- | |
647 | -- Parameters: | |
648 | -- # ##dt## : a datetime to be queried. | |
649 | -- | |
650 | -- Example 1: | |
651 | -- | |
652 | -- d = new(2008, 1, 1, 0, 0, 0) | |
653 | -- ? days_in_month(d) -- 31 | |
654 | -- d = new(2008, 2, 1, 0, 0, 0) -- Leap year | |
655 | -- ? days_in_month(d) -- 29 | |
656 | -- | |
657 | -- | |
658 | -- See Also: | |
659 | -- [[:is_leap_year]] | |
660 | ||
661 | 15 | |
662 | 15 | return daysInMonth(dt[YEAR], dt[MONTH]) |
663 | end function | |
664 | ||
665 | --** | |
666 | -- Return the number of days in the year of ##dt##. | |
667 | -- | |
668 | -- This takes into account leap year. | |
669 | -- | |
670 | -- Parameters: | |
671 | -- # ##dt## : a datetime to be queried. | |
672 | -- | |
673 | -- Example 1: | |
674 | -- | |
675 | -- d = new(2007, 1, 1, 0, 0, 0) | |
676 | -- ? days_in_year(d) -- 365 | |
677 | -- d = new(2008, 1, 1, 0, 0, 0) -- leap year | |
678 | -- ? days_in_year(d) -- 366 | |
679 | -- | |
680 | -- | |
681 | -- See Also: | |
682 | -- [[:is_leap_year]], [[:days_in_month]] | |
683 | ||
684 | 2 | |
685 | 2 | return daysInYear(dt[YEAR]) |
686 | end function | |
687 | ||
688 | --** | |
689 | -- Convert a datetime value to the unix numeric format (seconds since ##EPOCH_1970##) | |
690 | -- | |
691 | -- Parameters: | |
692 | -- # ##dt## : a datetime to be queried. | |
693 | -- | |
694 | -- Returns: | |
695 | -- An **atom**, so this will not overflow during the winter 2038-2039. | |
696 | -- | |
697 | -- | |
698 | -- Example 1: | |
699 | -- | |
700 | -- secs_since_epoch = to_unix(now()) | |
701 | -- -- secs_since_epoch is equal to the current seconds since epoch | |
702 | -- | |
703 | -- | |
704 | -- See Also: | |
705 | -- [[:from_unix]], [[:format]] | |
706 | ||
707 | 3 | |
708 | 3 | return datetimeToSeconds(dt) - EPOCH_1970 |
709 | end function | |
710 | ||
711 | --** | |
712 | -- Create a datetime value from the unix numeric format (seconds since EPOCH) | |
713 | -- | |
714 | -- Parameters: | |
715 | -- # ##unix## : an atom, counting seconds elapsed since EPOCH. | |
716 | -- | |
717 | -- Returns: | |
718 | -- A **sequence**, more precisely a **datetime** representing the same moment in time. | |
719 | -- | |
720 | -- Example 1: | |
721 | -- | |
722 | -- d = from_unix(0) | |
723 | -- -- d is 1970-01-01 00:00:00 (zero seconds since EPOCH) | |
724 | -- | |
725 | -- | |
726 | -- See Also: | |
727 | -- [[:to_unix]], [[:from_date]], [[:now]], [[:new]] | |
728 | ||
729 | 2 | |
730 | 2 | return secondsToDateTime(EPOCH_1970 + unix) |
731 | end function | |
732 | ||
733 | --** | |
734 | -- Format the date according to the format pattern string | |
735 | -- | |
736 | -- Parameters: | |
737 | -- # ##d## : a datetime which is to be printed out | |
738 | -- # ##pattern## : a format string, similar to the ones sprintf() uses, but with some Unicode encoding. | |
739 | -- The default is "%Y-%m-%d %H:%M:%S". | |
740 | -- | |
741 | -- Returns: | |
742 | -- A **string**, with the date ##d## formatted according to the specification in ##pattern##. | |
743 | -- | |
744 | -- Comments: | |
745 | -- | |
746 | -- Pattern string can include the following specifiers~: | |
747 | -- | |
748 | -- * ##~%%## ~-- a literal % | |
749 | -- * ##%a## ~-- locale's abbreviated weekday name (e.g., Sun) | |
750 | -- * ##%A## ~-- locale's full weekday name (e.g., Sunday) | |
751 | -- * ##%b## ~-- locale's abbreviated month name (e.g., Jan) | |
752 | -- * ##%B## ~-- locale's full month name (e.g., January) | |
753 | -- * ##%C## ~-- century; like %Y, except omit last two digits (e.g., 21) | |
754 | -- * ##%d## ~-- day of month (e.g, 01) | |
755 | -- * ##%H## ~-- hour (00..23) | |
756 | -- * ##%I## ~-- hour (01..12) | |
757 | -- * ##%j## ~-- day of year (001..366) | |
758 | -- * ##%k## ~-- hour ( 0..23) | |
759 | -- * ##%l## ~-- hour ( 1..12) | |
760 | -- * ##%m## ~-- month (01..12) | |
761 | -- * ##%M## ~-- minute (00..59) | |
762 | -- * ##%p## ~-- locale's equivalent of either AM or PM; blank if not known | |
763 | -- * ##%P## ~-- like %p, but lower case | |
764 | -- * ##%s## ~-- seconds since 1970-01-01 00:00:00 UTC | |
765 | -- * ##%S## ~-- second (00..60) | |
766 | -- * ##%u## ~-- day of week (1..7); 1 is Monday | |
767 | -- * ##%w## ~-- day of week (0..6); 0 is Sunday | |
768 | -- * ##%y## ~-- last two digits of year (00..99) | |
769 | -- * ##%Y## ~-- year | |
770 | -- | |
771 | -- Example 1: | |
772 | -- | |
773 | -- d = new(2008, 5, 2, 12, 58, 32) | |
774 | -- s = format(d, "%Y-%m-%d %H:%M:%S") | |
775 | -- -- s is "2008-05-02 12:58:32" | |
776 | -- | |
777 | -- | |
778 | -- Example 2: | |
779 | -- | |
780 | -- d = new(2008, 5, 2, 12, 58, 32) | |
781 | -- s = format(d, "%A, %B %d '%y %H:%M%p") | |
782 | -- -- s is "Friday, May 2 '08 12:58PM" | |
783 | -- | |
784 | -- | |
785 | -- See Also: | |
786 | -- [[:to_unix]], [[:parse]] | |
787 | ||
788 | 111 | |
789 | integer in_fmt, ch, tmp | |
790 | sequence res | |
791 | ||
792 | 111 | in_fmt = 0 |
793 | 111 | res = "" |
794 | ||
795 | 111 | for i = 1 to length(pattern) do |
796 | 372 | ch = pattern[i] |
797 | ||
798 | 372 | if in_fmt then |
799 | 181 | in_fmt = 0 |
800 | ||
801 | 181 | if ch = '%' then |
802 | 1 | res &= '%' |
803 | 180 | elsif ch = 'a' then |
804 | 8 | res &= day_abbrs[weeks_day(d)] |
805 | 172 | elsif ch = 'A' then |
806 | 8 | res &= day_names[weeks_day(d)] |
807 | 164 | elsif ch = 'b' then |
808 | 13 | res &= month_abbrs[d[MONTH]] |
809 | 151 | elsif ch = 'B' then |
810 | 13 | res &= month_names[d[MONTH]] |
811 | 138 | elsif ch = 'C' then |
812 | 1 | res &= sprintf("%02d", d[YEAR] / 100) |
813 | 137 | elsif ch = 'd' then |
814 | 6 | res &= sprintf("%02d", d[DAY]) |
815 | 131 | elsif ch = 'H' then |
816 | 30 | res &= sprintf("%02d", d[HOUR]) |
817 | 101 | elsif ch = 'I' then |
818 | 12 | tmp = d[HOUR] |
819 | 12 | if tmp > 12 then |
820 | 1 | tmp -= 12 |
821 | 11 | elsif tmp = 0 then |
822 | 0 | tmp = 12 |
823 | end if | |
824 | 12 | res &= sprintf("%02d", tmp) |
825 | 89 | elsif ch = 'j' then |
826 | 1 | res &= sprintf("%d", julianDayOfYear(d)) |
827 | 88 | elsif ch = 'k' then |
828 | 1 | res &= sprintf("%d", d[HOUR]) |
829 | 87 | elsif ch = 'l' then |
830 | 12 | tmp = d[HOUR] |
831 | 12 | if tmp > 12 then |
832 | 12 | tmp -= 12 |
833 | 0 | elsif tmp = 0 then |
834 | 0 | tmp = 12 |
835 | end if | |
836 | 12 | res &= sprintf("%d", tmp) |
837 | 75 | elsif ch = 'm' then |
838 | 6 | res &= sprintf("%02d", d[MONTH]) |
839 | 69 | elsif ch = 'M' then |
840 | 6 | res &= sprintf("%02d", d[MINUTE]) |
841 | 63 | elsif ch = 'p' then |
842 | 23 | if d[HOUR] <= 12 then |
843 | 11 | res &= ampm[1] |
844 | else | |
845 | 12 | res &= ampm[2] |
846 | end if | |
847 | 40 | elsif ch = 'P' then |
848 | 24 | if d[HOUR] <= 12 then |
849 | 11 | res &= tolower(ampm[1]) |
850 | else | |
851 | 13 | res &= tolower(ampm[2]) |
852 | end if | |
853 | 16 | elsif ch = 's' then |
854 | 1 | res &= sprintf("%d", to_unix(d)) |
855 | 15 | elsif ch = 'S' then |
856 | 6 | res &= sprintf("%02d", d[SECOND]) |
857 | 9 | elsif ch = 'u' then |
858 | 1 | tmp = weeks_day(d) |
859 | 1 | if tmp = 1 then |
860 | 0 | res &= "7" -- Sunday |
861 | else | |
862 | 1 | res &= sprintf("%d", weeks_day(d) - 1) |
863 | end if | |
864 | 8 | elsif ch = 'w' then |
865 | 1 | res &= sprintf("%d", weeks_day(d) - 1) |
866 | 7 | elsif ch = 'y' then |
867 | 1 | tmp = floor(d[YEAR] / 100) |
868 | 1 | res &= sprintf("%02d", d[YEAR] - (tmp * 100)) |
869 | 6 | elsif ch = 'Y' then |
870 | 6 | res &= sprintf("%04d", d[YEAR]) |
871 | else | |
872 | -- TODO: error or just add? | |
873 | end if | |
874 | 191 | elsif ch = '%' then |
875 | 181 | in_fmt = 1 |
876 | else | |
877 | 10 | res &= ch |
878 | end if | |
879 | 372 | end for |
880 | 111 | return res |
881 | end function | |
882 | ||
883 | --** | |
884 | -- Parse a datetime string according to the given format. | |
885 | -- | |
886 | -- Parameters: | |
887 | -- # ##val## : string datetime value | |
888 | -- # ##fmt## : datetime format. Default is "%Y-%m-%d %H:%M:%S" | |
889 | -- | |
890 | -- Returns: | |
891 | -- A **datetime**, value. | |
892 | -- | |
893 | -- Comments: | |
894 | -- Only a subset of the format specification is currently supported: | |
895 | -- | |
896 | -- * ##%d## ~-- day of month (e.g, 01) | |
897 | -- * ##%H## ~-- hour (00..23) | |
898 | -- * ##%m## ~-- month (01..12) | |
899 | -- * ##%M## ~-- minute (00..59) | |
900 | -- * ##%S## ~-- second (00..60) | |
901 | -- * ##%Y## ~-- year | |
902 | -- | |
903 | -- More format codes will be added in future versions. | |
904 | -- | |
905 | -- All non-format characters in the format string are ignored and are not | |
906 | -- matched against the input string. | |
907 | -- | |
908 | -- All non-digits in the input string are ignored. | |
909 | -- | |
910 | -- Example 1: | |
911 | -- | |
912 | -- datetime d = parse("05/01/2009 10:20:30", "%m/%d/%Y %H:%M:%S") | |
913 | -- | |
914 | -- | |
915 | -- See Also: | |
916 | -- [[:format]] | |
917 | -- | |
918 | ||
919 | 6 | |
920 | 6 | integer fpos = 1, spos = 1, maxlen, rpos |
921 | 6 | sequence res = {0,0,0,0,0,0} |
922 | ||
923 | 6 | while fpos <= length(fmt) do |
924 | 46 | if fmt[fpos] = '%' then |
925 | 36 | fpos += 1 |
926 | ||
927 | 36 | switch fmt[fpos] do |
928 | case 'Y' then | |
929 | 6 | rpos = 1 |
930 | 6 | maxlen = 4 |
931 | ||
932 | case 'm' then | |
933 | 6 | rpos = 2 |
934 | 6 | maxlen = 2 |
935 | ||
936 | case 'd' then | |
937 | 6 | rpos = 3 |
938 | 6 | maxlen = 2 |
939 | ||
940 | case 'H' then | |
941 | 6 | rpos = 4 |
942 | 6 | maxlen = 2 |
943 | ||
944 | case 'M' then | |
945 | 6 | rpos = 5 |
946 | 6 | maxlen = 2 |
947 | ||
948 | case 'S' then | |
949 | 6 | rpos = 6 |
950 | 6 | maxlen = 2 |
951 | ||
952 | case else | |
953 | -- Ignore any invalid format character. | |
954 | 0 | rpos = 0 |
955 | ||
956 | end switch | |
957 | ||
958 | 36 | if rpos then |
959 | 36 | sequence got |
960 | 36 | integer epos |
961 | 36 | while spos <= length(val) do |
962 | 61 | if t_digit(val[spos]) then |
963 | 36 | exit |
964 | end if | |
965 | 25 | spos += 1 |
966 | 25 | end while |
967 | ||
968 | 36 | epos = spos + 1 |
969 | 36 | while epos <= length(val) and epos < spos + maxlen do |
970 | 46 | if not t_digit(val[epos]) then |
971 | 5 | exit |
972 | end if | |
973 | 41 | epos += 1 |
974 | 41 | end while |
975 | ||
976 | 36 | got = value(val[spos .. epos-1], , GET_LONG_ANSWER) |
977 | 36 | if got[1] != GET_SUCCESS then |
978 | 0 | return -1 |
979 | end if | |
980 | ||
981 | 36 | res[rpos] = got[2] |
982 | 36 | spos = epos |
983 | end if | |
984 | end if | |
985 | 46 | fpos += 1 |
986 | ||
987 | 46 | end while |
988 | ||
989 | -- Ensure that what we got could be a date-time value. | |
990 | 6 | if not datetime(res) then |
991 | 0 | return -1 |
992 | end if | |
993 | ||
994 | -- Ensure no remaining digits in string. | |
995 | 6 | while spos <= length(val) do |
996 | 4 | if t_digit(val[spos]) then |
997 | 2 | return -1 |
998 | end if | |
999 | 2 | spos += 1 |
1000 | 2 | end while |
1001 | ||
1002 | 4 | return new(res[1], res[2], res[3], res[4], res[5], res[6]) |
1003 | end function | |
1004 | ||
1005 | --** | |
1006 | -- Add a number of //intervals// to a datetime. | |
1007 | -- | |
1008 | -- Parameters: | |
1009 | -- # ##dt## : the base datetime | |
1010 | -- # ##qty## : the number of //intervals// to add. It should be positive. | |
1011 | -- # ##interval## : which kind of interval to add. | |
1012 | -- | |
1013 | -- Returns: | |
1014 | -- A **sequence**, more precisely a **datetime** representing the new moment in time. | |
1015 | -- | |
1016 | -- Comments: | |
1017 | -- Please see Constants for Date/Time for a reference of valid intervals. | |
1018 | -- | |
1019 | -- Do not confuse the item access constants such as YEAR, MONTH, DAY, etc... with the | |
1020 | -- interval constants YEARS, MONTHS, DAYS, etc... | |
1021 | -- | |
1022 | -- When adding MONTHS, it is a calendar based addition. For instance, a date of | |
1023 | -- 5/2/2008 with 5 MONTHS added will become 10/2/2008. MONTHS does not compute the number | |
1024 | -- of days per each month and the average number of days per month. | |
1025 | -- | |
1026 | -- When adding YEARS, leap year is taken into account. Adding 4 YEARS to a date may result | |
1027 | -- in a different day of month number due to leap year. | |
1028 | -- | |
1029 | -- Example 1: | |
1030 | -- | |
1031 | -- d2 = add(d1, 35, SECONDS) -- add 35 seconds to d1 | |
1032 | -- d2 = add(d1, 7, WEEKS) -- add 7 weeks to d1 | |
1033 | -- d2 = add(d1, 19, YEARS) -- add 19 years to d1 | |
1034 | -- | |
1035 | -- | |
1036 | -- See Also: | |
1037 | -- [[:subtract]], [[:diff]] | |
1038 | ||
1039 | 11 | |
1040 | integer inc | |
1041 | ||
1042 | 11 | if interval = SECONDS then |
1043 | 10 | elsif interval = MINUTES then |
1044 | 1 | qty *= 60 |
1045 | 9 | elsif interval = HOURS then |
1046 | 1 | qty *= 3600 |
1047 | 8 | elsif interval = DAYS then |
1048 | 2 | qty *= 86400 |
1049 | 6 | elsif interval = WEEKS then |
1050 | 1 | qty *= 604800 |
1051 | 5 | elsif interval = MONTHS then |
1052 | 3 | if qty > 0 then |
1053 | 2 | inc = 1 |
1054 | else | |
1055 | 1 | inc = -1 |
1056 | 1 | qty = -(qty) |
1057 | end if | |
1058 | ||
1059 | 3 | for i = 1 to qty do |
1060 | 26 | if inc = 1 and dt[MONTH] = 12 then |
1061 | 1 | dt[MONTH] = 1 |
1062 | 1 | dt[YEAR] += 1 |
1063 | 25 | elsif inc = -1 and dt[MONTH] = 1 then |
1064 | 1 | dt[MONTH] = 12 |
1065 | 1 | dt[YEAR] -= 1 |
1066 | else | |
1067 | 24 | dt[MONTH] += inc |
1068 | end if | |
1069 | 26 | end for |
1070 | ||
1071 | 3 | return dt |
1072 | 2 | elsif interval = YEARS then |
1073 | 2 | dt[YEAR] += qty |
1074 | 2 | if isLeap(dt[YEAR]) = 0 and dt[MONTH] = 2 and dt[DAY] = 29 then |
1075 | 0 | dt[MONTH] = 3 |
1076 | 0 | dt[DAY] = 1 |
1077 | end if | |
1078 | ||
1079 | 2 | return dt |
1080 | 0 | elsif interval = DATE then |
1081 | 0 | qty = datetimeToSeconds(qty) |
1082 | end if | |
1083 | ||
1084 | 6 | return secondsToDateTime(datetimeToSeconds(dt) + qty) |
1085 | end function | |
1086 | ||
1087 | --** | |
1088 | -- Subtract a number of //intervals// to a base datetime. | |
1089 | -- | |
1090 | -- Parameters: | |
1091 | -- # ##dt## : the base datetime | |
1092 | -- # ##qty## : the number of //intervals// to subtract. It should be positive. | |
1093 | -- # ##interval## : which kind of interval to subtract. | |
1094 | -- | |
1095 | -- Returns: | |
1096 | -- A **sequence**, more precisely a **datetime** representing the new moment | |
1097 | -- in time. | |
1098 | -- | |
1099 | -- Comments: | |
1100 | -- Please see Constants for Date/Time for a reference of valid intervals. | |
1101 | -- | |
1102 | -- See the function ##add##() for more information on adding and subtracting date | |
1103 | -- intervals | |
1104 | -- | |
1105 | -- Example 1: | |
1106 | -- | |
1107 | -- dt2 = subtract(dt1, 18, MINUTES) -- subtract 18 minutes from dt1 | |
1108 | -- dt2 = subtract(dt1, 7, MONTHS) -- subtract 7 months from dt1 | |
1109 | -- dt2 = subtract(dt1, 12, HOURS) -- subtract 12 hours from dt1 | |
1110 | -- | |
1111 | -- | |
1112 | -- See Also: | |
1113 | -- [[:add]], [[:diff]] | |
1114 | ||
1115 | 3 | |
1116 | 3 | return add(dt, -(qty), interval) |
1117 | end function | |
1118 | ||
1119 | --** | |
1120 | -- Compute the difference, in seconds, between two dates. | |
1121 | -- | |
1122 | -- Parameters: | |
1123 | -- # ##dt1## : the end datetime | |
1124 | -- # ##dt2## : the start datetime | |
1125 | -- | |
1126 | -- Returns: | |
1127 | -- An **atom**, the number of seconds elapsed from ##dt2## to ##dt1##. | |
1128 | -- | |
1129 | -- Comments: | |
1130 | -- ##dt2## is subtracted from ##dt1##, therefore, you can come up with a negative value. | |
1131 | -- | |
1132 | -- Example 1: | |
1133 | -- | |
1134 | -- d1 = now() | |
1135 | -- sleep(15) -- sleep for 15 seconds | |
1136 | -- d2 = now() | |
1137 | -- | |
1138 | -- i = diff(d1, d2) -- i is 15 | |
1139 | -- | |
1140 | -- | |
1141 | -- See Also: | |
1142 | -- [[:add]], [[:subtract]] | |
1143 | ||
1144 | 1 | |
1145 | 1 | return datetimeToSeconds(dt2) - datetimeToSeconds(dt1) |
1146 | end function |