Exemplo n.º 1
0
static int calendar_spec_next_usec_impl(const CalendarSpec *spec, usec_t usec, usec_t *next) {
        struct tm tm;
        time_t t;
        int r;
        usec_t tm_usec;

        assert(spec);
        assert(next);

        if (usec > USEC_TIMESTAMP_FORMATTABLE_MAX)
                return -EINVAL;

        usec++;
        t = (time_t) (usec / USEC_PER_SEC);
        assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
        tm_usec = usec % USEC_PER_SEC;

        r = find_next(spec, &tm, &tm_usec);
        if (r < 0)
                return r;

        t = mktime_or_timegm(&tm, spec->utc);
        if (t < 0)
                return -EINVAL;

        *next = (usec_t) t * USEC_PER_SEC + tm_usec;
        return 0;
}
Exemplo n.º 2
0
static bool tm_out_of_bounds(const struct tm *tm, bool utc) {
        struct tm t;
        assert(tm);

        t = *tm;

        if (mktime_or_timegm(&t, utc) < 0)
                return true;

        /*
         * Set an upper bound on the year so impossible dates like "*-02-31"
         * don't cause find_next() to loop forever. tm_year contains years
         * since 1900, so adjust it accordingly.
         */
        if (tm->tm_year + 1900 > MAX_YEAR)
                return true;

        /* Did any normalization take place? If so, it was out of bounds before */
        return
                t.tm_year != tm->tm_year ||
                t.tm_mon != tm->tm_mon ||
                t.tm_mday != tm->tm_mday ||
                t.tm_hour != tm->tm_hour ||
                t.tm_min != tm->tm_min ||
                t.tm_sec != tm->tm_sec;
}
Exemplo n.º 3
0
static int find_end_of_month(struct tm *tm, bool utc, int day) {
        struct tm t = *tm;

        t.tm_mon++;
        t.tm_mday = 1 - day;

        if (mktime_or_timegm(&t, utc) < 0 ||
            t.tm_mon != tm->tm_mon)
                return -1;

        return t.tm_mday;
}
Exemplo n.º 4
0
static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) {
        struct tm t;
        int k;

        if (weekdays_bits < 0 || weekdays_bits >= BITS_WEEKDAYS)
                return true;

        t = *tm;
        if (mktime_or_timegm(&t, utc) < 0)
                return false;

        k = t.tm_wday == 0 ? 6 : t.tm_wday - 1;
        return (weekdays_bits & (1 << k));
}
Exemplo n.º 5
0
static bool tm_out_of_bounds(const struct tm *tm, bool utc) {
        struct tm t;
        assert(tm);

        t = *tm;

        if (mktime_or_timegm(&t, utc) == (time_t) -1)
                return true;

        /* Did any normalization take place? If so, it was out of bounds before */
        return
                t.tm_year != tm->tm_year ||
                t.tm_mon != tm->tm_mon ||
                t.tm_mday != tm->tm_mday ||
                t.tm_hour != tm->tm_hour ||
                t.tm_min != tm->tm_min ||
                t.tm_sec != tm->tm_sec;
}
Exemplo n.º 6
0
int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) {
        struct tm tm;
        time_t t;
        int r;

        assert(spec);
        assert(next);

        t = (time_t) (usec / USEC_PER_SEC) + 1;
        assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));

        r = find_next(spec, &tm);
        if (r < 0)
                return r;

        t = mktime_or_timegm(&tm, spec->utc);
        if (t == (time_t) -1)
                return -EINVAL;

        *next = (usec_t) t * USEC_PER_SEC;
        return 0;
}
Exemplo n.º 7
0
static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
        struct tm c;
        int tm_usec;
        int r;

        assert(spec);
        assert(tm);

        c = *tm;
        tm_usec = *usec;

        for (;;) {
                /* Normalize the current date */
                (void) mktime_or_timegm(&c, spec->utc);
                c.tm_isdst = spec->dst;

                c.tm_year += 1900;
                r = find_matching_component(spec, spec->year, &c, &c.tm_year);
                c.tm_year -= 1900;

                if (r > 0) {
                        c.tm_mon = 0;
                        c.tm_mday = 1;
                        c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
                }
                if (r < 0)
                        return r;
                if (tm_out_of_bounds(&c, spec->utc))
                        return -ENOENT;

                c.tm_mon += 1;
                r = find_matching_component(spec, spec->month, &c, &c.tm_mon);
                c.tm_mon -= 1;

                if (r > 0) {
                        c.tm_mday = 1;
                        c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
                }
                if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
                        c.tm_year++;
                        c.tm_mon = 0;
                        c.tm_mday = 1;
                        c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
                        continue;
                }

                r = find_matching_component(spec, spec->day, &c, &c.tm_mday);
                if (r > 0)
                        c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
                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 = tm_usec = 0;
                        continue;
                }

                if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) {
                        c.tm_mday++;
                        c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
                        continue;
                }

                r = find_matching_component(spec, spec->hour, &c, &c.tm_hour);
                if (r > 0)
                        c.tm_min = c.tm_sec = tm_usec = 0;
                if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
                        c.tm_mday++;
                        c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
                        continue;
                }

                r = find_matching_component(spec, spec->minute, &c, &c.tm_min);
                if (r > 0)
                        c.tm_sec = tm_usec = 0;
                if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
                        c.tm_hour++;
                        c.tm_min = c.tm_sec = tm_usec = 0;
                        continue;
                }

                c.tm_sec = c.tm_sec * USEC_PER_SEC + tm_usec;
                r = find_matching_component(spec, spec->microsecond, &c, &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)) {
                        c.tm_min++;
                        c.tm_sec = tm_usec = 0;
                        continue;
                }

                *tm = c;
                *usec = tm_usec;
                return 0;
        }
}
Exemplo n.º 8
0
int parse_timestamp(const char *t, usec_t *usec) {
        static const struct {
                const char *name;
                const int nr;
        } day_nr[] = {
                { "Sunday",    0 },
                { "Sun",       0 },
                { "Monday",    1 },
                { "Mon",       1 },
                { "Tuesday",   2 },
                { "Tue",       2 },
                { "Wednesday", 3 },
                { "Wed",       3 },
                { "Thursday",  4 },
                { "Thu",       4 },
                { "Friday",    5 },
                { "Fri",       5 },
                { "Saturday",  6 },
                { "Sat",       6 },
        };

        const char *k;
        const char *utc;
        struct tm tm, copy;
        time_t x;
        usec_t x_usec, plus = 0, minus = 0, ret;
        int r, weekday = -1;
        unsigned i;

        /*
         * Allowed syntaxes:
         *
         *   2012-09-22 16:34:22
         *   2012-09-22 16:34     (seconds will be set to 0)
         *   2012-09-22           (time will be set to 00:00:00)
         *   16:34:22             (date will be set to today)
         *   16:34                (date will be set to today, seconds to 0)
         *   now
         *   yesterday            (time is set to 00:00:00)
         *   today                (time is set to 00:00:00)
         *   tomorrow             (time is set to 00:00:00)
         *   +5min
         *   -5days
         *   @2147483647          (seconds since epoch)
         *
         */

        assert(t);
        assert(usec);

        if (t[0] == '@')
                return parse_sec(t + 1, usec);

        ret = now(CLOCK_REALTIME);

        if (streq(t, "now"))
                goto finish;

        else if (t[0] == '+') {
                r = parse_sec(t+1, &plus);
                if (r < 0)
                        return r;

                goto finish;

        } else if (t[0] == '-') {
                r = parse_sec(t+1, &minus);
                if (r < 0)
                        return r;

                goto finish;

        } else if ((k = endswith(t, " ago"))) {
                t = strndupa(t, k - t);

                r = parse_sec(t, &minus);
                if (r < 0)
                        return r;

                goto finish;

        } else if ((k = endswith(t, " left"))) {
                t = strndupa(t, k - t);

                r = parse_sec(t, &plus);
                if (r < 0)
                        return r;

                goto finish;
        }

        utc = endswith_no_case(t, " UTC");
        if (utc)
                t = strndupa(t, utc - t);

        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;

                if (!startswith_no_case(t, day_nr[i].name))
                        continue;

                skip = strlen(day_nr[i].name);
                if (t[skip] != ' ')
                        continue;

                weekday = day_nr[i].nr;
                t += skip + 1;
                break;
        }

        copy = tm;
        k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
        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) {
                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 from_tm;
        }

        tm = copy;
        k = strptime(t, "%Y-%m-%d %H:%M", &tm);
        if (k && *k == 0) {
                tm.tm_sec = 0;
                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 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 from_tm;
        }

        tm = copy;
        k = strptime(t, "%H:%M:%S", &tm);
        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 from_tm;
        }

        return -EINVAL;

parse_usec:
        {
                unsigned add;

                k++;
                r = parse_fractional_part_u(&k, 6, &add);
                if (r < 0)
                        return -EINVAL;

                if (*k)
                        return -EINVAL;

                x_usec = add;

        }

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 + x_usec;

finish:
        ret += plus;
        if (ret > minus)
                ret -= minus;
        else
                ret = 0;

        *usec = ret;

        return 0;
}
Exemplo n.º 9
0
static int find_next(const CalendarSpec *spec, struct tm *tm) {
        struct tm c;
        int r;

        assert(spec);
        assert(tm);

        c = *tm;

        for (;;) {
                /* Normalize the current date */
                mktime_or_timegm(&c, spec->utc);
                c.tm_isdst = -1;

                c.tm_year += 1900;
                r = find_matching_component(spec->year, &c.tm_year);
                c.tm_year -= 1900;

                if (r > 0) {
                        c.tm_mon = 0;
                        c.tm_mday = 1;
                        c.tm_hour = c.tm_min = c.tm_sec = 0;
                }
                if (r < 0 || tm_out_of_bounds(&c, spec->utc))
                        return r;

                c.tm_mon += 1;
                r = find_matching_component(spec->month, &c.tm_mon);
                c.tm_mon -= 1;

                if (r > 0) {
                        c.tm_mday = 1;
                        c.tm_hour = c.tm_min = c.tm_sec = 0;
                }
                if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
                        c.tm_year ++;
                        c.tm_mon = 0;
                        c.tm_mday = 1;
                        c.tm_hour = c.tm_min = c.tm_sec = 0;
                        continue;
                }

                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, 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, spec->utc)) {
                        c.tm_mday++;
                        c.tm_hour = c.tm_min = c.tm_sec = 0;
                        continue;
                }

                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, spec->utc)) {
                        c.tm_mday ++;
                        c.tm_hour = c.tm_min = c.tm_sec = 0;
                        continue;
                }

                r = find_matching_component(spec->minute, &c.tm_min);
                if (r > 0)
                        c.tm_sec = 0;
                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, spec->utc)) {
                        c.tm_min ++;
                        c.tm_sec = 0;
                        continue;
                }


                *tm = c;
                return 0;
        }
}
Exemplo n.º 10
0
int parse_timestamp(const char *t, usec_t *usec) {
        static const struct {
                const char *name;
                const int nr;
        } day_nr[] = {
                { "Sunday",    0 },
                { "Sun",       0 },
                { "Monday",    1 },
                { "Mon",       1 },
                { "Tuesday",   2 },
                { "Tue",       2 },
                { "Wednesday", 3 },
                { "Wed",       3 },
                { "Thursday",  4 },
                { "Thu",       4 },
                { "Friday",    5 },
                { "Fri",       5 },
                { "Saturday",  6 },
                { "Sat",       6 },
        };

        const char *k, *utc, *tzn = NULL;
        struct tm tm, copy;
        time_t x;
        usec_t x_usec, plus = 0, minus = 0, ret;
        int r, weekday = -1, dst = -1;
        unsigned i;

        /*
         * Allowed syntaxes:
         *
         *   2012-09-22 16:34:22
         *   2012-09-22 16:34     (seconds will be set to 0)
         *   2012-09-22           (time will be set to 00:00:00)
         *   16:34:22             (date will be set to today)
         *   16:34                (date will be set to today, seconds to 0)
         *   now
         *   yesterday            (time is set to 00:00:00)
         *   today                (time is set to 00:00:00)
         *   tomorrow             (time is set to 00:00:00)
         *   +5min
         *   -5days
         *   @2147483647          (seconds since epoch)
         *
         */

        assert(t);
        assert(usec);

        if (t[0] == '@')
                return parse_sec(t + 1, usec);

        ret = now(CLOCK_REALTIME);

        if (streq(t, "now"))
                goto finish;

        else if (t[0] == '+') {
                r = parse_sec(t+1, &plus);
                if (r < 0)
                        return r;

                goto finish;

        } else if (t[0] == '-') {
                r = parse_sec(t+1, &minus);
                if (r < 0)
                        return r;

                goto finish;

        } else if ((k = endswith(t, " ago"))) {
                t = strndupa(t, k - t);

                r = parse_sec(t, &minus);
                if (r < 0)
                        return r;

                goto finish;

        } else if ((k = endswith(t, " left"))) {
                t = strndupa(t, k - t);

                r = parse_sec(t, &plus);
                if (r < 0)
                        return r;

                goto finish;
        }

        /* See if the timestamp is suffixed with UTC */
        utc = endswith_no_case(t, " UTC");
        if (utc)
                t = strndupa(t, utc - t);
        else {
                const char *e = NULL;
                int j;

                tzset();

                /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only
                 * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because
                 * there are no nice APIs available to cover 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++) {

                        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(t, e - t - 1);
                        dst = j;
                        tzn = tzname[j];
                }
        }

        x = (time_t) (ret / USEC_PER_SEC);
        x_usec = 0;

        if (!localtime_or_gmtime_r(&x, &tm, utc))
                return -EINVAL;

        tm.tm_isdst = dst;
        if (tzn)
                tm.tm_zone = tzn;

        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;

                if (!startswith_no_case(t, day_nr[i].name))
                        continue;

                skip = strlen(day_nr[i].name);
                if (t[skip] != ' ')
                        continue;

                weekday = day_nr[i].nr;
                t += skip + 1;
                break;
        }

        copy = tm;
        k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
        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) {
                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 from_tm;
        }

        tm = copy;
        k = strptime(t, "%Y-%m-%d %H:%M", &tm);
        if (k && *k == 0) {
                tm.tm_sec = 0;
                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 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 from_tm;
        }

        tm = copy;
        k = strptime(t, "%H:%M:%S", &tm);
        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 from_tm;
        }

        return -EINVAL;

parse_usec:
        {
                unsigned add;

                k++;
                r = parse_fractional_part_u(&k, 6, &add);
                if (r < 0)
                        return -EINVAL;

                if (*k)
                        return -EINVAL;

                x_usec = add;
        }

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 + x_usec;

finish:
        ret += plus;
        if (ret > minus)
                ret -= minus;
        else
                ret = 0;

        *usec = ret;

        return 0;
}