]> git.saurik.com Git - wxWidgets.git/blob - src/msw/wince/time.cpp
fix crash which happened if you called SetAttr(NULL) followed by SetAttr(attr) (...
[wxWidgets.git] / src / msw / wince / time.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/wince/time.cpp
3 // Purpose: Implements missing time functionality for WinCE
4 // Author: Marco Cavallini (MCK) - wx@koansoftware.com
5 // Modified by: Vadim Zeitlin for VC8 support
6 // Created: 31-08-2003
7 // RCS-ID: $Id$
8 // Copyright: (c) Marco Cavallini
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #ifndef WX_PRECOMP
28 #include "wx/msw/wrapwin.h"
29 #endif
30
31 #include "wx/msw/wince/time.h"
32
33 #if defined(__VISUALC__) && (__VISUALC__ >= 1400)
34
35 // VC8 does provide the time functions but not the standard ones
36 #include <altcecrt.h>
37
38 time_t __cdecl time(time_t *t)
39 {
40 __time64_t t64;
41 if ( !_time64(&t64) )
42 return (time_t)-1;
43
44 if ( t )
45 *t = (time_t)t64;
46
47 return (time_t)t64;
48 }
49
50 time_t __cdecl mktime(struct tm *t)
51 {
52 return (time_t)_mktime64(t);
53 }
54
55 #else // !VC8
56
57 /////////////////////////////////////////////////////////////////////////////////////////////
58 // //
59 // strftime() - taken from OpenBSD //
60 // //
61 /////////////////////////////////////////////////////////////////////////////////////////////
62
63 #define IN_NONE 0
64 #define IN_SOME 1
65 #define IN_THIS 2
66 #define IN_ALL 3
67 #define CHAR_BIT 8
68
69 #define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
70 #define TYPE_SIGNED(type) (((type) -1) < 0)
71
72 #define INT_STRLEN_MAXIMUM(type) \
73 ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type))
74
75 #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
76
77 #define MONSPERYEAR 12
78 #define DAYSPERWEEK 7
79 #define TM_YEAR_BASE 1900
80 #define HOURSPERDAY 24
81 #define DAYSPERNYEAR 365
82 #define DAYSPERLYEAR 366
83
84 static char wildabbr[] = "WILDABBR";
85
86 char * tzname[2] = {
87 wildabbr,
88 wildabbr
89 };
90
91
92 #define Locale (&C_time_locale)
93
94 struct lc_time_T {
95 const char * mon[MONSPERYEAR];
96 const char * month[MONSPERYEAR];
97 const char * wday[DAYSPERWEEK];
98 const char * weekday[DAYSPERWEEK];
99 const char * X_fmt;
100 const char * x_fmt;
101 const char * c_fmt;
102 const char * am;
103 const char * pm;
104 const char * date_fmt;
105 };
106
107 static const struct lc_time_T C_time_locale = {
108 {
109 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
110 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
111 }, {
112 "January", "February", "March", "April", "May", "June",
113 "July", "August", "September", "October", "November", "December"
114 }, {
115 "Sun", "Mon", "Tue", "Wed",
116 "Thu", "Fri", "Sat"
117 }, {
118 "Sunday", "Monday", "Tuesday", "Wednesday",
119 "Thursday", "Friday", "Saturday"
120 },
121
122 /* X_fmt */
123 "%H:%M:%S",
124
125 /*
126 ** x_fmt
127 ** C99 requires this format.
128 ** Using just numbers (as here) makes Quakers happier;
129 ** it's also compatible with SVR4.
130 */
131 "%m/%d/%y",
132
133 /*
134 ** c_fmt
135 ** C99 requires this format.
136 ** Previously this code used "%D %X", but we now conform to C99.
137 ** Note that
138 ** "%a %b %d %H:%M:%S %Y"
139 ** is used by Solaris 2.3.
140 */
141 "%a %b %e %T %Y",
142
143 /* am */
144 "AM",
145
146 /* pm */
147 "PM",
148
149 /* date_fmt */
150 "%a %b %e %H:%M:%S %Z %Y"
151 };
152
153
154 static char *
155 _add(const char * str, char * pt, const char * const ptlim)
156 {
157 while (pt < ptlim && (*pt = *str++) != '\0')
158 ++pt;
159 return pt;
160 }
161
162
163 static char *
164 _conv(const int n, const char * const format, char * const pt, const char * const ptlim)
165 {
166 char buf[INT_STRLEN_MAXIMUM(int) + 1];
167
168 (void) _snprintf(buf, sizeof buf, format, n);
169 return _add(buf, pt, ptlim);
170 }
171
172
173 static char *
174 _fmt(const char * format, const struct tm * const t, char * pt, const char * const ptlim, int * warnp)
175 {
176 for ( ; *format; ++format) {
177 if (*format == '%') {
178 label:
179 switch (*++format) {
180 case '\0':
181 --format;
182 break;
183 case 'A':
184 pt = _add((t->tm_wday < 0 ||
185 t->tm_wday >= DAYSPERWEEK) ?
186 "?" : Locale->weekday[t->tm_wday],
187 pt, ptlim);
188 continue;
189 case 'a':
190 pt = _add((t->tm_wday < 0 ||
191 t->tm_wday >= DAYSPERWEEK) ?
192 "?" : Locale->wday[t->tm_wday],
193 pt, ptlim);
194 continue;
195 case 'B':
196 pt = _add((t->tm_mon < 0 ||
197 t->tm_mon >= MONSPERYEAR) ?
198 "?" : Locale->month[t->tm_mon],
199 pt, ptlim);
200 continue;
201 case 'b':
202 case 'h':
203 pt = _add((t->tm_mon < 0 ||
204 t->tm_mon >= MONSPERYEAR) ?
205 "?" : Locale->mon[t->tm_mon],
206 pt, ptlim);
207 continue;
208 case 'C':
209 /*
210 ** %C used to do a...
211 ** _fmt("%a %b %e %X %Y", t);
212 ** ...whereas now POSIX 1003.2 calls for
213 ** something completely different.
214 ** (ado, 1993-05-24)
215 */
216 pt = _conv((t->tm_year + TM_YEAR_BASE) / 100,
217 "%02d", pt, ptlim);
218 continue;
219 case 'c':
220 {
221 int warn2 = IN_SOME;
222
223 pt = _fmt(Locale->c_fmt, t, pt, ptlim, warnp);
224 if (warn2 == IN_ALL)
225 warn2 = IN_THIS;
226 if (warn2 > *warnp)
227 *warnp = warn2;
228 }
229 continue;
230 case 'D':
231 pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
232 continue;
233 case 'd':
234 pt = _conv(t->tm_mday, "%02d", pt, ptlim);
235 continue;
236 case 'E':
237 case 'O':
238 /*
239 ** C99 locale modifiers.
240 ** The sequences
241 ** %Ec %EC %Ex %EX %Ey %EY
242 ** %Od %oe %OH %OI %Om %OM
243 ** %OS %Ou %OU %OV %Ow %OW %Oy
244 ** are supposed to provide alternate
245 ** representations.
246 */
247 goto label;
248 case 'e':
249 pt = _conv(t->tm_mday, "%2d", pt, ptlim);
250 continue;
251 case 'F':
252 pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
253 continue;
254 case 'H':
255 pt = _conv(t->tm_hour, "%02d", pt, ptlim);
256 continue;
257 case 'I':
258 pt = _conv((t->tm_hour % 12) ?
259 (t->tm_hour % 12) : 12,
260 "%02d", pt, ptlim);
261 continue;
262 case 'j':
263 pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
264 continue;
265 case 'k':
266 /*
267 ** This used to be...
268 ** _conv(t->tm_hour % 12 ?
269 ** t->tm_hour % 12 : 12, 2, ' ');
270 ** ...and has been changed to the below to
271 ** match SunOS 4.1.1 and Arnold Robbins'
272 ** strftime version 3.0. That is, "%k" and
273 ** "%l" have been swapped.
274 ** (ado, 1993-05-24)
275 */
276 pt = _conv(t->tm_hour, "%2d", pt, ptlim);
277 continue;
278 #ifdef KITCHEN_SINK
279 case 'K':
280 /*
281 ** After all this time, still unclaimed!
282 */
283 pt = _add("kitchen sink", pt, ptlim);
284 continue;
285 #endif /* defined KITCHEN_SINK */
286 case 'l':
287 /*
288 ** This used to be...
289 ** _conv(t->tm_hour, 2, ' ');
290 ** ...and has been changed to the below to
291 ** match SunOS 4.1.1 and Arnold Robbin's
292 ** strftime version 3.0. That is, "%k" and
293 ** "%l" have been swapped.
294 ** (ado, 1993-05-24)
295 */
296 pt = _conv((t->tm_hour % 12) ?
297 (t->tm_hour % 12) : 12,
298 "%2d", pt, ptlim);
299 continue;
300 case 'M':
301 pt = _conv(t->tm_min, "%02d", pt, ptlim);
302 continue;
303 case 'm':
304 pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
305 continue;
306 case 'n':
307 pt = _add("\n", pt, ptlim);
308 continue;
309 case 'p':
310 pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
311 Locale->pm :
312 Locale->am,
313 pt, ptlim);
314 continue;
315 case 'R':
316 pt = _fmt("%H:%M", t, pt, ptlim, warnp);
317 continue;
318 case 'r':
319 pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
320 continue;
321 case 'S':
322 pt = _conv(t->tm_sec, "%02d", pt, ptlim);
323 continue;
324 case 's':
325 {
326 struct tm tm;
327 char buf[INT_STRLEN_MAXIMUM(
328 time_t) + 1];
329 time_t mkt;
330
331 tm = *t;
332 mkt = mktime(&tm);
333 if (TYPE_SIGNED(time_t))
334 (void) _snprintf(buf, sizeof buf,
335 "%ld", (long) mkt);
336 else (void) _snprintf(buf, sizeof buf,
337 "%lu", (unsigned long) mkt);
338 pt = _add(buf, pt, ptlim);
339 }
340 continue;
341 case 'T':
342 pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
343 continue;
344 case 't':
345 pt = _add("\t", pt, ptlim);
346 continue;
347 case 'U':
348 pt = _conv((t->tm_yday + DAYSPERWEEK -
349 t->tm_wday) / DAYSPERWEEK,
350 "%02d", pt, ptlim);
351 continue;
352 case 'u':
353 /*
354 ** From Arnold Robbins' strftime version 3.0:
355 ** "ISO 8601: Weekday as a decimal number
356 ** [1 (Monday) - 7]"
357 ** (ado, 1993-05-24)
358 */
359 pt = _conv((t->tm_wday == 0) ?
360 DAYSPERWEEK : t->tm_wday,
361 "%d", pt, ptlim);
362 continue;
363 case 'V': /* ISO 8601 week number */
364 case 'G': /* ISO 8601 year (four digits) */
365 case 'g': /* ISO 8601 year (two digits) */
366 {
367 int year;
368 int yday;
369 int wday;
370 int w;
371
372 year = t->tm_year + TM_YEAR_BASE;
373 yday = t->tm_yday;
374 wday = t->tm_wday;
375 for ( ; ; ) {
376 int len;
377 int bot;
378 int top;
379
380 len = isleap(year) ?
381 DAYSPERLYEAR :
382 DAYSPERNYEAR;
383 /*
384 ** What yday (-3 ... 3) does
385 ** the ISO year begin on?
386 */
387 bot = ((yday + 11 - wday) %
388 DAYSPERWEEK) - 3;
389 /*
390 ** What yday does the NEXT
391 ** ISO year begin on?
392 */
393 top = bot -
394 (len % DAYSPERWEEK);
395 if (top < -3)
396 top += DAYSPERWEEK;
397 top += len;
398 if (yday >= top) {
399 ++year;
400 w = 1;
401 break;
402 }
403 if (yday >= bot) {
404 w = 1 + ((yday - bot) /
405 DAYSPERWEEK);
406 break;
407 }
408 --year;
409 yday += isleap(year) ?
410 DAYSPERLYEAR :
411 DAYSPERNYEAR;
412 }
413 if (*format == 'V')
414 pt = _conv(w, "%02d",
415 pt, ptlim);
416 else if (*format == 'g') {
417 *warnp = IN_ALL;
418 pt = _conv(year % 100, "%02d",
419 pt, ptlim);
420 } else pt = _conv(year, "%04d",
421 pt, ptlim);
422 }
423 continue;
424 case 'v':
425 pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
426 continue;
427 case 'W':
428 pt = _conv((t->tm_yday + DAYSPERWEEK -
429 (t->tm_wday ?
430 (t->tm_wday - 1) :
431 (DAYSPERWEEK - 1))) / DAYSPERWEEK,
432 "%02d", pt, ptlim);
433 continue;
434 case 'w':
435 pt = _conv(t->tm_wday, "%d", pt, ptlim);
436 continue;
437 case 'X':
438 pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
439 continue;
440 case 'x':
441 {
442 int warn2 = IN_SOME;
443
444 pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
445 if (warn2 == IN_ALL)
446 warn2 = IN_THIS;
447 if (warn2 > *warnp)
448 *warnp = warn2;
449 }
450 continue;
451 case 'y':
452 *warnp = IN_ALL;
453 pt = _conv((t->tm_year + TM_YEAR_BASE) % 100,
454 "%02d", pt, ptlim);
455 continue;
456 case 'Y':
457 pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d",
458 pt, ptlim);
459 continue;
460 case 'Z':
461 if (t->tm_isdst >= 0)
462 pt = _add(tzname[t->tm_isdst != 0],
463 pt, ptlim);
464 /*
465 ** C99 says that %Z must be replaced by the
466 ** empty string if the time zone is not
467 ** determinable.
468 */
469 continue;
470 case 'z':
471 {
472 int diff = -timezone;
473 char const * sign;
474
475 if (diff < 0) {
476 sign = "-";
477 diff = -diff;
478 } else sign = "+";
479 pt = _add(sign, pt, ptlim);
480 diff /= 60;
481 pt = _conv((diff/60)*100 + diff%60,
482 "%04d", pt, ptlim);
483 }
484 continue;
485 case '+':
486 pt = _fmt(Locale->date_fmt, t, pt, ptlim,
487 warnp);
488 continue;
489 case '%':
490 default:
491 break;
492 }
493 }
494 if (pt == ptlim)
495 break;
496 *pt++ = *format;
497 }
498 return pt;
499 }
500
501 size_t
502 strftime(char * const s, const size_t maxsize, const char *format, const struct tm * const t)
503 {
504 char * p;
505 int warn;
506
507 //tzset();
508
509 warn = IN_NONE;
510 p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn);
511
512 if (p == s + maxsize) {
513 if (maxsize > 0)
514 s[maxsize - 1] = '\0';
515 return 0;
516 }
517 *p = '\0';
518 return p - s;
519 }
520
521 extern "C"
522 {
523
524 /* Not needed in VS Studio 2005 */
525
526 size_t wcsftime(wchar_t *s,
527 const size_t maxsize,
528 const wchar_t *format,
529 const struct tm *t)
530 {
531 wxCharBuffer sBuf(maxsize/sizeof(wchar_t));
532
533 wxString formatStr(format);
534 wxCharBuffer bufFormatStr(formatStr.mb_str());
535
536 size_t sz = strftime(sBuf.data(), maxsize/sizeof(wchar_t), bufFormatStr, t);
537
538 wxMB2WC(s, sBuf, maxsize);
539
540 return sz;
541 }
542
543 } /* extern "C" */
544
545 #define is_leap(y) (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
546 #define SECONDS_IN_ONE_MINUTE 60
547 #define DAYS_IN_ONE_YEAR 365
548 #define SECONDS_IN_ONE_MIN 60
549 #define SECONDS_IN_ONE_HOUR 3600
550 #define SECONDS_IN_ONE_DAY 86400
551 #define DEFAULT_TIMEZONE 28800
552 #define DO_GMTIME 0
553 #define DO_LOCALTIME 1
554
555
556 long timezone ; // global variable
557
558
559 ////////////////////////////////////////////////////////////////////////
560 // Common code for localtime and gmtime (static)
561 ////////////////////////////////////////////////////////////////////////
562
563 static struct tm * __cdecl common_localtime(const time_t *t, BOOL bLocal)
564 {
565 wxLongLong i64;
566 FILETIME ft;
567 wxString str ;
568 SYSTEMTIME SystemTime;
569 TIME_ZONE_INFORMATION pTz;
570 static struct tm st_res ; // data holder
571 static struct tm * res = &st_res ; // data pointer
572 int iLeap;
573 const unsigned short int __mon_yday[2][13] =
574 {
575 // Normal years
576 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
577 // Leap years
578 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
579 };
580
581 if (!*t)
582 ::GetLocalTime(&SystemTime);
583 else
584 {
585 i64 = *t;
586 i64 = i64 * 10000000 + 116444736000000000;
587
588 ft.dwLowDateTime = i64.GetLo();
589 ft.dwHighDateTime = i64.GetHi();
590
591 ::FileTimeToSystemTime(&ft, &SystemTime);
592 }
593
594 ::GetTimeZoneInformation(&pTz);
595
596 ///////////////////////////////////////////////
597 // Set timezone
598 timezone = pTz.Bias * SECONDS_IN_ONE_MINUTE ;
599 ///////////////////////////////////////////////
600
601 iLeap = is_leap(SystemTime.wYear) ;
602
603 res->tm_hour = SystemTime.wHour;
604 res->tm_min = SystemTime.wMinute;
605 res->tm_sec = SystemTime.wSecond;
606
607 res->tm_mday = SystemTime.wDay;
608 res->tm_mon = SystemTime.wMonth - 1; // this the correct month but localtime returns month aligned to zero
609 res->tm_year = SystemTime.wYear; // this the correct year
610 res->tm_year = res->tm_year - 1900; // but localtime returns the value starting at the 1900
611
612 res->tm_wday = SystemTime.wDayOfWeek;
613 res->tm_yday = __mon_yday[iLeap][res->tm_mon] + SystemTime.wDay - 1; // localtime returns year-day aligned to zero
614
615 // if localtime behavior and daylight saving
616 if (bLocal && pTz.DaylightBias != 0)
617 res->tm_isdst = 1;
618 else
619 res->tm_isdst = 0; // without daylight saving or gmtime
620
621 return res;
622 }
623
624 extern "C"
625 {
626
627 ////////////////////////////////////////////////////////////////////////
628 // Receive the number of seconds elapsed since midnight(00:00:00)
629 // and convert a time value and corrects for the local time zone
630 ////////////////////////////////////////////////////////////////////////
631 struct tm * __cdecl localtime(const time_t * t)
632 {
633 return common_localtime(t, DO_LOCALTIME) ;
634 }
635
636 ////////////////////////////////////////////////////////////////////////
637 // Receives the number of seconds elapsed since midnight(00:00:00)
638 // and converts a time value WITHOUT correcting for the local time zone
639 ////////////////////////////////////////////////////////////////////////
640 struct tm * __cdecl gmtime(const time_t *t)
641 {
642 return common_localtime(t, DO_GMTIME) ;
643 }
644
645 }
646
647 ////////////////////////////////////////////////////////////////////////
648 // Common code for conversion of struct tm into time_t (static)
649 ////////////////////////////////////////////////////////////////////////
650 static time_t __cdecl common_tm_to_time(int day, int month, int year, int hour, int minute, int second)
651 {
652 #if 1
653 // Use mktime since it seems less broken
654 tm t;
655 t.tm_isdst = -1;
656 t.tm_hour = hour;
657 t.tm_mday = day;
658 t.tm_min = minute;
659 t.tm_mon = month-1;
660 t.tm_sec = second;
661 t.tm_wday = -1;
662 t.tm_yday = -1;
663 t.tm_year = year - 1900;
664 return mktime(& t);
665 #else
666 time_t prog = 0 ;
667 static int mdays[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 } ;
668
669 while (--month)
670 {
671 prog += mdays[month - 1] ;
672 if (month == 2 && is_leap(year))
673 prog++ ;
674 }
675
676 // Calculate seconds in elapsed days
677 prog = day - 1 ; // align first day of the year to zero
678 prog += (DAYS_IN_ONE_YEAR * (year - 1970) + (year - 1901) / 4 - 19) ;
679 prog *= SECONDS_IN_ONE_DAY ;
680
681 // Add Calculated elapsed seconds in the current day
682 prog += (hour * SECONDS_IN_ONE_HOUR + minute *
683 SECONDS_IN_ONE_MIN + second) ;
684
685 return prog ;
686 #endif
687 }
688
689 extern "C"
690 {
691
692 ////////////////////////////////////////////////////////////////////////
693 // Returns the number of seconds elapsed since
694 // midnight(00:00:00) of 1 January 1970
695 ////////////////////////////////////////////////////////////////////////
696 time_t __cdecl time(time_t *t)
697 {
698 time_t prog = 0 ;
699
700 if (t != NULL)
701 {
702 SYSTEMTIME SystemTime;
703
704 ::GetLocalTime(&SystemTime) ;
705 prog = common_tm_to_time(SystemTime.wDay, SystemTime.wMonth, SystemTime.wYear,
706 SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond) ;
707 *t = prog ;
708 }
709
710 return prog ;
711 }
712
713 ////////////////////////////////////////////////////////////////////////
714 // Converts the local time provided by struct tm
715 // into a time_t calendar value
716 // Implementation from OpenBSD
717 ////////////////////////////////////////////////////////////////////////
718
719 #if 1
720 int month_to_day[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
721
722 time_t mktime(struct tm *t)
723 {
724 short month, year;
725 time_t result;
726
727 month = t->tm_mon;
728 year = t->tm_year + month / 12 + 1900;
729 month %= 12;
730 if (month < 0)
731 {
732 year -= 1;
733 month += 12;
734 }
735 result = (year - 1970) * 365 + (year - 1969) / 4 + month_to_day[month];
736 result = (year - 1970) * 365 + month_to_day[month];
737 if (month <= 1)
738 year -= 1;
739 result += (year - 1968) / 4;
740 result -= (year - 1900) / 100;
741 result += (year - 1600) / 400;
742 result += t->tm_mday;
743 result -= 1;
744 result *= 24;
745 result += t->tm_hour;
746 result *= 60;
747 result += t->tm_min;
748 result *= 60;
749 result += t->tm_sec;
750 return(result);
751 }
752
753 #else
754 time_t __cdecl mktime(struct tm *t)
755 {
756 return (common_tm_to_time(t->tm_mday, t->tm_mon+1, t->tm_year+1900, t->tm_hour, t->tm_min, t->tm_sec)) ;
757 }
758 #endif
759
760 } // extern "C"
761
762 #endif // VC8/!VC8