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