From fdff92fd19d91e55e362570fbfc22ffe70a41084 Mon Sep 17 00:00:00 2001 From: Petro Karashchenko Date: Thu, 27 Oct 2022 00:16:07 +0200 Subject: [PATCH] libs/libc/time: mktime normalize struct tm Signed-off-by: Petro Karashchenko --- include/nuttx/time.h | 45 ++++++++-- libs/libc/time/lib_asctimer.c | 5 +- libs/libc/time/lib_calendar2utc.c | 10 +-- libs/libc/time/lib_gmtimer.c | 35 ++++---- libs/libc/time/lib_localtime.c | 38 +------- libs/libc/time/lib_strftime.c | 8 +- libs/libc/time/lib_strptime.c | 4 +- libs/libc/time/lib_timegm.c | 140 +++++++++++++++++++++++++++++- 8 files changed, 211 insertions(+), 74 deletions(-) diff --git a/include/nuttx/time.h b/include/nuttx/time.h index 8a5ee73d45..7007225864 100644 --- a/include/nuttx/time.h +++ b/include/nuttx/time.h @@ -39,16 +39,51 @@ #ifndef CONFIG_GREGORIAN_TIME # undef CONFIG_JULIAN_TIME #else -# define JD_OF_EPOCH 2440588 /* Julian Date of noon, J1970 */ +# define JD_OF_EPOCH 2440588 /* Julian Date of noon, J1970 */ # ifdef CONFIG_JULIAN_TIME -# define GREG_DUTC -141427 /* Default is October 15, 1582 */ -# define GREG_YEAR 1582 -# define GREG_MONTH 10 -# define GREG_DAY 15 +# define GREG_DUTC -141427 /* Default is October 15, 1582 */ +# define GREG_YEAR 1582 +# define GREG_MONTH 10 +# define GREG_DAY 15 # endif /* CONFIG_JULIAN_TIME */ #endif /* !CONFIG_GREGORIAN_TIME */ +#define SECSPERMIN 60 +#define MINSPERHOUR 60 +#define HOURSPERDAY 24 +#define DAYSPERWEEK 7 +#define DAYSPERNYEAR 365 +#define DAYSPERLYEAR 366 +#define MONSPERYEAR 12 + +#define TM_SUNDAY 0 +#define TM_MONDAY 1 +#define TM_TUESDAY 2 +#define TM_WEDNESDAY 3 +#define TM_THURSDAY 4 +#define TM_FRIDAY 5 +#define TM_SATURDAY 6 + +#define TM_JANUARY 0 +#define TM_FEBRUARY 1 +#define TM_MARCH 2 +#define TM_APRIL 3 +#define TM_MAY 4 +#define TM_JUNE 5 +#define TM_JULY 6 +#define TM_AUGUST 7 +#define TM_SEPTEMBER 8 +#define TM_OCTOBER 9 +#define TM_NOVEMBER 10 +#define TM_DECEMBER 11 + +#define TM_YEAR_BASE 1900 +#define TM_WDAY_BASE TM_MONDAY + +#define EPOCH_YEAR 1970 +#define EPOCH_WDAY TM_THURSDAY + /**************************************************************************** * Public Data ****************************************************************************/ diff --git a/libs/libc/time/lib_asctimer.c b/libs/libc/time/lib_asctimer.c index b3ee92be5c..3f32301ef9 100644 --- a/libs/libc/time/lib_asctimer.c +++ b/libs/libc/time/lib_asctimer.c @@ -25,7 +25,8 @@ #include #include -#include + +#include /**************************************************************************** * Private Data @@ -75,7 +76,7 @@ FAR char *asctime_r(FAR const struct tm *tp, FAR char *buf) snprintf(tmp, sizeof(tmp), "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", g_wday_name[tp->tm_wday], g_mon_name[tp->tm_mon], tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec, - 1900 + tp->tm_year); + TM_YEAR_BASE + tp->tm_year); strlcpy(buf, tmp, 26); return buf; diff --git a/libs/libc/time/lib_calendar2utc.c b/libs/libc/time/lib_calendar2utc.c index 1be1fdb89d..41920b098e 100644 --- a/libs/libc/time/lib_calendar2utc.c +++ b/libs/libc/time/lib_calendar2utc.c @@ -95,13 +95,13 @@ time_t clock_calendar2utc(int year, int month, int day) /* Correct year & month ranges. Shift month into range 1-12 */ - dyear = (month - 1) / 12; - month -= 12 * dyear; + dyear = (month - 1) / MONSPERYEAR; + month -= MONSPERYEAR * dyear; year += dyear; if (month < 1) { - month += 12; + month += MONSPERYEAR; year -= 1; } @@ -158,11 +158,11 @@ time_t clock_calendar2utc(int year, int month, int day) /* Years since epoch in units of days (ignoring leap years). */ - days = (year - 1970) * 365; + days = (year - EPOCH_YEAR) * DAYSPERNYEAR; /* Add in the extra days for the leap years prior to the current year. */ - days += (year - 1969) >> 2; + days += (year - EPOCH_YEAR - 1) >> 2; /* Add in the days up to the beginning of this month. */ diff --git a/libs/libc/time/lib_gmtimer.c b/libs/libc/time/lib_gmtimer.c index efb35b24b4..64370d2802 100644 --- a/libs/libc/time/lib_gmtimer.c +++ b/libs/libc/time/lib_gmtimer.c @@ -25,7 +25,6 @@ #include #include -#include #include #include @@ -161,20 +160,20 @@ static void clock_utc2calendar(time_t days, FAR int *year, FAR int *month, * following: */ - value = days / (4 * 365 + 1); /* Number of 4-years periods since the epoch */ - days -= value * (4 * 365 + 1); /* Remaining days */ - value <<= 2; /* Years since the epoch */ + value = days / (4 * DAYSPERNYEAR + 1); /* Number of 4-years periods since the epoch */ + days -= value * (4 * DAYSPERNYEAR + 1); /* Remaining days */ + value <<= 2; /* Years since the epoch */ /* Then we will brute force the next 0-3 years * * Is this year a leap year? (we'll need this later too) */ - leapyear = clock_isleapyear(value + 1970); + leapyear = clock_isleapyear(value + EPOCH_YEAR); /* Get the number of days in the year */ - tmp = (leapyear ? 366 : 365); + tmp = (leapyear ? DAYSPERLYEAR : DAYSPERNYEAR); /* Do we have that many days left to account for? */ @@ -187,11 +186,11 @@ static void clock_utc2calendar(time_t days, FAR int *year, FAR int *month, /* Is the next year a leap year? */ - leapyear = clock_isleapyear(value + 1970); + leapyear = clock_isleapyear(value + EPOCH_YEAR); /* Get the number of days in the next year */ - tmp = (leapyear ? 366 : 365); + tmp = (leapyear ? DAYSPERLYEAR : DAYSPERNYEAR); } /* At this point, 'value' has the years since 1970 and 'days' has number @@ -199,7 +198,7 @@ static void clock_utc2calendar(time_t days, FAR int *year, FAR int *month, * a leap year. */ - *year = 1970 + value; + *year = EPOCH_YEAR + value; /* Handle the month (zero based) */ @@ -314,24 +313,22 @@ FAR struct tm *gmtime_r(FAR const time_t *timep, FAR struct tm *result) sec = epoch; - linfo("hour=%d min=%d sec=%d\n", - (int)hour, (int)min, (int)sec); + linfo("hour=%d min=%d sec=%d\n", hour, min, sec); /* Convert the days since the EPOCH to calendar day */ clock_utc2calendar(jdn, &year, &month, &day); - linfo("jdn=%d year=%d month=%d day=%d\n", - (int)jdn, (int)year, (int)month, (int)day); + linfo("jdn=%d year=%d month=%d day=%d\n", (int)jdn, year, month, day); /* Then return the struct tm contents */ - result->tm_year = (int)year - 1900; /* Relative to 1900 */ - result->tm_mon = (int)month - 1; /* zero-based */ - result->tm_mday = (int)day; /* one-based */ - result->tm_hour = (int)hour; - result->tm_min = (int)min; - result->tm_sec = (int)sec; + result->tm_year = year - TM_YEAR_BASE; /* Relative to 1900 */ + result->tm_mon = month - 1; /* zero-based */ + result->tm_mday = day; /* one-based */ + result->tm_hour = hour; + result->tm_min = min; + result->tm_sec = sec; result->tm_wday = clock_dayoftheweek(day, month, year); result->tm_yday = day - 1 + diff --git a/libs/libc/time/lib_localtime.c b/libs/libc/time/lib_localtime.c index e14a9e4b7e..528dc86688 100644 --- a/libs/libc/time/lib_localtime.c +++ b/libs/libc/time/lib_localtime.c @@ -52,13 +52,13 @@ #include #include -#include #include #include #include #include #include +#include #include #include #include @@ -94,42 +94,8 @@ #define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ #define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ -#define SECSPERMIN 60 -#define MINSPERHOUR 60 -#define HOURSPERDAY 24 -#define DAYSPERWEEK 7 -#define DAYSPERNYEAR 365 -#define DAYSPERLYEAR 366 #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) #define SECSPERDAY ((int_fast32_t)SECSPERHOUR * HOURSPERDAY) -#define MONSPERYEAR 12 - -#define TM_SUNDAY 0 -#define TM_MONDAY 1 -#define TM_TUESDAY 2 -#define TM_WEDNESDAY 3 -#define TM_THURSDAY 4 -#define TM_FRIDAY 5 -#define TM_SATURDAY 6 - -#define TM_JANUARY 0 -#define TM_FEBRUARY 1 -#define TM_MARCH 2 -#define TM_APRIL 3 -#define TM_MAY 4 -#define TM_JUNE 5 -#define TM_JULY 6 -#define TM_AUGUST 7 -#define TM_SEPTEMBER 8 -#define TM_OCTOBER 9 -#define TM_NOVEMBER 10 -#define TM_DECEMBER 11 - -#define TM_YEAR_BASE 1900 -#define TM_WDAY_BASE TM_MONDAY - -#define EPOCH_YEAR 1970 -#define EPOCH_WDAY TM_THURSDAY #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) @@ -2488,7 +2454,7 @@ static time_t time2sub(FAR struct tm *tmp, t = hi; } - if ((*funcp) (&t, offset, &mytm) == NULL) + if ((*funcp)(&t, offset, &mytm) == NULL) { /* Assume that t is too extreme to be represented in * a struct tm; arrange things so that it is less diff --git a/libs/libc/time/lib_strftime.c b/libs/libc/time/lib_strftime.c index b38ee976a0..43be9a03d8 100644 --- a/libs/libc/time/lib_strftime.c +++ b/libs/libc/time/lib_strftime.c @@ -26,7 +26,6 @@ #include #include -#include #include #include @@ -384,8 +383,8 @@ size_t strftime(FAR char *s, size_t max, FAR const char *format, case 's': { - len = snprintf(dest, chleft, "%ju", - (uintmax_t)mktime((FAR struct tm *)tm)); + struct tm tmp = *tm; + len = snprintf(dest, chleft, "%ju", (uintmax_t)mktime(&tmp)); } break; @@ -439,7 +438,8 @@ size_t strftime(FAR char *s, size_t max, FAR const char *format, case 'Y': { - len = snprintf(dest, chleft, "%04d", tm->tm_year + 1900); + len = snprintf(dest, chleft, "%04d", + tm->tm_year + TM_YEAR_BASE); } break; diff --git a/libs/libc/time/lib_strptime.c b/libs/libc/time/lib_strptime.c index c2723550dd..789076858d 100644 --- a/libs/libc/time/lib_strptime.c +++ b/libs/libc/time/lib_strptime.c @@ -35,13 +35,13 @@ #include #include #include -#include + +#include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ -#define TM_YEAR_BASE 1900 #define _ctloc(x) (g_defaulttimelocale.x) /* We do not implement alternate representations. However, we always diff --git a/libs/libc/time/lib_timegm.c b/libs/libc/time/lib_timegm.c index 86471d95ad..71cf39ac86 100644 --- a/libs/libc/time/lib_timegm.c +++ b/libs/libc/time/lib_timegm.c @@ -53,10 +53,143 @@ * Private Data ****************************************************************************/ +static const int g_mon_lengths[2][MONSPERYEAR] = +{ + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +}; + /**************************************************************************** * Private Functions ****************************************************************************/ +static void adjust(FAR int *tenx, FAR int *x, int base) +{ + while (*x < 0) + { + *x += base; + (*tenx)--; + } + + while (*x > (base - 1)) + { + *x -= base; + (*tenx)++; + } +} + +static void normalize(FAR struct tm *tm) +{ + bool leapyear = false; + int year; + + for (; ; ) + { + /* Adjust mon field */ + + adjust(&tm->tm_year, &tm->tm_mon, MONSPERYEAR); + + /* Get an absolute year */ + + year = tm->tm_year + TM_YEAR_BASE; + + /* Is this a leap year? */ + + leapyear = clock_isleapyear(year); + + /* Adjust mday field */ + + while (tm->tm_mday < 1) + { + tm->tm_mon--; + if (tm->tm_mon < 0) + { + tm->tm_mday += g_mon_lengths[leapyear][TM_DECEMBER]; + break; + } + + tm->tm_mday += g_mon_lengths[leapyear][tm->tm_mon]; + } + + if (tm->tm_mon < 0) + { + continue; + } + + while (tm->tm_mday > g_mon_lengths[leapyear][tm->tm_mon]) + { + tm->tm_mday -= g_mon_lengths[leapyear][tm->tm_mon]; + tm->tm_mon++; + if (tm->tm_mon > (MONSPERYEAR - 1)) + { + break; + } + } + + if (tm->tm_mon > (MONSPERYEAR - 1)) + { + continue; + } + + /* Adjust seconds field */ + + adjust(&tm->tm_min, &tm->tm_sec, SECSPERMIN); + + /* Adjust minutes field */ + + adjust(&tm->tm_hour, &tm->tm_min, MINSPERHOUR); + + /* Adjust hours field */ + + while (tm->tm_hour < 0) + { + tm->tm_hour += HOURSPERDAY; + tm->tm_mday--; + + if (tm->tm_mday < 1) + { + break; + } + } + + if (tm->tm_mday < 1) + { + continue; + } + + while (tm->tm_hour > (HOURSPERDAY - 1)) + { + tm->tm_hour -= HOURSPERDAY; + tm->tm_mday++; + + if (tm->tm_mday > g_mon_lengths[leapyear][tm->tm_mon]) + { + break; + } + } + + if (tm->tm_mday > g_mon_lengths[leapyear][tm->tm_mon]) + { + continue; + } + + break; + } + + /* Update the years field */ + + tm->tm_year = year - TM_YEAR_BASE; + + /* Determine the day of the year; -1 because the mday is 1-indexed */ + + tm->tm_yday = tm->tm_mday - 1 + clock_daysbeforemonth(tm->tm_mon, + leapyear); + + /* Finally calculate the weekday */ + + tm->tm_wday = clock_dayoftheweek(tm->tm_mday, tm->tm_mon + 1, year); +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -74,11 +207,16 @@ time_t timegm(FAR struct tm *tp) time_t ret; time_t jdn; + /* Normalize struct tm */ + + normalize(tp); + /* Get the EPOCH-relative julian date from the calendar year, * month, and date */ - jdn = clock_calendar2utc(tp->tm_year + 1900, tp->tm_mon, tp->tm_mday); + jdn = clock_calendar2utc(tp->tm_year + TM_YEAR_BASE, tp->tm_mon, + tp->tm_mday); linfo("jdn=%d tm_year=%d tm_mon=%d tm_mday=%d\n", (int)jdn, tp->tm_year, tp->tm_mon, tp->tm_mday);