]> git.saurik.com Git - wxWidgets.git/blame - src/common/datetime.cpp
Added wxIMAGE_OPTION_ORIGINAL_{WIDTH,HEIGHT} wxImage options.
[wxWidgets.git] / src / common / datetime.cpp
CommitLineData
1ef54dcf 1///////////////////////////////////////////////////////////////////////////////
96531a71 2// Name: src/common/datetime.cpp
0979c962 3// Purpose: implementation of time/date related classes
98919134 4// (for formatting&parsing see datetimefmt.cpp)
0979c962
VZ
5// Author: Vadim Zeitlin
6// Modified by:
7// Created: 11.05.99
8// RCS-ID: $Id$
1ef54dcf
VZ
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//
65571936 17// Licence: wxWindows licence
1ef54dcf
VZ
18///////////////////////////////////////////////////////////////////////////////
19
20/*
21 * Implementation notes:
22 *
23 * 1. the time is stored as a 64bit integer containing the signed number of
299fcbfe
VZ
24 * milliseconds since Jan 1. 1970 (the Unix Epoch) - so it is always
25 * expressed in GMT.
1ef54dcf
VZ
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 *
299fcbfe
VZ
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.
1ef54dcf 46 */
0979c962
VZ
47
48// ============================================================================
49// declarations
50// ============================================================================
51
52// ----------------------------------------------------------------------------
53// headers
54// ----------------------------------------------------------------------------
55
0979c962
VZ
56// For compilers that support precompilation, includes "wx.h".
57#include "wx/wxprec.h"
58
59#ifdef __BORLANDC__
60 #pragma hdrstop
61#endif
62
1e6feb95
VZ
63#if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
64
0979c962 65#ifndef WX_PRECOMP
57bd4c60
WS
66 #ifdef __WXMSW__
67 #include "wx/msw/wrapwin.h"
68 #endif
0979c962 69 #include "wx/string.h"
0979c962 70 #include "wx/log.h"
88a7a4e1 71 #include "wx/intl.h"
bb90a3e6 72 #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
02761f6c 73 #include "wx/module.h"
0bf751e7 74 #include "wx/crt.h"
0979c962
VZ
75#endif // WX_PRECOMP
76
fcc3d7cb 77#include "wx/thread.h"
cd0b1709 78#include "wx/tokenzr.h"
fcc3d7cb 79
ff0ea71c
GT
80#include <ctype.h>
81
64f9c100 82#ifdef __WINDOWS__
64f9c100 83 #include <winnls.h>
74b99d0f
WS
84 #ifndef __WXWINCE__
85 #include <locale.h>
86 #endif
64f9c100
VS
87#endif
88
0979c962
VZ
89#include "wx/datetime.h"
90
48fd6e9d
FM
91// ----------------------------------------------------------------------------
92// wxXTI
93// ----------------------------------------------------------------------------
bd4a25bc 94
cb73e600
SC
95#if wxUSE_EXTENDED_RTTI
96
97template<> void wxStringReadValue(const wxString &s , wxDateTime &data )
98{
1fcca711 99 data.ParseFormat(s,"%Y-%m-%d %H:%M:%S", NULL);
cb73e600
SC
100}
101
102template<> void wxStringWriteValue(wxString &s , const wxDateTime &data )
103{
c20d5805 104 s = data.Format("%Y-%m-%d %H:%M:%S");
cb73e600
SC
105}
106
8805dbab 107wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter<wxDateTime> , wxFromStringConverter<wxDateTime>)
cb73e600 108
c20d5805 109#endif // wxUSE_EXTENDED_RTTI
cb73e600 110
48fd6e9d 111
f0f951fa
VZ
112// ----------------------------------------------------------------------------
113// conditional compilation
114// ----------------------------------------------------------------------------
6de20863 115
31907d03
SC
116#if defined(__MWERKS__) && wxUSE_UNICODE
117 #include <wtime.h>
118#endif
119
8c3aa3ff
VZ
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__)
0779cc9a 129 #define WX_GMTOFF_IN_TM
f0f951fa 130 #endif
8c3aa3ff 131#endif
c25a510b 132
e09080ec
VZ
133// NB: VC8 safe time functions could/should be used for wxMSW as well probably
134#if defined(__WXWINCE__) && defined(__VISUALC8__)
135
136struct 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
142struct 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
5b52a684 150#if (!defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)) && wxUSE_THREADS && !defined(__WINDOWS__)
cf44a61c
SN
151static wxMutex timeLock;
152#endif
153
154#ifndef HAVE_LOCALTIME_R
155struct 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.
6f721331 160 wxMutexLocker locker(timeLock);
cf44a61c 161#endif
55809d13
VZ
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));
cf44a61c
SN
174 return temp;
175}
e09080ec 176#endif // !HAVE_LOCALTIME_R
cf44a61c
SN
177
178#ifndef HAVE_GMTIME_R
179struct 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.
6f721331 184 wxMutexLocker locker(timeLock);
cf44a61c 185#endif
55809d13
VZ
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
cf44a61c
SN
196 memcpy(temp, gmtime(ticks), sizeof(struct tm));
197 return temp;
198}
e09080ec
VZ
199#endif // !HAVE_GMTIME_R
200
201#endif // wxWinCE with VC8/other platforms
cf44a61c 202
384223b3
VZ
203// ----------------------------------------------------------------------------
204// macros
205// ----------------------------------------------------------------------------
206
207// debugging helper: just a convenient replacement of wxCHECK()
637b7e4f
VZ
208#define wxDATETIME_CHECK(expr, msg) \
209 wxCHECK2_MSG(expr, *this = wxInvalidDateTime; return *this, msg)
384223b3 210
4f6aed9c
VZ
211// ----------------------------------------------------------------------------
212// private classes
213// ----------------------------------------------------------------------------
214
215class wxDateTimeHolidaysModule : public wxModule
216{
217public:
218 virtual bool OnInit()
219 {
220 wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays);
221
68379eaf 222 return true;
4f6aed9c
VZ
223 }
224
225 virtual void OnExit()
226 {
227 wxDateTimeHolidayAuthority::ClearAllAuthorities();
df5168c4 228 wxDateTimeHolidayAuthority::ms_authorities.clear();
4f6aed9c
VZ
229 }
230
231private:
232 DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule)
233};
234
235IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule, wxModule)
236
b76b015e
VZ
237// ----------------------------------------------------------------------------
238// constants
239// ----------------------------------------------------------------------------
240
e6ec579c 241// some trivial ones
fcc3d7cb
VZ
242static const int MONTHS_IN_YEAR = 12;
243
df05cdc5
VZ
244static const int SEC_PER_MIN = 60;
245
246static const int MIN_PER_HOUR = 60;
247
e6ec579c
VZ
248static const long SECONDS_PER_DAY = 86400l;
249
df05cdc5
VZ
250static const int DAYS_PER_WEEK = 7;
251
e6ec579c
VZ
252static 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)
cd0b1709 256static const long EPOCH_JDN = 2440587l;
b76b015e 257
4b6a582b
VZ
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
1ef54dcf
VZ
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
263static const int JDN_0_YEAR = -4713;
264static const int JDN_0_MONTH = wxDateTime::Nov;
265static const int JDN_0_DAY = 24;
4b6a582b 266#endif // wxDEBUG_LEVEL
1ef54dcf
VZ
267
268// the constants used for JDN calculations
cd0b1709
VZ
269static const long JDN_OFFSET = 32046l;
270static const long DAYS_PER_5_MONTHS = 153l;
271static const long DAYS_PER_4_YEARS = 1461l;
272static const long DAYS_PER_400_YEARS = 146097l;
1ef54dcf 273
f0f951fa
VZ
274// this array contains the cumulated number of days in all previous months for
275// normal and leap years
276static 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
48fd6e9d
FM
282const long wxDateTime::TIME_T_FACTOR = 1000l;
283
fcc3d7cb 284// ----------------------------------------------------------------------------
2ef31e80
VZ
285// global data
286// ----------------------------------------------------------------------------
287
a243da29
PC
288const char wxDefaultDateTimeFormat[] = "%c";
289const char wxDefaultTimeSpanFormat[] = "%H:%M:%S";
1aaf88d2 290
384223b3
VZ
291// in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
292// indicate an invalid wxDateTime object
56d0c5a6 293const wxDateTime wxDefaultDateTime;
2ef31e80
VZ
294
295wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown;
296
b76b015e
VZ
297// ----------------------------------------------------------------------------
298// private functions
299// ----------------------------------------------------------------------------
300
4b6a582b
VZ
301// debugger helper: this function can be called from a debugger to show what
302// the date really is
77fa3d82 303extern const char *wxDumpDate(const wxDateTime* dt)
4f6aed9c 304{
77fa3d82 305 static char buf[128];
4f6aed9c 306
77fa3d82 307 wxString fmt(dt->Format("%Y-%m-%d (%a) %H:%M:%S"));
b3483429
VZ
308 wxStrlcpy(buf,
309 (fmt + " (" + dt->GetValue().ToString() + " ticks)").ToAscii(),
77fa3d82 310 WXSIZEOF(buf));
4f6aed9c
VZ
311
312 return buf;
313}
4f6aed9c 314
fcc3d7cb 315// get the number of days in the given month of the given year
c4e08560 316static inline
fcc3d7cb
VZ
317wxDateTime::wxDateTime_t GetNumOfDaysInMonth(int year, wxDateTime::Month month)
318{
e6ec579c
VZ
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
a243da29 321 static const wxDateTime::wxDateTime_t daysInMonth[2][MONTHS_IN_YEAR] =
e6ec579c
VZ
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];
fcc3d7cb
VZ
328}
329
c5432233
VZ
330// returns the time zone in the C sense, i.e. the difference UTC - local
331// (in seconds)
1ee2f9d9
FM
332// NOTE: not static because used by datetimefmt.cpp
333int GetTimeZone()
fcc3d7cb 334{
5e3566ff 335#ifdef WX_GMTOFF_IN_TM
68379eaf
WS
336 // set to true when the timezone is set
337 static bool s_timezoneSet = false;
0779cc9a 338 static long gmtoffset = LONG_MAX; // invalid timezone
c5432233 339
a452689b 340 // ensure that the timezone variable is set by calling wxLocaltime_r
fcc3d7cb
VZ
341 if ( !s_timezoneSet )
342 {
a452689b
SN
343 // just call wxLocaltime_r() instead of figuring out whether this
344 // system supports tzset(), _tzset() or something else
2673c239 345 time_t t = time(NULL);
3d561414 346 struct tm tm;
fcc3d7cb 347
3d561414 348 wxLocaltime_r(&t, &tm);
68379eaf 349 s_timezoneSet = true;
c5432233 350
c5432233
VZ
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
3d561414 354 gmtoffset = -tm.tm_gmtoff;
2673c239
VZ
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;
fcc3d7cb 361 }
0779cc9a 362 return (int)gmtoffset;
8c3aa3ff
VZ
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.
5e3566ff 383 return WX_TIMEZONE;
8c3aa3ff
VZ
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;
5e3566ff 390#endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
fcc3d7cb
VZ
391}
392
e6ec579c 393// return the integral part of the JDN for the midnight of the given date (to
1ef54dcf
VZ
394// get the real JDN you need to add 0.5, this is, in fact, JDN of the
395// noon of the previous day)
e6ec579c
VZ
396static long GetTruncatedJDN(wxDateTime::wxDateTime_t day,
397 wxDateTime::Month mon,
398 int year)
399{
1ef54dcf
VZ
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)),
9a83f860 407 wxT("date out of range - can't convert to JDN")
1ef54dcf
VZ
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 )
e6ec579c 416 {
1ef54dcf 417 month = mon - 2;
e6ec579c 418 }
1ef54dcf 419 else
e6ec579c 420 {
1ef54dcf
VZ
421 month = mon + 10;
422 year--;
423 }
e6ec579c 424
1ef54dcf
VZ
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;
e6ec579c
VZ
431}
432
dd36b5a3 433#ifdef wxHAS_STRFTIME
bb7afa49 434
2423ef22 435// this function is a wrapper around strftime(3) adding error checking
1ee2f9d9
FM
436// NOTE: not static because used by datetimefmt.cpp
437wxString CallStrftime(const wxString& format, const tm* tm)
b76b015e 438{
68ee7c47 439 wxChar buf[4096];
594588aa
MW
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
ba9bbf13 444 if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) )
b76b015e 445 {
c105b3cd
VZ
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 }
adec277f
VZ
455
456 buf[0] = '\0';
b76b015e
VZ
457 }
458
594588aa
MW
459 s = buf;
460 return s;
b76b015e 461}
bb7afa49 462
dd36b5a3 463#endif // wxHAS_STRFTIME
b76b015e 464
2f02cb89
VZ
465// if year and/or month have invalid values, replace them with the current ones
466static void ReplaceDefaultYearMonthWithCurrent(int *year,
467 wxDateTime::Month *month)
468{
469 struct tm *tmNow = NULL;
a452689b 470 struct tm tmstruct;
2f02cb89
VZ
471
472 if ( *year == wxDateTime::Inv_Year )
473 {
a452689b 474 tmNow = wxDateTime::GetTmNow(&tmstruct);
2f02cb89
VZ
475
476 *year = 1900 + tmNow->tm_year;
477 }
478
479 if ( *month == wxDateTime::Inv_Month )
480 {
481 if ( !tmNow )
a452689b 482 tmNow = wxDateTime::GetTmNow(&tmstruct);
2f02cb89
VZ
483
484 *month = (wxDateTime::Month)tmNow->tm_mon;
485 }
486}
487
1ee2f9d9
FM
488// fill the struct tm with default values
489// NOTE: not static because used by datetimefmt.cpp
490void InitTm(struct tm& tm)
f0f951fa
VZ
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
0979c962
VZ
501// ============================================================================
502// implementation of wxDateTime
503// ============================================================================
504
b76b015e
VZ
505// ----------------------------------------------------------------------------
506// struct Tm
507// ----------------------------------------------------------------------------
508
509wxDateTime::Tm::Tm()
510{
511 year = (wxDateTime_t)wxDateTime::Inv_Year;
512 mon = wxDateTime::Inv_Month;
fdb44eb2
VZ
513 mday =
514 yday = 0;
515 hour =
516 min =
517 sec =
518 msec = 0;
b76b015e
VZ
519 wday = wxDateTime::Inv_WeekDay;
520}
521
299fcbfe
VZ
522wxDateTime::Tm::Tm(const struct tm& tm, const TimeZone& tz)
523 : m_tz(tz)
b76b015e 524{
e6ec579c 525 msec = 0;
907173e5
WS
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;
fcc3d7cb 530 mon = (wxDateTime::Month)tm.tm_mon;
b76b015e 531 year = 1900 + tm.tm_year;
bb8a8478
WS
532 wday = (wxDateTime::wxDateTime_t)tm.tm_wday;
533 yday = (wxDateTime::wxDateTime_t)tm.tm_yday;
b76b015e
VZ
534}
535
536bool wxDateTime::Tm::IsValid() const
537{
8b3eb4a0
VZ
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
b76b015e 546 // we allow for the leap seconds, although we don't use them (yet)
fcc3d7cb 547 return (year != wxDateTime::Inv_Year) && (mon != wxDateTime::Inv_Month) &&
8b3eb4a0 548 (mday > 0 && mday <= GetNumOfDaysInMonth(year, mon)) &&
e6ec579c 549 (hour < 24) && (min < 60) && (sec < 62) && (msec < 1000);
b76b015e
VZ
550}
551
552void wxDateTime::Tm::ComputeWeekDay()
553{
c5a1681b
VZ
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
5a572f6c 557 wday = (wxDateTime::wxDateTime_t)((GetTruncatedJDN(mday, mon, year) + 2) % 7);
b76b015e
VZ
558}
559
e6ec579c 560void wxDateTime::Tm::AddMonths(int monDiff)
fcc3d7cb
VZ
561{
562 // normalize the months field
563 while ( monDiff < -mon )
564 {
565 year--;
566
567 monDiff += MONTHS_IN_YEAR;
568 }
569
2ef31e80 570 while ( monDiff + mon >= MONTHS_IN_YEAR )
fcc3d7cb
VZ
571 {
572 year++;
239446b4
VZ
573
574 monDiff -= MONTHS_IN_YEAR;
fcc3d7cb
VZ
575 }
576
577 mon = (wxDateTime::Month)(mon + monDiff);
578
9a83f860 579 wxASSERT_MSG( mon >= 0 && mon < MONTHS_IN_YEAR, wxT("logic error") );
4f6aed9c
VZ
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
fcc3d7cb
VZ
583}
584
e6ec579c 585void wxDateTime::Tm::AddDays(int dayDiff)
fcc3d7cb
VZ
586{
587 // normalize the days field
9d9b7755 588 while ( dayDiff + mday < 1 )
fcc3d7cb
VZ
589 {
590 AddMonths(-1);
591
9d9b7755 592 dayDiff += GetNumOfDaysInMonth(year, mon);
fcc3d7cb
VZ
593 }
594
907173e5 595 mday = (wxDateTime::wxDateTime_t)( mday + dayDiff );
fcc3d7cb
VZ
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),
9a83f860 604 wxT("logic error") );
fcc3d7cb
VZ
605}
606
607// ----------------------------------------------------------------------------
608// class TimeZone
609// ----------------------------------------------------------------------------
610
611wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz)
612{
613 switch ( tz )
614 {
615 case wxDateTime::Local:
299fcbfe
VZ
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();
fcc3d7cb
VZ
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:
299fcbfe 633 m_offset = -3600*(wxDateTime::GMT0 - tz);
fcc3d7cb
VZ
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:
34f90a1c 649 case wxDateTime::GMT13:
299fcbfe 650 m_offset = 3600*(tz - wxDateTime::GMT0);
fcc3d7cb
VZ
651 break;
652
653 case wxDateTime::A_CST:
654 // Central Standard Time in use in Australia = UTC + 9.5
d26adb9d 655 m_offset = 60l*(9*MIN_PER_HOUR + MIN_PER_HOUR/2);
fcc3d7cb
VZ
656 break;
657
658 default:
9a83f860 659 wxFAIL_MSG( wxT("unknown time zone") );
fcc3d7cb
VZ
660 }
661}
662
b76b015e
VZ
663// ----------------------------------------------------------------------------
664// static functions
665// ----------------------------------------------------------------------------
666
1ee2f9d9
FM
667/* static */
668struct tm *wxDateTime::GetTmNow(struct tm *tmstruct)
669{
670 time_t t = GetTimeNow();
671 return wxLocaltime_r(&t, tmstruct);
672}
673
b76b015e
VZ
674/* static */
675bool wxDateTime::IsLeapYear(int year, wxDateTime::Calendar cal)
676{
2f02cb89
VZ
677 if ( year == Inv_Year )
678 year = GetCurrentYear();
679
b76b015e
VZ
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 {
9a83f860 695 wxFAIL_MSG(wxT("unknown calendar"));
b76b015e 696
68379eaf 697 return false;
b76b015e
VZ
698 }
699}
700
fcc3d7cb
VZ
701/* static */
702int wxDateTime::GetCentury(int year)
703{
704 return year > 0 ? year / 100 : year / 100 - 1;
705}
706
b76b015e
VZ
707/* static */
708int wxDateTime::ConvertYearToBC(int year)
709{
710 // year 0 is BC 1
711 return year > 0 ? year : year - 1;
712}
713
714/* static */
715int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal)
716{
717 switch ( cal )
718 {
719 case Gregorian:
720 return Now().GetYear();
721
722 case Julian:
9a83f860 723 wxFAIL_MSG(wxT("TODO"));
b76b015e
VZ
724 break;
725
726 default:
9a83f860 727 wxFAIL_MSG(wxT("unsupported calendar"));
b76b015e
VZ
728 break;
729 }
730
731 return Inv_Year;
732}
733
734/* static */
735wxDateTime::Month wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal)
736{
737 switch ( cal )
738 {
739 case Gregorian:
740 return Now().GetMonth();
b76b015e
VZ
741
742 case Julian:
9a83f860 743 wxFAIL_MSG(wxT("TODO"));
b76b015e
VZ
744 break;
745
746 default:
9a83f860 747 wxFAIL_MSG(wxT("unsupported calendar"));
b76b015e
VZ
748 break;
749 }
750
751 return Inv_Month;
752}
753
2f02cb89
VZ
754/* static */
755wxDateTime::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;
2f02cb89
VZ
768
769 default:
9a83f860 770 wxFAIL_MSG(wxT("unsupported calendar"));
2f02cb89
VZ
771 break;
772 }
773
774 return 0;
775}
776
b76b015e
VZ
777/* static */
778wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(wxDateTime::Month month,
779 int year,
780 wxDateTime::Calendar cal)
781{
9a83f860 782 wxCHECK_MSG( month < MONTHS_IN_YEAR, 0, wxT("invalid month") );
b76b015e
VZ
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
fcc3d7cb 792 return GetNumOfDaysInMonth(year, month);
b76b015e
VZ
793 }
794 else
795 {
9a83f860 796 wxFAIL_MSG(wxT("unsupported calendar"));
b76b015e
VZ
797
798 return 0;
799 }
800}
801
e538985e
VZ
802namespace
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
811int 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 */
831wxString wxDateTime::GetEnglishMonthName(Month month, NameFlags flags)
832{
833 wxCHECK_MSG( month != Inv_Month, wxEmptyString, "invalid month" );
834
a243da29 835 static const char *const monthNames[2][MONTHS_IN_YEAR] =
e538985e
VZ
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
b76b015e 850/* static */
f0f951fa
VZ
851wxString wxDateTime::GetMonthName(wxDateTime::Month month,
852 wxDateTime::NameFlags flags)
b76b015e 853{
dd36b5a3 854#ifdef wxHAS_STRFTIME
9a83f860 855 wxCHECK_MSG( month != Inv_Month, wxEmptyString, wxT("invalid month") );
e538985e 856
68ee7c47
VZ
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)
cd0b1709 859 tm tm;
f0f951fa 860 InitTm(tm);
cd0b1709 861 tm.tm_mon = month;
b76b015e 862
9a83f860 863 return CallStrftime(flags == Name_Abbr ? wxT("%b") : wxT("%B"), &tm);
dd36b5a3 864#else // !wxHAS_STRFTIME
e538985e 865 return GetEnglishMonthName(month, flags);
dd36b5a3 866#endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
b76b015e
VZ
867}
868
e538985e
VZ
869/* static */
870wxString wxDateTime::GetEnglishWeekDayName(WeekDay wday, NameFlags flags)
871{
9a83f860 872 wxCHECK_MSG( wday != Inv_WeekDay, wxEmptyString, wxT("invalid weekday") );
e538985e 873
a243da29 874 static const char *const weekdayNames[2][DAYS_PER_WEEK] =
e538985e
VZ
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
b76b015e 888/* static */
f0f951fa
VZ
889wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday,
890 wxDateTime::NameFlags flags)
b76b015e 891{
dd36b5a3 892#ifdef wxHAS_STRFTIME
9a83f860 893 wxCHECK_MSG( wday != Inv_WeekDay, wxEmptyString, wxT("invalid weekday") );
e538985e 894
2c622d71
VZ
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!)
f0f951fa
VZ
898 tm tm;
899 InitTm(tm);
2c622d71 900 tm.tm_mday = 21;
f0f951fa
VZ
901 tm.tm_mon = Nov;
902 tm.tm_year = 99;
b76b015e 903
1ef54dcf 904 // and offset it by the number of days needed to get the correct wday
b76b015e
VZ
905 tm.tm_mday += wday;
906
c5a1681b
VZ
907 // call mktime() to normalize it...
908 (void)mktime(&tm);
909
910 // ... and call strftime()
9a83f860 911 return CallStrftime(flags == Name_Abbr ? wxT("%a") : wxT("%A"), &tm);
dd36b5a3 912#else // !wxHAS_STRFTIME
e538985e 913 return GetEnglishWeekDayName(wday, flags);
dd36b5a3 914#endif // wxHAS_STRFTIME/!wxHAS_STRFTIME
f0f951fa
VZ
915}
916
917/* static */
918void wxDateTime::GetAmPmStrings(wxString *am, wxString *pm)
919{
920 tm tm;
921 InitTm(tm);
653567bb 922 wxChar buffer[64];
0308fd61
JS
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.
f0f951fa
VZ
931 if ( am )
932 {
9a83f860 933 if (wxStrftime(buffer, WXSIZEOF(buffer), wxT("%p"), &tm) > 0)
0308fd61
JS
934 *am = wxString(buffer);
935 else
936 *am = wxString();
f0f951fa
VZ
937 }
938 if ( pm )
939 {
940 tm.tm_hour = 13;
9a83f860 941 if (wxStrftime(buffer, WXSIZEOF(buffer), wxT("%p"), &tm) > 0)
0308fd61
JS
942 *pm = wxString(buffer);
943 else
944 *pm = wxString();
f0f951fa 945 }
b76b015e
VZ
946}
947
54e660f8 948
239446b4
VZ
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 */
955wxDateTime::Country wxDateTime::GetCountry()
956{
f0f951fa 957 // TODO use LOCALE_ICOUNTRY setting under Win32
77b88df2 958#ifndef __WXWINCE__
239446b4
VZ
959 if ( ms_country == Country_Unknown )
960 {
961 // try to guess from the time zone name
962 time_t t = time(NULL);
a452689b
SN
963 struct tm tmstruct;
964 struct tm *tm = wxLocaltime_r(&t, &tmstruct);
239446b4 965
9a83f860
VZ
966 wxString tz = CallStrftime(wxT("%Z"), tm);
967 if ( tz == wxT("WET") || tz == wxT("WEST") )
239446b4
VZ
968 {
969 ms_country = UK;
970 }
9a83f860 971 else if ( tz == wxT("CET") || tz == wxT("CEST") )
239446b4
VZ
972 {
973 ms_country = Country_EEC;
974 }
9a83f860 975 else if ( tz == wxT("MSK") || tz == wxT("MSD") )
239446b4
VZ
976 {
977 ms_country = Russia;
978 }
9a83f860
VZ
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") )
239446b4
VZ
984 {
985 ms_country = USA;
986 }
987 else
988 {
989 // well, choose a default one
990 ms_country = USA;
991 }
992 }
bb7afa49 993#else // __WXWINCE__
77b88df2 994 ms_country = USA;
bb7afa49 995#endif // !__WXWINCE__/__WXWINCE__
239446b4
VZ
996
997 return ms_country;
998}
999
1000/* static */
1001void wxDateTime::SetCountry(wxDateTime::Country country)
1002{
1003 ms_country = country;
1004}
1005
1006/* static */
1007bool 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 */
1026bool 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 */
1056wxDateTime 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 {
2ef31e80 1071 return wxInvalidDateTime;
239446b4
VZ
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...
9a83f860 1082 wxFAIL_MSG( wxT("no last Sunday in March?") );
239446b4
VZ
1083 }
1084
1085 dt += wxTimeSpan::Hours(1);
239446b4
VZ
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...
9a83f860 1125 wxFAIL_MSG( wxT("no first Sunday in April?") );
239446b4
VZ
1126 }
1127 }
6b522db5
VZ
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...
9a83f860 1136 wxFAIL_MSG( wxT("no second Sunday in March?") );
6b522db5
VZ
1137 }
1138 }
239446b4
VZ
1139 else
1140 {
1141 if ( !dt.SetToWeekDay(Sun, 1, Apr, year) )
1142 {
1143 // weird...
9a83f860 1144 wxFAIL_MSG( wxT("no first Sunday in April?") );
239446b4
VZ
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 */
1165wxDateTime 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 {
2ef31e80 1180 return wxInvalidDateTime;
239446b4
VZ
1181 }
1182
1183 wxDateTime dt;
1184
1185 if ( IsWestEuropeanCountry(country) || (country == Russia) )
1186 {
5f287370 1187 // DST ends at 1 a.m. GMT on the last Sunday of October
239446b4
VZ
1188 if ( !dt.SetToLastWeekDay(Sun, Oct, year) )
1189 {
1190 // weirder and weirder...
9a83f860 1191 wxFAIL_MSG( wxT("no last Sunday in October?") );
239446b4
VZ
1192 }
1193
1194 dt += wxTimeSpan::Hours(1);
239446b4
VZ
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
6b522db5
VZ
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...
9a83f860 1224 wxFAIL_MSG( wxT("no first Sunday in November?") );
6b522db5
VZ
1225 }
1226 }
1227 else
1228 // pre-2007
1229 // DST ends at 2 a.m. on the last Sunday of October
239446b4 1230 {
6b522db5
VZ
1231 if ( !dt.SetToLastWeekDay(Sun, Oct, year) )
1232 {
1233 // weirder and weirder...
9a83f860 1234 wxFAIL_MSG( wxT("no last Sunday in October?") );
6b522db5 1235 }
239446b4
VZ
1236 }
1237
1238 dt += wxTimeSpan::Hours(2);
1239
6b522db5 1240 // TODO: what about timezone??
239446b4
VZ
1241 }
1242 break;
1243
6b522db5 1244 default: // default for switch (country)
239446b4
VZ
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
0979c962
VZ
1252// ----------------------------------------------------------------------------
1253// constructors and assignment operators
1254// ----------------------------------------------------------------------------
1255
f6bcfd97
BP
1256// return the current time with ms precision
1257/* static */ wxDateTime wxDateTime::UNow()
1258{
1259 return wxDateTime(wxGetLocalTimeMillis());
1260}
1261
299fcbfe
VZ
1262// the values in the tm structure contain the local time
1263wxDateTime& wxDateTime::Set(const struct tm& tm)
0979c962 1264{
299fcbfe 1265 struct tm tm2(tm);
b76b015e 1266 time_t timet = mktime(&tm2);
1ef54dcf 1267
4afd7529 1268 if ( timet == (time_t)-1 )
0979c962 1269 {
4afd7529
VZ
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 {
3d78a532
RD
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));
4afd7529
VZ
1279 }
1280
9a83f860 1281 wxFAIL_MSG( wxT("mktime() failed") );
0979c962 1282
384223b3
VZ
1283 *this = wxInvalidDateTime;
1284
1285 return *this;
0979c962
VZ
1286 }
1287 else
1288 {
1289 return Set(timet);
1290 }
1291}
1292
1293wxDateTime& 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
384223b3
VZ
1300 wxDATETIME_CHECK( hour < 24 &&
1301 second < 62 &&
1302 minute < 60 &&
1303 millisec < 1000,
9a83f860 1304 wxT("Invalid time in wxDateTime::Set()") );
0979c962
VZ
1305
1306 // get the current date from system
a452689b
SN
1307 struct tm tmstruct;
1308 struct tm *tm = GetTmNow(&tmstruct);
299fcbfe 1309
9a83f860 1310 wxDATETIME_CHECK( tm, wxT("wxLocaltime_r() failed") );
0979c962 1311
456639ac
VZ
1312 // make a copy so it isn't clobbered by the call to mktime() below
1313 struct tm tm1(*tm);
1314
0979c962 1315 // adjust the time
456639ac
VZ
1316 tm1.tm_hour = hour;
1317 tm1.tm_min = minute;
1318 tm1.tm_sec = second;
0979c962 1319
9cc311d3 1320 // and the DST in case it changes on this date
456639ac 1321 struct tm tm2(tm1);
9cc311d3 1322 mktime(&tm2);
456639ac
VZ
1323 if ( tm2.tm_isdst != tm1.tm_isdst )
1324 tm1.tm_isdst = tm2.tm_isdst;
9cc311d3 1325
456639ac 1326 (void)Set(tm1);
0979c962
VZ
1327
1328 // and finally adjust milliseconds
1329 return SetMillisecond(millisec);
1330}
1331
1332wxDateTime& 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{
384223b3
VZ
1340 wxDATETIME_CHECK( hour < 24 &&
1341 second < 62 &&
1342 minute < 60 &&
1343 millisec < 1000,
9a83f860 1344 wxT("Invalid time in wxDateTime::Set()") );
0979c962 1345
2f02cb89 1346 ReplaceDefaultYearMonthWithCurrent(&year, &month);
0979c962 1347
384223b3 1348 wxDATETIME_CHECK( (0 < day) && (day <= GetNumberOfDays(month, year)),
9a83f860 1349 wxT("Invalid date in wxDateTime::Set()") );
0979c962
VZ
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
2f02cb89 1357 if ( year >= yearMinInRange && year <= yearMaxInRange )
0979c962
VZ
1358 {
1359 // use the standard library version if the date is in range - this is
b76b015e 1360 // probably more efficient than our code
0979c962 1361 struct tm tm;
b76b015e 1362 tm.tm_year = year - 1900;
0979c962
VZ
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;
299fcbfe 1368 tm.tm_isdst = -1; // mktime() will guess it
0979c962
VZ
1369
1370 (void)Set(tm);
1371
1372 // and finally adjust milliseconds
3d78a532
RD
1373 if (IsValid())
1374 SetMillisecond(millisec);
1375
1376 return *this;
0979c962
VZ
1377 }
1378 else
1379 {
1380 // do time calculations ourselves: we want to calculate the number of
fcc3d7cb 1381 // milliseconds between the given date and the epoch
e6ec579c
VZ
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
299fcbfe
VZ
1388 // JDN corresponds to GMT, we take localtime
1389 Add(wxTimeSpan(hour, minute, second + GetTimeZone(), millisec));
b76b015e
VZ
1390 }
1391
1392 return *this;
1393}
1394
e6ec579c
VZ
1395wxDateTime& wxDateTime::Set(double jdn)
1396{
1ef54dcf
VZ
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
d26adb9d 1401 m_time.Assign(jdn*MILLISECONDS_PER_DAY);
cd0b1709 1402
d26adb9d 1403 // JDNs always are in UTC, so we don't need any adjustments for time zone
e6ec579c
VZ
1404
1405 return *this;
1406}
1407
9d9b7755
VZ
1408wxDateTime& 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
fb96cf85
VZ
1425wxDateTime 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
2b5f62a0
VZ
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
1449wxDateTime& wxDateTime::SetFromDOS(unsigned long ddt)
1450{
1451 struct tm tm;
4739346e 1452 InitTm(tm);
2b5f62a0
VZ
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
1482unsigned long wxDateTime::GetAsDOS() const
1483{
1484 unsigned long ddt;
1485 time_t ticks = GetTicks();
a452689b
SN
1486 struct tm tmstruct;
1487 struct tm *tm = wxLocaltime_r(&ticks, &tmstruct);
9a83f860 1488 wxCHECK_MSG( tm, ULONG_MAX, wxT("time can't be represented in DOS format") );
2b5f62a0
VZ
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
b76b015e
VZ
1514// ----------------------------------------------------------------------------
1515// time_t <-> broken down time conversions
1516// ----------------------------------------------------------------------------
1517
299fcbfe 1518wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
b76b015e 1519{
9a83f860 1520 wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
b76b015e
VZ
1521
1522 time_t time = GetTicks();
1523 if ( time != (time_t)-1 )
1524 {
1525 // use C RTL functions
a452689b 1526 struct tm tmstruct;
299fcbfe
VZ
1527 tm *tm;
1528 if ( tz.GetOffset() == -GetTimeZone() )
1529 {
1530 // we are working with local time
a452689b 1531 tm = wxLocaltime_r(&time, &tmstruct);
c5a1681b
VZ
1532
1533 // should never happen
9a83f860 1534 wxCHECK_MSG( tm, Tm(), wxT("wxLocaltime_r() failed") );
299fcbfe
VZ
1535 }
1536 else
1537 {
13111b2a 1538 time += (time_t)tz.GetOffset();
33ac7e6f 1539#if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
13111b2a 1540 int time2 = (int) time;
9d9b7755 1541 if ( time2 >= 0 )
fb10f04c 1542#else
9d9b7755 1543 if ( time >= 0 )
fb10f04c 1544#endif
c5a1681b 1545 {
a452689b 1546 tm = wxGmtime_r(&time, &tmstruct);
b76b015e 1547
c5a1681b 1548 // should never happen
9a83f860 1549 wxCHECK_MSG( tm, Tm(), wxT("wxGmtime_r() failed") );
c5a1681b
VZ
1550 }
1551 else
1552 {
1553 tm = (struct tm *)NULL;
1554 }
1555 }
b76b015e 1556
c5a1681b
VZ
1557 if ( tm )
1558 {
f6bcfd97
BP
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;
c5a1681b
VZ
1564 }
1565 //else: use generic code below
b76b015e 1566 }
e6ec579c 1567
c5a1681b
VZ
1568 // remember the time and do the calculations with the date only - this
1569 // eliminates rounding errors of the floating point arithmetics
299fcbfe 1570
c5a1681b 1571 wxLongLong timeMidnight = m_time + tz.GetOffset() * 1000;
1ef54dcf 1572
c5a1681b 1573 long timeOnly = (timeMidnight % MILLISECONDS_PER_DAY).ToLong();
1ef54dcf 1574
c5a1681b
VZ
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 }
e6ec579c 1581
c5a1681b 1582 timeMidnight -= timeOnly;
1ef54dcf 1583
c5a1681b
VZ
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
1ef54dcf 1586
c5a1681b
VZ
1587 // actually, this is the JDN for the noon of the previous day
1588 long jdn = (timeMidnight / MILLISECONDS_PER_DAY).ToLong() + EPOCH_JDN;
1ef54dcf 1589
c5a1681b 1590 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
1ef54dcf 1591
9a83f860 1592 wxASSERT_MSG( jdn > -2, wxT("JDN out of range") );
1ef54dcf 1593
c5a1681b 1594 // calculate the century
479cd5de
VZ
1595 long temp = (jdn + JDN_OFFSET) * 4 - 1;
1596 long century = temp / DAYS_PER_400_YEARS;
1ef54dcf 1597
c5a1681b
VZ
1598 // then the year and day of year (1 <= dayOfYear <= 366)
1599 temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
479cd5de
VZ
1600 long year = (century * 100) + (temp / DAYS_PER_4_YEARS);
1601 long dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
1ef54dcf 1602
c5a1681b
VZ
1603 // and finally the month and day of the month
1604 temp = dayOfYear * 5 - 3;
479cd5de
VZ
1605 long month = temp / DAYS_PER_5_MONTHS;
1606 long day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
c5a1681b
VZ
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 }
1ef54dcf 1618
c5a1681b
VZ
1619 // year is offset by 4800
1620 year -= 4800;
1ef54dcf 1621
c5a1681b 1622 // check that the algorithm gave us something reasonable
9a83f860
VZ
1623 wxASSERT_MSG( (0 < month) && (month <= 12), wxT("invalid month") );
1624 wxASSERT_MSG( (1 <= day) && (day < 32), wxT("invalid day") );
e6ec579c 1625
c5a1681b
VZ
1626 // construct Tm from these values
1627 Tm tm;
1628 tm.year = (int)year;
5ed8879e 1629 tm.yday = (wxDateTime_t)(dayOfYear - 1); // use C convention for day number
c5a1681b
VZ
1630 tm.mon = (Month)(month - 1); // algorithm yields 1 for January, not 0
1631 tm.mday = (wxDateTime_t)day;
479cd5de 1632 tm.msec = (wxDateTime_t)(timeOnly % 1000);
c5a1681b
VZ
1633 timeOnly -= tm.msec;
1634 timeOnly /= 1000; // now we have time in seconds
e6ec579c 1635
d26adb9d 1636 tm.sec = (wxDateTime_t)(timeOnly % SEC_PER_MIN);
c5a1681b 1637 timeOnly -= tm.sec;
d26adb9d 1638 timeOnly /= SEC_PER_MIN; // now we have time in minutes
e6ec579c 1639
d26adb9d 1640 tm.min = (wxDateTime_t)(timeOnly % MIN_PER_HOUR);
c5a1681b 1641 timeOnly -= tm.min;
e6ec579c 1642
d26adb9d 1643 tm.hour = (wxDateTime_t)(timeOnly / MIN_PER_HOUR);
b76b015e 1644
c5a1681b 1645 return tm;
b76b015e
VZ
1646}
1647
1648wxDateTime& wxDateTime::SetYear(int year)
1649{
9a83f860 1650 wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
b76b015e
VZ
1651
1652 Tm tm(GetTm());
1653 tm.year = year;
1654 Set(tm);
1655
1656 return *this;
1657}
1658
1659wxDateTime& wxDateTime::SetMonth(Month month)
1660{
9a83f860 1661 wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
b76b015e
VZ
1662
1663 Tm tm(GetTm());
1664 tm.mon = month;
1665 Set(tm);
1666
1667 return *this;
1668}
1669
1670wxDateTime& wxDateTime::SetDay(wxDateTime_t mday)
1671{
9a83f860 1672 wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
b76b015e
VZ
1673
1674 Tm tm(GetTm());
1675 tm.mday = mday;
1676 Set(tm);
1677
1678 return *this;
1679}
1680
1681wxDateTime& wxDateTime::SetHour(wxDateTime_t hour)
1682{
9a83f860 1683 wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
b76b015e
VZ
1684
1685 Tm tm(GetTm());
1686 tm.hour = hour;
1687 Set(tm);
1688
1689 return *this;
1690}
1691
1692wxDateTime& wxDateTime::SetMinute(wxDateTime_t min)
1693{
9a83f860 1694 wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
b76b015e
VZ
1695
1696 Tm tm(GetTm());
1697 tm.min = min;
1698 Set(tm);
1699
1700 return *this;
1701}
1702
1703wxDateTime& wxDateTime::SetSecond(wxDateTime_t sec)
1704{
9a83f860 1705 wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
b76b015e
VZ
1706
1707 Tm tm(GetTm());
1708 tm.sec = sec;
1709 Set(tm);
0979c962
VZ
1710
1711 return *this;
1712}
b76b015e
VZ
1713
1714wxDateTime& wxDateTime::SetMillisecond(wxDateTime_t millisecond)
1715{
9a83f860 1716 wxASSERT_MSG( IsValid(), wxT("invalid wxDateTime") );
b76b015e
VZ
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
1729wxDateTime& wxDateTime::Add(const wxDateSpan& diff)
1730{
1731 Tm tm(GetTm());
1732
1733 tm.year += diff.GetYears();
fcc3d7cb 1734 tm.AddMonths(diff.GetMonths());
4f6aed9c
VZ
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
fcc3d7cb 1749 tm.AddDays(diff.GetTotalDays());
b76b015e
VZ
1750
1751 Set(tm);
1752
9d9b7755 1753 wxASSERT_MSG( IsSameTime(tm),
9a83f860 1754 wxT("Add(wxDateSpan) shouldn't modify time") );
9d9b7755 1755
b76b015e
VZ
1756 return *this;
1757}
1758
2f02cb89
VZ
1759// ----------------------------------------------------------------------------
1760// Weekday and monthday stuff
1761// ----------------------------------------------------------------------------
1762
4c27e2fa
VZ
1763// convert Sun, Mon, ..., Sat into 6, 0, ..., 5
1764static inline int ConvertWeekDayToMondayBase(int wd)
1765{
1766 return wd == wxDateTime::Sun ? 6 : wd - 1;
1767}
1768
1769/* static */
1770wxDateTime
1771wxDateTime::SetToWeekOfYear(int year, wxDateTime_t numWeek, WeekDay wd)
4f6aed9c 1772{
2b5f62a0 1773 wxASSERT_MSG( numWeek > 0,
9a83f860 1774 wxT("invalid week number: weeks are counted from 1") );
2b5f62a0 1775
4c27e2fa
VZ
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}
4f6aed9c 1783
ca3e85cf 1784#if WXWIN_COMPATIBILITY_2_6
4c27e2fa
VZ
1785// use a separate function to avoid warnings about using deprecated
1786// SetToTheWeek in GetWeek below
1787static wxDateTime
1788SetToTheWeek(int year,
1789 wxDateTime::wxDateTime_t numWeek,
1790 wxDateTime::WeekDay weekday,
1791 wxDateTime::WeekFlags flags)
1792{
4f6aed9c 1793 // Jan 4 always lies in the 1st week of the year
4c27e2fa
VZ
1794 wxDateTime dt(4, wxDateTime::Jan, year);
1795 dt.SetToWeekDayInSameWeek(weekday, flags);
1796 dt += wxDateSpan::Weeks(numWeek - 1);
4f6aed9c 1797
4c27e2fa
VZ
1798 return dt;
1799}
1800
1801bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek,
1802 WeekDay weekday,
1803 WeekFlags flags)
1804{
1805 int year = GetYear();
1806 *this = ::SetToTheWeek(year, numWeek, weekday, flags);
4f6aed9c
VZ
1807 if ( GetYear() != year )
1808 {
1809 // oops... numWeek was too big
68379eaf 1810 return false;
4f6aed9c
VZ
1811 }
1812
68379eaf 1813 return true;
4f6aed9c
VZ
1814}
1815
4c27e2fa
VZ
1816wxDateTime wxDateTime::GetWeek(wxDateTime_t numWeek,
1817 WeekDay weekday,
1818 WeekFlags flags) const
1819{
1820 return ::SetToTheWeek(GetYear(), numWeek, weekday, flags);
1821}
ca3e85cf 1822#endif // WXWIN_COMPATIBILITY_2_6
4c27e2fa 1823
2f02cb89
VZ
1824wxDateTime& wxDateTime::SetToLastMonthDay(Month month,
1825 int year)
1826{
1827 // take the current month/year if none specified
1a8557b1
VZ
1828 if ( year == Inv_Year )
1829 year = GetYear();
1830 if ( month == Inv_Month )
1831 month = GetMonth();
2f02cb89 1832
fcc3d7cb 1833 return Set(GetNumOfDaysInMonth(year, month), month, year);
2f02cb89
VZ
1834}
1835
2b5f62a0 1836wxDateTime& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday, WeekFlags flags)
cd0b1709 1837{
9a83f860 1838 wxDATETIME_CHECK( weekday != Inv_WeekDay, wxT("invalid weekday") );
cd0b1709 1839
af80bc92
VZ
1840 int wdayDst = weekday,
1841 wdayThis = GetWeekDay();
1842 if ( wdayDst == wdayThis )
cd0b1709
VZ
1843 {
1844 // nothing to do
1845 return *this;
1846 }
2b5f62a0
VZ
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
af80bc92 1856 if ( flags == Monday_First )
2b5f62a0
VZ
1857 {
1858 if ( wdayThis == Sun )
1859 wdayThis += 7;
af80bc92
VZ
1860 if ( wdayDst == Sun )
1861 wdayDst += 7;
2b5f62a0
VZ
1862 }
1863 //else: Sunday_First, nothing to do
1864
1865 // go forward or back in time to the day we want
af80bc92 1866 if ( wdayDst < wdayThis )
cd0b1709 1867 {
af80bc92 1868 return Subtract(wxDateSpan::Days(wdayThis - wdayDst));
cd0b1709
VZ
1869 }
1870 else // weekday > wdayThis
1871 {
af80bc92 1872 return Add(wxDateSpan::Days(wdayDst - wdayThis));
cd0b1709
VZ
1873 }
1874}
1875
1876wxDateTime& wxDateTime::SetToNextWeekDay(WeekDay weekday)
1877{
9a83f860 1878 wxDATETIME_CHECK( weekday != Inv_WeekDay, wxT("invalid weekday") );
cd0b1709
VZ
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
4f6aed9c 1897 return Add(wxDateSpan::Days(diff));
cd0b1709
VZ
1898}
1899
1900wxDateTime& wxDateTime::SetToPrevWeekDay(WeekDay weekday)
1901{
9a83f860 1902 wxDATETIME_CHECK( weekday != Inv_WeekDay, wxT("invalid weekday") );
cd0b1709
VZ
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
f6bcfd97 1921 return Subtract(wxDateSpan::Days(diff));
cd0b1709
VZ
1922}
1923
2f02cb89
VZ
1924bool wxDateTime::SetToWeekDay(WeekDay weekday,
1925 int n,
1926 Month month,
1927 int year)
1928{
9a83f860 1929 wxCHECK_MSG( weekday != Inv_WeekDay, false, wxT("invalid weekday") );
2f02cb89 1930
68379eaf 1931 // we don't check explicitly that -5 <= n <= 5 because we will return false
2f02cb89
VZ
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
239446b4 1957 dt += wxDateSpan::Days(diff);
2f02cb89 1958 }
239446b4 1959 else // count from the end of the month
2f02cb89
VZ
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
239446b4 1973 diff += 7*(-n - 1);
2f02cb89
VZ
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
68379eaf 1983 return true;
2f02cb89
VZ
1984 }
1985 else
1986 {
1987 // no such day in this month
68379eaf 1988 return false;
2f02cb89
VZ
1989 }
1990}
1991
1c5d27e2
VZ
1992static inline
1993wxDateTime::wxDateTime_t GetDayOfYearFromTm(const wxDateTime::Tm& tm)
1994{
907173e5 1995 return (wxDateTime::wxDateTime_t)(gs_cumulatedDays[wxDateTime::IsLeapYear(tm.year)][tm.mon] + tm.mday);
1c5d27e2
VZ
1996}
1997
239446b4
VZ
1998wxDateTime::wxDateTime_t wxDateTime::GetDayOfYear(const TimeZone& tz) const
1999{
1c5d27e2
VZ
2000 return GetDayOfYearFromTm(GetTm(tz));
2001}
239446b4 2002
1c5d27e2
VZ
2003wxDateTime::wxDateTime_t
2004wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags, const TimeZone& tz) const
239446b4 2005{
9d9b7755
VZ
2006 if ( flags == Default_First )
2007 {
2008 flags = GetCountry() == USA ? Sunday_First : Monday_First;
2009 }
239446b4 2010
1c5d27e2
VZ
2011 Tm tm(GetTm(tz));
2012 wxDateTime_t nDayInYear = GetDayOfYearFromTm(tm);
239446b4 2013
1c5d27e2
VZ
2014 int wdTarget = GetWeekDay(tz);
2015 int wdYearStart = wxDateTime(1, Jan, GetYear()).GetWeekDay();
2016 int week;
9d9b7755
VZ
2017 if ( flags == Sunday_First )
2018 {
1c5d27e2
VZ
2019 // FIXME: First week is not calculated correctly.
2020 week = (nDayInYear - wdTarget + 7) / 7;
2021 if ( wdYearStart == Wed || wdYearStart == Thu )
2022 week++;
9d9b7755 2023 }
1c5d27e2 2024 else // week starts with monday
9d9b7755 2025 {
1c5d27e2
VZ
2026 // adjust the weekdays to non-US style.
2027 wdYearStart = ConvertWeekDayToMondayBase(wdYearStart);
2028 wdTarget = ConvertWeekDayToMondayBase(wdTarget);
239446b4 2029
1c5d27e2
VZ
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 }
239446b4
VZ
2061 }
2062
907173e5 2063 return (wxDateTime::wxDateTime_t)week;
68ee7c47
VZ
2064}
2065
9d9b7755
VZ
2066wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags,
2067 const TimeZone& tz) const
68ee7c47 2068{
9d9b7755 2069 Tm tm = GetTm(tz);
404013f8
VZ
2070 const wxDateTime dateFirst = wxDateTime(1, tm.mon, tm.year);
2071 const wxDateTime::WeekDay wdFirst = dateFirst.GetWeekDay();
2072
2073 if ( flags == Default_First )
68ee7c47 2074 {
404013f8 2075 flags = GetCountry() == USA ? Sunday_First : Monday_First;
68ee7c47 2076 }
68ee7c47 2077
404013f8
VZ
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);
239446b4
VZ
2086}
2087
f0f951fa
VZ
2088wxDateTime& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday)
2089{
2090 int year = GetYear();
384223b3 2091 wxDATETIME_CHECK( (0 < yday) && (yday <= GetNumberOfDays(year)),
9a83f860 2092 wxT("invalid year day") );
f0f951fa
VZ
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
f49d731f 2100 if ( (mon == Dec) || (yday <= gs_cumulatedDays[isLeap][mon + 1]) )
f0f951fa 2101 {
42841dfc 2102 Set((wxDateTime::wxDateTime_t)(yday - gs_cumulatedDays[isLeap][mon]), mon, year);
f0f951fa
VZ
2103
2104 break;
2105 }
2106 }
2107
2108 return *this;
2109}
2110
e6ec579c
VZ
2111// ----------------------------------------------------------------------------
2112// Julian day number conversion and related stuff
2113// ----------------------------------------------------------------------------
2114
2115double wxDateTime::GetJulianDayNumber() const
2116{
d26adb9d 2117 return m_time.ToDouble() / MILLISECONDS_PER_DAY + EPOCH_JDN + 0.5;
e6ec579c
VZ
2118}
2119
2120double 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
fcc3d7cb 2126// ----------------------------------------------------------------------------
299fcbfe 2127// timezone and DST stuff
fcc3d7cb
VZ
2128// ----------------------------------------------------------------------------
2129
299fcbfe 2130int wxDateTime::IsDST(wxDateTime::Country country) const
fcc3d7cb 2131{
299fcbfe 2132 wxCHECK_MSG( country == Country_Default, -1,
9a83f860 2133 wxT("country support not implemented") );
299fcbfe
VZ
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 {
a452689b
SN
2139 struct tm tmstruct;
2140 tm *tm = wxLocaltime_r(&timet, &tmstruct);
299fcbfe 2141
9a83f860 2142 wxCHECK_MSG( tm, -1, wxT("wxLocaltime_r() failed") );
299fcbfe
VZ
2143
2144 return tm->tm_isdst;
2145 }
2146 else
2147 {
239446b4
VZ
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 }
299fcbfe 2155
239446b4 2156 return IsBetween(GetBeginDST(year, country), GetEndDST(year, country));
299fcbfe 2157 }
fcc3d7cb
VZ
2158}
2159
41acf5c0 2160wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST)
fcc3d7cb 2161{
479cd5de 2162 long secDiff = GetTimeZone() + tz.GetOffset();
fcc3d7cb 2163
41acf5c0
VZ
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) )
299fcbfe
VZ
2167 {
2168 // FIXME we assume that the DST is always shifted by 1 hour
2169 secDiff -= 3600;
2170 }
2171
d26adb9d
VZ
2172 return Add(wxTimeSpan::Seconds(secDiff));
2173}
2174
2175wxDateTime& 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
f6bcfd97 2187 return Subtract(wxTimeSpan::Seconds(secDiff));
fcc3d7cb
VZ
2188}
2189
4f6aed9c
VZ
2190// ============================================================================
2191// wxDateTimeHolidayAuthority and related classes
2192// ============================================================================
2193
2194#include "wx/arrimpl.cpp"
2195
4115960d 2196WX_DEFINE_OBJARRAY(wxDateTimeArray)
4f6aed9c
VZ
2197
2198static int wxCMPFUNC_CONV
2199wxDateTimeCompareFunc(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
2211wxHolidayAuthoritiesArray wxDateTimeHolidayAuthority::ms_authorities;
2212
2213/* static */
2214bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime& dt)
2215{
df5168c4 2216 size_t count = ms_authorities.size();
4f6aed9c
VZ
2217 for ( size_t n = 0; n < count; n++ )
2218 {
2219 if ( ms_authorities[n]->DoIsHoliday(dt) )
2220 {
68379eaf 2221 return true;
4f6aed9c
VZ
2222 }
2223 }
2224
68379eaf 2225 return false;
4f6aed9c
VZ
2226}
2227
2228/* static */
2229size_t
2230wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime& dtStart,
2231 const wxDateTime& dtEnd,
2232 wxDateTimeArray& holidays)
2233{
2234 wxDateTimeArray hol;
2235
df5168c4 2236 holidays.Clear();
4f6aed9c 2237
7a7bd38a
VZ
2238 const size_t countAuth = ms_authorities.size();
2239 for ( size_t nAuth = 0; nAuth < countAuth; nAuth++ )
4f6aed9c 2240 {
6dc6fda6 2241 ms_authorities[nAuth]->DoGetHolidaysInRange(dtStart, dtEnd, hol);
4f6aed9c
VZ
2242
2243 WX_APPEND_ARRAY(holidays, hol);
2244 }
2245
2246 holidays.Sort(wxDateTimeCompareFunc);
2247
df5168c4 2248 return holidays.size();
4f6aed9c
VZ
2249}
2250
2251/* static */
2252void wxDateTimeHolidayAuthority::ClearAllAuthorities()
2253{
2254 WX_CLEAR_ARRAY(ms_authorities);
2255}
2256
2257/* static */
2258void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority *auth)
2259{
df5168c4 2260 ms_authorities.push_back(auth);
4f6aed9c
VZ
2261}
2262
5e233068
WS
2263wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
2264{
2265 // required here for Darwin
2266}
2267
4f6aed9c
VZ
2268// ----------------------------------------------------------------------------
2269// wxDateTimeWorkDays
2270// ----------------------------------------------------------------------------
2271
2272bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime& dt) const
2273{
2274 wxDateTime::WeekDay wd = dt.GetWeekDay();
2275
2276 return (wd == wxDateTime::Sun) || (wd == wxDateTime::Sat);
2277}
2278
2279size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime& dtStart,
2280 const wxDateTime& dtEnd,
2281 wxDateTimeArray& holidays) const
2282{
2283 if ( dtStart > dtEnd )
2284 {
9a83f860 2285 wxFAIL_MSG( wxT("invalid date range in GetHolidaysInRange") );
4f6aed9c
VZ
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}
3e0b743f 2312
26364344
WS
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
2323WXDLLIMPEXP_BASE void wxNextMonth(wxDateTime::Month& m)
2324{
9a83f860 2325 wxASSERT_MSG( m < wxDateTime::Inv_Month, wxT("invalid month") );
26364344
WS
2326
2327 // no wrapping or the for loop above would never end!
2328 m = (wxDateTime::Month)(m + 1);
2329}
2330
2331WXDLLIMPEXP_BASE void wxPrevMonth(wxDateTime::Month& m)
2332{
9a83f860 2333 wxASSERT_MSG( m < wxDateTime::Inv_Month, wxT("invalid month") );
26364344
WS
2334
2335 m = m == wxDateTime::Jan ? wxDateTime::Inv_Month
2336 : (wxDateTime::Month)(m - 1);
2337}
2338
2339WXDLLIMPEXP_BASE void wxNextWDay(wxDateTime::WeekDay& wd)
2340{
9a83f860 2341 wxASSERT_MSG( wd < wxDateTime::Inv_WeekDay, wxT("invalid week day") );
26364344
WS
2342
2343 // no wrapping or the for loop above would never end!
2344 wd = (wxDateTime::WeekDay)(wd + 1);
2345}
2346
2347WXDLLIMPEXP_BASE void wxPrevWDay(wxDateTime::WeekDay& wd)
2348{
9a83f860 2349 wxASSERT_MSG( wd < wxDateTime::Inv_WeekDay, wxT("invalid week day") );
26364344
WS
2350
2351 wd = wd == wxDateTime::Sun ? wxDateTime::Inv_WeekDay
2352 : (wxDateTime::WeekDay)(wd - 1);
2353}
2354
154014d6
VZ
2355#ifdef __WXMSW__
2356
2357wxDateTime& wxDateTime::SetFromMSWSysTime(const SYSTEMTIME& st)
2358{
2359 return Set(st.wDay,
5c33522f 2360 static_cast<wxDateTime::Month>(wxDateTime::Jan + st.wMonth - 1),
154014d6 2361 st.wYear,
582c3a19 2362 st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
154014d6
VZ
2363}
2364
10acc3ef
VZ
2365wxDateTime& 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
154014d6
VZ
2373void 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
582c3a19
VZ
2381 st->wDayOfWeek = 0;
2382 st->wHour = tm.hour;
2383 st->wMinute = tm.min;
2384 st->wSecond = tm.sec;
2385 st->wMilliseconds = tm.msec;
154014d6 2386}
10acc3ef
VZ
2387
2388void 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
154014d6
VZ
2403#endif // __WXMSW__
2404
1e6feb95 2405#endif // wxUSE_DATETIME