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
- crm_duration_as_string
- 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_copy_time
- crm_time_add
- 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_timeval_hr_convert
- pcmk__time_hr_new
- pcmk__time_hr_free
- pcmk__time_format_hr
- pcmk__epoch2str
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 <string.h>
21 #include <stdbool.h>
22 #include <crm/common/iso8601.h>
23 #include <crm/common/iso8601_internal.h>
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
41 # define GMTOFF(tm) ((tm)->tm_gmtoff)
42 #else
43
44 # define GMTOFF(tm) (-timezone+daylight)
45 #endif
46
47 #define HOUR_SECONDS (60 * 60)
48 #define DAY_SECONDS (HOUR_SECONDS * 24)
49
50
51 struct crm_time_s {
52 int years;
53 int months;
54 int days;
55 int seconds;
56 int offset;
57 bool duration;
58 };
59
60 static crm_time_t *parse_date(const char *date_str);
61
62 static crm_time_t *
63 crm_get_utc_time(crm_time_t *dt)
64 {
65 crm_time_t *utc = NULL;
66
67 if (dt == NULL) {
68 errno = EINVAL;
69 return NULL;
70 }
71
72 utc = crm_time_new_undefined();
73 utc->years = dt->years;
74 utc->days = dt->days;
75 utc->seconds = dt->seconds;
76 utc->offset = 0;
77
78 if (dt->offset) {
79 crm_time_add_seconds(utc, -dt->offset);
80 } else {
81
82 utc->months = dt->months;
83 }
84
85 crm_time_log(LOG_TRACE, "utc-source", dt,
86 crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
87 crm_time_log(LOG_TRACE, "utc-target", utc,
88 crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
89 return utc;
90 }
91
92 crm_time_t *
93 crm_time_new(const char *date_time)
94 {
95 time_t tm_now;
96 crm_time_t *dt = NULL;
97
98 tzset();
99 if (date_time == NULL) {
100 tm_now = time(NULL);
101 dt = crm_time_new_undefined();
102 crm_time_set_timet(dt, &tm_now);
103 } else {
104 dt = parse_date(date_time);
105 }
106 return dt;
107 }
108
109
110
111
112
113
114
115
116 crm_time_t *
117 crm_time_new_undefined()
118 {
119 crm_time_t *result = calloc(1, sizeof(crm_time_t));
120
121 CRM_ASSERT(result != NULL);
122 return result;
123 }
124
125
126
127
128
129
130
131
132 bool
133 crm_time_is_defined(const crm_time_t *t)
134 {
135
136 return (t != NULL) && (t->years || t->months || t->days || t->seconds
137 || t->offset || t->duration);
138 }
139
140 void
141 crm_time_free(crm_time_t * dt)
142 {
143 if (dt == NULL) {
144 return;
145 }
146 free(dt);
147 }
148
149 static int
150 year_days(int year)
151 {
152 int d = 365;
153
154 if (crm_time_leapyear(year)) {
155 d++;
156 }
157 return d;
158 }
159
160
161
162
163
164
165
166
167
168 int
169 crm_time_january1_weekday(int year)
170 {
171 int YY = (year - 1) % 100;
172 int C = (year - 1) - YY;
173 int G = YY + YY / 4;
174 int jan1 = 1 + (((((C / 100) % 4) * 5) + G) % 7);
175
176 crm_trace("YY=%d, C=%d, G=%d", YY, C, G);
177 crm_trace("January 1 %.4d: %d", year, jan1);
178 return jan1;
179 }
180
181 int
182 crm_time_weeks_in_year(int year)
183 {
184 int weeks = 52;
185 int jan1 = crm_time_january1_weekday(year);
186
187
188 if (jan1 == 4) {
189 weeks++;
190 } else {
191 jan1 = crm_time_january1_weekday(year + 1);
192
193 if (jan1 == 5) {
194 weeks++;
195 }
196
197 }
198 return weeks;
199 }
200
201
202 static int month_days[13] = {
203 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 29
204 };
205
206
207
208
209
210
211
212
213
214 int
215 crm_time_days_in_month(int month, int year)
216 {
217 if ((month < 1) || (month > 12)) {
218 return 0;
219 }
220 if ((month == 2) && crm_time_leapyear(year)) {
221 month = 13;
222 }
223 return month_days[month - 1];
224 }
225
226 bool
227 crm_time_leapyear(int year)
228 {
229 gboolean is_leap = FALSE;
230
231 if (year % 4 == 0) {
232 is_leap = TRUE;
233 }
234 if (year % 100 == 0 && year % 400 != 0) {
235 is_leap = FALSE;
236 }
237 return is_leap;
238 }
239
240 static uint32_t
241 get_ordinal_days(uint32_t y, uint32_t m, uint32_t d)
242 {
243 int lpc;
244
245 for (lpc = 1; lpc < m; lpc++) {
246 d += crm_time_days_in_month(lpc, y);
247 }
248 return d;
249 }
250
251 void
252 crm_time_log_alias(int log_level, const char *file, const char *function, int line,
253 const char *prefix, crm_time_t * date_time, int flags)
254 {
255 char *date_s = crm_time_as_string(date_time, flags);
256
257 if (log_level == LOG_STDOUT) {
258 printf("%s%s%s\n",
259 (prefix? prefix : ""), (prefix? ": " : ""), date_s);
260 } else {
261 do_crm_log_alias(log_level, file, function, line, "%s%s%s",
262 (prefix? prefix : ""), (prefix? ": " : ""), date_s);
263 }
264 free(date_s);
265 }
266
267 static void
268 crm_time_get_sec(int sec, uint * h, uint * m, uint * s)
269 {
270 uint hours, minutes, seconds;
271
272 if (sec < 0) {
273 seconds = 0 - sec;
274 } else {
275 seconds = sec;
276 }
277
278 hours = seconds / HOUR_SECONDS;
279 seconds -= HOUR_SECONDS * hours;
280
281 minutes = seconds / 60;
282 seconds -= 60 * minutes;
283
284 crm_trace("%d == %.2d:%.2d:%.2d", sec, hours, minutes, seconds);
285
286 *h = hours;
287 *m = minutes;
288 *s = seconds;
289 }
290
291 int
292 crm_time_get_timeofday(crm_time_t * dt, uint * h, uint * m, uint * s)
293 {
294 crm_time_get_sec(dt->seconds, h, m, s);
295 return TRUE;
296 }
297
298 int
299 crm_time_get_timezone(crm_time_t * dt, uint * h, uint * m)
300 {
301 uint s;
302
303 crm_time_get_sec(dt->seconds, h, m, &s);
304 return TRUE;
305 }
306
307 long long
308 crm_time_get_seconds(crm_time_t * dt)
309 {
310 int lpc;
311 crm_time_t *utc = NULL;
312 long long in_seconds = 0;
313
314 if (dt == NULL) {
315 return 0;
316 }
317
318 utc = crm_get_utc_time(dt);
319 if (utc == NULL) {
320 return 0;
321 }
322
323 for (lpc = 1; lpc < utc->years; lpc++) {
324 long long dmax = year_days(lpc);
325
326 in_seconds += DAY_SECONDS * dmax;
327 }
328
329
330
331
332
333
334
335
336 if (utc->months > 0) {
337 in_seconds += DAY_SECONDS * 30 * (long long) (utc->months);
338 }
339
340 if (utc->days > 0) {
341 in_seconds += DAY_SECONDS * (long long) (utc->days - 1);
342 }
343 in_seconds += utc->seconds;
344
345 crm_time_free(utc);
346 return in_seconds;
347 }
348
349 #define EPOCH_SECONDS 62135596800ULL
350 long long
351 crm_time_get_seconds_since_epoch(crm_time_t * dt)
352 {
353 return (dt == NULL)? 0 : (crm_time_get_seconds(dt) - EPOCH_SECONDS);
354 }
355
356 int
357 crm_time_get_gregorian(crm_time_t * dt, uint * y, uint * m, uint * d)
358 {
359 int months = 0;
360 int days = dt->days;
361
362 if(dt->years != 0) {
363 for (months = 1; months <= 12 && days > 0; months++) {
364 int mdays = crm_time_days_in_month(months, dt->years);
365
366 if (mdays >= days) {
367 break;
368 } else {
369 days -= mdays;
370 }
371 }
372
373 } else if (dt->months) {
374
375 months = dt->months;
376
377 } else {
378
379 }
380
381 *y = dt->years;
382 *m = months;
383 *d = days;
384 crm_trace("%.4d-%.3d -> %.4d-%.2d-%.2d", dt->years, dt->days, dt->years, months, days);
385 return TRUE;
386 }
387
388 int
389 crm_time_get_ordinal(crm_time_t * dt, uint * y, uint * d)
390 {
391 *y = dt->years;
392 *d = dt->days;
393 return TRUE;
394 }
395
396 int
397 crm_time_get_isoweek(crm_time_t * dt, uint * y, uint * w, uint * d)
398 {
399
400
401
402
403 int year_num = 0;
404 int jan1 = crm_time_january1_weekday(dt->years);
405 int h = -1;
406
407 CRM_CHECK(dt->days > 0, return FALSE);
408
409
410 h = dt->days + jan1 - 1;
411 *d = 1 + ((h - 1) % 7);
412
413
414 if (dt->days <= (8 - jan1) && jan1 > 4) {
415 crm_trace("year--, jan1=%d", jan1);
416 year_num = dt->years - 1;
417 *w = crm_time_weeks_in_year(year_num);
418
419 } else {
420 year_num = dt->years;
421 }
422
423
424 if (year_num == dt->years) {
425 int dmax = year_days(year_num);
426 int correction = 4 - *d;
427
428 if ((dmax - dt->days) < correction) {
429 crm_trace("year++, jan1=%d, i=%d vs. %d", jan1, dmax - dt->days, correction);
430 year_num = dt->years + 1;
431 *w = 1;
432 }
433 }
434
435
436 if (year_num == dt->years) {
437 int j = dt->days + (7 - *d) + (jan1 - 1);
438
439 *w = j / 7;
440 if (jan1 > 4) {
441 *w -= 1;
442 }
443 }
444
445 *y = year_num;
446 crm_trace("Converted %.4d-%.3d to %.4d-W%.2d-%d", dt->years, dt->days, *y, *w, *d);
447 return TRUE;
448 }
449
450 #define DATE_MAX 128
451
452 static void
453 crm_duration_as_string(crm_time_t *dt, char *result)
454 {
455 size_t offset = 0;
456
457 if (dt->years) {
458 offset += snprintf(result + offset, DATE_MAX - offset, "%4d year%s ",
459 dt->years, pcmk__plural_s(dt->years));
460 }
461 if (dt->months) {
462 offset += snprintf(result + offset, DATE_MAX - offset, "%2d month%s ",
463 dt->months, pcmk__plural_s(dt->months));
464 }
465 if (dt->days) {
466 offset += snprintf(result + offset, DATE_MAX - offset, "%2d day%s ",
467 dt->days, pcmk__plural_s(dt->days));
468 }
469
470 if (((offset == 0) || (dt->seconds != 0))
471 && (dt->seconds > -60) && (dt->seconds < 60)) {
472 offset += snprintf(result + offset, DATE_MAX - offset, "%d second%s",
473 dt->seconds, pcmk__plural_s(dt->seconds));
474 } else if (dt->seconds) {
475 uint h = 0, m = 0, s = 0;
476
477 offset += snprintf(result + offset, DATE_MAX - offset, "%d seconds (",
478 dt->seconds);
479 crm_time_get_sec(dt->seconds, &h, &m, &s);
480 if (h) {
481 offset += snprintf(result + offset, DATE_MAX - offset, "%u hour%s%s",
482 h, pcmk__plural_s(h), ((m || s)? " " : ""));
483 }
484 if (m) {
485 offset += snprintf(result + offset, DATE_MAX - offset, "%u minute%s%s",
486 m, pcmk__plural_s(m), (s? " " : ""));
487 }
488 if (s) {
489 offset += snprintf(result + offset, DATE_MAX - offset, "%u second%s",
490 s, pcmk__plural_s(s));
491 }
492 offset += snprintf(result + offset, DATE_MAX - offset, ")");
493 }
494 }
495
496 char *
497 crm_time_as_string(crm_time_t * date_time, int flags)
498 {
499 crm_time_t *dt = NULL;
500 crm_time_t *utc = NULL;
501 char result[DATE_MAX] = { '\0', };
502 char *result_copy = NULL;
503 size_t offset = 0;
504
505
506 if (date_time && date_time->offset
507 && !pcmk_is_set(flags, crm_time_log_with_timezone)) {
508 crm_trace("UTC conversion");
509 utc = crm_get_utc_time(date_time);
510 dt = utc;
511 } else {
512 dt = date_time;
513 }
514
515 if (!crm_time_is_defined(dt)) {
516 strcpy(result, "<undefined time>");
517 goto done;
518 }
519
520
521
522 if (flags & crm_time_log_duration) {
523 crm_duration_as_string(date_time, result);
524 goto done;
525 }
526
527 if (flags & crm_time_seconds) {
528 snprintf(result, DATE_MAX, "%lld", crm_time_get_seconds(date_time));
529 goto done;
530 }
531
532 if (flags & crm_time_epoch) {
533 snprintf(result, DATE_MAX, "%lld",
534 crm_time_get_seconds_since_epoch(date_time));
535 goto done;
536 }
537
538
539
540 if (flags & crm_time_log_date) {
541 if (flags & crm_time_weeks) {
542 uint y, w, d;
543
544 if (crm_time_get_isoweek(dt, &y, &w, &d)) {
545 offset += snprintf(result + offset, DATE_MAX - offset,
546 "%u-W%.2u-%u", y, w, d);
547 }
548
549 } else if (flags & crm_time_ordinal) {
550 uint y, d;
551
552 if (crm_time_get_ordinal(dt, &y, &d)) {
553 offset += snprintf(result + offset, DATE_MAX - offset,
554 "%u-%.3u", y, d);
555 }
556
557 } else {
558 uint y, m, d;
559
560 if (crm_time_get_gregorian(dt, &y, &m, &d)) {
561 offset += snprintf(result + offset, DATE_MAX - offset,
562 "%.4u-%.2u-%.2u", y, m, d);
563 }
564 }
565 }
566
567 if (flags & crm_time_log_timeofday) {
568 uint h = 0, m = 0, s = 0;
569
570 if (offset > 0) {
571 offset += snprintf(result + offset, DATE_MAX - offset, " ");
572 }
573
574 if (crm_time_get_timeofday(dt, &h, &m, &s)) {
575 offset += snprintf(result + offset, DATE_MAX - offset,
576 "%.2u:%.2u:%.2u", h, m, s);
577 }
578
579 if ((flags & crm_time_log_with_timezone) && (dt->offset != 0)) {
580 crm_time_get_sec(dt->offset, &h, &m, &s);
581 offset += snprintf(result + offset, DATE_MAX - offset,
582 " %c%.2u:%.2u",
583 ((dt->offset < 0)? '-' : '+'), h, m);
584 } else {
585 offset += snprintf(result + offset, DATE_MAX - offset, "Z");
586 }
587 }
588
589 done:
590 crm_time_free(utc);
591
592 result_copy = strdup(result);
593 CRM_ASSERT(result_copy != NULL);
594 return result_copy;
595 }
596
597
598
599
600
601
602
603
604
605
606
607
608 static bool
609 crm_time_parse_sec(const char *time_str, int *result)
610 {
611 int rc;
612 uint hour = 0;
613 uint minute = 0;
614 uint second = 0;
615
616 *result = 0;
617
618
619 rc = sscanf(time_str, "%d:%d:%d", &hour, &minute, &second);
620 if (rc == 1) {
621 rc = sscanf(time_str, "%2d%2d%2d", &hour, &minute, &second);
622 }
623 if (rc == 0) {
624 crm_err("%s is not a valid ISO 8601 time specification", time_str);
625 errno = EINVAL;
626 return FALSE;
627 }
628
629 crm_trace("Got valid time: %.2d:%.2d:%.2d", hour, minute, second);
630
631 if ((hour == 24) && (minute == 0) && (second == 0)) {
632
633 } else if (hour >= 24) {
634 crm_err("%s is not a valid ISO 8601 time specification "
635 "because %d is not a valid hour", time_str, hour);
636 errno = EINVAL;
637 return FALSE;
638 }
639 if (minute >= 60) {
640 crm_err("%s is not a valid ISO 8601 time specification "
641 "because %d is not a valid minute", time_str, minute);
642 errno = EINVAL;
643 return FALSE;
644 }
645 if (second >= 60) {
646 crm_err("%s is not a valid ISO 8601 time specification "
647 "because %d is not a valid second", time_str, second);
648 errno = EINVAL;
649 return FALSE;
650 }
651
652 *result = (hour * HOUR_SECONDS) + (minute * 60) + second;
653 return TRUE;
654 }
655
656 static bool
657 crm_time_parse_offset(const char *offset_str, int *offset)
658 {
659 tzset();
660
661 if (offset_str == NULL) {
662
663 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
664 time_t now = time(NULL);
665 struct tm *now_tm = localtime(&now);
666 #endif
667 int h_offset = GMTOFF(now_tm) / HOUR_SECONDS;
668 int m_offset = (GMTOFF(now_tm) - (HOUR_SECONDS * h_offset)) / 60;
669
670 if (h_offset < 0 && m_offset < 0) {
671 m_offset = 0 - m_offset;
672 }
673 *offset = (HOUR_SECONDS * h_offset) + (60 * m_offset);
674 return TRUE;
675 }
676
677 if (offset_str[0] == 'Z') {
678 *offset = 0;
679 return TRUE;
680 }
681
682 *offset = 0;
683 if ((offset_str[0] == '+') || (offset_str[0] == '-')
684 || isdigit((int)offset_str[0])) {
685
686 gboolean negate = FALSE;
687
688 if (offset_str[0] == '+') {
689 offset_str++;
690 } else if (offset_str[0] == '-') {
691 negate = TRUE;
692 offset_str++;
693 }
694 if (crm_time_parse_sec(offset_str, offset) == FALSE) {
695 return FALSE;
696 }
697 if (negate) {
698 *offset = 0 - *offset;
699 }
700 }
701 return TRUE;
702 }
703
704
705
706
707
708
709
710
711
712
713
714 static bool
715 crm_time_parse(const char *time_str, crm_time_t *a_time)
716 {
717 uint h, m, s;
718 char *offset_s = NULL;
719
720 tzset();
721
722 if (time_str) {
723 if (crm_time_parse_sec(time_str, &(a_time->seconds)) == FALSE) {
724 return FALSE;
725 }
726 offset_s = strstr(time_str, "Z");
727 if (offset_s == NULL) {
728 offset_s = strstr(time_str, " ");
729 if (offset_s) {
730 while (isspace(offset_s[0])) {
731 offset_s++;
732 }
733 }
734 }
735 }
736
737 if (crm_time_parse_offset(offset_s, &(a_time->offset)) == FALSE) {
738 return FALSE;
739 }
740 crm_time_get_sec(a_time->offset, &h, &m, &s);
741 crm_trace("Got tz: %c%2.d:%.2d", ((a_time->offset < 0)? '-' : '+'), h, m);
742
743 if (a_time->seconds == DAY_SECONDS) {
744
745 a_time->seconds = 0;
746 crm_time_add_days(a_time, 1);
747 }
748 return TRUE;
749 }
750
751
752
753
754
755
756
757
758
759 static crm_time_t *
760 parse_date(const char *date_str)
761 {
762 const char *time_s = NULL;
763 crm_time_t *dt = NULL;
764
765 int year = 0;
766 int month = 0;
767 int week = 0;
768 int day = 0;
769 int rc = 0;
770
771 if (pcmk__str_empty(date_str)) {
772 crm_err("No ISO 8601 date/time specification given");
773 goto invalid;
774 }
775
776 if ((date_str[0] == 'T') || (date_str[2] == ':')) {
777
778 dt = crm_time_new(NULL);
779 if (date_str[0] == 'T') {
780 time_s = date_str + 1;
781 } else {
782 time_s = date_str;
783 }
784 goto parse_time;
785 }
786
787 dt = crm_time_new_undefined();
788
789 if (!strncasecmp("epoch", date_str, 5)
790 && ((date_str[5] == '\0') || (date_str[5] == '/') || isspace(date_str[5]))) {
791 dt->days = 1;
792 dt->years = 1970;
793 crm_time_log(LOG_TRACE, "Unpacked", dt, crm_time_log_date | crm_time_log_timeofday);
794 return dt;
795 }
796
797
798 rc = sscanf(date_str, "%d-%d-%d", &year, &month, &day);
799 if (rc == 1) {
800
801 rc = sscanf(date_str, "%4d%2d%2d", &year, &month, &day);
802 }
803 if (rc == 3) {
804 if (month > 12) {
805 crm_err("'%s' is not a valid ISO 8601 date/time specification "
806 "because '%d' is not a valid month", date_str, month);
807 goto invalid;
808 } else if (day > crm_time_days_in_month(month, year)) {
809 crm_err("'%s' is not a valid ISO 8601 date/time specification "
810 "because '%d' is not a valid day of the month",
811 date_str, day);
812 goto invalid;
813 } else {
814 dt->years = year;
815 dt->days = get_ordinal_days(year, month, day);
816 crm_trace("Parsed Gregorian date '%.4d-%.3d' from date string '%s'",
817 year, dt->days, date_str);
818 }
819 goto parse_time;
820 }
821
822
823 rc = sscanf(date_str, "%d-%d", &year, &day);
824 if (rc == 2) {
825 if (day > year_days(year)) {
826 crm_err("'%s' is not a valid ISO 8601 date/time specification "
827 "because '%d' is not a valid day of the year (max %d)",
828 date_str, day, year_days(year));
829 goto invalid;
830 }
831 crm_trace("Parsed ordinal year %d and days %d from date string '%s'",
832 year, day, date_str);
833 dt->days = day;
834 dt->years = year;
835 goto parse_time;
836 }
837
838
839 rc = sscanf(date_str, "%d-W%d-%d", &year, &week, &day);
840 if (rc == 3) {
841 if (week > crm_time_weeks_in_year(year)) {
842 crm_err("'%s' is not a valid ISO 8601 date/time specification "
843 "because '%d' is not a valid week of the year (max %d)",
844 date_str, week, crm_time_weeks_in_year(year));
845 goto invalid;
846 } else if (day < 1 || day > 7) {
847 crm_err("'%s' is not a valid ISO 8601 date/time specification "
848 "because '%d' is not a valid day of the week",
849 date_str, day);
850 goto invalid;
851 } else {
852
853
854
855
856
857
858
859
860
861
862 int jan1 = crm_time_january1_weekday(year);
863
864 crm_trace("Got year %d (Jan 1 = %d), week %d, and day %d from date string '%s'",
865 year, jan1, week, day, date_str);
866
867 dt->years = year;
868 crm_time_add_days(dt, (week - 1) * 7);
869
870 if (jan1 <= 4) {
871 crm_time_add_days(dt, 1 - jan1);
872 } else {
873 crm_time_add_days(dt, 8 - jan1);
874 }
875
876 crm_time_add_days(dt, day);
877 }
878 goto parse_time;
879 }
880
881 crm_err("'%s' is not a valid ISO 8601 date/time specification", date_str);
882 goto invalid;
883
884 parse_time:
885
886 if (time_s == NULL) {
887 time_s = date_str + strspn(date_str, "0123456789-W");
888 if ((time_s[0] == ' ') || (time_s[0] == 'T')) {
889 ++time_s;
890 } else {
891 time_s = NULL;
892 }
893 }
894 if ((time_s != NULL) && (crm_time_parse(time_s, dt) == FALSE)) {
895 goto invalid;
896 }
897
898 crm_time_log(LOG_TRACE, "Unpacked", dt, crm_time_log_date | crm_time_log_timeofday);
899 if (crm_time_check(dt) == FALSE) {
900 crm_err("'%s' is not a valid ISO 8601 date/time specification",
901 date_str);
902 goto invalid;
903 }
904 return dt;
905
906 invalid:
907 crm_time_free(dt);
908 errno = EINVAL;
909 return NULL;
910 }
911
912
913
914
915
916 static int
917 parse_int(const char *str, int field_width, int upper_bound, int *result)
918 {
919 int lpc = 0;
920 int offset = 0;
921 int intermediate = 0;
922 gboolean fraction = FALSE;
923 gboolean negate = FALSE;
924
925 *result = 0;
926 if (*str == '\0') {
927 return 0;
928 }
929
930 if (str[offset] == 'T') {
931 offset++;
932 }
933
934 if (str[offset] == '.' || str[offset] == ',') {
935 fraction = TRUE;
936 field_width = -1;
937 offset++;
938 } else if (str[offset] == '-') {
939 negate = TRUE;
940 offset++;
941 } else if (str[offset] == '+' || str[offset] == ':') {
942 offset++;
943 }
944
945 for (; (fraction || lpc < field_width) && isdigit((int)str[offset]); lpc++) {
946 if (fraction) {
947 intermediate = (str[offset] - '0') / (10 ^ lpc);
948 } else {
949 *result *= 10;
950 intermediate = str[offset] - '0';
951 }
952 *result += intermediate;
953 offset++;
954 }
955 if (fraction) {
956 *result = (int)(*result * upper_bound);
957
958 } else if (upper_bound > 0 && *result > upper_bound) {
959 *result = upper_bound;
960 }
961 if (negate) {
962 *result = 0 - *result;
963 }
964 if (lpc > 0) {
965 crm_trace("Found int: %d. Stopped at str[%d]='%c'", *result, lpc, str[lpc]);
966 return offset;
967 }
968 return 0;
969 }
970
971
972
973
974
975
976
977
978
979
980
981
982 crm_time_t *
983 crm_time_parse_duration(const char *period_s)
984 {
985 gboolean is_time = FALSE;
986 crm_time_t *diff = NULL;
987
988 if (pcmk__str_empty(period_s)) {
989 crm_err("No ISO 8601 time duration given");
990 goto invalid;
991 }
992 if (period_s[0] != 'P') {
993 crm_err("'%s' is not a valid ISO 8601 time duration "
994 "because it does not start with a 'P'", period_s);
995 goto invalid;
996 }
997 if ((period_s[1] == '\0') || isspace(period_s[1])) {
998 crm_err("'%s' is not a valid ISO 8601 time duration "
999 "because nothing follows 'P'", period_s);
1000 goto invalid;
1001 }
1002
1003 diff = crm_time_new_undefined();
1004 diff->duration = TRUE;
1005
1006 for (const char *current = period_s + 1;
1007 current[0] && (current[0] != '/') && !isspace(current[0]);
1008 ++current) {
1009
1010 int an_int = 0, rc;
1011
1012 if (current[0] == 'T') {
1013
1014
1015
1016
1017 is_time = TRUE;
1018 continue;
1019 }
1020
1021
1022 rc = parse_int(current, 10, 0, &an_int);
1023 if (rc == 0) {
1024 crm_err("'%s' is not a valid ISO 8601 time duration "
1025 "because no integer at '%s'", period_s, current);
1026 goto invalid;
1027 }
1028 current += rc;
1029
1030
1031 switch (current[0]) {
1032 case 'Y':
1033 diff->years = an_int;
1034 break;
1035 case 'M':
1036 if (is_time) {
1037
1038 diff->seconds += an_int * 60;
1039 } else {
1040 diff->months = an_int;
1041 }
1042 break;
1043 case 'W':
1044 diff->days += an_int * 7;
1045 break;
1046 case 'D':
1047 diff->days += an_int;
1048 break;
1049 case 'H':
1050 diff->seconds += an_int * HOUR_SECONDS;
1051 break;
1052 case 'S':
1053 diff->seconds += an_int;
1054 break;
1055 case '\0':
1056 crm_err("'%s' is not a valid ISO 8601 time duration "
1057 "because no units after %d", period_s, an_int);
1058 goto invalid;
1059 default:
1060 crm_err("'%s' is not a valid ISO 8601 time duration "
1061 "because '%c' is not a valid time unit",
1062 period_s, current[0]);
1063 goto invalid;
1064 }
1065 }
1066
1067 if (!crm_time_is_defined(diff)) {
1068 crm_err("'%s' is not a valid ISO 8601 time duration "
1069 "because no amounts and units given", period_s);
1070 goto invalid;
1071 }
1072 return diff;
1073
1074 invalid:
1075 crm_time_free(diff);
1076 errno = EINVAL;
1077 return NULL;
1078 }
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090 crm_time_period_t *
1091 crm_time_parse_period(const char *period_str)
1092 {
1093 const char *original = period_str;
1094 crm_time_period_t *period = NULL;
1095
1096 if (pcmk__str_empty(period_str)) {
1097 crm_err("No ISO 8601 time period given");
1098 goto invalid;
1099 }
1100
1101 tzset();
1102 period = calloc(1, sizeof(crm_time_period_t));
1103 CRM_ASSERT(period != NULL);
1104
1105 if (period_str[0] == 'P') {
1106 period->diff = crm_time_parse_duration(period_str);
1107 if (period->diff == NULL) {
1108 goto error;
1109 }
1110 } else {
1111 period->start = parse_date(period_str);
1112 if (period->start == NULL) {
1113 goto error;
1114 }
1115 }
1116
1117 period_str = strstr(original, "/");
1118 if (period_str) {
1119 ++period_str;
1120 if (period_str[0] == 'P') {
1121 if (period->diff != NULL) {
1122 crm_err("'%s' is not a valid ISO 8601 time period "
1123 "because it has two durations",
1124 original);
1125 goto invalid;
1126 }
1127 period->diff = crm_time_parse_duration(period_str);
1128 if (period->diff == NULL) {
1129 goto error;
1130 }
1131 } else {
1132 period->end = parse_date(period_str);
1133 if (period->end == NULL) {
1134 goto error;
1135 }
1136 }
1137
1138 } else if (period->diff != NULL) {
1139
1140 period->start = crm_time_new(NULL);
1141
1142 } else {
1143
1144 crm_err("'%s' is not a valid ISO 8601 time period "
1145 "because it has no duration or ending time",
1146 original);
1147 goto invalid;
1148 }
1149
1150 if (period->start == NULL) {
1151 period->start = crm_time_subtract(period->end, period->diff);
1152
1153 } else if (period->end == NULL) {
1154 period->end = crm_time_add(period->start, period->diff);
1155 }
1156
1157 if (crm_time_check(period->start) == FALSE) {
1158 crm_err("'%s' is not a valid ISO 8601 time period "
1159 "because the start is invalid", period_str);
1160 goto invalid;
1161 }
1162 if (crm_time_check(period->end) == FALSE) {
1163 crm_err("'%s' is not a valid ISO 8601 time period "
1164 "because the end is invalid", period_str);
1165 goto invalid;
1166 }
1167 return period;
1168
1169 invalid:
1170 errno = EINVAL;
1171 error:
1172 crm_time_free_period(period);
1173 return NULL;
1174 }
1175
1176
1177
1178
1179
1180
1181 void
1182 crm_time_free_period(crm_time_period_t *period)
1183 {
1184 if (period) {
1185 crm_time_free(period->start);
1186 crm_time_free(period->end);
1187 crm_time_free(period->diff);
1188 free(period);
1189 }
1190 }
1191
1192 void
1193 crm_time_set(crm_time_t * target, crm_time_t * source)
1194 {
1195 crm_trace("target=%p, source=%p", target, source);
1196
1197 CRM_CHECK(target != NULL && source != NULL, return);
1198
1199 target->years = source->years;
1200 target->days = source->days;
1201 target->months = source->months;
1202 target->seconds = source->seconds;
1203 target->offset = source->offset;
1204
1205 crm_time_log(LOG_TRACE, "source", source,
1206 crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
1207 crm_time_log(LOG_TRACE, "target", target,
1208 crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
1209 }
1210
1211 static void
1212 ha_set_tm_time(crm_time_t * target, struct tm *source)
1213 {
1214 int h_offset = 0;
1215 int m_offset = 0;
1216
1217
1218 target->years = 0;
1219 target->months = 0;
1220 target->days = 0;
1221 target->seconds = 0;
1222 target->offset = 0;
1223 target->duration = FALSE;
1224
1225 if (source->tm_year > 0) {
1226
1227 target->years = 1900 + source->tm_year;
1228 }
1229
1230 if (source->tm_yday >= 0) {
1231
1232 target->days = 1 + source->tm_yday;
1233 }
1234
1235 if (source->tm_hour >= 0) {
1236 target->seconds += HOUR_SECONDS * source->tm_hour;
1237 }
1238 if (source->tm_min >= 0) {
1239 target->seconds += 60 * source->tm_min;
1240 }
1241 if (source->tm_sec >= 0) {
1242 target->seconds += source->tm_sec;
1243 }
1244
1245
1246 h_offset = GMTOFF(source) / HOUR_SECONDS;
1247 m_offset = (GMTOFF(source) - (HOUR_SECONDS * h_offset)) / 60;
1248 crm_trace("Offset (s): %ld, offset (hh:mm): %.2d:%.2d", GMTOFF(source), h_offset, m_offset);
1249
1250 target->offset += HOUR_SECONDS * h_offset;
1251 target->offset += 60 * m_offset;
1252 }
1253
1254 void
1255 crm_time_set_timet(crm_time_t * target, time_t * source)
1256 {
1257 ha_set_tm_time(target, localtime(source));
1258 }
1259
1260 crm_time_t *
1261 pcmk_copy_time(crm_time_t *source)
1262 {
1263 crm_time_t *target = crm_time_new_undefined();
1264
1265 crm_time_set(target, source);
1266 return target;
1267 }
1268
1269 crm_time_t *
1270 crm_time_add(crm_time_t * dt, crm_time_t * value)
1271 {
1272 crm_time_t *utc = NULL;
1273 crm_time_t *answer = NULL;
1274
1275 if ((dt == NULL) || (value == NULL)) {
1276 errno = EINVAL;
1277 return NULL;
1278 }
1279
1280 answer = pcmk_copy_time(dt);
1281
1282 utc = crm_get_utc_time(value);
1283 if (utc == NULL) {
1284 crm_time_free(answer);
1285 return NULL;
1286 }
1287
1288 answer->years += utc->years;
1289 crm_time_add_months(answer, utc->months);
1290 crm_time_add_days(answer, utc->days);
1291 crm_time_add_seconds(answer, utc->seconds);
1292
1293 crm_time_free(utc);
1294 return answer;
1295 }
1296
1297 crm_time_t *
1298 crm_time_calculate_duration(crm_time_t * dt, crm_time_t * value)
1299 {
1300 crm_time_t *utc = NULL;
1301 crm_time_t *answer = NULL;
1302
1303 if ((dt == NULL) || (value == NULL)) {
1304 errno = EINVAL;
1305 return NULL;
1306 }
1307
1308 utc = crm_get_utc_time(value);
1309 if (utc == NULL) {
1310 return NULL;
1311 }
1312
1313 answer = crm_get_utc_time(dt);
1314 if (answer == NULL) {
1315 crm_time_free(utc);
1316 return NULL;
1317 }
1318 answer->duration = TRUE;
1319
1320 answer->years -= utc->years;
1321 if(utc->months != 0) {
1322 crm_time_add_months(answer, -utc->months);
1323 }
1324 crm_time_add_days(answer, -utc->days);
1325 crm_time_add_seconds(answer, -utc->seconds);
1326
1327 crm_time_free(utc);
1328 return answer;
1329 }
1330
1331 crm_time_t *
1332 crm_time_subtract(crm_time_t * dt, crm_time_t * value)
1333 {
1334 crm_time_t *utc = NULL;
1335 crm_time_t *answer = NULL;
1336
1337 if ((dt == NULL) || (value == NULL)) {
1338 errno = EINVAL;
1339 return NULL;
1340 }
1341
1342 utc = crm_get_utc_time(value);
1343 if (utc == NULL) {
1344 return NULL;
1345 }
1346
1347 answer = pcmk_copy_time(dt);
1348 answer->years -= utc->years;
1349 if(utc->months != 0) {
1350 crm_time_add_months(answer, -utc->months);
1351 }
1352 crm_time_add_days(answer, -utc->days);
1353 crm_time_add_seconds(answer, -utc->seconds);
1354
1355 return answer;
1356 }
1357
1358
1359
1360
1361
1362
1363
1364
1365 bool
1366 crm_time_check(crm_time_t * dt)
1367 {
1368 return (dt != NULL)
1369 && (dt->days > 0) && (dt->days <= year_days(dt->years))
1370 && (dt->seconds >= 0) && (dt->seconds < DAY_SECONDS);
1371 }
1372
1373 #define do_cmp_field(l, r, field) \
1374 if(rc == 0) { \
1375 if(l->field > r->field) { \
1376 crm_trace("%s: %d > %d", \
1377 #field, l->field, r->field); \
1378 rc = 1; \
1379 } else if(l->field < r->field) { \
1380 crm_trace("%s: %d < %d", \
1381 #field, l->field, r->field); \
1382 rc = -1; \
1383 } \
1384 }
1385
1386 int
1387 crm_time_compare(crm_time_t *a, crm_time_t *b)
1388 {
1389 int rc = 0;
1390 crm_time_t *t1 = crm_get_utc_time(a);
1391 crm_time_t *t2 = crm_get_utc_time(b);
1392
1393 if ((t1 == NULL) && (t2 == NULL)) {
1394 rc = 0;
1395 } else if (t1 == NULL) {
1396 rc = -1;
1397 } else if (t2 == NULL) {
1398 rc = 1;
1399 } else {
1400 do_cmp_field(t1, t2, years);
1401 do_cmp_field(t1, t2, days);
1402 do_cmp_field(t1, t2, seconds);
1403 }
1404
1405 crm_time_free(t1);
1406 crm_time_free(t2);
1407 return rc;
1408 }
1409
1410
1411
1412
1413
1414
1415
1416 void
1417 crm_time_add_seconds(crm_time_t *a_time, int extra)
1418 {
1419 int days = 0;
1420
1421 crm_trace("Adding %d seconds to %d (max=%d)",
1422 extra, a_time->seconds, DAY_SECONDS);
1423 a_time->seconds += extra;
1424 days = a_time->seconds / DAY_SECONDS;
1425 a_time->seconds %= DAY_SECONDS;
1426
1427
1428 if (a_time->seconds < 0) {
1429 a_time->seconds += DAY_SECONDS;
1430 --days;
1431 }
1432
1433 crm_time_add_days(a_time, days);
1434 }
1435
1436 void
1437 crm_time_add_days(crm_time_t * a_time, int extra)
1438 {
1439 int lower_bound = 1;
1440 int ydays = crm_time_leapyear(a_time->years) ? 366 : 365;
1441
1442 crm_trace("Adding %d days to %.4d-%.3d", extra, a_time->years, a_time->days);
1443
1444 a_time->days += extra;
1445 while (a_time->days > ydays) {
1446 a_time->years++;
1447 a_time->days -= ydays;
1448 ydays = crm_time_leapyear(a_time->years) ? 366 : 365;
1449 }
1450
1451 if(a_time->duration) {
1452 lower_bound = 0;
1453 }
1454
1455 while (a_time->days < lower_bound) {
1456 a_time->years--;
1457 a_time->days += crm_time_leapyear(a_time->years) ? 366 : 365;
1458 }
1459 }
1460
1461 void
1462 crm_time_add_months(crm_time_t * a_time, int extra)
1463 {
1464 int lpc;
1465 uint32_t y, m, d, dmax;
1466
1467 crm_time_get_gregorian(a_time, &y, &m, &d);
1468 crm_trace("Adding %d months to %.4d-%.2d-%.2d", extra, y, m, d);
1469
1470 if (extra > 0) {
1471 for (lpc = extra; lpc > 0; lpc--) {
1472 m++;
1473 if (m == 13) {
1474 m = 1;
1475 y++;
1476 }
1477 }
1478 } else {
1479 for (lpc = -extra; lpc > 0; lpc--) {
1480 m--;
1481 if (m == 0) {
1482 m = 12;
1483 y--;
1484 }
1485 }
1486 }
1487
1488 dmax = crm_time_days_in_month(m, y);
1489 if (dmax < d) {
1490
1491 d = dmax;
1492 }
1493
1494 crm_trace("Calculated %.4d-%.2d-%.2d", y, m, d);
1495
1496 a_time->years = y;
1497 a_time->days = get_ordinal_days(y, m, d);
1498
1499 crm_time_get_gregorian(a_time, &y, &m, &d);
1500 crm_trace("Got %.4d-%.2d-%.2d", y, m, d);
1501 }
1502
1503 void
1504 crm_time_add_minutes(crm_time_t * a_time, int extra)
1505 {
1506 crm_time_add_seconds(a_time, extra * 60);
1507 }
1508
1509 void
1510 crm_time_add_hours(crm_time_t * a_time, int extra)
1511 {
1512 crm_time_add_seconds(a_time, extra * HOUR_SECONDS);
1513 }
1514
1515 void
1516 crm_time_add_weeks(crm_time_t * a_time, int extra)
1517 {
1518 crm_time_add_days(a_time, extra * 7);
1519 }
1520
1521 void
1522 crm_time_add_years(crm_time_t * a_time, int extra)
1523 {
1524 a_time->years += extra;
1525 }
1526
1527 static void
1528 ha_get_tm_time( struct tm *target, crm_time_t *source)
1529 {
1530 *target = (struct tm) {
1531 .tm_year = source->years - 1900,
1532 .tm_mday = source->days,
1533 .tm_sec = source->seconds % 60,
1534 .tm_min = ( source->seconds / 60 ) % 60,
1535 .tm_hour = source->seconds / HOUR_SECONDS,
1536 .tm_isdst = -1,
1537
1538 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
1539 .tm_gmtoff = source->offset
1540 #endif
1541 };
1542 mktime(target);
1543 }
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555 pcmk__time_hr_t *
1556 pcmk__time_hr_convert(pcmk__time_hr_t *target, crm_time_t *dt)
1557 {
1558 pcmk__time_hr_t *hr_dt = NULL;
1559
1560 if (dt) {
1561 hr_dt = target?target:calloc(1, sizeof(pcmk__time_hr_t));
1562 CRM_ASSERT(hr_dt != NULL);
1563 *hr_dt = (pcmk__time_hr_t) {
1564 .years = dt->years,
1565 .months = dt->months,
1566 .days = dt->days,
1567 .seconds = dt->seconds,
1568 .offset = dt->offset,
1569 .duration = dt->duration
1570 };
1571 }
1572
1573 return hr_dt;
1574 }
1575
1576 void
1577 pcmk__time_set_hr_dt(crm_time_t *target, pcmk__time_hr_t *hr_dt)
1578 {
1579 CRM_ASSERT((hr_dt) && (target));
1580 *target = (crm_time_t) {
1581 .years = hr_dt->years,
1582 .months = hr_dt->months,
1583 .days = hr_dt->days,
1584 .seconds = hr_dt->seconds,
1585 .offset = hr_dt->offset,
1586 .duration = hr_dt->duration
1587 };
1588 }
1589
1590 pcmk__time_hr_t *
1591 pcmk__time_timeval_hr_convert(pcmk__time_hr_t *target, struct timeval *tv)
1592 {
1593 crm_time_t dt;
1594 pcmk__time_hr_t *ret;
1595
1596 crm_time_set_timet(&dt, &tv->tv_sec);
1597 ret = pcmk__time_hr_convert(target, &dt);
1598 if (ret) {
1599 ret->useconds = tv->tv_usec;
1600 }
1601 return ret;
1602 }
1603
1604 pcmk__time_hr_t *
1605 pcmk__time_hr_new(const char *date_time)
1606 {
1607 pcmk__time_hr_t *hr_dt = NULL;
1608 struct timeval tv_now;
1609
1610 if (!date_time) {
1611 if (gettimeofday(&tv_now, NULL) == 0) {
1612 hr_dt = pcmk__time_timeval_hr_convert(NULL, &tv_now);
1613 }
1614 } else {
1615 crm_time_t *dt;
1616
1617 dt = parse_date(date_time);
1618 hr_dt = pcmk__time_hr_convert(NULL, dt);
1619 crm_time_free(dt);
1620 }
1621 return hr_dt;
1622 }
1623
1624 void
1625 pcmk__time_hr_free(pcmk__time_hr_t * hr_dt)
1626 {
1627 free(hr_dt);
1628 }
1629
1630 char *
1631 pcmk__time_format_hr(const char *format, pcmk__time_hr_t * hr_dt)
1632 {
1633 const char *mark_s;
1634 int max = 128, scanned_pos = 0, printed_pos = 0, fmt_pos = 0,
1635 date_len = 0, nano_digits = 0;
1636 char nano_s[10], date_s[max+1], nanofmt_s[5] = "%", *tmp_fmt_s;
1637 struct tm tm;
1638 crm_time_t dt;
1639
1640 if (!format) {
1641 return NULL;
1642 }
1643 pcmk__time_set_hr_dt(&dt, hr_dt);
1644 ha_get_tm_time(&tm, &dt);
1645 sprintf(nano_s, "%06d000", hr_dt->useconds);
1646
1647 while ((format[scanned_pos]) != '\0') {
1648 mark_s = strchr(&format[scanned_pos], '%');
1649 if (mark_s) {
1650 int fmt_len = 1;
1651
1652 fmt_pos = mark_s - format;
1653 while ((format[fmt_pos+fmt_len] != '\0') &&
1654 (format[fmt_pos+fmt_len] >= '0') &&
1655 (format[fmt_pos+fmt_len] <= '9')) {
1656 fmt_len++;
1657 }
1658 scanned_pos = fmt_pos + fmt_len + 1;
1659 if (format[fmt_pos+fmt_len] == 'N') {
1660 nano_digits = atoi(&format[fmt_pos+1]);
1661 nano_digits = (nano_digits > 6)?6:nano_digits;
1662 nano_digits = (nano_digits < 0)?0:nano_digits;
1663 sprintf(&nanofmt_s[1], ".%ds", nano_digits);
1664 } else {
1665 if (format[scanned_pos] != '\0') {
1666 continue;
1667 }
1668 fmt_pos = scanned_pos;
1669 }
1670 } else {
1671 scanned_pos = strlen(format);
1672 fmt_pos = scanned_pos;
1673 }
1674 tmp_fmt_s = strndup(&format[printed_pos], fmt_pos - printed_pos);
1675 #ifdef GCC_FORMAT_NONLITERAL_CHECKING_ENABLED
1676 #pragma GCC diagnostic push
1677 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
1678 #endif
1679 date_len += strftime(&date_s[date_len], max-date_len, tmp_fmt_s, &tm);
1680 #ifdef GCC_FORMAT_NONLITERAL_CHECKING_ENABLED
1681 #pragma GCC diagnostic pop
1682 #endif
1683 printed_pos = scanned_pos;
1684 free(tmp_fmt_s);
1685 if (nano_digits) {
1686 #ifdef GCC_FORMAT_NONLITERAL_CHECKING_ENABLED
1687 #pragma GCC diagnostic push
1688 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
1689 #endif
1690 date_len += snprintf(&date_s[date_len], max-date_len,
1691 nanofmt_s, nano_s);
1692 #ifdef GCC_FORMAT_NONLITERAL_CHECKING_ENABLED
1693 #pragma GCC diagnostic pop
1694 #endif
1695 nano_digits = 0;
1696 }
1697 }
1698
1699 return (date_len == 0)?NULL:strdup(date_s);
1700 }
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714 const char *
1715 pcmk__epoch2str(time_t *when)
1716 {
1717 char *since_epoch = NULL;
1718
1719 if (when == NULL) {
1720 time_t a_time = time(NULL);
1721
1722 if (a_time == (time_t) -1) {
1723 return NULL;
1724 } else {
1725 since_epoch = ctime(&a_time);
1726 }
1727 } else {
1728 since_epoch = ctime(when);
1729 }
1730
1731 if (since_epoch == NULL) {
1732 return NULL;
1733 } else {
1734 return pcmk__trim(since_epoch);
1735 }
1736 }