]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/common/datetime.cpp
Hopefully fixed library names generated by wx-config for OS/2's PM port.
[wxWidgets.git] / src / common / datetime.cpp
... / ...
CommitLineData
1///////////////////////////////////////////////////////////////////////////////
2// Name: wx/datetime.h
3// Purpose: implementation of time/date related classes
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 11.05.99
7// RCS-ID: $Id$
8// Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9// parts of code taken from sndcal library by Scott E. Lee:
10//
11// Copyright 1993-1995, Scott E. Lee, all rights reserved.
12// Permission granted to use, copy, modify, distribute and sell
13// so long as the above copyright and this permission statement
14// are retained in all copies.
15//
16// Licence: wxWindows licence
17///////////////////////////////////////////////////////////////////////////////
18
19// TODO: for $DEITY sake, someone please fix the #ifdef __WXWINCE__ everywhere,
20// the proper way to do it is to implement (subset of) wxStrftime() for
21// CE instead of this horror!!
22
23/*
24 * Implementation notes:
25 *
26 * 1. the time is stored as a 64bit integer containing the signed number of
27 * milliseconds since Jan 1. 1970 (the Unix Epoch) - so it is always
28 * expressed in GMT.
29 *
30 * 2. the range is thus something about 580 million years, but due to current
31 * algorithms limitations, only dates from Nov 24, 4714BC are handled
32 *
33 * 3. standard ANSI C functions are used to do time calculations whenever
34 * possible, i.e. when the date is in the range Jan 1, 1970 to 2038
35 *
36 * 4. otherwise, the calculations are done by converting the date to/from JDN
37 * first (the range limitation mentioned above comes from here: the
38 * algorithm used by Scott E. Lee's code only works for positive JDNs, more
39 * or less)
40 *
41 * 5. the object constructed for the given DD-MM-YYYY HH:MM:SS corresponds to
42 * this moment in local time and may be converted to the object
43 * corresponding to the same date/time in another time zone by using
44 * ToTimezone()
45 *
46 * 6. the conversions to the current (or any other) timezone are done when the
47 * internal time representation is converted to the broken-down one in
48 * wxDateTime::Tm.
49 */
50
51// ============================================================================
52// declarations
53// ============================================================================
54
55// ----------------------------------------------------------------------------
56// headers
57// ----------------------------------------------------------------------------
58
59#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
60 #pragma implementation "datetime.h"
61#endif
62
63// For compilers that support precompilation, includes "wx.h".
64#include "wx/wxprec.h"
65
66#ifdef __BORLANDC__
67 #pragma hdrstop
68#endif
69
70#if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
71
72#ifndef WX_PRECOMP
73 #include "wx/string.h"
74 #include "wx/log.h"
75#endif // WX_PRECOMP
76
77#include "wx/intl.h"
78#include "wx/thread.h"
79#include "wx/tokenzr.h"
80#include "wx/module.h"
81
82#include <ctype.h>
83
84#ifdef __WINDOWS__
85 #include "wx/msw/wrapwin.h"
86 #include <winnls.h>
87 #ifndef __WXWINCE__
88 #include <locale.h>
89 #endif
90#endif
91
92#include "wx/datetime.h"
93#include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
94
95const long wxDateTime::TIME_T_FACTOR = 1000l;
96
97#if wxUSE_EXTENDED_RTTI
98
99template<> void wxStringReadValue(const wxString &s , wxDateTime &data )
100{
101 data.ParseFormat(s,wxT("%Y-%m-%d %H:%M:%S")) ;
102}
103
104template<> void wxStringWriteValue(wxString &s , const wxDateTime &data )
105{
106 s = data.Format(wxT("%Y-%m-%d %H:%M:%S")) ;
107}
108
109wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter<wxDateTime> , wxFromStringConverter<wxDateTime>)
110
111#endif
112
113//
114// ----------------------------------------------------------------------------
115// conditional compilation
116// ----------------------------------------------------------------------------
117
118#if defined(HAVE_STRPTIME) && defined(__GLIBC__) && \
119 ((__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0))
120 // glibc 2.0.7 strptime() is broken - the following snippet causes it to
121 // crash (instead of just failing):
122 //
123 // strncpy(buf, "Tue Dec 21 20:25:40 1999", 128);
124 // strptime(buf, "%x", &tm);
125 //
126 // so don't use it
127 #undef HAVE_STRPTIME
128#endif // broken strptime()
129
130#if defined(HAVE_STRPTIME) && defined(__DARWIN__) && defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
131 // configure detects strptime as linkable because it's in the OS X
132 // System library but MSL headers don't declare it.
133
134// char *strptime(const char *, const char *, struct tm *);
135 // However, we DON'T want to just provide it here because we would
136 // crash and/or overwrite data when strptime from OS X tries
137 // to fill in MW's struct tm which is two fields shorter (no TZ stuff)
138 // So for now let's just say we don't have strptime
139 #undef HAVE_STRPTIME
140#endif
141
142#if defined(__MWERKS__) && wxUSE_UNICODE
143 #include <wtime.h>
144#endif
145
146#if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
147 #if defined(__WXPALMOS__)
148 #define WX_GMTOFF_IN_TM
149 #elif defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
150 #define WX_TIMEZONE _timezone
151 #elif defined(__MWERKS__)
152 long wxmw_timezone = 28800;
153 #define WX_TIMEZONE wxmw_timezone
154 #elif defined(__DJGPP__) || defined(__WINE__)
155 #include <sys/timeb.h>
156 #include <values.h>
157 static long wxGetTimeZone()
158 {
159 static long timezone = MAXLONG; // invalid timezone
160 if (timezone == MAXLONG)
161 {
162 struct timeb tb;
163 ftime(&tb);
164 timezone = tb.timezone;
165 }
166 return timezone;
167 }
168 #define WX_TIMEZONE wxGetTimeZone()
169 #elif defined(__DARWIN__)
170 #define WX_GMTOFF_IN_TM
171 #else // unknown platform - try timezone
172 #define WX_TIMEZONE timezone
173 #endif
174#endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
175
176// ----------------------------------------------------------------------------
177// macros
178// ----------------------------------------------------------------------------
179
180// debugging helper: just a convenient replacement of wxCHECK()
181#define wxDATETIME_CHECK(expr, msg) \
182 if ( !(expr) ) \
183 { \
184 wxFAIL_MSG(msg); \
185 *this = wxInvalidDateTime; \
186 return *this; \
187 }
188
189// ----------------------------------------------------------------------------
190// private classes
191// ----------------------------------------------------------------------------
192
193class wxDateTimeHolidaysModule : public wxModule
194{
195public:
196 virtual bool OnInit()
197 {
198 wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays);
199
200 return true;
201 }
202
203 virtual void OnExit()
204 {
205 wxDateTimeHolidayAuthority::ClearAllAuthorities();
206 wxDateTimeHolidayAuthority::ms_authorities.clear();
207 }
208
209private:
210 DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule)
211};
212
213IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule, wxModule)
214
215// ----------------------------------------------------------------------------
216// constants
217// ----------------------------------------------------------------------------
218
219// some trivial ones
220static const int MONTHS_IN_YEAR = 12;
221
222static const int SEC_PER_MIN = 60;
223
224static const int MIN_PER_HOUR = 60;
225
226static const int HOURS_PER_DAY = 24;
227
228static const long SECONDS_PER_DAY = 86400l;
229
230static const int DAYS_PER_WEEK = 7;
231
232static const long MILLISECONDS_PER_DAY = 86400000l;
233
234// this is the integral part of JDN of the midnight of Jan 1, 1970
235// (i.e. JDN(Jan 1, 1970) = 2440587.5)
236static const long EPOCH_JDN = 2440587l;
237
238// the date of JDN -0.5 (as we don't work with fractional parts, this is the
239// reference date for us) is Nov 24, 4714BC
240static const int JDN_0_YEAR = -4713;
241static const int JDN_0_MONTH = wxDateTime::Nov;
242static const int JDN_0_DAY = 24;
243
244// the constants used for JDN calculations
245static const long JDN_OFFSET = 32046l;
246static const long DAYS_PER_5_MONTHS = 153l;
247static const long DAYS_PER_4_YEARS = 1461l;
248static const long DAYS_PER_400_YEARS = 146097l;
249
250// this array contains the cumulated number of days in all previous months for
251// normal and leap years
252static const wxDateTime::wxDateTime_t gs_cumulatedDays[2][MONTHS_IN_YEAR] =
253{
254 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
255 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
256};
257
258// ----------------------------------------------------------------------------
259// global data
260// ----------------------------------------------------------------------------
261
262const wxChar * wxDefaultDateTimeFormat = wxT("%c");
263const wxChar * wxDefaultTimeSpanFormat = wxT("%H:%M:%S");
264
265// in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
266// indicate an invalid wxDateTime object
267const wxDateTime wxDefaultDateTime;
268
269wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown;
270
271// ----------------------------------------------------------------------------
272// private functions
273// ----------------------------------------------------------------------------
274
275// debugger helper: shows what the date really is
276#ifdef __WXDEBUG__
277extern const wxChar *wxDumpDate(const wxDateTime* dt)
278{
279 static wxChar buf[128];
280
281 wxStrcpy(buf, dt->Format(_T("%Y-%m-%d (%a) %H:%M:%S")));
282
283 return buf;
284}
285#endif // Debug
286
287// get the number of days in the given month of the given year
288static inline
289wxDateTime::wxDateTime_t GetNumOfDaysInMonth(int year, wxDateTime::Month month)
290{
291 // the number of days in month in Julian/Gregorian calendar: the first line
292 // is for normal years, the second one is for the leap ones
293 static wxDateTime::wxDateTime_t daysInMonth[2][MONTHS_IN_YEAR] =
294 {
295 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
296 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
297 };
298
299 return daysInMonth[wxDateTime::IsLeapYear(year)][month];
300}
301
302// returns the time zone in the C sense, i.e. the difference UTC - local
303// (in seconds)
304static int GetTimeZone()
305{
306#ifdef WX_GMTOFF_IN_TM
307 // set to true when the timezone is set
308 static bool s_timezoneSet = false;
309 static long gmtoffset = LONG_MAX; // invalid timezone
310
311 // ensure that the timezone variable is set by calling localtime
312 if ( !s_timezoneSet )
313 {
314 // just call localtime() instead of figuring out whether this system
315 // supports tzset(), _tzset() or something else
316 time_t t = 0;
317 struct tm *tm;
318
319 tm = localtime(&t);
320 s_timezoneSet = true;
321
322 // note that GMT offset is the opposite of time zone and so to return
323 // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
324 // cases we have to negate it
325 gmtoffset = -tm->tm_gmtoff;
326 }
327
328 return (int)gmtoffset;
329#else // !WX_GMTOFF_IN_TM
330 return (int)WX_TIMEZONE;
331#endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
332}
333
334// return the integral part of the JDN for the midnight of the given date (to
335// get the real JDN you need to add 0.5, this is, in fact, JDN of the
336// noon of the previous day)
337static long GetTruncatedJDN(wxDateTime::wxDateTime_t day,
338 wxDateTime::Month mon,
339 int year)
340{
341 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
342
343 // check the date validity
344 wxASSERT_MSG(
345 (year > JDN_0_YEAR) ||
346 ((year == JDN_0_YEAR) && (mon > JDN_0_MONTH)) ||
347 ((year == JDN_0_YEAR) && (mon == JDN_0_MONTH) && (day >= JDN_0_DAY)),
348 _T("date out of range - can't convert to JDN")
349 );
350
351 // make the year positive to avoid problems with negative numbers division
352 year += 4800;
353
354 // months are counted from March here
355 int month;
356 if ( mon >= wxDateTime::Mar )
357 {
358 month = mon - 2;
359 }
360 else
361 {
362 month = mon + 10;
363 year--;
364 }
365
366 // now we can simply add all the contributions together
367 return ((year / 100) * DAYS_PER_400_YEARS) / 4
368 + ((year % 100) * DAYS_PER_4_YEARS) / 4
369 + (month * DAYS_PER_5_MONTHS + 2) / 5
370 + day
371 - JDN_OFFSET;
372}
373
374#ifndef __WXWINCE__
375// this function is a wrapper around strftime(3) adding error checking
376static wxString CallStrftime(const wxChar *format, const tm* tm)
377{
378 wxChar buf[4096];
379 // Create temp wxString here to work around mingw/cygwin bug 1046059
380 // http://sourceforge.net/tracker/?func=detail&atid=102435&aid=1046059&group_id=2435
381 wxString s;
382
383 if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) )
384 {
385 // buffer is too small?
386 wxFAIL_MSG(_T("strftime() failed"));
387 }
388
389 s = buf;
390 return s;
391}
392#endif
393
394#ifdef HAVE_STRPTIME
395
396// glibc2 doesn't define this in the headers unless _XOPEN_SOURCE is defined
397// which, unfortunately, wreaks havoc elsewhere
398#if defined(__GLIBC__) && (__GLIBC__ == 2)
399 extern "C" char *strptime(const char *, const char *, struct tm *);
400#endif
401
402// Unicode-friendly strptime() wrapper
403static const wxChar *
404CallStrptime(const wxChar *input, const char *fmt, tm *tm)
405{
406 // the problem here is that strptime() returns pointer into the string we
407 // passed to it while we're really interested in the pointer into the
408 // original, Unicode, string so we try to transform the pointer back
409#if wxUSE_UNICODE
410 wxCharBuffer inputMB(wxConvertWX2MB(input));
411#else // ASCII
412 const char * const inputMB = input;
413#endif // Unicode/Ascii
414
415 const char *result = strptime(inputMB, fmt, tm);
416 if ( !result )
417 return NULL;
418
419#if wxUSE_UNICODE
420 // FIXME: this is wrong in presence of surrogates &c
421 return input + (result - inputMB.data());
422#else // ASCII
423 return result;
424#endif // Unicode/Ascii
425}
426
427#endif // HAVE_STRPTIME
428
429// if year and/or month have invalid values, replace them with the current ones
430static void ReplaceDefaultYearMonthWithCurrent(int *year,
431 wxDateTime::Month *month)
432{
433 struct tm *tmNow = NULL;
434
435 if ( *year == wxDateTime::Inv_Year )
436 {
437 tmNow = wxDateTime::GetTmNow();
438
439 *year = 1900 + tmNow->tm_year;
440 }
441
442 if ( *month == wxDateTime::Inv_Month )
443 {
444 if ( !tmNow )
445 tmNow = wxDateTime::GetTmNow();
446
447 *month = (wxDateTime::Month)tmNow->tm_mon;
448 }
449}
450
451// fll the struct tm with default values
452static void InitTm(struct tm& tm)
453{
454 // struct tm may have etxra fields (undocumented and with unportable
455 // names) which, nevertheless, must be set to 0
456 memset(&tm, 0, sizeof(struct tm));
457
458 tm.tm_mday = 1; // mday 0 is invalid
459 tm.tm_year = 76; // any valid year
460 tm.tm_isdst = -1; // auto determine
461}
462
463// parsing helpers
464// ---------------
465
466// return the month if the string is a month name or Inv_Month otherwise
467static wxDateTime::Month GetMonthFromName(const wxString& name, int flags)
468{
469 wxDateTime::Month mon;
470 for ( mon = wxDateTime::Jan; mon < wxDateTime::Inv_Month; wxNextMonth(mon) )
471 {
472 // case-insensitive comparison either one of or with both abbreviated
473 // and not versions
474 if ( flags & wxDateTime::Name_Full )
475 {
476 if ( name.CmpNoCase(wxDateTime::
477 GetMonthName(mon, wxDateTime::Name_Full)) == 0 )
478 {
479 break;
480 }
481 }
482
483 if ( flags & wxDateTime::Name_Abbr )
484 {
485 if ( name.CmpNoCase(wxDateTime::
486 GetMonthName(mon, wxDateTime::Name_Abbr)) == 0 )
487 {
488 break;
489 }
490 }
491 }
492
493 return mon;
494}
495
496// return the weekday if the string is a weekday name or Inv_WeekDay otherwise
497static wxDateTime::WeekDay GetWeekDayFromName(const wxString& name, int flags)
498{
499 wxDateTime::WeekDay wd;
500 for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
501 {
502 // case-insensitive comparison either one of or with both abbreviated
503 // and not versions
504 if ( flags & wxDateTime::Name_Full )
505 {
506 if ( name.CmpNoCase(wxDateTime::
507 GetWeekDayName(wd, wxDateTime::Name_Full)) == 0 )
508 {
509 break;
510 }
511 }
512
513 if ( flags & wxDateTime::Name_Abbr )
514 {
515 if ( name.CmpNoCase(wxDateTime::
516 GetWeekDayName(wd, wxDateTime::Name_Abbr)) == 0 )
517 {
518 break;
519 }
520 }
521 }
522
523 return wd;
524}
525
526// scans all digits (but no more than len) and returns the resulting number
527static bool GetNumericToken(size_t len, const wxChar*& p, unsigned long *number)
528{
529 size_t n = 1;
530 wxString s;
531 while ( wxIsdigit(*p) )
532 {
533 s += *p++;
534
535 if ( len && ++n > len )
536 break;
537 }
538
539 return !s.empty() && s.ToULong(number);
540}
541
542// scans all alphabetic characters and returns the resulting string
543static wxString GetAlphaToken(const wxChar*& p)
544{
545 wxString s;
546 while ( wxIsalpha(*p) )
547 {
548 s += *p++;
549 }
550
551 return s;
552}
553
554// ============================================================================
555// implementation of wxDateTime
556// ============================================================================
557
558// ----------------------------------------------------------------------------
559// struct Tm
560// ----------------------------------------------------------------------------
561
562wxDateTime::Tm::Tm()
563{
564 year = (wxDateTime_t)wxDateTime::Inv_Year;
565 mon = wxDateTime::Inv_Month;
566 mday = 0;
567 hour = min = sec = msec = 0;
568 wday = wxDateTime::Inv_WeekDay;
569}
570
571wxDateTime::Tm::Tm(const struct tm& tm, const TimeZone& tz)
572 : m_tz(tz)
573{
574 msec = 0;
575 sec = (wxDateTime::wxDateTime_t)tm.tm_sec;
576 min = (wxDateTime::wxDateTime_t)tm.tm_min;
577 hour = (wxDateTime::wxDateTime_t)tm.tm_hour;
578 mday = (wxDateTime::wxDateTime_t)tm.tm_mday;
579 mon = (wxDateTime::Month)tm.tm_mon;
580 year = 1900 + tm.tm_year;
581 wday = (wxDateTime::wxDateTime_t)tm.tm_wday;
582 yday = (wxDateTime::wxDateTime_t)tm.tm_yday;
583}
584
585bool wxDateTime::Tm::IsValid() const
586{
587 // we allow for the leap seconds, although we don't use them (yet)
588 return (year != wxDateTime::Inv_Year) && (mon != wxDateTime::Inv_Month) &&
589 (mday <= GetNumOfDaysInMonth(year, mon)) &&
590 (hour < 24) && (min < 60) && (sec < 62) && (msec < 1000);
591}
592
593void wxDateTime::Tm::ComputeWeekDay()
594{
595 // compute the week day from day/month/year: we use the dumbest algorithm
596 // possible: just compute our JDN and then use the (simple to derive)
597 // formula: weekday = (JDN + 1.5) % 7
598 wday = (wxDateTime::wxDateTime_t)((wxDateTime::WeekDay)(GetTruncatedJDN(mday, mon, year) + 2) % 7);
599}
600
601void wxDateTime::Tm::AddMonths(int monDiff)
602{
603 // normalize the months field
604 while ( monDiff < -mon )
605 {
606 year--;
607
608 monDiff += MONTHS_IN_YEAR;
609 }
610
611 while ( monDiff + mon >= MONTHS_IN_YEAR )
612 {
613 year++;
614
615 monDiff -= MONTHS_IN_YEAR;
616 }
617
618 mon = (wxDateTime::Month)(mon + monDiff);
619
620 wxASSERT_MSG( mon >= 0 && mon < MONTHS_IN_YEAR, _T("logic error") );
621
622 // NB: we don't check here that the resulting date is valid, this function
623 // is private and the caller must check it if needed
624}
625
626void wxDateTime::Tm::AddDays(int dayDiff)
627{
628 // normalize the days field
629 while ( dayDiff + mday < 1 )
630 {
631 AddMonths(-1);
632
633 dayDiff += GetNumOfDaysInMonth(year, mon);
634 }
635
636 mday = (wxDateTime::wxDateTime_t)( mday + dayDiff );
637 while ( mday > GetNumOfDaysInMonth(year, mon) )
638 {
639 mday -= GetNumOfDaysInMonth(year, mon);
640
641 AddMonths(1);
642 }
643
644 wxASSERT_MSG( mday > 0 && mday <= GetNumOfDaysInMonth(year, mon),
645 _T("logic error") );
646}
647
648// ----------------------------------------------------------------------------
649// class TimeZone
650// ----------------------------------------------------------------------------
651
652wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz)
653{
654 switch ( tz )
655 {
656 case wxDateTime::Local:
657 // get the offset from C RTL: it returns the difference GMT-local
658 // while we want to have the offset _from_ GMT, hence the '-'
659 m_offset = -GetTimeZone();
660 break;
661
662 case wxDateTime::GMT_12:
663 case wxDateTime::GMT_11:
664 case wxDateTime::GMT_10:
665 case wxDateTime::GMT_9:
666 case wxDateTime::GMT_8:
667 case wxDateTime::GMT_7:
668 case wxDateTime::GMT_6:
669 case wxDateTime::GMT_5:
670 case wxDateTime::GMT_4:
671 case wxDateTime::GMT_3:
672 case wxDateTime::GMT_2:
673 case wxDateTime::GMT_1:
674 m_offset = -3600*(wxDateTime::GMT0 - tz);
675 break;
676
677 case wxDateTime::GMT0:
678 case wxDateTime::GMT1:
679 case wxDateTime::GMT2:
680 case wxDateTime::GMT3:
681 case wxDateTime::GMT4:
682 case wxDateTime::GMT5:
683 case wxDateTime::GMT6:
684 case wxDateTime::GMT7:
685 case wxDateTime::GMT8:
686 case wxDateTime::GMT9:
687 case wxDateTime::GMT10:
688 case wxDateTime::GMT11:
689 case wxDateTime::GMT12:
690 m_offset = 3600*(tz - wxDateTime::GMT0);
691 break;
692
693 case wxDateTime::A_CST:
694 // Central Standard Time in use in Australia = UTC + 9.5
695 m_offset = 60l*(9*60 + 30);
696 break;
697
698 default:
699 wxFAIL_MSG( _T("unknown time zone") );
700 }
701}
702
703// ----------------------------------------------------------------------------
704// static functions
705// ----------------------------------------------------------------------------
706
707/* static */
708bool wxDateTime::IsLeapYear(int year, wxDateTime::Calendar cal)
709{
710 if ( year == Inv_Year )
711 year = GetCurrentYear();
712
713 if ( cal == Gregorian )
714 {
715 // in Gregorian calendar leap years are those divisible by 4 except
716 // those divisible by 100 unless they're also divisible by 400
717 // (in some countries, like Russia and Greece, additional corrections
718 // exist, but they won't manifest themselves until 2700)
719 return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
720 }
721 else if ( cal == Julian )
722 {
723 // in Julian calendar the rule is simpler
724 return year % 4 == 0;
725 }
726 else
727 {
728 wxFAIL_MSG(_T("unknown calendar"));
729
730 return false;
731 }
732}
733
734/* static */
735int wxDateTime::GetCentury(int year)
736{
737 return year > 0 ? year / 100 : year / 100 - 1;
738}
739
740/* static */
741int wxDateTime::ConvertYearToBC(int year)
742{
743 // year 0 is BC 1
744 return year > 0 ? year : year - 1;
745}
746
747/* static */
748int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal)
749{
750 switch ( cal )
751 {
752 case Gregorian:
753 return Now().GetYear();
754
755 case Julian:
756 wxFAIL_MSG(_T("TODO"));
757 break;
758
759 default:
760 wxFAIL_MSG(_T("unsupported calendar"));
761 break;
762 }
763
764 return Inv_Year;
765}
766
767/* static */
768wxDateTime::Month wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal)
769{
770 switch ( cal )
771 {
772 case Gregorian:
773 return Now().GetMonth();
774
775 case Julian:
776 wxFAIL_MSG(_T("TODO"));
777 break;
778
779 default:
780 wxFAIL_MSG(_T("unsupported calendar"));
781 break;
782 }
783
784 return Inv_Month;
785}
786
787/* static */
788wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(int year, Calendar cal)
789{
790 if ( year == Inv_Year )
791 {
792 // take the current year if none given
793 year = GetCurrentYear();
794 }
795
796 switch ( cal )
797 {
798 case Gregorian:
799 case Julian:
800 return IsLeapYear(year) ? 366 : 365;
801
802 default:
803 wxFAIL_MSG(_T("unsupported calendar"));
804 break;
805 }
806
807 return 0;
808}
809
810/* static */
811wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(wxDateTime::Month month,
812 int year,
813 wxDateTime::Calendar cal)
814{
815 wxCHECK_MSG( month < MONTHS_IN_YEAR, 0, _T("invalid month") );
816
817 if ( cal == Gregorian || cal == Julian )
818 {
819 if ( year == Inv_Year )
820 {
821 // take the current year if none given
822 year = GetCurrentYear();
823 }
824
825 return GetNumOfDaysInMonth(year, month);
826 }
827 else
828 {
829 wxFAIL_MSG(_T("unsupported calendar"));
830
831 return 0;
832 }
833}
834
835/* static */
836wxString wxDateTime::GetMonthName(wxDateTime::Month month,
837 wxDateTime::NameFlags flags)
838{
839 wxCHECK_MSG( month != Inv_Month, wxEmptyString, _T("invalid month") );
840#ifndef __WXWINCE__
841 // notice that we must set all the fields to avoid confusing libc (GNU one
842 // gets confused to a crash if we don't do this)
843 tm tm;
844 InitTm(tm);
845 tm.tm_mon = month;
846
847 return CallStrftime(flags == Name_Abbr ? _T("%b") : _T("%B"), &tm);
848#else
849 wxString ret;
850 switch(month)
851 {
852 case Jan:
853 ret = (flags == Name_Abbr ? wxT("Jan"): wxT("January"));
854 break;
855 case Feb:
856 ret = (flags == Name_Abbr ? wxT("Feb"): wxT("Febuary"));
857 break;
858 case Mar:
859 ret = (flags == Name_Abbr ? wxT("Mar"): wxT("March"));
860 break;
861 case Apr:
862 ret = (flags == Name_Abbr ? wxT("Apr"): wxT("April"));
863 break;
864 case May:
865 ret = (flags == Name_Abbr ? wxT("May"): wxT("May"));
866 break;
867 case Jun:
868 ret = (flags == Name_Abbr ? wxT("Jun"): wxT("June"));
869 break;
870 case Jul:
871 ret = (flags == Name_Abbr ? wxT("Jul"): wxT("July"));
872 break;
873 case Aug:
874 ret = (flags == Name_Abbr ? wxT("Aug"): wxT("August"));
875 break;
876 case Sep:
877 ret = (flags == Name_Abbr ? wxT("Sep"): wxT("September"));
878 break;
879 case Oct:
880 ret = (flags == Name_Abbr ? wxT("Oct"): wxT("October"));
881 break;
882 case Nov:
883 ret = (flags == Name_Abbr ? wxT("Nov"): wxT("November"));
884 break;
885 case Dec:
886 ret = (flags == Name_Abbr ? wxT("Dec"): wxT("December"));
887 break;
888 }
889 return ret;
890#endif
891}
892
893/* static */
894wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday,
895 wxDateTime::NameFlags flags)
896{
897 wxCHECK_MSG( wday != Inv_WeekDay, wxEmptyString, _T("invalid weekday") );
898#ifndef __WXWINCE__
899 // take some arbitrary Sunday (but notice that the day should be such that
900 // after adding wday to it below we still have a valid date, e.g. don't
901 // take 28 here!)
902 tm tm;
903 InitTm(tm);
904 tm.tm_mday = 21;
905 tm.tm_mon = Nov;
906 tm.tm_year = 99;
907
908 // and offset it by the number of days needed to get the correct wday
909 tm.tm_mday += wday;
910
911 // call mktime() to normalize it...
912 (void)mktime(&tm);
913
914 // ... and call strftime()
915 return CallStrftime(flags == Name_Abbr ? _T("%a") : _T("%A"), &tm);
916#else
917 wxString ret;
918 switch(wday)
919 {
920 case Sun:
921 ret = (flags == Name_Abbr ? wxT("Sun") : wxT("Sunday"));
922 break;
923 case Mon:
924 ret = (flags == Name_Abbr ? wxT("Mon") : wxT("Monday"));
925 break;
926 case Tue:
927 ret = (flags == Name_Abbr ? wxT("Tue") : wxT("Tuesday"));
928 break;
929 case Wed:
930 ret = (flags == Name_Abbr ? wxT("Wed") : wxT("Wednesday"));
931 break;
932 case Thu:
933 ret = (flags == Name_Abbr ? wxT("Thu") : wxT("Thursday"));
934 break;
935 case Fri:
936 ret = (flags == Name_Abbr ? wxT("Fri") : wxT("Friday"));
937 break;
938 case Sat:
939 ret = (flags == Name_Abbr ? wxT("Sat") : wxT("Saturday"));
940 break;
941 }
942 return ret;
943
944#endif
945}
946
947/* static */
948void wxDateTime::GetAmPmStrings(wxString *am, wxString *pm)
949{
950 tm tm;
951 InitTm(tm);
952 wxChar buffer[64];
953 // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code
954 // and causes an assertion failed if the buffer is to small (which is good) - OR -
955 // if strftime does not return anything because the format string is invalid - OR -
956 // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good).
957 // wxDateTime::ParseTime will try several different formats to parse the time.
958 // As a result, GetAmPmStrings might get called, even if the current locale
959 // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would
960 // assert, even though it is a perfectly legal use.
961 if ( am )
962 {
963 if (wxStrftime(buffer, sizeof(buffer)/sizeof(wxChar), _T("%p"), &tm) > 0)
964 *am = wxString(buffer);
965 else
966 *am = wxString();
967 }
968 if ( pm )
969 {
970 tm.tm_hour = 13;
971 if (wxStrftime(buffer, sizeof(buffer)/sizeof(wxChar), _T("%p"), &tm) > 0)
972 *pm = wxString(buffer);
973 else
974 *pm = wxString();
975 }
976}
977
978// ----------------------------------------------------------------------------
979// Country stuff: date calculations depend on the country (DST, work days,
980// ...), so we need to know which rules to follow.
981// ----------------------------------------------------------------------------
982
983/* static */
984wxDateTime::Country wxDateTime::GetCountry()
985{
986 // TODO use LOCALE_ICOUNTRY setting under Win32
987#ifndef __WXWINCE__
988 if ( ms_country == Country_Unknown )
989 {
990 // try to guess from the time zone name
991 time_t t = time(NULL);
992 struct tm *tm = localtime(&t);
993
994 wxString tz = CallStrftime(_T("%Z"), tm);
995 if ( tz == _T("WET") || tz == _T("WEST") )
996 {
997 ms_country = UK;
998 }
999 else if ( tz == _T("CET") || tz == _T("CEST") )
1000 {
1001 ms_country = Country_EEC;
1002 }
1003 else if ( tz == _T("MSK") || tz == _T("MSD") )
1004 {
1005 ms_country = Russia;
1006 }
1007 else if ( tz == _T("AST") || tz == _T("ADT") ||
1008 tz == _T("EST") || tz == _T("EDT") ||
1009 tz == _T("CST") || tz == _T("CDT") ||
1010 tz == _T("MST") || tz == _T("MDT") ||
1011 tz == _T("PST") || tz == _T("PDT") )
1012 {
1013 ms_country = USA;
1014 }
1015 else
1016 {
1017 // well, choose a default one
1018 ms_country = USA;
1019 }
1020 }
1021#else
1022 ms_country = USA;
1023#endif
1024
1025 return ms_country;
1026}
1027
1028/* static */
1029void wxDateTime::SetCountry(wxDateTime::Country country)
1030{
1031 ms_country = country;
1032}
1033
1034/* static */
1035bool wxDateTime::IsWestEuropeanCountry(Country country)
1036{
1037 if ( country == Country_Default )
1038 {
1039 country = GetCountry();
1040 }
1041
1042 return (Country_WesternEurope_Start <= country) &&
1043 (country <= Country_WesternEurope_End);
1044}
1045
1046// ----------------------------------------------------------------------------
1047// DST calculations: we use 3 different rules for the West European countries,
1048// USA and for the rest of the world. This is undoubtedly false for many
1049// countries, but I lack the necessary info (and the time to gather it),
1050// please add the other rules here!
1051// ----------------------------------------------------------------------------
1052
1053/* static */
1054bool wxDateTime::IsDSTApplicable(int year, Country country)
1055{
1056 if ( year == Inv_Year )
1057 {
1058 // take the current year if none given
1059 year = GetCurrentYear();
1060 }
1061
1062 if ( country == Country_Default )
1063 {
1064 country = GetCountry();
1065 }
1066
1067 switch ( country )
1068 {
1069 case USA:
1070 case UK:
1071 // DST was first observed in the US and UK during WWI, reused
1072 // during WWII and used again since 1966
1073 return year >= 1966 ||
1074 (year >= 1942 && year <= 1945) ||
1075 (year == 1918 || year == 1919);
1076
1077 default:
1078 // assume that it started after WWII
1079 return year > 1950;
1080 }
1081}
1082
1083/* static */
1084wxDateTime wxDateTime::GetBeginDST(int year, Country country)
1085{
1086 if ( year == Inv_Year )
1087 {
1088 // take the current year if none given
1089 year = GetCurrentYear();
1090 }
1091
1092 if ( country == Country_Default )
1093 {
1094 country = GetCountry();
1095 }
1096
1097 if ( !IsDSTApplicable(year, country) )
1098 {
1099 return wxInvalidDateTime;
1100 }
1101
1102 wxDateTime dt;
1103
1104 if ( IsWestEuropeanCountry(country) || (country == Russia) )
1105 {
1106 // DST begins at 1 a.m. GMT on the last Sunday of March
1107 if ( !dt.SetToLastWeekDay(Sun, Mar, year) )
1108 {
1109 // weird...
1110 wxFAIL_MSG( _T("no last Sunday in March?") );
1111 }
1112
1113 dt += wxTimeSpan::Hours(1);
1114
1115 // disable DST tests because it could result in an infinite recursion!
1116 dt.MakeGMT(true);
1117 }
1118 else switch ( country )
1119 {
1120 case USA:
1121 switch ( year )
1122 {
1123 case 1918:
1124 case 1919:
1125 // don't know for sure - assume it was in effect all year
1126
1127 case 1943:
1128 case 1944:
1129 case 1945:
1130 dt.Set(1, Jan, year);
1131 break;
1132
1133 case 1942:
1134 // DST was installed Feb 2, 1942 by the Congress
1135 dt.Set(2, Feb, year);
1136 break;
1137
1138 // Oil embargo changed the DST period in the US
1139 case 1974:
1140 dt.Set(6, Jan, 1974);
1141 break;
1142
1143 case 1975:
1144 dt.Set(23, Feb, 1975);
1145 break;
1146
1147 default:
1148 // before 1986, DST begun on the last Sunday of April, but
1149 // in 1986 Reagan changed it to begin at 2 a.m. of the
1150 // first Sunday in April
1151 if ( year < 1986 )
1152 {
1153 if ( !dt.SetToLastWeekDay(Sun, Apr, year) )
1154 {
1155 // weird...
1156 wxFAIL_MSG( _T("no first Sunday in April?") );
1157 }
1158 }
1159 else
1160 {
1161 if ( !dt.SetToWeekDay(Sun, 1, Apr, year) )
1162 {
1163 // weird...
1164 wxFAIL_MSG( _T("no first Sunday in April?") );
1165 }
1166 }
1167
1168 dt += wxTimeSpan::Hours(2);
1169
1170 // TODO what about timezone??
1171 }
1172
1173 break;
1174
1175 default:
1176 // assume Mar 30 as the start of the DST for the rest of the world
1177 // - totally bogus, of course
1178 dt.Set(30, Mar, year);
1179 }
1180
1181 return dt;
1182}
1183
1184/* static */
1185wxDateTime wxDateTime::GetEndDST(int year, Country country)
1186{
1187 if ( year == Inv_Year )
1188 {
1189 // take the current year if none given
1190 year = GetCurrentYear();
1191 }
1192
1193 if ( country == Country_Default )
1194 {
1195 country = GetCountry();
1196 }
1197
1198 if ( !IsDSTApplicable(year, country) )
1199 {
1200 return wxInvalidDateTime;
1201 }
1202
1203 wxDateTime dt;
1204
1205 if ( IsWestEuropeanCountry(country) || (country == Russia) )
1206 {
1207 // DST ends at 1 a.m. GMT on the last Sunday of October
1208 if ( !dt.SetToLastWeekDay(Sun, Oct, year) )
1209 {
1210 // weirder and weirder...
1211 wxFAIL_MSG( _T("no last Sunday in October?") );
1212 }
1213
1214 dt += wxTimeSpan::Hours(1);
1215
1216 // disable DST tests because it could result in an infinite recursion!
1217 dt.MakeGMT(true);
1218 }
1219 else switch ( country )
1220 {
1221 case USA:
1222 switch ( year )
1223 {
1224 case 1918:
1225 case 1919:
1226 // don't know for sure - assume it was in effect all year
1227
1228 case 1943:
1229 case 1944:
1230 dt.Set(31, Dec, year);
1231 break;
1232
1233 case 1945:
1234 // the time was reset after the end of the WWII
1235 dt.Set(30, Sep, year);
1236 break;
1237
1238 default:
1239 // DST ends at 2 a.m. on the last Sunday of October
1240 if ( !dt.SetToLastWeekDay(Sun, Oct, year) )
1241 {
1242 // weirder and weirder...
1243 wxFAIL_MSG( _T("no last Sunday in October?") );
1244 }
1245
1246 dt += wxTimeSpan::Hours(2);
1247
1248 // TODO what about timezone??
1249 }
1250 break;
1251
1252 default:
1253 // assume October 26th as the end of the DST - totally bogus too
1254 dt.Set(26, Oct, year);
1255 }
1256
1257 return dt;
1258}
1259
1260// ----------------------------------------------------------------------------
1261// constructors and assignment operators
1262// ----------------------------------------------------------------------------
1263
1264// return the current time with ms precision
1265/* static */ wxDateTime wxDateTime::UNow()
1266{
1267 return wxDateTime(wxGetLocalTimeMillis());
1268}
1269
1270// the values in the tm structure contain the local time
1271wxDateTime& wxDateTime::Set(const struct tm& tm)
1272{
1273 struct tm tm2(tm);
1274 time_t timet = mktime(&tm2);
1275
1276 if ( timet == (time_t)-1 )
1277 {
1278 // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1279 // less than timezone - try to make it work for this case
1280 if ( tm2.tm_year == 70 && tm2.tm_mon == 0 && tm2.tm_mday == 1 )
1281 {
1282 return Set((time_t)(
1283 GetTimeZone() +
1284 tm2.tm_hour * MIN_PER_HOUR * SEC_PER_MIN +
1285 tm2.tm_min * SEC_PER_MIN +
1286 tm2.tm_sec));
1287 }
1288
1289 wxFAIL_MSG( _T("mktime() failed") );
1290
1291 *this = wxInvalidDateTime;
1292
1293 return *this;
1294 }
1295 else
1296 {
1297 return Set(timet);
1298 }
1299}
1300
1301wxDateTime& wxDateTime::Set(wxDateTime_t hour,
1302 wxDateTime_t minute,
1303 wxDateTime_t second,
1304 wxDateTime_t millisec)
1305{
1306 // we allow seconds to be 61 to account for the leap seconds, even if we
1307 // don't use them really
1308 wxDATETIME_CHECK( hour < 24 &&
1309 second < 62 &&
1310 minute < 60 &&
1311 millisec < 1000,
1312 _T("Invalid time in wxDateTime::Set()") );
1313
1314 // get the current date from system
1315 struct tm *tm = GetTmNow();
1316
1317 wxDATETIME_CHECK( tm, _T("localtime() failed") );
1318
1319 // make a copy so it isn't clobbered by the call to mktime() below
1320 struct tm tm1(*tm);
1321
1322 // adjust the time
1323 tm1.tm_hour = hour;
1324 tm1.tm_min = minute;
1325 tm1.tm_sec = second;
1326
1327 // and the DST in case it changes on this date
1328 struct tm tm2(tm1);
1329 mktime(&tm2);
1330 if ( tm2.tm_isdst != tm1.tm_isdst )
1331 tm1.tm_isdst = tm2.tm_isdst;
1332
1333 (void)Set(tm1);
1334
1335 // and finally adjust milliseconds
1336 return SetMillisecond(millisec);
1337}
1338
1339wxDateTime& wxDateTime::Set(wxDateTime_t day,
1340 Month month,
1341 int year,
1342 wxDateTime_t hour,
1343 wxDateTime_t minute,
1344 wxDateTime_t second,
1345 wxDateTime_t millisec)
1346{
1347 wxDATETIME_CHECK( hour < 24 &&
1348 second < 62 &&
1349 minute < 60 &&
1350 millisec < 1000,
1351 _T("Invalid time in wxDateTime::Set()") );
1352
1353 ReplaceDefaultYearMonthWithCurrent(&year, &month);
1354
1355 wxDATETIME_CHECK( (0 < day) && (day <= GetNumberOfDays(month, year)),
1356 _T("Invalid date in wxDateTime::Set()") );
1357
1358 // the range of time_t type (inclusive)
1359 static const int yearMinInRange = 1970;
1360 static const int yearMaxInRange = 2037;
1361
1362 // test only the year instead of testing for the exact end of the Unix
1363 // time_t range - it doesn't bring anything to do more precise checks
1364 if ( year >= yearMinInRange && year <= yearMaxInRange )
1365 {
1366 // use the standard library version if the date is in range - this is
1367 // probably more efficient than our code
1368 struct tm tm;
1369 tm.tm_year = year - 1900;
1370 tm.tm_mon = month;
1371 tm.tm_mday = day;
1372 tm.tm_hour = hour;
1373 tm.tm_min = minute;
1374 tm.tm_sec = second;
1375 tm.tm_isdst = -1; // mktime() will guess it
1376
1377 (void)Set(tm);
1378
1379 // and finally adjust milliseconds
1380 if (IsValid())
1381 SetMillisecond(millisec);
1382
1383 return *this;
1384 }
1385 else
1386 {
1387 // do time calculations ourselves: we want to calculate the number of
1388 // milliseconds between the given date and the epoch
1389
1390 // get the JDN for the midnight of this day
1391 m_time = GetTruncatedJDN(day, month, year);
1392 m_time -= EPOCH_JDN;
1393 m_time *= SECONDS_PER_DAY * TIME_T_FACTOR;
1394
1395 // JDN corresponds to GMT, we take localtime
1396 Add(wxTimeSpan(hour, minute, second + GetTimeZone(), millisec));
1397 }
1398
1399 return *this;
1400}
1401
1402wxDateTime& wxDateTime::Set(double jdn)
1403{
1404 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1405 // EPOCH_JDN + 0.5
1406 jdn -= EPOCH_JDN + 0.5;
1407
1408 jdn *= MILLISECONDS_PER_DAY;
1409
1410 m_time.Assign(jdn);
1411
1412 // JDNs always suppose an UTC date, so bring it back to local time zone
1413 // (also see GetJulianDayNumber() implementation)
1414 long tzDiff = GetTimeZone();
1415 if ( IsDST() == 1 )
1416 {
1417 // FIXME: again, we suppose that DST is always one hour
1418 tzDiff -= 3600;
1419 }
1420
1421 m_time += tzDiff*1000; // tzDiff is in seconds
1422
1423 return *this;
1424}
1425
1426wxDateTime& wxDateTime::ResetTime()
1427{
1428 Tm tm = GetTm();
1429
1430 if ( tm.hour || tm.min || tm.sec || tm.msec )
1431 {
1432 tm.msec =
1433 tm.sec =
1434 tm.min =
1435 tm.hour = 0;
1436
1437 Set(tm);
1438 }
1439
1440 return *this;
1441}
1442
1443// ----------------------------------------------------------------------------
1444// DOS Date and Time Format functions
1445// ----------------------------------------------------------------------------
1446// the dos date and time value is an unsigned 32 bit value in the format:
1447// YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1448//
1449// Y = year offset from 1980 (0-127)
1450// M = month (1-12)
1451// D = day of month (1-31)
1452// h = hour (0-23)
1453// m = minute (0-59)
1454// s = bisecond (0-29) each bisecond indicates two seconds
1455// ----------------------------------------------------------------------------
1456
1457wxDateTime& wxDateTime::SetFromDOS(unsigned long ddt)
1458{
1459 struct tm tm;
1460 InitTm(tm);
1461
1462 long year = ddt & 0xFE000000;
1463 year >>= 25;
1464 year += 80;
1465 tm.tm_year = year;
1466
1467 long month = ddt & 0x1E00000;
1468 month >>= 21;
1469 month -= 1;
1470 tm.tm_mon = month;
1471
1472 long day = ddt & 0x1F0000;
1473 day >>= 16;
1474 tm.tm_mday = day;
1475
1476 long hour = ddt & 0xF800;
1477 hour >>= 11;
1478 tm.tm_hour = hour;
1479
1480 long minute = ddt & 0x7E0;
1481 minute >>= 5;
1482 tm.tm_min = minute;
1483
1484 long second = ddt & 0x1F;
1485 tm.tm_sec = second * 2;
1486
1487 return Set(mktime(&tm));
1488}
1489
1490unsigned long wxDateTime::GetAsDOS() const
1491{
1492 unsigned long ddt;
1493 time_t ticks = GetTicks();
1494 struct tm *tm = localtime(&ticks);
1495
1496 long year = tm->tm_year;
1497 year -= 80;
1498 year <<= 25;
1499
1500 long month = tm->tm_mon;
1501 month += 1;
1502 month <<= 21;
1503
1504 long day = tm->tm_mday;
1505 day <<= 16;
1506
1507 long hour = tm->tm_hour;
1508 hour <<= 11;
1509
1510 long minute = tm->tm_min;
1511 minute <<= 5;
1512
1513 long second = tm->tm_sec;
1514 second /= 2;
1515
1516 ddt = year | month | day | hour | minute | second;
1517 return ddt;
1518}
1519
1520// ----------------------------------------------------------------------------
1521// time_t <-> broken down time conversions
1522// ----------------------------------------------------------------------------
1523
1524wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
1525{
1526 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1527
1528 time_t time = GetTicks();
1529 if ( time != (time_t)-1 )
1530 {
1531 // use C RTL functions
1532 tm *tm;
1533 if ( tz.GetOffset() == -GetTimeZone() )
1534 {
1535 // we are working with local time
1536 tm = localtime(&time);
1537
1538 // should never happen
1539 wxCHECK_MSG( tm, Tm(), _T("localtime() failed") );
1540 }
1541 else
1542 {
1543 time += (time_t)tz.GetOffset();
1544#if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1545 int time2 = (int) time;
1546 if ( time2 >= 0 )
1547#else
1548 if ( time >= 0 )
1549#endif
1550 {
1551 tm = gmtime(&time);
1552
1553 // should never happen
1554 wxCHECK_MSG( tm, Tm(), _T("gmtime() failed") );
1555 }
1556 else
1557 {
1558 tm = (struct tm *)NULL;
1559 }
1560 }
1561
1562 if ( tm )
1563 {
1564 // adjust the milliseconds
1565 Tm tm2(*tm, tz);
1566 long timeOnly = (m_time % MILLISECONDS_PER_DAY).ToLong();
1567 tm2.msec = (wxDateTime_t)(timeOnly % 1000);
1568 return tm2;
1569 }
1570 //else: use generic code below
1571 }
1572
1573 // remember the time and do the calculations with the date only - this
1574 // eliminates rounding errors of the floating point arithmetics
1575
1576 wxLongLong timeMidnight = m_time + tz.GetOffset() * 1000;
1577
1578 long timeOnly = (timeMidnight % MILLISECONDS_PER_DAY).ToLong();
1579
1580 // we want to always have positive time and timeMidnight to be really
1581 // the midnight before it
1582 if ( timeOnly < 0 )
1583 {
1584 timeOnly = MILLISECONDS_PER_DAY + timeOnly;
1585 }
1586
1587 timeMidnight -= timeOnly;
1588
1589 // calculate the Gregorian date from JDN for the midnight of our date:
1590 // this will yield day, month (in 1..12 range) and year
1591
1592 // actually, this is the JDN for the noon of the previous day
1593 long jdn = (timeMidnight / MILLISECONDS_PER_DAY).ToLong() + EPOCH_JDN;
1594
1595 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1596
1597 wxASSERT_MSG( jdn > -2, _T("JDN out of range") );
1598
1599 // calculate the century
1600 long temp = (jdn + JDN_OFFSET) * 4 - 1;
1601 long century = temp / DAYS_PER_400_YEARS;
1602
1603 // then the year and day of year (1 <= dayOfYear <= 366)
1604 temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
1605 long year = (century * 100) + (temp / DAYS_PER_4_YEARS);
1606 long dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
1607
1608 // and finally the month and day of the month
1609 temp = dayOfYear * 5 - 3;
1610 long month = temp / DAYS_PER_5_MONTHS;
1611 long day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
1612
1613 // month is counted from March - convert to normal
1614 if ( month < 10 )
1615 {
1616 month += 3;
1617 }
1618 else
1619 {
1620 year += 1;
1621 month -= 9;
1622 }
1623
1624 // year is offset by 4800
1625 year -= 4800;
1626
1627 // check that the algorithm gave us something reasonable
1628 wxASSERT_MSG( (0 < month) && (month <= 12), _T("invalid month") );
1629 wxASSERT_MSG( (1 <= day) && (day < 32), _T("invalid day") );
1630
1631 // construct Tm from these values
1632 Tm tm;
1633 tm.year = (int)year;
1634 tm.mon = (Month)(month - 1); // algorithm yields 1 for January, not 0
1635 tm.mday = (wxDateTime_t)day;
1636 tm.msec = (wxDateTime_t)(timeOnly % 1000);
1637 timeOnly -= tm.msec;
1638 timeOnly /= 1000; // now we have time in seconds
1639
1640 tm.sec = (wxDateTime_t)(timeOnly % 60);
1641 timeOnly -= tm.sec;
1642 timeOnly /= 60; // now we have time in minutes
1643
1644 tm.min = (wxDateTime_t)(timeOnly % 60);
1645 timeOnly -= tm.min;
1646
1647 tm.hour = (wxDateTime_t)(timeOnly / 60);
1648
1649 return tm;
1650}
1651
1652wxDateTime& wxDateTime::SetYear(int year)
1653{
1654 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1655
1656 Tm tm(GetTm());
1657 tm.year = year;
1658 Set(tm);
1659
1660 return *this;
1661}
1662
1663wxDateTime& wxDateTime::SetMonth(Month month)
1664{
1665 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1666
1667 Tm tm(GetTm());
1668 tm.mon = month;
1669 Set(tm);
1670
1671 return *this;
1672}
1673
1674wxDateTime& wxDateTime::SetDay(wxDateTime_t mday)
1675{
1676 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1677
1678 Tm tm(GetTm());
1679 tm.mday = mday;
1680 Set(tm);
1681
1682 return *this;
1683}
1684
1685wxDateTime& wxDateTime::SetHour(wxDateTime_t hour)
1686{
1687 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1688
1689 Tm tm(GetTm());
1690 tm.hour = hour;
1691 Set(tm);
1692
1693 return *this;
1694}
1695
1696wxDateTime& wxDateTime::SetMinute(wxDateTime_t min)
1697{
1698 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1699
1700 Tm tm(GetTm());
1701 tm.min = min;
1702 Set(tm);
1703
1704 return *this;
1705}
1706
1707wxDateTime& wxDateTime::SetSecond(wxDateTime_t sec)
1708{
1709 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1710
1711 Tm tm(GetTm());
1712 tm.sec = sec;
1713 Set(tm);
1714
1715 return *this;
1716}
1717
1718wxDateTime& wxDateTime::SetMillisecond(wxDateTime_t millisecond)
1719{
1720 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1721
1722 // we don't need to use GetTm() for this one
1723 m_time -= m_time % 1000l;
1724 m_time += millisecond;
1725
1726 return *this;
1727}
1728
1729// ----------------------------------------------------------------------------
1730// wxDateTime arithmetics
1731// ----------------------------------------------------------------------------
1732
1733wxDateTime& wxDateTime::Add(const wxDateSpan& diff)
1734{
1735 Tm tm(GetTm());
1736
1737 tm.year += diff.GetYears();
1738 tm.AddMonths(diff.GetMonths());
1739
1740 // check that the resulting date is valid
1741 if ( tm.mday > GetNumOfDaysInMonth(tm.year, tm.mon) )
1742 {
1743 // We suppose that when adding one month to Jan 31 we want to get Feb
1744 // 28 (or 29), i.e. adding a month to the last day of the month should
1745 // give the last day of the next month which is quite logical.
1746 //
1747 // Unfortunately, there is no logic way to understand what should
1748 // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1749 // We make it Feb 28 (last day too), but it is highly questionable.
1750 tm.mday = GetNumOfDaysInMonth(tm.year, tm.mon);
1751 }
1752
1753 tm.AddDays(diff.GetTotalDays());
1754
1755 Set(tm);
1756
1757 wxASSERT_MSG( IsSameTime(tm),
1758 _T("Add(wxDateSpan) shouldn't modify time") );
1759
1760 return *this;
1761}
1762
1763// ----------------------------------------------------------------------------
1764// Weekday and monthday stuff
1765// ----------------------------------------------------------------------------
1766
1767// convert Sun, Mon, ..., Sat into 6, 0, ..., 5
1768static inline int ConvertWeekDayToMondayBase(int wd)
1769{
1770 return wd == wxDateTime::Sun ? 6 : wd - 1;
1771}
1772
1773/* static */
1774wxDateTime
1775wxDateTime::SetToWeekOfYear(int year, wxDateTime_t numWeek, WeekDay wd)
1776{
1777 wxASSERT_MSG( numWeek > 0,
1778 _T("invalid week number: weeks are counted from 1") );
1779
1780 // Jan 4 always lies in the 1st week of the year
1781 wxDateTime dt(4, Jan, year);
1782 dt.SetToWeekDayInSameWeek(wd);
1783 dt += wxDateSpan::Weeks(numWeek - 1);
1784
1785 return dt;
1786}
1787
1788// use a separate function to avoid warnings about using deprecated
1789// SetToTheWeek in GetWeek below
1790static wxDateTime
1791SetToTheWeek(int year,
1792 wxDateTime::wxDateTime_t numWeek,
1793 wxDateTime::WeekDay weekday,
1794 wxDateTime::WeekFlags flags)
1795{
1796 // Jan 4 always lies in the 1st week of the year
1797 wxDateTime dt(4, wxDateTime::Jan, year);
1798 dt.SetToWeekDayInSameWeek(weekday, flags);
1799 dt += wxDateSpan::Weeks(numWeek - 1);
1800
1801 return dt;
1802}
1803
1804bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek,
1805 WeekDay weekday,
1806 WeekFlags flags)
1807{
1808 int year = GetYear();
1809 *this = ::SetToTheWeek(year, numWeek, weekday, flags);
1810 if ( GetYear() != year )
1811 {
1812 // oops... numWeek was too big
1813 return false;
1814 }
1815
1816 return true;
1817}
1818
1819wxDateTime wxDateTime::GetWeek(wxDateTime_t numWeek,
1820 WeekDay weekday,
1821 WeekFlags flags) const
1822{
1823 return ::SetToTheWeek(GetYear(), numWeek, weekday, flags);
1824}
1825
1826wxDateTime& wxDateTime::SetToLastMonthDay(Month month,
1827 int year)
1828{
1829 // take the current month/year if none specified
1830 if ( year == Inv_Year )
1831 year = GetYear();
1832 if ( month == Inv_Month )
1833 month = GetMonth();
1834
1835 return Set(GetNumOfDaysInMonth(year, month), month, year);
1836}
1837
1838wxDateTime& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday, WeekFlags flags)
1839{
1840 wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
1841
1842 int wdayDst = weekday,
1843 wdayThis = GetWeekDay();
1844 if ( wdayDst == wdayThis )
1845 {
1846 // nothing to do
1847 return *this;
1848 }
1849
1850 if ( flags == Default_First )
1851 {
1852 flags = GetCountry() == USA ? Sunday_First : Monday_First;
1853 }
1854
1855 // the logic below based on comparing weekday and wdayThis works if Sun (0)
1856 // is the first day in the week, but breaks down for Monday_First case so
1857 // we adjust the week days in this case
1858 if ( flags == Monday_First )
1859 {
1860 if ( wdayThis == Sun )
1861 wdayThis += 7;
1862 if ( wdayDst == Sun )
1863 wdayDst += 7;
1864 }
1865 //else: Sunday_First, nothing to do
1866
1867 // go forward or back in time to the day we want
1868 if ( wdayDst < wdayThis )
1869 {
1870 return Subtract(wxDateSpan::Days(wdayThis - wdayDst));
1871 }
1872 else // weekday > wdayThis
1873 {
1874 return Add(wxDateSpan::Days(wdayDst - wdayThis));
1875 }
1876}
1877
1878wxDateTime& wxDateTime::SetToNextWeekDay(WeekDay weekday)
1879{
1880 wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
1881
1882 int diff;
1883 WeekDay wdayThis = GetWeekDay();
1884 if ( weekday == wdayThis )
1885 {
1886 // nothing to do
1887 return *this;
1888 }
1889 else if ( weekday < wdayThis )
1890 {
1891 // need to advance a week
1892 diff = 7 - (wdayThis - weekday);
1893 }
1894 else // weekday > wdayThis
1895 {
1896 diff = weekday - wdayThis;
1897 }
1898
1899 return Add(wxDateSpan::Days(diff));
1900}
1901
1902wxDateTime& wxDateTime::SetToPrevWeekDay(WeekDay weekday)
1903{
1904 wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
1905
1906 int diff;
1907 WeekDay wdayThis = GetWeekDay();
1908 if ( weekday == wdayThis )
1909 {
1910 // nothing to do
1911 return *this;
1912 }
1913 else if ( weekday > wdayThis )
1914 {
1915 // need to go to previous week
1916 diff = 7 - (weekday - wdayThis);
1917 }
1918 else // weekday < wdayThis
1919 {
1920 diff = wdayThis - weekday;
1921 }
1922
1923 return Subtract(wxDateSpan::Days(diff));
1924}
1925
1926bool wxDateTime::SetToWeekDay(WeekDay weekday,
1927 int n,
1928 Month month,
1929 int year)
1930{
1931 wxCHECK_MSG( weekday != Inv_WeekDay, false, _T("invalid weekday") );
1932
1933 // we don't check explicitly that -5 <= n <= 5 because we will return false
1934 // anyhow in such case - but may be should still give an assert for it?
1935
1936 // take the current month/year if none specified
1937 ReplaceDefaultYearMonthWithCurrent(&year, &month);
1938
1939 wxDateTime dt;
1940
1941 // TODO this probably could be optimised somehow...
1942
1943 if ( n > 0 )
1944 {
1945 // get the first day of the month
1946 dt.Set(1, month, year);
1947
1948 // get its wday
1949 WeekDay wdayFirst = dt.GetWeekDay();
1950
1951 // go to the first weekday of the month
1952 int diff = weekday - wdayFirst;
1953 if ( diff < 0 )
1954 diff += 7;
1955
1956 // add advance n-1 weeks more
1957 diff += 7*(n - 1);
1958
1959 dt += wxDateSpan::Days(diff);
1960 }
1961 else // count from the end of the month
1962 {
1963 // get the last day of the month
1964 dt.SetToLastMonthDay(month, year);
1965
1966 // get its wday
1967 WeekDay wdayLast = dt.GetWeekDay();
1968
1969 // go to the last weekday of the month
1970 int diff = wdayLast - weekday;
1971 if ( diff < 0 )
1972 diff += 7;
1973
1974 // and rewind n-1 weeks from there
1975 diff += 7*(-n - 1);
1976
1977 dt -= wxDateSpan::Days(diff);
1978 }
1979
1980 // check that it is still in the same month
1981 if ( dt.GetMonth() == month )
1982 {
1983 *this = dt;
1984
1985 return true;
1986 }
1987 else
1988 {
1989 // no such day in this month
1990 return false;
1991 }
1992}
1993
1994static inline
1995wxDateTime::wxDateTime_t GetDayOfYearFromTm(const wxDateTime::Tm& tm)
1996{
1997 return (wxDateTime::wxDateTime_t)(gs_cumulatedDays[wxDateTime::IsLeapYear(tm.year)][tm.mon] + tm.mday);
1998}
1999
2000wxDateTime::wxDateTime_t wxDateTime::GetDayOfYear(const TimeZone& tz) const
2001{
2002 return GetDayOfYearFromTm(GetTm(tz));
2003}
2004
2005wxDateTime::wxDateTime_t
2006wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags, const TimeZone& tz) const
2007{
2008 if ( flags == Default_First )
2009 {
2010 flags = GetCountry() == USA ? Sunday_First : Monday_First;
2011 }
2012
2013 Tm tm(GetTm(tz));
2014 wxDateTime_t nDayInYear = GetDayOfYearFromTm(tm);
2015
2016 int wdTarget = GetWeekDay(tz);
2017 int wdYearStart = wxDateTime(1, Jan, GetYear()).GetWeekDay();
2018 int week;
2019 if ( flags == Sunday_First )
2020 {
2021 // FIXME: First week is not calculated correctly.
2022 week = (nDayInYear - wdTarget + 7) / 7;
2023 if ( wdYearStart == Wed || wdYearStart == Thu )
2024 week++;
2025 }
2026 else // week starts with monday
2027 {
2028 // adjust the weekdays to non-US style.
2029 wdYearStart = ConvertWeekDayToMondayBase(wdYearStart);
2030 wdTarget = ConvertWeekDayToMondayBase(wdTarget);
2031
2032 // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
2033 //
2034 // Week 01 of a year is per definition the first week that has the
2035 // Thursday in this year, which is equivalent to the week that
2036 // contains the fourth day of January. In other words, the first
2037 // week of a new year is the week that has the majority of its
2038 // days in the new year. Week 01 might also contain days from the
2039 // previous year and the week before week 01 of a year is the last
2040 // week (52 or 53) of the previous year even if it contains days
2041 // from the new year. A week starts with Monday (day 1) and ends
2042 // with Sunday (day 7).
2043 //
2044
2045 // if Jan 1 is Thursday or less, it is in the first week of this year
2046 if ( wdYearStart < 4 )
2047 {
2048 // count the number of entire weeks between Jan 1 and this date
2049 week = (nDayInYear + wdYearStart + 6 - wdTarget)/7;
2050
2051 // be careful to check for overflow in the next year
2052 if ( week == 53 && tm.mday - wdTarget > 28 )
2053 week = 1;
2054 }
2055 else // Jan 1 is in the last week of the previous year
2056 {
2057 // check if we happen to be at the last week of previous year:
2058 if ( tm.mon == Jan && tm.mday < 8 - wdYearStart )
2059 week = wxDateTime(31, Dec, GetYear()-1).GetWeekOfYear();
2060 else
2061 week = (nDayInYear + wdYearStart - 1 - wdTarget)/7;
2062 }
2063 }
2064
2065 return (wxDateTime::wxDateTime_t)week;
2066}
2067
2068wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags,
2069 const TimeZone& tz) const
2070{
2071 Tm tm = GetTm(tz);
2072 wxDateTime dtMonthStart = wxDateTime(1, tm.mon, tm.year);
2073 int nWeek = GetWeekOfYear(flags) - dtMonthStart.GetWeekOfYear(flags) + 1;
2074 if ( nWeek < 0 )
2075 {
2076 // this may happen for January when Jan, 1 is the last week of the
2077 // previous year
2078 nWeek += IsLeapYear(tm.year - 1) ? 53 : 52;
2079 }
2080
2081 return (wxDateTime::wxDateTime_t)nWeek;
2082}
2083
2084wxDateTime& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday)
2085{
2086 int year = GetYear();
2087 wxDATETIME_CHECK( (0 < yday) && (yday <= GetNumberOfDays(year)),
2088 _T("invalid year day") );
2089
2090 bool isLeap = IsLeapYear(year);
2091 for ( Month mon = Jan; mon < Inv_Month; wxNextMonth(mon) )
2092 {
2093 // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
2094 // don't need it neither - because of the CHECK above we know that
2095 // yday lies in December then
2096 if ( (mon == Dec) || (yday <= gs_cumulatedDays[isLeap][mon + 1]) )
2097 {
2098 Set((wxDateTime::wxDateTime_t)(yday - gs_cumulatedDays[isLeap][mon]), mon, year);
2099
2100 break;
2101 }
2102 }
2103
2104 return *this;
2105}
2106
2107// ----------------------------------------------------------------------------
2108// Julian day number conversion and related stuff
2109// ----------------------------------------------------------------------------
2110
2111double wxDateTime::GetJulianDayNumber() const
2112{
2113 // JDN are always expressed for the UTC dates
2114 Tm tm(ToTimezone(UTC).GetTm(UTC));
2115
2116 double result = GetTruncatedJDN(tm.mday, tm.mon, tm.year);
2117
2118 // add the part GetTruncatedJDN() neglected
2119 result += 0.5;
2120
2121 // and now add the time: 86400 sec = 1 JDN
2122 return result + ((double)(60*(60*tm.hour + tm.min) + tm.sec)) / 86400;
2123}
2124
2125double wxDateTime::GetRataDie() const
2126{
2127 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
2128 return GetJulianDayNumber() - 1721119.5 - 306;
2129}
2130
2131// ----------------------------------------------------------------------------
2132// timezone and DST stuff
2133// ----------------------------------------------------------------------------
2134
2135int wxDateTime::IsDST(wxDateTime::Country country) const
2136{
2137 wxCHECK_MSG( country == Country_Default, -1,
2138 _T("country support not implemented") );
2139
2140 // use the C RTL for the dates in the standard range
2141 time_t timet = GetTicks();
2142 if ( timet != (time_t)-1 )
2143 {
2144 tm *tm = localtime(&timet);
2145
2146 wxCHECK_MSG( tm, -1, _T("localtime() failed") );
2147
2148 return tm->tm_isdst;
2149 }
2150 else
2151 {
2152 int year = GetYear();
2153
2154 if ( !IsDSTApplicable(year, country) )
2155 {
2156 // no DST time in this year in this country
2157 return -1;
2158 }
2159
2160 return IsBetween(GetBeginDST(year, country), GetEndDST(year, country));
2161 }
2162}
2163
2164wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST)
2165{
2166 long secDiff = GetTimeZone() + tz.GetOffset();
2167
2168 // we need to know whether DST is or not in effect for this date unless
2169 // the test disabled by the caller
2170 if ( !noDST && (IsDST() == 1) )
2171 {
2172 // FIXME we assume that the DST is always shifted by 1 hour
2173 secDiff -= 3600;
2174 }
2175
2176 return Subtract(wxTimeSpan::Seconds(secDiff));
2177}
2178
2179// ----------------------------------------------------------------------------
2180// wxDateTime to/from text representations
2181// ----------------------------------------------------------------------------
2182
2183wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
2184{
2185 wxCHECK_MSG( format, wxEmptyString, _T("NULL format in wxDateTime::Format") );
2186
2187 // we have to use our own implementation if the date is out of range of
2188 // strftime() or if we use non standard specificators
2189 time_t time = GetTicks();
2190 if ( (time != (time_t)-1) && !wxStrstr(format, _T("%l")) )
2191 {
2192 // use strftime()
2193 tm *tm;
2194 if ( tz.GetOffset() == -GetTimeZone() )
2195 {
2196 // we are working with local time
2197 tm = localtime(&time);
2198
2199 // should never happen
2200 wxCHECK_MSG( tm, wxEmptyString, _T("localtime() failed") );
2201 }
2202 else
2203 {
2204 time += (int)tz.GetOffset();
2205
2206#if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
2207 int time2 = (int) time;
2208 if ( time2 >= 0 )
2209#else
2210 if ( time >= 0 )
2211#endif
2212 {
2213 tm = gmtime(&time);
2214
2215 // should never happen
2216 wxCHECK_MSG( tm, wxEmptyString, _T("gmtime() failed") );
2217 }
2218 else
2219 {
2220 tm = (struct tm *)NULL;
2221 }
2222 }
2223#ifndef __WXWINCE__
2224 //Windows CE doesn't support strftime or wcsftime, so we use the generic implementation
2225 if ( tm )
2226 {
2227 return CallStrftime(format, tm);
2228 }
2229#endif
2230 //else: use generic code below
2231 }
2232
2233 // we only parse ANSI C format specifications here, no POSIX 2
2234 // complications, no GNU extensions but we do add support for a "%l" format
2235 // specifier allowing to get the number of milliseconds
2236 Tm tm = GetTm(tz);
2237
2238 // used for calls to strftime() when we only deal with time
2239 struct tm tmTimeOnly;
2240 tmTimeOnly.tm_hour = tm.hour;
2241 tmTimeOnly.tm_min = tm.min;
2242 tmTimeOnly.tm_sec = tm.sec;
2243 tmTimeOnly.tm_wday = 0;
2244 tmTimeOnly.tm_yday = 0;
2245 tmTimeOnly.tm_mday = 1; // any date will do
2246 tmTimeOnly.tm_mon = 0;
2247 tmTimeOnly.tm_year = 76;
2248 tmTimeOnly.tm_isdst = 0; // no DST, we adjust for tz ourselves
2249
2250 wxString tmp, res, fmt;
2251 for ( const wxChar *p = format; *p; p++ )
2252 {
2253 if ( *p != _T('%') )
2254 {
2255 // copy as is
2256 res += *p;
2257
2258 continue;
2259 }
2260
2261 // set the default format
2262 switch ( *++p )
2263 {
2264 case _T('Y'): // year has 4 digits
2265 fmt = _T("%04d");
2266 break;
2267
2268 case _T('j'): // day of year has 3 digits
2269 case _T('l'): // milliseconds have 3 digits
2270 fmt = _T("%03d");
2271 break;
2272
2273 case _T('w'): // week day as number has only one
2274 fmt = _T("%d");
2275 break;
2276
2277 default:
2278 // it's either another valid format specifier in which case
2279 // the format is "%02d" (for all the rest) or we have the
2280 // field width preceding the format in which case it will
2281 // override the default format anyhow
2282 fmt = _T("%02d");
2283 }
2284
2285 bool restart = true;
2286 while ( restart )
2287 {
2288 restart = false;
2289
2290 // start of the format specification
2291 switch ( *p )
2292 {
2293 case _T('a'): // a weekday name
2294 case _T('A'):
2295 // second parameter should be true for abbreviated names
2296 res += GetWeekDayName(tm.GetWeekDay(),
2297 *p == _T('a') ? Name_Abbr : Name_Full);
2298 break;
2299
2300 case _T('b'): // a month name
2301 case _T('B'):
2302 res += GetMonthName(tm.mon,
2303 *p == _T('b') ? Name_Abbr : Name_Full);
2304 break;
2305
2306 case _T('c'): // locale default date and time representation
2307 case _T('x'): // locale default date representation
2308#ifndef __WXWINCE__
2309 //
2310 // the problem: there is no way to know what do these format
2311 // specifications correspond to for the current locale.
2312 //
2313 // the solution: use a hack and still use strftime(): first
2314 // find the YEAR which is a year in the strftime() range (1970
2315 // - 2038) whose Jan 1 falls on the same week day as the Jan 1
2316 // of the real year. Then make a copy of the format and
2317 // replace all occurrences of YEAR in it with some unique
2318 // string not appearing anywhere else in it, then use
2319 // strftime() to format the date in year YEAR and then replace
2320 // YEAR back by the real year and the unique replacement
2321 // string back with YEAR. Notice that "all occurrences of YEAR"
2322 // means all occurrences of 4 digit as well as 2 digit form!
2323 //
2324 // the bugs: we assume that neither of %c nor %x contains any
2325 // fields which may change between the YEAR and real year. For
2326 // example, the week number (%U, %W) and the day number (%j)
2327 // will change if one of these years is leap and the other one
2328 // is not!
2329 {
2330 // find the YEAR: normally, for any year X, Jan 1 or the
2331 // year X + 28 is the same weekday as Jan 1 of X (because
2332 // the weekday advances by 1 for each normal X and by 2
2333 // for each leap X, hence by 5 every 4 years or by 35
2334 // which is 0 mod 7 every 28 years) but this rule breaks
2335 // down if there are years between X and Y which are
2336 // divisible by 4 but not leap (i.e. divisible by 100 but
2337 // not 400), hence the correction.
2338
2339 int yearReal = GetYear(tz);
2340 int mod28 = yearReal % 28;
2341
2342 // be careful to not go too far - we risk to leave the
2343 // supported range
2344 int year;
2345 if ( mod28 < 10 )
2346 {
2347 year = 1988 + mod28; // 1988 == 0 (mod 28)
2348 }
2349 else
2350 {
2351 year = 1970 + mod28 - 10; // 1970 == 10 (mod 28)
2352 }
2353
2354 int nCentury = year / 100,
2355 nCenturyReal = yearReal / 100;
2356
2357 // need to adjust for the years divisble by 400 which are
2358 // not leap but are counted like leap ones if we just take
2359 // the number of centuries in between for nLostWeekDays
2360 int nLostWeekDays = (nCentury - nCenturyReal) -
2361 (nCentury / 4 - nCenturyReal / 4);
2362
2363 // we have to gain back the "lost" weekdays: note that the
2364 // effect of this loop is to not do anything to
2365 // nLostWeekDays (which we won't use any more), but to
2366 // (indirectly) set the year correctly
2367 while ( (nLostWeekDays % 7) != 0 )
2368 {
2369 nLostWeekDays += year++ % 4 ? 1 : 2;
2370 }
2371
2372 // at any rate, we couldn't go further than 1988 + 9 + 28!
2373 wxASSERT_MSG( year < 2030,
2374 _T("logic error in wxDateTime::Format") );
2375
2376 wxString strYear, strYear2;
2377 strYear.Printf(_T("%d"), year);
2378 strYear2.Printf(_T("%d"), year % 100);
2379
2380 // find two strings not occurring in format (this is surely
2381 // not the optimal way of doing it... improvements welcome!)
2382 wxString fmt = format;
2383 wxString replacement = (wxChar)-1;
2384 while ( fmt.Find(replacement) != wxNOT_FOUND )
2385 {
2386 replacement << (wxChar)-1;
2387 }
2388
2389 wxString replacement2 = (wxChar)-2;
2390 while ( fmt.Find(replacement) != wxNOT_FOUND )
2391 {
2392 replacement << (wxChar)-2;
2393 }
2394
2395 // replace all occurrences of year with it
2396 bool wasReplaced = fmt.Replace(strYear, replacement) > 0;
2397 if ( !wasReplaced )
2398 wasReplaced = fmt.Replace(strYear2, replacement2) > 0;
2399
2400 // use strftime() to format the same date but in supported
2401 // year
2402 //
2403 // NB: we assume that strftime() doesn't check for the
2404 // date validity and will happily format the date
2405 // corresponding to Feb 29 of a non leap year (which
2406 // may happen if yearReal was leap and year is not)
2407 struct tm tmAdjusted;
2408 InitTm(tmAdjusted);
2409 tmAdjusted.tm_hour = tm.hour;
2410 tmAdjusted.tm_min = tm.min;
2411 tmAdjusted.tm_sec = tm.sec;
2412 tmAdjusted.tm_wday = tm.GetWeekDay();
2413 tmAdjusted.tm_yday = GetDayOfYear();
2414 tmAdjusted.tm_mday = tm.mday;
2415 tmAdjusted.tm_mon = tm.mon;
2416 tmAdjusted.tm_year = year - 1900;
2417 tmAdjusted.tm_isdst = 0; // no DST, already adjusted
2418 wxString str = CallStrftime(*p == _T('c') ? _T("%c")
2419 : _T("%x"),
2420 &tmAdjusted);
2421
2422 // now replace the occurrence of 1999 with the real year
2423 wxString strYearReal, strYearReal2;
2424 strYearReal.Printf(_T("%04d"), yearReal);
2425 strYearReal2.Printf(_T("%02d"), yearReal % 100);
2426 str.Replace(strYear, strYearReal);
2427 str.Replace(strYear2, strYearReal2);
2428
2429 // and replace back all occurrences of replacement string
2430 if ( wasReplaced )
2431 {
2432 str.Replace(replacement2, strYear2);
2433 str.Replace(replacement, strYear);
2434 }
2435
2436 res += str;
2437 }
2438#else
2439 //Use "%m/%d/%y %H:%M:%S" format instead
2440 res += wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
2441 tm.mon+1,tm.mday, tm.year, tm.hour, tm.min, tm.sec);
2442#endif
2443 break;
2444
2445 case _T('d'): // day of a month (01-31)
2446 res += wxString::Format(fmt, tm.mday);
2447 break;
2448
2449 case _T('H'): // hour in 24h format (00-23)
2450 res += wxString::Format(fmt, tm.hour);
2451 break;
2452
2453 case _T('I'): // hour in 12h format (01-12)
2454 {
2455 // 24h -> 12h, 0h -> 12h too
2456 int hour12 = tm.hour > 12 ? tm.hour - 12
2457 : tm.hour ? tm.hour : 12;
2458 res += wxString::Format(fmt, hour12);
2459 }
2460 break;
2461
2462 case _T('j'): // day of the year
2463 res += wxString::Format(fmt, GetDayOfYear(tz));
2464 break;
2465
2466 case _T('l'): // milliseconds (NOT STANDARD)
2467 res += wxString::Format(fmt, GetMillisecond(tz));
2468 break;
2469
2470 case _T('m'): // month as a number (01-12)
2471 res += wxString::Format(fmt, tm.mon + 1);
2472 break;
2473
2474 case _T('M'): // minute as a decimal number (00-59)
2475 res += wxString::Format(fmt, tm.min);
2476 break;
2477
2478 case _T('p'): // AM or PM string
2479#ifndef __WXWINCE__
2480 res += CallStrftime(_T("%p"), &tmTimeOnly);
2481#else
2482 res += (tmTimeOnly.tm_hour > 12) ? wxT("pm") : wxT("am");
2483#endif
2484 break;
2485
2486 case _T('S'): // second as a decimal number (00-61)
2487 res += wxString::Format(fmt, tm.sec);
2488 break;
2489
2490 case _T('U'): // week number in the year (Sunday 1st week day)
2491 res += wxString::Format(fmt, GetWeekOfYear(Sunday_First, tz));
2492 break;
2493
2494 case _T('W'): // week number in the year (Monday 1st week day)
2495 res += wxString::Format(fmt, GetWeekOfYear(Monday_First, tz));
2496 break;
2497
2498 case _T('w'): // weekday as a number (0-6), Sunday = 0
2499 res += wxString::Format(fmt, tm.GetWeekDay());
2500 break;
2501
2502 // case _T('x'): -- handled with "%c"
2503
2504 case _T('X'): // locale default time representation
2505 // just use strftime() to format the time for us
2506#ifndef __WXWINCE__
2507 res += CallStrftime(_T("%X"), &tmTimeOnly);
2508#else
2509 res += wxString::Format(wxT("%02d:%02d:%02d"),tm.hour, tm.min, tm.sec);
2510#endif
2511 break;
2512
2513 case _T('y'): // year without century (00-99)
2514 res += wxString::Format(fmt, tm.year % 100);
2515 break;
2516
2517 case _T('Y'): // year with century
2518 res += wxString::Format(fmt, tm.year);
2519 break;
2520
2521 case _T('Z'): // timezone name
2522#ifndef __WXWINCE__
2523 res += CallStrftime(_T("%Z"), &tmTimeOnly);
2524#endif
2525 break;
2526
2527 default:
2528 // is it the format width?
2529 fmt.Empty();
2530 while ( *p == _T('-') || *p == _T('+') ||
2531 *p == _T(' ') || wxIsdigit(*p) )
2532 {
2533 fmt += *p;
2534 }
2535
2536 if ( !fmt.empty() )
2537 {
2538 // we've only got the flags and width so far in fmt
2539 fmt.Prepend(_T('%'));
2540 fmt.Append(_T('d'));
2541
2542 restart = true;
2543
2544 break;
2545 }
2546
2547 // no, it wasn't the width
2548 wxFAIL_MSG(_T("unknown format specificator"));
2549
2550 // fall through and just copy it nevertheless
2551
2552 case _T('%'): // a percent sign
2553 res += *p;
2554 break;
2555
2556 case 0: // the end of string
2557 wxFAIL_MSG(_T("missing format at the end of string"));
2558
2559 // just put the '%' which was the last char in format
2560 res += _T('%');
2561 break;
2562 }
2563 }
2564 }
2565
2566 return res;
2567}
2568
2569// this function parses a string in (strict) RFC 822 format: see the section 5
2570// of the RFC for the detailed description, but briefly it's something of the
2571// form "Sat, 18 Dec 1999 00:48:30 +0100"
2572//
2573// this function is "strict" by design - it must reject anything except true
2574// RFC822 time specs.
2575//
2576// TODO a great candidate for using reg exps
2577const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date)
2578{
2579 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
2580
2581 const wxChar *p = date;
2582 const wxChar *comma = wxStrchr(p, _T(','));
2583 if ( comma )
2584 {
2585 // the part before comma is the weekday
2586
2587 // skip it for now - we don't use but might check that it really
2588 // corresponds to the specfied date
2589 p = comma + 1;
2590
2591 if ( *p != _T(' ') )
2592 {
2593 wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2594
2595 return (wxChar *)NULL;
2596 }
2597
2598 p++; // skip space
2599 }
2600
2601 // the following 1 or 2 digits are the day number
2602 if ( !wxIsdigit(*p) )
2603 {
2604 wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2605
2606 return (wxChar *)NULL;
2607 }
2608
2609 wxDateTime_t day = (wxDateTime_t)(*p++ - _T('0'));
2610 if ( wxIsdigit(*p) )
2611 {
2612 day *= 10;
2613 day = (wxDateTime_t)(day + (*p++ - _T('0')));
2614 }
2615
2616 if ( *p++ != _T(' ') )
2617 {
2618 return (wxChar *)NULL;
2619 }
2620
2621 // the following 3 letters specify the month
2622 wxString monName(p, 3);
2623 Month mon;
2624 if ( monName == _T("Jan") )
2625 mon = Jan;
2626 else if ( monName == _T("Feb") )
2627 mon = Feb;
2628 else if ( monName == _T("Mar") )
2629 mon = Mar;
2630 else if ( monName == _T("Apr") )
2631 mon = Apr;
2632 else if ( monName == _T("May") )
2633 mon = May;
2634 else if ( monName == _T("Jun") )
2635 mon = Jun;
2636 else if ( monName == _T("Jul") )
2637 mon = Jul;
2638 else if ( monName == _T("Aug") )
2639 mon = Aug;
2640 else if ( monName == _T("Sep") )
2641 mon = Sep;
2642 else if ( monName == _T("Oct") )
2643 mon = Oct;
2644 else if ( monName == _T("Nov") )
2645 mon = Nov;
2646 else if ( monName == _T("Dec") )
2647 mon = Dec;
2648 else
2649 {
2650 wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName.c_str());
2651
2652 return (wxChar *)NULL;
2653 }
2654
2655 p += 3;
2656
2657 if ( *p++ != _T(' ') )
2658 {
2659 return (wxChar *)NULL;
2660 }
2661
2662 // next is the year
2663 if ( !wxIsdigit(*p) )
2664 {
2665 // no year?
2666 return (wxChar *)NULL;
2667 }
2668
2669 int year = *p++ - _T('0');
2670
2671 if ( !wxIsdigit(*p) )
2672 {
2673 // should have at least 2 digits in the year
2674 return (wxChar *)NULL;
2675 }
2676
2677 year *= 10;
2678 year += *p++ - _T('0');
2679
2680 // is it a 2 digit year (as per original RFC 822) or a 4 digit one?
2681 if ( wxIsdigit(*p) )
2682 {
2683 year *= 10;
2684 year += *p++ - _T('0');
2685
2686 if ( !wxIsdigit(*p) )
2687 {
2688 // no 3 digit years please
2689 return (wxChar *)NULL;
2690 }
2691
2692 year *= 10;
2693 year += *p++ - _T('0');
2694 }
2695
2696 if ( *p++ != _T(' ') )
2697 {
2698 return (wxChar *)NULL;
2699 }
2700
2701 // time is in the format hh:mm:ss and seconds are optional
2702 if ( !wxIsdigit(*p) )
2703 {
2704 return (wxChar *)NULL;
2705 }
2706
2707 wxDateTime_t hour = (wxDateTime_t)(*p++ - _T('0'));
2708
2709 if ( !wxIsdigit(*p) )
2710 {
2711 return (wxChar *)NULL;
2712 }
2713
2714 hour *= 10;
2715 hour = (wxDateTime_t)(hour + (*p++ - _T('0')));
2716
2717 if ( *p++ != _T(':') )
2718 {
2719 return (wxChar *)NULL;
2720 }
2721
2722 if ( !wxIsdigit(*p) )
2723 {
2724 return (wxChar *)NULL;
2725 }
2726
2727 wxDateTime_t min = (wxDateTime_t)(*p++ - _T('0'));
2728
2729 if ( !wxIsdigit(*p) )
2730 {
2731 return (wxChar *)NULL;
2732 }
2733
2734 min *= 10;
2735 min = (wxDateTime_t)(min + *p++ - _T('0'));
2736
2737 wxDateTime_t sec = 0;
2738 if ( *p++ == _T(':') )
2739 {
2740 if ( !wxIsdigit(*p) )
2741 {
2742 return (wxChar *)NULL;
2743 }
2744
2745 sec = (wxDateTime_t)(*p++ - _T('0'));
2746
2747 if ( !wxIsdigit(*p) )
2748 {
2749 return (wxChar *)NULL;
2750 }
2751
2752 sec *= 10;
2753 sec = (wxDateTime_t)(sec + *p++ - _T('0'));
2754 }
2755
2756 if ( *p++ != _T(' ') )
2757 {
2758 return (wxChar *)NULL;
2759 }
2760
2761 // and now the interesting part: the timezone
2762 int offset wxDUMMY_INITIALIZE(0);
2763 if ( *p == _T('-') || *p == _T('+') )
2764 {
2765 // the explicit offset given: it has the form of hhmm
2766 bool plus = *p++ == _T('+');
2767
2768 if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) )
2769 {
2770 return (wxChar *)NULL;
2771 }
2772
2773 // hours
2774 offset = 60*(10*(*p - _T('0')) + (*(p + 1) - _T('0')));
2775
2776 p += 2;
2777
2778 if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) )
2779 {
2780 return (wxChar *)NULL;
2781 }
2782
2783 // minutes
2784 offset += 10*(*p - _T('0')) + (*(p + 1) - _T('0'));
2785
2786 if ( !plus )
2787 {
2788 offset = -offset;
2789 }
2790
2791 p += 2;
2792 }
2793 else
2794 {
2795 // the symbolic timezone given: may be either military timezone or one
2796 // of standard abbreviations
2797 if ( !*(p + 1) )
2798 {
2799 // military: Z = UTC, J unused, A = -1, ..., Y = +12
2800 static const int offsets[26] =
2801 {
2802 //A B C D E F G H I J K L M
2803 -1, -2, -3, -4, -5, -6, -7, -8, -9, 0, -10, -11, -12,
2804 //N O P R Q S T U V W Z Y Z
2805 +1, +2, +3, +4, +5, +6, +7, +8, +9, +10, +11, +12, 0
2806 };
2807
2808 if ( *p < _T('A') || *p > _T('Z') || *p == _T('J') )
2809 {
2810 wxLogDebug(_T("Invalid militaty timezone '%c'"), *p);
2811
2812 return (wxChar *)NULL;
2813 }
2814
2815 offset = offsets[*p++ - _T('A')];
2816 }
2817 else
2818 {
2819 // abbreviation
2820 wxString tz = p;
2821 if ( tz == _T("UT") || tz == _T("UTC") || tz == _T("GMT") )
2822 offset = 0;
2823 else if ( tz == _T("AST") )
2824 offset = AST - GMT0;
2825 else if ( tz == _T("ADT") )
2826 offset = ADT - GMT0;
2827 else if ( tz == _T("EST") )
2828 offset = EST - GMT0;
2829 else if ( tz == _T("EDT") )
2830 offset = EDT - GMT0;
2831 else if ( tz == _T("CST") )
2832 offset = CST - GMT0;
2833 else if ( tz == _T("CDT") )
2834 offset = CDT - GMT0;
2835 else if ( tz == _T("MST") )
2836 offset = MST - GMT0;
2837 else if ( tz == _T("MDT") )
2838 offset = MDT - GMT0;
2839 else if ( tz == _T("PST") )
2840 offset = PST - GMT0;
2841 else if ( tz == _T("PDT") )
2842 offset = PDT - GMT0;
2843 else
2844 {
2845 wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p);
2846
2847 return (wxChar *)NULL;
2848 }
2849
2850 p += tz.length();
2851 }
2852
2853 // make it minutes
2854 offset *= 60;
2855 }
2856
2857 // the spec was correct
2858 Set(day, mon, year, hour, min, sec);
2859 MakeTimezone((wxDateTime_t)(60*offset));
2860
2861 return p;
2862}
2863
2864#ifdef __WINDOWS__
2865
2866// returns the string containing strftime() format used for short dates in the
2867// current locale or an empty string
2868static wxString GetLocaleDateFormat()
2869{
2870 wxString fmtWX;
2871
2872 // there is no setlocale() under Windows CE, so just always query the
2873 // system there
2874#ifndef __WXWINCE__
2875 if ( strcmp(setlocale(LC_ALL, NULL), "C") != 0 )
2876#endif
2877 {
2878 // The locale was programatically set to non-C. We assume that this was
2879 // done using wxLocale, in which case thread's current locale is also
2880 // set to correct LCID value and we can use GetLocaleInfo to determine
2881 // the correct formatting string:
2882#ifdef __WXWINCE__
2883 LCID lcid = LOCALE_USER_DEFAULT;
2884#else
2885 LCID lcid = GetThreadLocale();
2886#endif
2887 // according to MSDN 80 chars is max allowed for short date format
2888 wxChar fmt[81];
2889 if ( ::GetLocaleInfo(lcid, LOCALE_SSHORTDATE, fmt, WXSIZEOF(fmt)) )
2890 {
2891 wxChar chLast = _T('\0');
2892 size_t lastCount = 0;
2893 for ( const wxChar *p = fmt; /* NUL handled inside */; p++ )
2894 {
2895 if ( *p == chLast )
2896 {
2897 lastCount++;
2898 continue;
2899 }
2900
2901 switch ( *p )
2902 {
2903 // these characters come in groups, start counting them
2904 case _T('d'):
2905 case _T('M'):
2906 case _T('y'):
2907 case _T('g'):
2908 chLast = *p;
2909 lastCount = 1;
2910 break;
2911
2912 default:
2913 // first deal with any special characters we have had
2914 if ( lastCount )
2915 {
2916 switch ( chLast )
2917 {
2918 case _T('d'):
2919 switch ( lastCount )
2920 {
2921 case 1: // d
2922 case 2: // dd
2923 // these two are the same as we
2924 // don't distinguish between 1 and
2925 // 2 digits for days
2926 fmtWX += _T("%d");
2927 break;
2928
2929 case 3: // ddd
2930 fmtWX += _T("%a");
2931 break;
2932
2933 case 4: // dddd
2934 fmtWX += _T("%A");
2935 break;
2936
2937 default:
2938 wxFAIL_MSG( _T("too many 'd's") );
2939 }
2940 break;
2941
2942 case _T('M'):
2943 switch ( lastCount )
2944 {
2945 case 1: // M
2946 case 2: // MM
2947 // as for 'd' and 'dd' above
2948 fmtWX += _T("%m");
2949 break;
2950
2951 case 3:
2952 fmtWX += _T("%b");
2953 break;
2954
2955 case 4:
2956 fmtWX += _T("%B");
2957 break;
2958
2959 default:
2960 wxFAIL_MSG( _T("too many 'M's") );
2961 }
2962 break;
2963
2964 case _T('y'):
2965 switch ( lastCount )
2966 {
2967 case 1: // y
2968 case 2: // yy
2969 fmtWX += _T("%y");
2970 break;
2971
2972 case 4: // yyyy
2973 fmtWX += _T("%Y");
2974 break;
2975
2976 default:
2977 wxFAIL_MSG( _T("wrong number of 'y's") );
2978 }
2979 break;
2980
2981 case _T('g'):
2982 // strftime() doesn't have era string,
2983 // ignore this format
2984 wxASSERT_MSG( lastCount <= 2,
2985 _T("too many 'g's") );
2986 break;
2987
2988 default:
2989 wxFAIL_MSG( _T("unreachable") );
2990 }
2991
2992 chLast = _T('\0');
2993 lastCount = 0;
2994 }
2995
2996 // not a special character so must be just a separator,
2997 // treat as is
2998 if ( *p != _T('\0') )
2999 {
3000 if ( *p == _T('%') )
3001 {
3002 // this one needs to be escaped
3003 fmtWX += _T('%');
3004 }
3005
3006 fmtWX += *p;
3007 }
3008 }
3009
3010 if ( *p == _T('\0') )
3011 break;
3012 }
3013 }
3014 //else: GetLocaleInfo() failed, leave fmtDate value unchanged and
3015 // try our luck with the default formats
3016 }
3017 //else: default C locale, default formats should work
3018
3019 return fmtWX;
3020}
3021
3022#endif // __WINDOWS__
3023
3024const wxChar *wxDateTime::ParseFormat(const wxChar *date,
3025 const wxChar *format,
3026 const wxDateTime& dateDef)
3027{
3028 wxCHECK_MSG( date && format, (wxChar *)NULL,
3029 _T("NULL pointer in wxDateTime::ParseFormat()") );
3030
3031 wxString str;
3032 unsigned long num;
3033
3034 // what fields have we found?
3035 bool haveWDay = false,
3036 haveYDay = false,
3037 haveDay = false,
3038 haveMon = false,
3039 haveYear = false,
3040 haveHour = false,
3041 haveMin = false,
3042 haveSec = false;
3043
3044 bool hourIsIn12hFormat = false, // or in 24h one?
3045 isPM = false; // AM by default
3046
3047 // and the value of the items we have (init them to get rid of warnings)
3048 wxDateTime_t sec = 0,
3049 min = 0,
3050 hour = 0;
3051 WeekDay wday = Inv_WeekDay;
3052 wxDateTime_t yday = 0,
3053 mday = 0;
3054 wxDateTime::Month mon = Inv_Month;
3055 int year = 0;
3056
3057 const wxChar *input = date;
3058 for ( const wxChar *fmt = format; *fmt; fmt++ )
3059 {
3060 if ( *fmt != _T('%') )
3061 {
3062 if ( wxIsspace(*fmt) )
3063 {
3064 // a white space in the format string matches 0 or more white
3065 // spaces in the input
3066 while ( wxIsspace(*input) )
3067 {
3068 input++;
3069 }
3070 }
3071 else // !space
3072 {
3073 // any other character (not whitespace, not '%') must be
3074 // matched by itself in the input
3075 if ( *input++ != *fmt )
3076 {
3077 // no match
3078 return (wxChar *)NULL;
3079 }
3080 }
3081
3082 // done with this format char
3083 continue;
3084 }
3085
3086 // start of a format specification
3087
3088 // parse the optional width
3089 size_t width = 0;
3090 while ( wxIsdigit(*++fmt) )
3091 {
3092 width *= 10;
3093 width += *fmt - _T('0');
3094 }
3095
3096 // the default widths for the various fields
3097 if ( !width )
3098 {
3099 switch ( *fmt )
3100 {
3101 case _T('Y'): // year has 4 digits
3102 width = 4;
3103 break;
3104
3105 case _T('j'): // day of year has 3 digits
3106 case _T('l'): // milliseconds have 3 digits
3107 width = 3;
3108 break;
3109
3110 case _T('w'): // week day as number has only one
3111 width = 1;
3112 break;
3113
3114 default:
3115 // default for all other fields
3116 width = 2;
3117 }
3118 }
3119
3120 // then the format itself
3121 switch ( *fmt )
3122 {
3123 case _T('a'): // a weekday name
3124 case _T('A'):
3125 {
3126 int flag = *fmt == _T('a') ? Name_Abbr : Name_Full;
3127 wday = GetWeekDayFromName(GetAlphaToken(input), flag);
3128 if ( wday == Inv_WeekDay )
3129 {
3130 // no match
3131 return (wxChar *)NULL;
3132 }
3133 }
3134 haveWDay = true;
3135 break;
3136
3137 case _T('b'): // a month name
3138 case _T('B'):
3139 {
3140 int flag = *fmt == _T('b') ? Name_Abbr : Name_Full;
3141 mon = GetMonthFromName(GetAlphaToken(input), flag);
3142 if ( mon == Inv_Month )
3143 {
3144 // no match
3145 return (wxChar *)NULL;
3146 }
3147 }
3148 haveMon = true;
3149 break;
3150
3151 case _T('c'): // locale default date and time representation
3152 {
3153 wxDateTime dt;
3154
3155 // this is the format which corresponds to ctime() output
3156 // and strptime("%c") should parse it, so try it first
3157 static const wxChar *fmtCtime = _T("%a %b %d %H:%M:%S %Y");
3158
3159 const wxChar *result = dt.ParseFormat(input, fmtCtime);
3160 if ( !result )
3161 {
3162 result = dt.ParseFormat(input, _T("%x %X"));
3163 }
3164
3165 if ( !result )
3166 {
3167 result = dt.ParseFormat(input, _T("%X %x"));
3168 }
3169
3170 if ( !result )
3171 {
3172 // we've tried everything and still no match
3173 return (wxChar *)NULL;
3174 }
3175
3176 Tm tm = dt.GetTm();
3177
3178 haveDay = haveMon = haveYear =
3179 haveHour = haveMin = haveSec = true;
3180
3181 hour = tm.hour;
3182 min = tm.min;
3183 sec = tm.sec;
3184
3185 year = tm.year;
3186 mon = tm.mon;
3187 mday = tm.mday;
3188
3189 input = result;
3190 }
3191 break;
3192
3193 case _T('d'): // day of a month (01-31)
3194 if ( !GetNumericToken(width, input, &num) ||
3195 (num > 31) || (num < 1) )
3196 {
3197 // no match
3198 return (wxChar *)NULL;
3199 }
3200
3201 // we can't check whether the day range is correct yet, will
3202 // do it later - assume ok for now
3203 haveDay = true;
3204 mday = (wxDateTime_t)num;
3205 break;
3206
3207 case _T('H'): // hour in 24h format (00-23)
3208 if ( !GetNumericToken(width, input, &num) || (num > 23) )
3209 {
3210 // no match
3211 return (wxChar *)NULL;
3212 }
3213
3214 haveHour = true;
3215 hour = (wxDateTime_t)num;
3216 break;
3217
3218 case _T('I'): // hour in 12h format (01-12)
3219 if ( !GetNumericToken(width, input, &num) || !num || (num > 12) )
3220 {
3221 // no match
3222 return (wxChar *)NULL;
3223 }
3224
3225 haveHour = true;
3226 hourIsIn12hFormat = true;
3227 hour = (wxDateTime_t)(num % 12); // 12 should be 0
3228 break;
3229
3230 case _T('j'): // day of the year
3231 if ( !GetNumericToken(width, input, &num) || !num || (num > 366) )
3232 {
3233 // no match
3234 return (wxChar *)NULL;
3235 }
3236
3237 haveYDay = true;
3238 yday = (wxDateTime_t)num;
3239 break;
3240
3241 case _T('m'): // month as a number (01-12)
3242 if ( !GetNumericToken(width, input, &num) || !num || (num > 12) )
3243 {
3244 // no match
3245 return (wxChar *)NULL;
3246 }
3247
3248 haveMon = true;
3249 mon = (Month)(num - 1);
3250 break;
3251
3252 case _T('M'): // minute as a decimal number (00-59)
3253 if ( !GetNumericToken(width, input, &num) || (num > 59) )
3254 {
3255 // no match
3256 return (wxChar *)NULL;
3257 }
3258
3259 haveMin = true;
3260 min = (wxDateTime_t)num;
3261 break;
3262
3263 case _T('p'): // AM or PM string
3264 {
3265 wxString am, pm, token = GetAlphaToken(input);
3266
3267 GetAmPmStrings(&am, &pm);
3268 if (am.empty() && pm.empty())
3269 return (wxChar *)NULL; // no am/pm strings defined
3270 if ( token.CmpNoCase(pm) == 0 )
3271 {
3272 isPM = true;
3273 }
3274 else if ( token.CmpNoCase(am) != 0 )
3275 {
3276 // no match
3277 return (wxChar *)NULL;
3278 }
3279 }
3280 break;
3281
3282 case _T('r'): // time as %I:%M:%S %p
3283 {
3284 wxDateTime dt;
3285 input = dt.ParseFormat(input, _T("%I:%M:%S %p"));
3286 if ( !input )
3287 {
3288 // no match
3289 return (wxChar *)NULL;
3290 }
3291
3292 haveHour = haveMin = haveSec = true;
3293
3294 Tm tm = dt.GetTm();
3295 hour = tm.hour;
3296 min = tm.min;
3297 sec = tm.sec;
3298 }
3299 break;
3300
3301 case _T('R'): // time as %H:%M
3302 {
3303 wxDateTime dt;
3304 input = dt.ParseFormat(input, _T("%H:%M"));
3305 if ( !input )
3306 {
3307 // no match
3308 return (wxChar *)NULL;
3309 }
3310
3311 haveHour = haveMin = true;
3312
3313 Tm tm = dt.GetTm();
3314 hour = tm.hour;
3315 min = tm.min;
3316 }
3317 break;
3318
3319 case _T('S'): // second as a decimal number (00-61)
3320 if ( !GetNumericToken(width, input, &num) || (num > 61) )
3321 {
3322 // no match
3323 return (wxChar *)NULL;
3324 }
3325
3326 haveSec = true;
3327 sec = (wxDateTime_t)num;
3328 break;
3329
3330 case _T('T'): // time as %H:%M:%S
3331 {
3332 wxDateTime dt;
3333 input = dt.ParseFormat(input, _T("%H:%M:%S"));
3334 if ( !input )
3335 {
3336 // no match
3337 return (wxChar *)NULL;
3338 }
3339
3340 haveHour = haveMin = haveSec = true;
3341
3342 Tm tm = dt.GetTm();
3343 hour = tm.hour;
3344 min = tm.min;
3345 sec = tm.sec;
3346 }
3347 break;
3348
3349 case _T('w'): // weekday as a number (0-6), Sunday = 0
3350 if ( !GetNumericToken(width, input, &num) || (wday > 6) )
3351 {
3352 // no match
3353 return (wxChar *)NULL;
3354 }
3355
3356 haveWDay = true;
3357 wday = (WeekDay)num;
3358 break;
3359
3360 case _T('x'): // locale default date representation
3361#ifdef HAVE_STRPTIME
3362 // try using strptime() -- it may fail even if the input is
3363 // correct but the date is out of range, so we will fall back
3364 // to our generic code anyhow
3365 {
3366 struct tm tm;
3367
3368 const wxChar *result = CallStrptime(input, "%x", &tm);
3369 if ( result )
3370 {
3371 input = result;
3372
3373 haveDay = haveMon = haveYear = true;
3374
3375 year = 1900 + tm.tm_year;
3376 mon = (Month)tm.tm_mon;
3377 mday = tm.tm_mday;
3378
3379 break;
3380 }
3381 }
3382#endif // HAVE_STRPTIME
3383
3384 {
3385 wxDateTime dt;
3386 wxString fmtDate,
3387 fmtDateAlt;
3388
3389#ifdef __WINDOWS__
3390 // The above doesn't work for all locales, try to query
3391 // Windows for the right way of formatting the date:
3392 fmtDate = GetLocaleDateFormat();
3393 if ( fmtDate.empty() )
3394#endif
3395 {
3396 if ( IsWestEuropeanCountry(GetCountry()) ||
3397 GetCountry() == Russia )
3398 {
3399 fmtDate = _T("%d/%m/%y");
3400 fmtDateAlt = _T("%m/%d/%y");
3401 }
3402 else // assume USA
3403 {
3404 fmtDate = _T("%m/%d/%y");
3405 fmtDateAlt = _T("%d/%m/%y");
3406 }
3407 }
3408
3409 const wxChar *result = dt.ParseFormat(input, fmtDate);
3410
3411 if ( !result && !fmtDateAlt.empty() )
3412 {
3413 // ok, be nice and try another one
3414 result = dt.ParseFormat(input, fmtDateAlt);
3415 }
3416
3417 if ( !result )
3418 {
3419 // bad luck
3420 return (wxChar *)NULL;
3421 }
3422
3423 Tm tm = dt.GetTm();
3424
3425 haveDay = haveMon = haveYear = true;
3426
3427 year = tm.year;
3428 mon = tm.mon;
3429 mday = tm.mday;
3430
3431 input = result;
3432 }
3433
3434 break;
3435
3436 case _T('X'): // locale default time representation
3437#ifdef HAVE_STRPTIME
3438 {
3439 // use strptime() to do it for us (FIXME !Unicode friendly)
3440 struct tm tm;
3441 input = CallStrptime(input, "%X", &tm);
3442 if ( !input )
3443 {
3444 return (wxChar *)NULL;
3445 }
3446
3447 haveHour = haveMin = haveSec = true;
3448
3449 hour = tm.tm_hour;
3450 min = tm.tm_min;
3451 sec = tm.tm_sec;
3452 }
3453#else // !HAVE_STRPTIME
3454 // TODO under Win32 we can query the LOCALE_ITIME system
3455 // setting which says whether the default time format is
3456 // 24 or 12 hour
3457 {
3458 // try to parse what follows as "%H:%M:%S" and, if this
3459 // fails, as "%I:%M:%S %p" - this should catch the most
3460 // common cases
3461 wxDateTime dt;
3462
3463 const wxChar *result = dt.ParseFormat(input, _T("%T"));
3464 if ( !result )
3465 {
3466 result = dt.ParseFormat(input, _T("%r"));
3467 }
3468
3469 if ( !result )
3470 {
3471 // no match
3472 return (wxChar *)NULL;
3473 }
3474
3475 haveHour = haveMin = haveSec = true;
3476
3477 Tm tm = dt.GetTm();
3478 hour = tm.hour;
3479 min = tm.min;
3480 sec = tm.sec;
3481
3482 input = result;
3483 }
3484#endif // HAVE_STRPTIME/!HAVE_STRPTIME
3485 break;
3486
3487 case _T('y'): // year without century (00-99)
3488 if ( !GetNumericToken(width, input, &num) || (num > 99) )
3489 {
3490 // no match
3491 return (wxChar *)NULL;
3492 }
3493
3494 haveYear = true;
3495
3496 // TODO should have an option for roll over date instead of
3497 // hard coding it here
3498 year = (num > 30 ? 1900 : 2000) + (wxDateTime_t)num;
3499 break;
3500
3501 case _T('Y'): // year with century
3502 if ( !GetNumericToken(width, input, &num) )
3503 {
3504 // no match
3505 return (wxChar *)NULL;
3506 }
3507
3508 haveYear = true;
3509 year = (wxDateTime_t)num;
3510 break;
3511
3512 case _T('Z'): // timezone name
3513 wxFAIL_MSG(_T("TODO"));
3514 break;
3515
3516 case _T('%'): // a percent sign
3517 if ( *input++ != _T('%') )
3518 {
3519 // no match
3520 return (wxChar *)NULL;
3521 }
3522 break;
3523
3524 case 0: // the end of string
3525 wxFAIL_MSG(_T("unexpected format end"));
3526
3527 // fall through
3528
3529 default: // not a known format spec
3530 return (wxChar *)NULL;
3531 }
3532 }
3533
3534 // format matched, try to construct a date from what we have now
3535 Tm tmDef;
3536 if ( dateDef.IsValid() )
3537 {
3538 // take this date as default
3539 tmDef = dateDef.GetTm();
3540 }
3541 else if ( IsValid() )
3542 {
3543 // if this date is valid, don't change it
3544 tmDef = GetTm();
3545 }
3546 else
3547 {
3548 // no default and this date is invalid - fall back to Today()
3549 tmDef = Today().GetTm();
3550 }
3551
3552 Tm tm = tmDef;
3553
3554 // set the date
3555 if ( haveYear )
3556 {
3557 tm.year = year;
3558 }
3559
3560 // TODO we don't check here that the values are consistent, if both year
3561 // day and month/day were found, we just ignore the year day and we
3562 // also always ignore the week day
3563 if ( haveMon && haveDay )
3564 {
3565 if ( mday > GetNumOfDaysInMonth(tm.year, mon) )
3566 {
3567 wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
3568
3569 return (wxChar *)NULL;
3570 }
3571
3572 tm.mon = mon;
3573 tm.mday = mday;
3574 }
3575 else if ( haveYDay )
3576 {
3577 if ( yday > GetNumberOfDays(tm.year) )
3578 {
3579 wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
3580
3581 return (wxChar *)NULL;
3582 }
3583
3584 Tm tm2 = wxDateTime(1, Jan, tm.year).SetToYearDay(yday).GetTm();
3585
3586 tm.mon = tm2.mon;
3587 tm.mday = tm2.mday;
3588 }
3589
3590 // deal with AM/PM
3591 if ( haveHour && hourIsIn12hFormat && isPM )
3592 {
3593 // translate to 24hour format
3594 hour += 12;
3595 }
3596 //else: either already in 24h format or no translation needed
3597
3598 // set the time
3599 if ( haveHour )
3600 {
3601 tm.hour = hour;
3602 }
3603
3604 if ( haveMin )
3605 {
3606 tm.min = min;
3607 }
3608
3609 if ( haveSec )
3610 {
3611 tm.sec = sec;
3612 }
3613
3614 Set(tm);
3615
3616 // finally check that the week day is consistent -- if we had it
3617 if ( haveWDay && GetWeekDay() != wday )
3618 {
3619 wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
3620
3621 return NULL;
3622 }
3623
3624 return input;
3625}
3626
3627const wxChar *wxDateTime::ParseDateTime(const wxChar *date)
3628{
3629 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
3630
3631 // Set to current day and hour, so strings like '14:00' becomes today at
3632 // 14, not some other random date
3633 wxDateTime dtDate = wxDateTime::Today();
3634 wxDateTime dtTime = wxDateTime::Today();
3635
3636 const wxChar* pchTime;
3637
3638 // Try to parse the beginning of the string as a date
3639 const wxChar* pchDate = dtDate.ParseDate(date);
3640
3641 // We got a date in the beginning, see if there is a time specified after the date
3642 if ( pchDate )
3643 {
3644 // Skip spaces, as the ParseTime() function fails on spaces
3645 while ( wxIsspace(*pchDate) )
3646 pchDate++;
3647
3648 pchTime = dtTime.ParseTime(pchDate);
3649 }
3650 else // no date in the beginning
3651 {
3652 // check and see if we have a time followed by a date
3653 pchTime = dtTime.ParseTime(date);
3654 if ( pchTime )
3655 {
3656 while ( wxIsspace(*pchTime) )
3657 pchTime++;
3658
3659 pchDate = dtDate.ParseDate(pchTime);
3660 }
3661 }
3662
3663 // If we have a date specified, set our own data to the same date
3664 if ( !pchDate || !pchTime )
3665 return NULL;
3666
3667 Set(dtDate.GetDay(), dtDate.GetMonth(), dtDate.GetYear(),
3668 dtTime.GetHour(), dtTime.GetMinute(), dtTime.GetSecond(),
3669 dtTime.GetMillisecond());
3670
3671 // Return endpoint of scan
3672 return pchDate > pchTime ? pchDate : pchTime;
3673}
3674
3675const wxChar *wxDateTime::ParseDate(const wxChar *date)
3676{
3677 // this is a simplified version of ParseDateTime() which understands only
3678 // "today" (for wxDate compatibility) and digits only otherwise (and not
3679 // all esoteric constructions ParseDateTime() knows about)
3680
3681 wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
3682
3683 const wxChar *p = date;
3684 while ( wxIsspace(*p) )
3685 p++;
3686
3687 // some special cases
3688 static struct
3689 {
3690 const wxChar *str;
3691 int dayDiffFromToday;
3692 } literalDates[] =
3693 {
3694 { wxTRANSLATE("today"), 0 },
3695 { wxTRANSLATE("yesterday"), -1 },
3696 { wxTRANSLATE("tomorrow"), 1 },
3697 };
3698
3699 for ( size_t n = 0; n < WXSIZEOF(literalDates); n++ )
3700 {
3701 wxString date = wxGetTranslation(literalDates[n].str);
3702 size_t len = date.length();
3703 if ( wxStrlen(p) >= len )
3704 {
3705 wxString str(p, len);
3706 if ( str.CmpNoCase(date) == 0 )
3707 {
3708 // nothing can follow this, so stop here
3709 p += len;
3710
3711 int dayDiffFromToday = literalDates[n].dayDiffFromToday;
3712 *this = Today();
3713 if ( dayDiffFromToday )
3714 {
3715 *this += wxDateSpan::Days(dayDiffFromToday);
3716 }
3717
3718 return p;
3719 }
3720 }
3721 }
3722
3723 // We try to guess what we have here: for each new (numeric) token, we
3724 // determine if it can be a month, day or a year. Of course, there is an
3725 // ambiguity as some numbers may be days as well as months, so we also
3726 // have the ability to back track.
3727
3728 // what do we have?
3729 bool haveDay = false, // the months day?
3730 haveWDay = false, // the day of week?
3731 haveMon = false, // the month?
3732 haveYear = false; // the year?
3733
3734 // and the value of the items we have (init them to get rid of warnings)
3735 WeekDay wday = Inv_WeekDay;
3736 wxDateTime_t day = 0;
3737 wxDateTime::Month mon = Inv_Month;
3738 int year = 0;
3739
3740 // tokenize the string
3741 size_t nPosCur = 0;
3742 static const wxChar *dateDelimiters = _T(".,/-\t\r\n ");
3743 wxStringTokenizer tok(p, dateDelimiters);
3744 while ( tok.HasMoreTokens() )
3745 {
3746 wxString token = tok.GetNextToken();
3747 if ( !token )
3748 continue;
3749
3750 // is it a number?
3751 unsigned long val;
3752 if ( token.ToULong(&val) )
3753 {
3754 // guess what this number is
3755
3756 bool isDay = false,
3757 isMonth = false,
3758 isYear = false;
3759
3760 if ( !haveMon && val > 0 && val <= 12 )
3761 {
3762 // assume it is month
3763 isMonth = true;
3764 }
3765 else // not the month
3766 {
3767 if ( haveDay )
3768 {
3769 // this can only be the year
3770 isYear = true;
3771 }
3772 else // may be either day or year
3773 {
3774 wxDateTime_t max_days = (wxDateTime_t)(
3775 haveMon
3776 ? GetNumOfDaysInMonth(haveYear ? year : Inv_Year, mon)
3777 : 31
3778 );
3779
3780 // can it be day?
3781 if ( (val == 0) || (val > (unsigned long)max_days) )
3782 {
3783 // no
3784 isYear = true;
3785 }
3786 else // yes, suppose it's the day
3787 {
3788 isDay = true;
3789 }
3790 }
3791 }
3792
3793 if ( isYear )
3794 {
3795 if ( haveYear )
3796 break;
3797
3798 haveYear = true;
3799
3800 year = (wxDateTime_t)val;
3801 }
3802 else if ( isDay )
3803 {
3804 if ( haveDay )
3805 break;
3806
3807 haveDay = true;
3808
3809 day = (wxDateTime_t)val;
3810 }
3811 else if ( isMonth )
3812 {
3813 haveMon = true;
3814
3815 mon = (Month)(val - 1);
3816 }
3817 }
3818 else // not a number
3819 {
3820 // be careful not to overwrite the current mon value
3821 Month mon2 = GetMonthFromName(token, Name_Full | Name_Abbr);
3822 if ( mon2 != Inv_Month )
3823 {
3824 // it's a month
3825 if ( haveMon )
3826 {
3827 // but we already have a month - maybe we guessed wrong?
3828 if ( !haveDay )
3829 {
3830 // no need to check in month range as always < 12, but
3831 // the days are counted from 1 unlike the months
3832 day = (wxDateTime_t)(mon + 1);
3833 haveDay = true;
3834 }
3835 else
3836 {
3837 // could possible be the year (doesn't the year come
3838 // before the month in the japanese format?) (FIXME)
3839 break;
3840 }
3841 }
3842
3843 mon = mon2;
3844
3845 haveMon = true;
3846 }
3847 else // not a valid month name
3848 {
3849 wday = GetWeekDayFromName(token, Name_Full | Name_Abbr);
3850 if ( wday != Inv_WeekDay )
3851 {
3852 // a week day
3853 if ( haveWDay )
3854 {
3855 break;
3856 }
3857
3858 haveWDay = true;
3859 }
3860 else // not a valid weekday name
3861 {
3862 // try the ordinals
3863 static const wxChar *ordinals[] =
3864 {
3865 wxTRANSLATE("first"),
3866 wxTRANSLATE("second"),
3867 wxTRANSLATE("third"),
3868 wxTRANSLATE("fourth"),
3869 wxTRANSLATE("fifth"),
3870 wxTRANSLATE("sixth"),
3871 wxTRANSLATE("seventh"),
3872 wxTRANSLATE("eighth"),
3873 wxTRANSLATE("ninth"),
3874 wxTRANSLATE("tenth"),
3875 wxTRANSLATE("eleventh"),
3876 wxTRANSLATE("twelfth"),
3877 wxTRANSLATE("thirteenth"),
3878 wxTRANSLATE("fourteenth"),
3879 wxTRANSLATE("fifteenth"),
3880 wxTRANSLATE("sixteenth"),
3881 wxTRANSLATE("seventeenth"),
3882 wxTRANSLATE("eighteenth"),
3883 wxTRANSLATE("nineteenth"),
3884 wxTRANSLATE("twentieth"),
3885 // that's enough - otherwise we'd have problems with
3886 // composite (or not) ordinals
3887 };
3888
3889 size_t n;
3890 for ( n = 0; n < WXSIZEOF(ordinals); n++ )
3891 {
3892 if ( token.CmpNoCase(ordinals[n]) == 0 )
3893 {
3894 break;
3895 }
3896 }
3897
3898 if ( n == WXSIZEOF(ordinals) )
3899 {
3900 // stop here - something unknown
3901 break;
3902 }
3903
3904 // it's a day
3905 if ( haveDay )
3906 {
3907 // don't try anything here (as in case of numeric day
3908 // above) - the symbolic day spec should always
3909 // precede the month/year
3910 break;
3911 }
3912
3913 haveDay = true;
3914
3915 day = (wxDateTime_t)(n + 1);
3916 }
3917 }
3918 }
3919
3920 nPosCur = tok.GetPosition();
3921 }
3922
3923 // either no more tokens or the scan was stopped by something we couldn't
3924 // parse - in any case, see if we can construct a date from what we have
3925 if ( !haveDay && !haveWDay )
3926 {
3927 wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
3928
3929 return (wxChar *)NULL;
3930 }
3931
3932 if ( haveWDay && (haveMon || haveYear || haveDay) &&
3933 !(haveDay && haveMon && haveYear) )
3934 {
3935 // without adjectives (which we don't support here) the week day only
3936 // makes sense completely separately or with the full date
3937 // specification (what would "Wed 1999" mean?)
3938 return (wxChar *)NULL;
3939 }
3940
3941 if ( !haveWDay && haveYear && !(haveDay && haveMon) )
3942 {
3943 // may be we have month and day instead of day and year?
3944 if ( haveDay && !haveMon )
3945 {
3946 if ( day <= 12 )
3947 {
3948 // exchange day and month
3949 mon = (wxDateTime::Month)(day - 1);
3950
3951 // we're in the current year then
3952 if ( (year > 0) && (year <= (int)GetNumOfDaysInMonth(Inv_Year, mon)) )
3953 {
3954 day = (wxDateTime_t)year;
3955
3956 haveMon = true;
3957 haveYear = false;
3958 }
3959 //else: no, can't exchange, leave haveMon == false
3960 }
3961 }
3962
3963 if ( !haveMon )
3964 {
3965 // if we give the year, month and day must be given too
3966 wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
3967
3968 return (wxChar *)NULL;
3969 }
3970 }
3971
3972 if ( !haveMon )
3973 {
3974 mon = GetCurrentMonth();
3975 }
3976
3977 if ( !haveYear )
3978 {
3979 year = GetCurrentYear();
3980 }
3981
3982 if ( haveDay )
3983 {
3984 Set(day, mon, year);
3985
3986 if ( haveWDay )
3987 {
3988 // check that it is really the same
3989 if ( GetWeekDay() != wday )
3990 {
3991 // inconsistency detected
3992 wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
3993
3994 return (wxChar *)NULL;
3995 }
3996 }
3997 }
3998 else // haveWDay
3999 {
4000 *this = Today();
4001
4002 SetToWeekDayInSameWeek(wday);
4003 }
4004
4005 // return the pointer to the first unparsed char
4006 p += nPosCur;
4007 if ( nPosCur && wxStrchr(dateDelimiters, *(p - 1)) )
4008 {
4009 // if we couldn't parse the token after the delimiter, put back the
4010 // delimiter as well
4011 p--;
4012 }
4013
4014 return p;
4015}
4016
4017const wxChar *wxDateTime::ParseTime(const wxChar *time)
4018{
4019 wxCHECK_MSG( time, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
4020
4021 // first try some extra things
4022 static const struct
4023 {
4024 const wxChar *name;
4025 wxDateTime_t hour;
4026 } stdTimes[] =
4027 {
4028 { wxTRANSLATE("noon"), 12 },
4029 { wxTRANSLATE("midnight"), 00 },
4030 // anything else?
4031 };
4032
4033 for ( size_t n = 0; n < WXSIZEOF(stdTimes); n++ )
4034 {
4035 wxString timeString = wxGetTranslation(stdTimes[n].name);
4036 size_t len = timeString.length();
4037 if ( timeString.CmpNoCase(wxString(time, len)) == 0 )
4038 {
4039 // casts required by DigitalMars
4040 Set(stdTimes[n].hour, wxDateTime_t(0), wxDateTime_t(0));
4041
4042 return time + len;
4043 }
4044 }
4045
4046 // try all time formats we may think about in the order from longest to
4047 // shortest
4048
4049 // 12hour with AM/PM?
4050 const wxChar *result = ParseFormat(time, _T("%I:%M:%S %p"));
4051
4052 if ( !result )
4053 {
4054 // normally, it's the same, but why not try it?
4055 result = ParseFormat(time, _T("%H:%M:%S"));
4056 }
4057
4058 if ( !result )
4059 {
4060 // 12hour with AM/PM but without seconds?
4061 result = ParseFormat(time, _T("%I:%M %p"));
4062 }
4063
4064 if ( !result )
4065 {
4066 // without seconds?
4067 result = ParseFormat(time, _T("%H:%M"));
4068 }
4069
4070 if ( !result )
4071 {
4072 // just the hour and AM/PM?
4073 result = ParseFormat(time, _T("%I %p"));
4074 }
4075
4076 if ( !result )
4077 {
4078 // just the hour?
4079 result = ParseFormat(time, _T("%H"));
4080 }
4081
4082 if ( !result )
4083 {
4084 // parse the standard format: normally it is one of the formats above
4085 // but it may be set to something completely different by the user
4086 result = ParseFormat(time, _T("%X"));
4087 }
4088
4089 // TODO: parse timezones
4090
4091 return result;
4092}
4093
4094// ----------------------------------------------------------------------------
4095// Workdays and holidays support
4096// ----------------------------------------------------------------------------
4097
4098bool wxDateTime::IsWorkDay(Country WXUNUSED(country)) const
4099{
4100 return !wxDateTimeHolidayAuthority::IsHoliday(*this);
4101}
4102
4103// ============================================================================
4104// wxDateSpan
4105// ============================================================================
4106
4107wxDateSpan WXDLLIMPEXP_BASE operator*(int n, const wxDateSpan& ds)
4108{
4109 wxDateSpan ds1(ds);
4110 return ds1.Multiply(n);
4111}
4112
4113// ============================================================================
4114// wxTimeSpan
4115// ============================================================================
4116
4117wxTimeSpan WXDLLIMPEXP_BASE operator*(int n, const wxTimeSpan& ts)
4118{
4119 return wxTimeSpan(ts).Multiply(n);
4120}
4121
4122// this enum is only used in wxTimeSpan::Format() below but we can't declare
4123// it locally to the method as it provokes an internal compiler error in egcs
4124// 2.91.60 when building with -O2
4125enum TimeSpanPart
4126{
4127 Part_Week,
4128 Part_Day,
4129 Part_Hour,
4130 Part_Min,
4131 Part_Sec,
4132 Part_MSec
4133};
4134
4135// not all strftime(3) format specifiers make sense here because, for example,
4136// a time span doesn't have a year nor a timezone
4137//
4138// Here are the ones which are supported (all of them are supported by strftime
4139// as well):
4140// %H hour in 24 hour format
4141// %M minute (00 - 59)
4142// %S second (00 - 59)
4143// %% percent sign
4144//
4145// Also, for MFC CTimeSpan compatibility, we support
4146// %D number of days
4147//
4148// And, to be better than MFC :-), we also have
4149// %E number of wEeks
4150// %l milliseconds (000 - 999)
4151wxString wxTimeSpan::Format(const wxChar *format) const
4152{
4153 wxCHECK_MSG( format, wxEmptyString, _T("NULL format in wxTimeSpan::Format") );
4154
4155 wxString str;
4156 str.Alloc(wxStrlen(format));
4157
4158 // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
4159 //
4160 // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
4161 // question is what should ts.Format("%S") do? The code here returns "3273"
4162 // in this case (i.e. the total number of seconds, not just seconds % 60)
4163 // because, for me, this call means "give me entire time interval in
4164 // seconds" and not "give me the seconds part of the time interval"
4165 //
4166 // If we agree that it should behave like this, it is clear that the
4167 // interpretation of each format specifier depends on the presence of the
4168 // other format specs in the string: if there was "%H" before "%M", we
4169 // should use GetMinutes() % 60, otherwise just GetMinutes() &c
4170
4171 // we remember the most important unit found so far
4172 TimeSpanPart partBiggest = Part_MSec;
4173
4174 for ( const wxChar *pch = format; *pch; pch++ )
4175 {
4176 wxChar ch = *pch;
4177
4178 if ( ch == _T('%') )
4179 {
4180 // the start of the format specification of the printf() below
4181 wxString fmtPrefix = _T('%');
4182
4183 // the number
4184 long n;
4185
4186 ch = *++pch; // get the format spec char
4187 switch ( ch )
4188 {
4189 default:
4190 wxFAIL_MSG( _T("invalid format character") );
4191 // fall through
4192
4193 case _T('%'):
4194 str += ch;
4195
4196 // skip the part below switch
4197 continue;
4198
4199 case _T('D'):
4200 n = GetDays();
4201 if ( partBiggest < Part_Day )
4202 {
4203 n %= DAYS_PER_WEEK;
4204 }
4205 else
4206 {
4207 partBiggest = Part_Day;
4208 }
4209 break;
4210
4211 case _T('E'):
4212 partBiggest = Part_Week;
4213 n = GetWeeks();
4214 break;
4215
4216 case _T('H'):
4217 n = GetHours();
4218 if ( partBiggest < Part_Hour )
4219 {
4220 n %= HOURS_PER_DAY;
4221 }
4222 else
4223 {
4224 partBiggest = Part_Hour;
4225 }
4226
4227 fmtPrefix += _T("02");
4228 break;
4229
4230 case _T('l'):
4231 n = GetMilliseconds().ToLong();
4232 if ( partBiggest < Part_MSec )
4233 {
4234 n %= 1000;
4235 }
4236 //else: no need to reset partBiggest to Part_MSec, it is
4237 // the least significant one anyhow
4238
4239 fmtPrefix += _T("03");
4240 break;
4241
4242 case _T('M'):
4243 n = GetMinutes();
4244 if ( partBiggest < Part_Min )
4245 {
4246 n %= MIN_PER_HOUR;
4247 }
4248 else
4249 {
4250 partBiggest = Part_Min;
4251 }
4252
4253 fmtPrefix += _T("02");
4254 break;
4255
4256 case _T('S'):
4257 n = GetSeconds().ToLong();
4258 if ( partBiggest < Part_Sec )
4259 {
4260 n %= SEC_PER_MIN;
4261 }
4262 else
4263 {
4264 partBiggest = Part_Sec;
4265 }
4266
4267 fmtPrefix += _T("02");
4268 break;
4269 }
4270
4271 str += wxString::Format(fmtPrefix + _T("ld"), n);
4272 }
4273 else
4274 {
4275 // normal character, just copy
4276 str += ch;
4277 }
4278 }
4279
4280 return str;
4281}
4282
4283// ============================================================================
4284// wxDateTimeHolidayAuthority and related classes
4285// ============================================================================
4286
4287#include "wx/arrimpl.cpp"
4288
4289WX_DEFINE_OBJARRAY(wxDateTimeArray);
4290
4291static int wxCMPFUNC_CONV
4292wxDateTimeCompareFunc(wxDateTime **first, wxDateTime **second)
4293{
4294 wxDateTime dt1 = **first,
4295 dt2 = **second;
4296
4297 return dt1 == dt2 ? 0 : dt1 < dt2 ? -1 : +1;
4298}
4299
4300// ----------------------------------------------------------------------------
4301// wxDateTimeHolidayAuthority
4302// ----------------------------------------------------------------------------
4303
4304wxHolidayAuthoritiesArray wxDateTimeHolidayAuthority::ms_authorities;
4305
4306/* static */
4307bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime& dt)
4308{
4309 size_t count = ms_authorities.size();
4310 for ( size_t n = 0; n < count; n++ )
4311 {
4312 if ( ms_authorities[n]->DoIsHoliday(dt) )
4313 {
4314 return true;
4315 }
4316 }
4317
4318 return false;
4319}
4320
4321/* static */
4322size_t
4323wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime& dtStart,
4324 const wxDateTime& dtEnd,
4325 wxDateTimeArray& holidays)
4326{
4327 wxDateTimeArray hol;
4328
4329 holidays.Clear();
4330
4331 size_t count = ms_authorities.size();
4332 for ( size_t nAuth = 0; nAuth < count; nAuth++ )
4333 {
4334 ms_authorities[nAuth]->DoGetHolidaysInRange(dtStart, dtEnd, hol);
4335
4336 WX_APPEND_ARRAY(holidays, hol);
4337 }
4338
4339 holidays.Sort(wxDateTimeCompareFunc);
4340
4341 return holidays.size();
4342}
4343
4344/* static */
4345void wxDateTimeHolidayAuthority::ClearAllAuthorities()
4346{
4347 WX_CLEAR_ARRAY(ms_authorities);
4348}
4349
4350/* static */
4351void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority *auth)
4352{
4353 ms_authorities.push_back(auth);
4354}
4355
4356wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
4357{
4358 // required here for Darwin
4359}
4360
4361// ----------------------------------------------------------------------------
4362// wxDateTimeWorkDays
4363// ----------------------------------------------------------------------------
4364
4365bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime& dt) const
4366{
4367 wxDateTime::WeekDay wd = dt.GetWeekDay();
4368
4369 return (wd == wxDateTime::Sun) || (wd == wxDateTime::Sat);
4370}
4371
4372size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime& dtStart,
4373 const wxDateTime& dtEnd,
4374 wxDateTimeArray& holidays) const
4375{
4376 if ( dtStart > dtEnd )
4377 {
4378 wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
4379
4380 return 0u;
4381 }
4382
4383 holidays.Empty();
4384
4385 // instead of checking all days, start with the first Sat after dtStart and
4386 // end with the last Sun before dtEnd
4387 wxDateTime dtSatFirst = dtStart.GetNextWeekDay(wxDateTime::Sat),
4388 dtSatLast = dtEnd.GetPrevWeekDay(wxDateTime::Sat),
4389 dtSunFirst = dtStart.GetNextWeekDay(wxDateTime::Sun),
4390 dtSunLast = dtEnd.GetPrevWeekDay(wxDateTime::Sun),
4391 dt;
4392
4393 for ( dt = dtSatFirst; dt <= dtSatLast; dt += wxDateSpan::Week() )
4394 {
4395 holidays.Add(dt);
4396 }
4397
4398 for ( dt = dtSunFirst; dt <= dtSunLast; dt += wxDateSpan::Week() )
4399 {
4400 holidays.Add(dt);
4401 }
4402
4403 return holidays.GetCount();
4404}
4405
4406// ============================================================================
4407// other helper functions
4408// ============================================================================
4409
4410// ----------------------------------------------------------------------------
4411// iteration helpers: can be used to write a for loop over enum variable like
4412// this:
4413// for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
4414// ----------------------------------------------------------------------------
4415
4416WXDLLIMPEXP_BASE void wxNextMonth(wxDateTime::Month& m)
4417{
4418 wxASSERT_MSG( m < wxDateTime::Inv_Month, _T("invalid month") );
4419
4420 // no wrapping or the for loop above would never end!
4421 m = (wxDateTime::Month)(m + 1);
4422}
4423
4424WXDLLIMPEXP_BASE void wxPrevMonth(wxDateTime::Month& m)
4425{
4426 wxASSERT_MSG( m < wxDateTime::Inv_Month, _T("invalid month") );
4427
4428 m = m == wxDateTime::Jan ? wxDateTime::Inv_Month
4429 : (wxDateTime::Month)(m - 1);
4430}
4431
4432WXDLLIMPEXP_BASE void wxNextWDay(wxDateTime::WeekDay& wd)
4433{
4434 wxASSERT_MSG( wd < wxDateTime::Inv_WeekDay, _T("invalid week day") );
4435
4436 // no wrapping or the for loop above would never end!
4437 wd = (wxDateTime::WeekDay)(wd + 1);
4438}
4439
4440WXDLLIMPEXP_BASE void wxPrevWDay(wxDateTime::WeekDay& wd)
4441{
4442 wxASSERT_MSG( wd < wxDateTime::Inv_WeekDay, _T("invalid week day") );
4443
4444 wd = wd == wxDateTime::Sun ? wxDateTime::Inv_WeekDay
4445 : (wxDateTime::WeekDay)(wd - 1);
4446}
4447
4448#endif // wxUSE_DATETIME