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