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