1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/common/datetime.cpp
3 // Purpose:     implementation of time/date related classes
4 //              (for formatting&parsing see datetimefmt.cpp)
5 // Author:      Vadim Zeitlin
6 // Modified by:
7 // Created:     11.05.99
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 // For compilers that support precompilation, includes "wx.h".
56 #include "wx/wxprec.h"
57 
58 
59 #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
60 
61 #ifndef WX_PRECOMP
62     #ifdef __WINDOWS__
63         #include "wx/msw/wrapwin.h"
64     #endif
65     #include "wx/string.h"
66     #include "wx/log.h"
67     #include "wx/intl.h"
68     #include "wx/stopwatch.h"           // for wxGetLocalTimeMillis()
69     #include "wx/module.h"
70     #include "wx/crt.h"
71 #endif // WX_PRECOMP
72 
73 #include "wx/thread.h"
74 #include "wx/time.h"
75 #include "wx/tokenzr.h"
76 
77 #include <ctype.h>
78 
79 #ifdef __WINDOWS__
80     #include <winnls.h>
81     #include <locale.h>
82 #endif
83 
84 #include "wx/datetime.h"
85 
86 // ----------------------------------------------------------------------------
87 // wxXTI
88 // ----------------------------------------------------------------------------
89 
90 #if wxUSE_EXTENDED_RTTI
91 
wxStringReadValue(const wxString & s,wxDateTime & data)92 template<> void wxStringReadValue(const wxString &s , wxDateTime &data )
93 {
94     data.ParseFormat(s,"%Y-%m-%d %H:%M:%S", NULL);
95 }
96 
wxStringWriteValue(wxString & s,const wxDateTime & data)97 template<> void wxStringWriteValue(wxString &s , const wxDateTime &data )
98 {
99     s = data.Format("%Y-%m-%d %H:%M:%S");
100 }
101 
102 wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter<wxDateTime> , wxFromStringConverter<wxDateTime>)
103 
104 #endif // wxUSE_EXTENDED_RTTI
105 
106 // ----------------------------------------------------------------------------
107 // macros
108 // ----------------------------------------------------------------------------
109 
110 // debugging helper: just a convenient replacement of wxCHECK()
111 #define wxDATETIME_CHECK(expr, msg) \
112     wxCHECK2_MSG(expr, *this = wxInvalidDateTime; return *this, msg)
113 
114 // ----------------------------------------------------------------------------
115 // private classes
116 // ----------------------------------------------------------------------------
117 
118 class wxDateTimeHolidaysModule : public wxModule
119 {
120 public:
OnInit()121     virtual bool OnInit() wxOVERRIDE
122     {
123         wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays);
124 
125         return true;
126     }
127 
OnExit()128     virtual void OnExit() wxOVERRIDE
129     {
130         wxDateTimeHolidayAuthority::ClearAllAuthorities();
131         wxDateTimeHolidayAuthority::ms_authorities.clear();
132     }
133 
134 private:
135     wxDECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule);
136 };
137 
138 wxIMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule, wxModule);
139 
140 // ----------------------------------------------------------------------------
141 // constants
142 // ----------------------------------------------------------------------------
143 
144 // some trivial ones
145 static const int MONTHS_IN_YEAR = 12;
146 
147 static const int SEC_PER_MIN = 60;
148 
149 static const int MIN_PER_HOUR = 60;
150 
151 static const long SECONDS_PER_DAY = 86400l;
152 
153 static const int DAYS_PER_WEEK = 7;
154 
155 static const long MILLISECONDS_PER_DAY = 86400000l;
156 
157 // this is the integral part of JDN of the midnight of Jan 1, 1970
158 // (i.e. JDN(Jan 1, 1970) = 2440587.5)
159 static const long EPOCH_JDN = 2440587l;
160 
161 // these values are only used in asserts so don't define them if asserts are
162 // disabled to avoid warnings about unused static variables
163 #if wxDEBUG_LEVEL
164 // the date of JDN -0.5 (as we don't work with fractional parts, this is the
165 // reference date for us) is Nov 24, 4714BC
166 static const int JDN_0_YEAR = -4713;
167 static const int JDN_0_MONTH = wxDateTime::Nov;
168 static const int JDN_0_DAY = 24;
169 #endif // wxDEBUG_LEVEL
170 
171 // the constants used for JDN calculations
172 static const long JDN_OFFSET         = 32046l;
173 static const long DAYS_PER_5_MONTHS  = 153l;
174 static const long DAYS_PER_4_YEARS   = 1461l;
175 static const long DAYS_PER_400_YEARS = 146097l;
176 
177 // this array contains the cumulated number of days in all previous months for
178 // normal and leap years
179 static const wxDateTime::wxDateTime_t gs_cumulatedDays[2][MONTHS_IN_YEAR] =
180 {
181     { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
182     { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
183 };
184 
185 const long wxDateTime::TIME_T_FACTOR = 1000l;
186 
187 // ----------------------------------------------------------------------------
188 // global data
189 // ----------------------------------------------------------------------------
190 
191 const char wxDefaultDateTimeFormat[] = "%c";
192 const char wxDefaultTimeSpanFormat[] = "%H:%M:%S";
193 
194 // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
195 // indicate an invalid wxDateTime object
196 const wxDateTime wxDefaultDateTime;
197 
198 wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown;
199 
200 // ----------------------------------------------------------------------------
201 // private functions
202 // ----------------------------------------------------------------------------
203 
204 // debugger helper: this function can be called from a debugger to show what
205 // the date really is
wxDumpDate(const wxDateTime * dt)206 extern const char *wxDumpDate(const wxDateTime* dt)
207 {
208     static char buf[128];
209 
210     wxString fmt(dt->Format("%Y-%m-%d (%a) %H:%M:%S"));
211     wxStrlcpy(buf,
212               (fmt + " (" + dt->GetValue().ToString() + " ticks)").ToAscii(),
213               WXSIZEOF(buf));
214 
215     return buf;
216 }
217 
218 // get the number of days in the given month of the given year
219 static inline
GetNumOfDaysInMonth(int year,wxDateTime::Month month)220 wxDateTime::wxDateTime_t GetNumOfDaysInMonth(int year, wxDateTime::Month month)
221 {
222     // the number of days in month in Julian/Gregorian calendar: the first line
223     // is for normal years, the second one is for the leap ones
224     static const wxDateTime::wxDateTime_t daysInMonth[2][MONTHS_IN_YEAR] =
225     {
226         { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
227         { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
228     };
229 
230     return daysInMonth[wxDateTime::IsLeapYear(year)][month];
231 }
232 
233 // return the integral part of the JDN for the midnight of the given date (to
234 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
235 // noon of the previous day)
GetTruncatedJDN(wxDateTime::wxDateTime_t day,wxDateTime::Month mon,int year)236 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day,
237                             wxDateTime::Month mon,
238                             int year)
239 {
240     // CREDIT: code below is by Scott E. Lee (but bugs are mine)
241 
242     // check the date validity
243     wxASSERT_MSG(
244       (year > JDN_0_YEAR) ||
245       ((year == JDN_0_YEAR) && (mon > JDN_0_MONTH)) ||
246       ((year == JDN_0_YEAR) && (mon == JDN_0_MONTH) && (day >= JDN_0_DAY)),
247       wxT("date out of range - can't convert to JDN")
248                 );
249 
250     // make the year positive to avoid problems with negative numbers division
251     year += 4800;
252 
253     // months are counted from March here
254     int month;
255     if ( mon >= wxDateTime::Mar )
256     {
257         month = mon - 2;
258     }
259     else
260     {
261         month = mon + 10;
262         year--;
263     }
264 
265     // now we can simply add all the contributions together
266     return ((year / 100) * DAYS_PER_400_YEARS) / 4
267             + ((year % 100) * DAYS_PER_4_YEARS) / 4
268             + (month * DAYS_PER_5_MONTHS + 2) / 5
269             + day
270             - JDN_OFFSET;
271 }
272 
273 #ifdef wxHAS_STRFTIME
274 
275 // this function is a wrapper around strftime(3) adding error checking
276 // NOTE: not static because used by datetimefmt.cpp
wxCallStrftime(const wxString & format,const tm * tm)277 wxString wxCallStrftime(const wxString& format, const tm* tm)
278 {
279     wxChar buf[4096];
280     // Create temp wxString here to work around mingw/cygwin bug 1046059
281     // http://sourceforge.net/tracker/?func=detail&atid=102435&aid=1046059&group_id=2435
282     wxString s;
283 
284     if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) )
285     {
286         // There is one special case in which strftime() can return 0 without
287         // indicating an error: "%p" may give empty string depending on the
288         // locale, so check for it explicitly. Apparently it's really the only
289         // exception.
290         if ( format != wxS("%p") )
291         {
292             // if the format is valid, buffer must be too small?
293             wxFAIL_MSG(wxT("strftime() failed"));
294         }
295 
296         buf[0] = '\0';
297     }
298 
299     s = buf;
300     return s;
301 }
302 
303 #endif // wxHAS_STRFTIME
304 
305 // if year and/or month have invalid values, replace them with the current ones
ReplaceDefaultYearMonthWithCurrent(int * year,wxDateTime::Month * month)306 static void ReplaceDefaultYearMonthWithCurrent(int *year,
307                                                wxDateTime::Month *month)
308 {
309     struct tm *tmNow = NULL;
310     struct tm tmstruct;
311 
312     if ( *year == wxDateTime::Inv_Year )
313     {
314         tmNow = wxDateTime::GetTmNow(&tmstruct);
315 
316         *year = 1900 + tmNow->tm_year;
317     }
318 
319     if ( *month == wxDateTime::Inv_Month )
320     {
321         if ( !tmNow )
322             tmNow = wxDateTime::GetTmNow(&tmstruct);
323 
324         *month = (wxDateTime::Month)tmNow->tm_mon;
325     }
326 }
327 
328 // fill the struct tm with default values
329 // NOTE: not static because used by datetimefmt.cpp
wxInitTm(struct tm & tm)330 void wxInitTm(struct tm& tm)
331 {
332     // struct tm may have etxra fields (undocumented and with unportable
333     // names) which, nevertheless, must be set to 0
334     memset(&tm, 0, sizeof(struct tm));
335 
336     tm.tm_mday = 1;   // mday 0 is invalid
337     tm.tm_year = 76;  // any valid year
338     tm.tm_isdst = -1; // auto determine
339 }
340 
341 // Internal helper function called only for times outside of standard time_t
342 // range.
343 //
344 // It is just a hack to work around the fact that we can't call IsDST() and
345 // related methods from GetTm() for the reasons explained there.
GetDSTOffset(wxLongLong t)346 static int GetDSTOffset(wxLongLong t)
347 {
348     bool isDST = false;
349 
350     switch ( wxDateTime::GetCountry() )
351     {
352         case wxDateTime::UK:
353             // We don't need to check for the end value in 1971 as this is
354             // inside the standard range, so check just for beginning of the
355             // permanent BST period in UK, see IsDST().
356             if ( t < 0 &&
357                     t >= wxDateTime(27, wxDateTime::Oct, 1968).GetValue() )
358                 isDST = true;
359             break;
360 
361         default:
362             break;
363     }
364 
365     return isDST ? wxDateTime::DST_OFFSET : 0;
366 }
367 
368 // ============================================================================
369 // implementation of wxDateTime
370 // ============================================================================
371 
372 // ----------------------------------------------------------------------------
373 // struct Tm
374 // ----------------------------------------------------------------------------
375 
Tm()376 wxDateTime::Tm::Tm()
377 {
378     year = (wxDateTime_t)wxDateTime::Inv_Year;
379     mon = wxDateTime::Inv_Month;
380     mday =
381     yday = 0;
382     hour =
383     min =
384     sec =
385     msec = 0;
386     wday = wxDateTime::Inv_WeekDay;
387 }
388 
Tm(const struct tm & tm,const TimeZone & tz)389 wxDateTime::Tm::Tm(const struct tm& tm, const TimeZone& tz)
390               : m_tz(tz)
391 {
392     msec = 0;
393     sec = (wxDateTime::wxDateTime_t)tm.tm_sec;
394     min = (wxDateTime::wxDateTime_t)tm.tm_min;
395     hour = (wxDateTime::wxDateTime_t)tm.tm_hour;
396     mday = (wxDateTime::wxDateTime_t)tm.tm_mday;
397     mon = (wxDateTime::Month)tm.tm_mon;
398     year = 1900 + tm.tm_year;
399     wday = (wxDateTime::wxDateTime_t)tm.tm_wday;
400     yday = (wxDateTime::wxDateTime_t)tm.tm_yday;
401 }
402 
IsValid() const403 bool wxDateTime::Tm::IsValid() const
404 {
405     if ( mon == wxDateTime::Inv_Month )
406         return false;
407 
408     // We need to check this here to avoid crashing in GetNumOfDaysInMonth() if
409     // somebody passed us "(wxDateTime::Month)1000".
410     wxCHECK_MSG( mon >= wxDateTime::Jan && mon < wxDateTime::Inv_Month, false,
411                  wxS("Invalid month value") );
412 
413     // we allow for the leap seconds, although we don't use them (yet)
414     return (year != wxDateTime::Inv_Year) && (mon != wxDateTime::Inv_Month) &&
415            (mday > 0 && mday <= GetNumOfDaysInMonth(year, mon)) &&
416            (hour < 24) && (min < 60) && (sec < 62) && (msec < 1000);
417 }
418 
ComputeWeekDay()419 void wxDateTime::Tm::ComputeWeekDay()
420 {
421     // compute the week day from day/month/year: we use the dumbest algorithm
422     // possible: just compute our JDN and then use the (simple to derive)
423     // formula: weekday = (JDN + 1.5) % 7
424     wday = (wxDateTime::wxDateTime_t)((GetTruncatedJDN(mday, mon, year) + 2) % 7);
425 }
426 
AddMonths(int monDiff)427 void wxDateTime::Tm::AddMonths(int monDiff)
428 {
429     // normalize the months field
430     while ( monDiff < -mon )
431     {
432         year--;
433 
434         monDiff += MONTHS_IN_YEAR;
435     }
436 
437     while ( monDiff + mon >= MONTHS_IN_YEAR )
438     {
439         year++;
440 
441         monDiff -= MONTHS_IN_YEAR;
442     }
443 
444     mon = (wxDateTime::Month)(mon + monDiff);
445 
446     wxASSERT_MSG( mon >= 0 && mon < MONTHS_IN_YEAR, wxT("logic error") );
447 
448     // NB: we don't check here that the resulting date is valid, this function
449     //     is private and the caller must check it if needed
450 }
451 
AddDays(int dayDiff)452 void wxDateTime::Tm::AddDays(int dayDiff)
453 {
454     // normalize the days field
455     while ( dayDiff + mday < 1 )
456     {
457         AddMonths(-1);
458 
459         dayDiff += GetNumOfDaysInMonth(year, mon);
460     }
461 
462     mday = (wxDateTime::wxDateTime_t)( mday + dayDiff );
463     while ( mday > GetNumOfDaysInMonth(year, mon) )
464     {
465         mday -= GetNumOfDaysInMonth(year, mon);
466 
467         AddMonths(1);
468     }
469 
470     wxASSERT_MSG( mday > 0 && mday <= GetNumOfDaysInMonth(year, mon),
471                   wxT("logic error") );
472 }
473 
474 // ----------------------------------------------------------------------------
475 // class TimeZone
476 // ----------------------------------------------------------------------------
477 
TimeZone(wxDateTime::TZ tz)478 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz)
479 {
480     switch ( tz )
481     {
482         case wxDateTime::Local:
483             // Use a special value for local time zone.
484             m_offset = -1;
485             break;
486 
487         case wxDateTime::GMT_12:
488         case wxDateTime::GMT_11:
489         case wxDateTime::GMT_10:
490         case wxDateTime::GMT_9:
491         case wxDateTime::GMT_8:
492         case wxDateTime::GMT_7:
493         case wxDateTime::GMT_6:
494         case wxDateTime::GMT_5:
495         case wxDateTime::GMT_4:
496         case wxDateTime::GMT_3:
497         case wxDateTime::GMT_2:
498         case wxDateTime::GMT_1:
499             m_offset = -3600*(wxDateTime::GMT0 - tz);
500             break;
501 
502         case wxDateTime::GMT0:
503         case wxDateTime::GMT1:
504         case wxDateTime::GMT2:
505         case wxDateTime::GMT3:
506         case wxDateTime::GMT4:
507         case wxDateTime::GMT5:
508         case wxDateTime::GMT6:
509         case wxDateTime::GMT7:
510         case wxDateTime::GMT8:
511         case wxDateTime::GMT9:
512         case wxDateTime::GMT10:
513         case wxDateTime::GMT11:
514         case wxDateTime::GMT12:
515         case wxDateTime::GMT13:
516             m_offset = 3600*(tz - wxDateTime::GMT0);
517             break;
518 
519         case wxDateTime::A_CST:
520             // Central Standard Time in use in Australia = UTC + 9.5
521             m_offset = 60l*(9*MIN_PER_HOUR + MIN_PER_HOUR/2);
522             break;
523 
524         default:
525             wxFAIL_MSG( wxT("unknown time zone") );
526     }
527 }
528 
GetOffset() const529 long wxDateTime::TimeZone::GetOffset() const
530 {
531     // get the offset from C RTL: it returns the difference GMT-local
532     // while we want to have the offset _from_ GMT, hence the '-'
533     return m_offset == -1 ? -wxGetTimeZone() : m_offset;
534 }
535 
536 // ----------------------------------------------------------------------------
537 // static functions
538 // ----------------------------------------------------------------------------
539 
540 /* static */
GetTmNow(struct tm * tmstruct)541 struct tm *wxDateTime::GetTmNow(struct tm *tmstruct)
542 {
543     time_t t = GetTimeNow();
544     return wxLocaltime_r(&t, tmstruct);
545 }
546 
547 /* static */
IsLeapYear(int year,wxDateTime::Calendar cal)548 bool wxDateTime::IsLeapYear(int year, wxDateTime::Calendar cal)
549 {
550     if ( year == Inv_Year )
551         year = GetCurrentYear();
552 
553     if ( cal == Gregorian )
554     {
555         // in Gregorian calendar leap years are those divisible by 4 except
556         // those divisible by 100 unless they're also divisible by 400
557         // (in some countries, like Russia and Greece, additional corrections
558         // exist, but they won't manifest themselves until 2700)
559         return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
560     }
561     else if ( cal == Julian )
562     {
563         // in Julian calendar the rule is simpler
564         return year % 4 == 0;
565     }
566     else
567     {
568         wxFAIL_MSG(wxT("unknown calendar"));
569 
570         return false;
571     }
572 }
573 
574 #ifdef __WINDOWS__
575 #include "wx/msw/registry.h"
576 
577 /* static */
GetFirstWeekDay(wxDateTime::WeekDay * firstDay)578 bool wxDateTime::GetFirstWeekDay(wxDateTime::WeekDay *firstDay)
579 {
580     wxCHECK_MSG( firstDay, false, wxS("output parameter must be non-null") );
581     wxRegKey key(wxRegKey::HKCU, "Control Panel\\International");
582     wxString val;
583 
584     if ( key.Exists() && key.HasValue("iFirstDayOfWeek") )
585     {
586         key.QueryValue("iFirstDayOfWeek", val);
587         *firstDay = wxDateTime::WeekDay((wxAtoi(val) + 1) % 7);
588         return true;
589     }
590     else
591     {
592         *firstDay = wxDateTime::Sun;
593         return false;
594     }
595 }
596 
597 #elif defined(__APPLE__)
598 // implementation in utils_base.mm
599 #elif defined(HAVE_NL_TIME_FIRST_WEEKDAY)
600 
601 #include <langinfo.h>
602 
603 /* static */
GetFirstWeekDay(wxDateTime::WeekDay * firstDay)604 bool wxDateTime::GetFirstWeekDay(wxDateTime::WeekDay *firstDay)
605 {
606     wxCHECK_MSG( firstDay, false, wxS("output parameter must be non-null") );
607     *firstDay = wxDateTime::WeekDay((*nl_langinfo(_NL_TIME_FIRST_WEEKDAY) - 1) % 7);
608     return true;
609 }
610 
611 #else
612 
613 /* static */
GetFirstWeekDay(wxDateTime::WeekDay * firstDay)614 bool wxDateTime::GetFirstWeekDay(wxDateTime::WeekDay *firstDay)
615 {
616     wxCHECK_MSG( firstDay, false, wxS("output parameter must be non-null") );
617     *firstDay = wxDateTime::Sun;
618     return false;
619 }
620 
621 #endif
622 
623 /* static */
GetCentury(int year)624 int wxDateTime::GetCentury(int year)
625 {
626     return year > 0 ? year / 100 : year / 100 - 1;
627 }
628 
629 /* static */
ConvertYearToBC(int year)630 int wxDateTime::ConvertYearToBC(int year)
631 {
632     // year 0 is BC 1
633     return year > 0 ? year : year - 1;
634 }
635 
636 /* static */
GetCurrentYear(wxDateTime::Calendar cal)637 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal)
638 {
639     switch ( cal )
640     {
641         case Gregorian:
642             return Now().GetYear();
643 
644         case Julian:
645             wxFAIL_MSG(wxT("TODO"));
646             break;
647 
648         default:
649             wxFAIL_MSG(wxT("unsupported calendar"));
650             break;
651     }
652 
653     return Inv_Year;
654 }
655 
656 /* static */
GetCurrentMonth(wxDateTime::Calendar cal)657 wxDateTime::Month wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal)
658 {
659     switch ( cal )
660     {
661         case Gregorian:
662             return Now().GetMonth();
663 
664         case Julian:
665             wxFAIL_MSG(wxT("TODO"));
666             break;
667 
668         default:
669             wxFAIL_MSG(wxT("unsupported calendar"));
670             break;
671     }
672 
673     return Inv_Month;
674 }
675 
676 /* static */
GetNumberOfDays(int year,Calendar cal)677 wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(int year, Calendar cal)
678 {
679     if ( year == Inv_Year )
680     {
681         // take the current year if none given
682         year = GetCurrentYear();
683     }
684 
685     switch ( cal )
686     {
687         case Gregorian:
688         case Julian:
689             return IsLeapYear(year) ? 366 : 365;
690 
691         default:
692             wxFAIL_MSG(wxT("unsupported calendar"));
693             break;
694     }
695 
696     return 0;
697 }
698 
699 /* static */
GetNumberOfDays(wxDateTime::Month month,int year,wxDateTime::Calendar cal)700 wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(wxDateTime::Month month,
701                                                      int year,
702                                                      wxDateTime::Calendar cal)
703 {
704     wxCHECK_MSG( month < MONTHS_IN_YEAR, 0, wxT("invalid month") );
705 
706     if ( cal == Gregorian || cal == Julian )
707     {
708         if ( year == Inv_Year )
709         {
710             // take the current year if none given
711             year = GetCurrentYear();
712         }
713 
714         return GetNumOfDaysInMonth(year, month);
715     }
716     else
717     {
718         wxFAIL_MSG(wxT("unsupported calendar"));
719 
720         return 0;
721     }
722 }
723 
724 namespace
725 {
726 
727 // helper function used by GetEnglish/WeekDayName(): returns 0 if flags is
728 // Name_Full and 1 if it is Name_Abbr or -1 if the flags is incorrect (and
729 // asserts in this case)
730 //
731 // the return value of this function is used as an index into 2D array
732 // containing full names in its first row and abbreviated ones in the 2nd one
NameArrayIndexFromFlag(wxDateTime::NameFlags flags)733 int NameArrayIndexFromFlag(wxDateTime::NameFlags flags)
734 {
735     switch ( flags )
736     {
737         case wxDateTime::Name_Full:
738             return 0;
739 
740         case wxDateTime::Name_Abbr:
741             return 1;
742 
743         default:
744             wxFAIL_MSG( "unknown wxDateTime::NameFlags value" );
745     }
746 
747     return -1;
748 }
749 
750 } // anonymous namespace
751 
752 /* static */
GetEnglishMonthName(Month month,NameFlags flags)753 wxString wxDateTime::GetEnglishMonthName(Month month, NameFlags flags)
754 {
755     wxCHECK_MSG( month != Inv_Month, wxEmptyString, "invalid month" );
756 
757     static const char *const monthNames[2][MONTHS_IN_YEAR] =
758     {
759         { "January", "February", "March", "April", "May", "June",
760           "July", "August", "September", "October", "November", "December" },
761         { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
762           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }
763     };
764 
765     const int idx = NameArrayIndexFromFlag(flags);
766     if ( idx == -1 )
767         return wxString();
768 
769     return monthNames[idx][month];
770 }
771 
772 /* static */
GetMonthName(wxDateTime::Month month,wxDateTime::NameFlags flags)773 wxString wxDateTime::GetMonthName(wxDateTime::Month month,
774                                   wxDateTime::NameFlags flags)
775 {
776 #ifdef wxHAS_STRFTIME
777     wxCHECK_MSG( month != Inv_Month, wxEmptyString, wxT("invalid month") );
778 
779     // notice that we must set all the fields to avoid confusing libc (GNU one
780     // gets confused to a crash if we don't do this)
781     tm tm;
782     wxInitTm(tm);
783     tm.tm_mon = month;
784 
785     return wxCallStrftime(flags == Name_Abbr ? wxS("%b") : wxS("%B"), &tm);
786 #else // !wxHAS_STRFTIME
787     return GetEnglishMonthName(month, flags);
788 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
789 }
790 
791 /* static */
GetEnglishWeekDayName(WeekDay wday,NameFlags flags)792 wxString wxDateTime::GetEnglishWeekDayName(WeekDay wday, NameFlags flags)
793 {
794     wxCHECK_MSG( wday != Inv_WeekDay, wxEmptyString, wxT("invalid weekday") );
795 
796     static const char *const weekdayNames[2][DAYS_PER_WEEK] =
797     {
798         { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
799           "Saturday" },
800         { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" },
801     };
802 
803     const int idx = NameArrayIndexFromFlag(flags);
804     if ( idx == -1 )
805         return wxString();
806 
807     return weekdayNames[idx][wday];
808 }
809 
810 /* static */
GetWeekDayName(wxDateTime::WeekDay wday,wxDateTime::NameFlags flags)811 wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday,
812                                     wxDateTime::NameFlags flags)
813 {
814 #ifdef wxHAS_STRFTIME
815     wxCHECK_MSG( wday != Inv_WeekDay, wxEmptyString, wxT("invalid weekday") );
816 
817     // take some arbitrary Sunday (but notice that the day should be such that
818     // after adding wday to it below we still have a valid date, e.g. don't
819     // take 28 here!)
820     tm tm;
821     wxInitTm(tm);
822     tm.tm_mday = 21;
823     tm.tm_mon = Nov;
824     tm.tm_year = 99;
825 
826     // and offset it by the number of days needed to get the correct wday
827     tm.tm_mday += wday;
828 
829     // call mktime() to normalize it...
830     (void)mktime(&tm);
831 
832     // ... and call strftime()
833     return wxCallStrftime(flags == Name_Abbr ? wxS("%a") : wxS("%A"), &tm);
834 #else // !wxHAS_STRFTIME
835     return GetEnglishWeekDayName(wday, flags);
836 #endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
837 }
838 
839 /* static */
GetAmPmStrings(wxString * am,wxString * pm)840 void wxDateTime::GetAmPmStrings(wxString *am, wxString *pm)
841 {
842     tm tm;
843     wxInitTm(tm);
844     wxChar buffer[64];
845     // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code
846     // and causes an assertion failed if the buffer is to small (which is good) - OR -
847     // if strftime does not return anything because the format string is invalid - OR -
848     // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good).
849     // wxDateTime::ParseTime will try several different formats to parse the time.
850     // As a result, GetAmPmStrings might get called, even if the current locale
851     // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would
852     // assert, even though it is a perfectly legal use.
853     if ( am )
854     {
855         if (wxStrftime(buffer, WXSIZEOF(buffer), wxT("%p"), &tm) > 0)
856             *am = wxString(buffer);
857         else
858             *am = wxString();
859     }
860     if ( pm )
861     {
862         tm.tm_hour = 13;
863         if (wxStrftime(buffer, WXSIZEOF(buffer), wxT("%p"), &tm) > 0)
864             *pm = wxString(buffer);
865         else
866             *pm = wxString();
867     }
868 }
869 
870 
871 // ----------------------------------------------------------------------------
872 // Country stuff: date calculations depend on the country (DST, work days,
873 // ...), so we need to know which rules to follow.
874 // ----------------------------------------------------------------------------
875 
876 /* static */
GetCountry()877 wxDateTime::Country wxDateTime::GetCountry()
878 {
879     // TODO use LOCALE_ICOUNTRY setting under Win32
880     if ( ms_country == Country_Unknown )
881     {
882         // try to guess from the time zone name
883         time_t t = time(NULL);
884         struct tm tmstruct;
885         struct tm *tm = wxLocaltime_r(&t, &tmstruct);
886 
887         wxString tz = wxCallStrftime(wxS("%Z"), tm);
888         if ( tz == wxT("WET") || tz == wxT("WEST") ||
889                 tz == wxT("BST") || tz == wxT("GMT") )
890         {
891             ms_country = UK;
892         }
893         else if ( tz == wxT("CET") || tz == wxT("CEST") )
894         {
895             ms_country = Country_EEC;
896         }
897         else if ( tz == wxT("MSK") || tz == wxT("MSD") )
898         {
899             ms_country = Russia;
900         }
901         else if ( tz == wxT("AST") || tz == wxT("ADT") ||
902                   tz == wxT("EST") || tz == wxT("EDT") ||
903                   tz == wxT("CST") || tz == wxT("CDT") ||
904                   tz == wxT("MST") || tz == wxT("MDT") ||
905                   tz == wxT("PST") || tz == wxT("PDT") )
906         {
907             ms_country = USA;
908         }
909         else
910         {
911             // well, choose a default one
912             ms_country = USA;
913         }
914     }
915 
916     return ms_country;
917 }
918 
919 /* static */
SetCountry(wxDateTime::Country country)920 void wxDateTime::SetCountry(wxDateTime::Country country)
921 {
922     ms_country = country;
923 }
924 
925 /* static */
IsWestEuropeanCountry(Country country)926 bool wxDateTime::IsWestEuropeanCountry(Country country)
927 {
928     if ( country == Country_Default )
929     {
930         country = GetCountry();
931     }
932 
933     return (Country_WesternEurope_Start <= country) &&
934            (country <= Country_WesternEurope_End);
935 }
936 
937 // ----------------------------------------------------------------------------
938 // DST calculations: we use 3 different rules for the West European countries,
939 // USA and for the rest of the world. This is undoubtedly false for many
940 // countries, but I lack the necessary info (and the time to gather it),
941 // please add the other rules here!
942 // ----------------------------------------------------------------------------
943 
944 /* static */
IsDSTApplicable(int year,Country country)945 bool wxDateTime::IsDSTApplicable(int year, Country country)
946 {
947     if ( year == Inv_Year )
948     {
949         // take the current year if none given
950         year = GetCurrentYear();
951     }
952 
953     if ( country == Country_Default )
954     {
955         country = GetCountry();
956     }
957 
958     switch ( country )
959     {
960         case USA:
961         case UK:
962             // DST was first observed in the US and UK during WWI, reused
963             // during WWII and used again since 1966
964             return year >= 1966 ||
965                    (year >= 1942 && year <= 1945) ||
966                    (year == 1918 || year == 1919);
967 
968         default:
969             // assume that it started after WWII
970             return year > 1950;
971     }
972 }
973 
974 /* static */
GetBeginDST(int year,Country country)975 wxDateTime wxDateTime::GetBeginDST(int year, Country country)
976 {
977     if ( year == Inv_Year )
978     {
979         // take the current year if none given
980         year = GetCurrentYear();
981     }
982 
983     if ( country == Country_Default )
984     {
985         country = GetCountry();
986     }
987 
988     if ( !IsDSTApplicable(year, country) )
989     {
990         return wxInvalidDateTime;
991     }
992 
993     wxDateTime dt;
994 
995     if ( IsWestEuropeanCountry(country) || (country == Russia) )
996     {
997         // DST begins at 1 a.m. GMT on the last Sunday of March
998         if ( !dt.SetToLastWeekDay(Sun, Mar, year) )
999         {
1000             // weird...
1001             wxFAIL_MSG( wxT("no last Sunday in March?") );
1002         }
1003 
1004         dt += wxTimeSpan::Hours(1);
1005     }
1006     else switch ( country )
1007     {
1008         case USA:
1009             switch ( year )
1010             {
1011                 case 1918:
1012                 case 1919:
1013                     // don't know for sure - assume it was in effect all year
1014 
1015                 case 1943:
1016                 case 1944:
1017                 case 1945:
1018                     dt.Set(1, Jan, year);
1019                     break;
1020 
1021                 case 1942:
1022                     // DST was installed Feb 2, 1942 by the Congress
1023                     dt.Set(2, Feb, year);
1024                     break;
1025 
1026                     // Oil embargo changed the DST period in the US
1027                 case 1974:
1028                     dt.Set(6, Jan, 1974);
1029                     break;
1030 
1031                 case 1975:
1032                     dt.Set(23, Feb, 1975);
1033                     break;
1034 
1035                 default:
1036                     // before 1986, DST begun on the last Sunday of April, but
1037                     // in 1986 Reagan changed it to begin at 2 a.m. of the
1038                     // first Sunday in April
1039                     if ( year < 1986 )
1040                     {
1041                         if ( !dt.SetToLastWeekDay(Sun, Apr, year) )
1042                         {
1043                             // weird...
1044                             wxFAIL_MSG( wxT("no first Sunday in April?") );
1045                         }
1046                     }
1047                     else if ( year > 2006 )
1048                     // Energy Policy Act of 2005, Pub. L. no. 109-58, 119 Stat 594 (2005).
1049                     // Starting in 2007, daylight time begins in the United States on the
1050                     // second Sunday in March and ends on the first Sunday in November
1051                     {
1052                         if ( !dt.SetToWeekDay(Sun, 2, Mar, year) )
1053                         {
1054                             // weird...
1055                             wxFAIL_MSG( wxT("no second Sunday in March?") );
1056                         }
1057                     }
1058                     else
1059                     {
1060                         if ( !dt.SetToWeekDay(Sun, 1, Apr, year) )
1061                         {
1062                             // weird...
1063                             wxFAIL_MSG( wxT("no first Sunday in April?") );
1064                         }
1065                     }
1066 
1067                     dt += wxTimeSpan::Hours(2);
1068 
1069                     // TODO what about timezone??
1070             }
1071 
1072             break;
1073 
1074         default:
1075             // assume Mar 30 as the start of the DST for the rest of the world
1076             // - totally bogus, of course
1077             dt.Set(30, Mar, year);
1078     }
1079 
1080     return dt;
1081 }
1082 
1083 /* static */
GetEndDST(int year,Country country)1084 wxDateTime wxDateTime::GetEndDST(int year, Country country)
1085 {
1086     if ( year == Inv_Year )
1087     {
1088         // take the current year if none given
1089         year = GetCurrentYear();
1090     }
1091 
1092     if ( country == Country_Default )
1093     {
1094         country = GetCountry();
1095     }
1096 
1097     if ( !IsDSTApplicable(year, country) )
1098     {
1099         return wxInvalidDateTime;
1100     }
1101 
1102     wxDateTime dt;
1103 
1104     if ( IsWestEuropeanCountry(country) || (country == Russia) )
1105     {
1106         // DST ends at 1 a.m. GMT on the last Sunday of October
1107         if ( !dt.SetToLastWeekDay(Sun, Oct, year) )
1108         {
1109             // weirder and weirder...
1110             wxFAIL_MSG( wxT("no last Sunday in October?") );
1111         }
1112 
1113         dt += wxTimeSpan::Hours(1);
1114     }
1115     else switch ( country )
1116     {
1117         case USA:
1118             switch ( year )
1119             {
1120                 case 1918:
1121                 case 1919:
1122                     // don't know for sure - assume it was in effect all year
1123 
1124                 case 1943:
1125                 case 1944:
1126                     dt.Set(31, Dec, year);
1127                     break;
1128 
1129                 case 1945:
1130                     // the time was reset after the end of the WWII
1131                     dt.Set(30, Sep, year);
1132                     break;
1133 
1134                 default: // default for switch (year)
1135                     if ( year > 2006 )
1136                       // Energy Policy Act of 2005, Pub. L. no. 109-58, 119 Stat 594 (2005).
1137                       // Starting in 2007, daylight time begins in the United States on the
1138                       // second Sunday in March and ends on the first Sunday in November
1139                     {
1140                         if ( !dt.SetToWeekDay(Sun, 1, Nov, year) )
1141                         {
1142                             // weird...
1143                             wxFAIL_MSG( wxT("no first Sunday in November?") );
1144                         }
1145                     }
1146                     else
1147                      // pre-2007
1148                      // DST ends at 2 a.m. on the last Sunday of October
1149                     {
1150                         if ( !dt.SetToLastWeekDay(Sun, Oct, year) )
1151                         {
1152                             // weirder and weirder...
1153                             wxFAIL_MSG( wxT("no last Sunday in October?") );
1154                         }
1155                     }
1156 
1157                     dt += wxTimeSpan::Hours(2);
1158 
1159             // TODO: what about timezone??
1160             }
1161             break;
1162 
1163         default: // default for switch (country)
1164             // assume October 26th as the end of the DST - totally bogus too
1165             dt.Set(26, Oct, year);
1166     }
1167 
1168     return dt;
1169 }
1170 
1171 // ----------------------------------------------------------------------------
1172 // constructors and assignment operators
1173 // ----------------------------------------------------------------------------
1174 
1175 // return the current time with ms precision
UNow()1176 /* static */ wxDateTime wxDateTime::UNow()
1177 {
1178     return wxDateTime(wxGetUTCTimeMillis());
1179 }
1180 
1181 // the values in the tm structure contain the local time
Set(const struct tm & tm)1182 wxDateTime& wxDateTime::Set(const struct tm& tm)
1183 {
1184     struct tm tm2(tm);
1185     time_t timet = mktime(&tm2);
1186 
1187     if ( timet == (time_t)-1 )
1188     {
1189         // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1190         // less than timezone - try to make it work for this case
1191         if ( tm2.tm_year == 70 && tm2.tm_mon == 0 && tm2.tm_mday == 1 )
1192         {
1193             return Set((time_t)(
1194                        wxGetTimeZone() +
1195                        tm2.tm_hour * MIN_PER_HOUR * SEC_PER_MIN +
1196                        tm2.tm_min * SEC_PER_MIN +
1197                        tm2.tm_sec));
1198         }
1199 
1200         wxFAIL_MSG( wxT("mktime() failed") );
1201 
1202         *this = wxInvalidDateTime;
1203 
1204         return *this;
1205     }
1206 
1207     // mktime() only adjusts tm_wday, tm_yday and tm_isdst fields normally, if
1208     // it changed anything else, it must have performed the DST adjustment. But
1209     // the trouble with this is that different implementations do it
1210     // differently, e.g. GNU libc moves the time forward if the specified time
1211     // is invalid in the local time zone, while MSVC CRT moves it backwards
1212     // which is especially pernicious as it can change the date if the DST
1213     // starts at midnight, as it does in some time zones (see #15419), and this
1214     // is completely unexpected for the code working with dates only.
1215     //
1216     // So standardize on moving the time forwards to have consistent behaviour
1217     // under all platforms and to avoid the problem above.
1218     if ( tm2.tm_hour != tm.tm_hour )
1219     {
1220         tm2 = tm;
1221         tm2.tm_hour++;
1222         if ( tm2.tm_hour == 24 )
1223         {
1224             // This shouldn't normally happen as the DST never starts at 23:00
1225             // but if it does, we have a problem as we need to adjust the day
1226             // as well. However we stop here, i.e. we don't adjust the month
1227             // (or the year) because mktime() is supposed to take care of this
1228             // for us.
1229             tm2.tm_hour = 0;
1230             tm2.tm_mday++;
1231         }
1232 
1233         timet = mktime(&tm2);
1234     }
1235 
1236     return Set(timet);
1237 }
1238 
Set(wxDateTime_t hour,wxDateTime_t minute,wxDateTime_t second,wxDateTime_t millisec)1239 wxDateTime& wxDateTime::Set(wxDateTime_t hour,
1240                             wxDateTime_t minute,
1241                             wxDateTime_t second,
1242                             wxDateTime_t millisec)
1243 {
1244     // we allow seconds to be 61 to account for the leap seconds, even if we
1245     // don't use them really
1246     wxDATETIME_CHECK( hour < 24 &&
1247                       second < 62 &&
1248                       minute < 60 &&
1249                       millisec < 1000,
1250                       wxT("Invalid time in wxDateTime::Set()") );
1251 
1252     // get the current date from system
1253     struct tm tmstruct;
1254     struct tm *tm = GetTmNow(&tmstruct);
1255 
1256     wxDATETIME_CHECK( tm, wxT("wxLocaltime_r() failed") );
1257 
1258     // make a copy so it isn't clobbered by the call to mktime() below
1259     struct tm tm1(*tm);
1260 
1261     // adjust the time
1262     tm1.tm_hour = hour;
1263     tm1.tm_min = minute;
1264     tm1.tm_sec = second;
1265 
1266     // and the DST in case it changes on this date
1267     struct tm tm2(tm1);
1268     mktime(&tm2);
1269     if ( tm2.tm_isdst != tm1.tm_isdst )
1270         tm1.tm_isdst = tm2.tm_isdst;
1271 
1272     (void)Set(tm1);
1273 
1274     // and finally adjust milliseconds
1275     return SetMillisecond(millisec);
1276 }
1277 
Set(wxDateTime_t day,Month month,int year,wxDateTime_t hour,wxDateTime_t minute,wxDateTime_t second,wxDateTime_t millisec)1278 wxDateTime& wxDateTime::Set(wxDateTime_t day,
1279                             Month        month,
1280                             int          year,
1281                             wxDateTime_t hour,
1282                             wxDateTime_t minute,
1283                             wxDateTime_t second,
1284                             wxDateTime_t millisec)
1285 {
1286     wxDATETIME_CHECK( hour < 24 &&
1287                       second < 62 &&
1288                       minute < 60 &&
1289                       millisec < 1000,
1290                       wxT("Invalid time in wxDateTime::Set()") );
1291 
1292     ReplaceDefaultYearMonthWithCurrent(&year, &month);
1293 
1294     wxDATETIME_CHECK( (0 < day) && (day <= GetNumberOfDays(month, year)),
1295                       wxT("Invalid date in wxDateTime::Set()") );
1296 
1297     // the range of time_t type (inclusive)
1298     static const int yearMinInRange = 1970;
1299     static const int yearMaxInRange = 2037;
1300 
1301     // test only the year instead of testing for the exact end of the Unix
1302     // time_t range - it doesn't bring anything to do more precise checks
1303     if ( year >= yearMinInRange && year <= yearMaxInRange )
1304     {
1305         // use the standard library version if the date is in range - this is
1306         // probably more efficient than our code
1307         struct tm tm;
1308         tm.tm_year = year - 1900;
1309         tm.tm_mon = month;
1310         tm.tm_mday = day;
1311         tm.tm_hour = hour;
1312         tm.tm_min = minute;
1313         tm.tm_sec = second;
1314         tm.tm_isdst = -1;       // mktime() will guess it
1315 
1316         (void)Set(tm);
1317 
1318         // and finally adjust milliseconds
1319         if (IsValid())
1320             SetMillisecond(millisec);
1321 
1322         return *this;
1323     }
1324     else
1325     {
1326         // do time calculations ourselves: we want to calculate the number of
1327         // milliseconds between the given date and the epoch
1328 
1329         // get the JDN for the midnight of this day
1330         m_time = GetTruncatedJDN(day, month, year);
1331         m_time -= EPOCH_JDN;
1332         m_time *= SECONDS_PER_DAY * TIME_T_FACTOR;
1333 
1334         // JDN corresponds to GMT, we take localtime
1335         Add(wxTimeSpan(hour, minute, second + wxGetTimeZone(), millisec));
1336     }
1337 
1338     return *this;
1339 }
1340 
Set(double jdn)1341 wxDateTime& wxDateTime::Set(double jdn)
1342 {
1343     // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1344     // EPOCH_JDN + 0.5
1345     jdn -= EPOCH_JDN + 0.5;
1346 
1347     m_time.Assign(jdn*MILLISECONDS_PER_DAY);
1348 
1349     // JDNs always are in UTC, so we don't need any adjustments for time zone
1350 
1351     return *this;
1352 }
1353 
ResetTime()1354 wxDateTime& wxDateTime::ResetTime()
1355 {
1356     Tm tm = GetTm();
1357 
1358     if ( tm.hour || tm.min || tm.sec || tm.msec )
1359     {
1360         tm.msec =
1361         tm.sec =
1362         tm.min =
1363         tm.hour = 0;
1364 
1365         Set(tm);
1366     }
1367 
1368     return *this;
1369 }
1370 
GetDateOnly() const1371 wxDateTime wxDateTime::GetDateOnly() const
1372 {
1373     Tm tm = GetTm();
1374     tm.msec =
1375     tm.sec =
1376     tm.min =
1377     tm.hour = 0;
1378     return wxDateTime(tm);
1379 }
1380 
1381 // ----------------------------------------------------------------------------
1382 // DOS Date and Time Format functions
1383 // ----------------------------------------------------------------------------
1384 // the dos date and time value is an unsigned 32 bit value in the format:
1385 // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1386 //
1387 // Y = year offset from 1980 (0-127)
1388 // M = month (1-12)
1389 // D = day of month (1-31)
1390 // h = hour (0-23)
1391 // m = minute (0-59)
1392 // s = bisecond (0-29) each bisecond indicates two seconds
1393 // ----------------------------------------------------------------------------
1394 
SetFromDOS(unsigned long ddt)1395 wxDateTime& wxDateTime::SetFromDOS(unsigned long ddt)
1396 {
1397     struct tm tm;
1398     wxInitTm(tm);
1399 
1400     long year = ddt & 0xFE000000;
1401     year >>= 25;
1402     year += 80;
1403     tm.tm_year = year;
1404 
1405     long month = ddt & 0x1E00000;
1406     month >>= 21;
1407     month -= 1;
1408     tm.tm_mon = month;
1409 
1410     long day = ddt & 0x1F0000;
1411     day >>= 16;
1412     tm.tm_mday = day;
1413 
1414     long hour = ddt & 0xF800;
1415     hour >>= 11;
1416     tm.tm_hour = hour;
1417 
1418     long minute = ddt & 0x7E0;
1419     minute >>= 5;
1420     tm.tm_min = minute;
1421 
1422     long second = ddt & 0x1F;
1423     tm.tm_sec = second * 2;
1424 
1425     return Set(mktime(&tm));
1426 }
1427 
GetAsDOS() const1428 unsigned long wxDateTime::GetAsDOS() const
1429 {
1430     unsigned long ddt;
1431     time_t ticks = GetTicks();
1432     struct tm tmstruct;
1433     struct tm *tm = wxLocaltime_r(&ticks, &tmstruct);
1434     wxCHECK_MSG( tm, ULONG_MAX, wxT("time can't be represented in DOS format") );
1435 
1436     long year = tm->tm_year;
1437     year -= 80;
1438     year <<= 25;
1439 
1440     long month = tm->tm_mon;
1441     month += 1;
1442     month <<= 21;
1443 
1444     long day = tm->tm_mday;
1445     day <<= 16;
1446 
1447     long hour = tm->tm_hour;
1448     hour <<= 11;
1449 
1450     long minute = tm->tm_min;
1451     minute <<= 5;
1452 
1453     long second = tm->tm_sec;
1454     second /= 2;
1455 
1456     ddt = year | month | day | hour | minute | second;
1457     return ddt;
1458 }
1459 
1460 // ----------------------------------------------------------------------------
1461 // time_t <-> broken down time conversions
1462 // ----------------------------------------------------------------------------
1463 
wxTryGetTm(tm & tmstruct,time_t t,const wxDateTime::TimeZone & tz)1464 const tm* wxTryGetTm(tm& tmstruct, time_t t, const wxDateTime::TimeZone& tz)
1465 {
1466     if ( tz.IsLocal() )
1467     {
1468         // we are working with local time
1469         return wxLocaltime_r(&t, &tmstruct);
1470     }
1471     else
1472     {
1473         t += (time_t)tz.GetOffset();
1474 #if !defined(__VMS__) // time is unsigned so avoid warning
1475         if ( t < 0 )
1476             return NULL;
1477 #endif
1478         return wxGmtime_r(&t, &tmstruct);
1479     }
1480 }
1481 
GetTm(const TimeZone & tz) const1482 wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
1483 {
1484     wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
1485 
1486     time_t time = GetTicks();
1487     if ( time != (time_t)-1 )
1488     {
1489         // Try to use the RTL.
1490         struct tm tmstruct;
1491         if ( const tm* tm = wxTryGetTm(tmstruct, time, tz) )
1492         {
1493             // adjust the milliseconds
1494             Tm tm2(*tm, tz);
1495             long timeOnly = (m_time % MILLISECONDS_PER_DAY).ToLong();
1496             tm2.msec = (wxDateTime_t)(timeOnly % 1000);
1497             return tm2;
1498         }
1499         //else: use generic code below
1500     }
1501 
1502     long secDiff = tz.GetOffset();
1503 
1504     // We need to account for DST as always when converting to broken down time
1505     // components, but we can't call IsDST() from here because this would
1506     // result in infinite recursion as IsDST() starts by calling GetYear()
1507     // which just calls back to this function. So call a special function which
1508     // is used just here to determine the DST offset to add.
1509     if ( tz.IsLocal() )
1510         secDiff += GetDSTOffset(m_time);
1511 
1512     wxLongLong timeMidnight = m_time + secDiff * 1000;
1513 
1514     // remember the time and do the calculations with the date only - this
1515     // eliminates rounding errors of the floating point arithmetics
1516 
1517     long timeOnly = (timeMidnight % MILLISECONDS_PER_DAY).ToLong();
1518 
1519     // we want to always have positive time and timeMidnight to be really
1520     // the midnight before it
1521     if ( timeOnly < 0 )
1522     {
1523         timeOnly = MILLISECONDS_PER_DAY + timeOnly;
1524     }
1525 
1526     timeMidnight -= timeOnly;
1527 
1528     // calculate the Gregorian date from JDN for the midnight of our date:
1529     // this will yield day, month (in 1..12 range) and year
1530 
1531     // actually, this is the JDN for the noon of the previous day
1532     long jdn = (timeMidnight / MILLISECONDS_PER_DAY).ToLong() + EPOCH_JDN;
1533 
1534     // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1535 
1536     wxASSERT_MSG( jdn > -2, wxT("JDN out of range") );
1537 
1538     // calculate the century
1539     long temp = (jdn + JDN_OFFSET) * 4 - 1;
1540     long century = temp / DAYS_PER_400_YEARS;
1541 
1542     // then the year and day of year (1 <= dayOfYear <= 366)
1543     temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
1544     long year = (century * 100) + (temp / DAYS_PER_4_YEARS);
1545     long dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
1546 
1547     // and finally the month and day of the month
1548     temp = dayOfYear * 5 - 3;
1549     long month = temp / DAYS_PER_5_MONTHS;
1550     long day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
1551 
1552     // month is counted from March - convert to normal
1553     if ( month < 10 )
1554     {
1555         month += 3;
1556     }
1557     else
1558     {
1559         year += 1;
1560         month -= 9;
1561     }
1562 
1563     // year is offset by 4800
1564     year -= 4800;
1565 
1566     // check that the algorithm gave us something reasonable
1567     wxASSERT_MSG( (0 < month) && (month <= 12), wxT("invalid month") );
1568     wxASSERT_MSG( (1 <= day) && (day < 32), wxT("invalid day") );
1569 
1570     // construct Tm from these values
1571     Tm tm;
1572     tm.year = (int)year;
1573     tm.yday = (wxDateTime_t)(dayOfYear - 1); // use C convention for day number
1574     tm.mon = (Month)(month - 1); // algorithm yields 1 for January, not 0
1575     tm.mday = (wxDateTime_t)day;
1576     tm.msec = (wxDateTime_t)(timeOnly % 1000);
1577     timeOnly -= tm.msec;
1578     timeOnly /= 1000;               // now we have time in seconds
1579 
1580     tm.sec = (wxDateTime_t)(timeOnly % SEC_PER_MIN);
1581     timeOnly -= tm.sec;
1582     timeOnly /= SEC_PER_MIN;        // now we have time in minutes
1583 
1584     tm.min = (wxDateTime_t)(timeOnly % MIN_PER_HOUR);
1585     timeOnly -= tm.min;
1586 
1587     tm.hour = (wxDateTime_t)(timeOnly / MIN_PER_HOUR);
1588 
1589     return tm;
1590 }
1591 
SetYear(int year)1592 wxDateTime& wxDateTime::SetYear(int year)
1593 {
1594     wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
1595 
1596     Tm tm(GetTm());
1597     tm.year = year;
1598     Set(tm);
1599 
1600     return *this;
1601 }
1602 
SetMonth(Month month)1603 wxDateTime& wxDateTime::SetMonth(Month month)
1604 {
1605     wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
1606 
1607     Tm tm(GetTm());
1608     tm.mon = month;
1609     Set(tm);
1610 
1611     return *this;
1612 }
1613 
SetDay(wxDateTime_t mday)1614 wxDateTime& wxDateTime::SetDay(wxDateTime_t mday)
1615 {
1616     wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
1617 
1618     Tm tm(GetTm());
1619     tm.mday = mday;
1620     Set(tm);
1621 
1622     return *this;
1623 }
1624 
SetHour(wxDateTime_t hour)1625 wxDateTime& wxDateTime::SetHour(wxDateTime_t hour)
1626 {
1627     wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
1628 
1629     Tm tm(GetTm());
1630     tm.hour = hour;
1631     Set(tm);
1632 
1633     return *this;
1634 }
1635 
SetMinute(wxDateTime_t min)1636 wxDateTime& wxDateTime::SetMinute(wxDateTime_t min)
1637 {
1638     wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
1639 
1640     Tm tm(GetTm());
1641     tm.min = min;
1642     Set(tm);
1643 
1644     return *this;
1645 }
1646 
SetSecond(wxDateTime_t sec)1647 wxDateTime& wxDateTime::SetSecond(wxDateTime_t sec)
1648 {
1649     wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
1650 
1651     Tm tm(GetTm());
1652     tm.sec = sec;
1653     Set(tm);
1654 
1655     return *this;
1656 }
1657 
SetMillisecond(wxDateTime_t millisecond)1658 wxDateTime& wxDateTime::SetMillisecond(wxDateTime_t millisecond)
1659 {
1660     wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
1661 
1662     // we don't need to use GetTm() for this one
1663     m_time -= m_time % 1000l;
1664     m_time += millisecond;
1665 
1666     return *this;
1667 }
1668 
1669 // ----------------------------------------------------------------------------
1670 // wxDateTime arithmetics
1671 // ----------------------------------------------------------------------------
1672 
Add(const wxDateSpan & diff)1673 wxDateTime& wxDateTime::Add(const wxDateSpan& diff)
1674 {
1675     Tm tm(GetTm());
1676 
1677     tm.year += diff.GetYears();
1678     tm.AddMonths(diff.GetMonths());
1679 
1680     // check that the resulting date is valid
1681     if ( tm.mday > GetNumOfDaysInMonth(tm.year, tm.mon) )
1682     {
1683         // We suppose that when adding one month to Jan 31 we want to get Feb
1684         // 28 (or 29), i.e. adding a month to the last day of the month should
1685         // give the last day of the next month which is quite logical.
1686         //
1687         // Unfortunately, there is no logic way to understand what should
1688         // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1689         // We make it Feb 28 (last day too), but it is highly questionable.
1690         tm.mday = GetNumOfDaysInMonth(tm.year, tm.mon);
1691     }
1692 
1693     tm.AddDays(diff.GetTotalDays());
1694 
1695     Set(tm);
1696 
1697     wxASSERT_MSG( IsSameTime(tm),
1698                   wxT("Add(wxDateSpan) shouldn't modify time") );
1699 
1700     return *this;
1701 }
1702 
DiffAsDateSpan(const wxDateTime & dt) const1703 wxDateSpan wxDateTime::DiffAsDateSpan(const wxDateTime& dt) const
1704 {
1705     wxASSERT_MSG( IsValid() && dt.IsValid(), wxT("invalid wxDateTime"));
1706 
1707     // If dt is larger than this, calculations below needs to be inverted.
1708     int inv = 1;
1709     if ( dt > *this )
1710         inv = -1;
1711 
1712     int y = GetYear() - dt.GetYear();
1713     int m = GetMonth() - dt.GetMonth();
1714     int d = GetDay() - dt.GetDay();
1715 
1716     // If month diff is negative, dt is the year before, so decrease year
1717     // and set month diff to its inverse, e.g. January - December should be 1,
1718     // not -11.
1719     if ( m * inv < 0 || (m == 0 && d * inv < 0))
1720     {
1721         m += inv * MONTHS_IN_YEAR;
1722         y -= inv;
1723     }
1724 
1725     // Same logic for days as for months above.
1726     if ( d * inv < 0 )
1727     {
1728         // Use number of days in month from the month which end date we're
1729         // crossing. That is month before this for positive diff, and this
1730         // month for negative diff.
1731         // If we're on january and using previous month, we get december
1732         // previous year, but don't care, december has same amount of days
1733         // every year.
1734         wxDateTime::Month monthfordays = GetMonth();
1735         if (inv > 0 && monthfordays == wxDateTime::Jan)
1736             monthfordays = wxDateTime::Dec;
1737         else if (inv > 0)
1738             monthfordays = static_cast<wxDateTime::Month>(monthfordays - 1);
1739 
1740         d += inv * wxDateTime::GetNumberOfDays(monthfordays, GetYear());
1741         m -= inv;
1742     }
1743 
1744     int w =  d / DAYS_PER_WEEK;
1745 
1746     // Remove weeks from d, since wxDateSpan only keep days as the ones
1747     // not in complete weeks
1748     d -= w * DAYS_PER_WEEK;
1749 
1750     return wxDateSpan(y, m, w, d);
1751 }
1752 
1753 // ----------------------------------------------------------------------------
1754 // Weekday and monthday stuff
1755 // ----------------------------------------------------------------------------
1756 
1757 // convert Sun, Mon, ..., Sat into 6, 0, ..., 5
ConvertWeekDayToMondayBase(int wd)1758 static inline int ConvertWeekDayToMondayBase(int wd)
1759 {
1760     return wd == wxDateTime::Sun ? 6 : wd - 1;
1761 }
1762 
1763 /* static */
1764 wxDateTime
SetToWeekOfYear(int year,wxDateTime_t numWeek,WeekDay wd)1765 wxDateTime::SetToWeekOfYear(int year, wxDateTime_t numWeek, WeekDay wd)
1766 {
1767     wxASSERT_MSG( numWeek > 0,
1768                   wxT("invalid week number: weeks are counted from 1") );
1769 
1770     // Jan 4 always lies in the 1st week of the year
1771     wxDateTime dt(4, Jan, year);
1772     dt.SetToWeekDayInSameWeek(wd);
1773     dt += wxDateSpan::Weeks(numWeek - 1);
1774 
1775     return dt;
1776 }
1777 
SetToLastMonthDay(Month month,int year)1778 wxDateTime& wxDateTime::SetToLastMonthDay(Month month,
1779                                           int year)
1780 {
1781     // take the current month/year if none specified
1782     if ( year == Inv_Year )
1783         year = GetYear();
1784     if ( month == Inv_Month )
1785         month = GetMonth();
1786 
1787     return Set(GetNumOfDaysInMonth(year, month), month, year);
1788 }
1789 
SetToWeekDayInSameWeek(WeekDay weekday,WeekFlags flags)1790 wxDateTime& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday, WeekFlags flags)
1791 {
1792     wxDATETIME_CHECK( weekday != Inv_WeekDay, wxT("invalid weekday") );
1793 
1794     int wdayDst = weekday,
1795         wdayThis = GetWeekDay();
1796     if ( wdayDst == wdayThis )
1797     {
1798         // nothing to do
1799         return *this;
1800     }
1801 
1802     UseEffectiveWeekDayFlags(flags);
1803 
1804     // the logic below based on comparing weekday and wdayThis works if Sun (0)
1805     // is the first day in the week, but breaks down for Monday_First case so
1806     // we adjust the week days in this case
1807     if ( flags == Monday_First )
1808     {
1809         if ( wdayThis == Sun )
1810             wdayThis += 7;
1811         if ( wdayDst == Sun )
1812             wdayDst += 7;
1813     }
1814     //else: Sunday_First, nothing to do
1815 
1816     // go forward or back in time to the day we want
1817     if ( wdayDst < wdayThis )
1818     {
1819         return Subtract(wxDateSpan::Days(wdayThis - wdayDst));
1820     }
1821     else // weekday > wdayThis
1822     {
1823         return Add(wxDateSpan::Days(wdayDst - wdayThis));
1824     }
1825 }
1826 
SetToNextWeekDay(WeekDay weekday)1827 wxDateTime& wxDateTime::SetToNextWeekDay(WeekDay weekday)
1828 {
1829     wxDATETIME_CHECK( weekday != Inv_WeekDay, wxT("invalid weekday") );
1830 
1831     int diff;
1832     WeekDay wdayThis = GetWeekDay();
1833     if ( weekday == wdayThis )
1834     {
1835         // nothing to do
1836         return *this;
1837     }
1838     else if ( weekday < wdayThis )
1839     {
1840         // need to advance a week
1841         diff = 7 - (wdayThis - weekday);
1842     }
1843     else // weekday > wdayThis
1844     {
1845         diff = weekday - wdayThis;
1846     }
1847 
1848     return Add(wxDateSpan::Days(diff));
1849 }
1850 
SetToPrevWeekDay(WeekDay weekday)1851 wxDateTime& wxDateTime::SetToPrevWeekDay(WeekDay weekday)
1852 {
1853     wxDATETIME_CHECK( weekday != Inv_WeekDay, wxT("invalid weekday") );
1854 
1855     int diff;
1856     WeekDay wdayThis = GetWeekDay();
1857     if ( weekday == wdayThis )
1858     {
1859         // nothing to do
1860         return *this;
1861     }
1862     else if ( weekday > wdayThis )
1863     {
1864         // need to go to previous week
1865         diff = 7 - (weekday - wdayThis);
1866     }
1867     else // weekday < wdayThis
1868     {
1869         diff = wdayThis - weekday;
1870     }
1871 
1872     return Subtract(wxDateSpan::Days(diff));
1873 }
1874 
SetToWeekDay(WeekDay weekday,int n,Month month,int year)1875 bool wxDateTime::SetToWeekDay(WeekDay weekday,
1876                               int n,
1877                               Month month,
1878                               int year)
1879 {
1880     wxCHECK_MSG( weekday != Inv_WeekDay, false, wxT("invalid weekday") );
1881 
1882     // we don't check explicitly that -5 <= n <= 5 because we will return false
1883     // anyhow in such case - but may be should still give an assert for it?
1884 
1885     // take the current month/year if none specified
1886     ReplaceDefaultYearMonthWithCurrent(&year, &month);
1887 
1888     wxDateTime dt;
1889 
1890     // TODO this probably could be optimised somehow...
1891 
1892     if ( n > 0 )
1893     {
1894         // get the first day of the month
1895         dt.Set(1, month, year);
1896 
1897         // get its wday
1898         WeekDay wdayFirst = dt.GetWeekDay();
1899 
1900         // go to the first weekday of the month
1901         int diff = weekday - wdayFirst;
1902         if ( diff < 0 )
1903             diff += 7;
1904 
1905         // add advance n-1 weeks more
1906         diff += 7*(n - 1);
1907 
1908         dt += wxDateSpan::Days(diff);
1909     }
1910     else // count from the end of the month
1911     {
1912         // get the last day of the month
1913         dt.SetToLastMonthDay(month, year);
1914 
1915         // get its wday
1916         WeekDay wdayLast = dt.GetWeekDay();
1917 
1918         // go to the last weekday of the month
1919         int diff = wdayLast - weekday;
1920         if ( diff < 0 )
1921             diff += 7;
1922 
1923         // and rewind n-1 weeks from there
1924         diff += 7*(-n - 1);
1925 
1926         dt -= wxDateSpan::Days(diff);
1927     }
1928 
1929     // check that it is still in the same month
1930     if ( dt.GetMonth() == month )
1931     {
1932         *this = dt;
1933 
1934         return true;
1935     }
1936     else
1937     {
1938         // no such day in this month
1939         return false;
1940     }
1941 }
1942 
1943 static inline
GetDayOfYearFromTm(const wxDateTime::Tm & tm)1944 wxDateTime::wxDateTime_t GetDayOfYearFromTm(const wxDateTime::Tm& tm)
1945 {
1946     return (wxDateTime::wxDateTime_t)(gs_cumulatedDays[wxDateTime::IsLeapYear(tm.year)][tm.mon] + tm.mday);
1947 }
1948 
GetDayOfYear(const TimeZone & tz) const1949 wxDateTime::wxDateTime_t wxDateTime::GetDayOfYear(const TimeZone& tz) const
1950 {
1951     return GetDayOfYearFromTm(GetTm(tz));
1952 }
1953 
1954 wxDateTime::wxDateTime_t
GetWeekOfYear(wxDateTime::WeekFlags flags,const TimeZone & tz) const1955 wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags, const TimeZone& tz) const
1956 {
1957     UseEffectiveWeekDayFlags(flags);
1958 
1959     Tm tm(GetTm(tz));
1960     wxDateTime_t nDayInYear = GetDayOfYearFromTm(tm);
1961 
1962     int wdTarget = GetWeekDay(tz);
1963     int wdYearStart = wxDateTime(1, Jan, GetYear()).GetWeekDay();
1964     int week;
1965     if ( flags == Sunday_First )
1966     {
1967         // FIXME: First week is not calculated correctly.
1968         week = (nDayInYear - wdTarget + 7) / 7;
1969         if ( wdYearStart == Wed || wdYearStart == Thu )
1970             week++;
1971     }
1972     else // week starts with monday
1973     {
1974         // adjust the weekdays to non-US style.
1975         wdYearStart = ConvertWeekDayToMondayBase(wdYearStart);
1976 
1977         // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
1978         //
1979         //      Week 01 of a year is per definition the first week that has the
1980         //      Thursday in this year, which is equivalent to the week that
1981         //      contains the fourth day of January. In other words, the first
1982         //      week of a new year is the week that has the majority of its
1983         //      days in the new year. Week 01 might also contain days from the
1984         //      previous year and the week before week 01 of a year is the last
1985         //      week (52 or 53) of the previous year even if it contains days
1986         //      from the new year. A week starts with Monday (day 1) and ends
1987         //      with Sunday (day 7).
1988         //
1989 
1990         // if Jan 1 is Thursday or less, it is in the first week of this year
1991         int dayCountFix = wdYearStart < 4 ? 6 : -1;
1992 
1993         // count the number of week
1994         week = (nDayInYear + wdYearStart + dayCountFix) / DAYS_PER_WEEK;
1995 
1996         // check if we happen to be at the last week of previous year:
1997         if ( week == 0 )
1998         {
1999             week = wxDateTime(31, Dec, GetYear() - 1).GetWeekOfYear();
2000         }
2001         else if ( week == 53 )
2002         {
2003             int wdYearEnd = (wdYearStart + 364 + IsLeapYear(GetYear()))
2004                                 % DAYS_PER_WEEK;
2005 
2006             // Week 53 only if last day of year is Thursday or later.
2007             if ( wdYearEnd < 3 )
2008                 week = 1;
2009         }
2010     }
2011 
2012     return (wxDateTime::wxDateTime_t)week;
2013 }
2014 
GetWeekBasedYear(const TimeZone & tz) const2015 int wxDateTime::GetWeekBasedYear(const TimeZone& tz) const
2016 {
2017     const wxDateTime::Tm tm = GetTm(tz);
2018 
2019     int year = tm.year;
2020 
2021     // The week-based year can only be different from the normal year for few
2022     // days in the beginning and the end of the year.
2023     if ( tm.yday > 361 )
2024     {
2025         if ( GetWeekOfYear(Monday_First, tz) == 1 )
2026             year++;
2027     }
2028     else if ( tm.yday < 5 )
2029     {
2030         if ( GetWeekOfYear(Monday_First, tz) == 53 )
2031             year--;
2032     }
2033 
2034     return year;
2035 }
2036 
GetWeekOfMonth(wxDateTime::WeekFlags flags,const TimeZone & tz) const2037 wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags,
2038                                                     const TimeZone& tz) const
2039 {
2040     Tm tm = GetTm(tz);
2041     const wxDateTime dateFirst = wxDateTime(1, tm.mon, tm.year);
2042     const wxDateTime::WeekDay wdFirst = dateFirst.GetWeekDay();
2043 
2044     UseEffectiveWeekDayFlags(flags);
2045 
2046     // compute offset of dateFirst from the beginning of the week
2047     int firstOffset;
2048     if ( flags == Sunday_First )
2049         firstOffset = wdFirst - Sun;
2050     else
2051         firstOffset = wdFirst == Sun ? DAYS_PER_WEEK - 1 : wdFirst - Mon;
2052 
2053     return (wxDateTime::wxDateTime_t)((tm.mday - 1 + firstOffset)/7 + 1);
2054 }
2055 
SetToYearDay(wxDateTime::wxDateTime_t yday)2056 wxDateTime& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday)
2057 {
2058     int year = GetYear();
2059     wxDATETIME_CHECK( (0 < yday) && (yday <= GetNumberOfDays(year)),
2060                       wxT("invalid year day") );
2061 
2062     bool isLeap = IsLeapYear(year);
2063     for ( Month mon = Jan; mon < Inv_Month; wxNextMonth(mon) )
2064     {
2065         // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
2066         // don't need it neither - because of the CHECK above we know that
2067         // yday lies in December then
2068         if ( (mon == Dec) || (yday <= gs_cumulatedDays[isLeap][mon + 1]) )
2069         {
2070             Set((wxDateTime::wxDateTime_t)(yday - gs_cumulatedDays[isLeap][mon]), mon, year);
2071 
2072             break;
2073         }
2074     }
2075 
2076     return *this;
2077 }
2078 
2079 // ----------------------------------------------------------------------------
2080 // Julian day number conversion and related stuff
2081 // ----------------------------------------------------------------------------
2082 
GetJulianDayNumber() const2083 double wxDateTime::GetJulianDayNumber() const
2084 {
2085     return m_time.ToDouble() / MILLISECONDS_PER_DAY + EPOCH_JDN + 0.5;
2086 }
2087 
GetRataDie() const2088 double wxDateTime::GetRataDie() const
2089 {
2090     // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
2091     return GetJulianDayNumber() - 1721119.5 - 306;
2092 }
2093 
2094 // ----------------------------------------------------------------------------
2095 // timezone and DST stuff
2096 // ----------------------------------------------------------------------------
2097 
IsDST(wxDateTime::Country country) const2098 int wxDateTime::IsDST(wxDateTime::Country country) const
2099 {
2100     wxCHECK_MSG( country == Country_Default, -1,
2101                  wxT("country support not implemented") );
2102 
2103     // use the C RTL for the dates in the standard range
2104     time_t timet = GetTicks();
2105     if ( timet != (time_t)-1 )
2106     {
2107         struct tm tmstruct;
2108         tm *tm = wxLocaltime_r(&timet, &tmstruct);
2109 
2110         wxCHECK_MSG( tm, -1, wxT("wxLocaltime_r() failed") );
2111 
2112         return tm->tm_isdst;
2113     }
2114     else
2115     {
2116         int year = GetYear();
2117 
2118         country = GetCountry();
2119         switch ( country )
2120         {
2121             case UK:
2122                 // There is a special, but important, case of UK which was
2123                 // permanently on BST, i.e. using DST, during this period. It
2124                 // is important because it covers Unix epoch and without
2125                 // accounting for the DST during it, various tests done around
2126                 // the epoch time would fail in BST time zone (only!).
2127                 if ( IsEarlierThan(wxDateTime(31, Oct, 1971)) &&
2128                         IsLaterThan(wxDateTime(27, Oct, 1968)) )
2129                 {
2130                     return true;
2131                 }
2132                 wxFALLTHROUGH;
2133 
2134             default:
2135                 if ( !IsDSTApplicable(year, country) )
2136                 {
2137                     // no DST time in this year in this country
2138                     return -1;
2139                 }
2140         }
2141 
2142         return IsBetween(GetBeginDST(year, country), GetEndDST(year, country));
2143     }
2144 }
2145 
MakeTimezone(const TimeZone & tz,bool noDST)2146 wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST)
2147 {
2148     long secDiff = wxGetTimeZone() + tz.GetOffset();
2149 
2150     // We are converting from the local time to some other time zone, but local
2151     // time zone does not include the DST offset (as it varies depending on the
2152     // date), so we have to handle DST manually, unless a special flag
2153     // inhibiting this was specified.
2154     if ( !noDST && (IsDST() == 1) && !tz.IsLocal() )
2155     {
2156         secDiff -= DST_OFFSET;
2157     }
2158 
2159     return Add(wxTimeSpan::Seconds(secDiff));
2160 }
2161 
MakeFromTimezone(const TimeZone & tz,bool noDST)2162 wxDateTime& wxDateTime::MakeFromTimezone(const TimeZone& tz, bool noDST)
2163 {
2164     long secDiff = wxGetTimeZone() + tz.GetOffset();
2165 
2166     // See comment in MakeTimezone() above, the logic here is exactly the same.
2167     if ( !noDST && (IsDST() == 1) && !tz.IsLocal() )
2168     {
2169         secDiff -= DST_OFFSET;
2170     }
2171 
2172     return Subtract(wxTimeSpan::Seconds(secDiff));
2173 }
2174 
UseEffectiveWeekDayFlags(WeekFlags & flags) const2175 void wxDateTime::UseEffectiveWeekDayFlags(WeekFlags &flags) const
2176 {
2177     if ( flags == Default_First )
2178     {
2179         WeekDay firstDay;
2180         GetFirstWeekDay(&firstDay);
2181         flags = firstDay == Sun ? Sunday_First : Monday_First;
2182     }
2183 }
2184 
2185 // ============================================================================
2186 // wxDateTimeHolidayAuthority and related classes
2187 // ============================================================================
2188 
2189 #include "wx/arrimpl.cpp"
2190 
WX_DEFINE_OBJARRAY(wxDateTimeArray)2191 WX_DEFINE_OBJARRAY(wxDateTimeArray)
2192 
2193 static int wxCMPFUNC_CONV
2194 wxDateTimeCompareFunc(wxDateTime **first, wxDateTime **second)
2195 {
2196     wxDateTime dt1 = **first,
2197                dt2 = **second;
2198 
2199     return dt1 == dt2 ? 0 : dt1 < dt2 ? -1 : +1;
2200 }
2201 
2202 // ----------------------------------------------------------------------------
2203 // wxDateTimeHolidayAuthority
2204 // ----------------------------------------------------------------------------
2205 
2206 wxHolidayAuthoritiesArray wxDateTimeHolidayAuthority::ms_authorities;
2207 
2208 /* static */
IsHoliday(const wxDateTime & dt)2209 bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime& dt)
2210 {
2211     size_t count = ms_authorities.size();
2212     for ( size_t n = 0; n < count; n++ )
2213     {
2214         if ( ms_authorities[n]->DoIsHoliday(dt) )
2215         {
2216             return true;
2217         }
2218     }
2219 
2220     return false;
2221 }
2222 
2223 /* static */
2224 size_t
GetHolidaysInRange(const wxDateTime & dtStart,const wxDateTime & dtEnd,wxDateTimeArray & holidays)2225 wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime& dtStart,
2226                                                const wxDateTime& dtEnd,
2227                                                wxDateTimeArray& holidays)
2228 {
2229     wxDateTimeArray hol;
2230 
2231     holidays.Clear();
2232 
2233     const size_t countAuth = ms_authorities.size();
2234     for ( size_t nAuth = 0; nAuth < countAuth; nAuth++ )
2235     {
2236         ms_authorities[nAuth]->DoGetHolidaysInRange(dtStart, dtEnd, hol);
2237 
2238         WX_APPEND_ARRAY(holidays, hol);
2239     }
2240 
2241     holidays.Sort(wxDateTimeCompareFunc);
2242 
2243     return holidays.size();
2244 }
2245 
2246 /* static */
ClearAllAuthorities()2247 void wxDateTimeHolidayAuthority::ClearAllAuthorities()
2248 {
2249     WX_CLEAR_ARRAY(ms_authorities);
2250 }
2251 
2252 /* static */
AddAuthority(wxDateTimeHolidayAuthority * auth)2253 void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority *auth)
2254 {
2255     ms_authorities.push_back(auth);
2256 }
2257 
~wxDateTimeHolidayAuthority()2258 wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
2259 {
2260     // required here for Darwin
2261 }
2262 
2263 // ----------------------------------------------------------------------------
2264 // wxDateTimeWorkDays
2265 // ----------------------------------------------------------------------------
2266 
DoIsHoliday(const wxDateTime & dt) const2267 bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime& dt) const
2268 {
2269     wxDateTime::WeekDay wd = dt.GetWeekDay();
2270 
2271     return (wd == wxDateTime::Sun) || (wd == wxDateTime::Sat);
2272 }
2273 
DoGetHolidaysInRange(const wxDateTime & dtStart,const wxDateTime & dtEnd,wxDateTimeArray & holidays) const2274 size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime& dtStart,
2275                                                 const wxDateTime& dtEnd,
2276                                                 wxDateTimeArray& holidays) const
2277 {
2278     if ( dtStart > dtEnd )
2279     {
2280         wxFAIL_MSG( wxT("invalid date range in GetHolidaysInRange") );
2281 
2282         return 0u;
2283     }
2284 
2285     holidays.Empty();
2286 
2287     // instead of checking all days, start with the first Sat after dtStart and
2288     // end with the last Sun before dtEnd
2289     wxDateTime dtSatFirst = dtStart.GetNextWeekDay(wxDateTime::Sat),
2290                dtSatLast = dtEnd.GetPrevWeekDay(wxDateTime::Sat),
2291                dtSunFirst = dtStart.GetNextWeekDay(wxDateTime::Sun),
2292                dtSunLast = dtEnd.GetPrevWeekDay(wxDateTime::Sun),
2293                dt;
2294 
2295     for ( dt = dtSatFirst; dt <= dtSatLast; dt += wxDateSpan::Week() )
2296     {
2297         holidays.Add(dt);
2298     }
2299 
2300     for ( dt = dtSunFirst; dt <= dtSunLast; dt += wxDateSpan::Week() )
2301     {
2302         holidays.Add(dt);
2303     }
2304 
2305     return holidays.GetCount();
2306 }
2307 
2308 // ============================================================================
2309 // other helper functions
2310 // ============================================================================
2311 
2312 // ----------------------------------------------------------------------------
2313 // iteration helpers: can be used to write a for loop over enum variable like
2314 // this:
2315 //  for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
2316 // ----------------------------------------------------------------------------
2317 
wxNextMonth(wxDateTime::Month & m)2318 WXDLLIMPEXP_BASE void wxNextMonth(wxDateTime::Month& m)
2319 {
2320     wxASSERT_MSG( m < wxDateTime::Inv_Month, wxT("invalid month") );
2321 
2322     // no wrapping or the for loop above would never end!
2323     m = (wxDateTime::Month)(m + 1);
2324 }
2325 
wxPrevMonth(wxDateTime::Month & m)2326 WXDLLIMPEXP_BASE void wxPrevMonth(wxDateTime::Month& m)
2327 {
2328     wxASSERT_MSG( m < wxDateTime::Inv_Month, wxT("invalid month") );
2329 
2330     m = m == wxDateTime::Jan ? wxDateTime::Inv_Month
2331                              : (wxDateTime::Month)(m - 1);
2332 }
2333 
wxNextWDay(wxDateTime::WeekDay & wd)2334 WXDLLIMPEXP_BASE void wxNextWDay(wxDateTime::WeekDay& wd)
2335 {
2336     wxASSERT_MSG( wd < wxDateTime::Inv_WeekDay, wxT("invalid week day") );
2337 
2338     // no wrapping or the for loop above would never end!
2339     wd = (wxDateTime::WeekDay)(wd + 1);
2340 }
2341 
wxPrevWDay(wxDateTime::WeekDay & wd)2342 WXDLLIMPEXP_BASE void wxPrevWDay(wxDateTime::WeekDay& wd)
2343 {
2344     wxASSERT_MSG( wd < wxDateTime::Inv_WeekDay, wxT("invalid week day") );
2345 
2346     wd = wd == wxDateTime::Sun ? wxDateTime::Inv_WeekDay
2347                                : (wxDateTime::WeekDay)(wd - 1);
2348 }
2349 
2350 #ifdef __WINDOWS__
2351 
SetFromMSWSysTime(const SYSTEMTIME & st)2352 wxDateTime& wxDateTime::SetFromMSWSysTime(const SYSTEMTIME& st)
2353 {
2354     return Set(st.wDay,
2355             static_cast<wxDateTime::Month>(wxDateTime::Jan + st.wMonth - 1),
2356             st.wYear,
2357             st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
2358 }
2359 
SetFromMSWSysDate(const SYSTEMTIME & st)2360 wxDateTime& wxDateTime::SetFromMSWSysDate(const SYSTEMTIME& st)
2361 {
2362     return Set(st.wDay,
2363             static_cast<wxDateTime::Month>(wxDateTime::Jan + st.wMonth - 1),
2364             st.wYear,
2365             0, 0, 0, 0);
2366 }
2367 
GetAsMSWSysTime(SYSTEMTIME * st) const2368 void wxDateTime::GetAsMSWSysTime(SYSTEMTIME* st) const
2369 {
2370     const wxDateTime::Tm tm(GetTm());
2371 
2372     st->wYear = (WXWORD)tm.year;
2373     st->wMonth = (WXWORD)(tm.mon - wxDateTime::Jan + 1);
2374     st->wDay = tm.mday;
2375 
2376     st->wDayOfWeek = 0;
2377     st->wHour = tm.hour;
2378     st->wMinute = tm.min;
2379     st->wSecond = tm.sec;
2380     st->wMilliseconds = tm.msec;
2381 }
2382 
GetAsMSWSysDate(SYSTEMTIME * st) const2383 void wxDateTime::GetAsMSWSysDate(SYSTEMTIME* st) const
2384 {
2385     const wxDateTime::Tm tm(GetTm());
2386 
2387     st->wYear = (WXWORD)tm.year;
2388     st->wMonth = (WXWORD)(tm.mon - wxDateTime::Jan + 1);
2389     st->wDay = tm.mday;
2390 
2391     st->wDayOfWeek =
2392     st->wHour =
2393     st->wMinute =
2394     st->wSecond =
2395     st->wMilliseconds = 0;
2396 }
2397 
2398 #endif // __WINDOWS__
2399 
2400 #endif // wxUSE_DATETIME
2401