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