libs/libc/time: mktime normalize struct tm

Signed-off-by: Petro Karashchenko <petro.karashchenko@gmail.com>
This commit is contained in:
Petro Karashchenko 2022-10-27 00:16:07 +02:00 committed by Xiang Xiao
parent fd07e7b012
commit fdff92fd19
8 changed files with 211 additions and 74 deletions

View file

@ -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
****************************************************************************/

View file

@ -25,7 +25,8 @@
#include <nuttx/config.h>
#include <stdio.h>
#include <time.h>
#include <nuttx/time.h>
/****************************************************************************
* 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;

View file

@ -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. */

View file

@ -25,7 +25,6 @@
#include <nuttx/config.h>
#include <stdbool.h>
#include <time.h>
#include <errno.h>
#include <debug.h>
@ -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 +

View file

@ -52,13 +52,13 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <limits.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <nuttx/time.h>
#include <nuttx/init.h>
#include <nuttx/fs/fs.h>
#include <nuttx/mutex.h>
@ -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

View file

@ -26,7 +26,6 @@
#include <sys/types.h>
#include <stdio.h>
#include <time.h>
#include <debug.h>
#include <nuttx/time.h>
@ -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;

View file

@ -35,13 +35,13 @@
#include <ctype.h>
#include <locale.h>
#include <strings.h>
#include <time.h>
#include <nuttx/time.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define TM_YEAR_BASE 1900
#define _ctloc(x) (g_defaulttimelocale.x)
/* We do not implement alternate representations. However, we always

View file

@ -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);