This source file includes following definitions.
- crm_get_utc_time
- crm_time_new
- crm_time_new_undefined
- crm_time_is_defined
- crm_time_free
- year_days
- crm_time_january1_weekday
- crm_time_weeks_in_year
- crm_time_days_in_month
- crm_time_leapyear
- get_ordinal_days
- crm_time_log_alias
- crm_time_get_sec
- crm_time_get_timeofday
- crm_time_get_timezone
- crm_time_get_seconds
- crm_time_get_seconds_since_epoch
- crm_time_get_gregorian
- crm_time_get_ordinal
- crm_time_get_isoweek
- sec_usec_as_string
- crm_duration_as_string
- time_as_string_common
- crm_time_as_string
- crm_time_parse_sec
- crm_time_parse_offset
- crm_time_parse
- parse_date
- parse_int
- crm_time_parse_duration
- crm_time_parse_period
- crm_time_free_period
- crm_time_set
- ha_set_tm_time
- crm_time_set_timet
- pcmk__set_time_if_earlier
- pcmk_copy_time
- pcmk__copy_timet
- crm_time_add
- pcmk__time_component_attr
- component_fn
- pcmk__add_time_from_xml
- crm_time_calculate_duration
- crm_time_subtract
- crm_time_check
- crm_time_compare
- crm_time_add_seconds
- crm_time_add_days
- crm_time_add_months
- crm_time_add_minutes
- crm_time_add_hours
- crm_time_add_weeks
- crm_time_add_years
- ha_get_tm_time
- pcmk__time_hr_convert
- pcmk__time_set_hr_dt
- pcmk__time_hr_now
- pcmk__time_hr_new
- pcmk__time_hr_free
- pcmk__time_format_hr
- pcmk__epoch2str
- pcmk__timespec2str
- pcmk__readable_interval
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 #include <crm_internal.h>
17 #include <crm/crm.h>
18 #include <time.h>
19 #include <ctype.h>
20 #include <inttypes.h>
21 #include <limits.h>
22 #include <string.h>
23 #include <stdbool.h>
24 #include <crm/common/iso8601.h>
25 #include <crm/common/iso8601_internal.h>
26 #include "crmcommon_private.h"
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
44 # define GMTOFF(tm) ((tm)->tm_gmtoff)
45 #else
46
47 # define GMTOFF(tm) (-timezone+daylight)
48 #endif
49
50 #define HOUR_SECONDS (60 * 60)
51 #define DAY_SECONDS (HOUR_SECONDS * 24)
52
53
54
55
56
57
58
59
60
61
62
63
64
65 #define valid_sec_usec(sec, usec) \
66 ((QB_ABS(usec) < QB_TIME_US_IN_SEC) \
67 && (((sec) == 0) || ((usec) == 0) || (((sec) < 0) == ((usec) < 0))))
68
69
70 struct crm_time_s {
71 int years;
72 int months;
73 int days;
74 int seconds;
75 int offset;
76 bool duration;
77 };
78
79 static crm_time_t *parse_date(const char *date_str);
80
81 static crm_time_t *
82 crm_get_utc_time(const crm_time_t *dt)
83 {
84 crm_time_t *utc = NULL;
85
86 if (dt == NULL) {
87 errno = EINVAL;
88 return NULL;
89 }
90
91 utc = crm_time_new_undefined();
92 utc->years = dt->years;
93 utc->days = dt->days;
94 utc->seconds = dt->seconds;
95 utc->offset = 0;
96
97 if (dt->offset) {
98 crm_time_add_seconds(utc, -dt->offset);
99 } else {
100
101 utc->months = dt->months;
102 }
103
104 crm_time_log(LOG_TRACE, "utc-source", dt,
105 crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
106 crm_time_log(LOG_TRACE, "utc-target", utc,
107 crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
108 return utc;
109 }
110
111 crm_time_t *
112 crm_time_new(const char *date_time)
113 {
114 tzset();
115 if (date_time == NULL) {
116 return pcmk__copy_timet(time(NULL));
117 }
118 return parse_date(date_time);
119 }
120
121
122
123
124
125
126
127
128 crm_time_t *
129 crm_time_new_undefined(void)
130 {
131 return (crm_time_t *) pcmk__assert_alloc(1, sizeof(crm_time_t));
132 }
133
134
135
136
137
138
139
140
141 bool
142 crm_time_is_defined(const crm_time_t *t)
143 {
144
145 return (t != NULL) && (t->years || t->months || t->days || t->seconds
146 || t->offset || t->duration);
147 }
148
149 void
150 crm_time_free(crm_time_t * dt)
151 {
152 if (dt == NULL) {
153 return;
154 }
155 free(dt);
156 }
157
158 static int
159 year_days(int year)
160 {
161 int d = 365;
162
163 if (crm_time_leapyear(year)) {
164 d++;
165 }
166 return d;
167 }
168
169
170
171
172
173
174
175
176
177 int
178 crm_time_january1_weekday(int year)
179 {
180 int YY = (year - 1) % 100;
181 int C = (year - 1) - YY;
182 int G = YY + YY / 4;
183 int jan1 = 1 + (((((C / 100) % 4) * 5) + G) % 7);
184
185 crm_trace("YY=%d, C=%d, G=%d", YY, C, G);
186 crm_trace("January 1 %.4d: %d", year, jan1);
187 return jan1;
188 }
189
190 int
191 crm_time_weeks_in_year(int year)
192 {
193 int weeks = 52;
194 int jan1 = crm_time_january1_weekday(year);
195
196
197 if (jan1 == 4) {
198 weeks++;
199 } else {
200 jan1 = crm_time_january1_weekday(year + 1);
201
202 if (jan1 == 5) {
203 weeks++;
204 }
205
206 }
207 return weeks;
208 }
209
210
211 static int month_days[13] = {
212 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 29
213 };
214
215
216
217
218
219
220
221
222
223 int
224 crm_time_days_in_month(int month, int year)
225 {
226 if ((month < 1) || (month > 12) || (year < 1)) {
227 return 0;
228 }
229 if ((month == 2) && crm_time_leapyear(year)) {
230 month = 13;
231 }
232 return month_days[month - 1];
233 }
234
235 bool
236 crm_time_leapyear(int year)
237 {
238 gboolean is_leap = FALSE;
239
240 if (year % 4 == 0) {
241 is_leap = TRUE;
242 }
243 if (year % 100 == 0 && year % 400 != 0) {
244 is_leap = FALSE;
245 }
246 return is_leap;
247 }
248
249
250
251
252
253
254
255
256
257
258
259
260 static int
261 get_ordinal_days(uint32_t y, uint32_t m, uint32_t d)
262 {
263 int result = 0;
264
265 CRM_CHECK((y > 0) && (y <= INT_MAX) && (m >= 1) && (m <= 12)
266 && (d >= 1) && (d <= 31), return 0);
267
268 result = d;
269 for (int lpc = 1; lpc < m; lpc++) {
270 result += crm_time_days_in_month(lpc, y);
271 }
272 return result;
273 }
274
275 void
276 crm_time_log_alias(int log_level, const char *file, const char *function,
277 int line, const char *prefix, const crm_time_t *date_time,
278 int flags)
279 {
280 char *date_s = crm_time_as_string(date_time, flags);
281
282 if (log_level == LOG_STDOUT) {
283 printf("%s%s%s\n",
284 (prefix? prefix : ""), (prefix? ": " : ""), date_s);
285 } else {
286 do_crm_log_alias(log_level, file, function, line, "%s%s%s",
287 (prefix? prefix : ""), (prefix? ": " : ""), date_s);
288 }
289 free(date_s);
290 }
291
292 static void
293 crm_time_get_sec(int sec, uint32_t *h, uint32_t *m, uint32_t *s)
294 {
295 uint32_t hours, minutes, seconds;
296
297 seconds = QB_ABS(sec);
298
299 hours = seconds / HOUR_SECONDS;
300 seconds -= HOUR_SECONDS * hours;
301
302 minutes = seconds / 60;
303 seconds -= 60 * minutes;
304
305 crm_trace("%d == %.2" PRIu32 ":%.2" PRIu32 ":%.2" PRIu32,
306 sec, hours, minutes, seconds);
307
308 *h = hours;
309 *m = minutes;
310 *s = seconds;
311 }
312
313 int
314 crm_time_get_timeofday(const crm_time_t *dt, uint32_t *h, uint32_t *m,
315 uint32_t *s)
316 {
317 crm_time_get_sec(dt->seconds, h, m, s);
318 return TRUE;
319 }
320
321 int
322 crm_time_get_timezone(const crm_time_t *dt, uint32_t *h, uint32_t *m)
323 {
324 uint32_t s;
325
326 crm_time_get_sec(dt->seconds, h, m, &s);
327 return TRUE;
328 }
329
330 long long
331 crm_time_get_seconds(const crm_time_t *dt)
332 {
333 int lpc;
334 crm_time_t *utc = NULL;
335 long long in_seconds = 0;
336
337 if (dt == NULL) {
338 return 0;
339 }
340
341 utc = crm_get_utc_time(dt);
342 if (utc == NULL) {
343 return 0;
344 }
345
346 for (lpc = 1; lpc < utc->years; lpc++) {
347 long long dmax = year_days(lpc);
348
349 in_seconds += DAY_SECONDS * dmax;
350 }
351
352
353
354
355
356
357
358
359 if (utc->months > 0) {
360 in_seconds += DAY_SECONDS * 30 * (long long) (utc->months);
361 }
362
363 if (utc->days > 0) {
364 in_seconds += DAY_SECONDS * (long long) (utc->days - 1);
365 }
366 in_seconds += utc->seconds;
367
368 crm_time_free(utc);
369 return in_seconds;
370 }
371
372 #define EPOCH_SECONDS 62135596800ULL
373 long long
374 crm_time_get_seconds_since_epoch(const crm_time_t *dt)
375 {
376 return (dt == NULL)? 0 : (crm_time_get_seconds(dt) - EPOCH_SECONDS);
377 }
378
379 int
380 crm_time_get_gregorian(const crm_time_t *dt, uint32_t *y, uint32_t *m,
381 uint32_t *d)
382 {
383 int months = 0;
384 int days = dt->days;
385
386 if(dt->years != 0) {
387 for (months = 1; months <= 12 && days > 0; months++) {
388 int mdays = crm_time_days_in_month(months, dt->years);
389
390 if (mdays >= days) {
391 break;
392 } else {
393 days -= mdays;
394 }
395 }
396
397 } else if (dt->months) {
398
399 months = dt->months;
400
401 } else {
402
403 }
404
405 *y = dt->years;
406 *m = months;
407 *d = days;
408 crm_trace("%.4d-%.3d -> %.4d-%.2d-%.2d", dt->years, dt->days, dt->years, months, days);
409 return TRUE;
410 }
411
412 int
413 crm_time_get_ordinal(const crm_time_t *dt, uint32_t *y, uint32_t *d)
414 {
415 *y = dt->years;
416 *d = dt->days;
417 return TRUE;
418 }
419
420 int
421 crm_time_get_isoweek(const crm_time_t *dt, uint32_t *y, uint32_t *w,
422 uint32_t *d)
423 {
424
425
426
427
428 int year_num = 0;
429 int jan1 = crm_time_january1_weekday(dt->years);
430 int h = -1;
431
432 CRM_CHECK(dt->days > 0, return FALSE);
433
434
435 h = dt->days + jan1 - 1;
436 *d = 1 + ((h - 1) % 7);
437
438
439 if (dt->days <= (8 - jan1) && jan1 > 4) {
440 crm_trace("year--, jan1=%d", jan1);
441 year_num = dt->years - 1;
442 *w = crm_time_weeks_in_year(year_num);
443
444 } else {
445 year_num = dt->years;
446 }
447
448
449 if (year_num == dt->years) {
450 int dmax = year_days(year_num);
451 int correction = 4 - *d;
452
453 if ((dmax - dt->days) < correction) {
454 crm_trace("year++, jan1=%d, i=%d vs. %d", jan1, dmax - dt->days, correction);
455 year_num = dt->years + 1;
456 *w = 1;
457 }
458 }
459
460
461 if (year_num == dt->years) {
462 int j = dt->days + (7 - *d) + (jan1 - 1);
463
464 *w = j / 7;
465 if (jan1 > 4) {
466 *w -= 1;
467 }
468 }
469
470 *y = year_num;
471 crm_trace("Converted %.4d-%.3d to %.4" PRIu32 "-W%.2" PRIu32 "-%" PRIu32,
472 dt->years, dt->days, *y, *w, *d);
473 return TRUE;
474 }
475
476 #define DATE_MAX 128
477
478
479
480
481
482
483
484
485
486
487
488 static inline void
489 sec_usec_as_string(long long sec, int usec, char *buf, size_t *offset)
490 {
491 *offset += snprintf(buf + *offset, DATE_MAX - *offset, "%s%lld.%06d",
492 ((sec == 0) && (usec < 0))? "-" : "",
493 sec, QB_ABS(usec));
494 }
495
496
497
498
499
500
501
502
503
504
505 static void
506 crm_duration_as_string(const crm_time_t *dt, int usec, bool show_usec,
507 char *result)
508 {
509 size_t offset = 0;
510
511 pcmk__assert(valid_sec_usec(dt->seconds, usec));
512
513 if (dt->years) {
514 offset += snprintf(result + offset, DATE_MAX - offset, "%4d year%s ",
515 dt->years, pcmk__plural_s(dt->years));
516 }
517 if (dt->months) {
518 offset += snprintf(result + offset, DATE_MAX - offset, "%2d month%s ",
519 dt->months, pcmk__plural_s(dt->months));
520 }
521 if (dt->days) {
522 offset += snprintf(result + offset, DATE_MAX - offset, "%2d day%s ",
523 dt->days, pcmk__plural_s(dt->days));
524 }
525
526
527 if ((offset == 0) || (dt->seconds != 0) || (show_usec && (usec != 0))) {
528 if (show_usec) {
529 sec_usec_as_string(dt->seconds, usec, result, &offset);
530 } else {
531 offset += snprintf(result + offset, DATE_MAX - offset, "%d",
532 dt->seconds);
533 }
534 offset += snprintf(result + offset, DATE_MAX - offset, " second%s",
535 pcmk__plural_s(dt->seconds));
536 }
537
538
539 if (QB_ABS(dt->seconds) >= 60) {
540 uint32_t h = 0;
541 uint32_t m = 0;
542 uint32_t s = 0;
543 uint32_t u = QB_ABS(usec);
544 bool print_sec_component = false;
545
546 crm_time_get_sec(dt->seconds, &h, &m, &s);
547 print_sec_component = ((s != 0) || (show_usec && (u != 0)));
548
549 offset += snprintf(result + offset, DATE_MAX - offset, " (");
550
551 if (h) {
552 offset += snprintf(result + offset, DATE_MAX - offset,
553 "%" PRIu32 " hour%s%s", h, pcmk__plural_s(h),
554 ((m != 0) || print_sec_component)? " " : "");
555 }
556
557 if (m) {
558 offset += snprintf(result + offset, DATE_MAX - offset,
559 "%" PRIu32 " minute%s%s", m, pcmk__plural_s(m),
560 print_sec_component? " " : "");
561 }
562
563 if (print_sec_component) {
564 if (show_usec) {
565 sec_usec_as_string(s, u, result, &offset);
566 } else {
567 offset += snprintf(result + offset, DATE_MAX - offset,
568 "%" PRIu32, s);
569 }
570 offset += snprintf(result + offset, DATE_MAX - offset, " second%s",
571 pcmk__plural_s(dt->seconds));
572 }
573
574 offset += snprintf(result + offset, DATE_MAX - offset, ")");
575 }
576 }
577
578
579
580
581
582
583
584
585
586
587
588
589 static void
590 time_as_string_common(const crm_time_t *dt, int usec, uint32_t flags,
591 char *result)
592 {
593 crm_time_t *utc = NULL;
594 size_t offset = 0;
595
596 if (!crm_time_is_defined(dt)) {
597 strcpy(result, "<undefined time>");
598 return;
599 }
600
601 pcmk__assert(valid_sec_usec(dt->seconds, usec));
602
603
604
605
606
607 if (pcmk_is_set(flags, crm_time_log_duration)) {
608 crm_duration_as_string(dt, usec, pcmk_is_set(flags, crm_time_usecs),
609 result);
610 return;
611 }
612
613 if (pcmk_any_flags_set(flags, crm_time_seconds|crm_time_epoch)) {
614 long long seconds = 0;
615
616 if (pcmk_is_set(flags, crm_time_seconds)) {
617 seconds = crm_time_get_seconds(dt);
618 } else {
619 seconds = crm_time_get_seconds_since_epoch(dt);
620 }
621
622 if (pcmk_is_set(flags, crm_time_usecs)) {
623 sec_usec_as_string(seconds, usec, result, &offset);
624 } else {
625 snprintf(result, DATE_MAX, "%lld", seconds);
626 }
627 return;
628 }
629
630
631 if ((dt->offset != 0) && !pcmk_is_set(flags, crm_time_log_with_timezone)) {
632 crm_trace("UTC conversion");
633 utc = crm_get_utc_time(dt);
634 dt = utc;
635 }
636
637
638
639 if (pcmk_is_set(flags, crm_time_log_date)) {
640 if (pcmk_is_set(flags, crm_time_weeks)) {
641 uint32_t y = 0;
642 uint32_t w = 0;
643 uint32_t d = 0;
644
645 if (crm_time_get_isoweek(dt, &y, &w, &d)) {
646 offset += snprintf(result + offset, DATE_MAX - offset,
647 "%" PRIu32 "-W%.2" PRIu32 "-%" PRIu32,
648 y, w, d);
649 }
650
651 } else if (pcmk_is_set(flags, crm_time_ordinal)) {
652 uint32_t y = 0;
653 uint32_t d = 0;
654
655 if (crm_time_get_ordinal(dt, &y, &d)) {
656 offset += snprintf(result + offset, DATE_MAX - offset,
657 "%" PRIu32 "-%.3" PRIu32, y, d);
658 }
659
660 } else {
661 uint32_t y = 0;
662 uint32_t m = 0;
663 uint32_t d = 0;
664
665 if (crm_time_get_gregorian(dt, &y, &m, &d)) {
666 offset += snprintf(result + offset, DATE_MAX - offset,
667 "%.4" PRIu32 "-%.2" PRIu32 "-%.2" PRIu32,
668 y, m, d);
669 }
670 }
671 }
672
673 if (pcmk_is_set(flags, crm_time_log_timeofday)) {
674 uint32_t h = 0, m = 0, s = 0;
675
676 if (offset > 0) {
677 offset += snprintf(result + offset, DATE_MAX - offset, " ");
678 }
679
680 if (crm_time_get_timeofday(dt, &h, &m, &s)) {
681 offset += snprintf(result + offset, DATE_MAX - offset,
682 "%.2" PRIu32 ":%.2" PRIu32 ":%.2" PRIu32,
683 h, m, s);
684
685 if (pcmk_is_set(flags, crm_time_usecs)) {
686 offset += snprintf(result + offset, DATE_MAX - offset,
687 ".%06" PRIu32, QB_ABS(usec));
688 }
689 }
690
691 if (pcmk_is_set(flags, crm_time_log_with_timezone)
692 && (dt->offset != 0)) {
693 crm_time_get_sec(dt->offset, &h, &m, &s);
694 offset += snprintf(result + offset, DATE_MAX - offset,
695 " %c%.2" PRIu32 ":%.2" PRIu32,
696 ((dt->offset < 0)? '-' : '+'), h, m);
697 } else {
698 offset += snprintf(result + offset, DATE_MAX - offset, "Z");
699 }
700 }
701
702 crm_time_free(utc);
703 }
704
705
706
707
708
709
710
711
712
713 char *
714 crm_time_as_string(const crm_time_t *dt, int flags)
715 {
716 char result[DATE_MAX] = { '\0', };
717
718 time_as_string_common(dt, 0, flags, result);
719 return pcmk__str_copy(result);
720 }
721
722
723
724
725
726
727
728
729
730
731
732
733 static bool
734 crm_time_parse_sec(const char *time_str, int *result)
735 {
736 int rc;
737 uint32_t hour = 0;
738 uint32_t minute = 0;
739 uint32_t second = 0;
740
741 *result = 0;
742
743
744 rc = sscanf(time_str, "%" SCNu32 ":%" SCNu32 ":%" SCNu32,
745 &hour, &minute, &second);
746 if (rc == 1) {
747 rc = sscanf(time_str, "%2" SCNu32 "%2" SCNu32 "%2" SCNu32,
748 &hour, &minute, &second);
749 }
750 if (rc == 0) {
751 crm_err("%s is not a valid ISO 8601 time specification", time_str);
752 errno = EINVAL;
753 return FALSE;
754 }
755
756 crm_trace("Got valid time: %.2" PRIu32 ":%.2" PRIu32 ":%.2" PRIu32,
757 hour, minute, second);
758
759 if ((hour == 24) && (minute == 0) && (second == 0)) {
760
761 } else if (hour >= 24) {
762 crm_err("%s is not a valid ISO 8601 time specification "
763 "because %" PRIu32 " is not a valid hour", time_str, hour);
764 errno = EINVAL;
765 return FALSE;
766 }
767 if (minute >= 60) {
768 crm_err("%s is not a valid ISO 8601 time specification "
769 "because %" PRIu32 " is not a valid minute", time_str, minute);
770 errno = EINVAL;
771 return FALSE;
772 }
773 if (second >= 60) {
774 crm_err("%s is not a valid ISO 8601 time specification "
775 "because %" PRIu32 " is not a valid second", time_str, second);
776 errno = EINVAL;
777 return FALSE;
778 }
779
780 *result = (hour * HOUR_SECONDS) + (minute * 60) + second;
781 return TRUE;
782 }
783
784 static bool
785 crm_time_parse_offset(const char *offset_str, int *offset)
786 {
787 tzset();
788
789 if (offset_str == NULL) {
790
791 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
792 time_t now = time(NULL);
793 struct tm *now_tm = localtime(&now);
794 #endif
795 int h_offset = GMTOFF(now_tm) / HOUR_SECONDS;
796 int m_offset = (GMTOFF(now_tm) - (HOUR_SECONDS * h_offset)) / 60;
797
798 if (h_offset < 0 && m_offset < 0) {
799 m_offset = 0 - m_offset;
800 }
801 *offset = (HOUR_SECONDS * h_offset) + (60 * m_offset);
802 return TRUE;
803 }
804
805 if (offset_str[0] == 'Z') {
806 *offset = 0;
807 return TRUE;
808 }
809
810 *offset = 0;
811 if ((offset_str[0] == '+') || (offset_str[0] == '-')
812 || isdigit((int)offset_str[0])) {
813
814 gboolean negate = FALSE;
815
816 if (offset_str[0] == '+') {
817 offset_str++;
818 } else if (offset_str[0] == '-') {
819 negate = TRUE;
820 offset_str++;
821 }
822 if (crm_time_parse_sec(offset_str, offset) == FALSE) {
823 return FALSE;
824 }
825 if (negate) {
826 *offset = 0 - *offset;
827 }
828 }
829 return TRUE;
830 }
831
832
833
834
835
836
837
838
839
840
841
842 static bool
843 crm_time_parse(const char *time_str, crm_time_t *a_time)
844 {
845 uint32_t h, m, s;
846 char *offset_s = NULL;
847
848 tzset();
849
850 if (time_str) {
851 if (crm_time_parse_sec(time_str, &(a_time->seconds)) == FALSE) {
852 return FALSE;
853 }
854 offset_s = strstr(time_str, "Z");
855 if (offset_s == NULL) {
856 offset_s = strstr(time_str, " ");
857 if (offset_s) {
858 while (isspace(offset_s[0])) {
859 offset_s++;
860 }
861 }
862 }
863 }
864
865 if (crm_time_parse_offset(offset_s, &(a_time->offset)) == FALSE) {
866 return FALSE;
867 }
868 crm_time_get_sec(a_time->offset, &h, &m, &s);
869 crm_trace("Got tz: %c%2." PRIu32 ":%.2" PRIu32,
870 (a_time->offset < 0)? '-' : '+', h, m);
871
872 if (a_time->seconds == DAY_SECONDS) {
873
874 a_time->seconds = 0;
875 crm_time_add_days(a_time, 1);
876 }
877 return TRUE;
878 }
879
880
881
882
883
884
885
886
887
888
889 static crm_time_t *
890 parse_date(const char *date_str)
891 {
892 const char *time_s = NULL;
893 crm_time_t *dt = NULL;
894
895 uint32_t year = 0U;
896 uint32_t month = 0U;
897 uint32_t day = 0U;
898 uint32_t week = 0U;
899
900 int rc = 0;
901
902 if (pcmk__str_empty(date_str)) {
903 crm_err("No ISO 8601 date/time specification given");
904 goto invalid;
905 }
906
907 if ((date_str[0] == 'T')
908 || ((strlen(date_str) > 2) && (date_str[2] == ':'))) {
909
910 dt = crm_time_new(NULL);
911 if (date_str[0] == 'T') {
912 time_s = date_str + 1;
913 } else {
914 time_s = date_str;
915 }
916 goto parse_time;
917 }
918
919 dt = crm_time_new_undefined();
920
921 if ((strncasecmp(PCMK__VALUE_EPOCH, date_str, 5) == 0)
922 && ((date_str[5] == '\0')
923 || (date_str[5] == '/')
924 || isspace(date_str[5]))) {
925 dt->days = 1;
926 dt->years = 1970;
927 crm_time_log(LOG_TRACE, "Unpacked", dt, crm_time_log_date | crm_time_log_timeofday);
928 return dt;
929 }
930
931
932 rc = sscanf(date_str, "%" SCNu32 "-%" SCNu32 "-%" SCNu32 "",
933 &year, &month, &day);
934 if (rc == 1) {
935
936 rc = sscanf(date_str, "%4" SCNu32 "%2" SCNu32 "%2" SCNu32 "",
937 &year, &month, &day);
938 }
939 if (rc == 3) {
940 if ((month < 1U) || (month > 12U)) {
941 crm_err("'%s' is not a valid ISO 8601 date/time specification "
942 "because '%" PRIu32 "' is not a valid month",
943 date_str, month);
944 goto invalid;
945 } else if ((year < 1U) || (year > INT_MAX)) {
946 crm_err("'%s' is not a valid ISO 8601 date/time specification "
947 "because '%" PRIu32 "' is not a valid year",
948 date_str, year);
949 goto invalid;
950 } else if ((day < 1) || (day > INT_MAX)
951 || (day > crm_time_days_in_month(month, year))) {
952 crm_err("'%s' is not a valid ISO 8601 date/time specification "
953 "because '%" PRIu32 "' is not a valid day of the month",
954 date_str, day);
955 goto invalid;
956 } else {
957 dt->years = year;
958 dt->days = get_ordinal_days(year, month, day);
959 crm_trace("Parsed Gregorian date '%.4" PRIu32 "-%.3d' "
960 "from date string '%s'", year, dt->days, date_str);
961 }
962 goto parse_time;
963 }
964
965
966 rc = sscanf(date_str, "%" SCNu32 "-%" SCNu32, &year, &day);
967 if (rc == 2) {
968 if ((year < 1U) || (year > INT_MAX)) {
969 crm_err("'%s' is not a valid ISO 8601 date/time specification "
970 "because '%" PRIu32 "' is not a valid year",
971 date_str, year);
972 goto invalid;
973 } else if ((day < 1U) || (day > INT_MAX) || (day > year_days(year))) {
974 crm_err("'%s' is not a valid ISO 8601 date/time specification "
975 "because '%" PRIu32 "' is not a valid day of year %"
976 PRIu32 " (1-%d)",
977 date_str, day, year, year_days(year));
978 goto invalid;
979 }
980 crm_trace("Parsed ordinal year %d and days %d from date string '%s'",
981 year, day, date_str);
982 dt->days = day;
983 dt->years = year;
984 goto parse_time;
985 }
986
987
988 rc = sscanf(date_str, "%" SCNu32 "-W%" SCNu32 "-%" SCNu32,
989 &year, &week, &day);
990 if (rc == 3) {
991 if ((week < 1U) || (week > crm_time_weeks_in_year(year))) {
992 crm_err("'%s' is not a valid ISO 8601 date/time specification "
993 "because '%" PRIu32 "' is not a valid week of year %"
994 PRIu32 " (1-%d)",
995 date_str, week, year, crm_time_weeks_in_year(year));
996 goto invalid;
997 } else if ((day < 1U) || (day > 7U)) {
998 crm_err("'%s' is not a valid ISO 8601 date/time specification "
999 "because '%" PRIu32 "' is not a valid day of the week",
1000 date_str, day);
1001 goto invalid;
1002 } else {
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014 int jan1 = crm_time_january1_weekday(year);
1015
1016 crm_trace("Parsed year %" PRIu32 " (Jan 1 = %d), week %" PRIu32
1017 ", and day %" PRIu32 " from date string '%s'",
1018 year, jan1, week, day, date_str);
1019
1020 dt->years = year;
1021 crm_time_add_days(dt, (week - 1) * 7);
1022
1023 if (jan1 <= 4) {
1024 crm_time_add_days(dt, 1 - jan1);
1025 } else {
1026 crm_time_add_days(dt, 8 - jan1);
1027 }
1028
1029 crm_time_add_days(dt, day);
1030 }
1031 goto parse_time;
1032 }
1033
1034 crm_err("'%s' is not a valid ISO 8601 date/time specification", date_str);
1035 goto invalid;
1036
1037 parse_time:
1038
1039 if (time_s == NULL) {
1040 time_s = date_str + strspn(date_str, "0123456789-W");
1041 if ((time_s[0] == ' ') || (time_s[0] == 'T')) {
1042 ++time_s;
1043 } else {
1044 time_s = NULL;
1045 }
1046 }
1047 if ((time_s != NULL) && (crm_time_parse(time_s, dt) == FALSE)) {
1048 goto invalid;
1049 }
1050
1051 crm_time_log(LOG_TRACE, "Unpacked", dt, crm_time_log_date | crm_time_log_timeofday);
1052 if (crm_time_check(dt) == FALSE) {
1053 crm_err("'%s' is not a valid ISO 8601 date/time specification",
1054 date_str);
1055 goto invalid;
1056 }
1057 return dt;
1058
1059 invalid:
1060 crm_time_free(dt);
1061 errno = EINVAL;
1062 return NULL;
1063 }
1064
1065
1066 static int
1067 parse_int(const char *str, int *result)
1068 {
1069 unsigned int lpc;
1070 int offset = (str[0] == 'T')? 1 : 0;
1071 bool negate = false;
1072
1073 *result = 0;
1074
1075
1076 switch (str[offset]) {
1077 case '.':
1078 case ',':
1079 return 0;
1080
1081 case '-':
1082 negate = true;
1083 offset++;
1084 break;
1085
1086 case '+':
1087 case ':':
1088 offset++;
1089 break;
1090
1091 default:
1092 break;
1093 }
1094
1095 for (lpc = 0; (lpc < 10) && isdigit(str[offset]); lpc++) {
1096 const int digit = str[offset++] - '0';
1097
1098 if ((*result * 10LL + digit) > INT_MAX) {
1099 return 0;
1100 }
1101 *result = *result * 10 + digit;
1102 }
1103 if (negate) {
1104 *result = 0 - *result;
1105 }
1106 return (lpc > 0)? offset : 0;
1107 }
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120 crm_time_t *
1121 crm_time_parse_duration(const char *period_s)
1122 {
1123 gboolean is_time = FALSE;
1124 crm_time_t *diff = NULL;
1125
1126 if (pcmk__str_empty(period_s)) {
1127 crm_err("No ISO 8601 time duration given");
1128 goto invalid;
1129 }
1130 if (period_s[0] != 'P') {
1131 crm_err("'%s' is not a valid ISO 8601 time duration "
1132 "because it does not start with a 'P'", period_s);
1133 goto invalid;
1134 }
1135 if ((period_s[1] == '\0') || isspace(period_s[1])) {
1136 crm_err("'%s' is not a valid ISO 8601 time duration "
1137 "because nothing follows 'P'", period_s);
1138 goto invalid;
1139 }
1140
1141 diff = crm_time_new_undefined();
1142
1143 for (const char *current = period_s + 1;
1144 current[0] && (current[0] != '/') && !isspace(current[0]);
1145 ++current) {
1146
1147 int an_int = 0, rc;
1148 long long result = 0LL;
1149
1150 if (current[0] == 'T') {
1151
1152
1153
1154
1155 is_time = TRUE;
1156 continue;
1157 }
1158
1159
1160 rc = parse_int(current, &an_int);
1161 if (rc == 0) {
1162 crm_err("'%s' is not a valid ISO 8601 time duration "
1163 "because no valid integer at '%s'", period_s, current);
1164 goto invalid;
1165 }
1166 current += rc;
1167
1168
1169 switch (current[0]) {
1170 case 'Y':
1171 diff->years = an_int;
1172 break;
1173
1174 case 'M':
1175 if (!is_time) {
1176 diff->months = an_int;
1177 } else {
1178 result = diff->seconds + an_int * 60LL;
1179 if ((result < INT_MIN) || (result > INT_MAX)) {
1180 crm_err("'%s' is not a valid ISO 8601 time duration "
1181 "because integer at '%s' is too %s",
1182 period_s, current - rc,
1183 ((result > 0)? "large" : "small"));
1184 goto invalid;
1185 } else {
1186 diff->seconds = (int) result;
1187 }
1188 }
1189
1190 break;
1191
1192 case 'W':
1193 result = diff->days + an_int * 7LL;
1194 if ((result < INT_MIN) || (result > INT_MAX)) {
1195 crm_err("'%s' is not a valid ISO 8601 time duration "
1196 "because integer at '%s' is too %s",
1197 period_s, current - rc,
1198 ((result > 0)? "large" : "small"));
1199 goto invalid;
1200 } else {
1201 diff->days = (int) result;
1202 }
1203 break;
1204
1205 case 'D':
1206 result = diff->days + (long long) an_int;
1207 if ((result < INT_MIN) || (result > INT_MAX)) {
1208 crm_err("'%s' is not a valid ISO 8601 time duration "
1209 "because integer at '%s' is too %s",
1210 period_s, current - rc,
1211 ((result > 0)? "large" : "small"));
1212 goto invalid;
1213 } else {
1214 diff->days = (int) result;
1215 }
1216 break;
1217
1218 case 'H':
1219 result = diff->seconds + (long long) an_int * HOUR_SECONDS;
1220 if ((result < INT_MIN) || (result > INT_MAX)) {
1221 crm_err("'%s' is not a valid ISO 8601 time duration "
1222 "because integer at '%s' is too %s",
1223 period_s, current - rc,
1224 ((result > 0)? "large" : "small"));
1225 goto invalid;
1226 } else {
1227 diff->seconds = (int) result;
1228 }
1229 break;
1230
1231 case 'S':
1232 result = diff->seconds + (long long) an_int;
1233 if ((result < INT_MIN) || (result > INT_MAX)) {
1234 crm_err("'%s' is not a valid ISO 8601 time duration "
1235 "because integer at '%s' is too %s",
1236 period_s, current - rc,
1237 ((result > 0)? "large" : "small"));
1238 goto invalid;
1239 } else {
1240 diff->seconds = (int) result;
1241 }
1242 break;
1243
1244 case '\0':
1245 crm_err("'%s' is not a valid ISO 8601 time duration "
1246 "because no units after %d", period_s, an_int);
1247 goto invalid;
1248
1249 default:
1250 crm_err("'%s' is not a valid ISO 8601 time duration "
1251 "because '%c' is not a valid time unit",
1252 period_s, current[0]);
1253 goto invalid;
1254 }
1255 }
1256
1257 if (!crm_time_is_defined(diff)) {
1258 crm_err("'%s' is not a valid ISO 8601 time duration "
1259 "because no amounts and units given", period_s);
1260 goto invalid;
1261 }
1262
1263 diff->duration = TRUE;
1264 return diff;
1265
1266 invalid:
1267 crm_time_free(diff);
1268 errno = EINVAL;
1269 return NULL;
1270 }
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282 crm_time_period_t *
1283 crm_time_parse_period(const char *period_str)
1284 {
1285 const char *original = period_str;
1286 crm_time_period_t *period = NULL;
1287
1288 if (pcmk__str_empty(period_str)) {
1289 crm_err("No ISO 8601 time period given");
1290 goto invalid;
1291 }
1292
1293 tzset();
1294 period = pcmk__assert_alloc(1, sizeof(crm_time_period_t));
1295
1296 if (period_str[0] == 'P') {
1297 period->diff = crm_time_parse_duration(period_str);
1298 if (period->diff == NULL) {
1299 goto error;
1300 }
1301 } else {
1302 period->start = parse_date(period_str);
1303 if (period->start == NULL) {
1304 goto error;
1305 }
1306 }
1307
1308 period_str = strstr(original, "/");
1309 if (period_str) {
1310 ++period_str;
1311 if (period_str[0] == 'P') {
1312 if (period->diff != NULL) {
1313 crm_err("'%s' is not a valid ISO 8601 time period "
1314 "because it has two durations",
1315 original);
1316 goto invalid;
1317 }
1318 period->diff = crm_time_parse_duration(period_str);
1319 if (period->diff == NULL) {
1320 goto error;
1321 }
1322 } else {
1323 period->end = parse_date(period_str);
1324 if (period->end == NULL) {
1325 goto error;
1326 }
1327 }
1328
1329 } else if (period->diff != NULL) {
1330
1331 period->start = crm_time_new(NULL);
1332
1333 } else {
1334
1335 crm_err("'%s' is not a valid ISO 8601 time period "
1336 "because it has no duration or ending time",
1337 original);
1338 goto invalid;
1339 }
1340
1341 if (period->start == NULL) {
1342 period->start = crm_time_subtract(period->end, period->diff);
1343
1344 } else if (period->end == NULL) {
1345 period->end = crm_time_add(period->start, period->diff);
1346 }
1347
1348 if (crm_time_check(period->start) == FALSE) {
1349 crm_err("'%s' is not a valid ISO 8601 time period "
1350 "because the start is invalid", period_str);
1351 goto invalid;
1352 }
1353 if (crm_time_check(period->end) == FALSE) {
1354 crm_err("'%s' is not a valid ISO 8601 time period "
1355 "because the end is invalid", period_str);
1356 goto invalid;
1357 }
1358 return period;
1359
1360 invalid:
1361 errno = EINVAL;
1362 error:
1363 crm_time_free_period(period);
1364 return NULL;
1365 }
1366
1367
1368
1369
1370
1371
1372 void
1373 crm_time_free_period(crm_time_period_t *period)
1374 {
1375 if (period) {
1376 crm_time_free(period->start);
1377 crm_time_free(period->end);
1378 crm_time_free(period->diff);
1379 free(period);
1380 }
1381 }
1382
1383 void
1384 crm_time_set(crm_time_t *target, const crm_time_t *source)
1385 {
1386 crm_trace("target=%p, source=%p", target, source);
1387
1388 CRM_CHECK(target != NULL && source != NULL, return);
1389
1390 target->years = source->years;
1391 target->days = source->days;
1392 target->months = source->months;
1393 target->seconds = source->seconds;
1394 target->offset = source->offset;
1395
1396 crm_time_log(LOG_TRACE, "source", source,
1397 crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
1398 crm_time_log(LOG_TRACE, "target", target,
1399 crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
1400 }
1401
1402 static void
1403 ha_set_tm_time(crm_time_t *target, const struct tm *source)
1404 {
1405 int h_offset = 0;
1406 int m_offset = 0;
1407
1408
1409 target->years = 0;
1410 target->months = 0;
1411 target->days = 0;
1412 target->seconds = 0;
1413 target->offset = 0;
1414 target->duration = FALSE;
1415
1416 if (source->tm_year > 0) {
1417
1418 target->years = 1900;
1419 crm_time_add_years(target, source->tm_year);
1420 }
1421
1422 if (source->tm_yday >= 0) {
1423
1424 target->days = 1 + source->tm_yday;
1425 }
1426
1427 if (source->tm_hour >= 0) {
1428 target->seconds += HOUR_SECONDS * source->tm_hour;
1429 }
1430 if (source->tm_min >= 0) {
1431 target->seconds += 60 * source->tm_min;
1432 }
1433 if (source->tm_sec >= 0) {
1434 target->seconds += source->tm_sec;
1435 }
1436
1437
1438 h_offset = GMTOFF(source) / HOUR_SECONDS;
1439 m_offset = (GMTOFF(source) - (HOUR_SECONDS * h_offset)) / 60;
1440 crm_trace("Time offset is %lds (%.2d:%.2d)",
1441 GMTOFF(source), h_offset, m_offset);
1442
1443 target->offset += HOUR_SECONDS * h_offset;
1444 target->offset += 60 * m_offset;
1445 }
1446
1447 void
1448 crm_time_set_timet(crm_time_t *target, const time_t *source)
1449 {
1450 ha_set_tm_time(target, localtime(source));
1451 }
1452
1453
1454
1455
1456
1457
1458
1459
1460 void
1461 pcmk__set_time_if_earlier(crm_time_t *target, const crm_time_t *source)
1462 {
1463 if ((target != NULL) && (source != NULL)
1464 && (!crm_time_is_defined(target)
1465 || (crm_time_compare(source, target) < 0))) {
1466 crm_time_set(target, source);
1467 }
1468 }
1469
1470 crm_time_t *
1471 pcmk_copy_time(const crm_time_t *source)
1472 {
1473 crm_time_t *target = crm_time_new_undefined();
1474
1475 crm_time_set(target, source);
1476 return target;
1477 }
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487 crm_time_t *
1488 pcmk__copy_timet(time_t source)
1489 {
1490 crm_time_t *target = crm_time_new_undefined();
1491
1492 crm_time_set_timet(target, &source);
1493 return target;
1494 }
1495
1496 crm_time_t *
1497 crm_time_add(const crm_time_t *dt, const crm_time_t *value)
1498 {
1499 crm_time_t *utc = NULL;
1500 crm_time_t *answer = NULL;
1501
1502 if ((dt == NULL) || (value == NULL)) {
1503 errno = EINVAL;
1504 return NULL;
1505 }
1506
1507 answer = pcmk_copy_time(dt);
1508
1509 utc = crm_get_utc_time(value);
1510 if (utc == NULL) {
1511 crm_time_free(answer);
1512 return NULL;
1513 }
1514
1515 crm_time_add_years(answer, utc->years);
1516 crm_time_add_months(answer, utc->months);
1517 crm_time_add_days(answer, utc->days);
1518 crm_time_add_seconds(answer, utc->seconds);
1519
1520 crm_time_free(utc);
1521 return answer;
1522 }
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533 const char *
1534 pcmk__time_component_attr(enum pcmk__time_component component)
1535 {
1536 switch (component) {
1537 case pcmk__time_years:
1538 return PCMK_XA_YEARS;
1539
1540 case pcmk__time_months:
1541 return PCMK_XA_MONTHS;
1542
1543 case pcmk__time_weeks:
1544 return PCMK_XA_WEEKS;
1545
1546 case pcmk__time_days:
1547 return PCMK_XA_DAYS;
1548
1549 case pcmk__time_hours:
1550 return PCMK_XA_HOURS;
1551
1552 case pcmk__time_minutes:
1553 return PCMK_XA_MINUTES;
1554
1555 case pcmk__time_seconds:
1556 return PCMK_XA_SECONDS;
1557
1558 default:
1559 return NULL;
1560 }
1561 }
1562
1563 typedef void (*component_fn_t)(crm_time_t *, int);
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573 static component_fn_t
1574 component_fn(enum pcmk__time_component component)
1575 {
1576 switch (component) {
1577 case pcmk__time_years:
1578 return crm_time_add_years;
1579
1580 case pcmk__time_months:
1581 return crm_time_add_months;
1582
1583 case pcmk__time_weeks:
1584 return crm_time_add_weeks;
1585
1586 case pcmk__time_days:
1587 return crm_time_add_days;
1588
1589 case pcmk__time_hours:
1590 return crm_time_add_hours;
1591
1592 case pcmk__time_minutes:
1593 return crm_time_add_minutes;
1594
1595 case pcmk__time_seconds:
1596 return crm_time_add_seconds;
1597
1598 default:
1599 return NULL;
1600 }
1601
1602 }
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614 int
1615 pcmk__add_time_from_xml(crm_time_t *t, enum pcmk__time_component component,
1616 const xmlNode *xml)
1617 {
1618 long long value;
1619 const char *attr = pcmk__time_component_attr(component);
1620 component_fn_t add = component_fn(component);
1621
1622 if ((t == NULL) || (attr == NULL) || (add == NULL)) {
1623 return EINVAL;
1624 }
1625
1626 if (xml == NULL) {
1627 return pcmk_rc_ok;
1628 }
1629
1630 if (pcmk__scan_ll(crm_element_value(xml, attr), &value,
1631 0LL) != pcmk_rc_ok) {
1632 return pcmk_rc_unpack_error;
1633 }
1634
1635 if ((value < INT_MIN) || (value > INT_MAX)) {
1636 return ERANGE;
1637 }
1638
1639 if (value != 0LL) {
1640 add(t, (int) value);
1641 }
1642 return pcmk_rc_ok;
1643 }
1644
1645 crm_time_t *
1646 crm_time_calculate_duration(const crm_time_t *dt, const crm_time_t *value)
1647 {
1648 crm_time_t *utc = NULL;
1649 crm_time_t *answer = NULL;
1650
1651 if ((dt == NULL) || (value == NULL)) {
1652 errno = EINVAL;
1653 return NULL;
1654 }
1655
1656 utc = crm_get_utc_time(value);
1657 if (utc == NULL) {
1658 return NULL;
1659 }
1660
1661 answer = crm_get_utc_time(dt);
1662 if (answer == NULL) {
1663 crm_time_free(utc);
1664 return NULL;
1665 }
1666 answer->duration = TRUE;
1667
1668 crm_time_add_years(answer, -utc->years);
1669 if(utc->months != 0) {
1670 crm_time_add_months(answer, -utc->months);
1671 }
1672 crm_time_add_days(answer, -utc->days);
1673 crm_time_add_seconds(answer, -utc->seconds);
1674
1675 crm_time_free(utc);
1676 return answer;
1677 }
1678
1679 crm_time_t *
1680 crm_time_subtract(const crm_time_t *dt, const crm_time_t *value)
1681 {
1682 crm_time_t *utc = NULL;
1683 crm_time_t *answer = NULL;
1684
1685 if ((dt == NULL) || (value == NULL)) {
1686 errno = EINVAL;
1687 return NULL;
1688 }
1689
1690 utc = crm_get_utc_time(value);
1691 if (utc == NULL) {
1692 return NULL;
1693 }
1694
1695 answer = pcmk_copy_time(dt);
1696 crm_time_add_years(answer, -utc->years);
1697 if(utc->months != 0) {
1698 crm_time_add_months(answer, -utc->months);
1699 }
1700 crm_time_add_days(answer, -utc->days);
1701 crm_time_add_seconds(answer, -utc->seconds);
1702 crm_time_free(utc);
1703
1704 return answer;
1705 }
1706
1707
1708
1709
1710
1711
1712
1713
1714 bool
1715 crm_time_check(const crm_time_t *dt)
1716 {
1717 return (dt != NULL)
1718 && (dt->days > 0) && (dt->days <= year_days(dt->years))
1719 && (dt->seconds >= 0) && (dt->seconds < DAY_SECONDS);
1720 }
1721
1722 #define do_cmp_field(l, r, field) \
1723 if(rc == 0) { \
1724 if(l->field > r->field) { \
1725 crm_trace("%s: %d > %d", \
1726 #field, l->field, r->field); \
1727 rc = 1; \
1728 } else if(l->field < r->field) { \
1729 crm_trace("%s: %d < %d", \
1730 #field, l->field, r->field); \
1731 rc = -1; \
1732 } \
1733 }
1734
1735 int
1736 crm_time_compare(const crm_time_t *a, const crm_time_t *b)
1737 {
1738 int rc = 0;
1739 crm_time_t *t1 = crm_get_utc_time(a);
1740 crm_time_t *t2 = crm_get_utc_time(b);
1741
1742 if ((t1 == NULL) && (t2 == NULL)) {
1743 rc = 0;
1744 } else if (t1 == NULL) {
1745 rc = -1;
1746 } else if (t2 == NULL) {
1747 rc = 1;
1748 } else {
1749 do_cmp_field(t1, t2, years);
1750 do_cmp_field(t1, t2, days);
1751 do_cmp_field(t1, t2, seconds);
1752 }
1753
1754 crm_time_free(t1);
1755 crm_time_free(t2);
1756 return rc;
1757 }
1758
1759
1760
1761
1762
1763
1764
1765 void
1766 crm_time_add_seconds(crm_time_t *a_time, int extra)
1767 {
1768 int days = extra / DAY_SECONDS;
1769
1770 pcmk__assert(a_time != NULL);
1771
1772 crm_trace("Adding %d seconds (including %d whole day%s) to %d",
1773 extra, days, pcmk__plural_s(days), a_time->seconds);
1774
1775 a_time->seconds += extra % DAY_SECONDS;
1776
1777
1778 if (a_time->seconds > DAY_SECONDS) {
1779 ++days;
1780 a_time->seconds -= DAY_SECONDS;
1781
1782 } else if (a_time->seconds < 0) {
1783 --days;
1784 a_time->seconds += DAY_SECONDS;
1785 }
1786
1787 crm_time_add_days(a_time, days);
1788 }
1789
1790 #define ydays(t) (crm_time_leapyear((t)->years)? 366 : 365)
1791
1792
1793
1794
1795
1796
1797
1798 void
1799 crm_time_add_days(crm_time_t *a_time, int extra)
1800 {
1801 pcmk__assert(a_time != NULL);
1802
1803 crm_trace("Adding %d days to %.4d-%.3d", extra, a_time->years, a_time->days);
1804
1805 if (extra > 0) {
1806 while ((a_time->days + (long long) extra) > ydays(a_time)) {
1807 if ((a_time->years + 1LL) > INT_MAX) {
1808 a_time->days = ydays(a_time);
1809 return;
1810 }
1811 extra -= ydays(a_time);
1812 a_time->years++;
1813 }
1814 } else if (extra < 0) {
1815 const int min_days = a_time->duration? 0 : 1;
1816
1817 while ((a_time->days + (long long) extra) < min_days) {
1818 if ((a_time->years - 1) < 1) {
1819 a_time->days = 1;
1820 return;
1821 }
1822 a_time->years--;
1823 extra += ydays(a_time);
1824 }
1825 }
1826 a_time->days += extra;
1827 }
1828
1829 void
1830 crm_time_add_months(crm_time_t * a_time, int extra)
1831 {
1832 int lpc;
1833 uint32_t y, m, d, dmax;
1834
1835 crm_time_get_gregorian(a_time, &y, &m, &d);
1836 crm_trace("Adding %d months to %.4" PRIu32 "-%.2" PRIu32 "-%.2" PRIu32,
1837 extra, y, m, d);
1838
1839 if (extra > 0) {
1840 for (lpc = extra; lpc > 0; lpc--) {
1841 m++;
1842 if (m == 13) {
1843 m = 1;
1844 y++;
1845 }
1846 }
1847 } else {
1848 for (lpc = -extra; lpc > 0; lpc--) {
1849 m--;
1850 if (m == 0) {
1851 m = 12;
1852 y--;
1853 }
1854 }
1855 }
1856
1857 dmax = crm_time_days_in_month(m, y);
1858 if (dmax < d) {
1859
1860 d = dmax;
1861 }
1862
1863 crm_trace("Calculated %.4" PRIu32 "-%.2" PRIu32 "-%.2" PRIu32, y, m, d);
1864
1865 a_time->years = y;
1866 a_time->days = get_ordinal_days(y, m, d);
1867
1868 crm_time_get_gregorian(a_time, &y, &m, &d);
1869 crm_trace("Got %.4" PRIu32 "-%.2" PRIu32 "-%.2" PRIu32, y, m, d);
1870 }
1871
1872 void
1873 crm_time_add_minutes(crm_time_t * a_time, int extra)
1874 {
1875 crm_time_add_seconds(a_time, extra * 60);
1876 }
1877
1878 void
1879 crm_time_add_hours(crm_time_t * a_time, int extra)
1880 {
1881 crm_time_add_seconds(a_time, extra * HOUR_SECONDS);
1882 }
1883
1884 void
1885 crm_time_add_weeks(crm_time_t * a_time, int extra)
1886 {
1887 crm_time_add_days(a_time, extra * 7);
1888 }
1889
1890 void
1891 crm_time_add_years(crm_time_t * a_time, int extra)
1892 {
1893 pcmk__assert(a_time != NULL);
1894
1895 if ((extra > 0) && ((a_time->years + (long long) extra) > INT_MAX)) {
1896 a_time->years = INT_MAX;
1897 } else if ((extra < 0) && ((a_time->years + (long long) extra) < 1)) {
1898 a_time->years = 1;
1899 } else {
1900 a_time->years += extra;
1901 }
1902 }
1903
1904 static void
1905 ha_get_tm_time(struct tm *target, const crm_time_t *source)
1906 {
1907 *target = (struct tm) {
1908 .tm_year = source->years - 1900,
1909 .tm_mday = source->days,
1910 .tm_sec = source->seconds % 60,
1911 .tm_min = ( source->seconds / 60 ) % 60,
1912 .tm_hour = source->seconds / HOUR_SECONDS,
1913 .tm_isdst = -1,
1914
1915 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
1916 .tm_gmtoff = source->offset
1917 #endif
1918 };
1919 mktime(target);
1920 }
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930 pcmk__time_hr_t *
1931 pcmk__time_hr_convert(pcmk__time_hr_t *target, const crm_time_t *dt)
1932 {
1933 pcmk__time_hr_t *hr_dt = NULL;
1934
1935 if (dt) {
1936 hr_dt = target;
1937 if (hr_dt == NULL) {
1938 hr_dt = pcmk__assert_alloc(1, sizeof(pcmk__time_hr_t));
1939 }
1940
1941 *hr_dt = (pcmk__time_hr_t) {
1942 .years = dt->years,
1943 .months = dt->months,
1944 .days = dt->days,
1945 .seconds = dt->seconds,
1946 .offset = dt->offset,
1947 .duration = dt->duration
1948 };
1949 }
1950
1951 return hr_dt;
1952 }
1953
1954 void
1955 pcmk__time_set_hr_dt(crm_time_t *target, const pcmk__time_hr_t *hr_dt)
1956 {
1957 pcmk__assert((target != NULL) && (hr_dt != NULL));
1958 *target = (crm_time_t) {
1959 .years = hr_dt->years,
1960 .months = hr_dt->months,
1961 .days = hr_dt->days,
1962 .seconds = hr_dt->seconds,
1963 .offset = hr_dt->offset,
1964 .duration = hr_dt->duration
1965 };
1966 }
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976 pcmk__time_hr_t *
1977 pcmk__time_hr_now(time_t *epoch)
1978 {
1979 struct timespec tv;
1980 crm_time_t dt;
1981 pcmk__time_hr_t *hr;
1982
1983 qb_util_timespec_from_epoch_get(&tv);
1984 if (epoch != NULL) {
1985 *epoch = tv.tv_sec;
1986 }
1987 crm_time_set_timet(&dt, &(tv.tv_sec));
1988 hr = pcmk__time_hr_convert(NULL, &dt);
1989 if (hr != NULL) {
1990 hr->useconds = tv.tv_nsec / QB_TIME_NS_IN_USEC;
1991 }
1992 return hr;
1993 }
1994
1995 pcmk__time_hr_t *
1996 pcmk__time_hr_new(const char *date_time)
1997 {
1998 pcmk__time_hr_t *hr_dt = NULL;
1999
2000 if (date_time == NULL) {
2001 hr_dt = pcmk__time_hr_now(NULL);
2002 } else {
2003 crm_time_t *dt;
2004
2005 dt = parse_date(date_time);
2006 hr_dt = pcmk__time_hr_convert(NULL, dt);
2007 crm_time_free(dt);
2008 }
2009 return hr_dt;
2010 }
2011
2012 void
2013 pcmk__time_hr_free(pcmk__time_hr_t * hr_dt)
2014 {
2015 free(hr_dt);
2016 }
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028 char *
2029 pcmk__time_format_hr(const char *format, const pcmk__time_hr_t *hr_dt)
2030 {
2031 int scanned_pos = 0;
2032 int printed_pos = 0;
2033 size_t date_len = 0;
2034
2035 char nano_s[10] = { '\0', };
2036 char date_s[128] = { '\0', };
2037
2038 struct tm tm = { 0, };
2039 crm_time_t dt = { 0, };
2040
2041 if (format == NULL) {
2042 return NULL;
2043 }
2044 pcmk__time_set_hr_dt(&dt, hr_dt);
2045 ha_get_tm_time(&tm, &dt);
2046 sprintf(nano_s, "%06d000", hr_dt->useconds);
2047
2048 while (format[scanned_pos] != '\0') {
2049 int fmt_pos;
2050 int nano_digits = 0;
2051 char *tmp_fmt_s = NULL;
2052 size_t nbytes = 0;
2053
2054
2055 const char *mark_s = strchr(&format[scanned_pos], '%');
2056
2057 if (mark_s == NULL) {
2058
2059 scanned_pos = strlen(format);
2060 fmt_pos = scanned_pos;
2061
2062 } else {
2063 fmt_pos = mark_s - format;
2064
2065
2066 scanned_pos = fmt_pos + 1;
2067 while (isdigit(format[scanned_pos])) {
2068 scanned_pos++;
2069 }
2070
2071 switch (format[scanned_pos]) {
2072 case '\0':
2073 fmt_pos = scanned_pos;
2074 break;
2075
2076 case 'N':
2077 scanned_pos++;
2078
2079
2080 nano_digits = atoi(&format[fmt_pos + 1]);
2081 nano_digits = QB_MAX(nano_digits, 0);
2082 nano_digits = QB_MIN(nano_digits, 6);
2083 break;
2084
2085 default:
2086 if (format[++scanned_pos] != '\0') {
2087 continue;
2088 }
2089 fmt_pos = scanned_pos;
2090 break;
2091 }
2092 }
2093
2094 if (date_len >= sizeof(date_s)) {
2095 return NULL;
2096 }
2097
2098 tmp_fmt_s = strndup(&format[printed_pos], fmt_pos - printed_pos);
2099 #ifdef HAVE_FORMAT_NONLITERAL
2100 #pragma GCC diagnostic push
2101 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
2102 #endif
2103 nbytes = strftime(&date_s[date_len], sizeof(date_s) - date_len,
2104 tmp_fmt_s, &tm);
2105 #ifdef HAVE_FORMAT_NONLITERAL
2106 #pragma GCC diagnostic pop
2107 #endif
2108 free(tmp_fmt_s);
2109 if (nbytes == 0) {
2110 return NULL;
2111 }
2112 date_len += nbytes;
2113 printed_pos = scanned_pos;
2114 if (nano_digits != 0) {
2115 int nc = 0;
2116
2117 if (date_len >= sizeof(date_s)) {
2118 return NULL;
2119 }
2120 nc = snprintf(&date_s[date_len], sizeof(date_s) - date_len,
2121 "%.*s", nano_digits, nano_s);
2122
2123 if ((nc < 0) || (nc == (sizeof(date_s) - date_len))) {
2124 return NULL;
2125 }
2126 date_len += nc;
2127 }
2128 }
2129
2130 return (date_len == 0)? NULL : pcmk__str_copy(date_s);
2131 }
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146 char *
2147 pcmk__epoch2str(const time_t *source, uint32_t flags)
2148 {
2149 time_t epoch_time = (source == NULL)? time(NULL) : *source;
2150
2151 if (flags == 0) {
2152 return pcmk__str_copy(pcmk__trim(ctime(&epoch_time)));
2153 } else {
2154 crm_time_t dt;
2155
2156 crm_time_set_timet(&dt, &epoch_time);
2157 return crm_time_as_string(&dt, flags);
2158 }
2159 }
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178 char *
2179 pcmk__timespec2str(const struct timespec *ts, uint32_t flags)
2180 {
2181 struct timespec tmp_ts;
2182 crm_time_t dt;
2183 char result[DATE_MAX] = { 0 };
2184
2185 if (ts == NULL) {
2186 qb_util_timespec_from_epoch_get(&tmp_ts);
2187 ts = &tmp_ts;
2188 }
2189 crm_time_set_timet(&dt, &ts->tv_sec);
2190 time_as_string_common(&dt, ts->tv_nsec / QB_TIME_NS_IN_USEC, flags, result);
2191 return pcmk__str_copy(result);
2192 }
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205 const char *
2206 pcmk__readable_interval(guint interval_ms)
2207 {
2208 #define MS_IN_S (1000)
2209 #define MS_IN_M (MS_IN_S * 60)
2210 #define MS_IN_H (MS_IN_M * 60)
2211 #define MS_IN_D (MS_IN_H * 24)
2212 #define MAXSTR sizeof("..d..h..m..s...ms")
2213 static char str[MAXSTR];
2214 int offset = 0;
2215
2216 str[0] = '\0';
2217 if (interval_ms >= MS_IN_D) {
2218 offset += snprintf(str + offset, MAXSTR - offset, "%ud",
2219 interval_ms / MS_IN_D);
2220 interval_ms -= (interval_ms / MS_IN_D) * MS_IN_D;
2221 }
2222 if (interval_ms >= MS_IN_H) {
2223 offset += snprintf(str + offset, MAXSTR - offset, "%uh",
2224 interval_ms / MS_IN_H);
2225 interval_ms -= (interval_ms / MS_IN_H) * MS_IN_H;
2226 }
2227 if (interval_ms >= MS_IN_M) {
2228 offset += snprintf(str + offset, MAXSTR - offset, "%um",
2229 interval_ms / MS_IN_M);
2230 interval_ms -= (interval_ms / MS_IN_M) * MS_IN_M;
2231 }
2232
2233
2234 if (interval_ms >= MS_IN_S) {
2235 offset += snprintf(str + offset, MAXSTR - offset, "%u",
2236 interval_ms / MS_IN_S);
2237 interval_ms -= (interval_ms / MS_IN_S) * MS_IN_S;
2238 if (interval_ms > 0) {
2239 offset += snprintf(str + offset, MAXSTR - offset, ".%03u",
2240 interval_ms);
2241 }
2242 (void) snprintf(str + offset, MAXSTR - offset, "s");
2243
2244 } else if (interval_ms > 0) {
2245 (void) snprintf(str + offset, MAXSTR - offset, "%ums", interval_ms);
2246
2247 } else if (str[0] == '\0') {
2248 strcpy(str, "0s");
2249 }
2250 return str;
2251 }