From 6090952296164ecb3953733f4008a033ccb177f8 Mon Sep 17 00:00:00 2001 From: Brian Inglis Date: Tue, 27 Oct 2015 09:41:22 +0100 Subject: [PATCH] strftime: Add support for %s (seconds since epoch) * libc/time/strftime.c (__strftime): add support for %s (seconds from Unix epoch). Fix whitespaces. Signed-off-by: Corinna Vinschen --- newlib/ChangeLog | 5 ++ newlib/libc/time/strftime.c | 124 ++++++++++++++++++++++++++++-------- winsup/cygwin/release/2.3.0 | 2 + winsup/doc/ChangeLog | 4 ++ winsup/doc/new-features.xml | 4 ++ 5 files changed, 114 insertions(+), 25 deletions(-) diff --git a/newlib/ChangeLog b/newlib/ChangeLog index 94b1f7b81..2c0cc5b80 100644 --- a/newlib/ChangeLog +++ b/newlib/ChangeLog @@ -1,3 +1,8 @@ +2015-10-26 Brian Inglis + + * libc/time/strftime.c (__strftime): add support for %s (seconds from + Unix epoch). Fix whitespaces. + 2015-10-19 Nick Clifton * libc/include/sys/_intsup.h: Add support for 16-bit and 20-bit diff --git a/newlib/libc/time/strftime.c b/newlib/libc/time/strftime.c index 9e677346e..ced106a5b 100644 --- a/newlib/libc/time/strftime.c +++ b/newlib/libc/time/strftime.c @@ -79,7 +79,7 @@ The century, that is, the year divided by 100 then truncated. For 4-digit years, the result is zero-padded and exactly two characters; but for other years, there may a negative sign or more digits. In this way, `<<%C%y>>' is equivalent to `<<%Y>>'. [tm_year] - + o %d The day of the month, formatted with two digits (from `<<01>>' to `<<31>>'). [tm_mday] @@ -110,7 +110,7 @@ includes January 4th, and begin on Mondays. Therefore, if January 1st, 2nd, or 3rd falls on a Sunday, that day and earlier belong to the last week of the previous year; and if December 29th, 30th, or 31st falls on Monday, that day and later belong to week 1 of the next year. For -consistency with %Y, it always has at least four characters. +consistency with %Y, it always has at least four characters. Example: "%G" for Saturday 2nd January 1999 gives "1998", and for Tuesday 30th December 1997 gives "1998". [tm_year, tm_wday, tm_yday] @@ -166,6 +166,10 @@ notations, the result is an empty string. [tm_sec, tm_min, tm_hour] o %R The 24-hour time, to the minute. Equivalent to "%H:%M". [tm_min, tm_hour] +o %s +The time elapsed, in seconds, since the start of the Unix epoch at +1970-01-01 00:00:00 UTC. + o %S The second, formatted with two digits (from `<<00>>' to `<<60>>'). The value 60 accounts for the occasional leap second. [tm_sec] @@ -273,7 +277,7 @@ the "C" locale settings. #include #include "local.h" #include "../locale/timelocal.h" - + /* Defines to make the file dual use for either strftime() or wcsftime(). * To get wcsftime, define MAKE_WCSFTIME. * To get strftime, do not define MAKE_WCSFTIME. @@ -738,7 +742,7 @@ _DEFUN (strftime, (s, maxsize, format, tim_p), if (*format == CQ('E')) { alt = *format++; -#ifdef _WANT_C99_TIME_FORMATS +#ifdef _WANT_C99_TIME_FORMATS #if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__) if (!*era_info && *_CurrentTimeLocale->wera) *era_info = get_era_info (tim_p, _CurrentTimeLocale->wera); @@ -751,7 +755,7 @@ _DEFUN (strftime, (s, maxsize, format, tim_p), else if (*format == CQ('O')) { alt = *format++; -#ifdef _WANT_C99_TIME_FORMATS +#ifdef _WANT_C99_TIME_FORMATS #if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__) if (!*alt_digits && *_CurrentTimeLocale->walt_digits) *alt_digits = get_alt_digits (_CurrentTimeLocale->walt_digits); @@ -934,7 +938,7 @@ recurse: { /* %F is equivalent to "%+4Y-%m-%d", flags and width can change that. Recurse to avoid need to replicate %Y formation. */ CHAR fmtbuf[32], *fmt = fmtbuf; - + *fmt++ = CQ('%'); if (pad) /* '0' or '+' */ *fmt++ = pad; @@ -1109,6 +1113,74 @@ recurse: tim_p->tm_hour, tim_p->tm_min); CHECK_LENGTH (); break; + case CQ('s'): +/* + * From: + * The Open Group Base Specifications Issue 7 + * IEEE Std 1003.1, 2013 Edition + * Copyright (c) 2001-2013 The IEEE and The Open Group + * XBD Base Definitions + * 4. General Concepts + * 4.15 Seconds Since the Epoch + * A value that approximates the number of seconds that have elapsed since the + * Epoch. A Coordinated Universal Time name (specified in terms of seconds + * (tm_sec), minutes (tm_min), hours (tm_hour), days since January 1 of the year + * (tm_yday), and calendar year minus 1900 (tm_year)) is related to a time + * represented as seconds since the Epoch, according to the expression below. + * If the year is <1970 or the value is negative, the relationship is undefined. + * If the year is >=1970 and the value is non-negative, the value is related to a + * Coordinated Universal Time name according to the C-language expression, where + * tm_sec, tm_min, tm_hour, tm_yday, and tm_year are all integer types: + * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 + + * (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 - + * ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400 + * OR + * ((((tm_year-69)/4 - (tm_year-1)/100 + (tm_year+299)/400 + + * (tm_year-70)*365 + tm_yday)*24 + tm_hour)*60 + tm_min)*60 + tm_sec + */ +/* modified from %z case by hoisting offset outside if block and initializing */ + { + long offset = 0; /* offset < 0 => W of GMT, > 0 => E of GMT: + subtract to get UTC */ + + if (tim_p->tm_isdst >= 0) + { + TZ_LOCK; + if (!tzset_called) + { + _tzset_unlocked (); + tzset_called = 1; + } + +#if defined (__CYGWIN__) + /* Cygwin must check if the application has been built with or + without the extra tm members for backward compatibility, and + then use either that or the old method fetching from tzinfo. + Rather than pulling in the version check infrastructure, we + just call a Cygwin function. */ + extern long __cygwin_gettzoffset (const struct tm *tmp); + offset = __cygwin_gettzoffset (tim_p); +#elif defined (__TM_GMTOFF) + offset = tim_p->__TM_GMTOFF; +#else + __tzinfo_type *tz = __gettzinfo (); + /* The sign of this is exactly opposite the envvar TZ. We + could directly use the global _timezone for tm_isdst==0, + but have to use __tzrule for daylight savings. */ + offset = -tz->__tzrule[tim_p->tm_isdst > 0].offset; +#endif + TZ_UNLOCK; + } + len = snprintf (&s[count], maxsize - count, CQ("%lld"), + (((((long long)tim_p->tm_year - 69)/4 + - (tim_p->tm_year - 1)/100 + + (tim_p->tm_year + 299)/400 + + (tim_p->tm_year - 70)*365 + tim_p->tm_yday)*24 + + tim_p->tm_hour)*60 + tim_p->tm_min)*60 + + tim_p->tm_sec - offset); + CHECK_LENGTH (); + } + break; case CQ('S'): #ifdef _WANT_C99_TIME_FORMATS if (alt != CQ('O') || !*alt_digits @@ -1378,13 +1450,13 @@ recurse: /* The remainder of this file can serve as a regression test. Compile * with -D_REGRESSION_TEST. */ #if defined(_REGRESSION_TEST) /* [Test code: */ - + /* This test code relies on ANSI C features, in particular on the ability * of adjacent strings to be pasted together into one string. */ - + /* Test output buffer size (should be larger than all expected results) */ #define OUTSIZE 256 - + struct test { CHAR *fmt; /* Testing format */ size_t max; /* Testing maxsize */ @@ -1396,9 +1468,9 @@ struct list { const struct test *vec; /* Test vectors */ int cnt; /* Number of vectors */ }; - + const char TZ[]="TZ=EST5EDT"; - + /* Define list of test inputs and expected outputs, for the given time zone * and time. */ const struct tm tm0 = { @@ -1442,6 +1514,7 @@ const struct test Vec0[] = { { CQ("%p"), 2+1, EXP(CQ("AM")) }, { CQ("%r"), 11+1, EXP(CQ("09:53:47 AM")) }, { CQ("%R"), 5+1, EXP(CQ("09:53")) }, + { CQ("%s"), 2+1, EXP(CQ("1230648827")) }, { CQ("%S"), 2+1, EXP(CQ("47")) }, { CQ("%t"), 1+1, EXP(CQ("\t")) }, { CQ("%T"), 8+1, EXP(CQ("09:53:47")) }, @@ -1502,6 +1575,7 @@ const struct test Vec1[] = { { CQ("%p"), 2+1, EXP(CQ("PM")) }, { CQ("%r"), 11+1, EXP(CQ("11:01:13 PM")) }, { CQ("%R"), 5+1, EXP(CQ("23:01")) }, + { CQ("%s"), 2+1, EXP(CQ("1215054073")) }, { CQ("%S"), 2+1, EXP(CQ("13")) }, { CQ("%t"), 1+1, EXP(CQ("\t")) }, { CQ("%T"), 8+1, EXP(CQ("23:01:13")) }, @@ -1526,7 +1600,7 @@ const struct test Vec1[] = { #undef VEC #undef EXP }; - + #if YEAR_BASE == 1900 /* ( */ /* Checks for very large years. YEAR_BASE value relied upon so that the * answer strings can be predetermined. @@ -1624,7 +1698,7 @@ const struct test Vecyr1[] = { #undef CENT #undef Year #endif /* YEAR_BASE ) */ - + /* Checks for years just over zero (also test for s=60). * Years less than 4 digits are not mentioned for %Y in the standard, so the * test for that case is based on the design intent. */ @@ -1695,7 +1769,7 @@ const struct test Vecyrzn[] = { #undef YEAR #undef CENT #undef Year - + const struct list ListYr[] = { { &tmyrzp, Vecyrzp, sizeof(Vecyrzp)/sizeof(Vecyrzp[0]) }, { &tmyrzn, Vecyrzn, sizeof(Vecyrzn)/sizeof(Vecyrzn[0]) }, @@ -1704,19 +1778,19 @@ const struct list ListYr[] = { { &tmyr1, Vecyr1, sizeof(Vecyr1)/sizeof(Vecyr1[0]) }, #endif }; - - + + /* List of tests to be run */ const struct list List[] = { { &tm0, Vec0, sizeof(Vec0)/sizeof(Vec0[0]) }, { &tm1, Vec1, sizeof(Vec1)/sizeof(Vec1[0]) }, }; - + #if defined(STUB_getenv_r) char * _getenv_r(struct _reent *p, const char *cp) { return getenv(cp); } #endif - + int main(void) { @@ -1724,7 +1798,7 @@ int i, l, errr=0, erro=0, tot=0; const char *cp; CHAR out[OUTSIZE]; size_t ret; - + /* Set timezone so that %z and %Z tests come out right */ cp = TZ; if((i=putenv(cp))) { @@ -1736,7 +1810,7 @@ if(strcmp(getenv("TZ"),strchr(TZ,'=')+1)) { return(-2); } tzset(); - + #if defined(VERBOSE) printf("_timezone=%d, _daylight=%d, _tzname[0]=%s, _tzname[1]=%s\n", _timezone, _daylight, _tzname[0], _tzname[1]); { @@ -1748,7 +1822,7 @@ __tzinfo_type *tz = __gettzinfo (); printf("tz->__tzrule[0].offset=%d, tz->__tzrule[1].offset=%d\n", tz->__tzrule[0].offset, tz->__tzrule[1].offset); } #endif - + /* Run all of the exact-length tests as-given--results should match */ for(l=0; l + + * new-features.xml (ov-new2.3): Document strftime %s addition. + 2015-10-22 Corinna Vinschen * posix.xml (std-iso): New section. diff --git a/winsup/doc/new-features.xml b/winsup/doc/new-features.xml index 4e0f3e2bf..469312fee 100644 --- a/winsup/doc/new-features.xml +++ b/winsup/doc/new-features.xml @@ -8,6 +8,10 @@ + +strftime(3) supports %s (seconds since Epoch) now. + + posix_madvise(POSIX_MADV_WILLNEED) now utilizes OS functionality available starting with Windows 8/Server 2012.