/[pcsx2_0.9.7]/trunk/3rdparty/wxWidgets/src/common/datetime.cpp
ViewVC logotype

Annotation of /trunk/3rdparty/wxWidgets/src/common/datetime.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 31 - (hide annotations) (download)
Tue Sep 7 03:24:11 2010 UTC (10 years, 4 months ago) by william
File size: 140298 byte(s)
committing r3113 initial commit again...
1 william 31 ///////////////////////////////////////////////////////////////////////////////
2     // Name: src/common/datetime.cpp
3     // Purpose: implementation of time/date related classes
4     // Author: Vadim Zeitlin
5     // Modified by:
6     // Created: 11.05.99
7     // RCS-ID: $Id: datetime.cpp 58486 2009-01-28 21:52:37Z VZ $
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     #ifdef __BORLANDC__
59     #pragma hdrstop
60     #endif
61    
62     #if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
63    
64     #ifndef WX_PRECOMP
65     #ifdef __WXMSW__
66     #include "wx/msw/wrapwin.h"
67     #endif
68     #include "wx/string.h"
69     #include "wx/log.h"
70     #include "wx/intl.h"
71     #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
72     #include "wx/module.h"
73     #endif // WX_PRECOMP
74    
75     #include "wx/thread.h"
76     #include "wx/tokenzr.h"
77    
78     #include <ctype.h>
79    
80     #ifdef __WINDOWS__
81     #include <winnls.h>
82     #ifndef __WXWINCE__
83     #include <locale.h>
84     #endif
85     #endif
86    
87     #include "wx/datetime.h"
88    
89     const long wxDateTime::TIME_T_FACTOR = 1000l;
90    
91     #if wxUSE_EXTENDED_RTTI
92    
93     template<> void wxStringReadValue(const wxString &s , wxDateTime &data )
94     {
95     data.ParseFormat(s,wxT("%Y-%m-%d %H:%M:%S")) ;
96     }
97    
98     template<> void wxStringWriteValue(wxString &s , const wxDateTime &data )
99     {
100     s = data.Format(wxT("%Y-%m-%d %H:%M:%S")) ;
101     }
102    
103     wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter<wxDateTime> , wxFromStringConverter<wxDateTime>)
104    
105     #endif
106    
107     //
108     // ----------------------------------------------------------------------------
109     // conditional compilation
110     // ----------------------------------------------------------------------------
111    
112     #if defined(HAVE_STRPTIME) && defined(__GLIBC__) && \
113     ((__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0))
114     // glibc 2.0.7 strptime() is broken - the following snippet causes it to
115     // crash (instead of just failing):
116     //
117     // strncpy(buf, "Tue Dec 21 20:25:40 1999", 128);
118     // strptime(buf, "%x", &tm);
119     //
120     // so don't use it
121     #undef HAVE_STRPTIME
122     #endif // broken strptime()
123    
124     #if defined(HAVE_STRPTIME) && defined(__DARWIN__) && defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
125     // configure detects strptime as linkable because it's in the OS X
126     // System library but MSL headers don't declare it.
127    
128     // char *strptime(const char *, const char *, struct tm *);
129     // However, we DON'T want to just provide it here because we would
130     // crash and/or overwrite data when strptime from OS X tries
131     // to fill in MW's struct tm which is two fields shorter (no TZ stuff)
132     // So for now let's just say we don't have strptime
133     #undef HAVE_STRPTIME
134     #endif
135    
136     #if defined(__MWERKS__) && wxUSE_UNICODE
137     #include <wtime.h>
138     #endif
139    
140     // define a special symbol for VC8 instead of writing tests for 1400 repeatedly
141     #ifdef __VISUALC__
142     #if __VISUALC__ >= 1400
143     #define __VISUALC8__
144     #endif
145     #endif
146    
147     #if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
148     #if defined(__WXPALMOS__)
149     #define WX_GMTOFF_IN_TM
150     #elif defined(__WXMSW__)
151     static long wxGetTimeZone()
152     {
153     static long s_timezone = MAXLONG; // invalid timezone
154     if (s_timezone == MAXLONG)
155     {
156     TIME_ZONE_INFORMATION info;
157     GetTimeZoneInformation(&info);
158     s_timezone = info.Bias * 60; // convert minutes to seconds
159     }
160     return s_timezone;
161     }
162     #define WX_TIMEZONE wxGetTimeZone()
163     #elif defined(__VISAGECPP__)
164     #define WX_TIMEZONE _timezone
165     #elif defined(__MWERKS__)
166     long wxmw_timezone = 28800;
167     #define WX_TIMEZONE wxmw_timezone
168     #elif defined(__DARWIN__)
169     #define WX_GMTOFF_IN_TM
170     #else // unknown platform - try timezone
171     #define WX_TIMEZONE timezone
172     #endif
173     #endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
174    
175     // everyone has strftime except Win CE unless VC8 is used
176     #if !defined(__WXWINCE__) || defined(__VISUALC8__)
177     #define HAVE_STRFTIME
178     #endif
179    
180     // NB: VC8 safe time functions could/should be used for wxMSW as well probably
181     #if defined(__WXWINCE__) && defined(__VISUALC8__)
182    
183     struct tm *wxLocaltime_r(const time_t *t, struct tm* tm)
184     {
185     __time64_t t64 = *t;
186     return _localtime64_s(tm, &t64) == 0 ? tm : NULL;
187     }
188    
189     struct tm *wxGmtime_r(const time_t* t, struct tm* tm)
190     {
191     __time64_t t64 = *t;
192     return _gmtime64_s(tm, &t64) == 0 ? tm : NULL;
193     }
194    
195     #else // !wxWinCE with VC8
196    
197     #if (!defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)) && wxUSE_THREADS && !defined(__WINDOWS__)
198     static wxMutex timeLock;
199     #endif
200    
201     #ifndef HAVE_LOCALTIME_R
202     struct tm *wxLocaltime_r(const time_t* ticks, struct tm* temp)
203     {
204     #if wxUSE_THREADS && !defined(__WINDOWS__)
205     // No need to waste time with a mutex on windows since it's using
206     // thread local storage for localtime anyway.
207     wxMutexLocker locker(timeLock);
208     #endif
209    
210     // Borland CRT crashes when passed 0 ticks for some reason, see SF bug 1704438
211     #ifdef __BORLANDC__
212     if ( !*ticks )
213     return NULL;
214     #endif
215    
216     const tm * const t = localtime(ticks);
217     if ( !t )
218     return NULL;
219    
220     memcpy(temp, t, sizeof(struct tm));
221     return temp;
222     }
223     #endif // !HAVE_LOCALTIME_R
224    
225     #ifndef HAVE_GMTIME_R
226     struct tm *wxGmtime_r(const time_t* ticks, struct tm* temp)
227     {
228     #if wxUSE_THREADS && !defined(__WINDOWS__)
229     // No need to waste time with a mutex on windows since it's
230     // using thread local storage for gmtime anyway.
231     wxMutexLocker locker(timeLock);
232     #endif
233    
234     #ifdef __BORLANDC__
235     if ( !*ticks )
236     return NULL;
237     #endif
238    
239     const tm * const t = gmtime(ticks);
240     if ( !t )
241     return NULL;
242    
243     memcpy(temp, gmtime(ticks), sizeof(struct tm));
244     return temp;
245     }
246     #endif // !HAVE_GMTIME_R
247    
248     #endif // wxWinCE with VC8/other platforms
249    
250     // ----------------------------------------------------------------------------
251     // macros
252     // ----------------------------------------------------------------------------
253    
254     // debugging helper: just a convenient replacement of wxCHECK()
255     #define wxDATETIME_CHECK(expr, msg) \
256     wxCHECK2_MSG(expr, *this = wxInvalidDateTime; return *this, msg)
257    
258     // ----------------------------------------------------------------------------
259     // private classes
260     // ----------------------------------------------------------------------------
261    
262     class wxDateTimeHolidaysModule : public wxModule
263     {
264     public:
265     virtual bool OnInit()
266     {
267     wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays);
268    
269     return true;
270     }
271    
272     virtual void OnExit()
273     {
274     wxDateTimeHolidayAuthority::ClearAllAuthorities();
275     wxDateTimeHolidayAuthority::ms_authorities.clear();
276     }
277    
278     private:
279     DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule)
280     };
281    
282     IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule, wxModule)
283    
284     // ----------------------------------------------------------------------------
285     // constants
286     // ----------------------------------------------------------------------------
287    
288     // some trivial ones
289     static const int MONTHS_IN_YEAR = 12;
290    
291     static const int SEC_PER_MIN = 60;
292    
293     static const int MIN_PER_HOUR = 60;
294    
295     static const int HOURS_PER_DAY = 24;
296    
297     static const long SECONDS_PER_DAY = 86400l;
298    
299     static const int DAYS_PER_WEEK = 7;
300    
301     static const long MILLISECONDS_PER_DAY = 86400000l;
302    
303     // this is the integral part of JDN of the midnight of Jan 1, 1970
304     // (i.e. JDN(Jan 1, 1970) = 2440587.5)
305     static const long EPOCH_JDN = 2440587l;
306    
307     // used only in asserts
308     #ifdef __WXDEBUG__
309     // the date of JDN -0.5 (as we don't work with fractional parts, this is the
310     // reference date for us) is Nov 24, 4714BC
311     static const int JDN_0_YEAR = -4713;
312     static const int JDN_0_MONTH = wxDateTime::Nov;
313     static const int JDN_0_DAY = 24;
314     #endif // __WXDEBUG__
315    
316     // the constants used for JDN calculations
317     static const long JDN_OFFSET = 32046l;
318     static const long DAYS_PER_5_MONTHS = 153l;
319     static const long DAYS_PER_4_YEARS = 1461l;
320     static const long DAYS_PER_400_YEARS = 146097l;
321    
322     // this array contains the cumulated number of days in all previous months for
323     // normal and leap years
324     static const wxDateTime::wxDateTime_t gs_cumulatedDays[2][MONTHS_IN_YEAR] =
325     {
326     { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
327     { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
328     };
329    
330     // ----------------------------------------------------------------------------
331     // global data
332     // ----------------------------------------------------------------------------
333    
334     const wxChar * wxDefaultDateTimeFormat = wxT("%c");
335     const wxChar * wxDefaultTimeSpanFormat = wxT("%H:%M:%S");
336    
337     // in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
338     // indicate an invalid wxDateTime object
339     const wxDateTime wxDefaultDateTime;
340    
341     wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown;
342    
343     // ----------------------------------------------------------------------------
344     // private functions
345     // ----------------------------------------------------------------------------
346    
347     // debugger helper: shows what the date really is
348     #ifdef __WXDEBUG__
349     extern const wxChar *wxDumpDate(const wxDateTime* dt)
350     {
351     static wxChar buf[128];
352    
353     wxStrcpy(buf, dt->Format(_T("%Y-%m-%d (%a) %H:%M:%S")));
354    
355     return buf;
356     }
357     #endif // Debug
358    
359     // get the number of days in the given month of the given year
360     static inline
361     wxDateTime::wxDateTime_t GetNumOfDaysInMonth(int year, wxDateTime::Month month)
362     {
363     // the number of days in month in Julian/Gregorian calendar: the first line
364     // is for normal years, the second one is for the leap ones
365     static wxDateTime::wxDateTime_t daysInMonth[2][MONTHS_IN_YEAR] =
366     {
367     { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
368     { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
369     };
370    
371     return daysInMonth[wxDateTime::IsLeapYear(year)][month];
372     }
373    
374     // returns the time zone in the C sense, i.e. the difference UTC - local
375     // (in seconds)
376     static int GetTimeZone()
377     {
378     // set to true when the timezone is set
379     static bool s_timezoneSet = false;
380     static long gmtoffset = LONG_MAX; // invalid timezone
381    
382     // ensure that the timezone variable is set by calling wxLocaltime_r
383     if ( !s_timezoneSet )
384     {
385     // just call wxLocaltime_r() instead of figuring out whether this
386     // system supports tzset(), _tzset() or something else
387     time_t t = 0;
388     struct tm tm;
389    
390     wxLocaltime_r(&t, &tm);
391     s_timezoneSet = true;
392    
393     #ifdef WX_GMTOFF_IN_TM
394     // note that GMT offset is the opposite of time zone and so to return
395     // consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
396     // cases we have to negate it
397     gmtoffset = -tm.tm_gmtoff;
398     #else // !WX_GMTOFF_IN_TM
399     gmtoffset = WX_TIMEZONE;
400     #endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
401     }
402    
403     return (int)gmtoffset;
404     }
405    
406     // return the integral part of the JDN for the midnight of the given date (to
407     // get the real JDN you need to add 0.5, this is, in fact, JDN of the
408     // noon of the previous day)
409     static long GetTruncatedJDN(wxDateTime::wxDateTime_t day,
410     wxDateTime::Month mon,
411     int year)
412     {
413     // CREDIT: code below is by Scott E. Lee (but bugs are mine)
414    
415     // check the date validity
416     wxASSERT_MSG(
417     (year > JDN_0_YEAR) ||
418     ((year == JDN_0_YEAR) && (mon > JDN_0_MONTH)) ||
419     ((year == JDN_0_YEAR) && (mon == JDN_0_MONTH) && (day >= JDN_0_DAY)),
420     _T("date out of range - can't convert to JDN")
421     );
422    
423     // make the year positive to avoid problems with negative numbers division
424     year += 4800;
425    
426     // months are counted from March here
427     int month;
428     if ( mon >= wxDateTime::Mar )
429     {
430     month = mon - 2;
431     }
432     else
433     {
434     month = mon + 10;
435     year--;
436     }
437    
438     // now we can simply add all the contributions together
439     return ((year / 100) * DAYS_PER_400_YEARS) / 4
440     + ((year % 100) * DAYS_PER_4_YEARS) / 4
441     + (month * DAYS_PER_5_MONTHS + 2) / 5
442     + day
443     - JDN_OFFSET;
444     }
445    
446     #ifdef HAVE_STRFTIME
447    
448     // this function is a wrapper around strftime(3) adding error checking
449     static wxString CallStrftime(const wxChar *format, const tm* tm)
450     {
451     wxChar buf[4096];
452     // Create temp wxString here to work around mingw/cygwin bug 1046059
453     // http://sourceforge.net/tracker/?func=detail&atid=102435&aid=1046059&group_id=2435
454     wxString s;
455    
456     if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) )
457     {
458     // buffer is too small?
459     wxFAIL_MSG(_T("strftime() failed"));
460     }
461    
462     s = buf;
463     return s;
464     }
465    
466     #endif // HAVE_STRFTIME
467    
468     #ifdef HAVE_STRPTIME
469    
470     #if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL)
471     // configure detected that we had strptime() but not its declaration,
472     // provide it ourselves
473     extern "C" char *strptime(const char *, const char *, struct tm *);
474     #endif
475    
476     // Unicode-friendly strptime() wrapper
477     static const wxChar *
478     CallStrptime(const wxChar *input, const char *fmt, tm *tm)
479     {
480     // the problem here is that strptime() returns pointer into the string we
481     // passed to it while we're really interested in the pointer into the
482     // original, Unicode, string so we try to transform the pointer back
483     #if wxUSE_UNICODE
484     wxCharBuffer inputMB(wxConvertWX2MB(input));
485     #else // ASCII
486     const char * const inputMB = input;
487     #endif // Unicode/Ascii
488    
489     const char *result = strptime(inputMB, fmt, tm);
490     if ( !result )
491     return NULL;
492    
493     #if wxUSE_UNICODE
494     // FIXME: this is wrong in presence of surrogates &c
495     return input + (result - inputMB.data());
496     #else // ASCII
497     return result;
498     #endif // Unicode/Ascii
499     }
500    
501     #endif // HAVE_STRPTIME
502    
503     // if year and/or month have invalid values, replace them with the current ones
504     static void ReplaceDefaultYearMonthWithCurrent(int *year,
505     wxDateTime::Month *month)
506     {
507     struct tm *tmNow = NULL;
508     struct tm tmstruct;
509    
510     if ( *year == wxDateTime::Inv_Year )
511     {
512     tmNow = wxDateTime::GetTmNow(&tmstruct);
513    
514     *year = 1900 + tmNow->tm_year;
515     }
516    
517     if ( *month == wxDateTime::Inv_Month )
518     {
519     if ( !tmNow )
520     tmNow = wxDateTime::GetTmNow(&tmstruct);
521    
522     *month = (wxDateTime::Month)tmNow->tm_mon;
523     }
524     }
525    
526     // fll the struct tm with default values
527     static void InitTm(struct tm& tm)
528     {
529     // struct tm may have etxra fields (undocumented and with unportable
530     // names) which, nevertheless, must be set to 0
531     memset(&tm, 0, sizeof(struct tm));
532    
533     tm.tm_mday = 1; // mday 0 is invalid
534     tm.tm_year = 76; // any valid year
535     tm.tm_isdst = -1; // auto determine
536     }
537    
538     // parsing helpers
539     // ---------------
540    
541     // return the month if the string is a month name or Inv_Month otherwise
542     static wxDateTime::Month GetMonthFromName(const wxString& name, int flags)
543     {
544     wxDateTime::Month mon;
545     for ( mon = wxDateTime::Jan; mon < wxDateTime::Inv_Month; wxNextMonth(mon) )
546     {
547     // case-insensitive comparison either one of or with both abbreviated
548     // and not versions
549     if ( flags & wxDateTime::Name_Full )
550     {
551     if ( name.CmpNoCase(wxDateTime::
552     GetMonthName(mon, wxDateTime::Name_Full)) == 0 )
553     {
554     break;
555     }
556     }
557    
558     if ( flags & wxDateTime::Name_Abbr )
559     {
560     if ( name.CmpNoCase(wxDateTime::
561     GetMonthName(mon, wxDateTime::Name_Abbr)) == 0 )
562     {
563     break;
564     }
565     }
566     }
567    
568     return mon;
569     }
570    
571     // return the weekday if the string is a weekday name or Inv_WeekDay otherwise
572     static wxDateTime::WeekDay GetWeekDayFromName(const wxString& name, int flags)
573     {
574     wxDateTime::WeekDay wd;
575     for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
576     {
577     // case-insensitive comparison either one of or with both abbreviated
578     // and not versions
579     if ( flags & wxDateTime::Name_Full )
580     {
581     if ( name.CmpNoCase(wxDateTime::
582     GetWeekDayName(wd, wxDateTime::Name_Full)) == 0 )
583     {
584     break;
585     }
586     }
587    
588     if ( flags & wxDateTime::Name_Abbr )
589     {
590     if ( name.CmpNoCase(wxDateTime::
591     GetWeekDayName(wd, wxDateTime::Name_Abbr)) == 0 )
592     {
593     break;
594     }
595     }
596     }
597    
598     return wd;
599     }
600    
601     /* static */
602     struct tm *wxDateTime::GetTmNow(struct tm *tmstruct)
603     {
604     time_t t = GetTimeNow();
605     return wxLocaltime_r(&t, tmstruct);
606     }
607    
608     // scans all digits (but no more than len) and returns the resulting number
609     static bool GetNumericToken(size_t len, const wxChar*& p, unsigned long *number)
610     {
611     size_t n = 1;
612     wxString s;
613     while ( wxIsdigit(*p) )
614     {
615     s += *p++;
616    
617     if ( len && ++n > len )
618     break;
619     }
620    
621     return !s.empty() && s.ToULong(number);
622     }
623    
624     // scans all alphabetic characters and returns the resulting string
625     static wxString GetAlphaToken(const wxChar*& p)
626     {
627     wxString s;
628     while ( wxIsalpha(*p) )
629     {
630     s += *p++;
631     }
632    
633     return s;
634     }
635    
636     // ============================================================================
637     // implementation of wxDateTime
638     // ============================================================================
639    
640     // ----------------------------------------------------------------------------
641     // struct Tm
642     // ----------------------------------------------------------------------------
643    
644     wxDateTime::Tm::Tm()
645     {
646     year = (wxDateTime_t)wxDateTime::Inv_Year;
647     mon = wxDateTime::Inv_Month;
648     mday = 0;
649     hour = min = sec = msec = 0;
650     wday = wxDateTime::Inv_WeekDay;
651     }
652    
653     wxDateTime::Tm::Tm(const struct tm& tm, const TimeZone& tz)
654     : m_tz(tz)
655     {
656     msec = 0;
657     sec = (wxDateTime::wxDateTime_t)tm.tm_sec;
658     min = (wxDateTime::wxDateTime_t)tm.tm_min;
659     hour = (wxDateTime::wxDateTime_t)tm.tm_hour;
660     mday = (wxDateTime::wxDateTime_t)tm.tm_mday;
661     mon = (wxDateTime::Month)tm.tm_mon;
662     year = 1900 + tm.tm_year;
663     wday = (wxDateTime::wxDateTime_t)tm.tm_wday;
664     yday = (wxDateTime::wxDateTime_t)tm.tm_yday;
665     }
666    
667     bool wxDateTime::Tm::IsValid() const
668     {
669     // we allow for the leap seconds, although we don't use them (yet)
670     return (year != wxDateTime::Inv_Year) && (mon != wxDateTime::Inv_Month) &&
671     (mday <= GetNumOfDaysInMonth(year, mon)) &&
672     (hour < 24) && (min < 60) && (sec < 62) && (msec < 1000);
673     }
674    
675     void wxDateTime::Tm::ComputeWeekDay()
676     {
677     // compute the week day from day/month/year: we use the dumbest algorithm
678     // possible: just compute our JDN and then use the (simple to derive)
679     // formula: weekday = (JDN + 1.5) % 7
680     wday = (wxDateTime::wxDateTime_t)((GetTruncatedJDN(mday, mon, year) + 2) % 7);
681     }
682    
683     void wxDateTime::Tm::AddMonths(int monDiff)
684     {
685     // normalize the months field
686     while ( monDiff < -mon )
687     {
688     year--;
689    
690     monDiff += MONTHS_IN_YEAR;
691     }
692    
693     while ( monDiff + mon >= MONTHS_IN_YEAR )
694     {
695     year++;
696    
697     monDiff -= MONTHS_IN_YEAR;
698     }
699    
700     mon = (wxDateTime::Month)(mon + monDiff);
701    
702     wxASSERT_MSG( mon >= 0 && mon < MONTHS_IN_YEAR, _T("logic error") );
703    
704     // NB: we don't check here that the resulting date is valid, this function
705     // is private and the caller must check it if needed
706     }
707    
708     void wxDateTime::Tm::AddDays(int dayDiff)
709     {
710     // normalize the days field
711     while ( dayDiff + mday < 1 )
712     {
713     AddMonths(-1);
714    
715     dayDiff += GetNumOfDaysInMonth(year, mon);
716     }
717    
718     mday = (wxDateTime::wxDateTime_t)( mday + dayDiff );
719     while ( mday > GetNumOfDaysInMonth(year, mon) )
720     {
721     mday -= GetNumOfDaysInMonth(year, mon);
722    
723     AddMonths(1);
724     }
725    
726     wxASSERT_MSG( mday > 0 && mday <= GetNumOfDaysInMonth(year, mon),
727     _T("logic error") );
728     }
729    
730     // ----------------------------------------------------------------------------
731     // class TimeZone
732     // ----------------------------------------------------------------------------
733    
734     wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz)
735     {
736     switch ( tz )
737     {
738     case wxDateTime::Local:
739     // get the offset from C RTL: it returns the difference GMT-local
740     // while we want to have the offset _from_ GMT, hence the '-'
741     m_offset = -GetTimeZone();
742     break;
743    
744     case wxDateTime::GMT_12:
745     case wxDateTime::GMT_11:
746     case wxDateTime::GMT_10:
747     case wxDateTime::GMT_9:
748     case wxDateTime::GMT_8:
749     case wxDateTime::GMT_7:
750     case wxDateTime::GMT_6:
751     case wxDateTime::GMT_5:
752     case wxDateTime::GMT_4:
753     case wxDateTime::GMT_3:
754     case wxDateTime::GMT_2:
755     case wxDateTime::GMT_1:
756     m_offset = -3600*(wxDateTime::GMT0 - tz);
757     break;
758    
759     case wxDateTime::GMT0:
760     case wxDateTime::GMT1:
761     case wxDateTime::GMT2:
762     case wxDateTime::GMT3:
763     case wxDateTime::GMT4:
764     case wxDateTime::GMT5:
765     case wxDateTime::GMT6:
766     case wxDateTime::GMT7:
767     case wxDateTime::GMT8:
768     case wxDateTime::GMT9:
769     case wxDateTime::GMT10:
770     case wxDateTime::GMT11:
771     case wxDateTime::GMT12:
772     case wxDateTime::GMT13:
773     m_offset = 3600*(tz - wxDateTime::GMT0);
774     break;
775    
776     case wxDateTime::A_CST:
777     // Central Standard Time in use in Australia = UTC + 9.5
778     m_offset = 60l*(9*MIN_PER_HOUR + MIN_PER_HOUR/2);
779     break;
780    
781     default:
782     wxFAIL_MSG( _T("unknown time zone") );
783     }
784     }
785    
786     // ----------------------------------------------------------------------------
787     // static functions
788     // ----------------------------------------------------------------------------
789    
790     /* static */
791     bool wxDateTime::IsLeapYear(int year, wxDateTime::Calendar cal)
792     {
793     if ( year == Inv_Year )
794     year = GetCurrentYear();
795    
796     if ( cal == Gregorian )
797     {
798     // in Gregorian calendar leap years are those divisible by 4 except
799     // those divisible by 100 unless they're also divisible by 400
800     // (in some countries, like Russia and Greece, additional corrections
801     // exist, but they won't manifest themselves until 2700)
802     return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
803     }
804     else if ( cal == Julian )
805     {
806     // in Julian calendar the rule is simpler
807     return year % 4 == 0;
808     }
809     else
810     {
811     wxFAIL_MSG(_T("unknown calendar"));
812    
813     return false;
814     }
815     }
816    
817     /* static */
818     int wxDateTime::GetCentury(int year)
819     {
820     return year > 0 ? year / 100 : year / 100 - 1;
821     }
822    
823     /* static */
824     int wxDateTime::ConvertYearToBC(int year)
825     {
826     // year 0 is BC 1
827     return year > 0 ? year : year - 1;
828     }
829    
830     /* static */
831     int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal)
832     {
833     switch ( cal )
834     {
835     case Gregorian:
836     return Now().GetYear();
837    
838     case Julian:
839     wxFAIL_MSG(_T("TODO"));
840     break;
841    
842     default:
843     wxFAIL_MSG(_T("unsupported calendar"));
844     break;
845     }
846    
847     return Inv_Year;
848     }
849    
850     /* static */
851     wxDateTime::Month wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal)
852     {
853     switch ( cal )
854     {
855     case Gregorian:
856     return Now().GetMonth();
857    
858     case Julian:
859     wxFAIL_MSG(_T("TODO"));
860     break;
861    
862     default:
863     wxFAIL_MSG(_T("unsupported calendar"));
864     break;
865     }
866    
867     return Inv_Month;
868     }
869    
870     /* static */
871     wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(int year, Calendar cal)
872     {
873     if ( year == Inv_Year )
874     {
875     // take the current year if none given
876     year = GetCurrentYear();
877     }
878    
879     switch ( cal )
880     {
881     case Gregorian:
882     case Julian:
883     return IsLeapYear(year) ? 366 : 365;
884    
885     default:
886     wxFAIL_MSG(_T("unsupported calendar"));
887     break;
888     }
889    
890     return 0;
891     }
892    
893     /* static */
894     wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(wxDateTime::Month month,
895     int year,
896     wxDateTime::Calendar cal)
897     {
898     wxCHECK_MSG( month < MONTHS_IN_YEAR, 0, _T("invalid month") );
899    
900     if ( cal == Gregorian || cal == Julian )
901     {
902     if ( year == Inv_Year )
903     {
904     // take the current year if none given
905     year = GetCurrentYear();
906     }
907    
908     return GetNumOfDaysInMonth(year, month);
909     }
910     else
911     {
912     wxFAIL_MSG(_T("unsupported calendar"));
913    
914     return 0;
915     }
916     }
917    
918     /* static */
919     wxString wxDateTime::GetMonthName(wxDateTime::Month month,
920     wxDateTime::NameFlags flags)
921     {
922     wxCHECK_MSG( month != Inv_Month, wxEmptyString, _T("invalid month") );
923     #ifdef HAVE_STRFTIME
924     // notice that we must set all the fields to avoid confusing libc (GNU one
925     // gets confused to a crash if we don't do this)
926     tm tm;
927     InitTm(tm);
928     tm.tm_mon = month;
929    
930     return CallStrftime(flags == Name_Abbr ? _T("%b") : _T("%B"), &tm);
931     #else // !HAVE_STRFTIME
932     wxString ret;
933     switch(month)
934     {
935     case Jan:
936     ret = (flags == Name_Abbr ? wxT("Jan"): wxT("January"));
937     break;
938     case Feb:
939     ret = (flags == Name_Abbr ? wxT("Feb"): wxT("Febuary"));
940     break;
941     case Mar:
942     ret = (flags == Name_Abbr ? wxT("Mar"): wxT("March"));
943     break;
944     case Apr:
945     ret = (flags == Name_Abbr ? wxT("Apr"): wxT("April"));
946     break;
947     case May:
948     ret = (flags == Name_Abbr ? wxT("May"): wxT("May"));
949     break;
950     case Jun:
951     ret = (flags == Name_Abbr ? wxT("Jun"): wxT("June"));
952     break;
953     case Jul:
954     ret = (flags == Name_Abbr ? wxT("Jul"): wxT("July"));
955     break;
956     case Aug:
957     ret = (flags == Name_Abbr ? wxT("Aug"): wxT("August"));
958     break;
959     case Sep:
960     ret = (flags == Name_Abbr ? wxT("Sep"): wxT("September"));
961     break;
962     case Oct:
963     ret = (flags == Name_Abbr ? wxT("Oct"): wxT("October"));
964     break;
965     case Nov:
966     ret = (flags == Name_Abbr ? wxT("Nov"): wxT("November"));
967     break;
968     case Dec:
969     ret = (flags == Name_Abbr ? wxT("Dec"): wxT("December"));
970     break;
971     }
972     return ret;
973     #endif // HAVE_STRFTIME/!HAVE_STRFTIME
974     }
975    
976     /* static */
977     wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday,
978     wxDateTime::NameFlags flags)
979     {
980     wxCHECK_MSG( wday != Inv_WeekDay, wxEmptyString, _T("invalid weekday") );
981     #ifdef HAVE_STRFTIME
982     // take some arbitrary Sunday (but notice that the day should be such that
983     // after adding wday to it below we still have a valid date, e.g. don't
984     // take 28 here!)
985     tm tm;
986     InitTm(tm);
987     tm.tm_mday = 21;
988     tm.tm_mon = Nov;
989     tm.tm_year = 99;
990    
991     // and offset it by the number of days needed to get the correct wday
992     tm.tm_mday += wday;
993    
994     // call mktime() to normalize it...
995     (void)mktime(&tm);
996    
997     // ... and call strftime()
998     return CallStrftime(flags == Name_Abbr ? _T("%a") : _T("%A"), &tm);
999     #else // !HAVE_STRFTIME
1000     wxString ret;
1001     switch(wday)
1002     {
1003     case Sun:
1004     ret = (flags == Name_Abbr ? wxT("Sun") : wxT("Sunday"));
1005     break;
1006     case Mon:
1007     ret = (flags == Name_Abbr ? wxT("Mon") : wxT("Monday"));
1008     break;
1009     case Tue:
1010     ret = (flags == Name_Abbr ? wxT("Tue") : wxT("Tuesday"));
1011     break;
1012     case Wed:
1013     ret = (flags == Name_Abbr ? wxT("Wed") : wxT("Wednesday"));
1014     break;
1015     case Thu:
1016     ret = (flags == Name_Abbr ? wxT("Thu") : wxT("Thursday"));
1017     break;
1018     case Fri:
1019     ret = (flags == Name_Abbr ? wxT("Fri") : wxT("Friday"));
1020     break;
1021     case Sat:
1022     ret = (flags == Name_Abbr ? wxT("Sat") : wxT("Saturday"));
1023     break;
1024     }
1025     return ret;
1026     #endif // HAVE_STRFTIME/!HAVE_STRFTIME
1027     }
1028    
1029     /* static */
1030     void wxDateTime::GetAmPmStrings(wxString *am, wxString *pm)
1031     {
1032     tm tm;
1033     InitTm(tm);
1034     wxChar buffer[64];
1035     // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code
1036     // and causes an assertion failed if the buffer is to small (which is good) - OR -
1037     // if strftime does not return anything because the format string is invalid - OR -
1038     // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good).
1039     // wxDateTime::ParseTime will try several different formats to parse the time.
1040     // As a result, GetAmPmStrings might get called, even if the current locale
1041     // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would
1042     // assert, even though it is a perfectly legal use.
1043     if ( am )
1044     {
1045     if (wxStrftime(buffer, sizeof(buffer)/sizeof(wxChar), _T("%p"), &tm) > 0)
1046     *am = wxString(buffer);
1047     else
1048     *am = wxString();
1049     }
1050     if ( pm )
1051     {
1052     tm.tm_hour = 13;
1053     if (wxStrftime(buffer, sizeof(buffer)/sizeof(wxChar), _T("%p"), &tm) > 0)
1054     *pm = wxString(buffer);
1055     else
1056     *pm = wxString();
1057     }
1058     }
1059    
1060     // ----------------------------------------------------------------------------
1061     // Country stuff: date calculations depend on the country (DST, work days,
1062     // ...), so we need to know which rules to follow.
1063     // ----------------------------------------------------------------------------
1064    
1065     /* static */
1066     wxDateTime::Country wxDateTime::GetCountry()
1067     {
1068     // TODO use LOCALE_ICOUNTRY setting under Win32
1069     #ifndef __WXWINCE__
1070     if ( ms_country == Country_Unknown )
1071     {
1072     // try to guess from the time zone name
1073     time_t t = time(NULL);
1074     struct tm tmstruct;
1075     struct tm *tm = wxLocaltime_r(&t, &tmstruct);
1076    
1077     wxString tz = CallStrftime(_T("%Z"), tm);
1078     if ( tz == _T("WET") || tz == _T("WEST") )
1079     {
1080     ms_country = UK;
1081     }
1082     else if ( tz == _T("CET") || tz == _T("CEST") )
1083     {
1084     ms_country = Country_EEC;
1085     }
1086     else if ( tz == _T("MSK") || tz == _T("MSD") )
1087     {
1088     ms_country = Russia;
1089     }
1090     else if ( tz == _T("AST") || tz == _T("ADT") ||
1091     tz == _T("EST") || tz == _T("EDT") ||
1092     tz == _T("CST") || tz == _T("CDT") ||
1093     tz == _T("MST") || tz == _T("MDT") ||
1094     tz == _T("PST") || tz == _T("PDT") )
1095     {
1096     ms_country = USA;
1097     }
1098     else
1099     {
1100     // well, choose a default one
1101     ms_country = USA;
1102     }
1103     }
1104     #else // __WXWINCE__
1105     ms_country = USA;
1106     #endif // !__WXWINCE__/__WXWINCE__
1107    
1108     return ms_country;
1109     }
1110    
1111     /* static */
1112     void wxDateTime::SetCountry(wxDateTime::Country country)
1113     {
1114     ms_country = country;
1115     }
1116    
1117     /* static */
1118     bool wxDateTime::IsWestEuropeanCountry(Country country)
1119     {
1120     if ( country == Country_Default )
1121     {
1122     country = GetCountry();
1123     }
1124    
1125     return (Country_WesternEurope_Start <= country) &&
1126     (country <= Country_WesternEurope_End);
1127     }
1128    
1129     // ----------------------------------------------------------------------------
1130     // DST calculations: we use 3 different rules for the West European countries,
1131     // USA and for the rest of the world. This is undoubtedly false for many
1132     // countries, but I lack the necessary info (and the time to gather it),
1133     // please add the other rules here!
1134     // ----------------------------------------------------------------------------
1135    
1136     /* static */
1137     bool wxDateTime::IsDSTApplicable(int year, Country country)
1138     {
1139     if ( year == Inv_Year )
1140     {
1141     // take the current year if none given
1142     year = GetCurrentYear();
1143     }
1144    
1145     if ( country == Country_Default )
1146     {
1147     country = GetCountry();
1148     }
1149    
1150     switch ( country )
1151     {
1152     case USA:
1153     case UK:
1154     // DST was first observed in the US and UK during WWI, reused
1155     // during WWII and used again since 1966
1156     return year >= 1966 ||
1157     (year >= 1942 && year <= 1945) ||
1158     (year == 1918 || year == 1919);
1159    
1160     default:
1161     // assume that it started after WWII
1162     return year > 1950;
1163     }
1164     }
1165    
1166     /* static */
1167     wxDateTime wxDateTime::GetBeginDST(int year, Country country)
1168     {
1169     if ( year == Inv_Year )
1170     {
1171     // take the current year if none given
1172     year = GetCurrentYear();
1173     }
1174    
1175     if ( country == Country_Default )
1176     {
1177     country = GetCountry();
1178     }
1179    
1180     if ( !IsDSTApplicable(year, country) )
1181     {
1182     return wxInvalidDateTime;
1183     }
1184    
1185     wxDateTime dt;
1186    
1187     if ( IsWestEuropeanCountry(country) || (country == Russia) )
1188     {
1189     // DST begins at 1 a.m. GMT on the last Sunday of March
1190     if ( !dt.SetToLastWeekDay(Sun, Mar, year) )
1191     {
1192     // weird...
1193     wxFAIL_MSG( _T("no last Sunday in March?") );
1194     }
1195    
1196     dt += wxTimeSpan::Hours(1);
1197    
1198     // disable DST tests because it could result in an infinite recursion!
1199     dt.MakeGMT(true);
1200     }
1201     else switch ( country )
1202     {
1203     case USA:
1204     switch ( year )
1205     {
1206     case 1918:
1207     case 1919:
1208     // don't know for sure - assume it was in effect all year
1209    
1210     case 1943:
1211     case 1944:
1212     case 1945:
1213     dt.Set(1, Jan, year);
1214     break;
1215    
1216     case 1942:
1217     // DST was installed Feb 2, 1942 by the Congress
1218     dt.Set(2, Feb, year);
1219     break;
1220    
1221     // Oil embargo changed the DST period in the US
1222     case 1974:
1223     dt.Set(6, Jan, 1974);
1224     break;
1225    
1226     case 1975:
1227     dt.Set(23, Feb, 1975);
1228     break;
1229    
1230     default:
1231     // before 1986, DST begun on the last Sunday of April, but
1232     // in 1986 Reagan changed it to begin at 2 a.m. of the
1233     // first Sunday in April
1234     if ( year < 1986 )
1235     {
1236     if ( !dt.SetToLastWeekDay(Sun, Apr, year) )
1237     {
1238     // weird...
1239     wxFAIL_MSG( _T("no first Sunday in April?") );
1240     }
1241     }
1242     else if ( year > 2006 )
1243     // Energy Policy Act of 2005, Pub. L. no. 109-58, 119 Stat 594 (2005).
1244     // Starting in 2007, daylight time begins in the United States on the
1245     // second Sunday in March and ends on the first Sunday in November
1246     {
1247     if ( !dt.SetToWeekDay(Sun, 2, Mar, year) )
1248     {
1249     // weird...
1250     wxFAIL_MSG( _T("no second Sunday in March?") );
1251     }
1252     }
1253     else
1254     {
1255     if ( !dt.SetToWeekDay(Sun, 1, Apr, year) )
1256     {
1257     // weird...
1258     wxFAIL_MSG( _T("no first Sunday in April?") );
1259     }
1260     }
1261    
1262     dt += wxTimeSpan::Hours(2);
1263    
1264     // TODO what about timezone??
1265     }
1266    
1267     break;
1268    
1269     default:
1270     // assume Mar 30 as the start of the DST for the rest of the world
1271     // - totally bogus, of course
1272     dt.Set(30, Mar, year);
1273     }
1274    
1275     return dt;
1276     }
1277    
1278     /* static */
1279     wxDateTime wxDateTime::GetEndDST(int year, Country country)
1280     {
1281     if ( year == Inv_Year )
1282     {
1283     // take the current year if none given
1284     year = GetCurrentYear();
1285     }
1286    
1287     if ( country == Country_Default )
1288     {
1289     country = GetCountry();
1290     }
1291    
1292     if ( !IsDSTApplicable(year, country) )
1293     {
1294     return wxInvalidDateTime;
1295     }
1296    
1297     wxDateTime dt;
1298    
1299     if ( IsWestEuropeanCountry(country) || (country == Russia) )
1300     {
1301     // DST ends at 1 a.m. GMT on the last Sunday of October
1302     if ( !dt.SetToLastWeekDay(Sun, Oct, year) )
1303     {
1304     // weirder and weirder...
1305     wxFAIL_MSG( _T("no last Sunday in October?") );
1306     }
1307    
1308     dt += wxTimeSpan::Hours(1);
1309    
1310     // disable DST tests because it could result in an infinite recursion!
1311     dt.MakeGMT(true);
1312     }
1313     else switch ( country )
1314     {
1315     case USA:
1316     switch ( year )
1317     {
1318     case 1918:
1319     case 1919:
1320     // don't know for sure - assume it was in effect all year
1321    
1322     case 1943:
1323     case 1944:
1324     dt.Set(31, Dec, year);
1325     break;
1326    
1327     case 1945:
1328     // the time was reset after the end of the WWII
1329     dt.Set(30, Sep, year);
1330     break;
1331    
1332     default: // default for switch (year)
1333     if ( year > 2006 )
1334     // Energy Policy Act of 2005, Pub. L. no. 109-58, 119 Stat 594 (2005).
1335     // Starting in 2007, daylight time begins in the United States on the
1336     // second Sunday in March and ends on the first Sunday in November
1337     {
1338     if ( !dt.SetToWeekDay(Sun, 1, Nov, year) )
1339     {
1340     // weird...
1341     wxFAIL_MSG( _T("no first Sunday in November?") );
1342     }
1343     }
1344     else
1345     // pre-2007
1346     // DST ends at 2 a.m. on the last Sunday of October
1347     {
1348     if ( !dt.SetToLastWeekDay(Sun, Oct, year) )
1349     {
1350     // weirder and weirder...
1351     wxFAIL_MSG( _T("no last Sunday in October?") );
1352     }
1353     }
1354    
1355     dt += wxTimeSpan::Hours(2);
1356    
1357     // TODO: what about timezone??
1358     }
1359     break;
1360    
1361     default: // default for switch (country)
1362     // assume October 26th as the end of the DST - totally bogus too
1363     dt.Set(26, Oct, year);
1364     }
1365    
1366     return dt;
1367     }
1368    
1369     // ----------------------------------------------------------------------------
1370     // constructors and assignment operators
1371     // ----------------------------------------------------------------------------
1372    
1373     // return the current time with ms precision
1374     /* static */ wxDateTime wxDateTime::UNow()
1375     {
1376     return wxDateTime(wxGetLocalTimeMillis());
1377     }
1378    
1379     // the values in the tm structure contain the local time
1380     wxDateTime& wxDateTime::Set(const struct tm& tm)
1381     {
1382     struct tm tm2(tm);
1383     time_t timet = mktime(&tm2);
1384    
1385     if ( timet == (time_t)-1 )
1386     {
1387     // mktime() rather unintuitively fails for Jan 1, 1970 if the hour is
1388     // less than timezone - try to make it work for this case
1389     if ( tm2.tm_year == 70 && tm2.tm_mon == 0 && tm2.tm_mday == 1 )
1390     {
1391     return Set((time_t)(
1392     GetTimeZone() +
1393     tm2.tm_hour * MIN_PER_HOUR * SEC_PER_MIN +
1394     tm2.tm_min * SEC_PER_MIN +
1395     tm2.tm_sec));
1396     }
1397    
1398     wxFAIL_MSG( _T("mktime() failed") );
1399    
1400     *this = wxInvalidDateTime;
1401    
1402     return *this;
1403     }
1404     else
1405     {
1406     return Set(timet);
1407     }
1408     }
1409    
1410     wxDateTime& wxDateTime::Set(wxDateTime_t hour,
1411     wxDateTime_t minute,
1412     wxDateTime_t second,
1413     wxDateTime_t millisec)
1414     {
1415     // we allow seconds to be 61 to account for the leap seconds, even if we
1416     // don't use them really
1417     wxDATETIME_CHECK( hour < 24 &&
1418     second < 62 &&
1419     minute < 60 &&
1420     millisec < 1000,
1421     _T("Invalid time in wxDateTime::Set()") );
1422    
1423     // get the current date from system
1424     struct tm tmstruct;
1425     struct tm *tm = GetTmNow(&tmstruct);
1426    
1427     wxDATETIME_CHECK( tm, _T("wxLocaltime_r() failed") );
1428    
1429     // make a copy so it isn't clobbered by the call to mktime() below
1430     struct tm tm1(*tm);
1431    
1432     // adjust the time
1433     tm1.tm_hour = hour;
1434     tm1.tm_min = minute;
1435     tm1.tm_sec = second;
1436    
1437     // and the DST in case it changes on this date
1438     struct tm tm2(tm1);
1439     mktime(&tm2);
1440     if ( tm2.tm_isdst != tm1.tm_isdst )
1441     tm1.tm_isdst = tm2.tm_isdst;
1442    
1443     (void)Set(tm1);
1444    
1445     // and finally adjust milliseconds
1446     return SetMillisecond(millisec);
1447     }
1448    
1449     wxDateTime& wxDateTime::Set(wxDateTime_t day,
1450     Month month,
1451     int year,
1452     wxDateTime_t hour,
1453     wxDateTime_t minute,
1454     wxDateTime_t second,
1455     wxDateTime_t millisec)
1456     {
1457     wxDATETIME_CHECK( hour < 24 &&
1458     second < 62 &&
1459     minute < 60 &&
1460     millisec < 1000,
1461     _T("Invalid time in wxDateTime::Set()") );
1462    
1463     ReplaceDefaultYearMonthWithCurrent(&year, &month);
1464    
1465     wxDATETIME_CHECK( (0 < day) && (day <= GetNumberOfDays(month, year)),
1466     _T("Invalid date in wxDateTime::Set()") );
1467    
1468     // the range of time_t type (inclusive)
1469     static const int yearMinInRange = 1970;
1470     static const int yearMaxInRange = 2037;
1471    
1472     // test only the year instead of testing for the exact end of the Unix
1473     // time_t range - it doesn't bring anything to do more precise checks
1474     if ( year >= yearMinInRange && year <= yearMaxInRange )
1475     {
1476     // use the standard library version if the date is in range - this is
1477     // probably more efficient than our code
1478     struct tm tm;
1479     tm.tm_year = year - 1900;
1480     tm.tm_mon = month;
1481     tm.tm_mday = day;
1482     tm.tm_hour = hour;
1483     tm.tm_min = minute;
1484     tm.tm_sec = second;
1485     tm.tm_isdst = -1; // mktime() will guess it
1486    
1487     (void)Set(tm);
1488    
1489     // and finally adjust milliseconds
1490     if (IsValid())
1491     SetMillisecond(millisec);
1492    
1493     return *this;
1494     }
1495     else
1496     {
1497     // do time calculations ourselves: we want to calculate the number of
1498     // milliseconds between the given date and the epoch
1499    
1500     // get the JDN for the midnight of this day
1501     m_time = GetTruncatedJDN(day, month, year);
1502     m_time -= EPOCH_JDN;
1503     m_time *= SECONDS_PER_DAY * TIME_T_FACTOR;
1504    
1505     // JDN corresponds to GMT, we take localtime
1506     Add(wxTimeSpan(hour, minute, second + GetTimeZone(), millisec));
1507     }
1508    
1509     return *this;
1510     }
1511    
1512     wxDateTime& wxDateTime::Set(double jdn)
1513     {
1514     // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
1515     // EPOCH_JDN + 0.5
1516     jdn -= EPOCH_JDN + 0.5;
1517    
1518     m_time.Assign(jdn*MILLISECONDS_PER_DAY);
1519    
1520     // JDNs always are in UTC, so we don't need any adjustments for time zone
1521    
1522     return *this;
1523     }
1524    
1525     wxDateTime& wxDateTime::ResetTime()
1526     {
1527     Tm tm = GetTm();
1528    
1529     if ( tm.hour || tm.min || tm.sec || tm.msec )
1530     {
1531     tm.msec =
1532     tm.sec =
1533     tm.min =
1534     tm.hour = 0;
1535    
1536     Set(tm);
1537     }
1538    
1539     return *this;
1540     }
1541    
1542     wxDateTime wxDateTime::GetDateOnly() const
1543     {
1544     Tm tm = GetTm();
1545     tm.msec =
1546     tm.sec =
1547     tm.min =
1548     tm.hour = 0;
1549     return wxDateTime(tm);
1550     }
1551    
1552     // ----------------------------------------------------------------------------
1553     // DOS Date and Time Format functions
1554     // ----------------------------------------------------------------------------
1555     // the dos date and time value is an unsigned 32 bit value in the format:
1556     // YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
1557     //
1558     // Y = year offset from 1980 (0-127)
1559     // M = month (1-12)
1560     // D = day of month (1-31)
1561     // h = hour (0-23)
1562     // m = minute (0-59)
1563     // s = bisecond (0-29) each bisecond indicates two seconds
1564     // ----------------------------------------------------------------------------
1565    
1566     wxDateTime& wxDateTime::SetFromDOS(unsigned long ddt)
1567     {
1568     struct tm tm;
1569     InitTm(tm);
1570    
1571     long year = ddt & 0xFE000000;
1572     year >>= 25;
1573     year += 80;
1574     tm.tm_year = year;
1575    
1576     long month = ddt & 0x1E00000;
1577     month >>= 21;
1578     month -= 1;
1579     tm.tm_mon = month;
1580    
1581     long day = ddt & 0x1F0000;
1582     day >>= 16;
1583     tm.tm_mday = day;
1584    
1585     long hour = ddt & 0xF800;
1586     hour >>= 11;
1587     tm.tm_hour = hour;
1588    
1589     long minute = ddt & 0x7E0;
1590     minute >>= 5;
1591     tm.tm_min = minute;
1592    
1593     long second = ddt & 0x1F;
1594     tm.tm_sec = second * 2;
1595    
1596     return Set(mktime(&tm));
1597     }
1598    
1599     unsigned long wxDateTime::GetAsDOS() const
1600     {
1601     unsigned long ddt;
1602     time_t ticks = GetTicks();
1603     struct tm tmstruct;
1604     struct tm *tm = wxLocaltime_r(&ticks, &tmstruct);
1605     wxCHECK_MSG( tm, ULONG_MAX, _T("time can't be represented in DOS format") );
1606    
1607     long year = tm->tm_year;
1608     year -= 80;
1609     year <<= 25;
1610    
1611     long month = tm->tm_mon;
1612     month += 1;
1613     month <<= 21;
1614    
1615     long day = tm->tm_mday;
1616     day <<= 16;
1617    
1618     long hour = tm->tm_hour;
1619     hour <<= 11;
1620    
1621     long minute = tm->tm_min;
1622     minute <<= 5;
1623    
1624     long second = tm->tm_sec;
1625     second /= 2;
1626    
1627     ddt = year | month | day | hour | minute | second;
1628     return ddt;
1629     }
1630    
1631     // ----------------------------------------------------------------------------
1632     // time_t <-> broken down time conversions
1633     // ----------------------------------------------------------------------------
1634    
1635     wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
1636     {
1637     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1638    
1639     time_t time = GetTicks();
1640     if ( time != (time_t)-1 )
1641     {
1642     // use C RTL functions
1643     struct tm tmstruct;
1644     tm *tm;
1645     if ( tz.GetOffset() == -GetTimeZone() )
1646     {
1647     // we are working with local time
1648     tm = wxLocaltime_r(&time, &tmstruct);
1649    
1650     // should never happen
1651     wxCHECK_MSG( tm, Tm(), _T("wxLocaltime_r() failed") );
1652     }
1653     else
1654     {
1655     time += (time_t)tz.GetOffset();
1656     #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
1657     int time2 = (int) time;
1658     if ( time2 >= 0 )
1659     #else
1660     if ( time >= 0 )
1661     #endif
1662     {
1663     tm = wxGmtime_r(&time, &tmstruct);
1664    
1665     // should never happen
1666     wxCHECK_MSG( tm, Tm(), _T("wxGmtime_r() failed") );
1667     }
1668     else
1669     {
1670     tm = (struct tm *)NULL;
1671     }
1672     }
1673    
1674     if ( tm )
1675     {
1676     // adjust the milliseconds
1677     Tm tm2(*tm, tz);
1678     long timeOnly = (m_time % MILLISECONDS_PER_DAY).ToLong();
1679     tm2.msec = (wxDateTime_t)(timeOnly % 1000);
1680     return tm2;
1681     }
1682     //else: use generic code below
1683     }
1684    
1685     // remember the time and do the calculations with the date only - this
1686     // eliminates rounding errors of the floating point arithmetics
1687    
1688     wxLongLong timeMidnight = m_time + tz.GetOffset() * 1000;
1689    
1690     long timeOnly = (timeMidnight % MILLISECONDS_PER_DAY).ToLong();
1691    
1692     // we want to always have positive time and timeMidnight to be really
1693     // the midnight before it
1694     if ( timeOnly < 0 )
1695     {
1696     timeOnly = MILLISECONDS_PER_DAY + timeOnly;
1697     }
1698    
1699     timeMidnight -= timeOnly;
1700    
1701     // calculate the Gregorian date from JDN for the midnight of our date:
1702     // this will yield day, month (in 1..12 range) and year
1703    
1704     // actually, this is the JDN for the noon of the previous day
1705     long jdn = (timeMidnight / MILLISECONDS_PER_DAY).ToLong() + EPOCH_JDN;
1706    
1707     // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1708    
1709     wxASSERT_MSG( jdn > -2, _T("JDN out of range") );
1710    
1711     // calculate the century
1712     long temp = (jdn + JDN_OFFSET) * 4 - 1;
1713     long century = temp / DAYS_PER_400_YEARS;
1714    
1715     // then the year and day of year (1 <= dayOfYear <= 366)
1716     temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
1717     long year = (century * 100) + (temp / DAYS_PER_4_YEARS);
1718     long dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
1719    
1720     // and finally the month and day of the month
1721     temp = dayOfYear * 5 - 3;
1722     long month = temp / DAYS_PER_5_MONTHS;
1723     long day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
1724    
1725     // month is counted from March - convert to normal
1726     if ( month < 10 )
1727     {
1728     month += 3;
1729     }
1730     else
1731     {
1732     year += 1;
1733     month -= 9;
1734     }
1735    
1736     // year is offset by 4800
1737     year -= 4800;
1738    
1739     // check that the algorithm gave us something reasonable
1740     wxASSERT_MSG( (0 < month) && (month <= 12), _T("invalid month") );
1741     wxASSERT_MSG( (1 <= day) && (day < 32), _T("invalid day") );
1742    
1743     // construct Tm from these values
1744     Tm tm;
1745     tm.year = (int)year;
1746     tm.mon = (Month)(month - 1); // algorithm yields 1 for January, not 0
1747     tm.mday = (wxDateTime_t)day;
1748     tm.msec = (wxDateTime_t)(timeOnly % 1000);
1749     timeOnly -= tm.msec;
1750     timeOnly /= 1000; // now we have time in seconds
1751    
1752     tm.sec = (wxDateTime_t)(timeOnly % SEC_PER_MIN);
1753     timeOnly -= tm.sec;
1754     timeOnly /= SEC_PER_MIN; // now we have time in minutes
1755    
1756     tm.min = (wxDateTime_t)(timeOnly % MIN_PER_HOUR);
1757     timeOnly -= tm.min;
1758    
1759     tm.hour = (wxDateTime_t)(timeOnly / MIN_PER_HOUR);
1760    
1761     return tm;
1762     }
1763    
1764     wxDateTime& wxDateTime::SetYear(int year)
1765     {
1766     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1767    
1768     Tm tm(GetTm());
1769     tm.year = year;
1770     Set(tm);
1771    
1772     return *this;
1773     }
1774    
1775     wxDateTime& wxDateTime::SetMonth(Month month)
1776     {
1777     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1778    
1779     Tm tm(GetTm());
1780     tm.mon = month;
1781     Set(tm);
1782    
1783     return *this;
1784     }
1785    
1786     wxDateTime& wxDateTime::SetDay(wxDateTime_t mday)
1787     {
1788     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1789    
1790     Tm tm(GetTm());
1791     tm.mday = mday;
1792     Set(tm);
1793    
1794     return *this;
1795     }
1796    
1797     wxDateTime& wxDateTime::SetHour(wxDateTime_t hour)
1798     {
1799     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1800    
1801     Tm tm(GetTm());
1802     tm.hour = hour;
1803     Set(tm);
1804    
1805     return *this;
1806     }
1807    
1808     wxDateTime& wxDateTime::SetMinute(wxDateTime_t min)
1809     {
1810     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1811    
1812     Tm tm(GetTm());
1813     tm.min = min;
1814     Set(tm);
1815    
1816     return *this;
1817     }
1818    
1819     wxDateTime& wxDateTime::SetSecond(wxDateTime_t sec)
1820     {
1821     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1822    
1823     Tm tm(GetTm());
1824     tm.sec = sec;
1825     Set(tm);
1826    
1827     return *this;
1828     }
1829    
1830     wxDateTime& wxDateTime::SetMillisecond(wxDateTime_t millisecond)
1831     {
1832     wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
1833    
1834     // we don't need to use GetTm() for this one
1835     m_time -= m_time % 1000l;
1836     m_time += millisecond;
1837    
1838     return *this;
1839     }
1840    
1841     // ----------------------------------------------------------------------------
1842     // wxDateTime arithmetics
1843     // ----------------------------------------------------------------------------
1844    
1845     wxDateTime& wxDateTime::Add(const wxDateSpan& diff)
1846     {
1847     Tm tm(GetTm());
1848    
1849     tm.year += diff.GetYears();
1850     tm.AddMonths(diff.GetMonths());
1851    
1852     // check that the resulting date is valid
1853     if ( tm.mday > GetNumOfDaysInMonth(tm.year, tm.mon) )
1854     {
1855     // We suppose that when adding one month to Jan 31 we want to get Feb
1856     // 28 (or 29), i.e. adding a month to the last day of the month should
1857     // give the last day of the next month which is quite logical.
1858     //
1859     // Unfortunately, there is no logic way to understand what should
1860     // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
1861     // We make it Feb 28 (last day too), but it is highly questionable.
1862     tm.mday = GetNumOfDaysInMonth(tm.year, tm.mon);
1863     }
1864    
1865     tm.AddDays(diff.GetTotalDays());
1866    
1867     Set(tm);
1868    
1869     wxASSERT_MSG( IsSameTime(tm),
1870     _T("Add(wxDateSpan) shouldn't modify time") );
1871    
1872     return *this;
1873     }
1874    
1875     // ----------------------------------------------------------------------------
1876     // Weekday and monthday stuff
1877     // ----------------------------------------------------------------------------
1878    
1879     // convert Sun, Mon, ..., Sat into 6, 0, ..., 5
1880     static inline int ConvertWeekDayToMondayBase(int wd)
1881     {
1882     return wd == wxDateTime::Sun ? 6 : wd - 1;
1883     }
1884    
1885     /* static */
1886     wxDateTime
1887     wxDateTime::SetToWeekOfYear(int year, wxDateTime_t numWeek, WeekDay wd)
1888     {
1889     wxASSERT_MSG( numWeek > 0,
1890     _T("invalid week number: weeks are counted from 1") );
1891    
1892     // Jan 4 always lies in the 1st week of the year
1893     wxDateTime dt(4, Jan, year);
1894     dt.SetToWeekDayInSameWeek(wd);
1895     dt += wxDateSpan::Weeks(numWeek - 1);
1896    
1897     return dt;
1898     }
1899    
1900     #if WXWIN_COMPATIBILITY_2_6
1901     // use a separate function to avoid warnings about using deprecated
1902     // SetToTheWeek in GetWeek below
1903     static wxDateTime
1904     SetToTheWeek(int year,
1905     wxDateTime::wxDateTime_t numWeek,
1906     wxDateTime::WeekDay weekday,
1907     wxDateTime::WeekFlags flags)
1908     {
1909     // Jan 4 always lies in the 1st week of the year
1910     wxDateTime dt(4, wxDateTime::Jan, year);
1911     dt.SetToWeekDayInSameWeek(weekday, flags);
1912     dt += wxDateSpan::Weeks(numWeek - 1);
1913    
1914     return dt;
1915     }
1916    
1917     bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek,
1918     WeekDay weekday,
1919     WeekFlags flags)
1920     {
1921     int year = GetYear();
1922     *this = ::SetToTheWeek(year, numWeek, weekday, flags);
1923     if ( GetYear() != year )
1924     {
1925     // oops... numWeek was too big
1926     return false;
1927     }
1928    
1929     return true;
1930     }
1931    
1932     wxDateTime wxDateTime::GetWeek(wxDateTime_t numWeek,
1933     WeekDay weekday,
1934     WeekFlags flags) const
1935     {
1936     return ::SetToTheWeek(GetYear(), numWeek, weekday, flags);
1937     }
1938     #endif // WXWIN_COMPATIBILITY_2_6
1939    
1940     wxDateTime& wxDateTime::SetToLastMonthDay(Month month,
1941     int year)
1942     {
1943     // take the current month/year if none specified
1944     if ( year == Inv_Year )
1945     year = GetYear();
1946     if ( month == Inv_Month )
1947     month = GetMonth();
1948    
1949     return Set(GetNumOfDaysInMonth(year, month), month, year);
1950     }
1951    
1952     wxDateTime& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday, WeekFlags flags)
1953     {
1954     wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
1955    
1956     int wdayDst = weekday,
1957     wdayThis = GetWeekDay();
1958     if ( wdayDst == wdayThis )
1959     {
1960     // nothing to do
1961     return *this;
1962     }
1963    
1964     if ( flags == Default_First )
1965     {
1966     flags = GetCountry() == USA ? Sunday_First : Monday_First;
1967     }
1968    
1969     // the logic below based on comparing weekday and wdayThis works if Sun (0)
1970     // is the first day in the week, but breaks down for Monday_First case so
1971     // we adjust the week days in this case
1972     if ( flags == Monday_First )
1973     {
1974     if ( wdayThis == Sun )
1975     wdayThis += 7;
1976     if ( wdayDst == Sun )
1977     wdayDst += 7;
1978     }
1979     //else: Sunday_First, nothing to do
1980    
1981     // go forward or back in time to the day we want
1982     if ( wdayDst < wdayThis )
1983     {
1984     return Subtract(wxDateSpan::Days(wdayThis - wdayDst));
1985     }
1986     else // weekday > wdayThis
1987     {
1988     return Add(wxDateSpan::Days(wdayDst - wdayThis));
1989     }
1990     }
1991    
1992     wxDateTime& wxDateTime::SetToNextWeekDay(WeekDay weekday)
1993     {
1994     wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
1995    
1996     int diff;
1997     WeekDay wdayThis = GetWeekDay();
1998     if ( weekday == wdayThis )
1999     {
2000     // nothing to do
2001     return *this;
2002     }
2003     else if ( weekday < wdayThis )
2004     {
2005     // need to advance a week
2006     diff = 7 - (wdayThis - weekday);
2007     }
2008     else // weekday > wdayThis
2009     {
2010     diff = weekday - wdayThis;
2011     }
2012    
2013     return Add(wxDateSpan::Days(diff));
2014     }
2015    
2016     wxDateTime& wxDateTime::SetToPrevWeekDay(WeekDay weekday)
2017     {
2018     wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
2019    
2020     int diff;
2021     WeekDay wdayThis = GetWeekDay();
2022     if ( weekday == wdayThis )
2023     {
2024     // nothing to do
2025     return *this;
2026     }
2027     else if ( weekday > wdayThis )
2028     {
2029     // need to go to previous week
2030     diff = 7 - (weekday - wdayThis);
2031     }
2032     else // weekday < wdayThis
2033     {
2034     diff = wdayThis - weekday;
2035     }
2036    
2037     return Subtract(wxDateSpan::Days(diff));
2038     }
2039    
2040     bool wxDateTime::SetToWeekDay(WeekDay weekday,
2041     int n,
2042     Month month,
2043     int year)
2044     {
2045     wxCHECK_MSG( weekday != Inv_WeekDay, false, _T("invalid weekday") );
2046    
2047     // we don't check explicitly that -5 <= n <= 5 because we will return false
2048     // anyhow in such case - but may be should still give an assert for it?
2049    
2050     // take the current month/year if none specified
2051     ReplaceDefaultYearMonthWithCurrent(&year, &month);
2052    
2053     wxDateTime dt;
2054    
2055     // TODO this probably could be optimised somehow...
2056    
2057     if ( n > 0 )
2058     {
2059     // get the first day of the month
2060     dt.Set(1, month, year);
2061    
2062     // get its wday
2063     WeekDay wdayFirst = dt.GetWeekDay();
2064    
2065     // go to the first weekday of the month
2066     int diff = weekday - wdayFirst;
2067     if ( diff < 0 )
2068     diff += 7;
2069    
2070     // add advance n-1 weeks more
2071     diff += 7*(n - 1);
2072    
2073     dt += wxDateSpan::Days(diff);
2074     }
2075     else // count from the end of the month
2076     {
2077     // get the last day of the month
2078     dt.SetToLastMonthDay(month, year);
2079    
2080     // get its wday
2081     WeekDay wdayLast = dt.GetWeekDay();
2082    
2083     // go to the last weekday of the month
2084     int diff = wdayLast - weekday;
2085     if ( diff < 0 )
2086     diff += 7;
2087    
2088     // and rewind n-1 weeks from there
2089     diff += 7*(-n - 1);
2090    
2091     dt -= wxDateSpan::Days(diff);
2092     }
2093    
2094     // check that it is still in the same month
2095     if ( dt.GetMonth() == month )
2096     {
2097     *this = dt;
2098    
2099     return true;
2100     }
2101     else
2102     {
2103     // no such day in this month
2104     return false;
2105     }
2106     }
2107    
2108     static inline
2109     wxDateTime::wxDateTime_t GetDayOfYearFromTm(const wxDateTime::Tm& tm)
2110     {
2111     return (wxDateTime::wxDateTime_t)(gs_cumulatedDays[wxDateTime::IsLeapYear(tm.year)][tm.mon] + tm.mday);
2112     }
2113    
2114     wxDateTime::wxDateTime_t wxDateTime::GetDayOfYear(const TimeZone& tz) const
2115     {
2116     return GetDayOfYearFromTm(GetTm(tz));
2117     }
2118    
2119     wxDateTime::wxDateTime_t
2120     wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags, const TimeZone& tz) const
2121     {
2122     if ( flags == Default_First )
2123     {
2124     flags = GetCountry() == USA ? Sunday_First : Monday_First;
2125     }
2126    
2127     Tm tm(GetTm(tz));
2128     wxDateTime_t nDayInYear = GetDayOfYearFromTm(tm);
2129    
2130     int wdTarget = GetWeekDay(tz);
2131     int wdYearStart = wxDateTime(1, Jan, GetYear()).GetWeekDay();
2132     int week;
2133     if ( flags == Sunday_First )
2134     {
2135     // FIXME: First week is not calculated correctly.
2136     week = (nDayInYear - wdTarget + 7) / 7;
2137     if ( wdYearStart == Wed || wdYearStart == Thu )
2138     week++;
2139     }
2140     else // week starts with monday
2141     {
2142     // adjust the weekdays to non-US style.
2143     wdYearStart = ConvertWeekDayToMondayBase(wdYearStart);
2144     wdTarget = ConvertWeekDayToMondayBase(wdTarget);
2145    
2146     // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
2147     //
2148     // Week 01 of a year is per definition the first week that has the
2149     // Thursday in this year, which is equivalent to the week that
2150     // contains the fourth day of January. In other words, the first
2151     // week of a new year is the week that has the majority of its
2152     // days in the new year. Week 01 might also contain days from the
2153     // previous year and the week before week 01 of a year is the last
2154     // week (52 or 53) of the previous year even if it contains days
2155     // from the new year. A week starts with Monday (day 1) and ends
2156     // with Sunday (day 7).
2157     //
2158    
2159     // if Jan 1 is Thursday or less, it is in the first week of this year
2160     if ( wdYearStart < 4 )
2161     {
2162     // count the number of entire weeks between Jan 1 and this date
2163     week = (nDayInYear + wdYearStart + 6 - wdTarget)/7;
2164    
2165     // be careful to check for overflow in the next year
2166     if ( week == 53 && tm.mday - wdTarget > 28 )
2167     week = 1;
2168     }
2169     else // Jan 1 is in the last week of the previous year
2170     {
2171     // check if we happen to be at the last week of previous year:
2172     if ( tm.mon == Jan && tm.mday < 8 - wdYearStart )
2173     week = wxDateTime(31, Dec, GetYear()-1).GetWeekOfYear();
2174     else
2175     week = (nDayInYear + wdYearStart - 1 - wdTarget)/7;
2176     }
2177     }
2178    
2179     return (wxDateTime::wxDateTime_t)week;
2180     }
2181    
2182     wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags,
2183     const TimeZone& tz) const
2184     {
2185     Tm tm = GetTm(tz);
2186     wxDateTime dtMonthStart = wxDateTime(1, tm.mon, tm.year);
2187     int nWeek = GetWeekOfYear(flags) - dtMonthStart.GetWeekOfYear(flags) + 1;
2188     if ( nWeek < 0 )
2189     {
2190     // this may happen for January when Jan, 1 is the last week of the
2191     // previous year
2192     nWeek += IsLeapYear(tm.year - 1) ? 53 : 52;
2193     }
2194    
2195     return (wxDateTime::wxDateTime_t)nWeek;
2196     }
2197    
2198     wxDateTime& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday)
2199     {
2200     int year = GetYear();
2201     wxDATETIME_CHECK( (0 < yday) && (yday <= GetNumberOfDays(year)),
2202     _T("invalid year day") );
2203    
2204     bool isLeap = IsLeapYear(year);
2205     for ( Month mon = Jan; mon < Inv_Month; wxNextMonth(mon) )
2206     {
2207     // for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
2208     // don't need it neither - because of the CHECK above we know that
2209     // yday lies in December then
2210     if ( (mon == Dec) || (yday <= gs_cumulatedDays[isLeap][mon + 1]) )
2211     {
2212     Set((wxDateTime::wxDateTime_t)(yday - gs_cumulatedDays[isLeap][mon]), mon, year);
2213    
2214     break;
2215     }
2216     }
2217    
2218     return *this;
2219     }
2220    
2221     // ----------------------------------------------------------------------------
2222     // Julian day number conversion and related stuff
2223     // ----------------------------------------------------------------------------
2224    
2225     double wxDateTime::GetJulianDayNumber() const
2226     {
2227     return m_time.ToDouble() / MILLISECONDS_PER_DAY + EPOCH_JDN + 0.5;
2228     }
2229    
2230     double wxDateTime::GetRataDie() const
2231     {
2232     // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
2233     return GetJulianDayNumber() - 1721119.5 - 306;
2234     }
2235    
2236     // ----------------------------------------------------------------------------
2237     // timezone and DST stuff
2238     // ----------------------------------------------------------------------------
2239    
2240     int wxDateTime::IsDST(wxDateTime::Country country) const
2241     {
2242     wxCHECK_MSG( country == Country_Default, -1,
2243     _T("country support not implemented") );
2244    
2245     // use the C RTL for the dates in the standard range
2246     time_t timet = GetTicks();
2247     if ( timet != (time_t)-1 )
2248     {
2249     struct tm tmstruct;
2250     tm *tm = wxLocaltime_r(&timet, &tmstruct);
2251    
2252     wxCHECK_MSG( tm, -1, _T("wxLocaltime_r() failed") );
2253    
2254     return tm->tm_isdst;
2255     }
2256     else
2257     {
2258     int year = GetYear();
2259    
2260     if ( !IsDSTApplicable(year, country) )
2261     {
2262     // no DST time in this year in this country
2263     return -1;
2264     }
2265    
2266     return IsBetween(GetBeginDST(year, country), GetEndDST(year, country));
2267     }
2268     }
2269    
2270     wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST)
2271     {
2272     long secDiff = GetTimeZone() + tz.GetOffset();
2273    
2274     // we need to know whether DST is or not in effect for this date unless
2275     // the test disabled by the caller
2276     if ( !noDST && (IsDST() == 1) )
2277     {
2278     // FIXME we assume that the DST is always shifted by 1 hour
2279     secDiff -= 3600;
2280     }
2281    
2282     return Add(wxTimeSpan::Seconds(secDiff));
2283     }
2284    
2285     wxDateTime& wxDateTime::MakeFromTimezone(const TimeZone& tz, bool noDST)
2286     {
2287     long secDiff = GetTimeZone() + tz.GetOffset();
2288    
2289     // we need to know whether DST is or not in effect for this date unless
2290     // the test disabled by the caller
2291     if ( !noDST && (IsDST() == 1) )
2292     {
2293     // FIXME we assume that the DST is always shifted by 1 hour
2294     secDiff -= 3600;
2295     }
2296    
2297     return Subtract(wxTimeSpan::Seconds(secDiff));
2298     }
2299    
2300     // ----------------------------------------------------------------------------
2301     // wxDateTime to/from text representations
2302     // ----------------------------------------------------------------------------
2303    
2304     wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
2305     {
2306     wxCHECK_MSG( format, wxEmptyString, _T("NULL format in wxDateTime::Format") );
2307    
2308     time_t time = GetTicks();
2309    
2310     // we have to use our own implementation if the date is out of range of
2311     // strftime() or if we use non standard specificators
2312     #ifdef HAVE_STRFTIME
2313     if ( (time != (time_t)-1) && !wxStrstr(format, _T("%l")) )
2314     {
2315     // use strftime()
2316     struct tm tmstruct;
2317     struct tm *tm;
2318     if ( tz.GetOffset() == -GetTimeZone() )
2319     {
2320     // we are working with local time
2321     tm = wxLocaltime_r(&time, &tmstruct);
2322    
2323     // should never happen
2324     wxCHECK_MSG( tm, wxEmptyString, _T("wxLocaltime_r() failed") );
2325     }
2326     else
2327     {
2328     time += (int)tz.GetOffset();
2329    
2330     #if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
2331     int time2 = (int) time;
2332     if ( time2 >= 0 )
2333     #else
2334     if ( time >= 0 )
2335     #endif
2336     {
2337     tm = wxGmtime_r(&time, &tmstruct);
2338    
2339     // should never happen
2340     wxCHECK_MSG( tm, wxEmptyString, _T("wxGmtime_r() failed") );
2341     }
2342     else
2343     {
2344     tm = (struct tm *)NULL;
2345     }
2346     }
2347    
2348     if ( tm )
2349     {
2350     return CallStrftime(format, tm);
2351     }
2352     }
2353     //else: use generic code below
2354     #endif // HAVE_STRFTIME
2355    
2356     // we only parse ANSI C format specifications here, no POSIX 2
2357     // complications, no GNU extensions but we do add support for a "%l" format
2358     // specifier allowing to get the number of milliseconds
2359     Tm tm = GetTm(tz);
2360    
2361     // used for calls to strftime() when we only deal with time
2362     struct tm tmTimeOnly;
2363     tmTimeOnly.tm_hour = tm.hour;
2364     tmTimeOnly.tm_min = tm.min;
2365     tmTimeOnly.tm_sec = tm.sec;
2366     tmTimeOnly.tm_wday = 0;
2367     tmTimeOnly.tm_yday = 0;
2368     tmTimeOnly.tm_mday = 1; // any date will do
2369     tmTimeOnly.tm_mon = 0;
2370     tmTimeOnly.tm_year = 76;
2371     tmTimeOnly.tm_isdst = 0; // no DST, we adjust for tz ourselves
2372    
2373     wxString tmp, res, fmt;
2374     for ( const wxChar *p = format; *p; p++ )
2375     {
2376     if ( *p != _T('%') )
2377     {
2378     // copy as is
2379     res += *p;
2380    
2381     continue;
2382     }
2383    
2384     // set the default format
2385     switch ( *++p )
2386     {
2387     case _T('Y'): // year has 4 digits
2388     fmt = _T("%04d");
2389     break;
2390    
2391     case _T('j'): // day of year has 3 digits
2392     case _T('l'): // milliseconds have 3 digits
2393     fmt = _T("%03d");
2394     break;
2395    
2396     case _T('w'): // week day as number has only one
2397     fmt = _T("%d");
2398     break;
2399    
2400     default:
2401     // it's either another valid format specifier in which case
2402     // the format is "%02d" (for all the rest) or we have the
2403     // field width preceding the format in which case it will
2404     // override the default format anyhow
2405     fmt = _T("%02d");
2406     }
2407    
2408     bool restart = true;
2409     while ( restart )
2410     {
2411     restart = false;
2412    
2413     // start of the format specification
2414     switch ( *p )
2415     {
2416     case _T('a'): // a weekday name
2417     case _T('A'):
2418     // second parameter should be true for abbreviated names
2419     res += GetWeekDayName(tm.GetWeekDay(),
2420     *p == _T('a') ? Name_Abbr : Name_Full);
2421     break;
2422    
2423     case _T('b'): // a month name
2424     case _T('B'):
2425     res += GetMonthName(tm.mon,
2426     *p == _T('b') ? Name_Abbr : Name_Full);
2427     break;
2428    
2429     case _T('c'): // locale default date and time representation
2430     case _T('x'): // locale default date representation
2431     #ifdef HAVE_STRFTIME
2432     //
2433     // the problem: there is no way to know what do these format
2434     // specifications correspond to for the current locale.
2435     //
2436     // the solution: use a hack and still use strftime(): first
2437     // find the YEAR which is a year in the strftime() range (1970
2438     // - 2038) whose Jan 1 falls on the same week day as the Jan 1
2439     // of the real year. Then make a copy of the format and
2440     // replace all occurrences of YEAR in it with some unique
2441     // string not appearing anywhere else in it, then use
2442     // strftime() to format the date in year YEAR and then replace
2443     // YEAR back by the real year and the unique replacement
2444     // string back with YEAR. Notice that "all occurrences of YEAR"
2445     // means all occurrences of 4 digit as well as 2 digit form!
2446     //
2447     // the bugs: we assume that neither of %c nor %x contains any
2448     // fields which may change between the YEAR and real year. For
2449     // example, the week number (%U, %W) and the day number (%j)
2450     // will change if one of these years is leap and the other one
2451     // is not!
2452     {
2453     // find the YEAR: normally, for any year X, Jan 1 or the
2454     // year X + 28 is the same weekday as Jan 1 of X (because
2455     // the weekday advances by 1 for each normal X and by 2
2456     // for each leap X, hence by 5 every 4 years or by 35
2457     // which is 0 mod 7 every 28 years) but this rule breaks
2458     // down if there are years between X and Y which are
2459     // divisible by 4 but not leap (i.e. divisible by 100 but
2460     // not 400), hence the correction.
2461    
2462     int yearReal = GetYear(tz);
2463     int mod28 = yearReal % 28;
2464    
2465     // be careful to not go too far - we risk to leave the
2466     // supported range
2467     int year;
2468     if ( mod28 < 10 )
2469     {
2470     year = 1988 + mod28; // 1988 == 0 (mod 28)
2471     }
2472     else
2473     {
2474     year = 1970 + mod28 - 10; // 1970 == 10 (mod 28)
2475     }
2476    
2477     int nCentury = year / 100,
2478     nCenturyReal = yearReal / 100;
2479    
2480     // need to adjust for the years divisble by 400 which are
2481     // not leap but are counted like leap ones if we just take
2482     // the number of centuries in between for nLostWeekDays
2483     int nLostWeekDays = (nCentury - nCenturyReal) -
2484     (nCentury / 4 - nCenturyReal / 4);
2485    
2486     // we have to gain back the "lost" weekdays: note that the
2487     // effect of this loop is to not do anything to
2488     // nLostWeekDays (which we won't use any more), but to
2489     // (indirectly) set the year correctly
2490     while ( (nLostWeekDays % 7) != 0 )
2491     {
2492     nLostWeekDays += year++ % 4 ? 1 : 2;
2493     }
2494    
2495     // Keep year below 2000 so the 2digit year number
2496     // can never match the month or day of the month
2497     if (year>=2000) year-=28;
2498     // at any rate, we couldn't go further than 1988 + 9 + 28!
2499     wxASSERT_MSG( year < 2030,
2500     _T("logic error in wxDateTime::Format") );
2501    
2502     wxString strYear, strYear2;
2503     strYear.Printf(_T("%d"), year);
2504     strYear2.Printf(_T("%d"), year % 100);
2505    
2506     // find four strings not occurring in format (this is surely
2507     // not the optimal way of doing it... improvements welcome!)
2508     wxString fmt2 = format;
2509     wxString replacement,replacement2,replacement3,replacement4;
2510     for (int rnr=1; rnr<5 ; rnr++)
2511     {
2512     wxString r = (wxChar)-rnr;
2513     while ( fmt2.Find(r) != wxNOT_FOUND )
2514     {
2515     r << (wxChar)-rnr;
2516     }
2517    
2518     switch (rnr)
2519     {
2520     case 1: replacement=r; break;
2521     case 2: replacement2=r; break;
2522     case 3: replacement3=r; break;
2523     case 4: replacement4=r; break;
2524     }
2525     }
2526     // replace all occurrences of year with it
2527     bool wasReplaced = fmt2.Replace(strYear, replacement) > 0;
2528     // evaluation order ensures we always attempt the replacement.
2529     wasReplaced = (fmt2.Replace(strYear2, replacement2) > 0) || wasReplaced;
2530    
2531     // use strftime() to format the same date but in supported
2532     // year
2533     //
2534     // NB: we assume that strftime() doesn't check for the
2535     // date validity and will happily format the date
2536     // corresponding to Feb 29 of a non leap year (which
2537     // may happen if yearReal was leap and year is not)
2538     struct tm tmAdjusted;
2539     InitTm(tmAdjusted);
2540     tmAdjusted.tm_hour = tm.hour;
2541     tmAdjusted.tm_min = tm.min;
2542     tmAdjusted.tm_sec = tm.sec;
2543     tmAdjusted.tm_wday = tm.GetWeekDay();
2544     tmAdjusted.tm_yday = GetDayOfYear();
2545     tmAdjusted.tm_mday = tm.mday;
2546     tmAdjusted.tm_mon = tm.mon;
2547     tmAdjusted.tm_year = year - 1900;
2548     tmAdjusted.tm_isdst = 0; // no DST, already adjusted
2549     wxString str = CallStrftime(*p == _T('c') ? _T("%c")
2550     : _T("%x"),
2551     &tmAdjusted);
2552    
2553     // now replace the occurrence of 1999 with the real year
2554     // we do this in two stages to stop the 2 digit year
2555     // matching any substring of the 4 digit year.
2556     // Any day,month hours and minutes components should be safe due
2557     // to ensuring the range of the years.
2558     wxString strYearReal, strYearReal2;
2559     strYearReal.Printf(_T("%04d"), yearReal);
2560     strYearReal2.Printf(_T("%02d"), yearReal % 100);
2561     str.Replace(strYear, replacement3);
2562     str.Replace(strYear2,replacement4);
2563     str.Replace(replacement3, strYearReal);
2564     str.Replace(replacement4, strYearReal2);
2565    
2566     // and replace back all occurrences of replacement string
2567     if ( wasReplaced )
2568     {
2569     str.Replace(replacement2, strYear2);
2570     str.Replace(replacement, strYear);
2571     }
2572    
2573     res += str;
2574     }
2575     #else // !HAVE_STRFTIME
2576     // Use "%m/%d/%y %H:%M:%S" format instead
2577     res += wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
2578     tm.mon+1,tm.mday, tm.year, tm.hour, tm.min, tm.sec);
2579     #endif // HAVE_STRFTIME/!HAVE_STRFTIME
2580     break;
2581    
2582     case _T('d'): // day of a month (01-31)
2583     res += wxString::Format(fmt, tm.mday);
2584     break;
2585    
2586     case _T('H'): // hour in 24h format (00-23)
2587     res += wxString::Format(fmt, tm.hour);
2588     break;
2589    
2590     case _T('I'): // hour in 12h format (01-12)
2591     {
2592     // 24h -> 12h, 0h -> 12h too
2593     int hour12 = tm.hour > 12 ? tm.hour - 12
2594     : tm.hour ? tm.hour : 12;
2595     res += wxString::Format(fmt, hour12);
2596     }
2597     break;
2598    
2599     case _T('j'): // day of the year
2600     res += wxString::Format(fmt, GetDayOfYear(tz));
2601     break;
2602    
2603     case _T('l'): // milliseconds (NOT STANDARD)
2604     res += wxString::Format(fmt, GetMillisecond(tz));
2605     break;
2606    
2607     case _T('m'): // month as a number (01-12)
2608     res += wxString::Format(fmt, tm.mon + 1);
2609     break;
2610    
2611     case _T('M'): // minute as a decimal number (00-59)
2612     res += wxString::Format(fmt, tm.min);
2613     break;
2614    
2615     case _T('p'): // AM or PM string
2616     #ifdef HAVE_STRFTIME
2617     res += CallStrftime(_T("%p"), &tmTimeOnly);
2618     #else // !HAVE_STRFTIME
2619     res += (tmTimeOnly.tm_hour > 12) ? wxT("pm") : wxT("am");
2620     #endif // HAVE_STRFTIME/!HAVE_STRFTIME
2621     break;
2622    
2623     case _T('S'): // second as a decimal number (00-61)
2624     res += wxString::Format(fmt, tm.sec);
2625     break;
2626    
2627     case _T('U'): // week number in the year (Sunday 1st week day)
2628     res += wxString::Format(fmt, GetWeekOfYear(Sunday_First, tz));
2629     break;
2630    
2631     case _T('W'): // week number in the year (Monday 1st week day)
2632     res += wxString::Format(fmt, GetWeekOfYear(Monday_First, tz));
2633     break;
2634    
2635     case _T('w'): // weekday as a number (0-6), Sunday = 0
2636     res += wxString::Format(fmt, tm.GetWeekDay());
2637     break;
2638    
2639     // case _T('x'): -- handled with "%c"
2640    
2641     case _T('X'): // locale default time representation
2642     // just use strftime() to format the time for us
2643     #ifdef HAVE_STRFTIME
2644     res += CallStrftime(_T("%X"), &tmTimeOnly);
2645     #else // !HAVE_STRFTIME
2646     res += wxString::Format(wxT("%02d:%02d:%02d"),tm.hour, tm.min, tm.sec);
2647     #endif // HAVE_STRFTIME/!HAVE_STRFTIME
2648     break;
2649    
2650     case _T('y'): // year without century (00-99)
2651     res += wxString::Format(fmt, tm.year % 100);
2652     break;
2653    
2654     case _T('Y'): // year with century
2655     res += wxString::Format(fmt, tm.year);
2656     break;
2657    
2658     case _T('Z'): // timezone name
2659     #ifdef HAVE_STRFTIME
2660     res += CallStrftime(_T("%Z"), &tmTimeOnly);
2661     #endif
2662     break;
2663    
2664     default:
2665     // is it the format width?
2666     fmt.Empty();
2667     while ( *p == _T('-') || *p == _T('+') ||
2668     *p == _T(' ') || wxIsdigit(*p) )
2669     {
2670     fmt += *p;
2671     }
2672    
2673     if ( !fmt.empty() )
2674     {
2675     // we've only got the flags and width so far in fmt
2676     fmt.Prepend(_T('%'));
2677     fmt.Append(_T('d'));
2678    
2679     restart = true;
2680    
2681     break;
2682     }
2683    
2684     // no, it wasn't the width
2685     wxFAIL_MSG(_T("unknown format specificator"));
2686    
2687     // fall through and just copy it nevertheless
2688    
2689     case _T('%'): // a percent sign
2690     res += *p;
2691     break;
2692    
2693     case 0: // the end of string
2694     wxFAIL_MSG(_T("missing format at the end of string"));
2695    
2696     // just put the '%' which was the last char in format
2697     res += _T('%');
2698     break;
2699     }
2700     }
2701     }
2702    
2703     return res;
2704     }
2705    
2706     // this function parses a string in (strict) RFC 822 format: see the section 5
2707     // of the RFC for the detailed description, but briefly it's something of the
2708     // form "Sat, 18 Dec 1999 00:48:30 +0100"
2709     //
2710     // this function is "strict" by design - it must reject anything except true
2711     // RFC822 time specs.
2712     //
2713     // TODO a great candidate for using reg exps
2714     const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date)
2715     {
2716     wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
2717    
2718     const wxChar *p = date;
2719     const wxChar *comma = wxStrchr(p, _T(','));
2720     if ( comma )
2721     {
2722     // the part before comma is the weekday
2723    
2724     // skip it for now - we don't use but might check that it really
2725     // corresponds to the specfied date
2726     p = comma + 1;
2727    
2728     if ( *p != _T(' ') )
2729     {
2730     wxLogDebug(_T("no space after weekday in RFC822 time spec"));
2731    
2732     return (wxChar *)NULL;
2733     }
2734    
2735     p++; // skip space
2736     }
2737    
2738     // the following 1 or 2 digits are the day number
2739     if ( !wxIsdigit(*p) )
2740     {
2741     wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
2742    
2743     return (wxChar *)NULL;
2744     }
2745    
2746     wxDateTime_t day = (wxDateTime_t)(*p++ - _T('0'));
2747     if ( wxIsdigit(*p) )
2748     {
2749     day *= 10;
2750     day = (wxDateTime_t)(day + (*p++ - _T('0')));
2751     }
2752    
2753     if ( *p++ != _T(' ') )
2754     {
2755     return (wxChar *)NULL;
2756     }
2757    
2758     // the following 3 letters specify the month
2759     wxString monName(p, 3);
2760     Month mon;
2761     if ( monName == _T("Jan") )
2762     mon = Jan;
2763     else