Line data Source code
1 : /*
2 : * Present - Date/Time Library
3 : *
4 : * Implementation of the Timestamp methods
5 : *
6 : * Licensed under the MIT License.
7 : * For details, see LICENSE.
8 : */
9 :
10 : #include <assert.h>
11 : #include <stddef.h>
12 :
13 : #include "present.h"
14 :
15 : #include "utils/constants.h"
16 : #include "utils/impl-utils.h"
17 : #include "utils/time-utils.h"
18 :
19 : /**
20 : * Convert a struct tm to a Date.
21 : *
22 : * Precondition: The struct tm must be valid (see @p clean_struct_tm).
23 : */
24 : static void
25 1256 : struct_tm_to_date(const struct tm * const tm, struct Date * const date)
26 : {
27 3768 : Date_ptr_from_year_month_day(
28 : date,
29 1256 : tm->tm_year + STRUCT_TM_YEAR_OFFSET,
30 1256 : tm->tm_mon + STRUCT_TM_MONTH_OFFSET,
31 1256 : tm->tm_mday);
32 1256 : }
33 :
34 : /**
35 : * Convert a struct tm to a ClockTime.
36 : *
37 : * Precondition: The struct tm must be valid (see @p clean_struct_tm).
38 : */
39 : static void
40 136 : struct_tm_to_clock_time(
41 : const struct tm * const tm,
42 : struct ClockTime * const clock_time)
43 : {
44 408 : ClockTime_ptr_from_hour_minute_second(
45 : clock_time,
46 136 : tm->tm_hour,
47 136 : tm->tm_min,
48 136 : tm->tm_sec);
49 136 : }
50 :
51 : /**
52 : * Check additional_nanoseconds to make sure it is a positive integer less
53 : * than NANOSECONDS_IN_SECOND.
54 : */
55 : #define CHECK_DATA(data) \
56 : do { \
57 : while (data.additional_nanoseconds < 0) { \
58 : data.timestamp_seconds -= 1; \
59 : data.additional_nanoseconds += NANOSECONDS_IN_SECOND; \
60 : } \
61 : if (data.additional_nanoseconds >= NANOSECONDS_IN_SECOND) { \
62 : data.timestamp_seconds += data.additional_nanoseconds / \
63 : NANOSECONDS_IN_SECOND; \
64 : data.additional_nanoseconds %= NANOSECONDS_IN_SECOND; \
65 : } \
66 : } while (0)
67 :
68 : /**
69 : * Check a Date and a ClockTime to make sure they aren't erroneous (if so, set
70 : * the proper errors on "result").
71 : */
72 : #define CHECK_DATE_AND_CLOCK_TIME(date, clock_time) \
73 : do { \
74 : if ((date)->has_error) { \
75 : result->has_error = 1; \
76 : result->errors.invalid_date = 1; \
77 : } \
78 : if ((clock_time)->has_error) { \
79 : result->has_error = 1; \
80 : result->errors.invalid_clock_time = 1; \
81 : } \
82 : } while (0)
83 :
84 :
85 : /** Initialize a new Timestamp instance based on its data parameters. */
86 : static void
87 1273 : init_timestamp(
88 : struct Timestamp * const result,
89 : int_timestamp timestamp_seconds,
90 : int_timestamp additional_nanoseconds)
91 : {
92 1273 : assert(result != NULL);
93 1273 : CLEAR(result);
94 :
95 1273 : result->data_.timestamp_seconds = timestamp_seconds;
96 1273 : result->data_.additional_nanoseconds = additional_nanoseconds;
97 1273 : }
98 :
99 : /**
100 : * Initialize a new Timestamp instance based on a Date and a ClockTime in UTC.
101 : */
102 : static void
103 1254 : init_timestamp_from_date_and_clock_time_utc(
104 : struct Timestamp * const result,
105 : const struct Date * const date,
106 : const struct ClockTime * const clock_time)
107 : {
108 : struct TimeDelta time_since_midnight;
109 :
110 1254 : assert(result != NULL);
111 1254 : assert(date != NULL);
112 1254 : assert(clock_time != NULL);
113 :
114 1254 : CLEAR(result);
115 :
116 : /* Make sure the Date and ClockTime aren't erroneous */
117 1254 : CHECK_DATE_AND_CLOCK_TIME(date, clock_time);
118 :
119 1254 : if (!result->has_error) {
120 : /* This is a more useful form of the clock time */
121 1252 : time_since_midnight = ClockTime_time_since_midnight(clock_time);
122 :
123 2504 : init_timestamp(
124 : result,
125 1252 : to_unix_timestamp(
126 : Date_year(date),
127 : Date_month(date),
128 : Date_day(date),
129 : 0, 0, 0) +
130 1252 : time_since_midnight.data_.delta_seconds,
131 : time_since_midnight.data_.delta_nanoseconds);
132 : }
133 1254 : }
134 :
135 : /**
136 : * Initialize a new Timestamp instance based on a Date, a ClockTime, and a time
137 : * zone offset.
138 : */
139 : static void
140 38 : init_timestamp_from_date_and_clock_time(
141 : struct Timestamp * const result,
142 : const struct Date * const date,
143 : const struct ClockTime * const clock_time,
144 : const struct TimeDelta * const time_zone_offset)
145 : {
146 38 : assert(result != NULL);
147 38 : assert(date != NULL);
148 38 : assert(clock_time != NULL);
149 :
150 38 : CLEAR(result);
151 :
152 : /* Make sure the Date and ClockTime aren't erroneous */
153 38 : CHECK_DATE_AND_CLOCK_TIME(date, clock_time);
154 :
155 38 : if (!result->has_error) {
156 : /* First, pretend it's just UTC */
157 36 : init_timestamp_from_date_and_clock_time_utc(result, date, clock_time);
158 : /* Now, subtract the UTC offset (since we're technically converting
159 : TO UTC) */
160 36 : Timestamp_subtract_TimeDelta(result, time_zone_offset);
161 : }
162 38 : }
163 :
164 : /**
165 : * Initialize a new Timestamp instance based on a "struct tm" in the system's
166 : * current local time zone.
167 : */
168 : static void
169 2 : init_timestamp_from_struct_tm_local(
170 : struct Timestamp * const result,
171 : const struct tm tm)
172 : {
173 : struct tm tm_copy;
174 : time_t time;
175 :
176 2 : tm_copy = tm;
177 : /* Throw it right through mktime */
178 2 : time = struct_tm_to_time_t_local(&tm_copy);
179 2 : init_timestamp(result, time_t_to_unix_timestamp(time), 0);
180 2 : }
181 :
182 :
183 : struct Timestamp
184 2 : Timestamp_from_time_t(const time_t time)
185 : {
186 : struct Timestamp result;
187 2 : init_timestamp(&result, time_t_to_unix_timestamp(time), 0);
188 2 : return result;
189 : }
190 :
191 : void
192 10 : Timestamp_ptr_from_time_t(
193 : struct Timestamp * const result,
194 : const time_t time)
195 : {
196 10 : init_timestamp(result, time_t_to_unix_timestamp(time), 0);
197 10 : }
198 :
199 : struct Timestamp
200 6 : Timestamp_from_struct_tm(
201 : const struct tm tm,
202 : const struct TimeDelta * const time_zone_offset)
203 : {
204 : struct Timestamp result;
205 6 : Timestamp_ptr_from_struct_tm(&result, tm, time_zone_offset);
206 6 : return result;
207 : }
208 :
209 : void
210 18 : Timestamp_ptr_from_struct_tm(
211 : struct Timestamp * const result,
212 : const struct tm tm,
213 : const struct TimeDelta * const time_zone_offset)
214 : {
215 : struct Date date;
216 : struct ClockTime clock_time;
217 : struct tm tm_copy;
218 :
219 18 : tm_copy = tm;
220 18 : clean_struct_tm(&tm_copy);
221 :
222 18 : struct_tm_to_date(&tm_copy, &date);
223 18 : struct_tm_to_clock_time(&tm_copy, &clock_time);
224 18 : init_timestamp_from_date_and_clock_time(
225 : result, &date, &clock_time, time_zone_offset);
226 18 : }
227 :
228 : struct Timestamp
229 1 : Timestamp_from_struct_tm_utc(const struct tm tm)
230 : {
231 : struct Timestamp result;
232 1 : Timestamp_ptr_from_struct_tm_utc(&result, tm);
233 1 : return result;
234 : }
235 :
236 : void
237 2 : Timestamp_ptr_from_struct_tm_utc(
238 : struct Timestamp * const result,
239 : const struct tm tm)
240 : {
241 : struct Date date;
242 : struct ClockTime clock_time;
243 : struct tm tm_copy;
244 :
245 2 : tm_copy = tm;
246 2 : clean_struct_tm(&tm_copy);
247 :
248 2 : struct_tm_to_date(&tm_copy, &date);
249 2 : struct_tm_to_clock_time(&tm_copy, &clock_time);
250 2 : init_timestamp_from_date_and_clock_time_utc(result, &date, &clock_time);
251 2 : }
252 :
253 : struct Timestamp
254 1 : Timestamp_from_struct_tm_local(const struct tm tm)
255 : {
256 : struct Timestamp result;
257 1 : Timestamp_ptr_from_struct_tm_local(&result, tm);
258 1 : return result;
259 : }
260 :
261 : void
262 1 : Timestamp_ptr_from_struct_tm_local(
263 : struct Timestamp * const result,
264 : const struct tm tm)
265 : {
266 1 : init_timestamp_from_struct_tm_local(result, tm);
267 1 : }
268 :
269 : struct Timestamp
270 6 : Timestamp_create(
271 : const struct Date * const date,
272 : const struct ClockTime * const clock_time,
273 : const struct TimeDelta * const time_zone_offset)
274 : {
275 : struct Timestamp result;
276 6 : Timestamp_ptr_create(&result, date, clock_time, time_zone_offset);
277 6 : return result;
278 : }
279 :
280 : void
281 20 : Timestamp_ptr_create(
282 : struct Timestamp * const result,
283 : const struct Date * const date,
284 : const struct ClockTime * const clock_time,
285 : const struct TimeDelta * const time_zone_offset)
286 : {
287 20 : init_timestamp_from_date_and_clock_time(
288 : result, date, clock_time, time_zone_offset);
289 20 : }
290 :
291 : struct Timestamp
292 8 : Timestamp_create_utc(
293 : const struct Date * const date,
294 : const struct ClockTime * const clock_time)
295 : {
296 : struct Timestamp result;
297 8 : Timestamp_ptr_create_utc(&result, date, clock_time);
298 8 : return result;
299 : }
300 :
301 : void
302 1216 : Timestamp_ptr_create_utc(
303 : struct Timestamp * const result,
304 : const struct Date * const date,
305 : const struct ClockTime * const clock_time)
306 : {
307 1216 : init_timestamp_from_date_and_clock_time_utc(result, date, clock_time);
308 1216 : }
309 :
310 : struct Timestamp
311 1 : Timestamp_create_local(
312 : const struct Date * const date,
313 : const struct ClockTime * const clock_time)
314 : {
315 : struct Timestamp result;
316 1 : Timestamp_ptr_create_local(&result, date, clock_time);
317 1 : return result;
318 : }
319 :
320 : void
321 3 : Timestamp_ptr_create_local(
322 : struct Timestamp * const result,
323 : const struct Date * const date,
324 : const struct ClockTime * const clock_time)
325 : {
326 : struct tm tm;
327 :
328 3 : assert(result != NULL);
329 3 : assert(date != NULL);
330 3 : assert(clock_time != NULL);
331 :
332 3 : CLEAR(result);
333 :
334 : /* Make sure the Date and ClockTime aren't erroneous */
335 3 : CHECK_DATE_AND_CLOCK_TIME(date, clock_time);
336 :
337 3 : if (!result->has_error) {
338 1 : CLEAR(&tm);
339 1 : tm.tm_year = Date_year(date) - STRUCT_TM_YEAR_OFFSET;
340 1 : tm.tm_mon = Date_month(date) - STRUCT_TM_MONTH_OFFSET;
341 1 : tm.tm_mday = Date_day(date);
342 1 : tm.tm_hour = ClockTime_hour(clock_time);
343 1 : tm.tm_min = ClockTime_minute(clock_time);
344 1 : tm.tm_sec = ClockTime_second(clock_time);
345 1 : tm.tm_isdst = -1;
346 :
347 1 : init_timestamp_from_struct_tm_local(
348 : result,
349 : tm);
350 : }
351 3 : }
352 :
353 : struct Timestamp
354 1 : Timestamp_now(void)
355 : {
356 : struct Timestamp result;
357 1 : Timestamp_ptr_now(&result);
358 1 : return result;
359 : }
360 :
361 : void
362 4 : Timestamp_ptr_now(struct Timestamp * const result)
363 : {
364 : struct PresentNowStruct now;
365 4 : present_now(&now);
366 4 : init_timestamp(result, time_t_to_unix_timestamp(now.sec), now.nsec);
367 4 : }
368 :
369 : struct Timestamp
370 1 : Timestamp_epoch(void)
371 : {
372 : struct Timestamp result;
373 1 : init_timestamp(&result, 0, 0);
374 1 : return result;
375 : }
376 :
377 : void
378 2 : Timestamp_ptr_epoch(struct Timestamp * const result)
379 : {
380 2 : init_timestamp(result, 0, 0);
381 2 : }
382 :
383 : time_t
384 7 : Timestamp_get_time_t(const struct Timestamp * const self)
385 : {
386 7 : assert(self != NULL);
387 7 : assert(self->has_error == 0);
388 :
389 7 : return unix_timestamp_to_time_t(self->data_.timestamp_seconds);
390 : }
391 :
392 : struct tm
393 8 : Timestamp_get_struct_tm(
394 : const struct Timestamp * const self,
395 : const struct TimeDelta * const time_zone_offset)
396 : {
397 : struct Timestamp copy;
398 :
399 8 : assert(self != NULL);
400 8 : assert(self->has_error == 0);
401 8 : assert(time_zone_offset != NULL);
402 :
403 8 : copy = *self;
404 8 : Timestamp_add_TimeDelta(©, time_zone_offset);
405 8 : return Timestamp_get_struct_tm_utc(©);
406 : }
407 :
408 : struct tm
409 1351 : Timestamp_get_struct_tm_utc(const struct Timestamp * const self)
410 : {
411 : time_t time;
412 : struct tm result;
413 :
414 1351 : assert(self != NULL);
415 1351 : assert(self->has_error == 0);
416 :
417 1351 : time = unix_timestamp_to_time_t(self->data_.timestamp_seconds);
418 1351 : time_t_to_struct_tm(&time, &result);
419 1351 : return result;
420 : }
421 :
422 : struct tm
423 6 : Timestamp_get_struct_tm_local(const struct Timestamp * const self)
424 : {
425 : time_t time;
426 : struct tm result;
427 :
428 6 : assert(self != NULL);
429 6 : assert(self->has_error == 0);
430 :
431 6 : time = unix_timestamp_to_time_t(self->data_.timestamp_seconds);
432 6 : time_t_to_struct_tm_local(&time, &result);
433 6 : return result;
434 : }
435 :
436 : struct Date
437 3 : Timestamp_get_date(
438 : const struct Timestamp * const self,
439 : const struct TimeDelta * const time_zone_offset)
440 : {
441 : struct tm tm;
442 : struct Date date;
443 :
444 3 : assert(self != NULL);
445 3 : assert(self->has_error == 0);
446 3 : assert(time_zone_offset != NULL);
447 :
448 3 : tm = Timestamp_get_struct_tm(self, time_zone_offset);
449 3 : struct_tm_to_date(&tm, &date);
450 3 : return date;
451 : }
452 :
453 : struct Date
454 1231 : Timestamp_get_date_utc(const struct Timestamp * const self)
455 : {
456 : struct tm tm;
457 : struct Date date;
458 :
459 1231 : assert(self != NULL);
460 1231 : assert(self->has_error == 0);
461 :
462 1231 : tm = Timestamp_get_struct_tm_utc(self);
463 1231 : struct_tm_to_date(&tm, &date);
464 1231 : return date;
465 : }
466 :
467 : struct Date
468 2 : Timestamp_get_date_local(const struct Timestamp * const self)
469 : {
470 : struct tm tm;
471 : struct Date date;
472 :
473 2 : assert(self != NULL);
474 2 : assert(self->has_error == 0);
475 :
476 2 : tm = Timestamp_get_struct_tm_local(self);
477 2 : struct_tm_to_date(&tm, &date);
478 2 : return date;
479 : }
480 :
481 : struct ClockTime
482 3 : Timestamp_get_clock_time(
483 : const struct Timestamp * const self,
484 : const struct TimeDelta * const time_zone_offset)
485 : {
486 : struct tm tm;
487 : struct ClockTime clock_time;
488 :
489 3 : assert(self != NULL);
490 3 : assert(self->has_error == 0);
491 :
492 3 : tm = Timestamp_get_struct_tm(self, time_zone_offset);
493 3 : struct_tm_to_clock_time(&tm, &clock_time);
494 3 : return clock_time;
495 : }
496 :
497 : struct ClockTime
498 111 : Timestamp_get_clock_time_utc(const struct Timestamp * const self)
499 : {
500 : struct tm tm;
501 : struct ClockTime clock_time;
502 :
503 111 : assert(self != NULL);
504 111 : assert(self->has_error == 0);
505 :
506 111 : tm = Timestamp_get_struct_tm_utc(self);
507 111 : struct_tm_to_clock_time(&tm, &clock_time);
508 111 : return clock_time;
509 : }
510 :
511 : struct ClockTime
512 2 : Timestamp_get_clock_time_local(const struct Timestamp * const self)
513 : {
514 : struct tm tm;
515 : struct ClockTime clock_time;
516 :
517 2 : assert(self != NULL);
518 2 : assert(self->has_error == 0);
519 :
520 2 : tm = Timestamp_get_struct_tm_local(self);
521 2 : struct_tm_to_clock_time(&tm, &clock_time);
522 2 : return clock_time;
523 : }
524 :
525 : struct TimeDelta
526 8 : Timestamp_difference(
527 : const struct Timestamp * const self,
528 : const struct Timestamp * const other)
529 : {
530 : struct TimeDelta delta, ns_delta;
531 :
532 8 : assert(self != NULL);
533 8 : assert(self->has_error == 0);
534 8 : assert(other != NULL);
535 8 : assert(other->has_error == 0);
536 :
537 8 : delta = TimeDelta_from_seconds(
538 8 : self->data_.timestamp_seconds - other->data_.timestamp_seconds);
539 8 : ns_delta = TimeDelta_from_nanoseconds(
540 8 : self->data_.additional_nanoseconds -
541 8 : other->data_.additional_nanoseconds);
542 8 : TimeDelta_add(&delta, &ns_delta);
543 8 : return delta;
544 : }
545 :
546 : struct TimeDelta
547 2 : Timestamp_absolute_difference(
548 : const struct Timestamp * const self,
549 : const struct Timestamp * const other)
550 : {
551 : struct TimeDelta delta;
552 :
553 2 : assert(self != NULL);
554 2 : assert(self->has_error == 0);
555 2 : assert(other != NULL);
556 2 : assert(other->has_error == 0);
557 :
558 2 : delta = Timestamp_difference(self, other);
559 2 : if (TimeDelta_is_negative(&delta)) {
560 1 : TimeDelta_negate(&delta);
561 : }
562 2 : return delta;
563 : }
564 :
565 : void
566 10 : Timestamp_add_TimeDelta(
567 : struct Timestamp * const self,
568 : const struct TimeDelta * const delta)
569 : {
570 10 : assert(self != NULL);
571 10 : assert(self->has_error == 0);
572 10 : assert(delta != NULL);
573 :
574 10 : self->data_.timestamp_seconds += delta->data_.delta_seconds;
575 10 : self->data_.additional_nanoseconds += delta->data_.delta_nanoseconds;
576 10 : CHECK_DATA(self->data_);
577 10 : }
578 :
579 : void
580 8 : Timestamp_add_DayDelta(
581 : struct Timestamp * const self,
582 : const struct DayDelta * const delta)
583 : {
584 8 : assert(self != NULL);
585 8 : assert(self->has_error == 0);
586 8 : assert(delta != NULL);
587 :
588 8 : self->data_.timestamp_seconds += delta->data_.delta_days * SECONDS_IN_DAY;
589 8 : }
590 :
591 : void
592 8 : Timestamp_add_MonthDelta(
593 : struct Timestamp * const self,
594 : const struct MonthDelta * const delta)
595 : {
596 : time_t time;
597 : struct tm tm;
598 :
599 8 : assert(self != NULL);
600 8 : assert(self->has_error == 0);
601 8 : assert(delta != NULL);
602 :
603 8 : time = unix_timestamp_to_time_t(self->data_.timestamp_seconds);
604 8 : time_t_to_struct_tm(&time, &tm);
605 8 : tm.tm_mon += delta->data_.delta_months;
606 48 : self->data_.timestamp_seconds = time_t_to_unix_timestamp(to_unix_timestamp(
607 8 : tm.tm_year + STRUCT_TM_YEAR_OFFSET,
608 8 : tm.tm_mon + STRUCT_TM_MONTH_OFFSET,
609 8 : tm.tm_mday,
610 8 : tm.tm_hour,
611 8 : tm.tm_min,
612 8 : tm.tm_sec));
613 8 : }
614 :
615 : void
616 38 : Timestamp_subtract_TimeDelta(
617 : struct Timestamp * const self,
618 : const struct TimeDelta * const delta)
619 : {
620 38 : assert(self != NULL);
621 38 : assert(self->has_error == 0);
622 38 : assert(delta != NULL);
623 :
624 38 : self->data_.timestamp_seconds -= delta->data_.delta_seconds;
625 38 : self->data_.additional_nanoseconds -= delta->data_.delta_nanoseconds;
626 38 : CHECK_DATA(self->data_);
627 38 : }
628 :
629 : void
630 6 : Timestamp_subtract_DayDelta(
631 : struct Timestamp * const self,
632 : const struct DayDelta * const delta)
633 : {
634 6 : assert(self != NULL);
635 6 : assert(self->has_error == 0);
636 6 : assert(delta != NULL);
637 :
638 6 : self->data_.timestamp_seconds -= delta->data_.delta_days * SECONDS_IN_DAY;
639 6 : }
640 :
641 : void
642 7 : Timestamp_subtract_MonthDelta(
643 : struct Timestamp * const self,
644 : const struct MonthDelta * const delta)
645 : {
646 : time_t time;
647 : struct tm tm;
648 :
649 7 : assert(self != NULL);
650 7 : assert(self->has_error == 0);
651 7 : assert(delta != NULL);
652 :
653 7 : time = unix_timestamp_to_time_t(self->data_.timestamp_seconds);
654 7 : time_t_to_struct_tm(&time, &tm);
655 7 : tm.tm_mon -= delta->data_.delta_months;
656 42 : self->data_.timestamp_seconds = time_t_to_unix_timestamp(to_unix_timestamp(
657 7 : tm.tm_year + STRUCT_TM_YEAR_OFFSET,
658 7 : tm.tm_mon + STRUCT_TM_MONTH_OFFSET,
659 7 : tm.tm_mday,
660 7 : tm.tm_hour,
661 7 : tm.tm_min,
662 7 : tm.tm_sec));
663 7 : }
664 :
665 : short
666 0 : Timestamp_compare(
667 : const struct Timestamp * const lhs,
668 : const struct Timestamp * const rhs)
669 : {
670 0 : assert(lhs != NULL);
671 0 : assert(lhs->has_error == 0);
672 0 : assert(rhs != NULL);
673 0 : assert(rhs->has_error == 0);
674 :
675 0 : return
676 0 : STRUCT_COMPARE(timestamp_seconds,
677 : STRUCT_COMPARE(additional_nanoseconds, 0));
678 : }
679 :
680 0 : STRUCT_COMPARISON_OPERATORS(Timestamp)
681 :
|