From 4a3bbead361b7c58c8259c3fcfc13a04866fd622 Mon Sep 17 00:00:00 2001 From: P. J. McDermott Date: Mon, 28 Mar 2022 19:49:46 -0400 Subject: datetime: Don't use mktime()/localtime() shortcuts --- (limited to 'src') diff --git a/src/datetime.c b/src/datetime.c index ccf4c29..2159bb2 100644 --- a/src/datetime.c +++ b/src/datetime.c @@ -86,7 +86,13 @@ _datetime_reset_tm(struct tm *tm) static void _datetime_normalize(struct tm *now_tm, struct tm *arg_tm, time_t *arg_sec) { - int wday; + int days_in_mon[12] = {31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31}; + + if (now_tm->tm_year % 4 == 0 && (now_tm->tm_year % 100 != 0 || + now_tm->tm_year % 400 == 0)) { + days_in_mon[1] = 29; + } if (arg_tm->tm_sec == INT_MIN) { arg_tm->tm_sec = 0; @@ -100,29 +106,37 @@ _datetime_normalize(struct tm *now_tm, struct tm *arg_tm, time_t *arg_sec) *arg_sec = mktime(arg_tm); if (*arg_sec <= mktime(now_tm)) { /* Specified time already happened today; use tomorrow. - * Adding the number of seconds in a day is a shortcut - * that ignores leap seconds. One better method would - * be to increment tm_mday % days in tm_mon, etc. */ - *arg_sec += 60 * 60 * 24; - now_tm = localtime(arg_sec); - arg_tm->tm_year = now_tm->tm_year; - arg_tm->tm_mon = now_tm->tm_mon; - arg_tm->tm_mday = now_tm->tm_mday; + */ + ++arg_tm->tm_mday; + arg_tm->tm_wday = (arg_tm->tm_wday + 1) % 7; + if (arg_tm->tm_mday > days_in_mon[arg_tm->tm_mon]) { + arg_tm->tm_mday = 1; + ++arg_tm->tm_mon; + if (arg_tm->tm_mon >= 12) { + arg_tm->tm_mon = 0; + ++arg_tm->tm_year; + } + } *arg_sec = mktime(arg_tm); } } else if (arg_tm->tm_mday == INT_MIN) { - /* Only a weekday specified; try tomorrow or next week. Uses - * same shortcut as above. */ - wday = arg_tm->tm_wday; + /* Only a weekday specified; try tomorrow or next week. */ arg_tm->tm_year = now_tm->tm_year; arg_tm->tm_mon = now_tm->tm_mon; arg_tm->tm_mday = now_tm->tm_mday; - arg_tm->tm_wday = now_tm->tm_wday; - if (wday <= now_tm->tm_wday) { - wday += 7; + if (arg_tm->tm_wday <= now_tm->tm_wday) { + arg_tm->tm_mday += 7; + } + arg_tm->tm_mday += arg_tm->tm_wday - now_tm->tm_wday; + if (arg_tm->tm_mday > days_in_mon[arg_tm->tm_mon]) { + arg_tm->tm_mday -= days_in_mon[arg_tm->tm_mon]; + ++arg_tm->tm_mon; + if (arg_tm->tm_mon >= 12) { + arg_tm->tm_mon = 0; + ++arg_tm->tm_year; + } } *arg_sec = mktime(arg_tm); - *arg_sec += (60 * 60 * 24) * (wday - now_tm->tm_wday); } else if (arg_tm->tm_year == INT_MIN) { /* No year specified; try this year. */ arg_tm->tm_year = now_tm->tm_year; -- cgit v0.9.1