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  offset_str++;
694  } else if (offset_str[0] == '-') {
695  negate = TRUE;
696  offset_str++;
697  }
698  if (crm_time_parse_sec(offset_str, offset) == FALSE) {
699  return FALSE;
700  }
701  if (negate) {
702  *offset = 0 - *offset;
703  }
704  } // @TODO else invalid?
705  return TRUE;
706 }
707 
718 static bool
719 crm_time_parse(const char *time_str, crm_time_t *a_time)
720 {
721  uint h, m, s;
722  char *offset_s = NULL;
723 
724  tzset();
725 
726  if (time_str) {
727  if (crm_time_parse_sec(time_str, &(a_time->seconds)) == FALSE) {
728  return FALSE;
729  }
730  offset_s = strstr(time_str, "Z");
731  if (offset_s == NULL) {
732  offset_s = strstr(time_str, " ");
733  if (offset_s) {
734  while (isspace(offset_s[0])) {
735  offset_s++;
736  }
737  }
738  }
739  }
740 
741  if (crm_time_parse_offset(offset_s, &(a_time->offset)) == FALSE) {
742  return FALSE;
743  }
744  crm_time_get_sec(a_time->offset, &h, &m, &s);
745  crm_trace("Got tz: %c%2.d:%.2d", ((a_time->offset < 0)? '-' : '+'), h, m);
746 
747  if (a_time->seconds == DAY_SECONDS) {
748  // 24:00:00 == 00:00:00 of next day
749  a_time->seconds = 0;
750  crm_time_add_days(a_time, 1);
751  }
752  return TRUE;
753 }
754 
755 /*
756  * \internal
757  * \brief Parse a time object from an ISO 8601 date/time specification
758  *
759  * \param[in] date_str ISO 8601 date/time specification (or "epoch")
760  *
761  * \return New time object on success, NULL (and set errno) otherwise
762  */
763 static crm_time_t *
764 parse_date(const char *date_str)
765 {
766  const char *time_s = NULL;
767  crm_time_t *dt = NULL;
768 
769  int year = 0;
770  int month = 0;
771  int week = 0;
772  int day = 0;
773  int rc = 0;
774 
775  if ((date_str == NULL) || (date_str[0] == '\0')) {
776  crm_err("No ISO 8601 date/time specification given");
777  goto invalid;
778  }
779 
780  if ((date_str[0] == 'T') || (date_str[2] == ':')) {
781  /* Just a time supplied - Infer current date */
782  dt = crm_time_new(NULL);
783  if (date_str[0] == 'T') {
784  time_s = date_str + 1;
785  } else {
786  time_s = date_str;
787  }
788  goto parse_time;
789  }
790 
791  dt = crm_time_new_undefined();
792 
793  if (!strncasecmp("epoch", date_str, 5)
794  && ((date_str[5] == '\0') || (date_str[5] == '/') || isspace(date_str[5]))) {
795  dt->days = 1;
796  dt->years = 1970;
798  return dt;
799  }
800 
801  /* YYYY-MM-DD */
802  rc = sscanf(date_str, "%d-%d-%d", &year, &month, &day);
803  if (rc == 1) {
804  /* YYYYMMDD */
805  rc = sscanf(date_str, "%4d%2d%2d", &year, &month, &day);
806  }
807  if (rc == 3) {
808  if (month > 12) {
809  crm_err("'%s' is not a valid ISO 8601 date/time specification "
810  "because '%d' is not a valid month", date_str, month);
811  goto invalid;
812  } else if (day > crm_time_days_in_month(month, year)) {
813  crm_err("'%s' is not a valid ISO 8601 date/time specification "
814  "because '%d' is not a valid day of the month",
815  date_str, day);
816  goto invalid;
817  } else {
818  dt->years = year;
819  dt->days = get_ordinal_days(year, month, day);
820  crm_trace("Parsed Gregorian date '%.4d-%.3d' from date string '%s'",
821  year, dt->days, date_str);
822  }
823  goto parse_time;
824  }
825 
826  /* YYYY-DDD */
827  rc = sscanf(date_str, "%d-%d", &year, &day);
828  if (rc == 2) {
829  if (day > year_days(year)) {
830  crm_err("'%s' is not a valid ISO 8601 date/time specification "
831  "because '%d' is not a valid day of the year (max %d)",
832  date_str, day, year_days(year));
833  goto invalid;
834  }
835  crm_trace("Parsed ordinal year %d and days %d from date string '%s'",
836  year, day, date_str);
837  dt->days = day;
838  dt->years = year;
839  goto parse_time;
840  }
841 
842  /* YYYY-Www-D */
843  rc = sscanf(date_str, "%d-W%d-%d", &year, &week, &day);
844  if (rc == 3) {
845  if (week > crm_time_weeks_in_year(year)) {
846  crm_err("'%s' is not a valid ISO 8601 date/time specification "
847  "because '%d' is not a valid week of the year (max %d)",
848  date_str, week, crm_time_weeks_in_year(year));
849  goto invalid;
850  } else if (day < 1 || day > 7) {
851  crm_err("'%s' is not a valid ISO 8601 date/time specification "
852  "because '%d' is not a valid day of the week",
853  date_str, day);
854  goto invalid;
855  } else {
856  /*
857  * See https://en.wikipedia.org/wiki/ISO_week_date
858  *
859  * Monday 29 December 2008 is written "2009-W01-1"
860  * Sunday 3 January 2010 is written "2009-W53-7"
861  * Saturday 27 September 2008 is written "2008-W37-6"
862  *
863  * If 1 January is on a Monday, Tuesday, Wednesday or Thursday, it is in week 01.
864  * If 1 January is on a Friday, Saturday or Sunday, it is in week 52 or 53 of the previous year.
865  */
866  int jan1 = crm_time_january1_weekday(year);
867 
868  crm_trace("Got year %d (Jan 1 = %d), week %d, and day %d from date string '%s'",
869  year, jan1, week, day, date_str);
870 
871  dt->years = year;
872  crm_time_add_days(dt, (week - 1) * 7);
873 
874  if (jan1 <= 4) {
875  crm_time_add_days(dt, 1 - jan1);
876  } else {
877  crm_time_add_days(dt, 8 - jan1);
878  }
879 
880  crm_time_add_days(dt, day);
881  }
882  goto parse_time;
883  }
884 
885  crm_err("'%s' is not a valid ISO 8601 date/time specification", date_str);
886  goto invalid;
887 
888  parse_time:
889 
890  if (time_s == NULL) {
891  time_s = date_str + strspn(date_str, "0123456789-W");
892  if ((time_s[0] == ' ') || (time_s[0] == 'T')) {
893  ++time_s;
894  } else {
895  time_s = NULL;
896  }
897  }
898  if ((time_s != NULL) && (crm_time_parse(time_s, dt) == FALSE)) {
899  goto invalid;
900  }
901 
903  if (crm_time_check(dt) == FALSE) {
904  crm_err("'%s' is not a valid ISO 8601 date/time specification",
905  date_str);
906  goto invalid;
907  }
908  return dt;
909 
910 invalid:
911  crm_time_free(dt);
912  errno = EINVAL;
913  return NULL;
914 }
915 
916 // Parse an ISO 8601 numeric value and return number of characters consumed
917 // @TODO This cannot handle >INT_MAX int values
918 // @TODO Fractions appear to be not working
919 // @TODO Error out on invalid specifications
920 static int
921 parse_int(const char *str, int field_width, int upper_bound, int *result)
922 {
923  int lpc = 0;
924  int offset = 0;
925  int intermediate = 0;
926  gboolean fraction = FALSE;
927  gboolean negate = FALSE;
928 
929  *result = 0;
930  if (*str == '\0') {
931  return 0;
932  }
933 
934  if (str[offset] == 'T') {
935  offset++;
936  }
937 
938  if (str[offset] == '.' || str[offset] == ',') {
939  fraction = TRUE;
940  field_width = -1;
941  offset++;
942  } else if (str[offset] == '-') {
943  negate = TRUE;
944  offset++;
945  } else if (str[offset] == '+' || str[offset] == ':') {
946  offset++;
947  }
948 
949  for (; (fraction || lpc < field_width) && isdigit((int)str[offset]); lpc++) {
950  if (fraction) {
951  intermediate = (str[offset] - '0') / (10 ^ lpc);
952  } else {
953  *result *= 10;
954  intermediate = str[offset] - '0';
955  }
956  *result += intermediate;
957  offset++;
958  }
959  if (fraction) {
960  *result = (int)(*result * upper_bound);
961 
962  } else if (upper_bound > 0 && *result > upper_bound) {
963  *result = upper_bound;
964  }
965  if (negate) {
966  *result = 0 - *result;
967  }
968  if (lpc > 0) {
969  crm_trace("Found int: %d. Stopped at str[%d]='%c'", *result, lpc, str[lpc]);
970  return offset;
971  }
972  return 0;
973 }
974 
986 crm_time_t *
987 crm_time_parse_duration(const char *period_s)
988 {
989  gboolean is_time = FALSE;
990  crm_time_t *diff = NULL;
991 
992  if ((period_s == NULL) || (period_s[0] == '\0')) {
993  crm_err("No ISO 8601 time duration given");
994  goto invalid;
995  }
996  if (period_s[0] != 'P') {
997  crm_err("'%s' is not a valid ISO 8601 time duration "
998  "because it does not start with a 'P'", period_s);
999  goto invalid;
1000  }
1001  if ((period_s[1] == '\0') || isspace(period_s[1])) {
1002  crm_err("'%s' is not a valid ISO 8601 time duration "
1003  "because nothing follows 'P'", period_s);
1004  goto invalid;
1005  }
1006 
1007  diff = crm_time_new_undefined();
1008  diff->duration = TRUE;
1009 
1010  for (const char *current = period_s + 1;
1011  current[0] && (current[0] != '/') && !isspace(current[0]);
1012  ++current) {
1013 
1014  int an_int = 0, rc;
1015 
1016  if (current[0] == 'T') {
1017  /* A 'T' separates year/month/day from hour/minute/seconds. We don't
1018  * require it strictly, but just use it to differentiate month from
1019  * minutes.
1020  */
1021  is_time = TRUE;
1022  continue;
1023  }
1024 
1025  // An integer must be next
1026  rc = parse_int(current, 10, 0, &an_int);
1027  if (rc == 0) {
1028  crm_err("'%s' is not a valid ISO 8601 time duration "
1029  "because no integer at '%s'", period_s, current);
1030  goto invalid;
1031  }
1032  current += rc;
1033 
1034  // A time unit must be next (we're not strict about the order)
1035  switch (current[0]) {
1036  case 'Y':
1037  diff->years = an_int;
1038  break;
1039  case 'M':
1040  if (is_time) {
1041  /* Minutes */
1042  diff->seconds += an_int * 60;
1043  } else {
1044  diff->months = an_int;
1045  }
1046  break;
1047  case 'W':
1048  diff->days += an_int * 7;
1049  break;
1050  case 'D':
1051  diff->days += an_int;
1052  break;
1053  case 'H':
1054  diff->seconds += an_int * HOUR_SECONDS;
1055  break;
1056  case 'S':
1057  diff->seconds += an_int;
1058  break;
1059  case '\0':
1060  crm_err("'%s' is not a valid ISO 8601 time duration "
1061  "because no units after %d", period_s, an_int);
1062  goto invalid;
1063  default:
1064  crm_err("'%s' is not a valid ISO 8601 time duration "
1065  "because '%c' is not a valid time unit",
1066  period_s, current[0]);
1067  goto invalid;
1068  }
1069  }
1070 
1071  if (!crm_time_is_defined(diff)) {
1072  crm_err("'%s' is not a valid ISO 8601 time duration "
1073  "because no amounts and units given", period_s);
1074  goto invalid;
1075  }
1076  return diff;
1077 
1078 invalid:
1079  crm_time_free(diff);
1080  errno = EINVAL;
1081  return NULL;
1082 }
1083 
1095 crm_time_parse_period(const char *period_str)
1096 {
1097  const char *original = period_str;
1098  crm_time_period_t *period = NULL;
1099 
1100  if ((period_str == NULL) || (period_str[0] == '\0')) {
1101  crm_err("No ISO 8601 time period given");
1102  goto invalid;
1103  }
1104 
1105  tzset();
1106  period = calloc(1, sizeof(crm_time_period_t));
1107  CRM_ASSERT(period != NULL);
1108 
1109  if (period_str[0] == 'P') {
1110  period->diff = crm_time_parse_duration(period_str);
1111  if (period->diff == NULL) {
1112  goto error;
1113  }
1114  } else {
1115  period->start = parse_date(period_str);
1116  if (period->start == NULL) {
1117  goto error;
1118  }
1119  }
1120 
1121  period_str = strstr(original, "/");
1122  if (period_str) {
1123  ++period_str;
1124  if (period_str[0] == 'P') {
1125  if (period->diff != NULL) {
1126  crm_err("'%s' is not a valid ISO 8601 time period "
1127  "because it has two durations",
1128  original);
1129  goto invalid;
1130  }
1131  period->diff = crm_time_parse_duration(period_str);
1132  if (period->diff == NULL) {
1133  goto error;
1134  }
1135  } else {
1136  period->end = parse_date(period_str);
1137  if (period->end == NULL) {
1138  goto error;
1139  }
1140  }
1141 
1142  } else if (period->diff != NULL) {
1143  // Only duration given, assume start is now
1144  period->start = crm_time_new(NULL);
1145 
1146  } else {
1147  // Only start given
1148  crm_err("'%s' is not a valid ISO 8601 time period "
1149  "because it has no duration or ending time",
1150  original);
1151  goto invalid;
1152  }
1153 
1154  if (period->start == NULL) {
1155  period->start = crm_time_subtract(period->end, period->diff);
1156 
1157  } else if (period->end == NULL) {
1158  period->end = crm_time_add(period->start, period->diff);
1159  }
1160 
1161  if (crm_time_check(period->start) == FALSE) {
1162  crm_err("'%s' is not a valid ISO 8601 time period "
1163  "because the start is invalid", period_str);
1164  goto invalid;
1165  }
1166  if (crm_time_check(period->end) == FALSE) {
1167  crm_err("'%s' is not a valid ISO 8601 time period "
1168  "because the end is invalid", period_str);
1169  goto invalid;
1170  }
1171  return period;
1172 
1173 invalid:
1174  errno = EINVAL;
1175 error:
1176  crm_time_free_period(period);
1177  return NULL;
1178 }
1179 
1185 void
1187 {
1188  if (period) {
1189  crm_time_free(period->start);
1190  crm_time_free(period->end);
1191  crm_time_free(period->diff);
1192  free(period);
1193  }
1194 }
1195 
1196 void
1198 {
1199  crm_trace("target=%p, source=%p", target, source);
1200 
1201  CRM_CHECK(target != NULL && source != NULL, return);
1202 
1203  target->years = source->years;
1204  target->days = source->days;
1205  target->months = source->months; /* Only for durations */
1206  target->seconds = source->seconds;
1207  target->offset = source->offset;
1208 
1209  crm_time_log(LOG_TRACE, "source", source,
1211  crm_time_log(LOG_TRACE, "target", target,
1213 }
1214 
1215 static void
1216 ha_set_tm_time(crm_time_t * target, struct tm *source)
1217 {
1218  int h_offset = 0;
1219  int m_offset = 0;
1220 
1221  /* Ensure target is fully initialized */
1222  target->years = 0;
1223  target->months = 0;
1224  target->days = 0;
1225  target->seconds = 0;
1226  target->offset = 0;
1227  target->duration = FALSE;
1228 
1229  if (source->tm_year > 0) {
1230  /* years since 1900 */
1231  target->years = 1900 + source->tm_year;
1232  }
1233 
1234  if (source->tm_yday >= 0) {
1235  /* days since January 1 [0-365] */
1236  target->days = 1 + source->tm_yday;
1237  }
1238 
1239  if (source->tm_hour >= 0) {
1240  target->seconds += HOUR_SECONDS * source->tm_hour;
1241  }
1242  if (source->tm_min >= 0) {
1243  target->seconds += 60 * source->tm_min;
1244  }
1245  if (source->tm_sec >= 0) {
1246  target->seconds += source->tm_sec;
1247  }
1248 
1249  /* tm_gmtoff == offset from UTC in seconds */
1250  h_offset = GMTOFF(source) / HOUR_SECONDS;
1251  m_offset = (GMTOFF(source) - (HOUR_SECONDS * h_offset)) / 60;
1252  crm_trace("Offset (s): %ld, offset (hh:mm): %.2d:%.2d", GMTOFF(source), h_offset, m_offset);
1253 
1254  target->offset += HOUR_SECONDS * h_offset;
1255  target->offset += 60 * m_offset;
1256 }
1257 
1258 void
1259 crm_time_set_timet(crm_time_t * target, time_t * source)
1260 {
1261  ha_set_tm_time(target, localtime(source));
1262 }
1263 
1264 crm_time_t *
1266 {
1267  crm_time_t *utc = NULL;
1268  crm_time_t *answer = NULL;
1269 
1270  if ((dt == NULL) || (value == NULL)) {
1271  errno = EINVAL;
1272  return NULL;
1273  }
1274 
1275  answer = crm_time_new_undefined();
1276  crm_time_set(answer, dt);
1277 
1278  utc = crm_get_utc_time(value);
1279  if (utc == NULL) {
1280  crm_time_free(answer);
1281  return NULL;
1282  }
1283 
1284  answer->years += utc->years;
1285  crm_time_add_months(answer, utc->months);
1286  crm_time_add_days(answer, utc->days);
1287  crm_time_add_seconds(answer, utc->seconds);
1288 
1289  crm_time_free(utc);
1290  return answer;
1291 }
1292 
1293 crm_time_t *
1295 {
1296  crm_time_t *utc = NULL;
1297  crm_time_t *answer = NULL;
1298 
1299  if ((dt == NULL) || (value == NULL)) {
1300  errno = EINVAL;
1301  return NULL;
1302  }
1303 
1304  utc = crm_get_utc_time(value);
1305  if (utc == NULL) {
1306  return NULL;
1307  }
1308 
1309  answer = crm_get_utc_time(dt);
1310  if (answer == NULL) {
1311  crm_time_free(utc);
1312  return NULL;
1313  }
1314  answer->duration = TRUE;
1315 
1316  answer->years -= utc->years;
1317  if(utc->months != 0) {
1318  crm_time_add_months(answer, -utc->months);
1319  }
1320  crm_time_add_days(answer, -utc->days);
1321  crm_time_add_seconds(answer, -utc->seconds);
1322 
1323  crm_time_free(utc);
1324  return answer;
1325 }
1326 
1327 crm_time_t *
1329 {
1330  crm_time_t *utc = NULL;
1331  crm_time_t *answer = NULL;
1332 
1333  if ((dt == NULL) || (value == NULL)) {
1334  errno = EINVAL;
1335  return NULL;
1336  }
1337 
1338  utc = crm_get_utc_time(value);
1339  if (utc == NULL) {
1340  return NULL;
1341  }
1342 
1343  answer = crm_time_new_undefined();
1344  crm_time_set(answer, dt);
1345  answer->years -= utc->years;
1346  if(utc->months != 0) {
1347  crm_time_add_months(answer, -utc->months);
1348  }
1349  crm_time_add_days(answer, -utc->days);
1350  crm_time_add_seconds(answer, -utc->seconds);
1351 
1352  return answer;
1353 }
1354 
1362 bool
1364 {
1365  return (dt != NULL)
1366  && (dt->days > 0) && (dt->days <= year_days(dt->years))
1367  && (dt->seconds >= 0) && (dt->seconds < DAY_SECONDS);
1368 }
1369 
1370 #define do_cmp_field(l, r, field) \
1371  if(rc == 0) { \
1372  if(l->field > r->field) { \
1373  crm_trace("%s: %d > %d", \
1374  #field, l->field, r->field); \
1375  rc = 1; \
1376  } else if(l->field < r->field) { \
1377  crm_trace("%s: %d < %d", \
1378  #field, l->field, r->field); \
1379  rc = -1; \
1380  } \
1381  }
1382 
1383 int
1385 {
1386  int rc = 0;
1387  crm_time_t *t1 = crm_get_utc_time(a);
1388  crm_time_t *t2 = crm_get_utc_time(b);
1389 
1390  if ((t1 == NULL) && (t2 == NULL)) {
1391  rc = 0;
1392  } else if (t1 == NULL) {
1393  rc = -1;
1394  } else if (t2 == NULL) {
1395  rc = 1;
1396  } else {
1397  do_cmp_field(t1, t2, years);
1398  do_cmp_field(t1, t2, days);
1399  do_cmp_field(t1, t2, seconds);
1400  }
1401 
1402  crm_time_free(t1);
1403  crm_time_free(t2);
1404  return rc;
1405 }
1406 
1413 void
1415 {
1416  int days = 0;
1417 
1418  crm_trace("Adding %d seconds to %d (max=%d)",
1419  extra, a_time->seconds, DAY_SECONDS);
1420  a_time->seconds += extra;
1421  days = a_time->seconds / DAY_SECONDS;
1422  a_time->seconds %= DAY_SECONDS;
1423 
1424  // Don't have negative seconds
1425  if (a_time->seconds < 0) {
1426  a_time->seconds += DAY_SECONDS;
1427  --days;
1428  }
1429 
1430  crm_time_add_days(a_time, days);
1431 }
1432 
1433 void
1434 crm_time_add_days(crm_time_t * a_time, int extra)
1435 {
1436  int lower_bound = 1;
1437  int ydays = crm_time_leapyear(a_time->years) ? 366 : 365;
1438 
1439  crm_trace("Adding %d days to %.4d-%.3d", extra, a_time->years, a_time->days);
1440 
1441  a_time->days += extra;
1442  while (a_time->days > ydays) {
1443  a_time->years++;
1444  a_time->days -= ydays;
1445  ydays = crm_time_leapyear(a_time->years) ? 366 : 365;
1446  }
1447 
1448  if(a_time->duration) {
1449  lower_bound = 0;
1450  }
1451 
1452  while (a_time->days < lower_bound) {
1453  a_time->years--;
1454  a_time->days += crm_time_leapyear(a_time->years) ? 366 : 365;
1455  }
1456 }
1457 
1458 void
1459 crm_time_add_months(crm_time_t * a_time, int extra)
1460 {
1461  int lpc;
1462  uint32_t y, m, d, dmax;
1463 
1464  crm_time_get_gregorian(a_time, &y, &m, &d);
1465  crm_trace("Adding %d months to %.4d-%.2d-%.2d", extra, y, m, d);
1466 
1467  if (extra > 0) {
1468  for (lpc = extra; lpc > 0; lpc--) {
1469  m++;
1470  if (m == 13) {
1471  m = 1;
1472  y++;
1473  }
1474  }
1475  } else {
1476  for (lpc = -extra; lpc > 0; lpc--) {
1477  m--;
1478  if (m == 0) {
1479  m = 12;
1480  y--;
1481  }
1482  }
1483  }
1484 
1485  dmax = crm_time_days_in_month(m, y);
1486  if (dmax < d) {
1487  /* Preserve day-of-month unless the month doesn't have enough days */
1488  d = dmax;
1489  }
1490 
1491  crm_trace("Calculated %.4d-%.2d-%.2d", y, m, d);
1492 
1493  a_time->years = y;
1494  a_time->days = get_ordinal_days(y, m, d);
1495 
1496  crm_time_get_gregorian(a_time, &y, &m, &d);
1497  crm_trace("Got %.4d-%.2d-%.2d", y, m, d);
1498 }
1499 
1500 void
1501 crm_time_add_minutes(crm_time_t * a_time, int extra)
1502 {
1503  crm_time_add_seconds(a_time, extra * 60);
1504 }
1505 
1506 void
1507 crm_time_add_hours(crm_time_t * a_time, int extra)
1508 {
1509  crm_time_add_seconds(a_time, extra * HOUR_SECONDS);
1510 }
1511 
1512 void
1513 crm_time_add_weeks(crm_time_t * a_time, int extra)
1514 {
1515  crm_time_add_days(a_time, extra * 7);
1516 }
1517 
1518 void
1519 crm_time_add_years(crm_time_t * a_time, int extra)
1520 {
1521  a_time->years += extra;
1522 }
1523 
1524 static void
1525 ha_get_tm_time( struct tm *target, crm_time_t *source)
1526 {
1527  *target = (struct tm) {
1528  .tm_year = source->years - 1900,
1529  .tm_mday = source->days,
1530  .tm_sec = source->seconds % 60,
1531  .tm_min = ( source->seconds / 60 ) % 60,
1532  .tm_hour = source->seconds / HOUR_SECONDS,
1533  .tm_isdst = -1, /* don't adjust */
1534 
1535 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
1536  .tm_gmtoff = source->offset
1537 #endif
1538  };
1539  mktime(target);
1540 }
1541 
1542 crm_time_hr_t *
1544 {
1545  crm_time_hr_t *hr_dt = NULL;
1546 
1547  if (dt) {
1548  hr_dt = target?target:calloc(1, sizeof(crm_time_hr_t));
1549  CRM_ASSERT(hr_dt != NULL);
1550  *hr_dt = (crm_time_hr_t) {
1551  .years = dt->years,
1552  .months = dt->months,
1553  .days = dt->days,
1554  .seconds = dt->seconds,
1555  .offset = dt->offset,
1556  .duration = dt->duration
1557  };
1558  }
1559 
1560  return hr_dt;
1561 }
1562 
1563 void
1565 {
1566  CRM_ASSERT((hr_dt) && (target));
1567  *target = (crm_time_t) {
1568  .years = hr_dt->years,
1569  .months = hr_dt->months,
1570  .days = hr_dt->days,
1571  .seconds = hr_dt->seconds,
1572  .offset = hr_dt->offset,
1573  .duration = hr_dt->duration
1574  };
1575 }
1576 
1577 crm_time_hr_t *
1578 crm_time_timeval_hr_convert(crm_time_hr_t *target, struct timeval *tv)
1579 {
1580  crm_time_t dt;
1581  crm_time_hr_t *ret;
1582 
1583  crm_time_set_timet(&dt, &tv->tv_sec);
1584  ret = crm_time_hr_convert(target, &dt);
1585  if (ret) {
1586  ret->useconds = tv->tv_usec;
1587  }
1588  return ret;
1589 }
1590 
1591 crm_time_hr_t *
1592 crm_time_hr_new(const char *date_time)
1593 {
1594  crm_time_hr_t *hr_dt = NULL;
1595  struct timeval tv_now;
1596 
1597  if (!date_time) {
1598  if (gettimeofday(&tv_now, NULL) == 0) {
1599  hr_dt = crm_time_timeval_hr_convert(NULL, &tv_now);
1600  }
1601  } else {
1602  crm_time_t *dt;
1603 
1604  dt = parse_date(date_time);
1605  hr_dt = crm_time_hr_convert(NULL, dt);
1606  crm_time_free(dt);
1607  }
1608  return hr_dt;
1609 }
1610 
1611 void
1613 {
1614  free(hr_dt);
1615 }
1616 
1617 char *
1618 crm_time_format_hr(const char *format, crm_time_hr_t * hr_dt)
1619 {
1620  const char *mark_s;
1621  int max = 128, scanned_pos = 0, printed_pos = 0, fmt_pos = 0,
1622  date_len = 0, nano_digits = 0;
1623  char nano_s[10], date_s[max+1], nanofmt_s[5] = "%", *tmp_fmt_s;
1624  struct tm tm;
1625  crm_time_t dt;
1626 
1627  if (!format) {
1628  return NULL;
1629  }
1630  crm_time_set_hr_dt(&dt, hr_dt);
1631  ha_get_tm_time(&tm, &dt);
1632  sprintf(nano_s, "%06d000", hr_dt->useconds);
1633 
1634  while ((format[scanned_pos]) != '\0') {
1635  mark_s = strchr(&format[scanned_pos], '%');
1636  if (mark_s) {
1637  int fmt_len = 1;
1638 
1639  fmt_pos = mark_s - format;
1640  while ((format[fmt_pos+fmt_len] != '\0') &&
1641  (format[fmt_pos+fmt_len] >= '0') &&
1642  (format[fmt_pos+fmt_len] <= '9')) {
1643  fmt_len++;
1644  }
1645  scanned_pos = fmt_pos + fmt_len + 1;
1646  if (format[fmt_pos+fmt_len] == 'N') {
1647  nano_digits = atoi(&format[fmt_pos+1]);
1648  nano_digits = (nano_digits > 6)?6:nano_digits;
1649  nano_digits = (nano_digits < 0)?0:nano_digits;
1650  sprintf(&nanofmt_s[1], ".%ds", nano_digits);
1651  } else {
1652  if (format[scanned_pos] != '\0') {
1653  continue;
1654  }
1655  fmt_pos = scanned_pos; /* print till end */
1656  }
1657  } else {
1658  scanned_pos = strlen(format);
1659  fmt_pos = scanned_pos; /* print till end */
1660  }
1661  tmp_fmt_s = strndup(&format[printed_pos], fmt_pos - printed_pos);
1662 #ifdef GCC_FORMAT_NONLITERAL_CHECKING_ENABLED
1663 #pragma GCC diagnostic push
1664 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
1665 #endif
1666  date_len += strftime(&date_s[date_len], max-date_len, tmp_fmt_s, &tm);
1667 #ifdef GCC_FORMAT_NONLITERAL_CHECKING_ENABLED
1668 #pragma GCC diagnostic pop
1669 #endif
1670  printed_pos = scanned_pos;
1671  free(tmp_fmt_s);
1672  if (nano_digits) {
1673 #ifdef GCC_FORMAT_NONLITERAL_CHECKING_ENABLED
1674 #pragma GCC diagnostic push
1675 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
1676 #endif
1677  date_len += snprintf(&date_s[date_len], max-date_len,
1678  nanofmt_s, nano_s);
1679 #ifdef GCC_FORMAT_NONLITERAL_CHECKING_ENABLED
1680 #pragma GCC diagnostic pop
1681 #endif
1682  nano_digits = 0;
1683  }
1684  }
1685 
1686  return (date_len == 0)?NULL:strdup(date_s);
1687 }
1688 
1700 const char *
1701 crm_now_string(time_t *when)
1702 {
1703  char *since_epoch = NULL;
1704 
1705  if (when == NULL) {
1706  time_t a_time = time(NULL);
1707 
1708  if (a_time == (time_t) -1) {
1709  return NULL;
1710  } else {
1711  since_epoch = ctime(&a_time);
1712  }
1713  } else {
1714  since_epoch = ctime(when);
1715  }
1716 
1717  if (since_epoch == NULL) {
1718  return NULL;
1719  } else {
1720  return crm_strip_trailing_newline(since_epoch);
1721  }
1722 }
crm_time_hr_convert
crm_time_hr_t * crm_time_hr_convert(crm_time_hr_t *target, crm_time_t *dt)
Definition: iso8601.c:1543
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:1259
crm_time_set_hr_dt
void crm_time_set_hr_dt(crm_time_t *target, crm_time_hr_t *hr_dt)
Definition: iso8601.c:1564
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:1370
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:1578
flags
uint64_t flags
Definition: remote.c:5
crm_time_add_minutes
void crm_time_add_minutes(crm_time_t *a_time, int extra)
Definition: iso8601.c:1501
crm_time_subtract
crm_time_t * crm_time_subtract(crm_time_t *dt, crm_time_t *value)
Definition: iso8601.c:1328
crm_time_free_period
void crm_time_free_period(crm_time_period_t *period)
Free a dynamically allocated time period object.
Definition: iso8601.c:1186
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:1197
crm_time_compare
int crm_time_compare(crm_time_t *a, crm_time_t *b)
Definition: iso8601.c:1384
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:1513
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:1414
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:987
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:1701
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:1434
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:1507
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:1095
crm_time_hr_free
void crm_time_hr_free(crm_time_hr_t *hr_dt)
Definition: iso8601.c:1612
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:1363
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:1618
crm_time_hr_new
crm_time_hr_t * crm_time_hr_new(const char *date_time)
Definition: iso8601.c:1592
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:1519
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:1294
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:1459
crm_internal.h
crm_time_add
crm_time_t * crm_time_add(crm_time_t *dt, crm_time_t *value)
Definition: iso8601.c:1265
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