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