libs/libc/time/strftime: complete the strftime implementation
1. make the strftime implementation can pass the ltp/open_posix_testsuite/strftime/1-1 testcase 2. the modification are referred to https://pubs.opengroup.org/onlinepubs/9699919799/functions/strftime.html Signed-off-by: guoshichao <guoshichao@xiaomi.com>
This commit is contained in:
parent
a40f07640f
commit
083c11ef1a
@ -25,6 +25,7 @@
|
||||
#include <nuttx/config.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <debug.h>
|
||||
|
||||
@ -81,6 +82,115 @@ static const char * const g_monthname[12] =
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: is_leap
|
||||
*
|
||||
* Description:
|
||||
* determine if the given year is a leap year or not
|
||||
*
|
||||
* Input Parameters:
|
||||
* year - a year value
|
||||
*
|
||||
* Returnd value:
|
||||
* true if current is leap year, false is not a leap year
|
||||
*/
|
||||
|
||||
static bool is_leap(int year)
|
||||
{
|
||||
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: get_week_num
|
||||
*
|
||||
* Description:
|
||||
* get the week number in a year based on iso8601 standard
|
||||
*
|
||||
* Input Parameters:
|
||||
* time - the specified time
|
||||
*
|
||||
* Returnd value:
|
||||
* the week numer in a year
|
||||
*/
|
||||
|
||||
static int get_week_num(FAR const struct tm *time)
|
||||
{
|
||||
/* calculate the total week number in a year */
|
||||
|
||||
int week = (time->tm_yday + DAYSPERWEEK -
|
||||
(time->tm_wday + 6) % DAYSPERWEEK) / DAYSPERWEEK;
|
||||
|
||||
/* if xxxx-1-1 is just passed 1-3 days after Monday
|
||||
* then the previous week will calculated into this year
|
||||
*/
|
||||
|
||||
if ((time->tm_wday + 371 - time->tm_yday - 2) % DAYSPERWEEK <= 2)
|
||||
{
|
||||
week++;
|
||||
}
|
||||
|
||||
if (week == 0)
|
||||
{
|
||||
week = 52;
|
||||
|
||||
/* if xxxx-12-31 is Thursday or Friday, and the previous year is
|
||||
* leap year, then the previous year has 53 weeks
|
||||
*/
|
||||
|
||||
int dec31 = (time->tm_wday + DAYSPERWEEK - time->tm_yday - 1)
|
||||
% DAYSPERWEEK;
|
||||
if (dec31 == TM_THURSDAY ||
|
||||
(dec31 == TM_FRIDAY && is_leap(time->tm_year % 400 - 1)))
|
||||
{
|
||||
week++;
|
||||
}
|
||||
}
|
||||
else if (week == 53)
|
||||
{
|
||||
/* If xxxx-1.1 is not a Thursday, and not a Wednesday of a leap year,
|
||||
* then this year has only 52 weeks
|
||||
*/
|
||||
|
||||
int jan1 = (time->tm_wday + 371 - time->tm_yday) % DAYSPERWEEK;
|
||||
if (jan1 != TM_THURSDAY &&
|
||||
(jan1 != TM_WEDNESDAY || !is_leap(time->tm_year)))
|
||||
{
|
||||
week = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return week;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: get_week_year
|
||||
*
|
||||
* Description:
|
||||
* get the week year based on iso8601 standard
|
||||
*
|
||||
* Input Parameters:
|
||||
* time - the specified time
|
||||
*
|
||||
* Returnd value:
|
||||
* the year that calculated based on week number
|
||||
*/
|
||||
|
||||
static int get_week_year(FAR const struct tm *time)
|
||||
{
|
||||
int week_num = get_week_num(time);
|
||||
int week_year = time->tm_year + TM_YEAR_BASE;
|
||||
if (time->tm_yday < 3 && week_num != 1)
|
||||
{
|
||||
week_year--;
|
||||
}
|
||||
else if (time->tm_yday > 360 && week_num == 1)
|
||||
{
|
||||
week_year++;
|
||||
}
|
||||
|
||||
return week_year;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
@ -106,6 +216,10 @@ static const char * const g_monthname[12] =
|
||||
* %d The day of the month as a decimal number (range 01 to 31).
|
||||
* %e Like %d, the day of the month as a decimal number, but a leading
|
||||
* zero is replaced by a space.
|
||||
* %F The full date format but with no time fields
|
||||
* %g The last 2 digits of the week-based year as a decimal number
|
||||
* [00,99]
|
||||
* %G The full version of %g, display the full year value
|
||||
* %h Equivalent to %b. (SU)
|
||||
* %H The hour as a decimal number using a 24-hour clock
|
||||
* (range 00 to 23).
|
||||
@ -124,14 +238,25 @@ static const char * const g_monthname[12] =
|
||||
* as "PM" and midnight as "AM".
|
||||
* %P Like %p but in lowercase: "am" or "pm" or a corresponding string
|
||||
* for the current locale. (GNU)
|
||||
* %r The time in a.m. and p.m. notation
|
||||
* %R The time in 24-hour notation ( %H : %M ).
|
||||
* %s The number of seconds since the Epoch, that is, since 1970-01-01
|
||||
* 00:00:00 UTC. (TZ)
|
||||
* %S The second as a decimal number (range 00 to 60). (The range is
|
||||
* up to 60 to allow for occasional leap seconds.)
|
||||
* %t A tab character. (SU)
|
||||
* %u The weekday as a decimal number [1,7], with 1 representing
|
||||
* Monday.
|
||||
* %U The week number of the year as a decimal number [00,53].
|
||||
* %V The week number of the year
|
||||
* %w The weekday as a decimal number (range 0 to 6).
|
||||
* %W The week number of the year as a decimal number [00,53].
|
||||
* %x The locale's appropriate date representation, but without time.
|
||||
* %X The locale's appropriate time representation, but without date.
|
||||
* %y The year as a decimal number without a century (range 00 to 99).
|
||||
* %Y The year as a decimal number including the century.
|
||||
* %z The timezone name or abbreviation, or by no bytes if no timezone
|
||||
* information exists.
|
||||
* %% A literal '%' character.
|
||||
*
|
||||
* Returned Value:
|
||||
@ -251,6 +376,33 @@ size_t strftime(FAR char *s, size_t max, FAR const char *format,
|
||||
}
|
||||
break;
|
||||
|
||||
/* %F: ISO 8601 date format: "%Y-%m-%d" */
|
||||
|
||||
case 'F':
|
||||
{
|
||||
len = snprintf(dest, chleft, "%04d-%02d-%02d",
|
||||
tm->tm_year + TM_YEAR_BASE, tm->tm_mon,
|
||||
tm->tm_mday);
|
||||
}
|
||||
break;
|
||||
|
||||
/* %g: 2-digit year version of %G, (00-99) */
|
||||
|
||||
case 'g':
|
||||
{
|
||||
value = get_week_year(tm) % 100;
|
||||
len = snprintf(dest, chleft, "%02d", value);
|
||||
}
|
||||
break;
|
||||
|
||||
/* %G: ISO 8601 week based year */
|
||||
|
||||
case 'G':
|
||||
{
|
||||
len = snprintf(dest, chleft, "%04d", get_week_year(tm));
|
||||
}
|
||||
break;
|
||||
|
||||
/* %H: The hour as a decimal number using a 24-hour clock
|
||||
* (range 00 to 23).
|
||||
*/
|
||||
@ -369,6 +521,28 @@ size_t strftime(FAR char *s, size_t max, FAR const char *format,
|
||||
}
|
||||
break;
|
||||
|
||||
/* %r: 12-hour clock time */
|
||||
|
||||
case 'r':
|
||||
{
|
||||
if (tm->tm_hour >= 12)
|
||||
{
|
||||
str = "pm";
|
||||
}
|
||||
else
|
||||
{
|
||||
str = "am";
|
||||
}
|
||||
|
||||
value = tm->tm_hour == 12 ?
|
||||
tm->tm_hour == 12 :
|
||||
tm->tm_hour % (HOURSPERDAY / 2);
|
||||
|
||||
len = snprintf(dest, chleft, "%02d:%02d:%02d %s",
|
||||
value, tm->tm_min, tm->tm_sec, str);
|
||||
}
|
||||
break;
|
||||
|
||||
/* %R: Shortcut for %H:%M. */
|
||||
|
||||
case 'R':
|
||||
@ -418,6 +592,39 @@ size_t strftime(FAR char *s, size_t max, FAR const char *format,
|
||||
}
|
||||
break;
|
||||
|
||||
/* %u: The day of the week as a decimal, (1-7). Monday being 1,
|
||||
* Sunday being 0.
|
||||
*/
|
||||
|
||||
case 'u':
|
||||
{
|
||||
value = tm->tm_wday == 0 ? 7 : tm->tm_wday;
|
||||
len = snprintf(dest, chleft, "%d", value);
|
||||
}
|
||||
break;
|
||||
|
||||
/* %U: week number of the current year as a decimal number,
|
||||
* (00-53). Starting with the first Sunday as the first day
|
||||
* of week 01.
|
||||
*/
|
||||
|
||||
case 'U':
|
||||
{
|
||||
value = (tm->tm_yday + DAYSPERWEEK - tm->tm_wday)
|
||||
/ DAYSPERWEEK;
|
||||
len = snprintf(dest, chleft, "%02d", value);
|
||||
}
|
||||
break;
|
||||
|
||||
/* %V: ISO 8601 week number */
|
||||
|
||||
case 'V':
|
||||
{
|
||||
value = get_week_num(tm);
|
||||
len = snprintf(dest, chleft, "%02d", value);
|
||||
}
|
||||
break;
|
||||
|
||||
/* %w: The weekday as a decimal number (range 0 to 6). */
|
||||
|
||||
case 'w':
|
||||
@ -426,6 +633,39 @@ size_t strftime(FAR char *s, size_t max, FAR const char *format,
|
||||
}
|
||||
break;
|
||||
|
||||
/* %W: Week number of the current year as a decimal number,
|
||||
* (00-53). Starting with the first Monday as the first day
|
||||
* of week 01.
|
||||
*/
|
||||
|
||||
case 'W':
|
||||
{
|
||||
value = (tm->tm_yday + DAYSPERWEEK -
|
||||
(tm->tm_wday + 6) % DAYSPERWEEK)
|
||||
/ DAYSPERWEEK;
|
||||
len = snprintf(dest, chleft, "%02d", value);
|
||||
}
|
||||
break;
|
||||
|
||||
/* %x Locale date without time */
|
||||
|
||||
case 'x':
|
||||
{
|
||||
len = snprintf(dest, chleft, "%02d/%02d/%04d",
|
||||
tm->tm_mon, tm->tm_mday,
|
||||
tm->tm_year + TM_YEAR_BASE);
|
||||
}
|
||||
break;
|
||||
|
||||
/* %X: Locale time without date */
|
||||
|
||||
case 'X':
|
||||
{
|
||||
len = snprintf(dest, chleft, "%02d:%02d:%02d",
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
}
|
||||
break;
|
||||
|
||||
/* %y: The year as a decimal number without a century
|
||||
* (range 00 to 99).
|
||||
*/
|
||||
@ -445,6 +685,19 @@ size_t strftime(FAR char *s, size_t max, FAR const char *format,
|
||||
}
|
||||
break;
|
||||
|
||||
/* %z: Numeric timezone as hour and minute offset from UTC
|
||||
* "+hhmm" or "-hhmm"
|
||||
*/
|
||||
|
||||
case 'z':
|
||||
{
|
||||
int hour = tm->tm_gmtoff / 3600;
|
||||
int min = tm->tm_gmtoff % 3600 / 60;
|
||||
int utc_val = hour * 100 + min;
|
||||
len = snprintf(dest, chleft, "+%04d", utc_val);
|
||||
}
|
||||
break;
|
||||
|
||||
/* %%: A literal '%' character. */
|
||||
|
||||
case '%':
|
||||
|
Loading…
Reference in New Issue
Block a user