1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-12 13:18:14 +03:00

Merge pull request #1569 from mustrumr/date-parse-additions

Date parse additions
This commit is contained in:
Lennart Poettering 2015-10-15 13:09:26 +02:00
commit 590a23de52
7 changed files with 260 additions and 118 deletions

View File

@ -117,10 +117,11 @@
<refsect1> <refsect1>
<title>Parsing Timestamps</title> <title>Parsing Timestamps</title>
<para>When parsing systemd will accept a similar timestamp syntax, <para>When parsing systemd will accept a similar syntax, but expects
but excluding any timezone specification (this limitation might be no timezone specification, unless it is given as the literal string
removed eventually). The weekday specification is optional, but "UTC". In this case the time is considered in UTC time, otherwise in
when the weekday is specified it must either be in the abbreviated the local timezone. The weekday specification is optional, but when
the weekday is specified it must either be in the abbreviated
(<literal>Wed</literal>) or non-abbreviated (<literal>Wed</literal>) or non-abbreviated
(<literal>Wednesday</literal>) English language form (case does (<literal>Wednesday</literal>) English language form (case does
not matter), and is not subject to the locale choice of the user. not matter), and is not subject to the locale choice of the user.
@ -157,22 +158,29 @@
00:00.</para> 00:00.</para>
<para>Examples for valid timestamps and their normalized form <para>Examples for valid timestamps and their normalized form
(assuming the current time was 2012-11-23 18:15:22):</para> (assuming the current time was 2012-11-23 18:15:22 and the timezone
was UTC+8, for example TZ=Asia/Shanghai):</para>
<programlisting>Fri 2012-11-23 11:12:13 → Fri 2012-11-23 11:12:13 <programlisting>Fri 2012-11-23 11:12:13 → Fri 2012-11-23 11:12:13
2012-11-23 11:12:13 → Fri 2012-11-23 11:12:13 2012-11-23 11:12:13 → Fri 2012-11-23 11:12:13
2012-11-23 → Fri 2012-11-23 00:00:00 2012-11-23 11:12:13 UTC → Fri 2012-11-23 19:12:13
12-11-23 → Fri 2012-11-23 00:00:00 2012-11-23 → Fri 2012-11-23 00:00:00
11:12:13 → Fri 2012-11-23 11:12:13 12-11-23 → Fri 2012-11-23 00:00:00
11:12 → Fri 2012-11-23 11:12:00 11:12:13 → Fri 2012-11-23 11:12:13
now → Fri 2012-11-23 18:15:22 11:12:13.9900009 → Fri 2012-11-23 11:12:13
today → Fri 2012-11-23 00:00:00 format_timestamp_us: Fri 2012-11-23 11:12:13.990000
yesterday → Fri 2012-11-22 00:00:00 11:12 → Fri 2012-11-23 11:12:00
tomorrow → Fri 2012-11-24 00:00:00 now → Fri 2012-11-23 18:15:22
+3h30min → Fri 2012-11-23 21:45:22 today → Fri 2012-11-23 00:00:00
-5s → Fri 2012-11-23 18:15:17 today UTC → Fri 2012-11-23 16:00:00
11min ago → Fri 2012-11-23 18:04:22 yesterday → Fri 2012-11-22 00:00:00
@1395716396 → Tue 2014-03-25 03:59:56</programlisting> tomorrow → Fri 2012-11-24 00:00:00
+3h30min → Fri 2012-11-23 21:45:22
+3h30min UTC → -EINVAL
-5s → Fri 2012-11-23 18:15:17
11min ago → Fri 2012-11-23 18:04:22
11min ago UTC → -EINVAL
@1395716396 → Tue 2014-03-25 03:59:56</programlisting>
<para>Note that timestamps printed by systemd will not be parsed <para>Note that timestamps printed by systemd will not be parsed
correctly by systemd, as the timezone specification is not correctly by systemd, as the timezone specification is not
@ -226,7 +234,8 @@
second component is not specified, <literal>:00</literal> is second component is not specified, <literal>:00</literal> is
assumed.</para> assumed.</para>
<para>Timezone names may not be specified.</para> <para>A timezone specification is not expected, unless it is given
as the literal string "UTC", similarly to timestamps.</para>
<para>The special expressions <para>The special expressions
<literal>minutely</literal>, <literal>minutely</literal>,
@ -242,7 +251,7 @@
<literal>*-*-01 00:00:00</literal>, <literal>*-*-01 00:00:00</literal>,
<literal>Mon *-*-* 00:00:00</literal>, <literal>Mon *-*-* 00:00:00</literal>,
<literal>*-01-01 00:00:00</literal>, <literal>*-01-01 00:00:00</literal>,
<literal>*-01,04,07,10-01 00:00:0</literal> and <literal>*-01,04,07,10-01 00:00:00</literal> and
<literal>*-01,07-01 00:00:00</literal> respectively. <literal>*-01,07-01 00:00:00</literal> respectively.
</para> </para>
@ -251,31 +260,33 @@
<programlisting> Sat,Thu,Mon-Wed,Sat-Sun → Mon-Thu,Sat,Sun *-*-* 00:00:00 <programlisting> Sat,Thu,Mon-Wed,Sat-Sun → Mon-Thu,Sat,Sun *-*-* 00:00:00
Mon,Sun 12-*-* 2,1:23 → Mon,Sun 2012-*-* 01,02:23:00 Mon,Sun 12-*-* 2,1:23 → Mon,Sun 2012-*-* 01,02:23:00
Wed *-1 → Wed *-*-01 00:00:00 Wed *-1 → Wed *-*-01 00:00:00
Wed-Wed,Wed *-1 → Wed *-*-01 00:00:00 Wed-Wed,Wed *-1 → Wed *-*-01 00:00:00
Wed, 17:48 → Wed *-*-* 17:48:00 Wed, 17:48 → Wed *-*-* 17:48:00
Wed-Sat,Tue 12-10-15 1:2:3 → Tue-Sat 2012-10-15 01:02:03 Wed-Sat,Tue 12-10-15 1:2:3 → Tue-Sat 2012-10-15 01:02:03
*-*-7 0:0:0 → *-*-07 00:00:00 *-*-7 0:0:0 → *-*-07 00:00:00
10-15 → *-10-15 00:00:00 10-15 → *-10-15 00:00:00
monday *-12-* 17:00 → Mon *-12-* 17:00:00 monday *-12-* 17:00 → Mon *-12-* 17:00:00
Mon,Fri *-*-3,1,2 *:30:45 → Mon,Fri *-*-01,02,03 *:30:45 Mon,Fri *-*-3,1,2 *:30:45 → Mon,Fri *-*-01,02,03 *:30:45
12,14,13,12:20,10,30 → *-*-* 12,13,14:10,20,30:00 12,14,13,12:20,10,30 → *-*-* 12,13,14:10,20,30:00
mon,fri *-1/2-1,3 *:30:45 → Mon,Fri *-01/2-01,03 *:30:45 mon,fri *-1/2-1,3 *:30:45 → Mon,Fri *-01/2-01,03 *:30:45
03-05 08:05:40 → *-03-05 08:05:40 03-05 08:05:40 → *-03-05 08:05:40
08:05:40 → *-*-* 08:05:40 08:05:40 → *-*-* 08:05:40
05:40 → *-*-* 05:40:00 05:40 → *-*-* 05:40:00
Sat,Sun 12-05 08:05:40 → Sat,Sun *-12-05 08:05:40 Sat,Sun 12-05 08:05:40 → Sat,Sun *-12-05 08:05:40
Sat,Sun 08:05:40 → Sat,Sun *-*-* 08:05:40 Sat,Sun 08:05:40 → Sat,Sun *-*-* 08:05:40
2003-03-05 05:40 → 2003-03-05 05:40:00 2003-03-05 05:40 → 2003-03-05 05:40:00
2003-03-05 → 2003-03-05 00:00:00 2003-03-05 05:40 UTC → 2003-03-05 05:40:00 UTC
03-05 → *-03-05 00:00:00 2003-03-05 → 2003-03-05 00:00:00
hourly → *-*-* *:00:00 03-05 → *-03-05 00:00:00
daily → *-*-* 00:00:00 hourly → *-*-* *:00:00
monthly → *-*-01 00:00:00 daily → *-*-* 00:00:00
weekly → Mon *-*-* 00:00:00 daily UTC → *-*-* 00:00:00 UTC
yearly → *-01-01 00:00:00 monthly → *-*-01 00:00:00
annually → *-01-01 00:00:00 weekly → Mon *-*-* 00:00:00
*:2/3 → *-*-* *:02/3:00</programlisting> yearly → *-01-01 00:00:00
annually → *-01-01 00:00:00
*:2/3 → *-*-* *:02/3:00</programlisting>
<para>Calendar events are used by timer units, see <para>Calendar events are used by timer units, see
<citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry> <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>

View File

@ -279,6 +279,9 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) {
fputc(':', f); fputc(':', f);
format_chain(f, 2, c->second); format_chain(f, 2, c->second);
if (c->utc)
fputs(" UTC", f);
r = fflush_and_check(f); r = fflush_and_check(f);
if (r < 0) { if (r < 0) {
free(buf); free(buf);
@ -657,6 +660,10 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
if (!c) if (!c)
return -ENOMEM; return -ENOMEM;
c->utc = endswith_no_case(p, "UTC");
if (c->utc)
p = strndupa(p, strlen(p) - strlen(" UTC"));
if (strcaseeq(p, "minutely")) { if (strcaseeq(p, "minutely")) {
r = const_chain(0, &c->second); r = const_chain(0, &c->second);
if (r < 0) if (r < 0)
@ -859,13 +866,13 @@ static int find_matching_component(const CalendarComponent *c, int *val) {
return r; return r;
} }
static bool tm_out_of_bounds(const struct tm *tm) { static bool tm_out_of_bounds(const struct tm *tm, bool utc) {
struct tm t; struct tm t;
assert(tm); assert(tm);
t = *tm; t = *tm;
if (mktime(&t) == (time_t) -1) if (mktime_or_timegm(&t, utc) == (time_t) -1)
return true; return true;
/* Did any normalization take place? If so, it was out of bounds before */ /* Did any normalization take place? If so, it was out of bounds before */
@ -878,7 +885,7 @@ static bool tm_out_of_bounds(const struct tm *tm) {
t.tm_sec != tm->tm_sec; t.tm_sec != tm->tm_sec;
} }
static bool matches_weekday(int weekdays_bits, const struct tm *tm) { static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) {
struct tm t; struct tm t;
int k; int k;
@ -886,7 +893,7 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm) {
return true; return true;
t = *tm; t = *tm;
if (mktime(&t) == (time_t) -1) if (mktime_or_timegm(&t, utc) == (time_t) -1)
return false; return false;
k = t.tm_wday == 0 ? 6 : t.tm_wday - 1; k = t.tm_wday == 0 ? 6 : t.tm_wday - 1;
@ -904,7 +911,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
for (;;) { for (;;) {
/* Normalize the current date */ /* Normalize the current date */
mktime(&c); mktime_or_timegm(&c, spec->utc);
c.tm_isdst = -1; c.tm_isdst = -1;
c.tm_year += 1900; c.tm_year += 1900;
@ -916,7 +923,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
c.tm_mday = 1; c.tm_mday = 1;
c.tm_hour = c.tm_min = c.tm_sec = 0; c.tm_hour = c.tm_min = c.tm_sec = 0;
} }
if (r < 0 || tm_out_of_bounds(&c)) if (r < 0 || tm_out_of_bounds(&c, spec->utc))
return r; return r;
c.tm_mon += 1; c.tm_mon += 1;
@ -927,7 +934,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
c.tm_mday = 1; c.tm_mday = 1;
c.tm_hour = c.tm_min = c.tm_sec = 0; c.tm_hour = c.tm_min = c.tm_sec = 0;
} }
if (r < 0 || tm_out_of_bounds(&c)) { if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_year ++; c.tm_year ++;
c.tm_mon = 0; c.tm_mon = 0;
c.tm_mday = 1; c.tm_mday = 1;
@ -938,14 +945,14 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
r = find_matching_component(spec->day, &c.tm_mday); r = find_matching_component(spec->day, &c.tm_mday);
if (r > 0) if (r > 0)
c.tm_hour = c.tm_min = c.tm_sec = 0; c.tm_hour = c.tm_min = c.tm_sec = 0;
if (r < 0 || tm_out_of_bounds(&c)) { if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_mon ++; c.tm_mon ++;
c.tm_mday = 1; c.tm_mday = 1;
c.tm_hour = c.tm_min = c.tm_sec = 0; c.tm_hour = c.tm_min = c.tm_sec = 0;
continue; continue;
} }
if (!matches_weekday(spec->weekdays_bits, &c)) { if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) {
c.tm_mday++; c.tm_mday++;
c.tm_hour = c.tm_min = c.tm_sec = 0; c.tm_hour = c.tm_min = c.tm_sec = 0;
continue; continue;
@ -954,7 +961,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
r = find_matching_component(spec->hour, &c.tm_hour); r = find_matching_component(spec->hour, &c.tm_hour);
if (r > 0) if (r > 0)
c.tm_min = c.tm_sec = 0; c.tm_min = c.tm_sec = 0;
if (r < 0 || tm_out_of_bounds(&c)) { if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_mday ++; c.tm_mday ++;
c.tm_hour = c.tm_min = c.tm_sec = 0; c.tm_hour = c.tm_min = c.tm_sec = 0;
continue; continue;
@ -963,14 +970,14 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
r = find_matching_component(spec->minute, &c.tm_min); r = find_matching_component(spec->minute, &c.tm_min);
if (r > 0) if (r > 0)
c.tm_sec = 0; c.tm_sec = 0;
if (r < 0 || tm_out_of_bounds(&c)) { if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_hour ++; c.tm_hour ++;
c.tm_min = c.tm_sec = 0; c.tm_min = c.tm_sec = 0;
continue; continue;
} }
r = find_matching_component(spec->second, &c.tm_sec); r = find_matching_component(spec->second, &c.tm_sec);
if (r < 0 || tm_out_of_bounds(&c)) { if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_min ++; c.tm_min ++;
c.tm_sec = 0; c.tm_sec = 0;
continue; continue;
@ -991,13 +998,13 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next)
assert(next); assert(next);
t = (time_t) (usec / USEC_PER_SEC) + 1; t = (time_t) (usec / USEC_PER_SEC) + 1;
assert_se(localtime_r(&t, &tm)); assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
r = find_next(spec, &tm); r = find_next(spec, &tm);
if (r < 0) if (r < 0)
return r; return r;
t = mktime(&tm); t = mktime_or_timegm(&tm, spec->utc);
if (t == (time_t) -1) if (t == (time_t) -1)
return -EINVAL; return -EINVAL;

View File

@ -36,6 +36,7 @@ typedef struct CalendarComponent {
typedef struct CalendarSpec { typedef struct CalendarSpec {
int weekdays_bits; int weekdays_bits;
bool utc;
CalendarComponent *year; CalendarComponent *year;
CalendarComponent *month; CalendarComponent *month;

View File

@ -19,7 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>. along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/ ***/
#include <time.h>
#include <string.h> #include <string.h>
#include <sys/timex.h> #include <sys/timex.h>
#include <sys/timerfd.h> #include <sys/timerfd.h>
@ -205,11 +204,8 @@ static char *format_timestamp_internal(char *buf, size_t l, usec_t t, bool utc)
return NULL; return NULL;
sec = (time_t) (t / USEC_PER_SEC); sec = (time_t) (t / USEC_PER_SEC);
localtime_or_gmtime_r(&sec, &tm, utc);
if (utc)
gmtime_r(&sec, &tm);
else
localtime_r(&sec, &tm);
if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm) <= 0) if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm) <= 0)
return NULL; return NULL;
@ -235,10 +231,7 @@ static char *format_timestamp_internal_us(char *buf, size_t l, usec_t t, bool ut
return NULL; return NULL;
sec = (time_t) (t / USEC_PER_SEC); sec = (time_t) (t / USEC_PER_SEC);
if (utc) localtime_or_gmtime_r(&sec, &tm, utc);
gmtime_r(&sec, &tm);
else
localtime_r(&sec, &tm);
if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0) if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
return NULL; return NULL;
@ -484,9 +477,10 @@ int parse_timestamp(const char *t, usec_t *usec) {
}; };
const char *k; const char *k;
bool utc;
struct tm tm, copy; struct tm tm, copy;
time_t x; time_t x;
usec_t plus = 0, minus = 0, ret; usec_t x_usec, plus = 0, minus = 0, ret;
int r, weekday = -1; int r, weekday = -1;
unsigned i; unsigned i;
@ -511,28 +505,15 @@ int parse_timestamp(const char *t, usec_t *usec) {
assert(t); assert(t);
assert(usec); assert(usec);
x = time(NULL); if (t[0] == '@')
assert_se(localtime_r(&x, &tm)); return parse_sec(t + 1, usec);
tm.tm_isdst = -1;
ret = now(CLOCK_REALTIME);
if (streq(t, "now")) if (streq(t, "now"))
goto finish; goto finish;
else if (streq(t, "today")) { else if (t[0] == '+') {
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
goto finish;
} else if (streq(t, "yesterday")) {
tm.tm_mday --;
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
goto finish;
} else if (streq(t, "tomorrow")) {
tm.tm_mday ++;
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
goto finish;
} else 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;
@ -546,35 +527,51 @@ int parse_timestamp(const char *t, usec_t *usec) {
goto finish; goto finish;
} else if (t[0] == '@') } else if (endswith(t, " ago")) {
return parse_sec(t + 1, usec); t = strndupa(t, strlen(t) - strlen(" ago"));
else if (endswith(t, " ago")) { r = parse_sec(t, &minus);
_cleanup_free_ char *z;
z = strndup(t, strlen(t) - 4);
if (!z)
return -ENOMEM;
r = parse_sec(z, &minus);
if (r < 0) if (r < 0)
return r; return r;
goto finish; goto finish;
} else if (endswith(t, " left")) { } else if (endswith(t, " left")) {
_cleanup_free_ char *z; t = strndupa(t, strlen(t) - strlen(" left"));
z = strndup(t, strlen(t) - 4); r = parse_sec(t, &plus);
if (!z)
return -ENOMEM;
r = parse_sec(z, &plus);
if (r < 0) if (r < 0)
return r; return r;
goto finish; goto finish;
} }
utc = endswith_no_case(t, " UTC");
if (utc)
t = strndupa(t, strlen(t) - strlen(" UTC"));
x = ret / USEC_PER_SEC;
x_usec = 0;
assert_se(localtime_or_gmtime_r(&x, &tm, utc));
tm.tm_isdst = -1;
if (streq(t, "today")) {
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
goto from_tm;
} else if (streq(t, "yesterday")) {
tm.tm_mday --;
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
goto from_tm;
} else if (streq(t, "tomorrow")) {
tm.tm_mday ++;
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
goto from_tm;
}
for (i = 0; i < ELEMENTSOF(day_nr); i++) { for (i = 0; i < ELEMENTSOF(day_nr); i++) {
size_t skip; size_t skip;
@ -592,66 +589,106 @@ int parse_timestamp(const char *t, usec_t *usec) {
copy = tm; copy = tm;
k = strptime(t, "%y-%m-%d %H:%M:%S", &tm); k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
if (k && *k == 0) if (k) {
goto finish; if (*k == '.')
goto parse_usec;
else if (*k == 0)
goto from_tm;
}
tm = copy; tm = copy;
k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm); k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
if (k && *k == 0) if (k) {
goto finish; if (*k == '.')
goto parse_usec;
else if (*k == 0)
goto from_tm;
}
tm = copy; tm = copy;
k = strptime(t, "%y-%m-%d %H:%M", &tm); k = strptime(t, "%y-%m-%d %H:%M", &tm);
if (k && *k == 0) { if (k && *k == 0) {
tm.tm_sec = 0; tm.tm_sec = 0;
goto finish; goto from_tm;
} }
tm = copy; tm = copy;
k = strptime(t, "%Y-%m-%d %H:%M", &tm); k = strptime(t, "%Y-%m-%d %H:%M", &tm);
if (k && *k == 0) { if (k && *k == 0) {
tm.tm_sec = 0; tm.tm_sec = 0;
goto finish; goto from_tm;
} }
tm = copy; tm = copy;
k = strptime(t, "%y-%m-%d", &tm); k = strptime(t, "%y-%m-%d", &tm);
if (k && *k == 0) { if (k && *k == 0) {
tm.tm_sec = tm.tm_min = tm.tm_hour = 0; tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
goto finish; goto from_tm;
} }
tm = copy; tm = copy;
k = strptime(t, "%Y-%m-%d", &tm); k = strptime(t, "%Y-%m-%d", &tm);
if (k && *k == 0) { if (k && *k == 0) {
tm.tm_sec = tm.tm_min = tm.tm_hour = 0; tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
goto finish; goto from_tm;
} }
tm = copy; tm = copy;
k = strptime(t, "%H:%M:%S", &tm); k = strptime(t, "%H:%M:%S", &tm);
if (k && *k == 0) if (k) {
goto finish; if (*k == '.')
goto parse_usec;
else if (*k == 0)
goto from_tm;
}
tm = copy; tm = copy;
k = strptime(t, "%H:%M", &tm); k = strptime(t, "%H:%M", &tm);
if (k && *k == 0) { if (k && *k == 0) {
tm.tm_sec = 0; tm.tm_sec = 0;
goto finish; goto from_tm;
} }
return -EINVAL; return -EINVAL;
finish: parse_usec:
x = mktime(&tm); {
char *end;
unsigned long long val;
size_t l;
k++;
if (*k < '0' || *k > '9')
return -EINVAL;
/* base 10 instead of base 0, .09 is not base 8 */
errno = 0;
val = strtoull(k, &end, 10);
if (*end || errno)
return -EINVAL;
l = end-k;
/* val has l digits, make them 6 */
for (; l < 6; l++)
val *= 10;
for (; l > 6; l--)
val /= 10;
x_usec = val;
}
from_tm:
x = mktime_or_timegm(&tm, utc);
if (x == (time_t) -1) if (x == (time_t) -1)
return -EINVAL; return -EINVAL;
if (weekday >= 0 && tm.tm_wday != weekday) if (weekday >= 0 && tm.tm_wday != weekday)
return -EINVAL; return -EINVAL;
ret = (usec_t) x * USEC_PER_SEC; ret = (usec_t) x * USEC_PER_SEC + x_usec;
finish:
ret += plus; ret += plus;
if (ret > minus) if (ret > minus)
ret -= minus; ret -= minus;
@ -1072,3 +1109,11 @@ int get_timezone(char **tz) {
*tz = z; *tz = z;
return 0; return 0;
} }
time_t mktime_or_timegm(struct tm *tm, bool utc) {
return utc ? timegm(tm) : mktime(tm);
}
struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
}

View File

@ -21,8 +21,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>. along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/ ***/
#include <stdio.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h>
#include <time.h>
typedef uint64_t usec_t; typedef uint64_t usec_t;
typedef uint64_t nsec_t; typedef uint64_t nsec_t;
@ -117,3 +118,6 @@ clockid_t clock_boottime_or_monotonic(void);
"xstrftime: " #buf "[] must be big enough") "xstrftime: " #buf "[] must be big enough")
int get_timezone(char **timezone); int get_timezone(char **timezone);
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);

View File

@ -50,6 +50,44 @@ static void test_one(const char *input, const char *output) {
assert_se(streq(q, p)); assert_se(streq(q, p));
} }
static void test_next(const char *input, const char *new_tz, usec_t after, usec_t expect) {
CalendarSpec *c;
usec_t u;
char *old_tz;
char buf[FORMAT_TIMESTAMP_MAX];
int r;
old_tz = getenv("TZ");
if (old_tz)
old_tz = strdupa(old_tz);
if (new_tz)
assert_se(setenv("TZ", new_tz, 1) >= 0);
else
assert_se(unsetenv("TZ") >= 0);
tzset();
assert_se(calendar_spec_from_string(input, &c) >= 0);
printf("\"%s\"\n", input);
u = after;
r = calendar_spec_next_usec(c, after, &u);
printf("At: %s\n", r < 0 ? strerror(-r) : format_timestamp(buf, sizeof(buf), u));
if (expect != (usec_t)-1)
assert_se(r >= 0 && u == expect);
else
assert(r == -ENOENT);
calendar_spec_free(c);
if (old_tz)
assert_se(setenv("TZ", old_tz, 1) >= 0);
else
assert_se(unsetenv("TZ") >= 0);
tzset();
}
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
CalendarSpec *c; CalendarSpec *c;
@ -82,6 +120,15 @@ int main(int argc, char* argv[]) {
test_one("semi-annually", "*-01,07-01 00:00:00"); test_one("semi-annually", "*-01,07-01 00:00:00");
test_one("annually", "*-01-01 00:00:00"); test_one("annually", "*-01-01 00:00:00");
test_one("*:2/3", "*-*-* *:02/3:00"); test_one("*:2/3", "*-*-* *:02/3:00");
test_one("2015-10-25 01:00:00 uTc", "2015-10-25 01:00:00 UTC");
test_next("2016-03-27 03:17:00", "", 12345, 1459048620000000);
test_next("2016-03-27 03:17:00", "CET", 12345, 1459041420000000);
test_next("2016-03-27 03:17:00", "EET", 12345, -1);
test_next("2016-03-27 03:17:00 UTC", NULL, 12345, 1459048620000000);
test_next("2016-03-27 03:17:00 UTC", "", 12345, 1459048620000000);
test_next("2016-03-27 03:17:00 UTC", "CET", 12345, 1459048620000000);
test_next("2016-03-27 03:17:00 UTC", "EET", 12345, 1459048620000000);
assert_se(calendar_spec_from_string("test", &c) < 0); assert_se(calendar_spec_from_string("test", &c) < 0);
assert_se(calendar_spec_from_string("", &c) < 0); assert_se(calendar_spec_from_string("", &c) < 0);

View File

@ -23,12 +23,12 @@
#include "util.h" #include "util.h"
static void test_one(const char *p) { static void test_should_pass(const char *p) {
usec_t t, q; usec_t t, q;
char buf[FORMAT_TIMESTAMP_MAX], buf_relative[FORMAT_TIMESTAMP_RELATIVE_MAX]; char buf[FORMAT_TIMESTAMP_MAX], buf_relative[FORMAT_TIMESTAMP_RELATIVE_MAX];
assert_se(parse_timestamp(p, &t) >= 0); assert_se(parse_timestamp(p, &t) >= 0);
format_timestamp(buf, sizeof(buf), t); format_timestamp_us(buf, sizeof(buf), t);
log_info("%s", buf); log_info("%s", buf);
/* Chop off timezone */ /* Chop off timezone */
@ -42,23 +42,50 @@ static void test_one(const char *p) {
assert_se(parse_timestamp(buf, &q) >= 0); assert_se(parse_timestamp(buf, &q) >= 0);
} }
static void test_should_fail(const char *p) {
usec_t t;
assert_se(parse_timestamp(p, &t) < 0);
}
static void test_one(const char *p) {
_cleanup_free_ char *with_utc;
log_info("Test: %s", p);
with_utc = strjoin(p, " UTC", NULL);
test_should_pass(p);
test_should_pass(with_utc);
}
static void test_one_noutc(const char *p) {
_cleanup_free_ char *with_utc;
log_info("Test: %s", p);
with_utc = strjoin(p, " UTC", NULL);
test_should_pass(p);
test_should_fail(with_utc);
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
test_one("17:41"); test_one("17:41");
test_one("18:42:44"); test_one("18:42:44");
test_one("18:42:44.0");
test_one("18:42:44.999999999999");
test_one("12-10-02 12:13:14"); test_one("12-10-02 12:13:14");
test_one("12-10-2 12:13:14"); test_one("12-10-2 12:13:14");
test_one("12-10-03 12:13"); test_one("12-10-03 12:13");
test_one("2012-12-30 18:42"); test_one("2012-12-30 18:42");
test_one("2012-10-02"); test_one("2012-10-02");
test_one("Tue 2012-10-02"); test_one("Tue 2012-10-02");
test_one("now"); test_one_noutc("now");
test_one("yesterday"); test_one("yesterday");
test_one("today"); test_one("today");
test_one("tomorrow"); test_one("tomorrow");
test_one("+2d"); test_one_noutc("+2d");
test_one("+2y 4d"); test_one_noutc("+2y 4d");
test_one("5months ago"); test_one_noutc("5months ago");
test_one("@1395716396"); test_one_noutc("@1395716396");
test_one_noutc("today UTC");
return 0; return 0;
} }