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