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