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: time.cpp 43076 2006-11-04 23:27:15Z VZ $
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
38time_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
50time_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
84static char		wildabbr[] = "WILDABBR";
85
86char *			tzname[2] = {
87	wildabbr,
88	wildabbr
89};
90
91
92#define Locale	(&C_time_locale)
93
94struct 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
107static 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
154static 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
163static 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
173static 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 == '%') {
178label:
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
501size_t
502strftime(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
521extern "C"
522{
523
524/* Not needed in VS Studio 2005 */
525
526size_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
556long timezone ; // global variable
557
558
559////////////////////////////////////////////////////////////////////////
560// Common code for localtime and gmtime (static)
561////////////////////////////////////////////////////////////////////////
562
563static 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
624extern "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////////////////////////////////////////////////////////////////////////
631struct 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////////////////////////////////////////////////////////////////////////
640struct 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////////////////////////////////////////////////////////////////////////
650static 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
689extern "C"
690{
691
692////////////////////////////////////////////////////////////////////////
693// Returns the number of seconds elapsed since
694// midnight(00:00:00) of 1 January 1970
695////////////////////////////////////////////////////////////////////////
696time_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
720int month_to_day[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
721
722time_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
754time_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
763