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]
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]
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]
#include <wctype.h>
#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.
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);
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);
{ /* %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;
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
/* 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 */
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 = {
{ 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")) },
{ 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")) },
#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.
#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. */
#undef YEAR
#undef CENT
#undef Year
-
+
const struct list ListYr[] = {
{ &tmyrzp, Vecyrzp, sizeof(Vecyrzp)/sizeof(Vecyrzp[0]) },
{ &tmyrzn, Vecyrzn, sizeof(Vecyrzn)/sizeof(Vecyrzn[0]) },
{ &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)
{
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))) {
return(-2);
}
tzset();
-
+
#if defined(VERBOSE)
printf("_timezone=%d, _daylight=%d, _tzname[0]=%s, _tzname[1]=%s\n", _timezone, _daylight, _tzname[0], _tzname[1]);
{
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<sizeof(List)/sizeof(List[0]); l++) {
const struct list *test = &List[l];
}
}
}
-
+
/* Run all of the exact-length tests with the length made too short--expect to
* fail. */
for(l=0; l<sizeof(List)/sizeof(List[0]); l++) {
}
}
}
-
+
/* Run all of the special year test cases */
for(l=0; l<sizeof(ListYr)/sizeof(ListYr[0]); l++) {
const struct list *test = &ListYr[l];
}
}
}
-
+
#define STRIZE(f) #f
#define NAME(f) STRIZE(f)
printf(NAME(strftime) "() test ");
if(errr || erro) printf("FAILED %d/%d of", errr, erro);
else printf("passed");
printf(" %d test cases.\n", tot);
-
+
return(errr || erro);
}
#endif /* defined(_REGRESSION_TEST) ] */