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