1 /********************************************************************/
2 /* */
3 /* tim_rtl.c Time access using the C capabilities. */
4 /* Copyright (C) 1989 - 2015, 2018, 2020 Thomas Mertes */
5 /* */
6 /* This file is part of the Seed7 Runtime Library. */
7 /* */
8 /* The Seed7 Runtime Library is free software; you can */
9 /* redistribute it and/or modify it under the terms of the GNU */
10 /* Lesser General Public License as published by the Free Software */
11 /* Foundation; either version 2.1 of the License, or (at your */
12 /* option) any later version. */
13 /* */
14 /* The Seed7 Runtime Library is distributed in the hope that it */
15 /* will be useful, but WITHOUT ANY WARRANTY; without even the */
16 /* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR */
17 /* PURPOSE. See the GNU Lesser General Public License for more */
18 /* details. */
19 /* */
20 /* You should have received a copy of the GNU Lesser General */
21 /* Public License along with this program; if not, write to the */
22 /* Free Software Foundation, Inc., 51 Franklin Street, */
23 /* Fifth Floor, Boston, MA 02110-1301, USA. */
24 /* */
25 /* Module: Seed7 Runtime Library */
26 /* File: seed7/src/tim_rtl.c */
27 /* Changes: 2009, 2013 - 2015, 2018, 2020 Thomas Mertes */
28 /* Content: Time access using the C capabilities. */
29 /* */
30 /********************************************************************/
31
32 #define LOG_FUNCTIONS 0
33 #define VERBOSE_EXCEPTIONS 0
34
35 #include "version.h"
36
37 #include "stdio.h"
38 #include "time.h"
39 #include "string.h"
40 #include "errno.h"
41
42 #include "common.h"
43 #include "big_drv.h"
44 #include "tim_drv.h"
45 #include "rtl_err.h"
46
47 #undef EXTERN
48 #define EXTERN
49 #include "tim_rtl.h"
50
51
52 #if TIME_T_SIZE == 32
53 #if TIME_T_SIGNED
54 #define STRUCT_TM_MIN_YEAR 1902
55 #else
56 #define STRUCT_TM_MIN_YEAR 1970
57 #endif
58 #define STRUCT_TM_MAX_YEAR 2037
59 #elif TIME_T_SIZE == 64
60 #if defined INT64TYPE_SUFFIX_LL
61 #define STRUCT_TM_MIN_YEAR (-292277022655LL)
62 #define STRUCT_TM_MAX_YEAR 292277026595LL
63 #elif defined INT64TYPE_SUFFIX_L
64 #define STRUCT_TM_MIN_YEAR (-292277022655L)
65 #define STRUCT_TM_MAX_YEAR 292277026595L
66 #else
67 #define STRUCT_TM_MIN_YEAR (-292277022655)
68 #define STRUCT_TM_MAX_YEAR 292277026595
69 #endif
70 #endif
71
72 #define SECONDS_PER_DAY 86400
73 #define SECONDS_PER_YEAR (SECONDS_PER_DAY * 365)
74 /* Days between 0-01-01 and 1900-01-01 */
75 #define DAYS_FROM_0_TO_1900 693595
76 /* Days between 0-01-01 and 1970-01-01 */
77 #define DAYS_FROM_0_TO_1970 719162
78 #define MINIMUM_TIMESTAMP_YEAR -292277022657
79 #define MAXIMUM_TIMESTAMP_YEAR 292277026596
80
81 static const int monthDays[2][12] = {
82 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
83 {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
84
85 static const time_t yearDays[2][13] = {
86 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 366},
87 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 367}};
88
89
90
91 /**
92 * Convert a broken down time in '*timeptr' to a timestamp.
93 * The timestamp is expressed in seconds since the Unix Epoch.
94 * The Unix Epoch (1970-01-01 00:00:00 UTC) corresponds to 0.
95 * The date/time in '*timeptr' is not checked for correctness.
96 * @return the timestamp that corresponds to the time.
97 */
unchecked_mkutc(struct tm * timeptr)98 time_t unchecked_mkutc (struct tm *timeptr)
99
100 {
101 int real_year;
102 int leap_year;
103 int year_before;
104 time_t timestamp;
105
106 /* unchecked_mkutc */
107 logFunction(printf("unchecked_mkutc([%d, %d, %d, %d, %d, %d])\n",
108 timeptr->tm_year, timeptr->tm_mon, timeptr->tm_mday,
109 timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec););
110 real_year = 1900 + timeptr->tm_year;
111 if ((real_year % 4 == 0 && real_year % 100 != 0) || real_year % 400 == 0) {
112 leap_year = 1;
113 } else {
114 leap_year = 0;
115 } /* if */
116 year_before = real_year - 1;
117 if (year_before < 0) {
118 /* Year_before is divided and truncated towards minus infinite. */
119 /* This is done with: quotient = (dividend + 1) / divisor - 1; */
120 timestamp = (time_t) year_before * 365 +
121 ((time_t) real_year / 4 - 1) -
122 ((time_t) real_year / 100 - 1) +
123 ((time_t) real_year / 400 - 1);
124 } else {
125 timestamp = (time_t) year_before * 365 +
126 (time_t) year_before / 4 -
127 (time_t) year_before / 100 +
128 (time_t) year_before / 400;
129 } /* if */
130 timestamp = (((timestamp -
131 (time_t) DAYS_FROM_0_TO_1970 +
132 yearDays[leap_year][timeptr->tm_mon] +
133 (time_t) timeptr->tm_mday - 1) * 24 +
134 (time_t) timeptr->tm_hour) * 60 +
135 (time_t) timeptr->tm_min) * 60 +
136 (time_t) timeptr->tm_sec;
137 logFunction(printf("unchecked_mkutc --> " FMT_T "\n", timestamp););
138 return timestamp;
139 } /* unchecked_mkutc */
140
141
142
143 /**
144 * Convert a timestamp into a time from the local time zone.
145 * The timestamp is expressed in seconds since the Unix Epoch.
146 * The Unix Epoch (1970-01-01 00:00:00 UTC) corresponds to 0.
147 * @param timestamp Unix timestamp to be converted.
148 * @return the local time that corresponds to the timestamp.
149 */
timFromTimestamp(time_t timestamp,intType * year,intType * month,intType * day,intType * hour,intType * minute,intType * second,intType * micro_sec,intType * timeZone,boolType * isDst)150 void timFromTimestamp (time_t timestamp,
151 intType *year, intType *month, intType *day, intType *hour,
152 intType *minute, intType *second, intType *micro_sec, intType *timeZone,
153 boolType *isDst)
154
155 {
156 #if defined USE_LOCALTIME_R || defined USE_LOCALTIME_S
157 struct tm tm_result;
158 #endif
159 struct tm *local_time;
160
161 /* timFromTimestamp */
162 logFunction(printf("timFromTimestamp(" FMT_T ")\n", timestamp););
163 #if defined USE_LOCALTIME_R
164 local_time = localtime_r(×tamp, &tm_result);
165 #elif defined USE_LOCALTIME_S
166 if (localtime_s(&tm_result, ×tamp) != 0) {
167 local_time = NULL;
168 } else {
169 local_time = &tm_result;
170 } /* if */
171 #else
172 local_time = localtime(×tamp);
173 #endif
174 if (unlikely(local_time == NULL)) {
175 logError(printf("timFromTimestamp(" FMT_T "): "
176 "One of localtime/localtime_r/localtime_s failed:\n"
177 "errno=%d\nerror: %s\n",
178 timestamp, errno, strerror(errno)););
179 raise_error(RANGE_ERROR);
180 } else {
181 *year = local_time->tm_year + 1900;
182 *month = local_time->tm_mon + 1;
183 *day = local_time->tm_mday;
184 *hour = local_time->tm_hour;
185 *minute = local_time->tm_min;
186 *second = local_time->tm_sec;
187 *micro_sec = 0;
188 *timeZone = (intType) (unchecked_mkutc(local_time) - timestamp) / 60;
189 /* Correct timeZone values that are outside of the allowed range. */
190 /* Under Linux this never happens, but Windows has this problem. */
191 if (unlikely(*timeZone < -12 * 60)) {
192 *timeZone += 24 * 60;
193 if (*day < monthDays[((*year % 4 == 0 && *year % 100 != 0) || *year % 400 == 0)][*month - 1]) {
194 (*day)++;
195 } else if (*month < 12) {
196 (*month)++;
197 *day = 1;
198 } else {
199 (*year)++;
200 *month = 1;
201 *day = 1;
202 } /* if */
203 } else if (unlikely(*timeZone > 14 * 60)) {
204 *timeZone -= 24 * 60;
205 if (*day > 1) {
206 (*day)--;
207 } else if (*month > 1) {
208 (*month)--;
209 *day = monthDays[((*year % 4 == 0 && *year % 100 != 0) || *year % 400 == 0)][*month - 1];
210 } else {
211 (*year)--;
212 *month = 12;
213 *day = 31;
214 } /* if */
215 } /* if */
216 *isDst = local_time->tm_isdst > 0;
217 } /* if */
218 logFunction(printf("timFromTimestamp(" FMT_T ", "
219 F_D(04) "-" F_D(02) "-" F_D(02) " "
220 F_D(02) ":" F_D(02) ":" F_D(02) "." F_D(06) " "
221 FMT_D " %d) -->\n",
222 timestamp, *year, *month, *day, *hour, *minute, *second,
223 *micro_sec, *timeZone, *isDst););
224 } /* timFromTimestamp */
225
226
227
228 /**
229 * Convert a timestamp into a time from the local time zone.
230 * The timestamp is expressed in seconds since the Unix Epoch.
231 * The Unix Epoch (1970-01-01 00:00:00 UTC) corresponds to 0.
232 * @param timestamp Unix timestamp to be converted.
233 * @return the local time that corresponds to the timestamp.
234 */
timFromIntTimestamp(intType timestamp,intType * year,intType * month,intType * day,intType * hour,intType * minute,intType * second,intType * micro_sec,intType * timeZone,boolType * isDst)235 void timFromIntTimestamp (intType timestamp,
236 intType *year, intType *month, intType *day, intType *hour,
237 intType *minute, intType *second, intType *micro_sec, intType *timeZone,
238 boolType *isDst)
239
240 { /* timFromIntTimestamp */
241 if (!inTimeTRange(timestamp)) {
242 logError(printf("timFromIntTimestamp(" FMT_D "): "
243 "Timestamp not in allowed range of time_t.\n",
244 timestamp););
245 raise_error(RANGE_ERROR);
246 } else {
247 timFromTimestamp((time_t) timestamp,
248 year, month, day, hour, minute, second, micro_sec, timeZone, isDst);
249 } /* if */
250 } /* timFromIntTimestamp */
251
252
253
254 /**
255 * Convert a timestamp into an UTC time.
256 * The timestamp is expressed in seconds since the Unix Epoch.
257 * The Unix Epoch (1970-01-01 00:00:00 UTC) corresponds to 0.
258 * @param timestamp Unix timestamp to be converted.
259 */
timUtcFromTimestamp(timeStampType timestamp,intType * year,intType * month,intType * day,intType * hour,intType * minute,intType * second)260 void timUtcFromTimestamp (timeStampType timestamp,
261 intType *year, intType *month, intType *day,
262 intType *hour, intType *minute, intType *second)
263
264 {
265 int64Type daysSince1970_01_01;
266 int64Type currentYear;
267 int64Type previousYear;
268 int64Type dayJan1;
269 int64Type yearDay;
270 int32Type secondsSinceMidnight;
271 int leapYear;
272 int monthIdx;
273
274 /* timUtcFromTimestamp */
275 logFunction(printf("timUtcFromTimestamp(" FMT_D64 ")\n", timestamp););
276 if (timestamp < 0) {
277 daysSince1970_01_01 = (timestamp + 1) / SECONDS_PER_DAY - 1;
278 secondsSinceMidnight = (int32Type) (timestamp - daysSince1970_01_01 * SECONDS_PER_DAY);
279 currentYear = 1969 + timestamp / SECONDS_PER_YEAR;
280 do {
281 previousYear = currentYear - 1;
282 if (previousYear < 0) {
283 /* previousYear is divided and truncated towards minus infinite. */
284 /* This is done with: quotient = (dividend + 1) / divisor - 1; */
285 dayJan1 = previousYear * 365 +
286 (currentYear / 4 - 1) -
287 (currentYear / 100 - 1) +
288 (currentYear / 400 - 1) -
289 DAYS_FROM_0_TO_1970;
290 } else {
291 dayJan1 = previousYear * 365 +
292 previousYear / 4 -
293 previousYear / 100 +
294 previousYear / 400 -
295 DAYS_FROM_0_TO_1970;
296 } /* if */
297 yearDay = daysSince1970_01_01 - dayJan1;
298 if ((currentYear % 4 == 0 && currentYear % 100 != 0) || currentYear % 400 == 0) {
299 leapYear = 1;
300 } else {
301 leapYear = 0;
302 } /* if */
303 if (yearDay >= (leapYear ? 366 : 365)) {
304 /* Add at least one year. Underestimate the added number */
305 /* of years, by assuming that every year is a leap year. */
306 currentYear += (yearDay - (leapYear ? 366 : 365)) / 366 + 1;
307 } /* if */
308 } while (yearDay >= (leapYear ? 366 : 365));
309 } else {
310 daysSince1970_01_01 = timestamp / SECONDS_PER_DAY;
311 secondsSinceMidnight = (int32Type) (timestamp % SECONDS_PER_DAY);
312 currentYear = 1970 + timestamp / SECONDS_PER_YEAR;
313 do {
314 previousYear = currentYear - 1;
315 dayJan1 = previousYear * 365 +
316 previousYear / 4 -
317 previousYear / 100 +
318 previousYear / 400 -
319 DAYS_FROM_0_TO_1970;
320 yearDay = daysSince1970_01_01 - dayJan1;
321 if (yearDay < 0) {
322 /* Subtract at least one year. Underestimate the subtracted */
323 /* number of years, by assuming that every year is a leap year. */
324 currentYear -= (-yearDay - 1) / 366 + 1;
325 } /* if */
326 } while (yearDay < 0);
327 } /* if */
328 if ((currentYear % 4 == 0 && currentYear % 100 != 0) || currentYear % 400 == 0) {
329 leapYear = 1;
330 } else {
331 leapYear = 0;
332 } /* if */
333 for (monthIdx = 0; monthIdx < 12 && yearDay >= yearDays[leapYear][monthIdx]; monthIdx++) ;
334 *year = currentYear;
335 *month = monthIdx;
336 *day = yearDay - yearDays[leapYear][monthIdx - 1] + 1;
337 *hour = secondsSinceMidnight / 3600;
338 *minute = (secondsSinceMidnight / 60) % 60;
339 *second = secondsSinceMidnight % 60;
340 logFunction(printf("timUtcFromTimestamp -> "
341 F_D(04) "-" F_D(02) "-" F_D(02) " "
342 F_D(02) ":" F_D(02) ":" F_D(02) "\n",
343 *year, *month, *day, *hour, *minute, *second););
344 } /* timUtcFromTimestamp */
345
346
347
348 /**
349 * Convert a time with timeZone to a timestamp.
350 * The timestamp is expressed in seconds since the Unix Epoch.
351 * The Unix Epoch (1970-01-01 00:00:00 UTC) corresponds to 0.
352 * @return the timestamp that corresponds to the time, or
353 * TIMESTAMPTYPE_MIN if a value is not in the allowed range.
354 */
timToTimestamp(intType year,intType month,intType day,intType hour,intType minute,intType second,intType timeZone)355 timeStampType timToTimestamp (intType year, intType month, intType day,
356 intType hour, intType minute, intType second, intType timeZone)
357
358 {
359 int leap_year;
360 timeStampType previousYear;
361 timeStampType timestamp;
362
363 /* timToTimestamp */
364 logFunction(printf("timToTimestamp(" F_D(04) "-" F_D(02) "-" F_D(02) " "
365 F_D(02) ":" F_D(02) ":" F_D(02) " "
366 FMT_D ")\n",
367 year, month, day, hour, minute, second, timeZone););
368 if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
369 leap_year = 1;
370 } else {
371 leap_year = 0;
372 } /* if */
373 if (unlikely(year <= MINIMUM_TIMESTAMP_YEAR ||
374 year >= MAXIMUM_TIMESTAMP_YEAR ||
375 month < 1 || month > 12 || day < 1 ||
376 day > monthDays[leap_year][month - 1] ||
377 hour < 0 || hour >= 24 ||
378 minute < 0 || minute >= 60 ||
379 second < 0 || second >= 60)) {
380 logError(printf("timToTimestamp(" F_D(04) "-" F_D(02) "-" F_D(02) " "
381 F_D(02) ":" F_D(02) ":" F_D(02) " "
382 FMT_D "): Not in allowed range.\n",
383 year, month, day, hour, minute, second, timeZone););
384 timestamp = TIMESTAMPTYPE_MIN;
385 } else {
386 previousYear = (timeStampType) year - 1;
387 if (previousYear < 0) {
388 /* previousYear is divided and truncated towards minus infinite. */
389 /* This is done with: quotient = (dividend + 1) / divisor - 1; */
390 timestamp = previousYear * 365 +
391 ((timeStampType) year / 4 - 1) -
392 ((timeStampType) year / 100 - 1) +
393 ((timeStampType) year / 400 - 1);
394 } else {
395 timestamp = previousYear * 365 +
396 previousYear / 4 -
397 previousYear / 100 +
398 previousYear / 400;
399 /* printf("timestamp: " FMT_D64 "\n", timestamp - DAYS_FROM_0_TO_1970); */
400 } /* if */
401 timestamp = (((timestamp -
402 (timeStampType) DAYS_FROM_0_TO_1970 +
403 yearDays[leap_year][month - 1] +
404 (timeStampType) day - 1) * 24 +
405 (timeStampType) hour) * 60 +
406 (timeStampType) minute) * 60 +
407 (timeStampType) second;
408 if (unlikely(timeZone < -1500 || timeZone > 1500)) {
409 logError(printf("timToTimestamp("
410 F_D(04) "-" F_D(02) "-" F_D(02) " "
411 F_D(02) ":" F_D(02) ":" F_D(02) " " FMT_D
412 "): timeZone not in allowed range.\n",
413 year, month, day, hour, minute, second, timeZone););
414 timestamp = TIMESTAMPTYPE_MIN;
415 } else {
416 timestamp -= (timeStampType) timeZone * 60;
417 } /* if */
418 } /* if */
419 logFunction(printf("timToTimestamp --> " FMT_D64 "\n", timestamp););
420 return timestamp;
421 } /* timToTimestamp */
422
423
424
425 #if TIME_T_SIZE != TIMESTAMPTYPE_SIZE || !TIME_T_SIGNED
426 /**
427 * Convert a time with timeZone to a timestamp.
428 * The timestamp is expressed in seconds since the Unix Epoch.
429 * The Unix Epoch (1970-01-01 00:00:00 UTC) corresponds to 0.
430 * @return the timestamp that corresponds to the time, or
431 * TIME_T_ERROR if a value is not in the allowed range.
432 */
timToOsTimestamp(intType year,intType month,intType day,intType hour,intType minute,intType second,intType timeZone)433 time_t timToOsTimestamp (intType year, intType month, intType day,
434 intType hour, intType minute, intType second, intType timeZone)
435
436 {
437 timeStampType timestamp;
438 time_t osTimestamp;
439
440 /* timToOsTimestamp */
441 logFunction(printf("timToOsTimestamp(" F_D(04) "-" F_D(02) "-" F_D(02) " "
442 F_D(02) ":" F_D(02) ":" F_D(02) " "
443 FMT_D ")\n",
444 year, month, day, hour, minute, second, timeZone););
445 timestamp = timToTimestamp(year, month, day, hour, minute, second,
446 timeZone);
447 if (unlikely(timestamp == TIMESTAMPTYPE_MIN || !inTimeTRange(timestamp))) {
448 osTimestamp = TIME_T_ERROR;
449 } else {
450 osTimestamp = (time_t) timestamp;
451 } /* if */
452 logFunction(printf("timToOsTimestamp --> " FMT_T "\n", timestamp););
453 return osTimestamp;
454 } /* timToOsTimestamp */
455 #endif
456
457
458
459 /**
460 * Sets timeZone and daylightSavingTime for a given time.
461 * @return the time in the local time zone.
462 */
timSetLocalTZ(intType year,intType month,intType day,intType hour,intType minute,intType second,intType * timeZone,boolType * isDst)463 void timSetLocalTZ (intType year, intType month, intType day, intType hour,
464 intType minute, intType second, intType *timeZone, boolType *isDst)
465
466 {
467 time_t timestamp;
468 #if defined USE_LOCALTIME_R || defined USE_LOCALTIME_S
469 struct tm tm_result;
470 #endif
471 struct tm *local_time;
472 time_t timeZoneReference;
473
474 /* timSetLocalTZ */
475 logFunction(printf("timSetLocalTZ(" F_D(04) "-" F_D(02) "-" F_D(02) " "
476 F_D(02) ":" F_D(02) ":" F_D(02) ")\n",
477 year, month, day, hour, minute, second););
478 timestamp = 0;
479 #if defined USE_LOCALTIME_R
480 local_time = localtime_r(×tamp, &tm_result);
481 #elif defined USE_LOCALTIME_S
482 if (localtime_s(&tm_result, ×tamp) != 0) {
483 local_time = NULL;
484 } else {
485 local_time = &tm_result;
486 } /* if */
487 #else
488 local_time = localtime(×tamp);
489 #endif
490 if (unlikely(local_time == NULL)) {
491 logError(printf("timSetLocalTZ: "
492 "One of localtime/localtime_r/localtime_s(" FMT_T ") failed:\n"
493 "errno=%d\nerror: %s\n",
494 timestamp, errno, strerror(errno)););
495 raise_error(RANGE_ERROR);
496 } else {
497 timeZoneReference = unchecked_mkutc(local_time) / 60;
498 /* printf("timeZoneReference: %ld\n", timeZoneReference); */
499 timestamp = timToOsTimestamp(year, month, day, hour, minute, second, 0);
500 if (unlikely(timestamp == TIME_T_ERROR)) {
501 *timeZone = timeZoneReference;
502 *isDst = 0;
503 } else {
504 /* printf("timestamp: %ld\n", timestamp); */
505 timestamp -= timeZoneReference * 60;
506 #if !LOCALTIME_WORKS_SIGNED
507 if (timestamp < 0) {
508 *timeZone = timeZoneReference;
509 *isDst = 0;
510 } else {
511 #endif
512 #if defined USE_LOCALTIME_R
513 local_time = localtime_r(×tamp, &tm_result);
514 #elif defined USE_LOCALTIME_S
515 if (localtime_s(&tm_result, ×tamp) != 0) {
516 local_time = NULL;
517 } else {
518 local_time = &tm_result;
519 } /* if */
520 #else
521 local_time = localtime(×tamp);
522 #endif
523 if (unlikely(local_time == NULL)) {
524 logError(printf("timSetLocalTZ: One of "
525 "localtime/localtime_r/localtime_s(" FMT_T ") failed:\n"
526 "errno=%d\nerror: %s\n",
527 timestamp, errno, strerror(errno)););
528 raise_error(RANGE_ERROR);
529 } else {
530 *timeZone = (intType) (unchecked_mkutc(local_time) - timestamp) / 60;
531 /* Correct timeZone values that are outside of the allowed range. */
532 /* Under Linux this never happens, but Windows has this problem. */
533 if (unlikely(*timeZone < -12 * 60)) {
534 *timeZone += 24 * 60;
535 } else if (unlikely(*timeZone > 14 * 60)) {
536 *timeZone -= 24 * 60;
537 } /* if */
538 *isDst = local_time->tm_isdst > 0;
539 } /* if */
540 #if !LOCALTIME_WORKS_SIGNED
541 } /* if */
542 #endif
543 } /* if */
544 } /* if */
545 logFunction(printf("timSetLocalTZ(" F_D(04) "-" F_D(02) "-" F_D(02) " "
546 F_D(02) ":" F_D(02) ":" F_D(02) " " FMT_D " %d) -->\n",
547 year, month, day, hour, minute, second,
548 *timeZone, *isDst););
549 } /* timSetLocalTZ */
550
551
552
553 /**
554 * Assign year, month and day from the number of days since 1900-01-01.
555 * This functions works correct for the whole range of daysSince1900_01_01.
556 */
dateFromDaysSince1900(int32Type daysSince1900_01_01,intType * year,intType * month,intType * day)557 void dateFromDaysSince1900 (int32Type daysSince1900_01_01,
558 intType *year, intType *month, intType *day)
559
560 {
561 int32Type currentYear;
562 int32Type previousYear;
563 int32Type dayJan1;
564 int32Type yearDay;
565 int leapYear;
566 int monthIdx;
567
568 /* dateFromDaysSince1900 */
569 logFunction(printf("dateFromDaysSince1900(" FMT_D32 ")\n", daysSince1900_01_01););
570 if (daysSince1900_01_01 < 0) {
571 currentYear = 1899 + daysSince1900_01_01 / 365;
572 do {
573 previousYear = currentYear - 1;
574 if (previousYear < 0) {
575 /* previousYear is divided and truncated towards minus infinite. */
576 /* This is done with: quotient = (dividend + 1) / divisor - 1; */
577 dayJan1 = previousYear * 365 +
578 (currentYear / 4 - 1) -
579 (currentYear / 100 - 1) +
580 (currentYear / 400 - 1) -
581 DAYS_FROM_0_TO_1900;
582 } else {
583 dayJan1 = previousYear * 365 +
584 previousYear / 4 -
585 previousYear / 100 +
586 previousYear / 400 -
587 DAYS_FROM_0_TO_1900;
588 } /* if */
589 yearDay = daysSince1900_01_01 - dayJan1;
590 if ((currentYear % 4 == 0 && currentYear % 100 != 0) || currentYear % 400 == 0) {
591 leapYear = 1;
592 } else {
593 leapYear = 0;
594 } /* if */
595 if (yearDay >= (leapYear ? 366 : 365)) {
596 /* Add at least one year. Underestimate the added number */
597 /* of years, by assuming that every year is a leap year. */
598 currentYear += (yearDay - (leapYear ? 366 : 365)) / 366 + 1;
599 } /* if */
600 } while (yearDay >= (leapYear ? 366 : 365));
601 } else {
602 currentYear = 1900 + daysSince1900_01_01 / 365;
603 do {
604 previousYear = currentYear - 1;
605 dayJan1 = previousYear * 365 +
606 previousYear / 4 -
607 previousYear / 100 +
608 previousYear / 400 -
609 DAYS_FROM_0_TO_1900;
610 yearDay = daysSince1900_01_01 - dayJan1;
611 if (yearDay < 0) {
612 /* Subtract at least one year. Underestimate the subtracted */
613 /* number of years, by assuming that every year is a leap year. */
614 currentYear -= (-yearDay - 1) / 366 + 1;
615 } /* if */
616 } while (yearDay < 0);
617 } /* if */
618 if ((currentYear % 4 == 0 && currentYear % 100 != 0) || currentYear % 400 == 0) {
619 leapYear = 1;
620 } else {
621 leapYear = 0;
622 } /* if */
623 for (monthIdx = 0; monthIdx < 12 && yearDay >= yearDays[leapYear][monthIdx]; monthIdx++) ;
624 *year = currentYear;
625 *month = monthIdx;
626 *day = yearDay - yearDays[leapYear][monthIdx - 1] + 1;
627 logFunction(printf("dateFromDaysSince1900 -> " F_D(04) "-" F_D(02) "-" F_D(02) "\n",
628 *year, *month, *day););
629 } /* dateFromDaysSince1900 */
630
631
632
633 /**
634 * Assign date and time from a string with the given 'isoDate'.
635 * Possible formats for 'isoData' are "yyyy-mm-dd hh:mm:ss.uuuuuu",
636 * "yyyy-mm-dd hh:mm:ss", "yyyy-mm-dd hh:mm", "yyyy-mm-dd hh",
637 * "yyyy-mm-dd", "yyyy-mm", "hh:mm:ss.uuuuuu", "hh:mm:ss" and "hh:mm".
638 * @param isoDate String with an ISO date/time.
639 */
assignTime(const_cstriType isoDate,intType * year,intType * month,intType * day,intType * hour,intType * minute,intType * second,intType * microSecond,boolType * isTime)640 boolType assignTime (const_cstriType isoDate, intType *year, intType *month,
641 intType *day, intType *hour, intType *minute, intType *second,
642 intType *microSecond, boolType *isTime)
643
644 {
645 int numAssigned;
646 memSizeType microsecStriLen;
647 char microsecStri[6 + NULL_TERMINATION_LEN];
648 boolType okay = FALSE;
649
650 /* assignTime */
651 numAssigned = sscanf(isoDate,
652 FMT_D "-" FMT_U "-" FMT_U " "
653 FMT_U ":" FMT_U ":" FMT_U ".%6s",
654 year, month, day, hour, minute, second,
655 microsecStri);
656 if (numAssigned >= 2) {
657 *isTime = FALSE;
658 if (numAssigned == 7) {
659 microsecStriLen = (memSizeType) strlen(microsecStri);
660 if (microsecStriLen < 6) {
661 memset(µsecStri[microsecStriLen], '0', 6 - microsecStriLen);
662 microsecStri[6] = '\0';
663 } /* if */
664 okay = sscanf((const char *) microsecStri, FMT_D, microSecond) == 1;
665 } else {
666 okay = TRUE;
667 *microSecond = 0;
668 if (numAssigned <= 5) {
669 *second = 0;
670 if (numAssigned <= 4) {
671 *minute = 0;
672 if (numAssigned <= 3) {
673 *hour = 0;
674 if (numAssigned <= 2) {
675 *day = 1;
676 } /* if */
677 } /* if */
678 } /* if */
679 } /* if */
680 } /* if */
681 } else {
682 numAssigned = sscanf(isoDate,
683 FMT_U ":" FMT_U ":" FMT_U ".%6s",
684 hour, minute, second, microsecStri);
685 if (numAssigned >= 2) {
686 *year = 0;
687 *month = 1;
688 *day = 1;
689 *microSecond = 0;
690 *isTime = TRUE;
691 if (numAssigned == 4) {
692 microsecStriLen = (memSizeType) strlen(microsecStri);
693 if (microsecStriLen < 6) {
694 memset(µsecStri[microsecStriLen], '0', 6 - microsecStriLen);
695 microsecStri[6] = '\0';
696 } /* if */
697 okay = sscanf((const char *) microsecStri, FMT_D, microSecond) == 1;
698 } else {
699 okay = TRUE;
700 *microSecond = 0;
701 if (numAssigned <= 2) {
702 *second = 0;
703 } /* if */
704 } /* if */
705 } /* if */
706 } /* if */
707 logFunction(printf("assignTime(" F_D(04) "-" F_D(02) "-" F_D(02) " "
708 F_D(02) ":" F_D(02) ":" F_D(02) "."
709 F_D(06) ", %d) --> %d\n",
710 *year, *month, *day, *hour, *minute, *second,
711 *microSecond, *isTime, okay););
712 return okay;
713 } /* assignTime */
714