root/lib/common/iso8601.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. crm_get_utc_time
  2. crm_time_new
  3. crm_time_free
  4. year_days
  5. crm_time_january1_weekday
  6. crm_time_weeks_in_year
  7. crm_time_days_in_month
  8. crm_time_leapyear
  9. get_ordinal_days
  10. crm_time_log_alias
  11. crm_time_get_sec
  12. crm_time_get_timeofday
  13. crm_time_get_timezone
  14. crm_time_get_seconds
  15. crm_time_get_seconds_since_epoch
  16. crm_time_get_gregorian
  17. crm_time_get_ordinal
  18. crm_time_get_isoweek
  19. crm_time_as_string
  20. crm_time_parse_sec
  21. crm_time_parse_offset
  22. crm_time_parse
  23. parse_date
  24. parse_int
  25. crm_time_parse_duration
  26. crm_time_parse_period
  27. crm_time_set
  28. ha_set_tm_time
  29. crm_time_set_timet
  30. crm_time_add
  31. crm_time_calculate_duration
  32. crm_time_subtract
  33. crm_time_check
  34. crm_time_compare
  35. crm_time_add_seconds
  36. crm_time_add_days
  37. crm_time_add_months
  38. crm_time_add_minutes
  39. crm_time_add_hours
  40. crm_time_add_weeks
  41. crm_time_add_years
  42. ha_get_tm_time
  43. crm_time_hr_convert
  44. crm_time_set_hr_dt
  45. crm_time_timeval_hr_convert
  46. crm_time_hr_new
  47. crm_time_hr_free
  48. crm_time_format_hr

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

/* [previous][next][first][last][top][bottom][index][help] */