summaryrefslogtreecommitdiffstats
path: root/time.c
diff options
context:
space:
mode:
Diffstat (limited to 'time.c')
-rw-r--r--time.c323
1 files changed, 0 insertions, 323 deletions
diff --git a/time.c b/time.c
deleted file mode 100644
index c7331b5..0000000
--- a/time.c
+++ /dev/null
@@ -1,323 +0,0 @@
-/****************************************************************************
- * bfs *
- * Copyright (C) 2020 Tavian Barnes <tavianator@tavianator.com> *
- * *
- * Permission to use, copy, modify, and/or distribute this software for any *
- * purpose with or without fee is hereby granted. *
- * *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF *
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR *
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES *
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN *
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF *
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *
- ****************************************************************************/
-
-#include "time.h"
-#include <errno.h>
-#include <limits.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <time.h>
-
-/** Whether tzset() has been called. */
-static bool tz_is_set = false;
-
-int xlocaltime(const time_t *timep, struct tm *result) {
- // Should be called before localtime_r() according to POSIX.1-2004
- if (!tz_is_set) {
- tzset();
- tz_is_set = true;
- }
-
- if (localtime_r(timep, result)) {
- return 0;
- } else {
- return -1;
- }
-}
-
-int xgmtime(const time_t *timep, struct tm *result) {
- // Should be called before gmtime_r() according to POSIX.1-2004
- if (!tz_is_set) {
- tzset();
- tz_is_set = true;
- }
-
- if (gmtime_r(timep, result)) {
- return 0;
- } else {
- return -1;
- }
-}
-
-int xmktime(struct tm *tm, time_t *timep) {
- *timep = mktime(tm);
-
- if (*timep == -1) {
- int error = errno;
-
- struct tm tmp;
- if (xlocaltime(timep, &tmp) != 0) {
- return -1;
- }
-
- if (tm->tm_year != tmp.tm_year || tm->tm_yday != tmp.tm_yday
- || tm->tm_hour != tmp.tm_hour || tm->tm_min != tmp.tm_min || tm->tm_sec != tmp.tm_sec) {
- errno = error;
- return -1;
- }
- }
-
- return 0;
-}
-
-static int safe_add(int *value, int delta) {
- if (*value >= 0) {
- if (delta > INT_MAX - *value) {
- return -1;
- }
- } else {
- if (delta < INT_MIN - *value) {
- return -1;
- }
- }
-
- *value += delta;
- return 0;
-}
-
-static int floor_div(int n, int d) {
- int a = n < 0;
- return (n + a)/d - a;
-}
-
-static int wrap(int *value, int max, int *next) {
- int carry = floor_div(*value, max);
- *value -= carry * max;
- return safe_add(next, carry);
-}
-
-static int month_length(int year, int month) {
- static const int month_lengths[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
- int ret = month_lengths[month];
- if (month == 1 && year%4 == 0 && (year%100 != 0 || (year + 300)%400 == 0)) {
- ++ret;
- }
- return ret;
-}
-
-int xtimegm(struct tm *tm, time_t *timep) {
- tm->tm_isdst = 0;
-
- if (wrap(&tm->tm_sec, 60, &tm->tm_min) != 0) {
- goto overflow;
- }
- if (wrap(&tm->tm_min, 60, &tm->tm_hour) != 0) {
- goto overflow;
- }
- if (wrap(&tm->tm_hour, 24, &tm->tm_mday) != 0) {
- goto overflow;
- }
-
- // In order to wrap the days of the month, we first need to know what
- // month it is
- if (wrap(&tm->tm_mon, 12, &tm->tm_year) != 0) {
- goto overflow;
- }
-
- if (tm->tm_mday < 1) {
- do {
- --tm->tm_mon;
- if (wrap(&tm->tm_mon, 12, &tm->tm_year) != 0) {
- goto overflow;
- }
-
- tm->tm_mday += month_length(tm->tm_year, tm->tm_mon);
- } while (tm->tm_mday < 1);
- } else {
- while (true) {
- int days = month_length(tm->tm_year, tm->tm_mon);
- if (tm->tm_mday <= days) {
- break;
- }
-
- tm->tm_mday -= days;
- ++tm->tm_mon;
- if (wrap(&tm->tm_mon, 12, &tm->tm_year) != 0) {
- goto overflow;
- }
- }
- }
-
- tm->tm_yday = 0;
- for (int i = 0; i < tm->tm_mon; ++i) {
- tm->tm_yday += month_length(tm->tm_year, i);
- }
- tm->tm_yday += tm->tm_mday - 1;
-
- int leap_days;
- // Compute floor((year - 69)/4) - floor((year - 1)/100) + floor((year + 299)/400) without overflows
- if (tm->tm_year >= 0) {
- leap_days = floor_div(tm->tm_year - 69, 4) - floor_div(tm->tm_year - 1, 100) + floor_div(tm->tm_year - 101, 400) + 1;
- } else {
- leap_days = floor_div(tm->tm_year + 3, 4) - floor_div(tm->tm_year + 99, 100) + floor_div(tm->tm_year + 299, 400) - 17;
- }
-
- long long epoch_days = 365LL*(tm->tm_year - 70) + leap_days + tm->tm_yday;
- tm->tm_wday = (epoch_days + 4)%7;
- if (tm->tm_wday < 0) {
- tm->tm_wday += 7;
- }
-
- long long epoch_time = tm->tm_sec + 60*(tm->tm_min + 60*(tm->tm_hour + 24*epoch_days));
- *timep = (time_t)epoch_time;
- if ((long long)*timep != epoch_time) {
- goto overflow;
- }
- return 0;
-
-overflow:
- errno = EOVERFLOW;
- return -1;
-}
-
-/** Parse some digits from a timestamp. */
-static int parse_timestamp_part(const char **str, size_t n, int *result) {
- char buf[n + 1];
- for (size_t i = 0; i < n; ++i, ++*str) {
- char c = **str;
- if (c < '0' || c > '9') {
- return -1;
- }
- buf[i] = c;
- }
- buf[n] = '\0';
-
- *result = atoi(buf);
- return 0;
-}
-
-int parse_timestamp(const char *str, struct timespec *result) {
- struct tm tm = {
- .tm_isdst = -1,
- };
-
- int tz_hour = 0;
- int tz_min = 0;
- bool tz_negative = false;
- bool local = true;
-
- // YYYY
- if (parse_timestamp_part(&str, 4, &tm.tm_year) != 0) {
- goto invalid;
- }
- tm.tm_year -= 1900;
-
- // MM
- if (*str == '-') {
- ++str;
- }
- if (parse_timestamp_part(&str, 2, &tm.tm_mon) != 0) {
- goto invalid;
- }
- tm.tm_mon -= 1;
-
- // DD
- if (*str == '-') {
- ++str;
- }
- if (parse_timestamp_part(&str, 2, &tm.tm_mday) != 0) {
- goto invalid;
- }
-
- if (!*str) {
- goto end;
- } else if (*str == 'T') {
- ++str;
- }
-
- // hh
- if (parse_timestamp_part(&str, 2, &tm.tm_hour) != 0) {
- goto invalid;
- }
-
- // mm
- if (!*str) {
- goto end;
- } else if (*str == ':') {
- ++str;
- }
- if (parse_timestamp_part(&str, 2, &tm.tm_min) != 0) {
- goto invalid;
- }
-
- // ss
- if (!*str) {
- goto end;
- } else if (*str == ':') {
- ++str;
- }
- if (parse_timestamp_part(&str, 2, &tm.tm_sec) != 0) {
- goto invalid;
- }
-
- if (!*str) {
- goto end;
- } else if (*str == 'Z') {
- local = false;
- ++str;
- } else if (*str == '+' || *str == '-') {
- local = false;
- tz_negative = *str == '-';
- ++str;
-
- // hh
- if (parse_timestamp_part(&str, 2, &tz_hour) != 0) {
- goto invalid;
- }
-
- // mm
- if (!*str) {
- goto end;
- } else if (*str == ':') {
- ++str;
- }
- if (parse_timestamp_part(&str, 2, &tz_min) != 0) {
- goto invalid;
- }
- } else {
- goto invalid;
- }
-
- if (*str) {
- goto invalid;
- }
-
-end:
- if (local) {
- if (xmktime(&tm, &result->tv_sec) != 0) {
- goto error;
- }
- } else {
- if (xtimegm(&tm, &result->tv_sec) != 0) {
- goto error;
- }
-
- int offset = 60*tz_hour + tz_min;
- if (tz_negative) {
- result->tv_sec -= offset;
- } else {
- result->tv_sec += offset;
- }
- }
-
- result->tv_nsec = 0;
- return 0;
-
-invalid:
- errno = EINVAL;
-error:
- return -1;
-}