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