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