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 <corinna@vinschen.de>
This commit is contained in:
Brian Inglis 2015-10-27 09:41:22 +01:00 committed by Corinna Vinschen
parent cf51db8baa
commit 6090952296
5 changed files with 114 additions and 25 deletions

View File

@ -1,3 +1,8 @@
2015-10-26 Brian Inglis <Brian.Inglis@SystematicSw.ab.ca>
* libc/time/strftime.c (__strftime): add support for %s (seconds from
Unix epoch). Fix whitespaces.
2015-10-19 Nick Clifton <nickc@redhat.com> 2015-10-19 Nick Clifton <nickc@redhat.com>
* libc/include/sys/_intsup.h: Add support for 16-bit and 20-bit * libc/include/sys/_intsup.h: Add support for 16-bit and 20-bit

View File

@ -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; 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 but for other years, there may a negative sign or more digits. In
this way, `<<%C%y>>' is equivalent to `<<%Y>>'. [tm_year] this way, `<<%C%y>>' is equivalent to `<<%Y>>'. [tm_year]
o %d o %d
The day of the month, formatted with two digits (from `<<01>>' to The day of the month, formatted with two digits (from `<<01>>' to
`<<31>>'). [tm_mday] `<<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 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 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 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 Example: "%G" for Saturday 2nd January 1999 gives "1998", and for
Tuesday 30th December 1997 gives "1998". [tm_year, tm_wday, tm_yday] 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 o %R
The 24-hour time, to the minute. Equivalent to "%H:%M". [tm_min, tm_hour] 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 o %S
The second, formatted with two digits (from `<<00>>' to `<<60>>'). The The second, formatted with two digits (from `<<00>>' to `<<60>>'). The
value 60 accounts for the occasional leap second. [tm_sec] value 60 accounts for the occasional leap second. [tm_sec]
@ -273,7 +277,7 @@ the "C" locale settings.
#include <wctype.h> #include <wctype.h>
#include "local.h" #include "local.h"
#include "../locale/timelocal.h" #include "../locale/timelocal.h"
/* Defines to make the file dual use for either strftime() or wcsftime(). /* Defines to make the file dual use for either strftime() or wcsftime().
* To get wcsftime, define MAKE_WCSFTIME. * To get wcsftime, define MAKE_WCSFTIME.
* To get strftime, do not 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')) if (*format == CQ('E'))
{ {
alt = *format++; alt = *format++;
#ifdef _WANT_C99_TIME_FORMATS #ifdef _WANT_C99_TIME_FORMATS
#if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__) #if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__)
if (!*era_info && *_CurrentTimeLocale->wera) if (!*era_info && *_CurrentTimeLocale->wera)
*era_info = get_era_info (tim_p, _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')) else if (*format == CQ('O'))
{ {
alt = *format++; alt = *format++;
#ifdef _WANT_C99_TIME_FORMATS #ifdef _WANT_C99_TIME_FORMATS
#if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__) #if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__)
if (!*alt_digits && *_CurrentTimeLocale->walt_digits) if (!*alt_digits && *_CurrentTimeLocale->walt_digits)
*alt_digits = get_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 { /* %F is equivalent to "%+4Y-%m-%d", flags and width can change
that. Recurse to avoid need to replicate %Y formation. */ that. Recurse to avoid need to replicate %Y formation. */
CHAR fmtbuf[32], *fmt = fmtbuf; CHAR fmtbuf[32], *fmt = fmtbuf;
*fmt++ = CQ('%'); *fmt++ = CQ('%');
if (pad) /* '0' or '+' */ if (pad) /* '0' or '+' */
*fmt++ = pad; *fmt++ = pad;
@ -1109,6 +1113,74 @@ recurse:
tim_p->tm_hour, tim_p->tm_min); tim_p->tm_hour, tim_p->tm_min);
CHECK_LENGTH (); CHECK_LENGTH ();
break; 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'): case CQ('S'):
#ifdef _WANT_C99_TIME_FORMATS #ifdef _WANT_C99_TIME_FORMATS
if (alt != CQ('O') || !*alt_digits if (alt != CQ('O') || !*alt_digits
@ -1378,13 +1450,13 @@ recurse:
/* The remainder of this file can serve as a regression test. Compile /* The remainder of this file can serve as a regression test. Compile
* with -D_REGRESSION_TEST. */ * with -D_REGRESSION_TEST. */
#if defined(_REGRESSION_TEST) /* [Test code: */ #if defined(_REGRESSION_TEST) /* [Test code: */
/* This test code relies on ANSI C features, in particular on the ability /* This test code relies on ANSI C features, in particular on the ability
* of adjacent strings to be pasted together into one string. */ * of adjacent strings to be pasted together into one string. */
/* Test output buffer size (should be larger than all expected results) */ /* Test output buffer size (should be larger than all expected results) */
#define OUTSIZE 256 #define OUTSIZE 256
struct test { struct test {
CHAR *fmt; /* Testing format */ CHAR *fmt; /* Testing format */
size_t max; /* Testing maxsize */ size_t max; /* Testing maxsize */
@ -1396,9 +1468,9 @@ struct list {
const struct test *vec; /* Test vectors */ const struct test *vec; /* Test vectors */
int cnt; /* Number of vectors */ int cnt; /* Number of vectors */
}; };
const char TZ[]="TZ=EST5EDT"; const char TZ[]="TZ=EST5EDT";
/* Define list of test inputs and expected outputs, for the given time zone /* Define list of test inputs and expected outputs, for the given time zone
* and time. */ * and time. */
const struct tm tm0 = { const struct tm tm0 = {
@ -1442,6 +1514,7 @@ const struct test Vec0[] = {
{ CQ("%p"), 2+1, EXP(CQ("AM")) }, { CQ("%p"), 2+1, EXP(CQ("AM")) },
{ CQ("%r"), 11+1, EXP(CQ("09:53:47 AM")) }, { CQ("%r"), 11+1, EXP(CQ("09:53:47 AM")) },
{ CQ("%R"), 5+1, EXP(CQ("09:53")) }, { CQ("%R"), 5+1, EXP(CQ("09:53")) },
{ CQ("%s"), 2+1, EXP(CQ("1230648827")) },
{ CQ("%S"), 2+1, EXP(CQ("47")) }, { CQ("%S"), 2+1, EXP(CQ("47")) },
{ CQ("%t"), 1+1, EXP(CQ("\t")) }, { CQ("%t"), 1+1, EXP(CQ("\t")) },
{ CQ("%T"), 8+1, EXP(CQ("09:53:47")) }, { 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("%p"), 2+1, EXP(CQ("PM")) },
{ CQ("%r"), 11+1, EXP(CQ("11:01:13 PM")) }, { CQ("%r"), 11+1, EXP(CQ("11:01:13 PM")) },
{ CQ("%R"), 5+1, EXP(CQ("23:01")) }, { CQ("%R"), 5+1, EXP(CQ("23:01")) },
{ CQ("%s"), 2+1, EXP(CQ("1215054073")) },
{ CQ("%S"), 2+1, EXP(CQ("13")) }, { CQ("%S"), 2+1, EXP(CQ("13")) },
{ CQ("%t"), 1+1, EXP(CQ("\t")) }, { CQ("%t"), 1+1, EXP(CQ("\t")) },
{ CQ("%T"), 8+1, EXP(CQ("23:01:13")) }, { CQ("%T"), 8+1, EXP(CQ("23:01:13")) },
@ -1526,7 +1600,7 @@ const struct test Vec1[] = {
#undef VEC #undef VEC
#undef EXP #undef EXP
}; };
#if YEAR_BASE == 1900 /* ( */ #if YEAR_BASE == 1900 /* ( */
/* Checks for very large years. YEAR_BASE value relied upon so that the /* Checks for very large years. YEAR_BASE value relied upon so that the
* answer strings can be predetermined. * answer strings can be predetermined.
@ -1624,7 +1698,7 @@ const struct test Vecyr1[] = {
#undef CENT #undef CENT
#undef Year #undef Year
#endif /* YEAR_BASE ) */ #endif /* YEAR_BASE ) */
/* Checks for years just over zero (also test for s=60). /* 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 * 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. */ * test for that case is based on the design intent. */
@ -1695,7 +1769,7 @@ const struct test Vecyrzn[] = {
#undef YEAR #undef YEAR
#undef CENT #undef CENT
#undef Year #undef Year
const struct list ListYr[] = { const struct list ListYr[] = {
{ &tmyrzp, Vecyrzp, sizeof(Vecyrzp)/sizeof(Vecyrzp[0]) }, { &tmyrzp, Vecyrzp, sizeof(Vecyrzp)/sizeof(Vecyrzp[0]) },
{ &tmyrzn, Vecyrzn, sizeof(Vecyrzn)/sizeof(Vecyrzn[0]) }, { &tmyrzn, Vecyrzn, sizeof(Vecyrzn)/sizeof(Vecyrzn[0]) },
@ -1704,19 +1778,19 @@ const struct list ListYr[] = {
{ &tmyr1, Vecyr1, sizeof(Vecyr1)/sizeof(Vecyr1[0]) }, { &tmyr1, Vecyr1, sizeof(Vecyr1)/sizeof(Vecyr1[0]) },
#endif #endif
}; };
/* List of tests to be run */ /* List of tests to be run */
const struct list List[] = { const struct list List[] = {
{ &tm0, Vec0, sizeof(Vec0)/sizeof(Vec0[0]) }, { &tm0, Vec0, sizeof(Vec0)/sizeof(Vec0[0]) },
{ &tm1, Vec1, sizeof(Vec1)/sizeof(Vec1[0]) }, { &tm1, Vec1, sizeof(Vec1)/sizeof(Vec1[0]) },
}; };
#if defined(STUB_getenv_r) #if defined(STUB_getenv_r)
char * char *
_getenv_r(struct _reent *p, const char *cp) { return getenv(cp); } _getenv_r(struct _reent *p, const char *cp) { return getenv(cp); }
#endif #endif
int int
main(void) main(void)
{ {
@ -1724,7 +1798,7 @@ int i, l, errr=0, erro=0, tot=0;
const char *cp; const char *cp;
CHAR out[OUTSIZE]; CHAR out[OUTSIZE];
size_t ret; size_t ret;
/* Set timezone so that %z and %Z tests come out right */ /* Set timezone so that %z and %Z tests come out right */
cp = TZ; cp = TZ;
if((i=putenv(cp))) { if((i=putenv(cp))) {
@ -1736,7 +1810,7 @@ if(strcmp(getenv("TZ"),strchr(TZ,'=')+1)) {
return(-2); return(-2);
} }
tzset(); tzset();
#if defined(VERBOSE) #if defined(VERBOSE)
printf("_timezone=%d, _daylight=%d, _tzname[0]=%s, _tzname[1]=%s\n", _timezone, _daylight, _tzname[0], _tzname[1]); 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); printf("tz->__tzrule[0].offset=%d, tz->__tzrule[1].offset=%d\n", tz->__tzrule[0].offset, tz->__tzrule[1].offset);
} }
#endif #endif
/* Run all of the exact-length tests as-given--results should match */ /* Run all of the exact-length tests as-given--results should match */
for(l=0; l<sizeof(List)/sizeof(List[0]); l++) { for(l=0; l<sizeof(List)/sizeof(List[0]); l++) {
const struct list *test = &List[l]; const struct list *test = &List[l];
@ -1769,7 +1843,7 @@ for(l=0; l<sizeof(List)/sizeof(List[0]); l++) {
} }
} }
} }
/* Run all of the exact-length tests with the length made too short--expect to /* Run all of the exact-length tests with the length made too short--expect to
* fail. */ * fail. */
for(l=0; l<sizeof(List)/sizeof(List[0]); l++) { for(l=0; l<sizeof(List)/sizeof(List[0]); l++) {
@ -1795,7 +1869,7 @@ for(l=0; l<sizeof(List)/sizeof(List[0]); l++) {
} }
} }
} }
/* Run all of the special year test cases */ /* Run all of the special year test cases */
for(l=0; l<sizeof(ListYr)/sizeof(ListYr[0]); l++) { for(l=0; l<sizeof(ListYr)/sizeof(ListYr[0]); l++) {
const struct list *test = &ListYr[l]; const struct list *test = &ListYr[l];
@ -1816,14 +1890,14 @@ for(l=0; l<sizeof(ListYr)/sizeof(ListYr[0]); l++) {
} }
} }
} }
#define STRIZE(f) #f #define STRIZE(f) #f
#define NAME(f) STRIZE(f) #define NAME(f) STRIZE(f)
printf(NAME(strftime) "() test "); printf(NAME(strftime) "() test ");
if(errr || erro) printf("FAILED %d/%d of", errr, erro); if(errr || erro) printf("FAILED %d/%d of", errr, erro);
else printf("passed"); else printf("passed");
printf(" %d test cases.\n", tot); printf(" %d test cases.\n", tot);
return(errr || erro); return(errr || erro);
} }
#endif /* defined(_REGRESSION_TEST) ] */ #endif /* defined(_REGRESSION_TEST) ] */

View File

@ -1,6 +1,8 @@
What's new: What's new:
----------- -----------
- strftime(3) supports %s (seconds since Epoch) now.
- posix_madvise(POSIX_MADV_WILLNEED) now utilizes OS functionality available - posix_madvise(POSIX_MADV_WILLNEED) now utilizes OS functionality available
starting with Windows 8/Server 2012. Still a no-op on older systems. starting with Windows 8/Server 2012. Still a no-op on older systems.

View File

@ -1,3 +1,7 @@
2015-10-27 Corinna Vinschen <corinna@vinschen.de>
* new-features.xml (ov-new2.3): Document strftime %s addition.
2015-10-22 Corinna Vinschen <corinna@vinschen.de> 2015-10-22 Corinna Vinschen <corinna@vinschen.de>
* posix.xml (std-iso): New section. * posix.xml (std-iso): New section.

View File

@ -8,6 +8,10 @@
<itemizedlist mark="bullet"> <itemizedlist mark="bullet">
<listitem><para>
strftime(3) supports %s (seconds since Epoch) now.
</para></listitem>
<listitem><para> <listitem><para>
posix_madvise(POSIX_MADV_WILLNEED) now utilizes OS functionality available posix_madvise(POSIX_MADV_WILLNEED) now utilizes OS functionality available
starting with Windows 8/Server 2012. starting with Windows 8/Server 2012.