mirror of
https://github.com/systemd/systemd.git
synced 2024-11-07 18:27:04 +03:00
Merge pull request #1569 from mustrumr/date-parse-additions
Date parse additions
This commit is contained in:
commit
590a23de52
@ -117,10 +117,11 @@
|
||||
<refsect1>
|
||||
<title>Parsing Timestamps</title>
|
||||
|
||||
<para>When parsing systemd will accept a similar timestamp syntax,
|
||||
but excluding any timezone specification (this limitation might be
|
||||
removed eventually). The weekday specification is optional, but
|
||||
when the weekday is specified it must either be in the abbreviated
|
||||
<para>When parsing systemd will accept a similar syntax, but expects
|
||||
no timezone specification, unless it is given as the literal string
|
||||
"UTC". In this case the time is considered in UTC time, otherwise in
|
||||
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>Wednesday</literal>) English language form (case does
|
||||
not matter), and is not subject to the locale choice of the user.
|
||||
@ -157,22 +158,29 @@
|
||||
00:00.</para>
|
||||
|
||||
<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
|
||||
2012-11-23 11:12:13 → Fri 2012-11-23 11:12:13
|
||||
2012-11-23 → Fri 2012-11-23 00:00:00
|
||||
12-11-23 → Fri 2012-11-23 00:00:00
|
||||
11:12:13 → Fri 2012-11-23 11:12:13
|
||||
11:12 → Fri 2012-11-23 11:12:00
|
||||
now → Fri 2012-11-23 18:15:22
|
||||
today → Fri 2012-11-23 00:00:00
|
||||
yesterday → Fri 2012-11-22 00:00:00
|
||||
tomorrow → Fri 2012-11-24 00:00:00
|
||||
+3h30min → Fri 2012-11-23 21:45:22
|
||||
-5s → Fri 2012-11-23 18:15:17
|
||||
11min ago → Fri 2012-11-23 18:04:22
|
||||
@1395716396 → Tue 2014-03-25 03:59:56</programlisting>
|
||||
2012-11-23 11:12:13 UTC → Fri 2012-11-23 19:12:13
|
||||
2012-11-23 → Fri 2012-11-23 00:00:00
|
||||
12-11-23 → Fri 2012-11-23 00:00:00
|
||||
11:12:13 → Fri 2012-11-23 11:12:13
|
||||
11:12:13.9900009 → Fri 2012-11-23 11:12:13
|
||||
format_timestamp_us: Fri 2012-11-23 11:12:13.990000
|
||||
11:12 → Fri 2012-11-23 11:12:00
|
||||
now → Fri 2012-11-23 18:15:22
|
||||
today → Fri 2012-11-23 00:00:00
|
||||
today UTC → Fri 2012-11-23 16:00:00
|
||||
yesterday → Fri 2012-11-22 00:00:00
|
||||
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
|
||||
correctly by systemd, as the timezone specification is not
|
||||
@ -226,7 +234,8 @@
|
||||
second component is not specified, <literal>:00</literal> is
|
||||
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
|
||||
<literal>minutely</literal>,
|
||||
@ -242,7 +251,7 @@
|
||||
<literal>*-*-01 00:00:00</literal>,
|
||||
<literal>Mon *-*-* 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.
|
||||
</para>
|
||||
|
||||
@ -251,31 +260,33 @@
|
||||
|
||||
<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
|
||||
Wed *-1 → Wed *-*-01 00:00:00
|
||||
Wed-Wed,Wed *-1 → Wed *-*-01 00:00:00
|
||||
Wed, 17:48 → Wed *-*-* 17:48:00
|
||||
Wed *-1 → Wed *-*-01 00:00:00
|
||||
Wed-Wed,Wed *-1 → Wed *-*-01 00:00: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
|
||||
*-*-7 0:0:0 → *-*-07 00:00:00
|
||||
10-15 → *-10-15 00:00:00
|
||||
*-*-7 0:0:0 → *-*-07 00:00:00
|
||||
10-15 → *-10-15 00: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
|
||||
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
|
||||
03-05 08:05:40 → *-03-05 08:05:40
|
||||
08:05:40 → *-*-* 08:05:40
|
||||
05:40 → *-*-* 05:40:00
|
||||
03-05 08:05:40 → *-03-05 08:05:40
|
||||
08:05:40 → *-*-* 08:05:40
|
||||
05:40 → *-*-* 05:40:00
|
||||
Sat,Sun 12-05 08:05:40 → Sat,Sun *-12-05 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 → 2003-03-05 00:00:00
|
||||
03-05 → *-03-05 00:00:00
|
||||
hourly → *-*-* *:00:00
|
||||
daily → *-*-* 00:00:00
|
||||
monthly → *-*-01 00:00:00
|
||||
weekly → Mon *-*-* 00:00:00
|
||||
yearly → *-01-01 00:00:00
|
||||
annually → *-01-01 00:00:00
|
||||
*:2/3 → *-*-* *:02/3:00</programlisting>
|
||||
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 UTC → 2003-03-05 05:40:00 UTC
|
||||
2003-03-05 → 2003-03-05 00:00:00
|
||||
03-05 → *-03-05 00:00:00
|
||||
hourly → *-*-* *:00:00
|
||||
daily → *-*-* 00:00:00
|
||||
daily UTC → *-*-* 00:00:00 UTC
|
||||
monthly → *-*-01 00:00:00
|
||||
weekly → Mon *-*-* 00:00:00
|
||||
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
|
||||
<citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
|
@ -279,6 +279,9 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) {
|
||||
fputc(':', f);
|
||||
format_chain(f, 2, c->second);
|
||||
|
||||
if (c->utc)
|
||||
fputs(" UTC", f);
|
||||
|
||||
r = fflush_and_check(f);
|
||||
if (r < 0) {
|
||||
free(buf);
|
||||
@ -657,6 +660,10 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
c->utc = endswith_no_case(p, "UTC");
|
||||
if (c->utc)
|
||||
p = strndupa(p, strlen(p) - strlen(" UTC"));
|
||||
|
||||
if (strcaseeq(p, "minutely")) {
|
||||
r = const_chain(0, &c->second);
|
||||
if (r < 0)
|
||||
@ -859,13 +866,13 @@ static int find_matching_component(const CalendarComponent *c, int *val) {
|
||||
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;
|
||||
assert(tm);
|
||||
|
||||
t = *tm;
|
||||
|
||||
if (mktime(&t) == (time_t) -1)
|
||||
if (mktime_or_timegm(&t, utc) == (time_t) -1)
|
||||
return true;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
int k;
|
||||
|
||||
@ -886,7 +893,7 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm) {
|
||||
return true;
|
||||
|
||||
t = *tm;
|
||||
if (mktime(&t) == (time_t) -1)
|
||||
if (mktime_or_timegm(&t, utc) == (time_t) -1)
|
||||
return false;
|
||||
|
||||
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 (;;) {
|
||||
/* Normalize the current date */
|
||||
mktime(&c);
|
||||
mktime_or_timegm(&c, spec->utc);
|
||||
c.tm_isdst = -1;
|
||||
|
||||
c.tm_year += 1900;
|
||||
@ -916,7 +923,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
|
||||
c.tm_mday = 1;
|
||||
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;
|
||||
|
||||
c.tm_mon += 1;
|
||||
@ -927,7 +934,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
|
||||
c.tm_mday = 1;
|
||||
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_mon = 0;
|
||||
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);
|
||||
if (r > 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_mday = 1;
|
||||
c.tm_hour = c.tm_min = c.tm_sec = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!matches_weekday(spec->weekdays_bits, &c)) {
|
||||
if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) {
|
||||
c.tm_mday++;
|
||||
c.tm_hour = c.tm_min = c.tm_sec = 0;
|
||||
continue;
|
||||
@ -954,7 +961,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
|
||||
r = find_matching_component(spec->hour, &c.tm_hour);
|
||||
if (r > 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_hour = c.tm_min = c.tm_sec = 0;
|
||||
continue;
|
||||
@ -963,14 +970,14 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
|
||||
r = find_matching_component(spec->minute, &c.tm_min);
|
||||
if (r > 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_min = c.tm_sec = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
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_sec = 0;
|
||||
continue;
|
||||
@ -991,13 +998,13 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next)
|
||||
assert(next);
|
||||
|
||||
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);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
t = mktime(&tm);
|
||||
t = mktime_or_timegm(&tm, spec->utc);
|
||||
if (t == (time_t) -1)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -36,6 +36,7 @@ typedef struct CalendarComponent {
|
||||
|
||||
typedef struct CalendarSpec {
|
||||
int weekdays_bits;
|
||||
bool utc;
|
||||
|
||||
CalendarComponent *year;
|
||||
CalendarComponent *month;
|
||||
|
@ -19,7 +19,6 @@
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <sys/timex.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;
|
||||
|
||||
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)
|
||||
return NULL;
|
||||
|
||||
@ -235,10 +231,7 @@ static char *format_timestamp_internal_us(char *buf, size_t l, usec_t t, bool ut
|
||||
return NULL;
|
||||
|
||||
sec = (time_t) (t / USEC_PER_SEC);
|
||||
if (utc)
|
||||
gmtime_r(&sec, &tm);
|
||||
else
|
||||
localtime_r(&sec, &tm);
|
||||
localtime_or_gmtime_r(&sec, &tm, utc);
|
||||
|
||||
if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
|
||||
return NULL;
|
||||
@ -484,9 +477,10 @@ int parse_timestamp(const char *t, usec_t *usec) {
|
||||
};
|
||||
|
||||
const char *k;
|
||||
bool utc;
|
||||
struct tm tm, copy;
|
||||
time_t x;
|
||||
usec_t plus = 0, minus = 0, ret;
|
||||
usec_t x_usec, plus = 0, minus = 0, ret;
|
||||
int r, weekday = -1;
|
||||
unsigned i;
|
||||
|
||||
@ -511,28 +505,15 @@ int parse_timestamp(const char *t, usec_t *usec) {
|
||||
assert(t);
|
||||
assert(usec);
|
||||
|
||||
x = time(NULL);
|
||||
assert_se(localtime_r(&x, &tm));
|
||||
tm.tm_isdst = -1;
|
||||
if (t[0] == '@')
|
||||
return parse_sec(t + 1, usec);
|
||||
|
||||
ret = now(CLOCK_REALTIME);
|
||||
|
||||
if (streq(t, "now"))
|
||||
goto finish;
|
||||
|
||||
else if (streq(t, "today")) {
|
||||
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] == '+') {
|
||||
else if (t[0] == '+') {
|
||||
r = parse_sec(t+1, &plus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -546,35 +527,51 @@ int parse_timestamp(const char *t, usec_t *usec) {
|
||||
|
||||
goto finish;
|
||||
|
||||
} else if (t[0] == '@')
|
||||
return parse_sec(t + 1, usec);
|
||||
} else if (endswith(t, " ago")) {
|
||||
t = strndupa(t, strlen(t) - strlen(" ago"));
|
||||
|
||||
else if (endswith(t, " ago")) {
|
||||
_cleanup_free_ char *z;
|
||||
|
||||
z = strndup(t, strlen(t) - 4);
|
||||
if (!z)
|
||||
return -ENOMEM;
|
||||
|
||||
r = parse_sec(z, &minus);
|
||||
r = parse_sec(t, &minus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
goto finish;
|
||||
|
||||
} else if (endswith(t, " left")) {
|
||||
_cleanup_free_ char *z;
|
||||
t = strndupa(t, strlen(t) - strlen(" left"));
|
||||
|
||||
z = strndup(t, strlen(t) - 4);
|
||||
if (!z)
|
||||
return -ENOMEM;
|
||||
|
||||
r = parse_sec(z, &plus);
|
||||
r = parse_sec(t, &plus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
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++) {
|
||||
size_t skip;
|
||||
|
||||
@ -592,66 +589,106 @@ int parse_timestamp(const char *t, usec_t *usec) {
|
||||
|
||||
copy = tm;
|
||||
k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
|
||||
if (k && *k == 0)
|
||||
goto finish;
|
||||
if (k) {
|
||||
if (*k == '.')
|
||||
goto parse_usec;
|
||||
else if (*k == 0)
|
||||
goto from_tm;
|
||||
}
|
||||
|
||||
tm = copy;
|
||||
k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
|
||||
if (k && *k == 0)
|
||||
goto finish;
|
||||
if (k) {
|
||||
if (*k == '.')
|
||||
goto parse_usec;
|
||||
else if (*k == 0)
|
||||
goto from_tm;
|
||||
}
|
||||
|
||||
tm = copy;
|
||||
k = strptime(t, "%y-%m-%d %H:%M", &tm);
|
||||
if (k && *k == 0) {
|
||||
tm.tm_sec = 0;
|
||||
goto finish;
|
||||
goto from_tm;
|
||||
}
|
||||
|
||||
tm = copy;
|
||||
k = strptime(t, "%Y-%m-%d %H:%M", &tm);
|
||||
if (k && *k == 0) {
|
||||
tm.tm_sec = 0;
|
||||
goto finish;
|
||||
goto from_tm;
|
||||
}
|
||||
|
||||
tm = copy;
|
||||
k = strptime(t, "%y-%m-%d", &tm);
|
||||
if (k && *k == 0) {
|
||||
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
|
||||
goto finish;
|
||||
goto from_tm;
|
||||
}
|
||||
|
||||
tm = copy;
|
||||
k = strptime(t, "%Y-%m-%d", &tm);
|
||||
if (k && *k == 0) {
|
||||
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
|
||||
goto finish;
|
||||
goto from_tm;
|
||||
}
|
||||
|
||||
tm = copy;
|
||||
k = strptime(t, "%H:%M:%S", &tm);
|
||||
if (k && *k == 0)
|
||||
goto finish;
|
||||
if (k) {
|
||||
if (*k == '.')
|
||||
goto parse_usec;
|
||||
else if (*k == 0)
|
||||
goto from_tm;
|
||||
}
|
||||
|
||||
tm = copy;
|
||||
k = strptime(t, "%H:%M", &tm);
|
||||
if (k && *k == 0) {
|
||||
tm.tm_sec = 0;
|
||||
goto finish;
|
||||
goto from_tm;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
|
||||
finish:
|
||||
x = mktime(&tm);
|
||||
parse_usec:
|
||||
{
|
||||
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)
|
||||
return -EINVAL;
|
||||
|
||||
if (weekday >= 0 && tm.tm_wday != weekday)
|
||||
return -EINVAL;
|
||||
|
||||
ret = (usec_t) x * USEC_PER_SEC;
|
||||
ret = (usec_t) x * USEC_PER_SEC + x_usec;
|
||||
|
||||
finish:
|
||||
ret += plus;
|
||||
if (ret > minus)
|
||||
ret -= minus;
|
||||
@ -1072,3 +1109,11 @@ int get_timezone(char **tz) {
|
||||
*tz = z;
|
||||
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);
|
||||
}
|
||||
|
@ -21,8 +21,9 @@
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef uint64_t usec_t;
|
||||
typedef uint64_t nsec_t;
|
||||
@ -117,3 +118,6 @@ clockid_t clock_boottime_or_monotonic(void);
|
||||
"xstrftime: " #buf "[] must be big enough")
|
||||
|
||||
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);
|
||||
|
@ -50,6 +50,44 @@ static void test_one(const char *input, const char *output) {
|
||||
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[]) {
|
||||
CalendarSpec *c;
|
||||
|
||||
@ -82,6 +120,15 @@ int main(int argc, char* argv[]) {
|
||||
test_one("semi-annually", "*-01,07-01 00:00:00");
|
||||
test_one("annually", "*-01-01 00:00: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("", &c) < 0);
|
||||
|
@ -23,12 +23,12 @@
|
||||
|
||||
#include "util.h"
|
||||
|
||||
static void test_one(const char *p) {
|
||||
static void test_should_pass(const char *p) {
|
||||
usec_t t, q;
|
||||
char buf[FORMAT_TIMESTAMP_MAX], buf_relative[FORMAT_TIMESTAMP_RELATIVE_MAX];
|
||||
|
||||
assert_se(parse_timestamp(p, &t) >= 0);
|
||||
format_timestamp(buf, sizeof(buf), t);
|
||||
format_timestamp_us(buf, sizeof(buf), t);
|
||||
log_info("%s", buf);
|
||||
|
||||
/* Chop off timezone */
|
||||
@ -42,23 +42,50 @@ static void test_one(const char *p) {
|
||||
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[]) {
|
||||
test_one("17:41");
|
||||
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-2 12:13:14");
|
||||
test_one("12-10-03 12:13");
|
||||
test_one("2012-12-30 18:42");
|
||||
test_one("2012-10-02");
|
||||
test_one("Tue 2012-10-02");
|
||||
test_one("now");
|
||||
test_one_noutc("now");
|
||||
test_one("yesterday");
|
||||
test_one("today");
|
||||
test_one("tomorrow");
|
||||
test_one("+2d");
|
||||
test_one("+2y 4d");
|
||||
test_one("5months ago");
|
||||
test_one("@1395716396");
|
||||
test_one_noutc("+2d");
|
||||
test_one_noutc("+2y 4d");
|
||||
test_one_noutc("5months ago");
|
||||
test_one_noutc("@1395716396");
|
||||
test_one_noutc("today UTC");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user