This is an automated email from the ASF dual-hosted git repository.

lupyuen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 116a3257637 libs/libc/time: Add configuration options for the strftime
116a3257637 is described below

commit 116a325763749f5649e49d63356770d763aae6cb
Author: Jukka Laitinen <[email protected]>
AuthorDate: Tue Jun 2 09:48:36 2026 +0300

    libs/libc/time: Add configuration options for the strftime
    
    This adds 3 configuration options for the lib_strftime, which can be
    used to save flash memory when all the formatters are not needed by an
    embedded application.
    
    There is always a minimal set of formatters supported:
    "%a, %b/%h, %d, %H, %m, %M, %S, %Y, %%". To add on top of that one can
    specify:
    
    - LIBC_STRFTIME_C_STANDARD_FORMATS  : All ISO-C conversion specifiers
    - LIBC_STRFTIME_POSIX_FORMATS       : Additional posix formats
    - LIBC_STRFTIME_NONSTANDARD_FORMATS : Additional GNU nonstandard formats
    
    All of these are enabled by default unless building for 
CONFIG_DEFAULT_SMALL.
    Disabling these options can save over 3KB of flash on an 32-bit
    ARM system, when all the format specifiers are not needed.
    
    Signed-off-by: Jukka Laitinen <[email protected]>
---
 libs/libc/time/Kconfig        |  35 ++++
 libs/libc/time/lib_strftime.c | 419 ++++++++++++++++++++++--------------------
 2 files changed, 258 insertions(+), 196 deletions(-)

diff --git a/libs/libc/time/Kconfig b/libs/libc/time/Kconfig
index 20abdfc3bc3..d59119e47eb 100644
--- a/libs/libc/time/Kconfig
+++ b/libs/libc/time/Kconfig
@@ -5,6 +5,41 @@
 
 menu "Time/Time Zone Support"
 
+config LIBC_STRFTIME_C_STANDARD_FORMATS
+       bool "strftime ISO C conversion specifiers"
+       default !DEFAULT_SMALL
+       ---help---
+               Enable ISO C strftime conversion specifiers that are not part of
+               the small always-enabled subset.  Disabling this option
+               removes support for %A, %B, %I, %j, %p, %U, %w, %W, %x, %X, and
+               %y.  The always-enabled subset is %a, %b/%h, %d, %H, %m, %M,
+               %S, %Y, and %%.
+
+               Disabling this option makes strftime smaller, but the 
implementation
+               no longer supports all conversion specifiers required by ISO C.
+
+config LIBC_STRFTIME_POSIX_FORMATS
+       bool "strftime POSIX conversion specifiers"
+       default !DEFAULT_SMALL
+       depends on LIBC_STRFTIME_C_STANDARD_FORMATS
+       ---help---
+               Enable POSIX strftime conversion specifiers beyond ISO C that 
are
+               implemented by NuttX.  Disabling this option removes support for
+               %C, %e, %F, %g, %G, %n, %r, %R, %t, %T, %u, %V, and %z.
+               Enabling this option also enables the ISO C conversion 
specifiers.
+
+               Note that %h is kept in the always-enabled subset as an alias 
for
+               %b because it shares the same implementation.  Some POSIX
+               specifiers may be controlled by other options.
+
+config LIBC_STRFTIME_NONSTANDARD_FORMATS
+       bool "strftime non-standard conversion specifiers"
+       default !DEFAULT_SMALL
+       ---help---
+               Enable NuttX/GNU-style strftime conversion specifiers that are 
not
+               required by ISO C or POSIX.  Disabling this option removes 
support
+               for %k, %l, %P, and %s.
+
 config LIBC_LOCALTIME
        bool "localtime API call support"
        default "n"
diff --git a/libs/libc/time/lib_strftime.c b/libs/libc/time/lib_strftime.c
index 5e169d4e3bf..c16a8611d64 100644
--- a/libs/libc/time/lib_strftime.c
+++ b/libs/libc/time/lib_strftime.c
@@ -62,11 +62,13 @@ static const char * const g_abbrev_wdayname[7] =
   "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
 };
 
+#ifdef CONFIG_LIBC_STRFTIME_C_STANDARD_FORMATS
 static const char * const g_wdayname[7] =
 {
   "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
   "Saturday"
 };
+#endif
 
 static const char * const g_abbrev_monthname[12] =
 {
@@ -74,11 +76,13 @@ static const char * const g_abbrev_monthname[12] =
   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
 };
 
+#ifdef CONFIG_LIBC_STRFTIME_C_STANDARD_FORMATS
 static const char * const g_monthname[12] =
 {
   "January", "February", "March",     "April",   "May",      "June",
   "July",    "August",   "September", "October", "November", "December"
 };
+#endif
 
 /****************************************************************************
  * Private Functions
@@ -97,10 +101,12 @@ static const char * const g_monthname[12] =
  *  true if current is leap year, false is not a leap year
  */
 
+#ifdef CONFIG_LIBC_STRFTIME_POSIX_FORMATS
 static bool is_leap(int year)
 {
   return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
 }
+#endif
 
 /****************************************************************************
  * Name: get_week_num
@@ -115,6 +121,7 @@ static bool is_leap(int year)
  *  the week number in a year
  */
 
+#ifdef CONFIG_LIBC_STRFTIME_POSIX_FORMATS
 static int get_week_num(FAR const struct tm *time)
 {
   /* calculate the total week number in a year */
@@ -163,6 +170,7 @@ static int get_week_num(FAR const struct tm *time)
 
   return week;
 }
+#endif
 
 /****************************************************************************
  * Name: get_week_year
@@ -177,6 +185,7 @@ static int get_week_num(FAR const struct tm *time)
  *  the year that calculated based on week number
  */
 
+#ifdef CONFIG_LIBC_STRFTIME_POSIX_FORMATS
 static int get_week_year(FAR const struct tm *time)
 {
   int week_num = get_week_num(time);
@@ -192,6 +201,7 @@ static int get_week_year(FAR const struct tm *time)
 
   return week_year;
 }
+#endif
 
 /****************************************************************************
  * Public Functions
@@ -275,7 +285,10 @@ size_t strftime(FAR char *s, size_t max, FAR const char 
*format,
   FAR const char *str;
   FAR char       *dest   = s;
   int             chleft = max;
+#if defined(CONFIG_LIBC_STRFTIME_C_STANDARD_FORMATS) || \
+    defined(CONFIG_LIBC_STRFTIME_POSIX_FORMATS)
   int             value;
+#endif
   int             len;
 
   while (*format && chleft > 0)
@@ -297,6 +310,8 @@ size_t strftime(FAR char *s, size_t max, FAR const char 
*format,
 process_next:
        switch (*format++)
          {
+           /* Always-enabled conversion specifiers. */
+
            /* %a: A three-letter abbreviation for the day of the week. */
 
            case 'a':
@@ -309,18 +324,6 @@ process_next:
              }
              break;
 
-           /* %A: The full name for the day of the week. */
-
-           case 'A':
-             {
-               if (tm->tm_wday < 7)
-                 {
-                   str = g_wdayname[tm->tm_wday];
-                   len = snprintf(dest, chleft, "%s", str);
-                 }
-             }
-             break;
-
            /* %h: Equivalent to %b */
 
            case 'h':
@@ -339,26 +342,6 @@ process_next:
              }
              break;
 
-           /* %B: The full month name according to the current locale. */
-
-           case 'B':
-             {
-               if (tm->tm_mon < 12)
-                 {
-                   str = g_monthname[tm->tm_mon];
-                   len = snprintf(dest, chleft, "%s", str);
-                 }
-             }
-             break;
-
-           /* %C: The century number (year/100) as a 2-digit integer. */
-
-           case 'C':
-             {
-               len = snprintf(dest, chleft, "%02d", tm->tm_year / 100);
-             }
-             break;
-
            /* %d: The day of the month as a decimal number
             * (range 01 to 31).
             */
@@ -387,54 +370,88 @@ process_next:
                goto process_next;
              }
 
-           /* %e: Like %d, the day of the month as a decimal number, but
-            * a leading zero is replaced by a space.
+           /* %H: The hour as a decimal number using a 24-hour clock
+            * (range 00  to 23).
             */
 
-           case 'e':
+           case 'H':
              {
-               len = snprintf(dest, chleft, "%2d", tm->tm_mday);
+               len = snprintf(dest, chleft, "%02d", tm->tm_hour);
              }
              break;
 
-            /* %F: ISO 8601 date format: "%Y-%m-%d" */
+           /* %m: The month as a decimal number (range 01 to 12). */
 
-            case 'F':
-              {
-                len = snprintf(dest, chleft, "%04d-%02d-%02d",
-                              tm->tm_year + TM_YEAR_BASE, tm->tm_mon + 1,
-                              tm->tm_mday);
-              }
-              break;
+           case 'm':
+             {
+               len = snprintf(dest, chleft, "%02d", tm->tm_mon + 1);
+             }
+             break;
 
-            /* %g: 2-digit year version of %G, (00-99) */
+           /* %M: The minute as a decimal number (range 00 to 59). */
 
-            case 'g':
-              {
-                value = get_week_year(tm) % 100;
-                len = snprintf(dest, chleft, "%02d", value);
-              }
-              break;
+           case 'M':
+             {
+               len = snprintf(dest, chleft, "%02d", tm->tm_min);
+             }
+             break;
 
-            /* %G: ISO 8601 week based year */
+           /* %S: The second as a decimal number (range 00 to 60).
+            * (The range is up to 60 to allow for occasional leap seconds.)
+            */
 
-            case 'G':
-              {
-                len = snprintf(dest, chleft, "%04d", get_week_year(tm));
-              }
-              break;
+           case 'S':
+             {
+               len = snprintf(dest, chleft, "%02d", tm->tm_sec);
+             }
+             break;
 
-           /* %H: The hour as a decimal number using a 24-hour clock
-            * (range 00  to 23).
-            */
+           /* %Y: The year as a decimal number including the century. */
 
-           case 'H':
+           case 'Y':
              {
-               len = snprintf(dest, chleft, "%02d", tm->tm_hour);
+               len = snprintf(dest, chleft, "%04d",
+                              tm->tm_year + TM_YEAR_BASE);
+             }
+             break;
+
+           /* %%:  A literal '%' character. */
+
+           case '%':
+             {
+               *dest = '%';
+               len   = 1;
+             }
+             break;
+
+#ifdef CONFIG_LIBC_STRFTIME_C_STANDARD_FORMATS
+           /* ISO C conversion specifiers. */
+
+           /* %A: The full name for the day of the week. */
+
+           case 'A':
+             {
+               if (tm->tm_wday < 7)
+                 {
+                   str = g_wdayname[tm->tm_wday];
+                   len = snprintf(dest, chleft, "%s", str);
+                 }
+             }
+             break;
+
+           /* %B: The full month name according to the current locale. */
+
+           case 'B':
+             {
+               if (tm->tm_mon < 12)
+                 {
+                   str = g_monthname[tm->tm_mon];
+                   len = snprintf(dest, chleft, "%s", str);
+                 }
              }
              break;
 
-           /* %I: The  hour as a decimal number using a 12-hour clock
+           /* %I: The hour as a decimal number using a 12-hour clock
             * (range 01 to 12).
             */
 
@@ -460,85 +477,142 @@ process_next:
              }
              break;
 
-           /* %k: The hour (24-hour clock) as a decimal number
-            * (range  0  to  23);
-            * single digits are preceded by a blank.
+           /* %p: Either "AM" or "PM" according to the given time value. */
+
+           case 'p':
+             {
+               if (tm->tm_hour >= 12)
+                 {
+                   str = "PM";
+                 }
+               else
+                 {
+                   str = "AM";
+                 }
+
+               len = snprintf(dest, chleft, "%s", str);
+             }
+             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 'k':
+           case 'U':
              {
-               len = snprintf(dest, chleft, "%2d", tm->tm_hour);
+               value = (tm->tm_yday + DAYSPERWEEK - tm->tm_wday)
+                                  / DAYSPERWEEK;
+               len = snprintf(dest, chleft, "%02d", value);
              }
              break;
 
-           /* %l: The  hour  (12-hour  clock) as a decimal number
-            * (range 1 to 12);
-            * single digits are preceded by a blank.
+           /* %w: The weekday as a decimal number (range 0 to 6). */
+
+           case 'w':
+             {
+               len = snprintf(dest, chleft, "%d", tm->tm_wday);
+             }
+             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 'l':
+           case 'W':
              {
-               len = snprintf(dest, chleft, "%2d", (tm->tm_hour % 12) != 0 ?
-                                                   (tm->tm_hour % 12) : 12);
+               value = (tm->tm_yday + DAYSPERWEEK -
+                                 (tm->tm_wday + 6) % DAYSPERWEEK)
+                                  / DAYSPERWEEK;
+               len = snprintf(dest, chleft, "%02d", value);
              }
              break;
 
-           /* %m: The month as a decimal number (range 01 to 12). */
+           /* %x Locale date without time */
 
-           case 'm':
+           case 'x':
              {
-               len = snprintf(dest, chleft, "%02d", tm->tm_mon + 1);
+                len = snprintf(dest, chleft, "%02d/%02d/%04d",
+                              tm->tm_mon, tm->tm_mday,
+                              tm->tm_year + TM_YEAR_BASE);
              }
              break;
 
-           /* %M: The minute as a decimal number (range 00 to 59). */
+           /* %X: Locale time without date */
 
-           case 'M':
+           case 'X':
              {
-               len = snprintf(dest, chleft, "%02d", tm->tm_min);
+               len = snprintf(dest, chleft, "%02d:%02d:%02d",
+                              tm->tm_hour, tm->tm_min, tm->tm_sec);
              }
              break;
 
-           /* %n: A newline character. */
+           /* %y: The year as a decimal number without a century
+            * (range 00 to 99).
+            */
 
-           case 'n':
+           case 'y':
              {
-               *dest = '\n';
-               len   = 1;
+               len = snprintf(dest, chleft, "%02d", tm->tm_year % 100);
              }
              break;
+#endif
 
-           /* %p: Either "AM" or "PM" according to the given time  value. */
+#ifdef CONFIG_LIBC_STRFTIME_POSIX_FORMATS
+           /* POSIX conversion specifiers beyond ISO C. */
 
-           case 'p':
+           /* %C: The century number (year/100) as a 2-digit integer. */
+
+           case 'C':
              {
-               if (tm->tm_hour >= 12)
-                 {
-                   str = "PM";
-                 }
-               else
-                 {
-                   str = "AM";
-                 }
+               len = snprintf(dest, chleft, "%02d", tm->tm_year / 100);
+             }
+             break;
 
-               len = snprintf(dest, chleft, "%s", str);
+           /* %e: Like %d, the day of the month as a decimal number, but
+            * a leading zero is replaced by a space.
+            */
+
+           case 'e':
+             {
+               len = snprintf(dest, chleft, "%2d", tm->tm_mday);
              }
              break;
 
-           /* %P: Like %p but in lowercase: "am" or "pm" */
+           /* %F: ISO 8601 date format: "%Y-%m-%d" */
 
-           case 'P':
+           case 'F':
              {
-               if (tm->tm_hour >= 12)
-                 {
-                   str = "pm";
-                 }
-               else
-                 {
-                   str = "am";
-                 }
+               len = snprintf(dest, chleft, "%04d-%02d-%02d",
+                              tm->tm_year + TM_YEAR_BASE, tm->tm_mon + 1,
+                              tm->tm_mday);
+             }
+             break;
 
-               len = snprintf(dest, chleft, "%s", str);
+           /* %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;
+
+           /* %n: A newline character. */
+
+           case 'n':
+             {
+               *dest = '\n';
+               len   = 1;
              }
              break;
 
@@ -564,7 +638,7 @@ process_next:
              }
              break;
 
-            /* %R: Shortcut for %H:%M. */
+           /* %R: Shortcut for %H:%M. */
 
            case 'R':
              {
@@ -573,28 +647,6 @@ process_next:
              }
              break;
 
-           /* %s: The number of seconds since the Epoch, that is,
-            * since 1970-01-01 00:00:00 UTC.
-            * Hmmm... mktime argume is not 'const'.
-            */
-
-           case 's':
-             {
-               struct tm tmp = *tm;
-               len = snprintf(dest, chleft, "%ju", (uintmax_t)mktime(&tmp));
-             }
-             break;
-
-           /* %S: The second as a decimal number (range 00 to 60).
-            * (The range is up to 60 to allow for occasional leap seconds.)
-            */
-
-           case 'S':
-             {
-               len = snprintf(dest, chleft, "%02d", tm->tm_sec);
-             }
-             break;
-
            /* %t: A tab character. */
 
            case 't':
@@ -624,19 +676,6 @@ process_next:
              }
              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':
@@ -646,87 +685,75 @@ process_next:
              }
              break;
 
-           /* %w: The weekday as a decimal number (range 0 to 6). */
-
-           case 'w':
-             {
-               len = snprintf(dest, chleft, "%d", tm->tm_wday);
-             }
-             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.
+           /* %z: Numeric timezone as hour and minute offset from UTC
+            * "+hhmm" or "-hhmm"
             */
 
-           case 'W':
+           case 'z':
              {
-               value = (tm->tm_yday + DAYSPERWEEK -
-                                 (tm->tm_wday + 6) % DAYSPERWEEK)
-                                  / DAYSPERWEEK;
-               len = snprintf(dest, chleft, "%02d", value);
+               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;
+#endif
 
-           /* %x Locale date without time */
+#ifdef CONFIG_LIBC_STRFTIME_NONSTANDARD_FORMATS
+           /* Non-standard conversion specifiers. */
 
-           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 */
+           /* %k: The hour (24-hour clock) as a decimal number
+            * (range  0  to  23);
+            * single digits are preceded by a blank.
+            */
 
-           case 'X':
+           case 'k':
              {
-               len = snprintf(dest, chleft, "%02d:%02d:%02d",
-                              tm->tm_hour, tm->tm_min, tm->tm_sec);
+               len = snprintf(dest, chleft, "%2d", tm->tm_hour);
              }
              break;
 
-           /* %y: The year as a decimal number without a century
-            * (range 00 to 99).
+           /* %l: The hour (12-hour clock) as a decimal number
+            * (range 1 to 12);
+            * single digits are preceded by a blank.
             */
 
-           case 'y':
+           case 'l':
              {
-               len = snprintf(dest, chleft, "%02d", tm->tm_year % 100);
+               len = snprintf(dest, chleft, "%2d", (tm->tm_hour % 12) != 0 ?
+                                                   (tm->tm_hour % 12) : 12);
              }
              break;
 
-           /* %Y: The year as a decimal number including the century. */
+           /* %P: Like %p but in lowercase: "am" or "pm" */
 
-           case 'Y':
+           case 'P':
              {
-               len = snprintf(dest, chleft, "%04d",
-                              tm->tm_year + TM_YEAR_BASE);
+               if (tm->tm_hour >= 12)
+                 {
+                   str = "pm";
+                 }
+               else
+                 {
+                   str = "am";
+                 }
+
+               len = snprintf(dest, chleft, "%s", str);
              }
              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. */
+           /* %s: The number of seconds since the Epoch, that is,
+            * since 1970-01-01 00:00:00 UTC.
+            * Hmmm... mktime argume is not 'const'.
+            */
 
-           case '%':
+           case 's':
              {
-               *dest = '%';
-               len   = 1;
+               struct tm tmp = *tm;
+               len = snprintf(dest, chleft, "%ju", (uintmax_t)mktime(&tmp));
              }
              break;
+#endif
         }
 
       /* Update counts and pointers */

Reply via email to