diff options
-rw-r--r-- | docs/status.md | 17 | ||||
-rw-r--r-- | libc/tzcode/strptime.c | 40 | ||||
-rw-r--r-- | tests/time_test.cpp | 53 |
3 files changed, 103 insertions, 7 deletions
diff --git a/docs/status.md b/docs/status.md index 0cbcb47a8..d6a2f4c0a 100644 --- a/docs/status.md +++ b/docs/status.md @@ -45,14 +45,17 @@ New libc functions in Q (API level 29): * `getloadavg` (BSD/GNU extension in <stdlib.h>) New libc behavior in Q (API level 29): - * Whole printf family now supports the GNU `%m` extension, rather than a special-case hack in `syslog` - * `popen` now always uses `O_CLOEXEC`, not just with the `e` extension - * Bug fixes to handling of UTF-8 U+fffe/U+ffff and code points above U+10ffff - * `aligned_alloc` correctly verifies that `size` is a multiple of `alignment` + * Whole printf family now supports the GNU `%m` extension, rather than a + special-case hack in `syslog`. + * `popen` now always uses `O_CLOEXEC`, not just with the `e` extension. + * Bug fixes to handling of UTF-8 U+fffe/U+ffff and code points above U+10ffff. + * `aligned_alloc` correctly verifies that `size` is a multiple of `alignment`. * Using `%n` with the printf family is now reported as a FORTIFY failure. Previous versions of Android would ignore the `%n` but not consume the corresponding pointer argument, leading to obscure errors. The scanf family is unchanged. + * Support in strptime for `%F`, `%G`, `%g`, `%P`, `%u`, `%V`, and `%v`. + (strftime already supported them all.) * [fdsan](fdsan.md) detects common file descriptor errors at runtime. New libc functions in P (API level 28): @@ -74,9 +77,9 @@ New libc functions in P (API level 28): * `syncfs` New libc behavior in P (API level 28): - * `%C` and `%S` support in the printf family (previously only the wprintf family supported these) - * `%mc`/`%ms`/`%m[` support in the scanf family - * `%s` support in strptime (strftime already supported it) + * `%C` and `%S` support in the printf family (previously only the wprintf family supported these). + * `%mc`/`%ms`/`%m[` support in the scanf family. + * `%s` support in strptime (strftime already supported it). * Using a `pthread_mutex_t` after it's been destroyed will be detected at runtime and reported as a FORTIFY failure. * Passing a null `FILE*` or `DIR*` to libc is now detected at runtime and diff --git a/libc/tzcode/strptime.c b/libc/tzcode/strptime.c index 0e9a7d323..41eaa9b42 100644 --- a/libc/tzcode/strptime.c +++ b/libc/tzcode/strptime.c @@ -172,6 +172,12 @@ literal: return (NULL); break; + case 'F': /* The date as "%Y-%m-%d". */ + _LEGAL_ALT(0); + if (!(bp = _strptime(bp, "%Y-%m-%d", tm, cr))) + return (NULL); + continue; + case 'R': /* The time as "%H:%M". */ _LEGAL_ALT(0); if (!(bp = _strptime(bp, "%H:%M", tm, cr))) @@ -190,6 +196,12 @@ literal: return (NULL); break; + case 'v': /* The date as "%e-%b-%Y". */ + _LEGAL_ALT(0); + if (!(bp = _strptime(bp, "%e-%b-%Y", tm, cr))) + return (NULL); + break; + case 'X': /* The time, using the locale's format. */ _LEGAL_ALT(_ALT_E); if (!(bp = _strptime(bp, _ctloc(t_fmt), tm, cr))) @@ -305,6 +317,7 @@ literal: tm->tm_mon--; break; + case 'P': case 'p': /* The locale's equivalent of AM/PM. */ _LEGAL_ALT(0); /* AM? */ @@ -377,6 +390,33 @@ literal: return (NULL); break; + case 'u': /* The day of week, monday = 1. */ + _LEGAL_ALT(_ALT_O); + if (!(_conv_num(&bp, &i, 1, 7))) + return (NULL); + tm->tm_wday = i % 7; + continue; + + case 'g': /* The year corresponding to the ISO week + * number but without the century. + */ + if (!(_conv_num(&bp, &i, 0, 99))) + return (NULL); + continue; + + case 'G': /* The year corresponding to the ISO week + * number with century. + */ + do + bp++; + while (isdigit(*bp)); + continue; + + case 'V': /* The ISO 8601:1988 week number as decimal */ + if (!(_conv_num(&bp, &i, 0, 53))) + return (NULL); + continue; + case 'Y': /* The year. */ _LEGAL_ALT(_ALT_E); if (!(_conv_num(&bp, &i, 0, 9999))) diff --git a/tests/time_test.cpp b/tests/time_test.cpp index 637fa85e6..8653d91ea 100644 --- a/tests/time_test.cpp +++ b/tests/time_test.cpp @@ -296,6 +296,59 @@ TEST(time, strptime_l) { EXPECT_STREQ("09:41:53", buf); } +TEST(time, strptime_F) { + setenv("TZ", "UTC", 1); + + struct tm tm = {}; + ASSERT_EQ('\0', *strptime("2019-03-26", "%F", &tm)); + EXPECT_EQ(119, tm.tm_year); + EXPECT_EQ(2, tm.tm_mon); + EXPECT_EQ(26, tm.tm_mday); +} + +TEST(time, strptime_P_p) { + setenv("TZ", "UTC", 1); + + struct tm tm = {.tm_hour = 12}; + ASSERT_EQ('\0', *strptime("AM", "%p", &tm)); + EXPECT_EQ(0, tm.tm_hour); + + tm = {.tm_hour = 12}; + ASSERT_EQ('\0', *strptime("AM", "%P", &tm)); + EXPECT_EQ(0, tm.tm_hour); +} + +TEST(time, strptime_u) { + setenv("TZ", "UTC", 1); + + struct tm tm = {}; + ASSERT_EQ('\0', *strptime("2", "%u", &tm)); + EXPECT_EQ(2, tm.tm_wday); +} + +TEST(time, strptime_v) { + setenv("TZ", "UTC", 1); + + struct tm tm = {}; + ASSERT_EQ('\0', *strptime("26-Mar-1980", "%v", &tm)); + EXPECT_EQ(80, tm.tm_year); + EXPECT_EQ(2, tm.tm_mon); + EXPECT_EQ(26, tm.tm_mday); +} + +TEST(time, strptime_V_G_g) { + setenv("TZ", "UTC", 1); + + // %V (ISO-8601 week number), %G (year of week number, without century), and + // %g (year of week number) have no effect when parsed, and are supported + // solely so that it's possible for strptime(3) to parse everything that + // strftime(3) can output. + struct tm tm = {}; + ASSERT_EQ('\0', *strptime("1 2 3", "%V %G %g", &tm)); + struct tm zero = {}; + EXPECT_TRUE(memcmp(&tm, &zero, sizeof(tm)) == 0); +} + void SetTime(timer_t t, time_t value_s, time_t value_ns, time_t interval_s, time_t interval_ns) { itimerspec ts; ts.it_value.tv_sec = value_s; |