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