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