1/*
2 * Copyright (c) 1999
3 * Silicon Graphics Computer Systems, Inc.
4 *
5 * Copyright (c) 1999
6 * Boris Fomitchev
7 *
8 * This material is provided "as is", with absolutely no warranty expressed
9 * or implied. Any use is at your own risk.
10 *
11 * Permission to use or copy this software for any purpose is hereby granted
12 * without fee, provided the above notices are retained on all copies.
13 * Permission to modify the code and to distribute modified code is granted,
14 * provided the above notices are retained, and a notice that the code was
15 * modified is included with the above copyright notice.
16 *
17 */
18
19#include "stlport_prefix.h"
20
21#include <cstdio>
22#include <locale>
23#include <istream>
24
25#include "c_locale.h"
26#include "acquire_release.h"
27
28_STLP_BEGIN_NAMESPACE
29
30_STLP_MOVE_TO_PRIV_NAMESPACE
31
32// default "C" values for month and day names
33
34const char default_dayname[][14] = {
35  "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
36  "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
37  "Friday", "Saturday"};
38
39const char default_monthname[][24] = {
40  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
41  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
42  "January", "February", "March", "April", "May", "June",
43  "July", "August", "September", "October", "November", "December"};
44
45#ifndef _STLP_NO_WCHAR_T
46const wchar_t default_wdayname[][14] = {
47  L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat",
48  L"Sunday", L"Monday", L"Tuesday", L"Wednesday", L"Thursday",
49  L"Friday", L"Saturday"};
50
51const wchar_t default_wmonthname[][24] = {
52  L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun",
53  L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec",
54  L"January", L"February", L"March", L"April", L"May", L"June",
55  L"July", L"August", L"September", L"October", L"November", L"December"};
56#endif
57
58#if defined (__BORLANDC__)
59_Time_Info time_init<char>::_M_timeinfo;
60#  ifndef _STLP_NO_WCHAR_T
61_WTime_Info time_init<wchar_t>::_M_timeinfo;
62#  endif
63#endif
64
65// _Init_time_info: initialize table with
66// "C" values (note these are not defined in the C standard, so this
67// is somewhat arbitrary).
68
69static void _Init_timeinfo_base(_Time_Info_Base& table) {
70  table._M_time_format = "%H:%M:%S";
71  table._M_date_format = "%m/%d/%y";
72  table._M_date_time_format = "%m/%d/%y";
73}
74
75static void _Init_timeinfo(_Time_Info& table) {
76  int i;
77  for (i = 0; i < 14; ++i)
78    table._M_dayname[i] = default_dayname[i];
79  for (i = 0; i < 24; ++i)
80    table._M_monthname[i] = default_monthname[i];
81  table._M_am_pm[0] = "AM";
82  table._M_am_pm[1] = "PM";
83  _Init_timeinfo_base(table);
84}
85
86#ifndef _STLP_NO_WCHAR_T
87static void _Init_timeinfo(_WTime_Info& table) {
88  int i;
89  for (i = 0; i < 14; ++i)
90    table._M_dayname[i] = default_wdayname[i];
91  for (i = 0; i < 24; ++i)
92    table._M_monthname[i] = default_wmonthname[i];
93  table._M_am_pm[0] = L"AM";
94  table._M_am_pm[1] = L"PM";
95  _Init_timeinfo_base(table);
96}
97#endif
98
99static void _Init_timeinfo_base(_Time_Info_Base& table, _Locale_time * time) {
100  table._M_time_format = _Locale_t_fmt(time);
101  if ( table._M_time_format == "%T" ) {
102    table._M_time_format = "%H:%M:%S";
103  } else if ( table._M_time_format == "%r" ) {
104    table._M_time_format = "%I:%M:%S %p";
105  } else if ( table._M_time_format == "%R" ) {
106    table._M_time_format = "%H:%M";
107  }
108  table._M_date_format = _Locale_d_fmt(time);
109  table._M_date_time_format = _Locale_d_t_fmt(time);
110  table._M_long_date_format = _Locale_long_d_fmt(time);
111  table._M_long_date_time_format = _Locale_long_d_t_fmt(time);
112}
113
114static void _Init_timeinfo(_Time_Info& table, _Locale_time * time) {
115  int i;
116  for (i = 0; i < 7; ++i)
117    table._M_dayname[i] = _Locale_abbrev_dayofweek(time, i);
118  for (i = 0; i < 7; ++i)
119    table._M_dayname[i+7] = _Locale_full_dayofweek(time, i);
120  for (i = 0; i < 12; ++i)
121    table._M_monthname[i] = _Locale_abbrev_monthname(time, i);
122  for (i = 0; i < 12; ++i)
123    table._M_monthname[i+12] = _Locale_full_monthname(time, i);
124  table._M_am_pm[0] = _Locale_am_str(time);
125  table._M_am_pm[1] = _Locale_pm_str(time);
126  _Init_timeinfo_base(table, time);
127}
128
129#ifndef _STLP_NO_WCHAR_T
130static void _Init_timeinfo(_WTime_Info& table, _Locale_time * time) {
131  wchar_t buf[128];
132  int i;
133  for (i = 0; i < 7; ++i)
134    table._M_dayname[i] = _WLocale_abbrev_dayofweek(time, i, _STLP_ARRAY_AND_SIZE(buf));
135  for (i = 0; i < 7; ++i)
136    table._M_dayname[i+7] = _WLocale_full_dayofweek(time, i, _STLP_ARRAY_AND_SIZE(buf));
137  for (i = 0; i < 12; ++i)
138    table._M_monthname[i] = _WLocale_abbrev_monthname(time, i, _STLP_ARRAY_AND_SIZE(buf));
139  for (i = 0; i < 12; ++i)
140    table._M_monthname[i+12] = _WLocale_full_monthname(time, i, _STLP_ARRAY_AND_SIZE(buf));
141  table._M_am_pm[0] = _WLocale_am_str(time, _STLP_ARRAY_AND_SIZE(buf));
142  table._M_am_pm[1] = _WLocale_pm_str(time, _STLP_ARRAY_AND_SIZE(buf));
143  _Init_timeinfo_base(table, time);
144}
145#endif
146
147template <class _Ch, class _TimeInfo>
148void __subformat(_STLP_BASIC_IOSTRING(_Ch) &buf, const ctype<_Ch>& ct,
149                 const string& format, const _TimeInfo& table, const tm* t) {
150  const char * cp = format.data();
151  const char * cp_end = cp + format.size();
152  while (cp != cp_end) {
153    if (*cp == '%') {
154      char mod = 0;
155      ++cp;
156      if (*cp == '#') {
157        mod = *cp; ++cp;
158      }
159      __write_formatted_timeT(buf, ct, *cp++, mod, table, t);
160    } else
161      buf.append(1, *cp++);
162  }
163}
164
165static void __append(__iostring &buf, const string& name)
166{ buf.append(name.data(), name.data() + name.size()); }
167
168static void __append(__iowstring &buf, const wstring& name)
169{ buf.append(name.data(), name.data() + name.size()); }
170
171static void __append(__iostring &buf, char *first, char *last, const ctype<char>& /* ct */)
172{ buf.append(first, last); }
173
174static void __append(__iowstring &buf, char *first, char *last, const ctype<wchar_t>& ct) {
175  wchar_t _wbuf[64];
176  ct.widen(first, last, _wbuf);
177  buf.append(_wbuf, _wbuf + (last - first));
178}
179
180#if defined (__GNUC__)
181/* The number of days from the first day of the first ISO week of this
182   year to the year day YDAY with week day WDAY.  ISO weeks start on
183   Monday; the first ISO week has the year's first Thursday.  YDAY may
184   be as small as YDAY_MINIMUM.  */
185#  define __ISO_WEEK_START_WDAY 1 /* Monday */
186#  define __ISO_WEEK1_WDAY 4 /* Thursday */
187#  define __YDAY_MINIMUM (-366)
188#  define __TM_YEAR_BASE 1900
189static int
190__iso_week_days(int yday, int wday) {
191  /* Add enough to the first operand of % to make it nonnegative.  */
192  int big_enough_multiple_of_7 = (-__YDAY_MINIMUM / 7 + 2) * 7;
193  return (yday
194          - (yday - wday + __ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
195          + __ISO_WEEK1_WDAY - __ISO_WEEK_START_WDAY);
196}
197
198#  define __is_leap(year)\
199  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
200
201#endif
202
203#define __hour12(hour) \
204  (((hour) % 12 == 0) ? (12) : (hour) % 12)
205
206#if !defined (_STLP_USE_SAFE_STRING_FUNCTIONS)
207#  define _STLP_SPRINTF sprintf
208#else
209#  define _STLP_SPRINTF sprintf_s
210#endif
211
212template <class _Ch, class _TimeInfo>
213void _STLP_CALL __write_formatted_timeT(_STLP_BASIC_IOSTRING(_Ch) &buf,
214                                        const ctype<_Ch>& ct,
215                                        char format, char modifier,
216                                        const _TimeInfo& table, const tm* t) {
217  char _buf[64];
218  char *_bend;
219
220  switch (format) {
221    case 'a':
222      __append(buf, table._M_dayname[t->tm_wday]);
223      break;
224
225    case 'A':
226      __append(buf, table._M_dayname[t->tm_wday + 7]);
227      break;
228
229    case 'b':
230      __append(buf, table._M_monthname[t->tm_mon]);
231      break;
232
233    case 'B':
234      __append(buf, table._M_monthname[t->tm_mon + 12]);
235      break;
236
237    case 'c':
238      __subformat(buf, ct, (modifier != '#') ? table._M_date_time_format
239                                             : table._M_long_date_time_format, table, t);
240      break;
241
242    case 'd':
243      _STLP_SPRINTF(_buf, (modifier != '#') ? "%.2ld" : "%ld", (long)t->tm_mday);
244      __append(buf, _buf, ((long)t->tm_mday < 10L && modifier == '#') ? _buf + 1 : _buf + 2, ct);
245      break;
246
247    case 'e':
248      _STLP_SPRINTF(_buf, "%2ld", (long)t->tm_mday);
249      __append(buf, _buf, _buf + 2, ct);
250      break;
251
252    case 'H':
253      _STLP_SPRINTF(_buf, (modifier != '#') ? "%.2ld" : "%ld", (long)t->tm_hour);
254      __append(buf, _buf, ((long)t->tm_hour < 10L && modifier == '#') ? _buf + 1 : _buf + 2, ct);
255      break;
256
257    case 'I':
258      _STLP_SPRINTF(_buf, (modifier != '#') ? "%.2ld" : "%ld", (long)__hour12(t->tm_hour));
259      __append(buf, _buf, ((long)__hour12(t->tm_hour) < 10L && modifier == '#') ? _buf + 1 : _buf + 2, ct);
260      break;
261
262    case 'j':
263      _bend = __write_integer(_buf, 0, (long)((long)t->tm_yday + 1));
264      __append(buf, _buf, _bend, ct);
265      break;
266
267    case 'm':
268      _STLP_SPRINTF(_buf, (modifier != '#') ? "%.2ld" : "%ld", (long)t->tm_mon + 1);
269      __append(buf, _buf, ((long)(t->tm_mon + 1) < 10L && modifier == '#') ? _buf + 1 : _buf + 2, ct);
270      break;
271
272    case 'M':
273      _STLP_SPRINTF(_buf, (modifier != '#') ? "%.2ld" : "%ld", (long)t->tm_min);
274      __append(buf, _buf, ((long)t->tm_min < 10L && modifier == '#') ? _buf + 1 : _buf + 2, ct);
275      break;
276
277    case 'p':
278      __append(buf, table._M_am_pm[t->tm_hour / 12]);
279      break;
280
281    case 'S': // pad with zeros
282       _STLP_SPRINTF(_buf, (modifier != '#') ? "%.2ld" : "%ld", (long)t->tm_sec);
283       __append(buf, _buf, ((long)t->tm_sec < 10L && modifier == '#') ? _buf + 1 : _buf + 2, ct);
284       break;
285
286    case 'U':
287      _bend = __write_integer(_buf, 0, long((t->tm_yday - t->tm_wday + 7) / 7));
288      __append(buf, _buf, _bend, ct);
289      break;
290
291    case 'w':
292      _bend = __write_integer(_buf, 0, (long)t->tm_wday);
293      __append(buf, _buf, _bend, ct);
294      break;
295
296    case 'W':
297      _bend = __write_integer(_buf, 0,
298                             (long)(t->tm_wday == 0 ? (t->tm_yday + 1) / 7 :
299                                                      (t->tm_yday + 8 - t->tm_wday) / 7));
300      __append(buf, _buf, _bend, ct);
301      break;
302
303    case'x':
304      __subformat(buf, ct, (modifier != '#') ? table._M_date_format
305                                             : table._M_long_date_format, table, t);
306      break;
307
308    case 'X':
309      __subformat(buf, ct, table._M_time_format, table, t);
310      break;
311
312    case 'y':
313      _bend = __write_integer(_buf, 0, (long)((long)(t->tm_year + 1900) % 100));
314      __append(buf, _buf, _bend, ct);
315      break;
316
317    case 'Y':
318      _bend = __write_integer(_buf, 0, (long)((long)t->tm_year + 1900));
319      __append(buf, _buf, _bend, ct);
320      break;
321
322    case '%':
323      buf.append(1, ct.widen('%'));
324      break;
325
326#if defined (__GNUC__)
327      // fbp : at least on SUN
328#  if defined (_STLP_UNIX) && !defined (__linux__)
329#    define __USE_BSD 1
330#  endif
331
332   /*********************************************
333    *     JGS, handle various extensions        *
334    *********************************************/
335
336    case 'h': /* POSIX.2 extension */
337      // same as 'b', abbrev month name
338      __append(buf, table._M_monthname[t->tm_mon]);
339      break;
340    case 'C': /* POSIX.2 extension */
341      // same as 'd', the day
342      _STLP_SPRINTF(_buf, "%2ld", (long)t->tm_mday);
343      __append(buf, _buf, _buf + 2, ct);
344      break;
345
346    case 'D': /* POSIX.2 extension */
347      // same as 'x'
348      __subformat(buf, ct, table._M_date_format, table, t);
349      break;
350
351    case 'k': /* GNU extension */
352      _STLP_SPRINTF(_buf, "%2ld", (long)t->tm_hour);
353      __append(buf, _buf, _buf + 2, ct);
354      break;
355
356    case 'l': /* GNU extension */
357      _STLP_SPRINTF(_buf, "%2ld", (long)t->tm_hour % 12);
358      __append(buf, _buf, _buf + 2, ct);
359      break;
360
361    case 'n': /* POSIX.2 extension */
362      buf.append(1, ct.widen('\n'));
363      break;
364
365    case 'R': /* GNU extension */
366      __subformat(buf, ct, "%H:%M", table, t);
367      break;
368
369    case 'r': /* POSIX.2 extension */
370      __subformat(buf, ct, "%I:%M:%S %p", table, t);
371      break;
372
373    case 'T': /* POSIX.2 extension.  */
374      __subformat(buf, ct, "%H:%M:%S", table, t);
375      break;
376
377    case 't': /* POSIX.2 extension.  */
378      buf.append(1, ct.widen('\t'));
379
380    case 'u': /* POSIX.2 extension.  */
381      _bend = __write_integer(_buf, 0, long((t->tm_wday - 1 + 7)) % 7 + 1);
382      __append(buf, _buf, _bend, ct);
383      break;
384
385    case 's': {
386      time_t __t = mktime(__CONST_CAST(tm*, t));
387      _bend = __write_integer(_buf, 0, (long)__t );
388      __append(buf, _buf, _bend, ct);
389      break;
390    }
391    case 'g': /* GNU extension */
392    case 'G': {
393      int year = t->tm_year + __TM_YEAR_BASE;
394      int days = __iso_week_days (t->tm_yday, t->tm_wday);
395      if (days < 0) {
396        /* This ISO week belongs to the previous year.  */
397        year--;
398        days = __iso_week_days (t->tm_yday + (365 + __is_leap (year)), t->tm_wday);
399      }
400      else {
401        int d = __iso_week_days (t->tm_yday - (365 + __is_leap (year)), t->tm_wday);
402        if (0 <= d) {
403          /* This ISO week belongs to the next year.  */
404          ++year;
405          days = d;
406        }
407      }
408      long val;
409      switch (format) {
410      case 'g':
411        val = (long)(year % 100 + 100) % 100;
412        break;
413      case 'G':
414        val = (long)year;
415        break;
416      default:
417        val = (long)days / 7 + 1;
418        break;
419      }
420      _bend = __write_integer(_buf, 0, val);
421      __append(buf, _buf, _bend, ct);
422      break;
423    }
424
425#  if defined (_STLP_USE_GLIBC)
426    case 'z':   /* GNU extension.  */
427      if (t->tm_isdst < 0)
428        break;
429      {
430        int diff;
431#    if defined (__USE_BSD) || defined (__BEOS__)
432        diff = t->tm_gmtoff;
433#    else
434        diff = t->__tm_gmtoff;
435#    endif
436        if (diff < 0) {
437          buf.append(1, ct.widen('-'));
438          diff = -diff;
439        } else
440          buf.append(1, ct.widen('+'));
441        diff /= 60;
442        _STLP_SPRINTF(_buf, "%.4d", (diff / 60) * 100 + diff % 60);
443        __append(buf, _buf, _buf + 4, ct);
444        break;
445      }
446#  endif /* __GLIBC__ */
447#endif /* __GNUC__ */
448
449    default:
450      break;
451  }
452}
453
454void _STLP_CALL __write_formatted_time(__iostring &buf, const ctype<char>& ct,
455                                       char format, char modifier,
456                                       const _Time_Info& table, const tm* t)
457{ __write_formatted_timeT(buf, ct, format, modifier, table, t); }
458
459void _STLP_CALL __write_formatted_time(__iowstring &buf, const ctype<wchar_t>& ct,
460                                       char format, char modifier,
461                                       const _WTime_Info& table, const tm* t)
462{ __write_formatted_timeT(buf, ct, format, modifier, table, t); }
463
464static time_base::dateorder __get_date_order(_Locale_time* time) {
465  const char * fmt = _Locale_d_fmt(time);
466  char first, second, third;
467
468  while (*fmt != 0 && *fmt != '%') ++fmt;
469  if (*fmt == 0)
470    return time_base::no_order;
471  first = *++fmt;
472  while (*fmt != 0 && *fmt != '%') ++fmt;
473  if (*fmt == 0)
474    return time_base::no_order;
475  second = *++fmt;
476  while (*fmt != 0 && *fmt != '%') ++fmt;
477  if (*fmt == 0)
478    return time_base::no_order;
479  third = *++fmt;
480
481  switch (first) {
482    case 'd':
483      return (second == 'm' && third == 'y') ? time_base::dmy
484                                             : time_base::no_order;
485    case 'm':
486      return (second == 'd' && third == 'y') ? time_base::mdy
487                                             : time_base::no_order;
488    case 'y':
489      switch (second) {
490        case 'd':
491          return third == 'm' ? time_base::ydm : time_base::no_order;
492        case 'm':
493          return third == 'd' ? time_base::ymd : time_base::no_order;
494        default:
495          return time_base::no_order;
496      }
497    default:
498      return time_base::no_order;
499  }
500}
501
502time_init<char>::time_init()
503  : _M_dateorder(time_base::no_order)
504{ _Init_timeinfo(_M_timeinfo); }
505
506time_init<char>::time_init(const char* __name) {
507  if (!__name)
508    locale::_M_throw_on_null_name();
509
510  int __err_code;
511  char buf[_Locale_MAX_SIMPLE_NAME];
512  _Locale_time *__time = __acquire_time(__name, buf, 0, &__err_code);
513  if (!__time)
514    locale::_M_throw_on_creation_failure(__err_code, __name, "time");
515
516  _Init_timeinfo(this->_M_timeinfo, __time);
517  _M_dateorder = __get_date_order(__time);
518  __release_time(__time);
519}
520
521time_init<char>::time_init(_Locale_time *__time) {
522  _Init_timeinfo(this->_M_timeinfo, __time);
523  _M_dateorder = __get_date_order(__time);
524}
525
526#ifndef _STLP_NO_WCHAR_T
527time_init<wchar_t>::time_init()
528  : _M_dateorder(time_base::no_order)
529{ _Init_timeinfo(_M_timeinfo); }
530
531time_init<wchar_t>::time_init(const char* __name) {
532  if (!__name)
533    locale::_M_throw_on_null_name();
534
535  int __err_code;
536  char buf[_Locale_MAX_SIMPLE_NAME];
537  _Locale_time *__time = __acquire_time(__name, buf, 0, &__err_code);
538  if (!__time)
539    locale::_M_throw_on_creation_failure(__err_code, __name, "time");
540
541  _Init_timeinfo(this->_M_timeinfo, __time);
542  _M_dateorder = __get_date_order(__time);
543  __release_time(__time);
544}
545
546time_init<wchar_t>::time_init(_Locale_time *__time) {
547  _Init_timeinfo(this->_M_timeinfo, __time);
548  _M_dateorder = __get_date_order(__time);
549}
550#endif
551
552_STLP_MOVE_TO_STD_NAMESPACE
553
554#if !defined (_STLP_NO_FORCE_INSTANTIATE)
555template class time_get<char, istreambuf_iterator<char, char_traits<char> > >;
556template class time_put<char, ostreambuf_iterator<char, char_traits<char> > >;
557
558#  ifndef _STLP_NO_WCHAR_T
559template class time_get<wchar_t, istreambuf_iterator<wchar_t, char_traits<wchar_t> > >;
560template class time_put<wchar_t, ostreambuf_iterator<wchar_t, char_traits<wchar_t> > >;
561#  endif
562
563#endif
564
565_STLP_END_NAMESPACE
566