40 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
41 # define GMTOFF(tm) ((tm)->tm_gmtoff)
44 # define GMTOFF(tm) (-timezone+daylight)
47 #define HOUR_SECONDS (60 * 60)
48 #define DAY_SECONDS (HOUR_SECONDS * 24)
61 static crm_time_t *parse_date(
const char *date_str);
76 utc->years = dt->years;
78 utc->seconds = dt->seconds;
85 utc->months = dt->months;
102 if (date_time == NULL) {
107 dt = parse_date(date_time);
139 return (t != NULL) && (t->years || t->months || t->days || t->seconds
140 || t->offset || t->duration);
174 int YY = (year - 1) % 100;
175 int C = (year - 1) - YY;
177 int jan1 = 1 + (((((C / 100) % 4) * 5) + G) % 7);
179 crm_trace(
"YY=%d, C=%d, G=%d", YY, C, G);
180 crm_trace(
"January 1 %.4d: %d", year, jan1);
205 static int month_days[13] = {
206 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 29
220 if ((month < 1) || (month > 12)) {
226 return month_days[month - 1];
232 gboolean is_leap = FALSE;
237 if (year % 100 == 0 && year % 400 != 0) {
244 get_ordinal_days(uint32_t y, uint32_t m, uint32_t d)
248 for (lpc = 1; lpc < m; lpc++) {
260 if (log_level < LOG_CRIT) {
262 (prefix? prefix :
""), (prefix?
": " :
""), date_s);
265 (prefix? prefix :
""), (prefix?
": " :
""), date_s);
271 crm_time_get_sec(
int sec, uint * h, uint * m, uint * s)
273 uint hours, minutes, seconds;
284 minutes = seconds / 60;
285 seconds -= 60 * minutes;
287 crm_trace(
"%d == %.2d:%.2d:%.2d", sec, hours, minutes, seconds);
299 return crm_time_get_sec(dt->seconds, h, m, s);
307 return crm_time_get_sec(dt->seconds, h, m, &s);
315 long long in_seconds = 0;
321 utc = crm_get_utc_time(dt);
326 for (lpc = 1; lpc < utc->years; lpc++) {
327 int dmax = year_days(lpc);
339 if (utc->months > 0) {
346 in_seconds += utc->seconds;
352 #define EPOCH_SECONDS 62135596800ULL
366 for (months = 1; months <= 12 && days > 0; months++) {
376 }
else if (dt->months) {
387 crm_trace(
"%.4d-%.3d -> %.4d-%.2d-%.2d", dt->years, dt->days, dt->years, months, days);
413 h = dt->days + jan1 - 1;
414 *d = 1 + ((h - 1) % 7);
417 if (dt->days <= (8 - jan1) && jan1 > 4) {
419 year_num = dt->years - 1;
423 year_num = dt->years;
427 if (year_num == dt->years) {
428 int dmax = year_days(year_num);
429 int correction = 4 - *d;
431 if ((dmax - dt->days) < correction) {
432 crm_trace(
"year++, jan1=%d, i=%d vs. %d", jan1, dmax - dt->days, correction);
433 year_num = dt->years + 1;
439 if (year_num == dt->years) {
440 int j = dt->days + (7 - *d) + (jan1 - 1);
449 crm_trace(
"Converted %.4d-%.3d to %.4d-W%.2d-%d", dt->years, dt->days, *y, *w, *d);
454 #define s_if_plural(i) (((i) == 1)? "" : "s")
457 crm_duration_as_string(
crm_time_t *dt,
char *result)
462 offset += snprintf(result + offset,
DATE_MAX - offset,
"%4d year%s ",
466 offset += snprintf(result + offset,
DATE_MAX - offset,
"%2d month%s ",
470 offset += snprintf(result + offset,
DATE_MAX - offset,
"%2d day%s ",
474 if (((offset == 0) || (dt->seconds != 0))
475 && (dt->seconds > -60) && (dt->seconds < 60)) {
476 offset += snprintf(result + offset,
DATE_MAX - offset,
"%d second%s",
478 }
else if (dt->seconds) {
479 uint h = 0, m = 0, s = 0;
481 offset += snprintf(result + offset,
DATE_MAX - offset,
"%d seconds (",
483 crm_time_get_sec(dt->seconds, &h, &m, &s);
485 offset += snprintf(result + offset,
DATE_MAX - offset,
"%u hour%s%s",
489 offset += snprintf(result + offset,
DATE_MAX - offset,
"%u minute%s%s",
493 offset += snprintf(result + offset,
DATE_MAX - offset,
"%u second%s",
496 offset += snprintf(result + offset,
DATE_MAX - offset,
")");
506 char *result_copy = NULL;
510 if (date_time && date_time->offset
513 utc = crm_get_utc_time(date_time);
520 strcpy(result,
"<undefined time>");
527 crm_duration_as_string(date_time, result);
549 offset += snprintf(result + offset,
DATE_MAX - offset,
550 "%u-W%.2u-%u", y, w, d);
557 offset += snprintf(result + offset,
DATE_MAX - offset,
565 offset += snprintf(result + offset,
DATE_MAX - offset,
566 "%.4u-%.2u-%.2u", y, m, d);
572 uint h = 0, m = 0, s = 0;
575 offset += snprintf(result + offset,
DATE_MAX - offset,
" ");
579 offset += snprintf(result + offset,
DATE_MAX - offset,
580 "%.2u:%.2u:%.2u", h, m, s);
584 crm_time_get_sec(dt->offset, &h, &m, &s);
585 offset += snprintf(result + offset,
DATE_MAX - offset,
587 ((dt->offset < 0)?
'-' :
'+'), h, m);
589 offset += snprintf(result + offset,
DATE_MAX - offset,
"Z");
596 result_copy = strdup(result);
613 crm_time_parse_sec(
const char *time_str,
int *result)
623 rc = sscanf(time_str,
"%d:%d:%d", &hour, &minute, &second);
625 rc = sscanf(time_str,
"%2d%2d%2d", &hour, &minute, &second);
628 crm_err(
"%s is not a valid ISO 8601 time specification", time_str);
633 crm_trace(
"Got valid time: %.2d:%.2d:%.2d", hour, minute, second);
635 if ((hour == 24) && (minute == 0) && (second == 0)) {
637 }
else if (hour >= 24) {
638 crm_err(
"%s is not a valid ISO 8601 time specification "
639 "because %d is not a valid hour", time_str, hour);
644 crm_err(
"%s is not a valid ISO 8601 time specification "
645 "because %d is not a valid minute", time_str, minute);
650 crm_err(
"%s is not a valid ISO 8601 time specification "
651 "because %d is not a valid second", time_str, second);
656 *result = (hour *
HOUR_SECONDS) + (minute * 60) + second;
661 crm_time_parse_offset(
const char *offset_str,
int *offset)
665 if (offset_str == NULL) {
667 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
668 time_t now = time(NULL);
669 struct tm *now_tm = localtime(&now);
674 if (h_offset < 0 && m_offset < 0) {
675 m_offset = 0 - m_offset;
681 if (offset_str[0] ==
'Z') {
687 if ((offset_str[0] ==
'+') || (offset_str[0] ==
'-')
688 || isdigit((
int)offset_str[0])) {
690 gboolean negate = FALSE;
692 if (offset_str[0] ==
'-') {
696 if (crm_time_parse_sec(offset_str, offset) == FALSE) {
700 *offset = 0 - *offset;
717 crm_time_parse(
const char *time_str,
crm_time_t *a_time)
720 char *offset_s = NULL;
725 if (crm_time_parse_sec(time_str, &(a_time->seconds)) == FALSE) {
728 offset_s = strstr(time_str,
"Z");
729 if (offset_s == NULL) {
730 offset_s = strstr(time_str,
" ");
732 while (isspace(offset_s[0])) {
739 if (crm_time_parse_offset(offset_s, &(a_time->offset)) == FALSE) {
742 crm_time_get_sec(a_time->offset, &h, &m, &s);
743 crm_trace(
"Got tz: %c%2.d:%.2d", ((a_time->offset < 0)?
'-' :
'+'), h, m);
762 parse_date(
const char *date_str)
764 const char *time_s = NULL;
773 if ((date_str == NULL) || (date_str[0] ==
'\0')) {
774 crm_err(
"No ISO 8601 date/time specification given");
778 if ((date_str[0] ==
'T') || (date_str[2] ==
':')) {
781 if (date_str[0] ==
'T') {
782 time_s = date_str + 1;
791 if (!strncasecmp(
"epoch", date_str, 5)
792 && ((date_str[5] ==
'\0') || (date_str[5] ==
'/') || isspace(date_str[5]))) {
800 rc = sscanf(date_str,
"%d-%d-%d", &year, &month, &day);
803 rc = sscanf(date_str,
"%4d%2d%2d", &year, &month, &day);
807 crm_err(
"'%s' is not a valid ISO 8601 date/time specification "
808 "because '%d' is not a valid month", date_str, month);
811 crm_err(
"'%s' is not a valid ISO 8601 date/time specification "
812 "because '%d' is not a valid day of the month",
817 dt->days = get_ordinal_days(year, month, day);
818 crm_trace(
"Parsed Gregorian date '%.4d-%.3d' from date string '%s'",
819 year, dt->days, date_str);
825 rc = sscanf(date_str,
"%d-%d", &year, &day);
827 if (day > year_days(year)) {
828 crm_err(
"'%s' is not a valid ISO 8601 date/time specification "
829 "because '%d' is not a valid day of the year (max %d)",
830 date_str, day, year_days(year));
833 crm_trace(
"Parsed ordinal year %d and days %d from date string '%s'",
834 year, day, date_str);
841 rc = sscanf(date_str,
"%d-W%d-%d", &year, &week, &day);
844 crm_err(
"'%s' is not a valid ISO 8601 date/time specification "
845 "because '%d' is not a valid week of the year (max %d)",
848 }
else if (day < 1 || day > 7) {
849 crm_err(
"'%s' is not a valid ISO 8601 date/time specification "
850 "because '%d' is not a valid day of the week",
866 crm_trace(
"Got year %d (Jan 1 = %d), week %d, and day %d from date string '%s'",
867 year, jan1, week, day, date_str);
883 crm_err(
"'%s' is not a valid ISO 8601 date/time specification", date_str);
888 if (time_s == NULL) {
889 time_s = date_str + strspn(date_str,
"0123456789-W");
890 if ((time_s[0] ==
' ') || (time_s[0] ==
'T')) {
896 if ((time_s != NULL) && (crm_time_parse(time_s, dt) == FALSE)) {
902 crm_err(
"'%s' is not a valid ISO 8601 date/time specification",
919 parse_int(
const char *str,
int field_width,
int upper_bound,
int *result)
923 int intermediate = 0;
924 gboolean fraction = FALSE;
925 gboolean negate = FALSE;
932 if (str[offset] ==
'T') {
936 if (str[offset] ==
'.' || str[offset] ==
',') {
940 }
else if (str[offset] ==
'-') {
943 }
else if (str[offset] ==
'+' || str[offset] ==
':') {
947 for (; (fraction || lpc < field_width) && isdigit((
int)str[offset]); lpc++) {
949 intermediate = (str[offset] -
'0') / (10 ^ lpc);
952 intermediate = str[offset] -
'0';
954 *result += intermediate;
958 *result = (int)(*result * upper_bound);
960 }
else if (upper_bound > 0 && *result > upper_bound) {
961 *result = upper_bound;
964 *result = 0 - *result;
967 crm_trace(
"Found int: %d. Stopped at str[%d]='%c'", *result, lpc, str[lpc]);
987 gboolean is_time = FALSE;
990 if ((period_s == NULL) || (period_s[0] ==
'\0')) {
991 crm_err(
"No ISO 8601 time duration given");
994 if (period_s[0] !=
'P') {
995 crm_err(
"'%s' is not a valid ISO 8601 time duration "
996 "because it does not start with a 'P'", period_s);
999 if ((period_s[1] ==
'\0') || isspace(period_s[1])) {
1000 crm_err(
"'%s' is not a valid ISO 8601 time duration "
1001 "because nothing follows 'P'", period_s);
1006 diff->duration = TRUE;
1008 for (
const char *current = period_s + 1;
1009 current[0] && (current[0] !=
'/') && !isspace(current[0]);
1014 if (current[0] ==
'T') {
1024 rc = parse_int(current, 10, 0, &an_int);
1026 crm_err(
"'%s' is not a valid ISO 8601 time duration "
1027 "because no integer at '%s'", period_s, current);
1033 switch (current[0]) {
1035 diff->years = an_int;
1040 diff->seconds += an_int * 60;
1042 diff->months = an_int;
1046 diff->days += an_int * 7;
1049 diff->days += an_int;
1055 diff->seconds += an_int;
1058 crm_err(
"'%s' is not a valid ISO 8601 time duration "
1059 "because no units after %d", period_s, an_int);
1062 crm_err(
"'%s' is not a valid ISO 8601 time duration "
1063 "because '%c' is not a valid time unit",
1064 period_s, current[0]);
1070 crm_err(
"'%s' is not a valid ISO 8601 time duration "
1071 "because no amounts and units given", period_s);
1095 const char *original = period_str;
1098 if ((period_str == NULL) || (period_str[0] ==
'\0')) {
1099 crm_err(
"No ISO 8601 time period given");
1107 if (period_str[0] ==
'P') {
1109 if (period->
diff == NULL) {
1113 period->
start = parse_date(period_str);
1114 if (period->
start == NULL) {
1119 period_str = strstr(original,
"/");
1122 if (period_str[0] ==
'P') {
1123 if (period->
diff != NULL) {
1124 crm_err(
"'%s' is not a valid ISO 8601 time period "
1125 "because it has two durations",
1130 if (period->
diff == NULL) {
1134 period->
end = parse_date(period_str);
1135 if (period->
end == NULL) {
1140 }
else if (period->
diff != NULL) {
1146 crm_err(
"'%s' is not a valid ISO 8601 time period "
1147 "because it has no duration or ending time",
1152 if (period->
start == NULL) {
1155 }
else if (period->
end == NULL) {
1160 crm_err(
"'%s' is not a valid ISO 8601 time period "
1161 "because the start is invalid", period_str);
1165 crm_err(
"'%s' is not a valid ISO 8601 time period "
1166 "because the end is invalid", period_str);
1197 crm_trace(
"target=%p, source=%p", target, source);
1199 CRM_CHECK(target != NULL && source != NULL,
return);
1201 target->years = source->years;
1202 target->days = source->days;
1203 target->months = source->months;
1204 target->seconds = source->seconds;
1205 target->offset = source->offset;
1214 ha_set_tm_time(
crm_time_t * target,
struct tm *source)
1223 target->seconds = 0;
1225 target->duration = FALSE;
1227 if (source->tm_year > 0) {
1229 target->years = 1900 + source->tm_year;
1232 if (source->tm_yday >= 0) {
1234 target->days = 1 + source->tm_yday;
1237 if (source->tm_hour >= 0) {
1240 if (source->tm_min >= 0) {
1241 target->seconds += 60 * source->tm_min;
1243 if (source->tm_sec >= 0) {
1244 target->seconds += source->tm_sec;
1250 crm_trace(
"Offset (s): %ld, offset (hh:mm): %.2d:%.2d",
GMTOFF(source), h_offset, m_offset);
1253 target->offset += 60 * m_offset;
1259 ha_set_tm_time(target, localtime(source));
1268 if ((dt == NULL) || (value == NULL)) {
1276 utc = crm_get_utc_time(value);
1282 answer->years += utc->years;
1297 if ((dt == NULL) || (value == NULL)) {
1302 utc = crm_get_utc_time(value);
1307 answer = crm_get_utc_time(dt);
1308 if (answer == NULL) {
1312 answer->duration = TRUE;
1314 answer->years -= utc->years;
1315 if(utc->months != 0) {
1331 if ((dt == NULL) || (value == NULL)) {
1336 utc = crm_get_utc_time(value);
1343 answer->years -= utc->years;
1344 if(utc->months != 0) {
1364 && (dt->days > 0) && (dt->days <= year_days(dt->years))
1365 && (dt->seconds >= 0) && (dt->seconds <
DAY_SECONDS);
1368 #define do_cmp_field(l, r, field) \
1370 if(l->field > r->field) { \
1371 crm_trace("%s: %d > %d", \
1372 #field, l->field, r->field); \
1374 } else if(l->field < r->field) { \
1375 crm_trace("%s: %d < %d", \
1376 #field, l->field, r->field); \
1388 if ((t1 == NULL) && (t2 == NULL)) {
1390 }
else if (t1 == NULL) {
1392 }
else if (t2 == NULL) {
1416 crm_trace(
"Adding %d seconds to %d (max=%d)",
1418 a_time->seconds += extra;
1423 if (a_time->seconds < 0) {
1434 int lower_bound = 1;
1437 crm_trace(
"Adding %d days to %.4d-%.3d", extra, a_time->years, a_time->days);
1439 a_time->days += extra;
1440 while (a_time->days > ydays) {
1442 a_time->days -= ydays;
1446 if(a_time->duration) {
1450 while (a_time->days < lower_bound) {
1460 uint32_t y, m, d, dmax;
1463 crm_trace(
"Adding %d months to %.4d-%.2d-%.2d", extra, y, m, d);
1466 for (lpc = extra; lpc > 0; lpc--) {
1474 for (lpc = -extra; lpc > 0; lpc--) {
1489 crm_trace(
"Calculated %.4d-%.2d-%.2d", y, m, d);
1492 a_time->days = get_ordinal_days(y, m, d);
1495 crm_trace(
"Got %.4d-%.2d-%.2d", y, m, d);
1519 a_time->years += extra;
1523 ha_get_tm_time(
struct tm *target,
crm_time_t *source)
1525 *target = (
struct tm) {
1526 .tm_year = source->years - 1900,
1527 .tm_mday = source->days,
1528 .tm_sec = source->seconds % 60,
1529 .tm_min = ( source->seconds / 60 ) % 60,
1534 .tm_gmtoff = source->offset
1550 .months = dt->months,
1552 .seconds = dt->seconds,
1553 .offset = dt->offset,
1554 .duration = dt->duration
1566 .years = hr_dt->
years,
1568 .days = hr_dt->
days,
1593 struct timeval tv_now;
1596 if (gettimeofday(&tv_now, NULL) == 0) {
1602 dt = parse_date(date_time);
1619 int max = 128, scanned_pos = 0, printed_pos = 0, fmt_pos = 0,
1620 date_len = 0, nano_digits = 0;
1621 char nano_s[10], date_s[max+1], nanofmt_s[5] =
"%", *tmp_fmt_s;
1629 ha_get_tm_time(&tm, &dt);
1630 sprintf(nano_s,
"%06d000", hr_dt->
useconds);
1632 while ((format[scanned_pos]) !=
'\0') {
1633 mark_s = strchr(&format[scanned_pos],
'%');
1637 fmt_pos = mark_s - format;
1638 while ((format[fmt_pos+fmt_len] !=
'\0') &&
1639 (format[fmt_pos+fmt_len] >=
'0') &&
1640 (format[fmt_pos+fmt_len] <=
'9')) {
1643 scanned_pos = fmt_pos + fmt_len + 1;
1644 if (format[fmt_pos+fmt_len] ==
'N') {
1645 nano_digits = atoi(&format[fmt_pos+1]);
1646 nano_digits = (nano_digits > 6)?6:nano_digits;
1647 nano_digits = (nano_digits < 0)?0:nano_digits;
1648 sprintf(&nanofmt_s[1],
".%ds", nano_digits);
1650 if (format[scanned_pos] !=
'\0') {
1653 fmt_pos = scanned_pos;
1656 scanned_pos = strlen(format);
1657 fmt_pos = scanned_pos;
1659 tmp_fmt_s =
strndup(&format[printed_pos], fmt_pos - printed_pos);
1660 #ifdef GCC_FORMAT_NONLITERAL_CHECKING_ENABLED
1661 #pragma GCC diagnostic push
1662 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
1664 date_len += strftime(&date_s[date_len], max-date_len, tmp_fmt_s, &tm);
1665 #ifdef GCC_FORMAT_NONLITERAL_CHECKING_ENABLED
1666 #pragma GCC diagnostic pop
1668 printed_pos = scanned_pos;
1671 #ifdef GCC_FORMAT_NONLITERAL_CHECKING_ENABLED
1672 #pragma GCC diagnostic push
1673 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
1675 date_len += snprintf(&date_s[date_len], max-date_len,
1677 #ifdef GCC_FORMAT_NONLITERAL_CHECKING_ENABLED
1678 #pragma GCC diagnostic pop
1684 return (date_len == 0)?NULL:strdup(date_s);
1701 char *since_epoch = NULL;
1704 time_t a_time = time(NULL);
1706 if (a_time == (time_t) -1) {
1709 since_epoch = ctime(&a_time);
1712 since_epoch = ctime(when);
1715 if (since_epoch == NULL) {