mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-12 09:17:44 +03:00
Merge pull request #1912 from mustrumr/calendarspec-sub-second-v3
Calendarspec sub second v3
This commit is contained in:
commit
aa2fb804a1
@ -229,6 +229,10 @@
|
|||||||
the value and all values plus multiples of the repetition value
|
the value and all values plus multiples of the repetition value
|
||||||
are matched.</para>
|
are matched.</para>
|
||||||
|
|
||||||
|
<para>The seconds component may contain decimal fractions both in
|
||||||
|
the value and the repetition. All fractions are rounded to 6
|
||||||
|
decimal places.</para>
|
||||||
|
|
||||||
<para>Either time or date specification may be omitted, in which
|
<para>Either time or date specification may be omitted, in which
|
||||||
case the current day and 00:00:00 is implied, respectively. If the
|
case the current day and 00:00:00 is implied, respectively. If the
|
||||||
second component is not specified, <literal>:00</literal> is
|
second component is not specified, <literal>:00</literal> is
|
||||||
@ -276,6 +280,7 @@ Wed-Sat,Tue 12-10-15 1:2:3 → Tue-Sat 2012-10-15 01:02:03
|
|||||||
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
|
||||||
|
05:40:23.4200004/3.1700005 → 05:40:23.420000/3.170001
|
||||||
2003-03-05 05:40 UTC → 2003-03-05 05:40:00 UTC
|
2003-03-05 05:40 UTC → 2003-03-05 05:40:00 UTC
|
||||||
2003-03-05 → 2003-03-05 00:00:00
|
2003-03-05 → 2003-03-05 00:00:00
|
||||||
03-05 → *-03-05 00:00:00
|
03-05 → *-03-05 00:00:00
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "calendarspec.h"
|
#include "calendarspec.h"
|
||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
|
#include "parse-util.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
|
|
||||||
#define BITS_WEEKDAYS 127
|
#define BITS_WEEKDAYS 127
|
||||||
@ -49,7 +50,7 @@ void calendar_spec_free(CalendarSpec *c) {
|
|||||||
free_chain(c->day);
|
free_chain(c->day);
|
||||||
free_chain(c->hour);
|
free_chain(c->hour);
|
||||||
free_chain(c->minute);
|
free_chain(c->minute);
|
||||||
free_chain(c->second);
|
free_chain(c->microsecond);
|
||||||
|
|
||||||
free(c);
|
free(c);
|
||||||
}
|
}
|
||||||
@ -135,7 +136,7 @@ int calendar_spec_normalize(CalendarSpec *c) {
|
|||||||
sort_chain(&c->day);
|
sort_chain(&c->day);
|
||||||
sort_chain(&c->hour);
|
sort_chain(&c->hour);
|
||||||
sort_chain(&c->minute);
|
sort_chain(&c->minute);
|
||||||
sort_chain(&c->second);
|
sort_chain(&c->microsecond);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -177,7 +178,7 @@ _pure_ bool calendar_spec_valid(CalendarSpec *c) {
|
|||||||
if (!chain_valid(c->minute, 0, 59))
|
if (!chain_valid(c->minute, 0, 59))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!chain_valid(c->second, 0, 59))
|
if (!chain_valid(c->microsecond, 0, 60*USEC_PER_SEC-1))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -232,7 +233,7 @@ static void format_weekdays(FILE *f, const CalendarSpec *c) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void format_chain(FILE *f, int space, const CalendarComponent *c) {
|
static void format_chain(FILE *f, int space, const CalendarComponent *c, bool usec) {
|
||||||
assert(f);
|
assert(f);
|
||||||
|
|
||||||
if (!c) {
|
if (!c) {
|
||||||
@ -241,14 +242,25 @@ static void format_chain(FILE *f, int space, const CalendarComponent *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert(c->value >= 0);
|
assert(c->value >= 0);
|
||||||
fprintf(f, "%0*i", space, c->value);
|
if (!usec)
|
||||||
|
fprintf(f, "%0*i", space, c->value);
|
||||||
|
else if (c->value % USEC_PER_SEC == 0)
|
||||||
|
fprintf(f, "%0*i", space, (int) (c->value / USEC_PER_SEC));
|
||||||
|
else
|
||||||
|
fprintf(f, "%0*i.%06i", space, (int) (c->value / USEC_PER_SEC), (int) (c->value % USEC_PER_SEC));
|
||||||
|
|
||||||
if (c->repeat > 0)
|
if (c->repeat > 0) {
|
||||||
fprintf(f, "/%i", c->repeat);
|
if (!usec)
|
||||||
|
fprintf(f, "/%i", c->repeat);
|
||||||
|
else if (c->repeat % USEC_PER_SEC == 0)
|
||||||
|
fprintf(f, "/%i", (int) (c->repeat / USEC_PER_SEC));
|
||||||
|
else
|
||||||
|
fprintf(f, "/%i.%06i", (int) (c->repeat / USEC_PER_SEC), (int) (c->repeat % USEC_PER_SEC));
|
||||||
|
}
|
||||||
|
|
||||||
if (c->next) {
|
if (c->next) {
|
||||||
fputc(',', f);
|
fputc(',', f);
|
||||||
format_chain(f, space, c->next);
|
format_chain(f, space, c->next, usec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,17 +282,17 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) {
|
|||||||
fputc(' ', f);
|
fputc(' ', f);
|
||||||
}
|
}
|
||||||
|
|
||||||
format_chain(f, 4, c->year);
|
format_chain(f, 4, c->year, false);
|
||||||
fputc('-', f);
|
fputc('-', f);
|
||||||
format_chain(f, 2, c->month);
|
format_chain(f, 2, c->month, false);
|
||||||
fputc('-', f);
|
fputc('-', f);
|
||||||
format_chain(f, 2, c->day);
|
format_chain(f, 2, c->day, false);
|
||||||
fputc(' ', f);
|
fputc(' ', f);
|
||||||
format_chain(f, 2, c->hour);
|
format_chain(f, 2, c->hour, false);
|
||||||
fputc(':', f);
|
fputc(':', f);
|
||||||
format_chain(f, 2, c->minute);
|
format_chain(f, 2, c->minute, false);
|
||||||
fputc(':', f);
|
fputc(':', f);
|
||||||
format_chain(f, 2, c->second);
|
format_chain(f, 2, c->microsecond, true);
|
||||||
|
|
||||||
if (c->utc)
|
if (c->utc)
|
||||||
fputs(" UTC", f);
|
fputs(" UTC", f);
|
||||||
@ -391,35 +403,70 @@ static int parse_weekdays(const char **p, CalendarSpec *c) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int prepend_component(const char **p, CalendarComponent **c) {
|
static int parse_component_decimal(const char **p, bool usec, unsigned long *res) {
|
||||||
|
unsigned long value;
|
||||||
|
const char *e = NULL;
|
||||||
|
char *ee = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
value = strtoul(*p, &ee, 10);
|
||||||
|
if (errno > 0)
|
||||||
|
return -errno;
|
||||||
|
if (ee == *p)
|
||||||
|
return -EINVAL;
|
||||||
|
if ((unsigned long) (int) value != value)
|
||||||
|
return -ERANGE;
|
||||||
|
e = ee;
|
||||||
|
|
||||||
|
if (usec) {
|
||||||
|
if (value * USEC_PER_SEC / USEC_PER_SEC != value)
|
||||||
|
return -ERANGE;
|
||||||
|
|
||||||
|
value *= USEC_PER_SEC;
|
||||||
|
if (*e == '.') {
|
||||||
|
unsigned add;
|
||||||
|
|
||||||
|
e++;
|
||||||
|
r = parse_fractional_part_u(&e, 6, &add);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (add + value < value)
|
||||||
|
return -ERANGE;
|
||||||
|
value += add;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*p = e;
|
||||||
|
*res = value;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int prepend_component(const char **p, bool usec, CalendarComponent **c) {
|
||||||
unsigned long value, repeat = 0;
|
unsigned long value, repeat = 0;
|
||||||
char *e = NULL, *ee = NULL;
|
|
||||||
CalendarComponent *cc;
|
CalendarComponent *cc;
|
||||||
|
int r;
|
||||||
|
const char *e;
|
||||||
|
|
||||||
assert(p);
|
assert(p);
|
||||||
assert(c);
|
assert(c);
|
||||||
|
|
||||||
errno = 0;
|
e = *p;
|
||||||
value = strtoul(*p, &e, 10);
|
|
||||||
if (errno > 0)
|
r = parse_component_decimal(&e, usec, &value);
|
||||||
return -errno;
|
if (r < 0)
|
||||||
if (e == *p)
|
return r;
|
||||||
return -EINVAL;
|
|
||||||
if ((unsigned long) (int) value != value)
|
|
||||||
return -ERANGE;
|
|
||||||
|
|
||||||
if (*e == '/') {
|
if (*e == '/') {
|
||||||
repeat = strtoul(e+1, &ee, 10);
|
e++;
|
||||||
if (errno > 0)
|
r = parse_component_decimal(&e, usec, &repeat);
|
||||||
return -errno;
|
if (r < 0)
|
||||||
if (ee == e+1)
|
return r;
|
||||||
return -EINVAL;
|
|
||||||
if ((unsigned long) (int) repeat != repeat)
|
|
||||||
return -ERANGE;
|
|
||||||
if (repeat <= 0)
|
|
||||||
return -ERANGE;
|
|
||||||
|
|
||||||
e = ee;
|
if (repeat == 0)
|
||||||
|
return -ERANGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != ':')
|
if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != ':')
|
||||||
@ -438,39 +485,12 @@ static int prepend_component(const char **p, CalendarComponent **c) {
|
|||||||
|
|
||||||
if (*e ==',') {
|
if (*e ==',') {
|
||||||
*p += 1;
|
*p += 1;
|
||||||
return prepend_component(p, c);
|
return prepend_component(p, usec, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_chain(const char **p, CalendarComponent **c) {
|
|
||||||
const char *t;
|
|
||||||
CalendarComponent *cc = NULL;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(p);
|
|
||||||
assert(c);
|
|
||||||
|
|
||||||
t = *p;
|
|
||||||
|
|
||||||
if (t[0] == '*') {
|
|
||||||
*p = t + 1;
|
|
||||||
*c = NULL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = prepend_component(&t, &cc);
|
|
||||||
if (r < 0) {
|
|
||||||
free_chain(cc);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
*p = t;
|
|
||||||
*c = cc;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int const_chain(int value, CalendarComponent **c) {
|
static int const_chain(int value, CalendarComponent **c) {
|
||||||
CalendarComponent *cc = NULL;
|
CalendarComponent *cc = NULL;
|
||||||
|
|
||||||
@ -489,6 +509,40 @@ static int const_chain(int value, CalendarComponent **c) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int parse_chain(const char **p, bool usec, CalendarComponent **c) {
|
||||||
|
const char *t;
|
||||||
|
CalendarComponent *cc = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(p);
|
||||||
|
assert(c);
|
||||||
|
|
||||||
|
t = *p;
|
||||||
|
|
||||||
|
if (t[0] == '*') {
|
||||||
|
if (usec) {
|
||||||
|
r = const_chain(0, c);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
(*c)->repeat = USEC_PER_SEC;
|
||||||
|
} else
|
||||||
|
*c = NULL;
|
||||||
|
|
||||||
|
*p = t + 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = prepend_component(&t, usec, &cc);
|
||||||
|
if (r < 0) {
|
||||||
|
free_chain(cc);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
*p = t;
|
||||||
|
*c = cc;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_date(const char **p, CalendarSpec *c) {
|
static int parse_date(const char **p, CalendarSpec *c) {
|
||||||
const char *t;
|
const char *t;
|
||||||
int r;
|
int r;
|
||||||
@ -503,7 +557,7 @@ static int parse_date(const char **p, CalendarSpec *c) {
|
|||||||
if (*t == 0)
|
if (*t == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
r = parse_chain(&t, &first);
|
r = parse_chain(&t, false, &first);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -519,7 +573,7 @@ static int parse_date(const char **p, CalendarSpec *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t++;
|
t++;
|
||||||
r = parse_chain(&t, &second);
|
r = parse_chain(&t, false, &second);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
free_chain(first);
|
free_chain(first);
|
||||||
return r;
|
return r;
|
||||||
@ -540,7 +594,7 @@ static int parse_date(const char **p, CalendarSpec *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t++;
|
t++;
|
||||||
r = parse_chain(&t, &third);
|
r = parse_chain(&t, false, &third);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
free_chain(first);
|
free_chain(first);
|
||||||
free_chain(second);
|
free_chain(second);
|
||||||
@ -582,7 +636,7 @@ static int parse_calendar_time(const char **p, CalendarSpec *c) {
|
|||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = parse_chain(&t, &h);
|
r = parse_chain(&t, false, &h);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
@ -592,7 +646,7 @@ static int parse_calendar_time(const char **p, CalendarSpec *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t++;
|
t++;
|
||||||
r = parse_chain(&t, &m);
|
r = parse_chain(&t, false, &m);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
@ -610,7 +664,7 @@ static int parse_calendar_time(const char **p, CalendarSpec *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t++;
|
t++;
|
||||||
r = parse_chain(&t, &s);
|
r = parse_chain(&t, true, &s);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
@ -639,7 +693,8 @@ finish:
|
|||||||
*p = t;
|
*p = t;
|
||||||
c->hour = h;
|
c->hour = h;
|
||||||
c->minute = m;
|
c->minute = m;
|
||||||
c->second = s;
|
c->microsecond = s;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
@ -671,7 +726,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (strcaseeq(p, "minutely")) {
|
if (strcaseeq(p, "minutely")) {
|
||||||
r = const_chain(0, &c->second);
|
r = const_chain(0, &c->microsecond);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
@ -679,7 +734,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
|
|||||||
r = const_chain(0, &c->minute);
|
r = const_chain(0, &c->minute);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
r = const_chain(0, &c->second);
|
r = const_chain(0, &c->microsecond);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
@ -690,7 +745,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
|
|||||||
r = const_chain(0, &c->minute);
|
r = const_chain(0, &c->minute);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
r = const_chain(0, &c->second);
|
r = const_chain(0, &c->microsecond);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
@ -704,7 +759,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
|
|||||||
r = const_chain(0, &c->minute);
|
r = const_chain(0, &c->minute);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
r = const_chain(0, &c->second);
|
r = const_chain(0, &c->microsecond);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
@ -724,7 +779,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
|
|||||||
r = const_chain(0, &c->minute);
|
r = const_chain(0, &c->minute);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
r = const_chain(0, &c->second);
|
r = const_chain(0, &c->microsecond);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
@ -738,7 +793,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
|
|||||||
r = const_chain(0, &c->minute);
|
r = const_chain(0, &c->minute);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
r = const_chain(0, &c->second);
|
r = const_chain(0, &c->microsecond);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
@ -765,7 +820,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
|
|||||||
r = const_chain(0, &c->minute);
|
r = const_chain(0, &c->minute);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
r = const_chain(0, &c->second);
|
r = const_chain(0, &c->microsecond);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
@ -789,7 +844,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
|
|||||||
r = const_chain(0, &c->minute);
|
r = const_chain(0, &c->minute);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
r = const_chain(0, &c->second);
|
r = const_chain(0, &c->microsecond);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
@ -906,14 +961,16 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) {
|
|||||||
return (weekdays_bits & (1 << k));
|
return (weekdays_bits & (1 << k));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int find_next(const CalendarSpec *spec, struct tm *tm) {
|
static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
|
||||||
struct tm c;
|
struct tm c;
|
||||||
|
int tm_usec;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(spec);
|
assert(spec);
|
||||||
assert(tm);
|
assert(tm);
|
||||||
|
|
||||||
c = *tm;
|
c = *tm;
|
||||||
|
tm_usec = *usec;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
/* Normalize the current date */
|
/* Normalize the current date */
|
||||||
@ -927,7 +984,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
|
|||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
c.tm_mon = 0;
|
c.tm_mon = 0;
|
||||||
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 = tm_usec = 0;
|
||||||
}
|
}
|
||||||
if (r < 0 || tm_out_of_bounds(&c, spec->utc))
|
if (r < 0 || tm_out_of_bounds(&c, spec->utc))
|
||||||
return r;
|
return r;
|
||||||
@ -938,29 +995,29 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
|
|||||||
|
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
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 = tm_usec = 0;
|
||||||
}
|
}
|
||||||
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
|
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;
|
||||||
c.tm_hour = c.tm_min = c.tm_sec = 0;
|
c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = tm_usec = 0;
|
||||||
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
|
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 = tm_usec = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) {
|
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 = tm_usec = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -969,7 +1026,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
|
|||||||
c.tm_min = c.tm_sec = 0;
|
c.tm_min = c.tm_sec = 0;
|
||||||
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
|
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 = tm_usec = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -978,19 +1035,23 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
|
|||||||
c.tm_sec = 0;
|
c.tm_sec = 0;
|
||||||
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
|
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 = tm_usec = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = find_matching_component(spec->second, &c.tm_sec);
|
c.tm_sec = c.tm_sec * USEC_PER_SEC + tm_usec;
|
||||||
|
r = find_matching_component(spec->microsecond, &c.tm_sec);
|
||||||
|
tm_usec = c.tm_sec % USEC_PER_SEC;
|
||||||
|
c.tm_sec /= USEC_PER_SEC;
|
||||||
|
|
||||||
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
|
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
|
||||||
c.tm_min ++;
|
c.tm_min ++;
|
||||||
c.tm_sec = 0;
|
c.tm_sec = tm_usec = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
*tm = c;
|
*tm = c;
|
||||||
|
*usec = tm_usec;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -999,14 +1060,17 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next)
|
|||||||
struct tm tm;
|
struct tm tm;
|
||||||
time_t t;
|
time_t t;
|
||||||
int r;
|
int r;
|
||||||
|
usec_t tm_usec;
|
||||||
|
|
||||||
assert(spec);
|
assert(spec);
|
||||||
assert(next);
|
assert(next);
|
||||||
|
|
||||||
t = (time_t) (usec / USEC_PER_SEC) + 1;
|
usec++;
|
||||||
|
t = (time_t) (usec / USEC_PER_SEC);
|
||||||
assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
|
assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
|
||||||
|
tm_usec = usec % USEC_PER_SEC;
|
||||||
|
|
||||||
r = find_next(spec, &tm);
|
r = find_next(spec, &tm, &tm_usec);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -1014,6 +1078,6 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next)
|
|||||||
if (t == (time_t) -1)
|
if (t == (time_t) -1)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
*next = (usec_t) t * USEC_PER_SEC;
|
*next = (usec_t) t * USEC_PER_SEC + tm_usec;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ typedef struct CalendarSpec {
|
|||||||
|
|
||||||
CalendarComponent *hour;
|
CalendarComponent *hour;
|
||||||
CalendarComponent *minute;
|
CalendarComponent *minute;
|
||||||
CalendarComponent *second;
|
CalendarComponent *microsecond;
|
||||||
} CalendarSpec;
|
} CalendarSpec;
|
||||||
|
|
||||||
void calendar_spec_free(CalendarSpec *c);
|
void calendar_spec_free(CalendarSpec *c);
|
||||||
|
@ -490,3 +490,39 @@ int safe_atod(const char *s, double *ret_d) {
|
|||||||
*ret_d = (double) d;
|
*ret_d = (double) d;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
|
||||||
|
size_t i;
|
||||||
|
unsigned val = 0;
|
||||||
|
const char *s;
|
||||||
|
|
||||||
|
s = *p;
|
||||||
|
|
||||||
|
/* accept any number of digits, strtoull is limted to 19 */
|
||||||
|
for(i=0; i < digits; i++,s++) {
|
||||||
|
if (*s < '0' || *s > '9') {
|
||||||
|
if (i == 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* too few digits, pad with 0 */
|
||||||
|
for (; i < digits; i++)
|
||||||
|
val *= 10;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
val *= 10;
|
||||||
|
val += *s - '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* maybe round up */
|
||||||
|
if (*s >= '5' && *s <= '9')
|
||||||
|
val++;
|
||||||
|
|
||||||
|
s += strspn(s, DIGITS);
|
||||||
|
|
||||||
|
*p = s;
|
||||||
|
*res = val;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -90,3 +90,5 @@ static inline int safe_atoli(const char *s, long int *ret_u) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
int safe_atod(const char *s, double *ret_d);
|
int safe_atod(const char *s, double *ret_d);
|
||||||
|
|
||||||
|
int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
|
#include "parse-util.h"
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
@ -658,29 +659,18 @@ int parse_timestamp(const char *t, usec_t *usec) {
|
|||||||
|
|
||||||
parse_usec:
|
parse_usec:
|
||||||
{
|
{
|
||||||
char *end;
|
unsigned add;
|
||||||
unsigned long long val;
|
|
||||||
size_t l;
|
|
||||||
|
|
||||||
k++;
|
k++;
|
||||||
if (*k < '0' || *k > '9')
|
r = parse_fractional_part_u(&k, 6, &add);
|
||||||
|
if (r < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* base 10 instead of base 0, .09 is not base 8 */
|
if (*k)
|
||||||
errno = 0;
|
|
||||||
val = strtoull(k, &end, 10);
|
|
||||||
if (*end || errno)
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
l = end-k;
|
x_usec = add;
|
||||||
|
|
||||||
/* val has l digits, make them 6 */
|
|
||||||
for (; l < 6; l++)
|
|
||||||
val *= 10;
|
|
||||||
for (; l > 6; l--)
|
|
||||||
val /= 10;
|
|
||||||
|
|
||||||
x_usec = val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
from_tm:
|
from_tm:
|
||||||
|
@ -75,7 +75,7 @@ static void test_next(const char *input, const char *new_tz, usec_t after, usec_
|
|||||||
|
|
||||||
u = after;
|
u = after;
|
||||||
r = calendar_spec_next_usec(c, after, &u);
|
r = calendar_spec_next_usec(c, after, &u);
|
||||||
printf("At: %s\n", r < 0 ? strerror(-r) : format_timestamp(buf, sizeof(buf), u));
|
printf("At: %s\n", r < 0 ? strerror(-r) : format_timestamp_us(buf, sizeof(buf), u));
|
||||||
if (expect != (usec_t)-1)
|
if (expect != (usec_t)-1)
|
||||||
assert_se(r >= 0 && u == expect);
|
assert_se(r >= 0 && u == expect);
|
||||||
else
|
else
|
||||||
@ -123,6 +123,9 @@ int main(int argc, char* argv[]) {
|
|||||||
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_one("2015-10-25 01:00:00 uTc", "2015-10-25 01:00:00 UTC");
|
||||||
|
test_one("2016-03-27 03:17:00.4200005", "2016-03-27 03:17:00.420001");
|
||||||
|
test_one("2016-03-27 03:17:00/0.42", "2016-03-27 03:17:00/0.420000");
|
||||||
|
test_one("2016-03-27 03:17:00/0.42", "2016-03-27 03:17:00/0.420000");
|
||||||
|
|
||||||
test_next("2016-03-27 03:17:00", "", 12345, 1459048620000000);
|
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", "CET", 12345, 1459041420000000);
|
||||||
@ -131,11 +134,19 @@ int main(int argc, char* argv[]) {
|
|||||||
test_next("2016-03-27 03:17:00 UTC", "", 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", "CET", 12345, 1459048620000000);
|
||||||
test_next("2016-03-27 03:17:00 UTC", "EET", 12345, 1459048620000000);
|
test_next("2016-03-27 03:17:00 UTC", "EET", 12345, 1459048620000000);
|
||||||
|
test_next("2016-03-27 03:17:00.420000001 UTC", "EET", 12345, 1459048620420000);
|
||||||
|
test_next("2016-03-27 03:17:00.4200005 UTC", "EET", 12345, 1459048620420001);
|
||||||
|
test_next("2015-11-13 09:11:23.42", "EET", 12345, 1447398683420000);
|
||||||
|
test_next("2015-11-13 09:11:23.42/1.77", "EET", 1447398683420000, 1447398685190000);
|
||||||
|
test_next("2015-11-13 09:11:23.42/1.77", "EET", 1447398683419999, 1447398683420000);
|
||||||
|
|
||||||
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);
|
||||||
assert_se(calendar_spec_from_string("7", &c) < 0);
|
assert_se(calendar_spec_from_string("7", &c) < 0);
|
||||||
assert_se(calendar_spec_from_string("121212:1:2", &c) < 0);
|
assert_se(calendar_spec_from_string("121212:1:2", &c) < 0);
|
||||||
|
assert_se(calendar_spec_from_string("2000-03-05.23 00:00:00", &c) < 0);
|
||||||
|
assert_se(calendar_spec_from_string("2000-03-05 00:00.1:00", &c) < 0);
|
||||||
|
assert_se(calendar_spec_from_string("00:00:00/0.00000001", &c) < 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user