diff --git a/libc/tzcode/strptime.c b/libc/tzcode/strptime.c index 210063007..0e9a7d323 100644 --- a/libc/tzcode/strptime.c +++ b/libc/tzcode/strptime.c @@ -38,7 +38,9 @@ //#include #include +#include #include +#include #include #include #include "tzfile.h" @@ -128,7 +130,7 @@ _strptime(const unsigned char *buf, const char *fmt, struct tm *tm, struct centu fmt++; continue; } - + if ((c = *fmt++) != '%') goto literal; @@ -154,7 +156,7 @@ literal: _LEGAL_ALT(0); alt_format |= _ALT_O; goto again; - + /* * "Complex" conversion rules, implemented through recursion. */ @@ -169,7 +171,7 @@ literal: if (!(bp = _strptime(bp, "%m/%d/%y", tm, cr))) return (NULL); break; - + case 'R': /* The time as "%H:%M". */ _LEGAL_ALT(0); if (!(bp = _strptime(bp, "%H:%M", tm, cr))) @@ -337,6 +339,25 @@ literal: return (NULL); break; + case 's': + { + // Android addition, based on FreeBSD's implementation. + int saved_errno = errno; + errno = 0; + const unsigned char* old_bp = bp; + long n = strtol((const char*) bp, (char**) &bp, 10); + time_t t = n; + if (bp == old_bp || errno == ERANGE || ((long) t) != n) { + errno = saved_errno; + return NULL; + } + errno = saved_errno; + + if (localtime_r(&t, tm) == NULL) return NULL; + } + break; + + case 'U': /* The week of year, beginning on sunday. */ case 'W': /* The week of year, beginning on monday. */ _LEGAL_ALT(_ALT_O); diff --git a/tests/time_test.cpp b/tests/time_test.cpp index 2b9935a90..ff427a634 100644 --- a/tests/time_test.cpp +++ b/tests/time_test.cpp @@ -871,3 +871,54 @@ TEST(time, ctime_r) { ASSERT_EQ(buf, ctime_r(&t, buf)); ASSERT_STREQ("Thu Jan 1 00:00:00 1970\n", buf); } + +// https://issuetracker.google.com/37128336 +TEST(time, strftime_strptime_s) { + char buf[32]; + const struct tm tm0 = { .tm_year = 1982-1900, .tm_mon = 0, .tm_mday = 1 }; + + setenv("TZ", "America/Los_Angeles", 1); + strftime(buf, sizeof(buf), "<%s>", &tm0); + EXPECT_STREQ("<378720000>", buf); + + setenv("TZ", "UTC", 1); + strftime(buf, sizeof(buf), "<%s>", &tm0); + EXPECT_STREQ("<378691200>", buf); + + struct tm tm; + + setenv("TZ", "America/Los_Angeles", 1); + tzset(); + memset(&tm, 0xff, sizeof(tm)); + char* p = strptime("378720000x", "%s", &tm); + ASSERT_EQ('x', *p); + EXPECT_EQ(0, tm.tm_sec); + EXPECT_EQ(0, tm.tm_min); + EXPECT_EQ(0, tm.tm_hour); + EXPECT_EQ(1, tm.tm_mday); + EXPECT_EQ(0, tm.tm_mon); + EXPECT_EQ(82, tm.tm_year); + EXPECT_EQ(5, tm.tm_wday); + EXPECT_EQ(0, tm.tm_yday); + EXPECT_EQ(0, tm.tm_isdst); + + setenv("TZ", "UTC", 1); + tzset(); + memset(&tm, 0xff, sizeof(tm)); + p = strptime("378691200x", "%s", &tm); + ASSERT_EQ('x', *p); + EXPECT_EQ(0, tm.tm_sec); + EXPECT_EQ(0, tm.tm_min); + EXPECT_EQ(0, tm.tm_hour); + EXPECT_EQ(1, tm.tm_mday); + EXPECT_EQ(0, tm.tm_mon); + EXPECT_EQ(82, tm.tm_year); + EXPECT_EQ(5, tm.tm_wday); + EXPECT_EQ(0, tm.tm_yday); + EXPECT_EQ(0, tm.tm_isdst); +} + +TEST(time, strptime_s_nothing) { + struct tm tm; + ASSERT_EQ(nullptr, strptime("x", "%s", &tm)); +}