mirror of
https://github.com/systemd/systemd.git
synced 2024-10-29 21:55:36 +03:00
Merge pull request #26409 from yuwata/parse-timestamp
fix parse_timestamp()
This commit is contained in:
commit
e1e6d5f353
@ -171,6 +171,8 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
|
|||||||
dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u) {
|
dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u) {
|
||||||
usec_t nowm;
|
usec_t nowm;
|
||||||
|
|
||||||
|
assert(ts);
|
||||||
|
|
||||||
if (u == USEC_INFINITY) {
|
if (u == USEC_INFINITY) {
|
||||||
ts->realtime = ts->monotonic = USEC_INFINITY;
|
ts->realtime = ts->monotonic = USEC_INFINITY;
|
||||||
return ts;
|
return ts;
|
||||||
@ -183,6 +185,7 @@ dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) {
|
usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) {
|
||||||
|
assert(ts);
|
||||||
|
|
||||||
switch (clock) {
|
switch (clock) {
|
||||||
|
|
||||||
@ -228,7 +231,7 @@ nsec_t timespec_load_nsec(const struct timespec *ts) {
|
|||||||
return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec;
|
return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct timespec *timespec_store(struct timespec *ts, usec_t u) {
|
struct timespec *timespec_store(struct timespec *ts, usec_t u) {
|
||||||
assert(ts);
|
assert(ts);
|
||||||
|
|
||||||
if (u == USEC_INFINITY ||
|
if (u == USEC_INFINITY ||
|
||||||
@ -244,7 +247,7 @@ struct timespec *timespec_store(struct timespec *ts, usec_t u) {
|
|||||||
return ts;
|
return ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n) {
|
struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n) {
|
||||||
assert(ts);
|
assert(ts);
|
||||||
|
|
||||||
if (n == NSEC_INFINITY ||
|
if (n == NSEC_INFINITY ||
|
||||||
@ -422,6 +425,8 @@ char* format_timestamp_relative_full(char *buf, size_t l, usec_t t, bool implici
|
|||||||
const char *s;
|
const char *s;
|
||||||
usec_t n, d;
|
usec_t n, d;
|
||||||
|
|
||||||
|
assert(buf);
|
||||||
|
|
||||||
if (!timestamp_is_set(t))
|
if (!timestamp_is_set(t))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@ -605,7 +610,14 @@ char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
|
static int parse_timestamp_impl(
|
||||||
|
const char *t,
|
||||||
|
bool with_tz,
|
||||||
|
bool utc,
|
||||||
|
int isdst,
|
||||||
|
long gmtoff,
|
||||||
|
usec_t *ret) {
|
||||||
|
|
||||||
static const struct {
|
static const struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
const int nr;
|
const int nr;
|
||||||
@ -626,12 +638,12 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
|
|||||||
{ "Sat", 6 },
|
{ "Sat", 6 },
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *k, *utc = NULL, *tzn = NULL;
|
usec_t usec, plus = 0, minus = 0;
|
||||||
|
int r, weekday = -1;
|
||||||
|
unsigned fractional = 0;
|
||||||
|
const char *k;
|
||||||
struct tm tm, copy;
|
struct tm tm, copy;
|
||||||
time_t x;
|
time_t sec;
|
||||||
usec_t x_usec, plus = 0, minus = 0, ret;
|
|
||||||
int r, weekday = -1, dst = -1;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
/* Allowed syntaxes:
|
/* Allowed syntaxes:
|
||||||
*
|
*
|
||||||
@ -652,98 +664,65 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
|
|||||||
assert(t);
|
assert(t);
|
||||||
|
|
||||||
if (t[0] == '@' && !with_tz)
|
if (t[0] == '@' && !with_tz)
|
||||||
return parse_sec(t + 1, usec);
|
return parse_sec(t + 1, ret);
|
||||||
|
|
||||||
ret = now(CLOCK_REALTIME);
|
usec = now(CLOCK_REALTIME);
|
||||||
|
|
||||||
if (!with_tz) {
|
if (!with_tz) {
|
||||||
if (streq(t, "now"))
|
if (streq(t, "now"))
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
||||||
else if (t[0] == '+') {
|
if (t[0] == '+') {
|
||||||
r = parse_sec(t+1, &plus);
|
r = parse_sec(t+1, &plus);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
goto finish;
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (t[0] == '-') {
|
if (t[0] == '-') {
|
||||||
r = parse_sec(t+1, &minus);
|
r = parse_sec(t+1, &minus);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
goto finish;
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
} else if ((k = endswith(t, " ago"))) {
|
if ((k = endswith(t, " ago"))) {
|
||||||
t = strndupa_safe(t, k - t);
|
_cleanup_free_ char *buf = NULL;
|
||||||
|
|
||||||
r = parse_sec(t, &minus);
|
buf = strndup(t, k - t);
|
||||||
if (r < 0)
|
if (!buf)
|
||||||
return r;
|
return -ENOMEM;
|
||||||
|
|
||||||
goto finish;
|
r = parse_sec(buf, &minus);
|
||||||
|
|
||||||
} else if ((k = endswith(t, " left"))) {
|
|
||||||
t = strndupa_safe(t, k - t);
|
|
||||||
|
|
||||||
r = parse_sec(t, &plus);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* See if the timestamp is suffixed with UTC */
|
if ((k = endswith(t, " left"))) {
|
||||||
utc = endswith_no_case(t, " UTC");
|
_cleanup_free_ char *buf = NULL;
|
||||||
if (utc)
|
|
||||||
t = strndupa_safe(t, utc - t);
|
|
||||||
else {
|
|
||||||
const char *e = NULL;
|
|
||||||
int j;
|
|
||||||
|
|
||||||
tzset();
|
buf = strndup(t, k - t);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
/* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note
|
r = parse_sec(buf, &plus);
|
||||||
* that we only support the local timezones here, nothing else. Not because we
|
if (r < 0)
|
||||||
* wouldn't want to, but simply because there are no nice APIs available to cover
|
return r;
|
||||||
* this. By accepting the local time zone strings, we make sure that all timestamps
|
|
||||||
* written by format_timestamp() can be parsed correctly, even though we don't
|
|
||||||
* support arbitrary timezone specifications. */
|
|
||||||
|
|
||||||
for (j = 0; j <= 1; j++) {
|
goto finish;
|
||||||
|
|
||||||
if (isempty(tzname[j]))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
e = endswith_no_case(t, tzname[j]);
|
|
||||||
if (!e)
|
|
||||||
continue;
|
|
||||||
if (e == t)
|
|
||||||
continue;
|
|
||||||
if (e[-1] != ' ')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IN_SET(j, 0, 1)) {
|
|
||||||
/* Found one of the two timezones specified. */
|
|
||||||
t = strndupa_safe(t, e - t - 1);
|
|
||||||
dst = j;
|
|
||||||
tzn = tzname[j];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
x = (time_t) (ret / USEC_PER_SEC);
|
sec = (time_t) (usec / USEC_PER_SEC);
|
||||||
x_usec = 0;
|
|
||||||
|
|
||||||
if (!localtime_or_gmtime_r(&x, &tm, utc))
|
if (!localtime_or_gmtime_r(&sec, &tm, utc))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
tm.tm_isdst = dst;
|
tm.tm_isdst = isdst;
|
||||||
if (!with_tz && tzn)
|
|
||||||
tm.tm_zone = tzn;
|
|
||||||
|
|
||||||
if (streq(t, "today")) {
|
if (streq(t, "today")) {
|
||||||
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
|
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
|
||||||
@ -760,18 +739,13 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
|
|||||||
goto from_tm;
|
goto from_tm;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < ELEMENTSOF(day_nr); i++) {
|
for (size_t i = 0; i < ELEMENTSOF(day_nr); i++) {
|
||||||
size_t skip;
|
k = startswith_no_case(t, day_nr[i].name);
|
||||||
|
if (!k || *k != ' ')
|
||||||
if (!startswith_no_case(t, day_nr[i].name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
skip = strlen(day_nr[i].name);
|
|
||||||
if (t[skip] != ' ')
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
weekday = day_nr[i].nr;
|
weekday = day_nr[i].nr;
|
||||||
t += skip + 1;
|
t = k + 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -850,65 +824,149 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
parse_usec:
|
parse_usec:
|
||||||
{
|
k++;
|
||||||
unsigned add;
|
r = parse_fractional_part_u(&k, 6, &fractional);
|
||||||
|
if (r < 0)
|
||||||
k++;
|
return -EINVAL;
|
||||||
r = parse_fractional_part_u(&k, 6, &add);
|
if (*k != '\0')
|
||||||
if (r < 0)
|
return -EINVAL;
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (*k)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
x_usec = add;
|
|
||||||
}
|
|
||||||
|
|
||||||
from_tm:
|
from_tm:
|
||||||
|
assert(plus == 0);
|
||||||
|
assert(minus == 0);
|
||||||
|
|
||||||
if (weekday >= 0 && tm.tm_wday != weekday)
|
if (weekday >= 0 && tm.tm_wday != weekday)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
x = mktime_or_timegm(&tm, utc);
|
if (gmtoff < 0) {
|
||||||
if (x < 0)
|
plus = -gmtoff * USEC_PER_SEC;
|
||||||
|
|
||||||
|
/* If gmtoff is negative, the string maye be too old to be parsed as UTC.
|
||||||
|
* E.g. 1969-12-31 23:00:00 -06 == 1970-01-01 05:00:00 UTC
|
||||||
|
* We assumed that gmtoff is in the range of -24:00…+24:00, hence the only date we need to
|
||||||
|
* handle here is 1969-12-31. So, let's shift the date with one day, then subtract the shift
|
||||||
|
* later. */
|
||||||
|
if (tm.tm_year == 69 && tm.tm_mon == 11 && tm.tm_mday == 31) {
|
||||||
|
/* Thu 1970-01-01-00:00:00 */
|
||||||
|
tm.tm_year = 70;
|
||||||
|
tm.tm_mon = 0;
|
||||||
|
tm.tm_mday = 1;
|
||||||
|
tm.tm_wday = 4;
|
||||||
|
tm.tm_yday = 0;
|
||||||
|
minus = USEC_PER_DAY;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
minus = gmtoff * USEC_PER_SEC;
|
||||||
|
|
||||||
|
sec = mktime_or_timegm(&tm, utc);
|
||||||
|
if (sec < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = (usec_t) x * USEC_PER_SEC + x_usec;
|
usec = usec_add(sec * USEC_PER_SEC, fractional);
|
||||||
if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
if (ret + plus < ret) /* overflow? */
|
usec = usec_add(usec, plus);
|
||||||
return -EINVAL;
|
|
||||||
ret += plus;
|
if (usec < minus)
|
||||||
if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX)
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (ret >= minus)
|
usec = usec_sub_unsigned(usec, minus);
|
||||||
ret -= minus;
|
|
||||||
else
|
if (usec > USEC_TIMESTAMP_FORMATTABLE_MAX)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (usec)
|
if (ret)
|
||||||
*usec = ret;
|
*ret = usec;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int parse_timestamp_with_tz(const char *t, size_t len, bool utc, int isdst, long gmtoff, usec_t *ret) {
|
||||||
|
_cleanup_free_ char *buf = NULL;
|
||||||
|
|
||||||
|
assert(t);
|
||||||
|
assert(len > 0);
|
||||||
|
|
||||||
|
buf = strndup(t, len);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
return parse_timestamp_impl(buf, /* with_tz = */ true, utc, isdst, gmtoff, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_timestamp_maybe_with_tz(const char *t, size_t len, bool valid_tz, usec_t *ret) {
|
||||||
|
assert(t);
|
||||||
|
assert(len > 0);
|
||||||
|
|
||||||
|
tzset();
|
||||||
|
|
||||||
|
for (int j = 0; j <= 1; j++) {
|
||||||
|
if (isempty(tzname[j]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!streq(t + len + 1, tzname[j]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* The specified timezone matches tzname[] of the local timezone. */
|
||||||
|
return parse_timestamp_with_tz(t, len, /* utc = */ false, /* isdst = */ j, /* gmtoff = */ 0, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid_tz)
|
||||||
|
/* We know that the specified timezone is a valid zoneinfo (e.g. Asia/Tokyo). So, simply drop
|
||||||
|
* the timezone and parse the remaining string as a local time. */
|
||||||
|
return parse_timestamp_with_tz(t, len, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret);
|
||||||
|
|
||||||
|
return parse_timestamp_impl(t, /* with_tz = */ false, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret);
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct ParseTimestampResult {
|
typedef struct ParseTimestampResult {
|
||||||
usec_t usec;
|
usec_t usec;
|
||||||
int return_value;
|
int return_value;
|
||||||
} ParseTimestampResult;
|
} ParseTimestampResult;
|
||||||
|
|
||||||
int parse_timestamp(const char *t, usec_t *usec) {
|
int parse_timestamp(const char *t, usec_t *ret) {
|
||||||
char *last_space, *tz = NULL;
|
|
||||||
ParseTimestampResult *shared, tmp;
|
ParseTimestampResult *shared, tmp;
|
||||||
|
const char *k, *tz, *space;
|
||||||
|
struct tm tm;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
last_space = strrchr(t, ' ');
|
assert(t);
|
||||||
if (last_space != NULL && timezone_is_valid(last_space + 1, LOG_DEBUG))
|
|
||||||
tz = last_space + 1;
|
|
||||||
|
|
||||||
if (!tz || endswith_no_case(t, " UTC"))
|
space = strrchr(t, ' ');
|
||||||
return parse_timestamp_impl(t, usec, false);
|
if (!space)
|
||||||
|
return parse_timestamp_impl(t, /* with_tz = */ false, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret);
|
||||||
|
|
||||||
|
/* The string starts with space. */
|
||||||
|
if (space == t)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Shortcut, parse the string as UTC. */
|
||||||
|
if (streq(space + 1, "UTC"))
|
||||||
|
return parse_timestamp_with_tz(t, space - t, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret);
|
||||||
|
|
||||||
|
/* If the timezone is compatible with RFC-822/ISO 8601 (e.g. +06, or -03:00) then parse the string as
|
||||||
|
* UTC and shift the result. */
|
||||||
|
k = strptime(space + 1, "%z", &tm);
|
||||||
|
if (k && *k == '\0') {
|
||||||
|
/* glibc accepts gmtoff more than 24 hours, but we refuse it. */
|
||||||
|
if ((usec_t) labs(tm.tm_gmtoff) > USEC_PER_DAY / USEC_PER_SEC)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return parse_timestamp_with_tz(t, space - t, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the last word is not a timezone file (e.g. Asia/Tokyo), then let's check if it matches
|
||||||
|
* tzname[] of the local timezone, e.g. JST or CEST. */
|
||||||
|
if (!timezone_is_valid(space + 1, LOG_DEBUG))
|
||||||
|
return parse_timestamp_maybe_with_tz(t, space - t, /* valid_tz = */ false, ret);
|
||||||
|
|
||||||
|
/* Shortcut. If the current $TZ is equivalent to the specified timezone, it is not necessary to fork
|
||||||
|
* the process. */
|
||||||
|
tz = getenv("TZ");
|
||||||
|
if (tz && *tz == ':' && streq(tz + 1, space + 1))
|
||||||
|
return parse_timestamp_maybe_with_tz(t, space - t, /* valid_tz = */ true, ret);
|
||||||
|
|
||||||
|
/* Otherwise, to avoid polluting the current environment variables, let's fork the process and set
|
||||||
|
* the specified timezone in the child process. */
|
||||||
|
|
||||||
shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
|
shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
|
||||||
if (shared == MAP_FAILED)
|
if (shared == MAP_FAILED)
|
||||||
@ -920,11 +978,10 @@ int parse_timestamp(const char *t, usec_t *usec) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
bool with_tz = true;
|
const char *colon_tz;
|
||||||
char *colon_tz;
|
|
||||||
|
|
||||||
/* tzset(3) says $TZ should be prefixed with ":" if we reference timezone files */
|
/* tzset(3) says $TZ should be prefixed with ":" if we reference timezone files */
|
||||||
colon_tz = strjoina(":", tz);
|
colon_tz = strjoina(":", space + 1);
|
||||||
|
|
||||||
if (setenv("TZ", colon_tz, 1) != 0) {
|
if (setenv("TZ", colon_tz, 1) != 0) {
|
||||||
shared->return_value = negative_errno();
|
shared->return_value = negative_errno();
|
||||||
@ -933,15 +990,7 @@ int parse_timestamp(const char *t, usec_t *usec) {
|
|||||||
|
|
||||||
tzset();
|
tzset();
|
||||||
|
|
||||||
/* If there is a timezone that matches the tzname fields, leave the parsing to the implementation.
|
shared->return_value = parse_timestamp_maybe_with_tz(t, space - t, /* valid_tz = */ true, &shared->usec);
|
||||||
* Otherwise just cut it off. */
|
|
||||||
with_tz = !STR_IN_SET(tz, tzname[0], tzname[1]);
|
|
||||||
|
|
||||||
/* Cut off the timezone if we don't need it. */
|
|
||||||
if (with_tz)
|
|
||||||
t = strndupa_safe(t, last_space - t);
|
|
||||||
|
|
||||||
shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz);
|
|
||||||
|
|
||||||
_exit(EXIT_SUCCESS);
|
_exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
@ -950,13 +999,13 @@ int parse_timestamp(const char *t, usec_t *usec) {
|
|||||||
if (munmap(shared, sizeof *shared) != 0)
|
if (munmap(shared, sizeof *shared) != 0)
|
||||||
return negative_errno();
|
return negative_errno();
|
||||||
|
|
||||||
if (tmp.return_value == 0 && usec)
|
if (tmp.return_value == 0 && ret)
|
||||||
*usec = tmp.usec;
|
*ret = tmp.usec;
|
||||||
|
|
||||||
return tmp.return_value;
|
return tmp.return_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* extract_multiplier(const char *p, usec_t *multiplier) {
|
static const char* extract_multiplier(const char *p, usec_t *ret) {
|
||||||
static const struct {
|
static const struct {
|
||||||
const char *suffix;
|
const char *suffix;
|
||||||
usec_t usec;
|
usec_t usec;
|
||||||
@ -992,12 +1041,15 @@ static const char* extract_multiplier(const char *p, usec_t *multiplier) {
|
|||||||
{ "µs", 1ULL },
|
{ "µs", 1ULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
assert(p);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
for (size_t i = 0; i < ELEMENTSOF(table); i++) {
|
for (size_t i = 0; i < ELEMENTSOF(table); i++) {
|
||||||
char *e;
|
char *e;
|
||||||
|
|
||||||
e = startswith(p, table[i].suffix);
|
e = startswith(p, table[i].suffix);
|
||||||
if (e) {
|
if (e) {
|
||||||
*multiplier = table[i].usec;
|
*ret = table[i].usec;
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1005,9 +1057,9 @@ static const char* extract_multiplier(const char *p, usec_t *multiplier) {
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
|
int parse_time(const char *t, usec_t *ret, usec_t default_unit) {
|
||||||
const char *p, *s;
|
const char *p, *s;
|
||||||
usec_t r = 0;
|
usec_t usec = 0;
|
||||||
bool something = false;
|
bool something = false;
|
||||||
|
|
||||||
assert(t);
|
assert(t);
|
||||||
@ -1022,8 +1074,8 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
|
|||||||
if (*s != 0)
|
if (*s != 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (usec)
|
if (ret)
|
||||||
*usec = USEC_INFINITY;
|
*ret = USEC_INFINITY;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1070,10 +1122,10 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
|
|||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
|
|
||||||
k = (usec_t) l * multiplier;
|
k = (usec_t) l * multiplier;
|
||||||
if (k >= USEC_INFINITY - r)
|
if (k >= USEC_INFINITY - usec)
|
||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
|
|
||||||
r += k;
|
usec += k;
|
||||||
|
|
||||||
something = true;
|
something = true;
|
||||||
|
|
||||||
@ -1083,10 +1135,10 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
|
|||||||
|
|
||||||
for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) {
|
for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) {
|
||||||
k = (usec_t) (*b - '0') * m;
|
k = (usec_t) (*b - '0') * m;
|
||||||
if (k >= USEC_INFINITY - r)
|
if (k >= USEC_INFINITY - usec)
|
||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
|
|
||||||
r += k;
|
usec += k;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
|
/* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
|
||||||
@ -1095,13 +1147,13 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usec)
|
if (ret)
|
||||||
*usec = r;
|
*ret = usec;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int parse_sec(const char *t, usec_t *usec) {
|
int parse_sec(const char *t, usec_t *ret) {
|
||||||
return parse_time(t, usec, USEC_PER_SEC);
|
return parse_time(t, ret, USEC_PER_SEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
int parse_sec_fix_0(const char *t, usec_t *ret) {
|
int parse_sec_fix_0(const char *t, usec_t *ret) {
|
||||||
@ -1120,6 +1172,9 @@ int parse_sec_fix_0(const char *t, usec_t *ret) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int parse_sec_def_infinity(const char *t, usec_t *ret) {
|
int parse_sec_def_infinity(const char *t, usec_t *ret) {
|
||||||
|
assert(t);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
t += strspn(t, WHITESPACE);
|
t += strspn(t, WHITESPACE);
|
||||||
if (isempty(t)) {
|
if (isempty(t)) {
|
||||||
*ret = USEC_INFINITY;
|
*ret = USEC_INFINITY;
|
||||||
@ -1128,7 +1183,7 @@ int parse_sec_def_infinity(const char *t, usec_t *ret) {
|
|||||||
return parse_sec(t, ret);
|
return parse_sec(t, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* extract_nsec_multiplier(const char *p, nsec_t *multiplier) {
|
static const char* extract_nsec_multiplier(const char *p, nsec_t *ret) {
|
||||||
static const struct {
|
static const struct {
|
||||||
const char *suffix;
|
const char *suffix;
|
||||||
nsec_t nsec;
|
nsec_t nsec;
|
||||||
@ -1168,12 +1223,15 @@ static const char* extract_nsec_multiplier(const char *p, nsec_t *multiplier) {
|
|||||||
};
|
};
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
|
assert(p);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
for (i = 0; i < ELEMENTSOF(table); i++) {
|
for (i = 0; i < ELEMENTSOF(table); i++) {
|
||||||
char *e;
|
char *e;
|
||||||
|
|
||||||
e = startswith(p, table[i].suffix);
|
e = startswith(p, table[i].suffix);
|
||||||
if (e) {
|
if (e) {
|
||||||
*multiplier = table[i].nsec;
|
*ret = table[i].nsec;
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1181,13 +1239,13 @@ static const char* extract_nsec_multiplier(const char *p, nsec_t *multiplier) {
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
int parse_nsec(const char *t, nsec_t *nsec) {
|
int parse_nsec(const char *t, nsec_t *ret) {
|
||||||
const char *p, *s;
|
const char *p, *s;
|
||||||
nsec_t r = 0;
|
nsec_t nsec = 0;
|
||||||
bool something = false;
|
bool something = false;
|
||||||
|
|
||||||
assert(t);
|
assert(t);
|
||||||
assert(nsec);
|
assert(ret);
|
||||||
|
|
||||||
p = t;
|
p = t;
|
||||||
|
|
||||||
@ -1198,7 +1256,7 @@ int parse_nsec(const char *t, nsec_t *nsec) {
|
|||||||
if (*s != 0)
|
if (*s != 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
*nsec = NSEC_INFINITY;
|
*ret = NSEC_INFINITY;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1245,10 +1303,10 @@ int parse_nsec(const char *t, nsec_t *nsec) {
|
|||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
|
|
||||||
k = (nsec_t) l * multiplier;
|
k = (nsec_t) l * multiplier;
|
||||||
if (k >= NSEC_INFINITY - r)
|
if (k >= NSEC_INFINITY - nsec)
|
||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
|
|
||||||
r += k;
|
nsec += k;
|
||||||
|
|
||||||
something = true;
|
something = true;
|
||||||
|
|
||||||
@ -1258,10 +1316,10 @@ int parse_nsec(const char *t, nsec_t *nsec) {
|
|||||||
|
|
||||||
for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) {
|
for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) {
|
||||||
k = (nsec_t) (*b - '0') * m;
|
k = (nsec_t) (*b - '0') * m;
|
||||||
if (k >= NSEC_INFINITY - r)
|
if (k >= NSEC_INFINITY - nsec)
|
||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
|
|
||||||
r += k;
|
nsec += k;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
|
/* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
|
||||||
@ -1270,7 +1328,7 @@ int parse_nsec(const char *t, nsec_t *nsec) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*nsec = r;
|
*ret = nsec;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1321,6 +1379,8 @@ static int get_timezones_from_tzdata_zi(char ***ret) {
|
|||||||
_cleanup_strv_free_ char **zones = NULL;
|
_cleanup_strv_free_ char **zones = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
f = fopen("/usr/share/zoneinfo/tzdata.zi", "re");
|
f = fopen("/usr/share/zoneinfo/tzdata.zi", "re");
|
||||||
if (!f)
|
if (!f)
|
||||||
return -errno;
|
return -errno;
|
||||||
@ -1478,6 +1538,8 @@ int get_timezone(char **ret) {
|
|||||||
char *z;
|
char *z;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
r = readlink_malloc("/etc/localtime", &t);
|
r = readlink_malloc("/etc/localtime", &t);
|
||||||
if (r == -ENOENT) {
|
if (r == -ENOENT) {
|
||||||
/* If the symlink does not exist, assume "UTC", like glibc does */
|
/* If the symlink does not exist, assume "UTC", like glibc does */
|
||||||
@ -1507,10 +1569,15 @@ int get_timezone(char **ret) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
time_t mktime_or_timegm(struct tm *tm, bool utc) {
|
time_t mktime_or_timegm(struct tm *tm, bool utc) {
|
||||||
|
assert(tm);
|
||||||
|
|
||||||
return utc ? timegm(tm) : mktime(tm);
|
return utc ? timegm(tm) : mktime(tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
|
struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
|
||||||
|
assert(t);
|
||||||
|
assert(tm);
|
||||||
|
|
||||||
return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
|
return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1610,14 +1677,14 @@ int time_change_fd(void) {
|
|||||||
|
|
||||||
static const char* const timestamp_style_table[_TIMESTAMP_STYLE_MAX] = {
|
static const char* const timestamp_style_table[_TIMESTAMP_STYLE_MAX] = {
|
||||||
[TIMESTAMP_PRETTY] = "pretty",
|
[TIMESTAMP_PRETTY] = "pretty",
|
||||||
[TIMESTAMP_US] = "us",
|
[TIMESTAMP_US] = "us",
|
||||||
[TIMESTAMP_UTC] = "utc",
|
[TIMESTAMP_UTC] = "utc",
|
||||||
[TIMESTAMP_US_UTC] = "us+utc",
|
[TIMESTAMP_US_UTC] = "us+utc",
|
||||||
[TIMESTAMP_UNIX] = "unix",
|
[TIMESTAMP_UNIX] = "unix",
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Use the macro for enum → string to allow for aliases */
|
/* Use the macro for enum → string to allow for aliases */
|
||||||
_DEFINE_STRING_TABLE_LOOKUP_TO_STRING(timestamp_style, TimestampStyle,);
|
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(timestamp_style, TimestampStyle);
|
||||||
|
|
||||||
/* For the string → enum mapping we use the generic implementation, but also support two aliases */
|
/* For the string → enum mapping we use the generic implementation, but also support two aliases */
|
||||||
TimestampStyle timestamp_style_from_string(const char *s) {
|
TimestampStyle timestamp_style_from_string(const char *s) {
|
||||||
|
@ -66,7 +66,6 @@ typedef enum TimestampStyle {
|
|||||||
/* We assume a maximum timezone length of 6. TZNAME_MAX is not defined on Linux, but glibc internally initializes this
|
/* We assume a maximum timezone length of 6. TZNAME_MAX is not defined on Linux, but glibc internally initializes this
|
||||||
* to 6. Let's rely on that. */
|
* to 6. Let's rely on that. */
|
||||||
#define FORMAT_TIMESTAMP_MAX (3U+1U+10U+1U+8U+1U+6U+1U+6U+1U)
|
#define FORMAT_TIMESTAMP_MAX (3U+1U+10U+1U+8U+1U+6U+1U+6U+1U)
|
||||||
#define FORMAT_TIMESTAMP_WIDTH 28U /* when outputting, assume this width */
|
|
||||||
#define FORMAT_TIMESTAMP_RELATIVE_MAX 256U
|
#define FORMAT_TIMESTAMP_RELATIVE_MAX 256U
|
||||||
#define FORMAT_TIMESPAN_MAX 64U
|
#define FORMAT_TIMESPAN_MAX 64U
|
||||||
|
|
||||||
@ -147,15 +146,15 @@ static inline char* format_timestamp(char *buf, size_t l, usec_t t) {
|
|||||||
#define FORMAT_TIMESTAMP_STYLE(t, style) \
|
#define FORMAT_TIMESTAMP_STYLE(t, style) \
|
||||||
format_timestamp_style((char[FORMAT_TIMESTAMP_MAX]){}, FORMAT_TIMESTAMP_MAX, t, style)
|
format_timestamp_style((char[FORMAT_TIMESTAMP_MAX]){}, FORMAT_TIMESTAMP_MAX, t, style)
|
||||||
|
|
||||||
int parse_timestamp(const char *t, usec_t *usec);
|
int parse_timestamp(const char *t, usec_t *ret);
|
||||||
|
|
||||||
int parse_sec(const char *t, usec_t *usec);
|
int parse_sec(const char *t, usec_t *ret);
|
||||||
int parse_sec_fix_0(const char *t, usec_t *usec);
|
int parse_sec_fix_0(const char *t, usec_t *ret);
|
||||||
int parse_sec_def_infinity(const char *t, usec_t *usec);
|
int parse_sec_def_infinity(const char *t, usec_t *ret);
|
||||||
int parse_time(const char *t, usec_t *usec, usec_t default_unit);
|
int parse_time(const char *t, usec_t *ret, usec_t default_unit);
|
||||||
int parse_nsec(const char *t, nsec_t *nsec);
|
int parse_nsec(const char *t, nsec_t *ret);
|
||||||
|
|
||||||
int get_timezones(char ***l);
|
int get_timezones(char ***ret);
|
||||||
int verify_timezone(const char *name, int log_level);
|
int verify_timezone(const char *name, int log_level);
|
||||||
static inline bool timezone_is_valid(const char *name, int log_level) {
|
static inline bool timezone_is_valid(const char *name, int log_level) {
|
||||||
return verify_timezone(name, log_level) >= 0;
|
return verify_timezone(name, log_level) >= 0;
|
||||||
@ -165,7 +164,7 @@ bool clock_supported(clockid_t clock);
|
|||||||
|
|
||||||
usec_t usec_shift_clock(usec_t, clockid_t from, clockid_t to);
|
usec_t usec_shift_clock(usec_t, clockid_t from, clockid_t to);
|
||||||
|
|
||||||
int get_timezone(char **timezone);
|
int get_timezone(char **ret);
|
||||||
|
|
||||||
time_t mktime_or_timegm(struct tm *tm, bool utc);
|
time_t mktime_or_timegm(struct tm *tm, bool utc);
|
||||||
struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc);
|
struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc);
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include "dirent-util.h"
|
||||||
#include "env-util.h"
|
#include "env-util.h"
|
||||||
|
#include "fd-util.h"
|
||||||
|
#include "fileio.h"
|
||||||
#include "random-util.h"
|
#include "random-util.h"
|
||||||
#include "serialize.h"
|
#include "serialize.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
@ -8,6 +11,8 @@
|
|||||||
#include "tests.h"
|
#include "tests.h"
|
||||||
#include "time-util.h"
|
#include "time-util.h"
|
||||||
|
|
||||||
|
#define TRIAL 100u
|
||||||
|
|
||||||
TEST(parse_sec) {
|
TEST(parse_sec) {
|
||||||
usec_t u;
|
usec_t u;
|
||||||
|
|
||||||
@ -334,11 +339,11 @@ TEST(usec_sub_signed) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(format_timestamp) {
|
TEST(format_timestamp) {
|
||||||
for (unsigned i = 0; i < 100; i++) {
|
for (unsigned i = 0; i < TRIAL; i++) {
|
||||||
char buf[CONST_MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESPAN_MAX)];
|
char buf[CONST_MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESPAN_MAX)];
|
||||||
usec_t x, y;
|
usec_t x, y;
|
||||||
|
|
||||||
x = random_u64_range(2147483600 * USEC_PER_SEC) + 1;
|
x = random_u64_range(USEC_TIMESTAMP_FORMATTABLE_MAX - USEC_PER_SEC) + USEC_PER_SEC;
|
||||||
|
|
||||||
assert_se(format_timestamp(buf, sizeof(buf), x));
|
assert_se(format_timestamp(buf, sizeof(buf), x));
|
||||||
log_debug("%s", buf);
|
log_debug("%s", buf);
|
||||||
@ -384,20 +389,91 @@ TEST(format_timestamp) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_format_timestamp_impl(usec_t x) {
|
||||||
|
bool success;
|
||||||
|
const char *xx, *yy;
|
||||||
|
usec_t y;
|
||||||
|
|
||||||
|
xx = FORMAT_TIMESTAMP(x);
|
||||||
|
assert_se(xx);
|
||||||
|
assert_se(parse_timestamp(xx, &y) >= 0);
|
||||||
|
yy = FORMAT_TIMESTAMP(y);
|
||||||
|
assert_se(yy);
|
||||||
|
|
||||||
|
success = (x / USEC_PER_SEC == y / USEC_PER_SEC) && streq(xx, yy);
|
||||||
|
log_full(success ? LOG_DEBUG : LOG_ERR, "@" USEC_FMT " → %s → @" USEC_FMT " → %s", x, xx, y, yy);
|
||||||
|
assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC);
|
||||||
|
assert_se(streq(xx, yy));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_format_timestamp_loop(void) {
|
||||||
|
test_format_timestamp_impl(USEC_PER_SEC);
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < TRIAL; i++) {
|
||||||
|
usec_t x;
|
||||||
|
|
||||||
|
x = random_u64_range(USEC_TIMESTAMP_FORMATTABLE_MAX - USEC_PER_SEC) + USEC_PER_SEC;
|
||||||
|
test_format_timestamp_impl(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST(FORMAT_TIMESTAMP) {
|
TEST(FORMAT_TIMESTAMP) {
|
||||||
for (unsigned i = 0; i < 100; i++) {
|
test_format_timestamp_loop();
|
||||||
_cleanup_free_ char *buf;
|
}
|
||||||
usec_t x, y;
|
|
||||||
|
|
||||||
x = random_u64_range(2147483600 * USEC_PER_SEC) + 1;
|
static void test_format_timestamp_with_tz_one(const char *name1, const char *name2) {
|
||||||
|
_cleanup_free_ char *buf = NULL, *tz = NULL;
|
||||||
|
const char *name, *saved_tz;
|
||||||
|
|
||||||
/* strbuf() is to test the macro in an argument to a function call. */
|
if (name2)
|
||||||
assert_se(buf = strdup(FORMAT_TIMESTAMP(x)));
|
assert_se(buf = path_join(name1, name2));
|
||||||
log_debug("%s", buf);
|
name = buf ?: name1;
|
||||||
assert_se(parse_timestamp(buf, &y) >= 0);
|
|
||||||
assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC);
|
|
||||||
|
|
||||||
assert_se(streq(FORMAT_TIMESTAMP(x), buf));
|
if (!timezone_is_valid(name, LOG_DEBUG))
|
||||||
|
return;
|
||||||
|
|
||||||
|
log_info("/* %s(%s) */", __func__, name);
|
||||||
|
|
||||||
|
saved_tz = getenv("TZ");
|
||||||
|
|
||||||
|
assert_se(tz = strjoin(":", name));
|
||||||
|
assert_se(setenv("TZ", tz, 1) >= 0);
|
||||||
|
tzset();
|
||||||
|
log_debug("%s: tzname[0]=%s, tzname[1]=%s", tz, strempty(tzname[0]), strempty(tzname[1]));
|
||||||
|
|
||||||
|
test_format_timestamp_loop();
|
||||||
|
|
||||||
|
assert_se(set_unset_env("TZ", saved_tz, true) == 0);
|
||||||
|
tzset();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FORMAT_TIMESTAMP_with_tz) {
|
||||||
|
if (!slow_tests_enabled())
|
||||||
|
return (void) log_tests_skipped("slow tests are disabled");
|
||||||
|
|
||||||
|
_cleanup_closedir_ DIR *dir = opendir("/usr/share/zoneinfo");
|
||||||
|
if (!dir)
|
||||||
|
return (void) log_tests_skipped_errno(errno, "Failed to open /usr/share/zoneinfo");
|
||||||
|
|
||||||
|
FOREACH_DIRENT(de, dir, break) {
|
||||||
|
if (de->d_type == DT_REG)
|
||||||
|
test_format_timestamp_with_tz_one(de->d_name, NULL);
|
||||||
|
|
||||||
|
else if (de->d_type == DT_DIR) {
|
||||||
|
if (streq(de->d_name, "right"))
|
||||||
|
/* The test does not support timezone with leap second info. */
|
||||||
|
continue;
|
||||||
|
|
||||||
|
_cleanup_closedir_ DIR *subdir = xopendirat(dirfd(dir), de->d_name, 0);
|
||||||
|
if (!subdir) {
|
||||||
|
log_notice_errno(errno, "Failed to open /usr/share/zoneinfo/%s, ignoring: %m", de->d_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
FOREACH_DIRENT(subde, subdir, break)
|
||||||
|
if (subde->d_type == DT_REG)
|
||||||
|
test_format_timestamp_with_tz_one(de->d_name, subde->d_name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -579,6 +655,219 @@ TEST(format_timestamp_range) {
|
|||||||
test_format_timestamp_one(USEC_INFINITY, TIMESTAMP_UTC, NULL);
|
test_format_timestamp_one(USEC_INFINITY, TIMESTAMP_UTC, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_parse_timestamp_one(const char *str, usec_t max_diff, usec_t expected) {
|
||||||
|
usec_t usec;
|
||||||
|
|
||||||
|
log_debug("/* %s(%s) */", __func__, str);
|
||||||
|
assert_se(parse_timestamp(str, &usec) >= 0);
|
||||||
|
assert_se(usec >= expected);
|
||||||
|
assert_se(usec_sub_unsigned(usec, expected) <= max_diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(parse_timestamp) {
|
||||||
|
usec_t today, now_usec;
|
||||||
|
|
||||||
|
/* UTC */
|
||||||
|
test_parse_timestamp_one("Thu 1970-01-01 00:01 UTC", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("Thu 1970-01-01 00:00:01 UTC", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("Thu 1970-01-01 00:00:01.001 UTC", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("Thu 1970-01-01 00:00:01.0010 UTC", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("Thu 70-01-01 00:01 UTC", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("Thu 70-01-01 00:00:01 UTC", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("Thu 70-01-01 00:00:01.001 UTC", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("Thu 70-01-01 00:00:01.0010 UTC", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("1970-01-01 00:01 UTC", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("1970-01-01 00:00:01 UTC", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("1970-01-01 00:00:01.001 UTC", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("1970-01-01 00:00:01.0010 UTC", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("70-01-01 00:01 UTC", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("70-01-01 00:00:01 UTC", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("70-01-01 00:00:01.001 UTC", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("70-01-01 00:00:01.0010 UTC", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
if (timezone_is_valid("Asia/Tokyo", LOG_DEBUG)) {
|
||||||
|
/* Asia/Tokyo (+0900) */
|
||||||
|
test_parse_timestamp_one("Thu 1970-01-01 09:01 Asia/Tokyo", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("Thu 1970-01-01 09:00:01 Asia/Tokyo", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("Thu 1970-01-01 09:00:01.001 Asia/Tokyo", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("Thu 1970-01-01 09:00:01.0010 Asia/Tokyo", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("Thu 70-01-01 09:01 Asia/Tokyo", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("Thu 70-01-01 09:00:01 Asia/Tokyo", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("Thu 70-01-01 09:00:01.001 Asia/Tokyo", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("Thu 70-01-01 09:00:01.0010 Asia/Tokyo", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("1970-01-01 09:01 Asia/Tokyo", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("1970-01-01 09:00:01 Asia/Tokyo", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("1970-01-01 09:00:01.001 Asia/Tokyo", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("1970-01-01 09:00:01.0010 Asia/Tokyo", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("70-01-01 09:01 Asia/Tokyo", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("70-01-01 09:00:01 Asia/Tokyo", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("70-01-01 09:00:01.001 Asia/Tokyo", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("70-01-01 09:00:01.0010 Asia/Tokyo", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
const char *saved_tz = getenv("TZ");
|
||||||
|
assert_se(setenv("TZ", ":Asia/Tokyo", 1) >= 0);
|
||||||
|
|
||||||
|
/* JST (+0900) */
|
||||||
|
test_parse_timestamp_one("Thu 1970-01-01 09:01 JST", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("Thu 1970-01-01 09:00:01 JST", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("Thu 1970-01-01 09:00:01.001 JST", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("Thu 1970-01-01 09:00:01.0010 JST", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("Thu 70-01-01 09:01 JST", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("Thu 70-01-01 09:00:01 JST", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("Thu 70-01-01 09:00:01.001 JST", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("Thu 70-01-01 09:00:01.0010 JST", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("1970-01-01 09:01 JST", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("1970-01-01 09:00:01 JST", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("1970-01-01 09:00:01.001 JST", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("1970-01-01 09:00:01.0010 JST", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("70-01-01 09:01 JST", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("70-01-01 09:00:01 JST", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("70-01-01 09:00:01.001 JST", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("70-01-01 09:00:01.0010 JST", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
assert_se(set_unset_env("TZ", saved_tz, true) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timezone_is_valid("America/New_York", LOG_DEBUG)) {
|
||||||
|
/* America/New_York (-0500) */
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 19:01 America/New_York", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 19:00:01 America/New_York", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 19:00:01.001 America/New_York", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 19:00:01.0010 America/New_York", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 19:01 America/New_York", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 19:00:01 America/New_York", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 19:00:01.001 America/New_York", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 19:00:01.0010 America/New_York", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("1969-12-31 19:01 America/New_York", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("1969-12-31 19:00:01 America/New_York", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("1969-12-31 19:00:01.001 America/New_York", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("1969-12-31 19:00:01.0010 America/New_York", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("69-12-31 19:01 America/New_York", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("69-12-31 19:00:01 America/New_York", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("69-12-31 19:00:01.001 America/New_York", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("69-12-31 19:00:01.0010 America/New_York", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
const char *saved_tz = getenv("TZ");
|
||||||
|
assert_se(setenv("TZ", ":America/New_York", 1) >= 0);
|
||||||
|
|
||||||
|
/* EST (-0500) */
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 19:01 EST", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 19:00:01 EST", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 19:00:01.001 EST", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 19:00:01.0010 EST", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 19:01 EST", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 19:00:01 EST", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 19:00:01.001 EST", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 19:00:01.0010 EST", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("1969-12-31 19:01 EST", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("1969-12-31 19:00:01 EST", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("1969-12-31 19:00:01.001 EST", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("1969-12-31 19:00:01.0010 EST", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("69-12-31 19:01 EST", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("69-12-31 19:00:01 EST", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("69-12-31 19:00:01.001 EST", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("69-12-31 19:00:01.0010 EST", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
assert_se(set_unset_env("TZ", saved_tz, true) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -06 */
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 18:01 -06", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 18:00:01 -06", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 18:00:01.001 -06", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 18:00:01.0010 -06", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 18:01 -06", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 18:00:01 -06", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 18:00:01.001 -06", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 18:00:01.0010 -06", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("1969-12-31 18:01 -06", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("1969-12-31 18:00:01 -06", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("1969-12-31 18:00:01.001 -06", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("1969-12-31 18:00:01.0010 -06", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("69-12-31 18:01 -06", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("69-12-31 18:00:01 -06", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("69-12-31 18:00:01.001 -06", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("69-12-31 18:00:01.0010 -06", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
/* -0600 */
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 18:01 -0600", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 18:00:01 -0600", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 18:00:01.001 -0600", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 18:00:01.0010 -0600", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 18:01 -0600", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 18:00:01 -0600", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 18:00:01.001 -0600", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 18:00:01.0010 -0600", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("1969-12-31 18:01 -0600", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("1969-12-31 18:00:01 -0600", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("1969-12-31 18:00:01.001 -0600", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("1969-12-31 18:00:01.0010 -0600", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("69-12-31 18:01 -0600", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("69-12-31 18:00:01 -0600", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("69-12-31 18:00:01.001 -0600", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("69-12-31 18:00:01.0010 -0600", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
/* -06:00 */
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 18:01 -06:00", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 18:00:01 -06:00", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 18:00:01.001 -06:00", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("Wed 1969-12-31 18:00:01.0010 -06:00", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 18:01 -06:00", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 18:00:01 -06:00", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 18:00:01.001 -06:00", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("Wed 69-12-31 18:00:01.0010 -06:00", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("1969-12-31 18:01 -06:00", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("1969-12-31 18:00:01 -06:00", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("1969-12-31 18:00:01.001 -06:00", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("1969-12-31 18:00:01.0010 -06:00", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
test_parse_timestamp_one("69-12-31 18:01 -06:00", 0, USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("69-12-31 18:00:01 -06:00", 0, USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("69-12-31 18:00:01.001 -06:00", 0, USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("69-12-31 18:00:01.0010 -06:00", 0, USEC_PER_SEC + 1000);
|
||||||
|
|
||||||
|
/* without date */
|
||||||
|
assert_se(parse_timestamp("today", &today) == 0);
|
||||||
|
test_parse_timestamp_one("00:01", 0, today + USEC_PER_MINUTE);
|
||||||
|
test_parse_timestamp_one("00:00:01", 0, today + USEC_PER_SEC);
|
||||||
|
test_parse_timestamp_one("00:00:01.001", 0, today + USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("00:00:01.0010", 0, today + USEC_PER_SEC + 1000);
|
||||||
|
test_parse_timestamp_one("tomorrow", 0, today + USEC_PER_DAY);
|
||||||
|
test_parse_timestamp_one("yesterday", 0, today - USEC_PER_DAY);
|
||||||
|
|
||||||
|
/* relative */
|
||||||
|
assert_se(parse_timestamp("now", &now_usec) == 0);
|
||||||
|
test_parse_timestamp_one("+5hours", USEC_PER_MINUTE, now_usec + 5 * USEC_PER_HOUR);
|
||||||
|
if (now_usec >= 10 * USEC_PER_DAY)
|
||||||
|
test_parse_timestamp_one("-10days", USEC_PER_MINUTE, now_usec - 10 * USEC_PER_DAY);
|
||||||
|
test_parse_timestamp_one("2weeks left", USEC_PER_MINUTE, now_usec + 2 * USEC_PER_WEEK);
|
||||||
|
if (now_usec >= 30 * USEC_PER_MINUTE)
|
||||||
|
test_parse_timestamp_one("30minutes ago", USEC_PER_MINUTE, now_usec - 30 * USEC_PER_MINUTE);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(deserialize_dual_timestamp) {
|
TEST(deserialize_dual_timestamp) {
|
||||||
int r;
|
int r;
|
||||||
dual_timestamp t;
|
dual_timestamp t;
|
||||||
@ -702,6 +991,71 @@ TEST(map_clock_usec) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_timezone_offset_change_one(const char *utc, const char *pretty) {
|
||||||
|
usec_t x, y, z;
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
assert_se(parse_timestamp(utc, &x) >= 0);
|
||||||
|
|
||||||
|
s = FORMAT_TIMESTAMP_STYLE(x, TIMESTAMP_UTC);
|
||||||
|
assert_se(parse_timestamp(s, &y) >= 0);
|
||||||
|
log_debug("%s -> " USEC_FMT " -> %s -> " USEC_FMT, utc, x, s, y);
|
||||||
|
assert_se(streq(s, utc));
|
||||||
|
assert_se(x == y);
|
||||||
|
|
||||||
|
assert_se(parse_timestamp(pretty, &y) >= 0);
|
||||||
|
s = FORMAT_TIMESTAMP_STYLE(y, TIMESTAMP_PRETTY);
|
||||||
|
assert_se(parse_timestamp(s, &z) >= 0);
|
||||||
|
log_debug("%s -> " USEC_FMT " -> %s -> " USEC_FMT, pretty, y, s, z);
|
||||||
|
assert_se(streq(s, pretty));
|
||||||
|
assert_se(x == y);
|
||||||
|
assert_se(x == z);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(timezone_offset_change) {
|
||||||
|
const char *tz = getenv("TZ");
|
||||||
|
|
||||||
|
/* See issue #26370. */
|
||||||
|
|
||||||
|
if (timezone_is_valid("Africa/Casablanca", LOG_DEBUG)) {
|
||||||
|
assert_se(setenv("TZ", ":Africa/Casablanca", 1) >= 0);
|
||||||
|
tzset();
|
||||||
|
log_debug("Africa/Casablanca: tzname[0]=%s, tzname[1]=%s", strempty(tzname[0]), strempty(tzname[1]));
|
||||||
|
|
||||||
|
test_timezone_offset_change_one("Sun 2015-10-25 01:59:59 UTC", "Sun 2015-10-25 02:59:59 +01");
|
||||||
|
test_timezone_offset_change_one("Sun 2015-10-25 02:00:00 UTC", "Sun 2015-10-25 02:00:00 +00");
|
||||||
|
test_timezone_offset_change_one("Sun 2018-06-17 01:59:59 UTC", "Sun 2018-06-17 01:59:59 +00");
|
||||||
|
test_timezone_offset_change_one("Sun 2018-06-17 02:00:00 UTC", "Sun 2018-06-17 03:00:00 +01");
|
||||||
|
test_timezone_offset_change_one("Sun 2018-10-28 01:59:59 UTC", "Sun 2018-10-28 02:59:59 +01");
|
||||||
|
test_timezone_offset_change_one("Sun 2018-10-28 02:00:00 UTC", "Sun 2018-10-28 03:00:00 +01");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timezone_is_valid("Asia/Atyrau", LOG_DEBUG)) {
|
||||||
|
assert_se(setenv("TZ", ":Asia/Atyrau", 1) >= 0);
|
||||||
|
tzset();
|
||||||
|
log_debug("Asia/Atyrau: tzname[0]=%s, tzname[1]=%s", strempty(tzname[0]), strempty(tzname[1]));
|
||||||
|
|
||||||
|
test_timezone_offset_change_one("Sat 2004-03-27 21:59:59 UTC", "Sun 2004-03-28 01:59:59 +04");
|
||||||
|
test_timezone_offset_change_one("Sat 2004-03-27 22:00:00 UTC", "Sun 2004-03-28 03:00:00 +05");
|
||||||
|
test_timezone_offset_change_one("Sat 2004-10-30 21:59:59 UTC", "Sun 2004-10-31 02:59:59 +05");
|
||||||
|
test_timezone_offset_change_one("Sat 2004-10-30 22:00:00 UTC", "Sun 2004-10-31 03:00:00 +05");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timezone_is_valid("Chile/EasterIsland", LOG_DEBUG)) {
|
||||||
|
assert_se(setenv("TZ", ":Chile/EasterIsland", 1) >= 0);
|
||||||
|
tzset();
|
||||||
|
log_debug("Chile/EasterIsland: tzname[0]=%s, tzname[1]=%s", strempty(tzname[0]), strempty(tzname[1]));
|
||||||
|
|
||||||
|
test_timezone_offset_change_one("Sun 1981-10-11 03:59:59 UTC", "Sat 1981-10-10 20:59:59 -07");
|
||||||
|
test_timezone_offset_change_one("Sun 1981-10-11 04:00:00 UTC", "Sat 1981-10-10 22:00:00 -06");
|
||||||
|
test_timezone_offset_change_one("Sun 1982-03-14 02:59:59 UTC", "Sat 1982-03-13 20:59:59 -06");
|
||||||
|
test_timezone_offset_change_one("Sun 1982-03-14 03:00:00 UTC", "Sat 1982-03-13 21:00:00 -06");
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_se(set_unset_env("TZ", tz, true) == 0);
|
||||||
|
tzset();
|
||||||
|
}
|
||||||
|
|
||||||
static int intro(void) {
|
static int intro(void) {
|
||||||
log_info("realtime=" USEC_FMT "\n"
|
log_info("realtime=" USEC_FMT "\n"
|
||||||
"monotonic=" USEC_FMT "\n"
|
"monotonic=" USEC_FMT "\n"
|
||||||
|
Loading…
Reference in New Issue
Block a user