blob: dfe8a5e46da68f5ad84bc561ee37f7bf8bb23821 [file] [log] [blame]
/***************************************************************************
*
* time_put.cpp
*
* $Id$
*
***************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* Copyright 2001-2008 Rogue Wave Software, Inc.
*
**************************************************************************/
#define _RWSTD_LIB_SRC
#include <rw/_defs.h>
// disable Compaq C++ pure C headers
#undef __PURE_CNAME
#if (defined (__sun__) || defined (__sun) || defined (sun)) && defined (__EDG__)
// get tzset() from <time.h>
# define _XOPEN_SOURCE
#endif
#ifdef __CYGWIN__
// force CygWin <time.h> to define timezone as an int variable,
// not as a void function returning char*
# define timezonevar
#endif // __CYGWIN__
#include <ctype.h> // for isspace(), ...
#include <stdio.h> // for sprintf()
#include <stddef.h> // for offsetof()
#include <stdlib.h> // for getenv()
#include <string.h> // for memcpy(), strlen()
#include <time.h> // for strftime(), struct tm, tzset()
#include <wchar.h> // for wcsftime()
#ifndef _MSC_VER
# ifndef _RWSTD_NO_PURE_C_HEADERS
# include <locale.h>
# ifndef LC_MESSAGES
# define LC_MESSAGES _RWSTD_LC_MESSAGES
# endif // LC_MESSAGES
# endif // _RWSTD_NO_PURE_C_HEADERS
# ifndef _RWSTD_NO_NL_LANGINFO
# include <langinfo.h> // for nl_langinfo()
# endif
#else // if defined (_MSC_VER)
# if defined (_RWSTD_MSVC) && defined (_WIN64)
// shut up MSVC/Win64 complaints about possible loss of data
# pragma warning (disable: 4244)
# endif
#endif // _MSC_VER
#include "access.h"
#include <loc/_ctype.h>
#include <loc/_localedef.h>
#include <loc/_punct.h>
#include <loc/_time_put.h>
#include <rw/_traits.h>
#include "setlocale.h"
#include "strtol.h"
#if defined (__EDG__) || !defined (_RWSTD_NO_PURE_C_HEADERS)
# if defined (__linux__) || defined (__sun)
extern "C" {
// declare these for Linux glibc and SunOS
extern int daylight;
extern long int timezone;
extern void tzset () _LIBC_THROWS ();
} // extern "C"
# endif // __linux__ || __sun
#endif // __EDG__ || !_RWSTD_NO_PURE_C_HEADERS
#ifdef _RWSTD_NO_DAYLIGHT
// the XSI POSIX extension, daylight, is not declared in <time.h>
# define daylight 0
#endif // _RWSTD_NO_DAYLIGHT
#if defined (_RWSTD_NO_WCSFTIME) && !defined (_RWSTD_NO_WCSFTIME_IN_LIBC)
# if defined _RWSTD_WCSFTIME_ARG3_T
# undef _RWSTD_NO_WCSFTIME
extern "C" {
size_t wcsftime (wchar_t*, size_t, _RWSTD_WCSFTIME_ARG3_T, const struct tm*);
}
# endif // _RWSTD_WCSFTIME_ARG3_T
#endif // _RWSTD_NO_WCSFTIME && !_RWSTD_NO_WCSFTIME_IN_LIBC
_RWSTD_NAMESPACE (__rw) {
typedef _STD::ctype_base::mask MaskT;
extern const MaskT __rw_classic_tab [];
#define ISALPHA(c) \
(_RW::__rw_classic_tab [(unsigned char)c] & _RW::__rw_alpha)
#define ISDIGIT(c) \
(_RW::__rw_classic_tab [(unsigned char)c] & _RW::__rw_digit)
size_t
__rw_get_timepunct (const _RW::__rw_facet*, int,
const void*[], const size_t []);
// also declared in _time_put.cc
_RWSTD_EXPORT char*
__rw_put_time (const __rw_facet*, char*, size_t,
_STD::ios_base&, char, const tm*,
char, char, int, int);
#ifndef _RWSTD_NO_WCHAR_T
// also declared in _time_put.cc
_RWSTD_EXPORT wchar_t*
__rw_put_time (const __rw_facet*, wchar_t*, size_t,
_STD::ios_base&, wchar_t, const tm*,
char, char, int, int);
#endif // _RWSTD_NO_WCHAR_T
/***************************************************************************/
#ifdef _RWSTD_NO_NL_LANGINFO
typedef unsigned char UChar;
// compute the format string corresponding to the "%x" format specifier
// in the current locale (set by setlocale (LC_ALL, ...))
static size_t
__rw_get_date_fmat (char *fmt)
{
tm t;
memset (&t, 0, sizeof t);
// set a date consisting of unique components
// such as 2/1/1933
t.tm_mday = 1; t.tm_mon = 1; t.tm_year = 33;
char tmp [256];
tmp [0] = '\0';
// shut up gcc 2.9x warning: `%x' yields only last 2 digits of year
const char percent_x[] = "%x";
strftime (tmp, sizeof tmp, percent_x, &t);
char *pfmt = fmt;
char abday [256]; // buffer for abbreviated day name
char day [256]; // buffer for full day name
char abmon [256]; // buffer for abbreviated month name
char mon [256]; // buffer for full month name
size_t abday_len = 0;
size_t day_len;
size_t abmon_len;
size_t mon_len;
for (char *ptmp = tmp; *ptmp; ) {
// store all whitespace as part of format
for (; (isspace)(UChar (*ptmp)); ++ptmp)
*pfmt++ = *ptmp;
const char *begin = ptmp;
// skip over all non-digit characters
for (; *ptmp && !(isdigit)(UChar (*ptmp)); ++ptmp) {
if ((ispunct)(UChar (*ptmp)) || (isspace)(UChar (*ptmp))) {
// store all punctuators as part of format
for ( ; (ispunct)(UChar (*ptmp)) || (isspace)(UChar (*ptmp)); ++ptmp)
*pfmt++ = *ptmp;
break;
}
else
*pfmt++ = *ptmp;
}
const size_t len = ptmp - begin;
if (len > 1) {
// if there are 2 or more characters in this date component
// try to match the sequence against one of the names
if (!abday_len) {
// initialize day and month names only when necessary
abday_len = strftime (abday, sizeof abday, "%a", &t);
day_len = strftime (day, sizeof day, "%A", &t);
abmon_len = strftime (abmon, sizeof abmon, "%b", &t);
mon_len = strftime (mon, sizeof mon, "%B", &t);
}
char fmtchar = '\0';
size_t start = 0;
if (day_len <= len && !memcmp (day, begin, day_len)) {
fmtchar = 'A';
start = day_len;
}
else if (mon_len <= len && !memcmp (mon, begin, mon_len)) {
fmtchar = 'B';
start = mon_len;
}
else if (abday_len <= len && !memcmp (abday, begin, abday_len)) {
fmtchar = 'a';
start = abday_len;
}
else if (abmon_len <= len && !memcmp (abmon, begin, abmon_len)) {
fmtchar = 'b';
start = abmon_len;
}
if (fmtchar) {
pfmt -= len;
*pfmt++ = '%';
*pfmt++ = fmtchar;
for (; start != len; ++start)
*pfmt++ = begin [start];
}
}
if ((isdigit)(UChar (*ptmp))) {
for (begin = ptmp; (isdigit)(UChar (*ptmp)); ++ptmp);
*pfmt++ = '%';
if (ptmp - begin == 1) {
if ('1' == *begin) {
if (pfmt != fmt + 1 && ' ' == pfmt [-2]) {
pfmt [-2] = '%';
--pfmt;
}
*pfmt++ = 'e';
}
else if ('2' == *begin) {
*pfmt++ = 'm';
}
}
else if (ptmp - begin == 2) {
if ('0' == begin [0] && '1' == begin [1])
*pfmt++ = 'd';
else if ('0' == begin [0] && '2' == begin [1])
*pfmt++ = 'm';
else if ('3' == begin [0] && '3' == begin [1])
*pfmt++ = 'y';
else
*pfmt++ = '*';
}
else if (ptmp - begin == 4 && !memcmp (begin, "1933", 4))
*pfmt++ = 'Y';
else
*pfmt++ = '*';
}
}
*pfmt++ = '\0';
return pfmt - fmt;
}
// compute the format string corresponding to the "%X" format specifier
// in the current locale (set by setlocale (LC_ALL, ...))
static size_t
__rw_get_time_fmat (char *fmt)
{
tm t;
memset (&t, 0, sizeof t);
t.tm_sec = 3; t.tm_min = 4; t.tm_hour = 21; // "9 PM" or "21"
char tmp [256];
tmp [0] = '\0';
strftime (tmp, sizeof tmp, "%X", &t);
char *pfmt = fmt;
char pm [256]; // buffer for pm designator
size_t pm_len = 0;
for (char *ptmp = tmp; *ptmp; ) {
for (; (isspace)(UChar (*ptmp)); ++ptmp)
*pfmt++ = *ptmp;
const char *begin = ptmp;
for (; *ptmp && !(isdigit)(UChar (*ptmp)); ++ptmp) {
if ( (ispunct)(UChar (*ptmp))
|| (isspace)(UChar (*ptmp))) {
for (; (ispunct)(UChar (*ptmp))
|| (isspace)(UChar (*ptmp)); ++ptmp)
*pfmt++ = *ptmp;
break;
}
else
*pfmt++ = *ptmp;
}
const size_t len = ptmp - begin;
if (len > 1) {
if (0 == pm_len) {
strftime (pm, sizeof pm, "%p", &t);
pm_len = strlen (pm);
}
if ( pm_len <= len
&& !::memcmp (pm, begin, pm_len)) {
pfmt -= len;
*pfmt++ = '%';
*pfmt++ = 'p';
// copy whatever follows the pm designator
for (size_t i = pm_len; i != len; ++i)
*pfmt++ = begin [i];
}
}
if ((isdigit)(UChar (*ptmp))) {
for (begin = ptmp; (isdigit)(UChar (*ptmp)); ++ptmp);
*pfmt++ = '%';
if (pfmt != fmt + 1 && ' ' == pfmt [-2]) {
pfmt [-2] = '%';
--pfmt;
}
if (ptmp - begin == 1) {
switch (*begin) {
case '3': *pfmt++ = 'S'; break; // tm_sec
case '4': *pfmt++ = 'M'; break; // tm_min
case '9': *pfmt++ = 'I'; break; // tm_hour
default: *pfmt++ = '\0';
}
}
else if (ptmp - begin == 2) {
if ('0' == begin [0]) {
switch (begin [1]) {
case '3': *pfmt++ = 'S'; break; // tm_sec
case '4': *pfmt++ = 'M'; break; // tm_min
case '9': *pfmt++ = 'I'; break; // tm_hour
default: *pfmt++ = '\0';
}
}
else if ('2' == begin [0] && '1' == begin [1]) {
*pfmt++ = 'H';
}
else
*pfmt++ = '\0';
}
else
*pfmt++ = '\0';
}
}
*pfmt++ = '\0';
return pfmt - fmt;
}
#endif // _RWSTD_NO_NL_LANGINFO
const void*
__rw_get_timepunct (const __rw_facet *pfacet, int flags, size_t inx)
{
const int member = flags & ~__rw_wide;
const bool wide = !!(flags & __rw_wide);
const void *pdata = pfacet->_C_data ();
if (pdata) {
const __rw_time_t* const pun =
_RWSTD_STATIC_CAST (const __rw_time_t*, pdata);
switch (member) {
case __rw_abday: return pun->abday (inx, wide);
case __rw_day: return pun->day (inx, wide);
case __rw_abmon: return pun->abmon (inx, wide);
case __rw_month: return pun->mon (inx, wide);
case __rw_ampm: return pun->am_pm (inx, wide);
case __rw_d_t_fmt: return pun->d_t_fmt (wide);
case __rw_d_fmt: return pun->d_fmt (wide);
case __rw_t_fmt: return pun->t_fmt (wide);
case __rw_t_fmt_ampm: return pun->t_fmt_ampm (wide);
case __rw_era_d_t_fmt: return pun->era_d_t_fmt (wide);
case __rw_era_d_fmt: return pun->era_d_fmt (wide);
case __rw_era_t_fmt: return pun->era_t_fmt (wide);
case __rw_era_names: return pun->era_name (inx, wide);
case __rw_era_fmts: return pun->era_fmt (inx, wide);
case __rw_alt_digits: return pun->alt_digits (inx, wide);
default: return pdata;
}
}
const char* const locname = pfacet->_C_get_name ();
// set all categories (need LC_TIME and LC_CTYPE) and lock
const __rw_setlocale clocale (locname, _RWSTD_LC_ALL);
size_t bufsize = 2048;
const size_t newsize = bufsize + sizeof (__rw_time_t);
// allocate memory using operator new to avoid mismatch with
// facet destructor
char *pbuf = _RWSTD_STATIC_CAST (char*, ::operator new (newsize));
__rw_time_t *pun = _RWSTD_REINTERPRET_CAST (__rw_time_t*, pbuf);
// zero out all members
memset (pun, 0, sizeof *pun);
// advance buffer pointer past the end of the structure
pbuf += sizeof *pun;
#ifndef _RWSTD_NO_WCHAR_T
// reserve offset 0 for the empty string (both narrow and wide)
*_RWSTD_REINTERPRET_CAST (wchar_t*, pbuf) = L'\0';
size_t off = sizeof (wchar_t);
#else // if defined (_RWSTD_NO_WCHAR_T)
// reserve offset 0 for the empty string
*pbuf = '\0';
size_t off = sizeof (char);
#endif // _RWSTD_NO_WCHAR_T
#ifndef _RWSTD_NO_NL_LANGINFO
static const struct {
int nl_item; // argument to nl_langinfo()
size_t moff [2]; // member offset
} nl_items [] = {
#ifndef _RWSTD_NO_OFFSETOF
# define OFF(T, m) offsetof (T, m)
#else
# define OFF(T, m) ((char*)&((T*)0)->m - (char*)0)
#endif // _RWSTD_NO_OFFSETOF
#undef ENTRY
#define ENTRY(item, mem1, mem2) { \
item, \
{ OFF (__rw_time_t, __rw_time_t::mem1) / sizeof (_RWSTD_UINT32_T), \
OFF (__rw_time_t, __rw_time_t::mem2) / sizeof (_RWSTD_UINT32_T) } \
}
// each entry contains an `nl_item' value followed by the offset
// of __rw_time_t member; offsets rather than addresses are used
// to allow the __rw_time_t struct to be reallocated w/o having
// to reinitialize the array; this also allows the array to be
// static const for further speed improvement
ENTRY (D_T_FMT, d_t_fmt_off [0], d_t_fmt_off [1]),
ENTRY (D_FMT, d_fmt_off [0], d_fmt_off [1]),
ENTRY (T_FMT, t_fmt_off [0], t_fmt_off [1]),
#ifdef T_FMT_AMPM
ENTRY (T_FMT_AMPM, t_fmt_ampm_off [0], t_fmt_ampm_off [1]),
#else
ENTRY (D_T_FMT, t_fmt_ampm_off [0], t_fmt_ampm_off [1]),
#endif // T_FMT_AMPM
ENTRY (AM_STR, am_pm_off [0][0], am_pm_off [1][0]),
ENTRY (PM_STR, am_pm_off [0][1], am_pm_off [1][1]),
ENTRY (ABDAY_1, abday_off [0][0], abday_off [1][0]),
ENTRY (ABDAY_2, abday_off [0][1], abday_off [1][1]),
ENTRY (ABDAY_3, abday_off [0][2], abday_off [1][2]),
ENTRY (ABDAY_4, abday_off [0][3], abday_off [1][3]),
ENTRY (ABDAY_5, abday_off [0][4], abday_off [1][4]),
ENTRY (ABDAY_6, abday_off [0][5], abday_off [1][5]),
ENTRY (ABDAY_7, abday_off [0][6], abday_off [1][6]),
ENTRY (DAY_1, day_off [0][0], day_off [1][0]),
ENTRY (DAY_2, day_off [0][1], day_off [1][1]),
ENTRY (DAY_3, day_off [0][2], day_off [1][2]),
ENTRY (DAY_4, day_off [0][3], day_off [1][3]),
ENTRY (DAY_5, day_off [0][4], day_off [1][4]),
ENTRY (DAY_6, day_off [0][5], day_off [1][5]),
ENTRY (DAY_7, day_off [0][6], day_off [1][6]),
ENTRY (ABMON_1, abmon_off [0][ 0], abmon_off [1][ 0]),
ENTRY (ABMON_2, abmon_off [0][ 1], abmon_off [1][ 1]),
ENTRY (ABMON_3, abmon_off [0][ 2], abmon_off [1][ 2]),
ENTRY (ABMON_4, abmon_off [0][ 3], abmon_off [1][ 3]),
ENTRY (ABMON_5, abmon_off [0][ 4], abmon_off [1][ 4]),
ENTRY (ABMON_6, abmon_off [0][ 5], abmon_off [1][ 5]),
ENTRY (ABMON_7, abmon_off [0][ 6], abmon_off [1][ 6]),
ENTRY (ABMON_8, abmon_off [0][ 7], abmon_off [1][ 7]),
ENTRY (ABMON_9, abmon_off [0][ 8], abmon_off [1][ 8]),
ENTRY (ABMON_10, abmon_off [0][ 9], abmon_off [1][ 9]),
ENTRY (ABMON_11, abmon_off [0][10], abmon_off [1][10]),
ENTRY (ABMON_12, abmon_off [0][11], abmon_off [1][11]),
ENTRY (MON_1, mon_off [0][ 0], mon_off [1][ 0]),
ENTRY (MON_2, mon_off [0][ 1], mon_off [1][ 1]),
ENTRY (MON_3, mon_off [0][ 2], mon_off [1][ 2]),
ENTRY (MON_4, mon_off [0][ 3], mon_off [1][ 3]),
ENTRY (MON_5, mon_off [0][ 4], mon_off [1][ 4]),
ENTRY (MON_6, mon_off [0][ 5], mon_off [1][ 5]),
ENTRY (MON_7, mon_off [0][ 6], mon_off [1][ 6]),
ENTRY (MON_8, mon_off [0][ 7], mon_off [1][ 7]),
ENTRY (MON_9, mon_off [0][ 8], mon_off [1][ 8]),
ENTRY (MON_10, mon_off [0][ 9], mon_off [1][ 9]),
ENTRY (MON_11, mon_off [0][10], mon_off [1][10]),
ENTRY (MON_12, mon_off [0][11], mon_off [1][11]),
#ifdef ERA_D_T_FMT
ENTRY (ERA_D_T_FMT, era_d_t_fmt_off [0], era_d_t_fmt_off [1]),
#else
ENTRY (D_T_FMT, era_d_t_fmt_off [0], era_d_t_fmt_off [1]),
#endif // ERA_D_T_FMT
#ifdef ERA_D_FMT
ENTRY (ERA_D_FMT, era_d_fmt_off [0], era_d_fmt_off [1]),
#else
ENTRY (D_T_FMT, era_d_fmt_off [0], era_d_fmt_off [1]),
#endif // ERA_D_FMT
#ifdef ERA_T_FMT
ENTRY (ERA_T_FMT, era_t_fmt_off [0], era_t_fmt_off [1])
#else
ENTRY (D_T_FMT, era_t_fmt_off [0], era_t_fmt_off [1])
#endif // ERA_T_FMT
};
// points to the first __rw_time_t data member
_RWSTD_UINT32_T *pmem = _RWSTD_REINTERPRET_CAST (_RWSTD_UINT32_T*, pun);
const bool c_locale = 'C' == locname [0] && '\0' == locname [1];
for (size_t i = 0; i != sizeof nl_items / sizeof *nl_items; ++i) {
if (D_T_FMT == nl_items [i].nl_item && i)
continue;
// enforce POSIX rules for "%c" in the "C" locale
const char* const str = i || !c_locale ?
nl_langinfo (nl_items [i].nl_item) : "%a %b %e %H:%M:%S %Y";
_RWSTD_ASSERT (0 != str);
size_t size = 1 + strlen (str);
// estimate the size required to accommodate both
// the narrow and the wide char representations
const size_t estsize = size * (1 + sizeof (wchar_t));
if (off + estsize >= bufsize) {
// reallocate
const size_t tmpsize = sizeof *pun + bufsize * 2 + estsize;
// reallocate, again using operator new to avoid mismatch
// with facet destructor
char* const tmp =
_RWSTD_STATIC_CAST(char*, ::operator new (tmpsize));
memcpy (tmp, pun, sizeof *pun + off);
::operator delete (pun);
pun = _RWSTD_REINTERPRET_CAST (__rw_time_t*, tmp);
pmem = _RWSTD_REINTERPRET_CAST (_RWSTD_UINT32_T*, pun);
pbuf = tmp + sizeof *pun;
bufsize = bufsize * 2 + estsize;
}
// copy string to the allocated buffer and set its offset
memcpy (pbuf + off, str, size);
pmem [nl_items [i].moff [0]] =
_RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += size;
#ifndef _RWSTD_NO_WCHAR_T
// make sure wide string is properly aligned
const size_t align = off % sizeof (wchar_t);
if (align)
off += sizeof (wchar_t) - align;
// widen the narrow (multibyte) string into the allocated buffer
// (at an appropriately aligned offset) and set its offset
wchar_t* const pwbuf = _RWSTD_REINTERPRET_CAST (wchar_t*, pbuf + off);
size = mbstowcs (pwbuf, str, (bufsize - off) / sizeof (*pwbuf));
if (_RWSTD_SIZE_MAX == size) {
// conversion failure - should not happen
*pwbuf = L'\0';
size = 1;
}
else {
_RWSTD_ASSERT (L'\0' == pwbuf [size]);
size += 1;
}
pmem [nl_items [i].moff [1]] =
_RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += size * sizeof (wchar_t);
#endif // _RWSTD_NO_WCHAR_T
}
#else // if defined (_RWSTD_NO_NL_LANGINFO)
tm t;
memset (&t, 0, sizeof t);
size_t len;
#if 0 // FIXME: implement same way as above
static const struct {
int tm:: *pmem;
size_t moff;
const char fmt [3];
int begin;
int end;
int step;
} fmats [] = {
{ &tm::tm_wday, "%a", 0, 7, 1 },
{ &tm::tm_wday, "%A", 0, 7, 1 },
{ &tm::tm_mon, "%b", 0, 12, 1 },
{ &tm::tm_mon, "%B", 0, 12, 1 },
{ &tm::tm_hour, "%p", 1, 13, 12 }
};
for (size_t i = 0; i != sizeof fmats / sizef *fmats; ++i) {
for (int j = fmats [i].begin; j != fmats [i].end; j != fmats [i].step) {
t.*fmats [i].pmem = j;
len = strftime (pbuf + off, bufsize - off, fmats [i].fmt, &t);
pun->abday_off [0][t.tm_wday] = off;
off += len + 1;
}
}
#endif // 0/1
// copy abbreaviated and full names of days
for (t.tm_wday = 0; t.tm_wday != 7; ++t.tm_wday) {
len = strftime (pbuf + off, bufsize - off, "%a", &t);
pun->abday_off [0][t.tm_wday] =
_RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += len + 1;
len = strftime (pbuf + off, bufsize - off, "%A", &t);
pun->day_off [0][t.tm_wday] =
_RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += len + 1;
# ifndef _RWSTD_NO_WCHAR_T
// make sure wide strings are properly aligned
const size_t align = off % sizeof (wchar_t);
if (align)
off += sizeof (wchar_t) - align;
# ifndef _RWSTD_NO_WCSFTIME
wchar_t *pwbuf = _RWSTD_REINTERPRET_CAST (wchar_t*, pbuf + off);
len = wcsftime (pwbuf, (bufsize - off) / sizeof (*pwbuf), L"%a", &t);
pun->abday_off [1][t.tm_wday] =
_RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += (len + 1) * sizeof (wchar_t);
pwbuf = _RWSTD_REINTERPRET_CAST (wchar_t*, pbuf + off);
len = wcsftime (pwbuf, (bufsize - off) / sizeof (*pwbuf), L"%A", &t);
pun->day_off [1][t.tm_wday] = _RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += (len + 1) * sizeof (wchar_t);
# else // if defined (_RWSTD_NO_WCSFTIME)
// widen the narrow (multibyte) string into the allocated buffer
// (at an appropriately aligned offset) and set its offset
const char *str =
_RWSTD_STATIC_CAST (const char*, pun->abday (t.tm_wday, 0));
wchar_t *pwbuf = _RWSTD_REINTERPRET_CAST (wchar_t*, pbuf + off);
size_t size =
mbstowcs (pwbuf, str, (bufsize - off) / sizeof (*pwbuf));
if (_RWSTD_SIZE_MAX == size) {
// conversion failure - should not happen
*pwbuf = L'\0';
size = 1;
}
else {
_RWSTD_ASSERT (L'\0' == pwbuf [size]);
size += 1;
}
pun->abday_off [1][t.tm_wday] = off;
off += size * sizeof (wchar_t);
str = _RWSTD_STATIC_CAST (const char*, pun->day (t.tm_wday, 0));
pwbuf = _RWSTD_REINTERPRET_CAST (wchar_t*, pbuf + off);
size = mbstowcs (pwbuf, str, (bufsize - off) / sizeof (*pwbuf));
if (_RWSTD_SIZE_MAX == size) {
// conversion failure - should not happen
*pwbuf = L'\0';
size = 1;
}
else {
_RWSTD_ASSERT (L'\0' == pwbuf [size]);
size += 1;
}
pun->day_off [1][t.tm_wday] = off;
off += size * sizeof (wchar_t);
# endif // _RWSTD_NO_WCSFTIME
# endif // _RWSTD_NO_WCHAR_T
}
// copy abbreaviated and full names of months
for (t.tm_mon = 0; t.tm_mon != 12; ++t.tm_mon) {
len = strftime (pbuf + off, bufsize - off, "%b", &t);
pun->abmon_off [0][t.tm_mon] =
_RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += len + 1;
len = strftime (pbuf + off, bufsize - off, "%B", &t);
pun->mon_off [0][t.tm_mon] = _RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += len + 1;
# ifndef _RWSTD_NO_WCHAR_T
// make sure wide strings are properly aligned
const size_t align = off % sizeof (wchar_t);
if (align)
off += sizeof (wchar_t) - align;
# ifndef _RWSTD_NO_WCSFTIME
wchar_t *pwbuf = _RWSTD_REINTERPRET_CAST (wchar_t*, pbuf + off);
len = wcsftime (pwbuf, (bufsize - off) / sizeof (*pwbuf), L"%b", &t);
pun->abmon_off [1][t.tm_mon] =
_RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += (len + 1) * sizeof (wchar_t);
pwbuf = _RWSTD_REINTERPRET_CAST (wchar_t*, pbuf + off);
len = wcsftime (pwbuf, (bufsize - off) / sizeof (*pwbuf), L"%B", &t);
pun->mon_off [1][t.tm_mon] = _RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += (len + 1) * sizeof (wchar_t);
# else // if defined (_RWSTD_NO_WCSFTIME)
// widen the narrow (multibyte) string into the allocated buffer
// (at an appropriately aligned offset) and set its offset
const char *str =
_RWSTD_STATIC_CAST (const char*, pun->abmon (t.tm_mon, 0));
wchar_t *pwbuf = _RWSTD_REINTERPRET_CAST (wchar_t*, pbuf + off);
size_t size =
mbstowcs (pwbuf, str, (bufsize - off) / sizeof (*pwbuf));
if (_RWSTD_SIZE_MAX == size) {
// conversion failure - should not happen
*pwbuf = L'\0';
size = 1;
}
else {
_RWSTD_ASSERT (L'\0' == pwbuf [size]);
size += 1;
}
pun->abmon_off [1][t.tm_mon] = off;
off += size * sizeof (wchar_t);
str = _RWSTD_STATIC_CAST (const char*, pun->mon (t.tm_mon, 0));
pwbuf = _RWSTD_REINTERPRET_CAST (wchar_t*, pbuf + off);
size = mbstowcs (pwbuf, str, (bufsize - off) / sizeof (*pwbuf));
if (_RWSTD_SIZE_MAX == size) {
// conversion failure - should not happen
*pwbuf = L'\0';
size = 1;
}
else {
_RWSTD_ASSERT (L'\0' == pwbuf [size]);
size += 1;
}
pun->mon_off [1][t.tm_mon] = off;
off += size * sizeof (wchar_t);
# endif // _RWSTD_NO_WCSFTIME
# endif // _RWSTD_NO_WCHAR_T
}
// copy AM/PM designations
t.tm_hour = 1;
len = strftime (pbuf + off, bufsize - off, "%p", &t);
pun->am_pm_off [0][0] = _RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += len + 1;
t.tm_hour = 13;
len = strftime (pbuf + off, bufsize - off, "%p", &t);
pun->am_pm_off [0][1] = _RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += len + 1;
// determine the locale's "%x" format (date representation)
len = __rw_get_date_fmat (pbuf + off);
pun->d_fmt_off [0] = _RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += len + 1;
// determine the locale's "%X" format (time representation)
len = __rw_get_time_fmat (pbuf + off);
pun->t_fmt_off [0] = _RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += len + 1;
// FIXME: determine "%r" at runtime (just like "%x" and "%X")
// or have time_put use strftime() instead of hardcoding
// the default "POSIX" format
static const char t_fmt_ampm_fmat[] = "%I:%M:%S %p";
len = sizeof t_fmt_ampm_fmat;
memcpy (pbuf + off, t_fmt_ampm_fmat, len);
pun->t_fmt_ampm_off [0] = _RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += len;
// FIXME: determine "%c" at runtime (just like "%x" and "%X"),
// or have time_put use strftime() instead of hardcoding
// the default "C" format
static const char d_t_fmat[] = "%a %b %e %H:%M:%S %Y";
len = sizeof d_t_fmat;
memcpy (pbuf + off, d_t_fmat, len);
pun->d_t_fmt_off [0] = _RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += len;
// FIXME: determine "%Ec", "%EX", "%Ex"
pbuf [off] = '\0';
pun->era_d_t_fmt_off [0] = _RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
pun->era_d_fmt_off [0] = _RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
pun->era_t_fmt_off [0] = _RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += 1;
# ifndef _RWSTD_NO_WCHAR_T
const size_t align = off % sizeof (wchar_t);
if (align)
off += sizeof (wchar_t) - align;
const char *str;
wchar_t *pwbuf;
size_t size;
# ifndef _RWSTD_NO_WCSFTIME
t.tm_hour = 1;
pwbuf = _RWSTD_REINTERPRET_CAST (wchar_t*, pbuf + off);
len = wcsftime (pwbuf, (bufsize - off) / sizeof (*pwbuf), L"%p", &t);
pun->am_pm_off [1][0] = _RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += (len + 1) * sizeof (wchar_t);
t.tm_hour = 13;
pwbuf = _RWSTD_REINTERPRET_CAST (wchar_t*, pbuf + off);
len = wcsftime (pwbuf, (bufsize - off) / sizeof (*pwbuf), L"%p", &t);
pun->am_pm_off [1][1] = _RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += (len + 1) * sizeof (wchar_t);
# else // if defined (_RWSTD_NO_WCSFTIME)
str = _RWSTD_STATIC_CAST (const char*, pun->am_pm (0, 0));
pwbuf = _RWSTD_REINTERPRET_CAST (wchar_t*, pbuf + off);
size = mbstowcs (pwbuf, str, (bufsize - off) / sizeof (*pwbuf));
if (_RWSTD_SIZE_MAX == size) {
// conversion failure - should not happen
*pwbuf = L'\0';
size = 1;
}
else {
_RWSTD_ASSERT (L'\0' == pwbuf [size]);
size += 1;
}
pun->am_pm_off [1][0] = off;
off += size * sizeof (wchar_t);
str = _RWSTD_STATIC_CAST (const char*, pun->am_pm (1, 0));
pwbuf = _RWSTD_REINTERPRET_CAST (wchar_t*, pbuf + off);
size = mbstowcs (pwbuf, str, (bufsize - off) / sizeof (*pwbuf));
if (_RWSTD_SIZE_MAX == size) {
// conversion failure - should not happen
*pwbuf = L'\0';
size = 1;
}
else {
_RWSTD_ASSERT (L'\0' == pwbuf [size]);
size += 1;
}
pun->am_pm_off [1][1] = off;
off += size * sizeof (wchar_t);
# endif // _RWSTD_NO_WCSFTIME
// convert "%x" to its wide equivalent
str = _RWSTD_STATIC_CAST (const char*, pun->d_fmt (0));
pwbuf = _RWSTD_REINTERPRET_CAST (wchar_t*, pbuf + off);
size = mbstowcs (pwbuf, str, (bufsize - off) / sizeof (*pwbuf));
if (_RWSTD_SIZE_MAX == size) {
// conversion failure - should not happen
*pwbuf = L'\0';
size = 1;
}
else {
_RWSTD_ASSERT (L'\0' == pwbuf [size]);
size += 1;
}
pun->d_fmt_off [1] = _RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += size * sizeof (wchar_t);
// convert "%X" to its wide equivalent
str = _RWSTD_STATIC_CAST (const char*, pun->t_fmt (0));
pwbuf = _RWSTD_REINTERPRET_CAST (wchar_t*, pbuf + off);
size = mbstowcs (pwbuf, str, (bufsize - off) / sizeof (*pwbuf));
if (_RWSTD_SIZE_MAX == size) {
// conversion failure - should not happen
*pwbuf = L'\0';
size = 1;
}
else {
_RWSTD_ASSERT (L'\0' == pwbuf [size]);
size += 1;
}
pun->t_fmt_off [1] = _RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += size * sizeof (wchar_t);
// convert "%c" to its wide equivalent
str = _RWSTD_STATIC_CAST (const char*, pun->d_t_fmt (0));
pwbuf = _RWSTD_REINTERPRET_CAST (wchar_t*, pbuf + off);
size = mbstowcs (pwbuf, str, (bufsize - off) / sizeof (*pwbuf));
if (_RWSTD_SIZE_MAX == size) {
// conversion failure - should not happen
*pwbuf = L'\0';
size = 1;
}
else {
_RWSTD_ASSERT (L'\0' == pwbuf [size]);
size += 1;
}
pun->d_t_fmt_off [1] = _RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += size * sizeof (wchar_t);
// convert "%r" to its wide equivalent
str = _RWSTD_STATIC_CAST (const char*, pun->t_fmt_ampm (0));
pwbuf = _RWSTD_REINTERPRET_CAST (wchar_t*, pbuf + off);
size = mbstowcs (pwbuf, str, (bufsize - off) / sizeof (*pwbuf));
if (_RWSTD_SIZE_MAX == size) {
// conversion failure - should not happen
*pwbuf = L'\0';
size = 1;
}
else {
_RWSTD_ASSERT (L'\0' == pwbuf [size]);
size += 1;
}
pun->t_fmt_ampm_off [1] = _RWSTD_STATIC_CAST (_RWSTD_UINT32_T, off);
off += size * sizeof (wchar_t);
# endif // _RWSTD_NO_WCHAR_T
#endif // _RWSTD_NO_NL_LANGINFO
// set `impdata' and `impsize' (base dtor will delete)
__rw_access::_C_get_impdata (*_RWSTD_CONST_CAST (__rw_facet*, pfacet))
= pun;
__rw_access::_C_get_impsize (*_RWSTD_CONST_CAST (__rw_facet*, pfacet))
= _RWSTD_SIZE_MAX;
// call self recursively on already initialized `impdata'
return __rw_get_timepunct (pfacet, flags, inx);
}
size_t
__rw_get_timepunct (const __rw_facet *pfacet, int flags,
const void *names[], size_t sizes[])
{
const bool wide = !!(flags & __rw_wide);
const int member = flags & ~__rw_wide;
size_t count = 0;
int flags_2 = 0;
switch (member) {
case __rw_abday:
case __rw_day:
count = 7;
break;
case __rw_abmon:
case __rw_month:
count = 12;
break;
case __rw_ampm:
count = 2;
break;
case __rw_d_t_fmt:
case __rw_d_fmt:
case __rw_t_fmt:
case __rw_t_fmt_ampm:
case __rw_era_d_t_fmt:
case __rw_era_d_fmt:
case __rw_era_t_fmt:
count = 1;
break;
case __rw_era_fmts:
case __rw_era_names: {
const __rw_time_t* const ptime =
_RWSTD_STATIC_CAST (const __rw_time_t*,
__rw_get_timepunct (pfacet, 0, 0));
count = size_t (ptime->era_count ());
break;
}
case __rw_abday | __rw_day:
count = 7;
flags = __rw_day;
flags_2 = __rw_abday;
break;
case __rw_abmon | __rw_month:
count = 12;
flags = __rw_month;
flags_2 = __rw_abmon;
break;
case __rw_alt_digits: {
const __rw_time_t* const ptime =
_RWSTD_STATIC_CAST (const __rw_time_t*,
__rw_get_timepunct (pfacet, 0, 0));
count = size_t (ptime->alt_digits_count ());
break;
}
default:
_RWSTD_ASSERT (!"bad discriminant");
return 0;
}
typedef _STD::char_traits<char> CTraits;
#ifndef _RWSTD_NO_WCHAR_T
typedef _STD::char_traits<wchar_t> WTraits;
#endif // _RWSTD_NO_WCHAR_T
if (wide)
flags |= __rw_wide;
for (size_t i = 0; i != count; ++i) {
names [i] = __rw_get_timepunct (pfacet, flags, i);
#ifndef _RWSTD_NO_WCHAR_T
if (wide)
sizes [i] = WTraits::length ((const wchar_t*)names [i]);
else
#endif // _RWSTD_NO_WCHAR_T
sizes [i] = CTraits::length ((const char*)names [i]);
}
if (flags_2) {
if (wide)
flags_2 |= __rw_wide;
for (size_t j = count; j != 2 * count; ++j) {
names [j] = __rw_get_timepunct (pfacet, flags_2, j - count);
#ifndef _RWSTD_NO_WCHAR_T
if (wide)
sizes [j] = WTraits::length ((const wchar_t*)names [j]);
else
#endif // _RWSTD_NO_WCHAR_T
sizes [j] = CTraits::length ((const char*)names [j]);
}
return 2 * count;
}
return count;
}
_RWSTD_EXPORT size_t
__rw_get_timepunct (const __rw_facet *pfacet,
int data [4], tm *tmb, int **pmem,
const void *names[], size_t sizes[])
{
enum _Fmt { // for convenience
_AmPm = __rw_ampm,
_Date = __rw_d_fmt,
_DateTime = __rw_d_t_fmt,
_Days = __rw_day | __rw_abday,
_Mons = __rw_month | __rw_abmon,
_Time = __rw_t_fmt,
_TimeAmPm = __rw_t_fmt_ampm,
// alternative formats and eras
_AltDigits = __rw_alt_digits,
_EraDate = __rw_era_d_fmt,
_EraDateTime = __rw_era_d_t_fmt,
_EraFmts = __rw_era_fmts,
_EraTime = __rw_era_t_fmt,
_EraNames = __rw_era_names
};
const char fmt = data [0]; // on input, get the format...
const char mod = data [1]; // ...and modifier characters
const bool wide = 0 != data [2]; // narrow or wide specialization?
int &adj = data [0]; // value to increment computed result by
int &fac = data [1]; // value to multiply computed result by
int &lob = data [2]; // lower bound on valid input value
int &hib = data [3]; // higher bound on valid input value
adj = 0;
fac = 1;
lob = 0;
hib = 0;
const void** pv =
_RWSTD_REINTERPRET_CAST (const void**, names);
size_t cnt = 0;
int flags = 0;
if ('E' == mod) {
switch (fmt) {
case 'C':
// %EC: the name of the period in alternative representation
*pmem = &tmb->tm_year;
flags = _EraNames;
break;
case 'c':
// %Ec: alternative date and time representation
flags = _EraDateTime;
break;
case 'X':
// %EX: alternative time representation
flags = _EraTime;
break;
case 'x':
// %Ex: alternative date representation
flags = _EraDate;
break;
case 'Y':
// %EY: full alternative year representation
flags = _EraFmts;
break;
case 'y':
// %Ey: offset from %EC (year only) in alternative representation
// FIXME: implement
break;
}
}
else if ('O' == mod) {
flags = _AltDigits;
switch (fmt) {
case 'd': case 'e':
// %Od: day of the month using alternative numeric symbols
// %Oe: equivalent to %Od
*pmem = &tmb->tm_mday;
lob = 1;
hib = 31;
break;
case 'H':
// %OH: hour (24-hour clock) using alternative numeric symbols
*pmem = &tmb->tm_hour;
hib = 23;
break;
case 'I':
// %OI: hour (12-hour clock) using the alternative numeric symbols
*pmem = &tmb->tm_hour;
hib = 12;
break;
case 'm':
// %Om: month using alternative numeric symbols
*pmem = &tmb->tm_mon;
adj = -1;
hib = 11;
break;
case 'M':
// %OM: minutes using alternative numeric symbols
*pmem = &tmb->tm_min;
hib = 59;
break;
case 'S':
// %OS seconds using alternative numeric symbols
*pmem = &tmb->tm_sec;
hib = 60;
break;
case 'U':
// %OU: Sunday-based week number of the year
// using alternative numeric symbols
// FIXME: implement
hib = 52;
break;
case 'W':
// %OW: Monday-based week number of the year
// using alternative numeric symbols
// FIXME: implement
hib = 52;
break;
case 'w':
// %Ow: Sunday-based weekday using alternative numeric symbols
*pmem = &tmb->tm_wday;
hib = 6;
break;
case 'y':
// %Oy: year (offset from %C) using alternative numeric symbols
*pmem = &tmb->tm_year;
hib = 99;
break;
default:
flags = 0;
}
}
else {
switch (fmt) {
case 'A': case 'a': // weekday [00..11]
*pmem = &tmb->tm_wday;
hib = 6;
flags = _Days;
break;
case 'B': case 'b': case 'h': // month [00..11]
*pmem = &tmb->tm_mon;
hib = 11;
flags = _Mons;
break;
case 'C': // century [00..99]
adj = -1900;
fac = 100;
lob = -1900;
hib = 8000;
*pmem = &tmb->tm_year;
break;
case 'c': // locale-specific date
flags = _DateTime;
break;
case 'D': { // equivalent to "%m/%d/%y"
static const void* const pat[] = { "%m/%d/%y", L"%m/%d/%y" };
names [0] = pat [wide];
sizes [0] = 8;
cnt = 1;
break;
}
case 'd': case 'e': // day of month [01..31]
lob = 1;
hib = 31;
*pmem = &tmb->tm_mday;
break;
case 'k': // popular extension (AIX, Linux, Solaris)
case 'H': // hour [00..23]
hib = 23;
*pmem = &tmb->tm_hour;
break;
case 'l': // popular extension (Linux, Solaris)
case 'I': // hour [01..12]
adj = -1;
hib = 11;
*pmem = &tmb->tm_hour;
break;
case 'j': // day number of the year [001..366]
adj = -1;
hib = 365;
*pmem = &tmb->tm_yday;
break;
case 'M': // minutes [00..59]
hib = 59;
*pmem = &tmb->tm_min;
break;
case 'm': // month [00..11]
adj = -1;
hib = 11;
*pmem = &tmb->tm_mon;
break;
case 'p': // the locale's equivalent of AM or PM
*pmem = &tmb->tm_hour;
flags = _AmPm;
break;
case 'R': { // equivalent to "%H:%M"
static const void* const pat[] = { "%H:%M", L"%H:%M" };
names [0] = pat [wide];
sizes [0] = 5;
cnt = 1;
break;
}
case 'r': // 12-hour clock time using the AM/PM notation
flags = _TimeAmPm;
break;
case 'S': // seconds [00..60]
hib = 60;
*pmem = &tmb->tm_sec;
break;
case 'T': { // equivalent to "%H:%M:%S"
static const void* const pat[] = { "%H:%M:%S", L"%H:%M:%S" };
names [0] = pat [wide];
sizes [0] = 8;
cnt = 1;
break;
}
case 'U': // Sunday-based week number [00..53]
hib = 53;
// FIXME: implement
break;
case 'W': // Monday-based week number [00..53]
hib = 53;
// FIXME: implement
break;
case 'w': // Sunday-based weekday [0..6]
hib = 6;
*pmem = &tmb->tm_wday;
break;
case 'x': // locale-specific date
flags = _Date;
break;
case 'X': // locale-specific time
flags = _Time;
break;
case 'y': // 2-digit year, [00..99]
*pmem = &tmb->tm_year;
break;
case 'Y': // 4-digit year
adj = -1900;
lob = -1900;
hib = _RWSTD_INT_MAX - 1900;
*pmem = &tmb->tm_year;
break;
case '%':
break;
}
}
if (flags)
cnt = __rw_get_timepunct (pfacet, flags | (wide ? __rw_wide : 0),
pv, sizes);
return cnt;
}
static char*
__rw_fmt_time (const __rw_facet *pfacet, char *buf, size_t bufsize,
_STD::ios_base &flags, char fill, const tm *tmb,
const char *pat)
{
_RWSTD_ASSERT (0 != tmb);
_RWSTD_ASSERT (0 != pat);
for (; *pat; ++pat) {
if ('%' == *pat && pat [1]) {
int width = -1; // width of curent specifier (-1 = unspecified)
int prec = -1; // precision of current specifier
++pat;
// Extension: width and precision
// Reference: strftime() on HP-UX 11.00
// Format: %[[-|0]w][.p]
// [-|0] right justification (left by default),
// padded with '0' characters (spaces by default)
// w minimum field width
// .p minimum number of digits (expanded on the left with '0')
// for the following specifiers:
// d, H, I, j, m, M, o, S, U, w, W, y and Y
// maximum number of characters (truncated if necessary)
// for the following specifiers:
// a, A, b, B, c, D, E, F, h, n, N, p, r, t,
// T, x, X, z, Z, and %
if ('-' == *pat) {
// right justify
// FIXME: handle right justification
++pat;
}
else if ('0' == *pat) {
// pad justified output with '0'
fill = '0';
++pat;
}
if (*pat >= '0' && *pat <= '9') {
// if width is given, set precision to 0 to prevent
// it from being overridden later by __rw_put_time()
width = 0;
prec = 0;
for (; *pat >= '0' && *pat <= '9'; ++pat) {
// set the width of the current specifier
width = width * 10 + *pat - '0';
}
}
if ('.' == *pat) {
++pat;
if (*pat >= '0' && *pat <= '9') {
// treat missing width as 0 (e.g., "%.3d") to prevent
// it from being overridden later by __rw_put_time()
if (-1 == width)
width = 0;
prec = 0;
for (; *pat >= '0' && *pat <= '9'; ++pat) {
// set the precision of the current specifier
prec = prec * 10 + *pat - '0';
}
}
}
char fmtmod = *pat; // format modifier
char fmt; // format specifier
if ('E' == fmtmod && *pat) {
fmt = *++pat;
switch (fmt) {
// check valid format modifiers
case 'c': case 'C': case 'x': case 'X': case 'y': case 'Y':
break;
case '\0':
break;
default:
fmt = 0; // `pat' doesn't point to a valid format
}
}
else if ('O' == fmtmod) {
fmt = *++pat;
switch (fmt) {
// check valid format modifiers
case 'd': case 'e': case 'H': case 'I': case 'm': case 'M':
case 'S': case 'u': case 'U': case 'V': case 'w': case 'W':
case 'y':
break;
case '\0':
break;
default:
fmt = 0; // `pat' doesn't point to a valid format
}
}
else {
fmt = fmtmod;
fmtmod = 0;
}
if (char (-1) != fmt) {
char *end = __rw_put_time (pfacet, buf, bufsize, flags, fill,
tmb, fmt, fmtmod, width, prec);
if (!end)
return 0;
buf = end;
continue;
}
}
*buf++ = *pat;
}
return buf;
}
#ifndef _RWSTD_NO_WCHAR_T
static wchar_t*
__rw_fmt_time (const __rw_facet *pfacet, wchar_t *wbuf, size_t bufsize,
_STD::ios_base &flags, wchar_t fill, const tm *tmb,
const wchar_t *wpat)
{
_RWSTD_ASSERT (0 != tmb);
_RWSTD_ASSERT (0 != wpat);
for (; *wpat; ++wpat) {
if (L'%' == *wpat && wpat [1]) {
int width = -1; // width of the curent specifier (-1 = unspecified)
int prec = -1; // precision of the current specifier
++wpat;
// Extension: width and precision
// Reference: strftime() on HP-UX 11.00
// Format: %[[-|0]w][.p]
// [-|0] right justification (left by default),
// padded with '0' characters (spaces by default)
// w minimum field width
// .p minimum number of digits (expanded on the left with '0')
// for the following specifiers:
// d, H, I, j, m, M, o, S, U, w, W, y and Y
// maximum number of characters (truncated if necessary)
// for the following specifiers:
// a, A, b, B, c, D, E, F, h, n, N, p, r, t,
// T, x, X, z, Z, and %
if (L'-' == *wpat) {
// right justify
// FIXME: handle right justification
++wpat;
}
else if (L'0' == *wpat) {
// pad justified output with '0'
fill = L'0';
++wpat;
}
if (*wpat >= L'0' && *wpat <= L'9') {
// if width is given, set precision to 0 to prevent
// it from being overridden later by __rw_put_time()
width = 0;
prec = 0;
for (; *wpat >= L'0' && *wpat <= L'9'; ++wpat) {
// set the width of the current specifier
width = width * 10U + *wpat - L'0';
}
}
if (L'.' == *wpat) {
++wpat;
if (*wpat >= L'0' && *wpat <= L'9') {
// treat missing width as 0 (e.g., "%.3d") to prevent
// it from being overridden later by __rw_put_time()
if (-1 == width)
width = 0;
prec = 0;
for (; *wpat >= L'0' && *wpat <= L'9'; ++wpat) {
// set the precision of the current specifier
prec = prec * 10 + *wpat - L'0';
}
}
}
char fmtmod = char (*wpat); // format modifier
char fmt; // format specifier
if ('E' == fmtmod && *wpat) {
fmt = char (*++wpat);
switch (fmt) {
// check valid format modifiers
case 'c': case 'C': case 'x': case 'X': case 'y': case 'Y':
break;
case '\0':
break;
default:
fmt = 0; // `wpat' doesn't point to a valid format
}
}
else if ('O' == fmtmod) {
fmt = char (*++wpat);
switch (fmt) {
// check valid format modifiers
case 'd': case 'e': case 'H': case 'I': case 'm': case 'M':
case 'S': case 'u': case 'U': case 'V': case 'w': case 'W':
case 'y':
break;
case '\0':
break;
default:
fmt = 0; // `wpat' doesn't point to a valid format
}
}
else {
fmt = fmtmod;
fmtmod = 0;
}
if (char (-1) != fmt) {
wchar_t* const end =
__rw_put_time (pfacet, wbuf, bufsize, flags, fill,
tmb, fmt, fmtmod, width, prec);
if (!end)
return 0;
wbuf = end;
continue;
}
}
*wbuf++ = *wpat;
}
return wbuf;
}
#endif // _RWSTD_NO_WCHAR_T
// returns true iff `y' is a leap year
static inline bool
__rw_isleap (int y)
{
return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
}
// returns the ISO 8601 week number from the given date
// sets `year' to the ISO 8601 year number for that date
static int
__rw_iso_week (int& year, int yday, int wday)
{
// adjust the week day - tm struct uses a 0-based weekday number
// starting with Sunday, while ISO 8601 week starts with Monday
const int w = (wday + 7 - 1) % 7;
// number of days in the current year
const int y = 365 + __rw_isleap (year);
// number of days between first of January and the first week-end
const int fdoy = (yday + 7 - w) % 7;
int isow;
if (fdoy >= 4) {
// first of Jan belongs to first week
isow = yday + (7 - fdoy);
}
else {
// first of Jan belongs to last week of last year
isow = yday - fdoy;
}
if (isow < 0) {
// the day belongs to last year
year--;
return __rw_iso_week (year, yday + 365 + __rw_isleap (year), wday);
}
// the day could belong to next year; check that
const int r = y - yday;
if (r <= 3 && 3 - r >= w) {
year++;
return w;
}
return isow;
}
// returns a pointer to the era_t element describing the era
// corresponding to the date pointed to by `tmb', or 0 if no
// such era exists
static const __rw_time_t::era_t*
__rw_get_era (const __rw_time_t *ptime, const tm *tmb)
{
_RWSTD_ASSERT (0 != tmb);
const int year = 1900 + tmb->tm_year;
// get the number of eras in this locale
const size_t neras = size_t (ptime->era_count ());
for (size_t i = 0; i != neras; ++i) {
const __rw_time_t::era_t* const pera = ptime->era (i);
_RWSTD_ASSERT (0 != pera);
// offset < 0 implies direction of '-'
// offset >= 0 implies direction of '+'
const int b = pera->offset < 0; // start index
const int e = !b; // end index
// check to see if the specified date belongs to the era
if ( (year > pera->year [b] && year < pera->year [e])
|| ( (year == pera->year [b] || year == pera->year [e])
&& ( ( tmb->tm_mon > pera->month [b]
&& tmb->tm_mon < pera->month [e])
|| ( ( tmb->tm_mon == pera->month [b]
|| tmb->tm_mon == pera->month [e])
&& tmb->tm_mday >= pera->day [b]
&& tmb->tm_mday <= pera->day [e])))) {
return pera;
}
}
return 0;
}
static int
__rw_get_zone_off (const char *var, const char **var_end)
{
_RWSTD_ASSERT (0 != var);
_RWSTD_ASSERT (0 != var_end);
bool neg = false;
if ('-' == *var) {
neg = true;
++var;
}
else if ('+' == *var) {
++var;
}
int offset = 0;
// compute the four-digit offset in the format hhmm from
// the string `var' in the format hh[:mm[:ss]], where
// hh is in the range "0" through "24", and ss and mm
// both in the range "00" through "59"
if (ISDIGIT (*var)) {
offset = *var++ - '0';
if (ISDIGIT (*var)) {
if (offset < 2 || (*var >= '0' && *var <= '4')) {
// add offset in hours
offset = offset * 10 + *var++ - '0';
}
else
offset = _RWSTD_INT_MIN;
}
if (0 <= offset)
offset *= 100;
if (':' == *var) {
++var;
if (var [0] >= '0' && var [0] <= '5' && ISDIGIT (var [1])) {
// add offset in minutes
offset += 10 * (*var++ - '0');
offset += (*var++ - '0');
if (':' == *var) {
++var;
if ( var [0] >= '0' && var [0] <= '5'
&& ISDIGIT (var [1])) {
// ignore offset in seconds
var += 2;
}
else {
offset = _RWSTD_INT_MIN;
}
}
}
else {
offset = _RWSTD_INT_MIN;
}
}
}
else {
offset = _RWSTD_INT_MIN;
}
*var_end = var;
return neg ? -offset : offset;
}
// Also used as a parameter to rw_get_static_mutex to ensure
// the uniqueness of the mutex object used in synchronizing
// the threads using __rw_get_time_put_data()
// (see RWSTD_MT_STATIC_GUARD below)
struct __rw_time_put_data
{
const void *fmt; // format string to use to format val with
const void *str; // the final formatted string
int val; // numeric value to format
int altval; // value to format using alternative symbols
int width; // the width of numeric output (as in "%*.0d")
int prec; // the precision of numeric output (as in "%0.*d")
};
static int
__rw_get_zone (__rw_time_put_data &tpd, const char *var, int isdst)
{
_RWSTD_ASSERT (0 != var);
// TZ format:
// :characters
// or
// std offset[dst[offset][,start[/time],end[/time]]]
int std_off;
if (':' == *var)
goto use_tzset; // can't parse implementation-defined formats
if ('<' == *var) {
// skip past the alphanumeric std designator enclosed in <>
while (*var && '>' != *var++)
/* no-op */;
}
else {
const char* const stdbeg = var;
// skip past the alphabetic std designator to the beginning
// of the required dst offset; if no dst offset is found,
// the variable is not in the required POSIX format and must
// be handled in an implementation-defined way (i.e., using
// tzset())
while (ISALPHA (*var))
++var;
if ( var == stdbeg
|| (*var && '+' != *var && '-' != *var && !ISDIGIT (*var)))
goto use_tzset;
}
// get the std offset from TZ in case dst must be computed
std_off = __rw_get_zone_off (var, &var);
if (*var && _RWSTD_INT_MIN != std_off && isdst) {
const char* const dstbeg = var;
if ('<' == *var)
// skip past the quoted alphanumeric dst designator
while (*var && '>' != *var++)
/* no-op */;
else {
// skip past the alphabetic dst designator
while (ISALPHA (*var))
++var;
}
if ( var == dstbeg
|| (*var && '+' != *var && '-' != *var && !ISDIGIT (*var)))
goto use_tzset;
tpd.val = __rw_get_zone_off (var, &var);
if (_RWSTD_INT_MIN == tpd.val) {
// failed to extract dst offset from TZ
// check if that's because there was none
if ('\0' == *var) {
// according to SUSv3 (POSIC), if no offset follows dst,
// the alternative time is assumed to be one hour ahead
// of standard time
tpd.val = std_off + 100;
if (2359 < tpd.val)
tpd.val %= 2400;
}
else
goto use_tzset;
}
}
else
goto use_tzset;
return 0; // success
use_tzset:
// the lock cannot prevent another thread from calling tzset()
// directly (or one of the other functions in the #else block)
// for "stronger" thread-safety it might be better to take the
// #else branch below and use gmtime_r() there (that would
// only be safe if mktime() were thread-safe)
_RWSTD_MT_STATIC_GUARD (__rw_time_put_data);
#ifndef _RWSTD_NO_TIMEZONE
// set the POSIX `timezone' and `daylight' extern variables
tzset ();
// tzet() sets timezone to the difference, in seconds, between
// Coordinated Universal Time (UTC) and local standard time
tpd.val = _RWSTD_STATIC_CAST (int, timezone / 60);
#else // if defined (_RWSTD_NO_TIMEZONE)
tpd.val = 0;
// calculate the difference the hard way
static const time_t tgm = 0;
const tm* const tmp = gmtime (&tgm);
if (tmp) {
tm tmgm = *tmp;
const time_t tlocal = mktime (&tmgm);
if (time_t (-1) != tlocal) {
const double diff = difftime (tlocal, tgm);
tpd.val = int (diff / 60);
}
else {
// FIXME: indicate the nature of the error to the caller somehow
return -1;
}
}
else {
// FIXME: indicate the nature of the error to the caller somehow
return -1;
}
#endif // _RWSTD_NO_TIMEZONE
// set the value to the difference in the HH:MM "format"
tpd.val = 100 * (tpd.val / 60) + tpd.val % 60;
if (daylight && isdst)
tpd.val += (tpd.val < 0 ? -100 : 100) * isdst;
return 0; // success
}
static void
__rw_get_zone_name (__rw_time_put_data&, const char*, int)
{
// FIXME: retrieve tiem zone name from TZ, store it in a suitable
// buffer, and have time_put::put() output the contents of that
// buffer
}
static void
__rw_get_time_put_data (__rw_time_put_data &tpd,
const __rw_facet *facet,
const tm *tmb,
char fmt, char mod, bool wide)
{
_RWSTD_ASSERT (0 != facet);
// extension: if `tmb' is 0, output the locale-specific format string
_RWSTD_ASSERT ( 0 != tmb
|| 'c' == fmt || 'r' == fmt || 'x' == fmt || 'X' == fmt);
static const union {
#ifndef _RWSTD_NO_WCHAR_T
wchar_t wide_data [1];
#endif // _RWSTD_NO_WCHAR_T
char data [4];
} null_fmt = { { 0 } };
const __rw_time_t *ptime =
_RWSTD_STATIC_CAST (const __rw_time_t*, facet->_C_data ());
if (!ptime)
ptime = _RWSTD_STATIC_CAST (const __rw_time_t*,
__rw_get_timepunct (facet, 0, 0));
_RWSTD_ASSERT (0 != ptime);
// invalidate data
tpd.fmt = tpd.str = 0;
tpd.val = tpd.altval = _RWSTD_INT_MIN;
tpd.width = tpd.prec = 1;
switch (fmt) {
case 'a':
// %a: the locale's abbreviated weekday name. [tm_wday]
tpd.str = ptime->abday (tmb->tm_wday % 7U, wide);
break;
case 'A':
// %A: the locale's full weekday name. [tm_wday]
tpd.str = ptime->day (tmb->tm_wday % 7U, wide);
break;
case 'b':
// %b: the locale's abbreviated month name. [tm_mon]
tpd.str = ptime->abmon (tmb->tm_mon % 12U, wide);
break;
case 'B':
// %B: the locale's full month name. [tm_mon]
tpd.str = ptime->mon (tmb->tm_mon % 12U, wide);
break;
case 'c':
// %c: the locale's appropriate date and time representation.
// %Ec: the locale's alternative date and time representation.
if ('E' == mod)
tpd.fmt = ptime->era_d_t_fmt (wide);
if ( !tpd.fmt
#ifndef _RWSTD_NO_WCHAR_T
|| (wide && !*_RWSTD_STATIC_CAST (const wchar_t*, tpd.fmt))
#endif // _RWSTD_NO_WCHAR_T
|| (!wide && !*_RWSTD_STATIC_CAST (const char*, tpd.fmt)))
tpd.fmt = ptime->d_t_fmt (wide);
_RWSTD_ASSERT (0 != tpd.fmt);
break;
case 'C': {
// %C: the year divided by 100 and truncated to an integer,
// as a decimal number (00-99). [tm_year]
// %EC: the name of the base year (period) in the locale's
// alternative representation.
if ('E' == mod) {
const __rw_time_t::era_t *pera = __rw_get_era (ptime, tmb);
if (pera) {
const _RWSTD_PTRDIFF_T i = pera - ptime->era (0);
tpd.str = ptime->era_name (i, wide);
break;
}
}
// take care to handle negative tm_year
tpd.val = ((1900 + tmb->tm_year) / 100) % 100;
tpd.prec = 2;
break;
}
case 'd':
// %d: the day of the month as a decimal number (01-31). [tm_mday]
// %Od: the day of the month, using the locale's alternative numeric
// symbols (filled as needed with leading zeros, or with leading
// spaces if there is no alternative symbol for zero).
if ('O' == mod)
tpd.altval = tmb->tm_mday;
else
tpd.val = tmb->tm_mday;
tpd.prec = 2;
break;
case 'D': {
// %D: equivalent to "%m/%d/%y". [tm_mon, tm_mday, tm_year]
static const void* const fmats[] = { "%m/%d/%y", L"%m/%d/%y" };
tpd.fmt = fmats [wide];
break;
}
case 'e':
// %e: the day of the month as a decimal number (1-31);
// a single digit is preceded by a space. [tm_mday]
// %Oe: is replaced by the day of the month, using the locale's
// alternative numeric symbols (filled as needed with leading
// spaces).
if ('O' == mod)
tpd.altval = tmb->tm_mday;
else
tpd.val = tmb->tm_mday;
tpd.width = 2;
break;
case 'F': {
// %F: equivalent to "%Y-%m-%d" (the ISO 8601 date format).
// [tm_year, tm_mon, tm_mday]
static const void* const fmats[] = { "%Y-%m-%d", L"%Y-%m-%d" };
tpd.fmt = fmats [wide];
break;
}
case 'g':
// %g: the last 2 digits of the week-based year as a decimal number
// (00-99). [tm_year, tm_wday, tm_yday]
// get the ISO 8601 year
tpd.val = tmb->tm_year + 1900;
__rw_iso_week (tpd.val, tmb->tm_yday, tmb->tm_wday);
tpd.val %= 100;
tpd.prec = 2;
break;
case 'G':
// %G: the week-based year as a decimal number (e.g., 1997).
// [tm_year, tm_wday, tm_yday]
// get the ISO 8601 year
tpd.val = tmb->tm_year + 1900;
__rw_iso_week (tpd.val, tmb->tm_yday, tmb->tm_wday);
break;
case 'h':
// %h: equivalent to "%b". [tm_mon]
tpd.fmt = ptime->abmon (tmb->tm_mon % 12U, wide);
break;
case 'k':
// %k: the hour (24-hour clock) as a decimal number (0-23)
// with single digits preceded by a blank. [tm_hour]
// this is a popular extension implemented for example
// on AIX, Linux and Solaris
tpd.width = 2;
// fall through
case 'H':
// %H: the hour (24-hour clock) as a decimal number (00-23). [tm_hour]
// %OH: the hour (24-hour clock), using the locale's alternative
// numeric symbols.
if ('O' == mod)
tpd.altval = tmb->tm_hour;
else
tpd.val = tmb->tm_hour;
// set precision only for "%H" and not for "%k"
// (i.e., when width hasn't been overridden above)
if (1 == tpd.width)
tpd.prec = 2;
break;
case 'l':
// %l: the hour (12-hour clock) as a decimal number (1,12)
// with single digits preceded by a blank. [tm_hour]
// this is a popular extension implemented for example
// on Linux and Solaris
tpd.width = 2;
// fall through
case 'I': {
// %I: the hour (12-hour clock) as a decimal number (01-12). [tm_hour]
// %OI: the hour (12-hour clock), using the locale's alternative
// numeric symbols.
unsigned hour = tmb->tm_hour % 12;
if (!hour)
hour = 12;
if ('O' == mod)
tpd.altval = hour;
else
tpd.val = hour;
// set precision only for "%I" and not for "%l"
// (i.e., when width hasn't been overridden above)
if (1 == tpd.width)
tpd.prec = 2;
break;
}
case 'j':
// %j: the day of the year as a decimal number (001-366). [tm_yday]
tpd.val = tmb->tm_yday + 1;
tpd.prec = 3;
break;
case 'm':
// %m: the month as a decimal number (01-12). [tm_mon]
// %Om: the month, using the locale's alternative numeric symbols.
if ('O' == mod)
tpd.altval = tmb->tm_mon + 1;
else
tpd.val = tmb->tm_mon + 1;
tpd.prec = 2;
break;
case 'M':
// %M: the minute as a decimal number (00-59). [tm_min]
// %OM: the minutes, using the locale's alternative numeric symbols.
if ('O' == mod)
tpd.altval = tmb->tm_min;
else
tpd.val = tmb->tm_min;
tpd.prec = 2;
break;
case 'n': {
// %n: a new-line character.
static const void* const fmats [] = { "\n", L"\n" };
tpd.str = fmats [wide];
break;
}
case 'p':
// %p: the locale's equivalent of the AM/PM designations
// associated with a 12-hour clock. [tm_hour]
tpd.fmt = ptime->am_pm ((tmb->tm_hour / 12) % 2U, wide);
break;
case 'r':
// %r: the locale's 12-hour clock time. [tm_hour, tm_min, tm_sec]
tpd.fmt = ptime->t_fmt_ampm (wide);
break;
case 'R': {
// %R: equivalent to "%H:%M". [tm_hour, tm_min]
static const void* const fmats[] = { "%H:%M", L"%H:%M" };
tpd.fmt = fmats [wide];
break;
}
case 'S':
// %S: the second as a decimal number (00-60). [tm_sec]
// %OS: the seconds, using the locale's alternative numeric symbols.
if ('O' == mod)
tpd.altval = tmb->tm_sec;
else
tpd.val = tmb->tm_sec;
tpd.prec = 2;
break;
case 't': {
// %t: a horizontal-tab character.
static const void* const fmats[] = { "\t", L"\t" };
tpd.str = fmats [wide];
break;
}
case 'T': {
// %T: equivalent to "%H:%M:%S" (the ISO 8601 time format).
// [tm_hour, tm_min, tm_sec]
static const void* const fmats[] = { "%H:%M:%S", L"%H:%M:%S" };
tpd.fmt = fmats [wide];
break;
}
case 'u':
// %u: the ISO 8601 weekday as a decimal number (1-7),
// where Monday is 1. [tm_wday]
// %Ou: the ISO 8601 weekday as a number in the locale's alternative
// representation, where Monday is 1.
if ('O' == mod)
tpd.altval = 1 + (tmb->tm_wday ? tmb->tm_wday - 1 : 6);
else
tpd.val = 1 + (tmb->tm_wday ? tmb->tm_wday - 1 : 6);
break;
case 'U': {
// %U: the week number of the year (the first Sunday as the first
// day of week 1) as a decimal number (00-53).
// [tm_year, tm_wday, tm_yday]
// %OU: the week number, using the locale's alternative numeric
// symbols.
const int week = (tmb->tm_yday - tmb->tm_wday + 7) / 7;
if ('O' == mod)
tpd.altval = week;
else
tpd.val = week;
tpd.prec = 2;
break;
}
case 'V': {
// %V: the ISO 8601 week number (see below) as a decimal number
// (01-53). [tm_year, tm_wday, tm_yday]
// %OV: the ISO 8601 week number, using the locale's alternative
// numeric symbols.
int year = tmb->tm_year + 1900;
const int week =
__rw_iso_week (year, tmb->tm_yday, tmb->tm_wday) / 7 + 1;
if ('O' == mod)
tpd.altval = week;
else
tpd.val = week;
tpd.prec = 2;
break;
}
case 'w':
// %w: the weekday as a decimal number (0-6), where Sunday is 0.
// [tm_wday]
// %Ow: the weekday as a number, using the locale's alternative
// numeric symbols.
if ('O' == mod)
tpd.altval = tmb->tm_wday;
else
tpd.val = tmb->tm_wday;
break;
case 'W': {
// %W: the week number of the year (the first Monday as the first
// day of week 1) as a decimal number (00-53).
// [tm_year, tm_wday, tm_yday]
// %OW: the week number of the year, using the locale's alternative
// numeric symbols.
const int week = (tmb->tm_yday - (tmb->tm_wday - 1 + 7) % 7 + 7) / 7;
if ('O' == mod)
tpd.altval = week;
else
tpd.val = week;
tpd.prec = 2;
break;
}
case 'x':
// %x: the locale's appropriate date representation.
// %Ex: the locale's alternative date representation.
if ('E' == mod)
tpd.fmt = ptime->era_d_fmt (wide);
if ( !tpd.fmt
#ifndef _RWSTD_NO_WCHAR_T
|| (wide && !*_RWSTD_STATIC_CAST (const wchar_t*, tpd.fmt))
#endif // _RWSTD_NO_WCHAR_T
|| (!wide && !*_RWSTD_STATIC_CAST (const char*, tpd.fmt)))
tpd.fmt = ptime->d_fmt (wide);
break;
case 'X':
// %X: the locale's appropriate time representation.
// %EX: the locale's alternative time representation.
if ('E' == mod)
tpd.fmt = ptime->era_t_fmt (wide);
if ( !tpd.fmt
#ifndef _RWSTD_NO_WCHAR_T
|| (wide && !*_RWSTD_STATIC_CAST (const wchar_t*, tpd.fmt))
#endif // _RWSTD_NO_WCHAR_T
|| (!wide && !*_RWSTD_STATIC_CAST (const char*, tpd.fmt)))
tpd.fmt = ptime->t_fmt (wide);
break;
case 'y':
// %y: the last 2 digits of the year as a decimal number (00-99).
// [tm_year]
// %Ey: the offset from %EC (year only) in the locale's alternative
/// representation.
// %Oy: the last 2 digits of the year, using the locale's alternative
// numeric symbols.
if ('E' == mod) {
const __rw_time_t::era_t* const pera = __rw_get_era (ptime, tmb);
if (pera) {
tpd.altval = pera->offset < 0
? pera->year [0] - 1900 - tmb->tm_year - pera->offset
: 1900 + tmb->tm_year - pera->year [0] + pera->offset;
break;
}
}
if ('O' == mod)
tpd.altval = (1900 + tmb->tm_year) % 100;
else
tpd.val = (1900 + tmb->tm_year) % 100;
tpd.prec = 2;
break;
case 'Y':
// %Y: the year as a decimal number (e.g., 1997). [tm_year]
// %EY: the locale's full alternative year representation.
if (mod) {
const __rw_time_t::era_t* const pera = __rw_get_era (ptime, tmb);
if ( pera
&& !!(tpd.fmt = ptime->era_fmt (pera - ptime->era (0), wide)))
break;
}
tpd.val = 1900 + tmb->tm_year;
break;
case 'z':
// %z: the offset from UTC in the ISO 8601:2000 standard format
// (+hhmm or -hhmm), or by no characters if no timezone is
// determinable.
// For example, "-0430" means 4 hours 30 minutes behind UTC
// (west of Greenwich).
// If tm_isdst is zero, the standard time offset is used.
// If tm_isdst is greater than zero, the daylight savings
// time offset is used.
// If tm_isdst is negative, no characters are returned.
// [tm_isdst]
if (tmb->tm_isdst < 0) {
// force no output
tpd.val = _RWSTD_INT_MIN;
tpd.fmt = null_fmt.data;
break;
}
// fall through
case 'Z': {
// %Z: the locale's time zone name or abbreviation, or no
// characters if no time zone is determinable. [tm_isdst]
if ('z' == fmt) {
tpd.prec = 4;
#if defined (__GLIBC__) && defined (_RWSTD_NO_PURE_C_HEADERS)
// GNU glibc uses gmtoff and zone instead of timezone and
// tzname when computing/formatting time zone information
# if defined (__STRICT_ANSI__) && !defined (__USE_BSD)
tpd.val = tmb->__tm_gmtoff;
# else // if !defined (__STRICT_ANSI__) || defined (__USE_BSD)
tpd.val = tmb->tm_gmtoff;
# endif // __STRICT_ANSI__, __USE_BSD
if (tpd.val) {
// use the offset from GMT only if it's non-zero
// and ignore the tm_isdst flag (glibc behavior)
tpd.val /= 60;
tpd.val = 100 * (tpd.val / 60) + tpd.val % 60;
break;
}
// otherwise (0 GMT offset), use the TZ environment variable
// to determine the global offset and consider the tm_isdst
// flag (POSIX behavior)
#endif // __GLIBC__ && _RWSTD_NO_PURE_C_HEADERS
const char* const var = getenv ("TZ");
if (!var || !*var || __rw_get_zone (tpd, var, tmb->tm_isdst)) {
// force no output on error
tpd.val = _RWSTD_INT_MIN;
tpd.fmt = null_fmt.data;
break;
}
}
else {
const char* const var = getenv ("TZ");
if (!var || !*var) {
// force no output
tpd.val = _RWSTD_INT_MIN;
tpd.fmt = null_fmt.data;
break;
}
__rw_get_zone_name (tpd, var, tmb->tm_isdst);
}
break;
}
case '%': {
static const void* const fmats[] = { "%", L"%" };
tpd.str = fmats [wide];
break;
}
default: break;
}
}
_RWSTD_EXPORT char*
__rw_put_time (const __rw_facet *facet, char *buf, size_t bufsize,
_STD::ios_base &flags, char fill, const tm *tmb,
char fmt, char mod, int width, int prec)
{
_RWSTD_ASSERT (0 != facet);
size_t res = 0; // size of formatted output, -1 on error
// compute the values and get the format
__rw_time_put_data tpd;
__rw_get_time_put_data (tpd, facet, tmb, fmt, mod, false /* narrow */);
if (_RWSTD_INT_MIN != tpd.altval) {
// retrieve the alternative numeric symbols (if any)
const __rw_time_t *ptime =
_RWSTD_STATIC_CAST (const __rw_time_t*, facet->_C_data ());
_RWSTD_ASSERT (0 != ptime);
const _RWSTD_UINT32_T ndigits = ptime->alt_digits_count ();
const _RWSTD_UINT32_T altval =
_RWSTD_STATIC_CAST (_RWSTD_UINT32_T, tpd.altval);
if (altval < ndigits) {
// format using alternative numeric symbols
const char* digit =
_RWSTD_STATIC_CAST (const char*, ptime->alt_digits (altval, 0));
_RWSTD_ASSERT (digit);
char *pbuf = buf;
while (*digit)
*pbuf++ = *digit++;
return pbuf;
}
// if the value exceeds the range representable by the available
// alternative numeric symbols, format using ordinary digits
tpd.val = tpd.altval;
}
if (_RWSTD_INT_MIN != tpd.val) {
const char* const fmtstr = 'z' == fmt ? "%+*.*d" : "%*.*d";
res = size_t (sprintf (buf, fmtstr,
width < 0 ? tpd.width : width,
prec < 0 ? tpd.prec : prec, tpd.val));
}
else {
if (!tmb && tpd.fmt) {
tpd.str = tpd.fmt;
tpd.fmt = 0;
}
if (tpd.fmt) {
const char* const fmtstr =
_RWSTD_STATIC_CAST (const char*, tpd.fmt);
return __rw_fmt_time (facet, buf, bufsize, flags,
fill, tmb, fmtstr);
}
if (tpd.str) {
// copy data if available
const char *src = _RWSTD_STATIC_CAST (const char*, tpd.str);
for (char *dst = buf; *src && res < bufsize; ++src, ++dst, ++res)
*dst = *src;
_RWSTD_ASSERT (bufsize >= res);
}
else {
char fmtstr [4] = { '%', fmt, '\0', '\0' };
if (mod) {
fmtstr [1] = mod;
fmtstr [2] = fmt;
}
// use strftime() for locale-independent formatting
res = strftime (buf, bufsize, fmtstr, tmb);
_RWSTD_ASSERT (bufsize > res);
}
}
return buf + res;
}
#ifndef _RWSTD_NO_WCHAR_T
_RWSTD_EXPORT wchar_t*
__rw_put_time (const __rw_facet *facet, wchar_t *wbuf, size_t bufsize,
_STD::ios_base &flags, wchar_t fill, const tm *tmb,
char fmt, char mod, int width, int prec)
{
_RWSTD_ASSERT (0 != facet);
size_t res = 0; // size of formatted output, -1 on error
__rw_time_put_data tpd;
__rw_get_time_put_data (tpd, facet, tmb, fmt, mod, true /* wide */);
if (_RWSTD_INT_MIN != tpd.altval) {
const __rw_time_t *ptime =
_RWSTD_STATIC_CAST (const __rw_time_t*, facet->_C_data ());
_RWSTD_ASSERT (0 != ptime);
const _RWSTD_UINT32_T n = ptime->alt_digits_count ();
if (tpd.altval >= 0 && tpd.altval < int (n)) {
const size_t altval =
_RWSTD_STATIC_CAST (size_t, tpd.altval);
// format using alternative numeric symbols
const wchar_t* digit =
_RWSTD_STATIC_CAST (const wchar_t*, ptime->alt_digits (altval, 1));
_RWSTD_ASSERT (digit);
wchar_t *pbuf = wbuf;
while (*digit)
*pbuf++ = *digit++;
return pbuf;
}
// format using ordinary digits
tpd.val = tpd.altval;
}
if (_RWSTD_INT_MIN != tpd.val) {
#ifndef _RWSTD_NO_SWPRINTF
const wchar_t *fmtstr = 'z' == fmt ? L"%+*.*d" : L"%*.*d";
res = swprintf (wbuf,
#if !defined (__MINGW32__) && (!defined (_MSC_VER) || 1400 <= _MSC_VER)
// MSVC 8.0 changed swprintf() to conform
// to the C standard signature
bufsize,
#endif // not MSVC || MSVC >= 8.0
fmtstr,
width < 0 ? tpd.width : width,
prec < 0 ? tpd.prec : prec, tpd.val);
#else // if defined (_RWSTD_NO_SWPRINTF)
const char *fmtstr = 'z' == fmt ? "%+*.*d" : "%*.*d";
char buf [64];
res = size_t (sprintf (buf, fmtstr,
width < 0 ? tpd.width : width,
prec < 0 ? tpd.prec : prec, tpd.val));
_RWSTD_ASSERT (res < sizeof buf);
wchar_t *dst = wbuf;
for (const char *s = buf; *s; ++s, ++dst)
*dst = _RWSTD_STATIC_CAST (unsigned char, *s);
res = dst - wbuf;
#endif // _RWSTD_NO_SWPRINTF
}
else {
if (!tmb && tpd.fmt) {
tpd.str = tpd.fmt;
tpd.fmt = 0;
}
if (tpd.fmt) {
const wchar_t* const wfmt =
_RWSTD_STATIC_CAST (const wchar_t*, tpd.fmt);
return __rw_fmt_time (facet, wbuf, bufsize, flags, fill, tmb, wfmt);
}
if (tpd.str) {
// copy data if available
const wchar_t *src = _RWSTD_STATIC_CAST (const wchar_t*, tpd.str);
for (wchar_t *dst = wbuf; *src && res < bufsize; ++src, ++dst, ++res)
*dst = *src;
_RWSTD_ASSERT (bufsize >= res);
}
else {
#if !defined (_RWSTD_NO_WCSFTIME_WCHAR_T_FMAT) && !defined (_RWSTD_NO_WCSFTIME)
wchar_t fmtstr [4] = { L'%', fmt, L'\0', L'\0' };
#else // if _RWSTD_NO_WCSFTIME_WCHAR_T_FMAT || _RWSTD_NO_WCSFTIME
// work around incorrect wcsftime() declarations some
// platforms
char fmtstr [4] = { '%', fmt, '\0', '\0' };
#endif // !_RWSTD_NO_WCSFTIME_WCHAR_T_FMAT && !_RWSTD_NO_WCSFTIME
if (mod) {
fmtstr [1] = mod;
fmtstr [2] = fmt;
}
#ifndef _RWSTD_NO_WCSFTIME
// use wcsftime() for locale-independent formatting
res = wcsftime (wbuf, bufsize, fmtstr, tmb);
_RWSTD_ASSERT (res < bufsize);
#else // if defined (_RWSTD_NO_WCSFTIME)
char buf [256];
// use strftime() for locale-independent formatting
res = strftime (buf, sizeof buf, fmtstr, tmb);
if (res <= sizeof buf) {
// widen narrow (not multibyte) result into wide buffer
for (size_t i = 0; i != res; ++i)
wbuf [i] = _RWSTD_STATIC_CAST (unsigned char, buf [i]);
}
#endif // _RWSTD_NO_WCSFTIME
}
}
return wbuf + res;
}
#endif // _RWSTD_NO_WCHAR_T
} // namespace __rw