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