]> git.saurik.com Git - wxWidgets.git/blob - src/common/datetime.cpp
source clean-up
[wxWidgets.git] / src / common / datetime.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: wx/datetime.h
3 // Purpose: implementation of time/date related classes
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 11.05.99
7 // RCS-ID: $Id$
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 license
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)
24 *
25 * 2. the range is thus something about 580 million years, but due to current
26 * algorithms limitations, only dates from Nov 24, 4714BC are handled
27 *
28 * 3. standard ANSI C functions are used to do time calculations whenever
29 * possible, i.e. when the date is in the range Jan 1, 1970 to 2038
30 *
31 * 4. otherwise, the calculations are done by converting the date to/from JDN
32 * first (the range limitation mentioned above comes from here: the
33 * algorithm used by Scott E. Lee's code only works for positive JDNs, more
34 * or less)
35 *
36 */
37
38 // ============================================================================
39 // declarations
40 // ============================================================================
41
42 // ----------------------------------------------------------------------------
43 // headers
44 // ----------------------------------------------------------------------------
45
46 #ifdef __GNUG__
47 #pragma implementation "datetime.h"
48 #endif
49
50 // For compilers that support precompilation, includes "wx.h".
51 #include "wx/wxprec.h"
52
53 #ifdef __BORLANDC__
54 #pragma hdrstop
55 #endif
56
57 #ifndef WX_PRECOMP
58 #include "wx/string.h"
59 #include "wx/intl.h"
60 #include "wx/log.h"
61 #endif // WX_PRECOMP
62
63 #include "wx/thread.h"
64
65 #define wxDEFINE_TIME_CONSTANTS
66
67 #include "wx/datetime.h"
68
69 // ----------------------------------------------------------------------------
70 // constants
71 // ----------------------------------------------------------------------------
72
73 // some trivial ones
74 static const int MONTHS_IN_YEAR = 12;
75
76 static const int SECONDS_IN_MINUTE = 60;
77
78 static const long SECONDS_PER_DAY = 86400l;
79
80 static const long MILLISECONDS_PER_DAY = 86400000l;
81
82 // this is the integral part of JDN of the midnight of Jan 1, 1970
83 // (i.e. JDN(Jan 1, 1970) = 2440587.5)
84 static const int EPOCH_JDN = 2440587;
85
86 // the date of JDN -0.5 (as we don't work with fractional parts, this is the
87 // reference date for us) is Nov 24, 4714BC
88 static const int JDN_0_YEAR = -4713;
89 static const int JDN_0_MONTH = wxDateTime::Nov;
90 static const int JDN_0_DAY = 24;
91
92 // the constants used for JDN calculations
93 static const int JDN_OFFSET = 32046;
94 static const int DAYS_PER_5_MONTHS = 153;
95 static const int DAYS_PER_4_YEARS = 1461;
96 static const int DAYS_PER_400_YEARS = 146097;
97
98 // ----------------------------------------------------------------------------
99 // globals
100 // ----------------------------------------------------------------------------
101
102 // a critical section is needed to protect GetTimeZone() static
103 // variable in MT case
104 #ifdef wxUSE_THREADS
105 wxCriticalSection gs_critsectTimezone;
106 #endif // wxUSE_THREADS
107
108 // ----------------------------------------------------------------------------
109 // private functions
110 // ----------------------------------------------------------------------------
111
112 // get the number of days in the given month of the given year
113 static inline
114 wxDateTime::wxDateTime_t GetNumOfDaysInMonth(int year, wxDateTime::Month month)
115 {
116 // the number of days in month in Julian/Gregorian calendar: the first line
117 // is for normal years, the second one is for the leap ones
118 static wxDateTime::wxDateTime_t daysInMonth[2][MONTHS_IN_YEAR] =
119 {
120 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
121 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
122 };
123
124 return daysInMonth[wxDateTime::IsLeapYear(year)][month];
125 }
126
127 // ensure that the timezone variable is set by calling localtime
128 static int GetTimeZone()
129 {
130 // set to TRUE when the timezone is set
131 static bool s_timezoneSet = FALSE;
132
133 wxCRIT_SECT_LOCKER(lock, gs_critsectTimezone);
134
135 if ( !s_timezoneSet )
136 {
137 // just call localtime() instead of figuring out whether this system
138 // supports tzset(), _tzset() or something else
139 time_t t;
140 (void)localtime(&t);
141
142 s_timezoneSet = TRUE;
143 }
144
145 return (int)timezone;
146 }
147
148 // return the integral part of the JDN for the midnight of the given date (to
149 // get the real JDN you need to add 0.5, this is, in fact, JDN of the
150 // noon of the previous day)
151 static long GetTruncatedJDN(wxDateTime::wxDateTime_t day,
152 wxDateTime::Month mon,
153 int year)
154 {
155 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
156
157 // check the date validity
158 wxASSERT_MSG(
159 (year > JDN_0_YEAR) ||
160 ((year == JDN_0_YEAR) && (mon > JDN_0_MONTH)) ||
161 ((year == JDN_0_YEAR) && (mon == JDN_0_MONTH) && (day >= JDN_0_DAY)),
162 _T("date out of range - can't convert to JDN")
163 );
164
165 // make the year positive to avoid problems with negative numbers division
166 year += 4800;
167
168 // months are counted from March here
169 int month;
170 if ( mon >= wxDateTime::Mar )
171 {
172 month = mon - 2;
173 }
174 else
175 {
176 month = mon + 10;
177 year--;
178 }
179
180 // now we can simply add all the contributions together
181 return ((year / 100) * DAYS_PER_400_YEARS) / 4
182 + ((year % 100) * DAYS_PER_4_YEARS) / 4
183 + (month * DAYS_PER_5_MONTHS + 2) / 5
184 + day
185 - JDN_OFFSET;
186 }
187
188 // this function is a wrapper around strftime(3)
189 static wxString CallStrftime(const wxChar *format, const tm* tm)
190 {
191 wxChar buf[1024];
192 if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) )
193 {
194 // is ti really possible that 1024 is too short?
195 wxFAIL_MSG(_T("strftime() failed"));
196 }
197
198 return wxString(buf);
199 }
200
201 // if year and/or month have invalid values, replace them with the current ones
202 static void ReplaceDefaultYearMonthWithCurrent(int *year,
203 wxDateTime::Month *month)
204 {
205 struct tm *tmNow = NULL;
206
207 if ( *year == wxDateTime::Inv_Year )
208 {
209 tmNow = wxDateTime::GetTmNow();
210
211 *year = 1900 + tmNow->tm_year;
212 }
213
214 if ( *month == wxDateTime::Inv_Month )
215 {
216 if ( !tmNow )
217 tmNow = wxDateTime::GetTmNow();
218
219 *month = (wxDateTime::Month)tmNow->tm_mon;
220 }
221 }
222
223 // ============================================================================
224 // implementation of wxDateTime
225 // ============================================================================
226
227 // ----------------------------------------------------------------------------
228 // static data
229 // ----------------------------------------------------------------------------
230
231 wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown;
232 wxDateTime wxDateTime::ms_InvDateTime;
233
234 // ----------------------------------------------------------------------------
235 // struct Tm
236 // ----------------------------------------------------------------------------
237
238 wxDateTime::Tm::Tm()
239 {
240 year = (wxDateTime_t)wxDateTime::Inv_Year;
241 mon = wxDateTime::Inv_Month;
242 mday = 0;
243 hour = min = sec = msec = 0;
244 wday = wxDateTime::Inv_WeekDay;
245 }
246
247 wxDateTime::Tm::Tm(const struct tm& tm)
248 {
249 msec = 0;
250 sec = tm.tm_sec;
251 min = tm.tm_min;
252 hour = tm.tm_hour;
253 mday = tm.tm_mday;
254 mon = (wxDateTime::Month)tm.tm_mon;
255 year = 1900 + tm.tm_year;
256 wday = tm.tm_wday;
257 yday = tm.tm_yday;
258 }
259
260 bool wxDateTime::Tm::IsValid() const
261 {
262 // we allow for the leap seconds, although we don't use them (yet)
263 return (year != wxDateTime::Inv_Year) && (mon != wxDateTime::Inv_Month) &&
264 (mday < GetNumOfDaysInMonth(year, mon)) &&
265 (hour < 24) && (min < 60) && (sec < 62) && (msec < 1000);
266 }
267
268 void wxDateTime::Tm::ComputeWeekDay()
269 {
270 wxFAIL_MSG(_T("TODO"));
271 }
272
273 void wxDateTime::Tm::AddMonths(int monDiff)
274 {
275 // normalize the months field
276 while ( monDiff < -mon )
277 {
278 year--;
279
280 monDiff += MONTHS_IN_YEAR;
281 }
282
283 while ( monDiff + mon > MONTHS_IN_YEAR )
284 {
285 year++;
286 }
287
288 mon = (wxDateTime::Month)(mon + monDiff);
289
290 wxASSERT_MSG( mon >= 0 && mon < MONTHS_IN_YEAR, _T("logic error") );
291 }
292
293 void wxDateTime::Tm::AddDays(int dayDiff)
294 {
295 // normalize the days field
296 mday += dayDiff;
297 while ( mday < 1 )
298 {
299 AddMonths(-1);
300
301 mday += GetNumOfDaysInMonth(year, mon);
302 }
303
304 while ( mday > GetNumOfDaysInMonth(year, mon) )
305 {
306 mday -= GetNumOfDaysInMonth(year, mon);
307
308 AddMonths(1);
309 }
310
311 wxASSERT_MSG( mday > 0 && mday <= GetNumOfDaysInMonth(year, mon),
312 _T("logic error") );
313 }
314
315 // ----------------------------------------------------------------------------
316 // class TimeZone
317 // ----------------------------------------------------------------------------
318
319 wxDateTime::TimeZone::TimeZone(wxDateTime::TZ tz)
320 {
321 switch ( tz )
322 {
323 case wxDateTime::Local:
324 // leave offset to be 0
325 break;
326
327 case wxDateTime::GMT_12:
328 case wxDateTime::GMT_11:
329 case wxDateTime::GMT_10:
330 case wxDateTime::GMT_9:
331 case wxDateTime::GMT_8:
332 case wxDateTime::GMT_7:
333 case wxDateTime::GMT_6:
334 case wxDateTime::GMT_5:
335 case wxDateTime::GMT_4:
336 case wxDateTime::GMT_3:
337 case wxDateTime::GMT_2:
338 case wxDateTime::GMT_1:
339 m_offset = -60*(wxDateTime::GMT0 - tz);
340 break;
341
342 case wxDateTime::GMT0:
343 case wxDateTime::GMT1:
344 case wxDateTime::GMT2:
345 case wxDateTime::GMT3:
346 case wxDateTime::GMT4:
347 case wxDateTime::GMT5:
348 case wxDateTime::GMT6:
349 case wxDateTime::GMT7:
350 case wxDateTime::GMT8:
351 case wxDateTime::GMT9:
352 case wxDateTime::GMT10:
353 case wxDateTime::GMT11:
354 case wxDateTime::GMT12:
355 m_offset = 60*(tz - wxDateTime::GMT0);
356 break;
357
358 case wxDateTime::A_CST:
359 // Central Standard Time in use in Australia = UTC + 9.5
360 m_offset = 9*60 + 30;
361 break;
362
363 default:
364 wxFAIL_MSG( _T("unknown time zone") );
365 }
366 }
367
368 // ----------------------------------------------------------------------------
369 // static functions
370 // ----------------------------------------------------------------------------
371
372 /* static */
373 bool wxDateTime::IsLeapYear(int year, wxDateTime::Calendar cal)
374 {
375 if ( year == Inv_Year )
376 year = GetCurrentYear();
377
378 if ( cal == Gregorian )
379 {
380 // in Gregorian calendar leap years are those divisible by 4 except
381 // those divisible by 100 unless they're also divisible by 400
382 // (in some countries, like Russia and Greece, additional corrections
383 // exist, but they won't manifest themselves until 2700)
384 return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
385 }
386 else if ( cal == Julian )
387 {
388 // in Julian calendar the rule is simpler
389 return year % 4 == 0;
390 }
391 else
392 {
393 wxFAIL_MSG(_T("unknown calendar"));
394
395 return FALSE;
396 }
397 }
398
399 /* static */
400 int wxDateTime::GetCentury(int year)
401 {
402 return year > 0 ? year / 100 : year / 100 - 1;
403 }
404
405 /* static */
406 void wxDateTime::SetCountry(wxDateTime::Country country)
407 {
408 ms_country = country;
409 }
410
411 /* static */
412 int wxDateTime::ConvertYearToBC(int year)
413 {
414 // year 0 is BC 1
415 return year > 0 ? year : year - 1;
416 }
417
418 /* static */
419 int wxDateTime::GetCurrentYear(wxDateTime::Calendar cal)
420 {
421 switch ( cal )
422 {
423 case Gregorian:
424 return Now().GetYear();
425
426 case Julian:
427 wxFAIL_MSG(_T("TODO"));
428 break;
429
430 default:
431 wxFAIL_MSG(_T("unsupported calendar"));
432 break;
433 }
434
435 return Inv_Year;
436 }
437
438 /* static */
439 wxDateTime::Month wxDateTime::GetCurrentMonth(wxDateTime::Calendar cal)
440 {
441 switch ( cal )
442 {
443 case Gregorian:
444 return Now().GetMonth();
445 break;
446
447 case Julian:
448 wxFAIL_MSG(_T("TODO"));
449 break;
450
451 default:
452 wxFAIL_MSG(_T("unsupported calendar"));
453 break;
454 }
455
456 return Inv_Month;
457 }
458
459 /* static */
460 wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(int year, Calendar cal)
461 {
462 if ( year == Inv_Year )
463 {
464 // take the current year if none given
465 year = GetCurrentYear();
466 }
467
468 switch ( cal )
469 {
470 case Gregorian:
471 case Julian:
472 return IsLeapYear(year) ? 366 : 365;
473 break;
474
475 default:
476 wxFAIL_MSG(_T("unsupported calendar"));
477 break;
478 }
479
480 return 0;
481 }
482
483 /* static */
484 wxDateTime::wxDateTime_t wxDateTime::GetNumberOfDays(wxDateTime::Month month,
485 int year,
486 wxDateTime::Calendar cal)
487 {
488 wxCHECK_MSG( month < MONTHS_IN_YEAR, 0, _T("invalid month") );
489
490 if ( cal == Gregorian || cal == Julian )
491 {
492 if ( year == Inv_Year )
493 {
494 // take the current year if none given
495 year = GetCurrentYear();
496 }
497
498 return GetNumOfDaysInMonth(year, month);
499 }
500 else
501 {
502 wxFAIL_MSG(_T("unsupported calendar"));
503
504 return 0;
505 }
506 }
507
508 /* static */
509 wxString wxDateTime::GetMonthName(wxDateTime::Month month, bool abbr)
510 {
511 wxCHECK_MSG( month != Inv_Month, _T(""), _T("invalid month") );
512
513 tm tm = { 0, 0, 0, 1, month, 76 }; // any year will do
514
515 return CallStrftime(abbr ? _T("%b") : _T("%B"), &tm);
516 }
517
518 /* static */
519 wxString wxDateTime::GetWeekDayName(wxDateTime::WeekDay wday, bool abbr)
520 {
521 wxCHECK_MSG( wday != Inv_WeekDay, _T(""), _T("invalid weekday") );
522
523 // take some arbitrary Sunday
524 tm tm = { 0, 0, 0, 28, Nov, 99 };
525
526 // and offset it by the number of days needed to get the correct wday
527 tm.tm_mday += wday;
528
529 return CallStrftime(abbr ? _T("%a") : _T("%A"), &tm);
530 }
531
532 // ----------------------------------------------------------------------------
533 // constructors and assignment operators
534 // ----------------------------------------------------------------------------
535
536 wxDateTime& wxDateTime::Set(const struct tm& tm1)
537 {
538 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
539
540 tm tm2(tm1);
541
542 // we want the time in GMT, mktime() takes the local time, so use timegm()
543 // if it's available
544 #ifdef HAVE_TIMEGM
545 time_t timet = timegm(&tm2);
546 #else // !HAVE_TIMEGM
547 // FIXME this almost surely doesn't work
548 tm2.tm_sec -= GetTimeZone();
549
550 time_t timet = mktime(&tm2);
551
552 if ( tm2.tm_isdst )
553 {
554 tm2.tm_hour += 1;
555
556 timet = mktime(&tm2);
557 }
558 #endif // HAVE_TIMEGM/!HAVE_TIMEGM
559
560 if ( timet == (time_t)(-1) )
561 {
562 wxFAIL_MSG(_T("Invalid time"));
563
564 return ms_InvDateTime;
565 }
566 else
567 {
568 return Set(timet);
569 }
570 }
571
572 wxDateTime& wxDateTime::Set(wxDateTime_t hour,
573 wxDateTime_t minute,
574 wxDateTime_t second,
575 wxDateTime_t millisec)
576 {
577 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
578
579 // we allow seconds to be 61 to account for the leap seconds, even if we
580 // don't use them really
581 wxCHECK_MSG( hour < 24 && second < 62 && minute < 60 && millisec < 1000,
582 ms_InvDateTime,
583 _T("Invalid time in wxDateTime::Set()") );
584
585 // get the current date from system
586 time_t timet = GetTimeNow();
587 struct tm *tm = gmtime(&timet);
588
589 // adjust the time
590 tm->tm_hour = hour;
591 tm->tm_min = minute;
592 tm->tm_sec = second;
593
594 (void)Set(*tm);
595
596 // and finally adjust milliseconds
597 return SetMillisecond(millisec);
598 }
599
600 wxDateTime& wxDateTime::Set(wxDateTime_t day,
601 Month month,
602 int year,
603 wxDateTime_t hour,
604 wxDateTime_t minute,
605 wxDateTime_t second,
606 wxDateTime_t millisec)
607 {
608 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
609
610 wxCHECK_MSG( hour < 24 && second < 62 && minute < 60 && millisec < 1000,
611 ms_InvDateTime,
612 _T("Invalid time in wxDateTime::Set()") );
613
614 ReplaceDefaultYearMonthWithCurrent(&year, &month);
615
616 wxCHECK_MSG( (0 < day) && (day <= GetNumberOfDays(month, year)),
617 ms_InvDateTime,
618 _T("Invalid date in wxDateTime::Set()") );
619
620 // the range of time_t type (inclusive)
621 static const int yearMinInRange = 1970;
622 static const int yearMaxInRange = 2037;
623
624 // test only the year instead of testing for the exact end of the Unix
625 // time_t range - it doesn't bring anything to do more precise checks
626 if ( year >= yearMinInRange && year <= yearMaxInRange )
627 {
628 // use the standard library version if the date is in range - this is
629 // probably more efficient than our code
630 struct tm tm;
631 tm.tm_year = year - 1900;
632 tm.tm_mon = month;
633 tm.tm_mday = day;
634 tm.tm_hour = hour;
635 tm.tm_min = minute;
636 tm.tm_sec = second;
637 tm.tm_isdst = -1; // mktime() will guess it (wrongly, probably)
638
639 (void)Set(tm);
640
641 // and finally adjust milliseconds
642 return SetMillisecond(millisec);
643 }
644 else
645 {
646 // do time calculations ourselves: we want to calculate the number of
647 // milliseconds between the given date and the epoch
648
649 // get the JDN for the midnight of this day
650 m_time = GetTruncatedJDN(day, month, year);
651 m_time -= EPOCH_JDN;
652 m_time *= SECONDS_PER_DAY * TIME_T_FACTOR;
653
654 Add(wxTimeSpan(hour, minute, second, millisec));
655 }
656
657 return *this;
658 }
659
660 wxDateTime& wxDateTime::Set(double jdn)
661 {
662 // so that m_time will be 0 for the midnight of Jan 1, 1970 which is jdn
663 // EPOCH_JDN + 0.5
664 jdn -= EPOCH_JDN + 0.5;
665
666 m_time = jdn;
667 m_time *= MILLISECONDS_PER_DAY;
668
669 return *this;
670 }
671
672 // ----------------------------------------------------------------------------
673 // time_t <-> broken down time conversions
674 // ----------------------------------------------------------------------------
675
676 wxDateTime::Tm wxDateTime::GetTm() const
677 {
678 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
679
680 time_t time = GetTicks();
681 if ( time != (time_t)-1 )
682 {
683 // use C RTL functions
684 tm *tm = gmtime(&time);
685
686 // should never happen
687 wxCHECK_MSG( tm, Tm(), _T("gmtime() failed") );
688
689 return Tm(*tm);
690 }
691 else
692 {
693 // remember the time and do the calculations with the date only - this
694 // eliminates rounding errors of the floating point arithmetics
695
696 wxLongLong timeMidnight = m_time;
697
698 long timeOnly = (m_time % MILLISECONDS_PER_DAY).ToLong();
699 if ( timeOnly < 0 )
700 {
701 timeOnly = MILLISECONDS_PER_DAY - timeOnly;
702 }
703
704 timeMidnight -= timeOnly;
705
706 // calculate the Gregorian date from JDN for the midnight of our date:
707 // this will yield day, month (in 1..12 range) and year
708
709 // actually, this is the JDN for the noon of the previous day
710 long jdn = (timeMidnight / MILLISECONDS_PER_DAY).ToLong() + EPOCH_JDN;
711
712 // CREDIT: code below is by Scott E. Lee (but bugs are mine)
713
714 wxASSERT_MSG( jdn > -2, _T("JDN out of range") );
715
716 // calculate the century
717 int temp = (jdn + JDN_OFFSET) * 4 - 1;
718 int century = temp / DAYS_PER_400_YEARS;
719
720 // then the year and day of year (1 <= dayOfYear <= 366)
721 temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
722 int year = (century * 100) + (temp / DAYS_PER_4_YEARS);
723 int dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
724
725 // and finally the month and day of the month
726 temp = dayOfYear * 5 - 3;
727 int month = temp / DAYS_PER_5_MONTHS;
728 int day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
729
730 // month is counted from March - convert to normal
731 if ( month < 10 )
732 {
733 month += 3;
734 }
735 else
736 {
737 year += 1;
738 month -= 9;
739 }
740
741 // year is offset by 4800
742 year -= 4800;
743
744 // check that the algorithm gave us something reasonable
745 wxASSERT_MSG( (0 < month) && (month <= 12), _T("invalid month") );
746 wxASSERT_MSG( (1 <= day) && (day < 32), _T("invalid day") );
747 wxASSERT_MSG( (INT_MIN <= year) && (year <= INT_MAX),
748 _T("year range overflow") );
749
750 // construct Tm from these values
751 Tm tm;
752 tm.year = (int)year;
753 tm.mon = (Month)(month - 1); // algorithm yields 1 for January, not 0
754 tm.mday = (wxDateTime_t)day;
755 tm.msec = timeOnly % 1000;
756 timeOnly -= tm.msec;
757 timeOnly /= 1000; // now we have time in seconds
758
759 tm.sec = timeOnly % 60;
760 timeOnly -= tm.sec;
761 timeOnly /= 60; // now we have time in minutes
762
763 tm.min = timeOnly % 60;
764 timeOnly -= tm.min;
765
766 tm.hour = timeOnly / 60;
767
768 return tm;
769 }
770 }
771
772 wxDateTime& wxDateTime::SetYear(int year)
773 {
774 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
775
776 Tm tm(GetTm());
777 tm.year = year;
778 Set(tm);
779
780 return *this;
781 }
782
783 wxDateTime& wxDateTime::SetMonth(Month month)
784 {
785 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
786
787 Tm tm(GetTm());
788 tm.mon = month;
789 Set(tm);
790
791 return *this;
792 }
793
794 wxDateTime& wxDateTime::SetDay(wxDateTime_t mday)
795 {
796 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
797
798 Tm tm(GetTm());
799 tm.mday = mday;
800 Set(tm);
801
802 return *this;
803 }
804
805 wxDateTime& wxDateTime::SetHour(wxDateTime_t hour)
806 {
807 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
808
809 Tm tm(GetTm());
810 tm.hour = hour;
811 Set(tm);
812
813 return *this;
814 }
815
816 wxDateTime& wxDateTime::SetMinute(wxDateTime_t min)
817 {
818 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
819
820 Tm tm(GetTm());
821 tm.min = min;
822 Set(tm);
823
824 return *this;
825 }
826
827 wxDateTime& wxDateTime::SetSecond(wxDateTime_t sec)
828 {
829 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
830
831 Tm tm(GetTm());
832 tm.sec = sec;
833 Set(tm);
834
835 return *this;
836 }
837
838 wxDateTime& wxDateTime::SetMillisecond(wxDateTime_t millisecond)
839 {
840 wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
841
842 // we don't need to use GetTm() for this one
843 m_time -= m_time % 1000l;
844 m_time += millisecond;
845
846 return *this;
847 }
848
849 // ----------------------------------------------------------------------------
850 // wxDateTime arithmetics
851 // ----------------------------------------------------------------------------
852
853 wxDateTime& wxDateTime::Add(const wxDateSpan& diff)
854 {
855 Tm tm(GetTm());
856
857 tm.year += diff.GetYears();
858 tm.AddMonths(diff.GetMonths());
859 tm.AddDays(diff.GetTotalDays());
860
861 Set(tm);
862
863 return *this;
864 }
865
866 // ----------------------------------------------------------------------------
867 // Weekday and monthday stuff
868 // ----------------------------------------------------------------------------
869
870 wxDateTime& wxDateTime::SetToLastMonthDay(Month month,
871 int year)
872 {
873 // take the current month/year if none specified
874 ReplaceDefaultYearMonthWithCurrent(&year, &month);
875
876 return Set(GetNumOfDaysInMonth(year, month), month, year);
877 }
878
879 bool wxDateTime::SetToWeekDay(WeekDay weekday,
880 int n,
881 Month month,
882 int year)
883 {
884 wxCHECK_MSG( weekday != Inv_WeekDay, FALSE, _T("invalid weekday") );
885
886 // we don't check explicitly that -5 <= n <= 5 because we will return FALSE
887 // anyhow in such case - but may be should still give an assert for it?
888
889 // take the current month/year if none specified
890 ReplaceDefaultYearMonthWithCurrent(&year, &month);
891
892 wxDateTime dt;
893
894 // TODO this probably could be optimised somehow...
895
896 if ( n > 0 )
897 {
898 // get the first day of the month
899 dt.Set(1, month, year);
900
901 // get its wday
902 WeekDay wdayFirst = dt.GetWeekDay();
903
904 // go to the first weekday of the month
905 int diff = weekday - wdayFirst;
906 if ( diff < 0 )
907 diff += 7;
908
909 // add advance n-1 weeks more
910 diff += 7*(n - 1);
911
912 dt -= wxDateSpan::Days(diff);
913 }
914 else
915 {
916 // get the last day of the month
917 dt.SetToLastMonthDay(month, year);
918
919 // get its wday
920 WeekDay wdayLast = dt.GetWeekDay();
921
922 // go to the last weekday of the month
923 int diff = wdayLast - weekday;
924 if ( diff < 0 )
925 diff += 7;
926
927 // and rewind n-1 weeks from there
928 diff += 7*(n - 1);
929
930 dt -= wxDateSpan::Days(diff);
931 }
932
933 // check that it is still in the same month
934 if ( dt.GetMonth() == month )
935 {
936 *this = dt;
937
938 return TRUE;
939 }
940 else
941 {
942 // no such day in this month
943 return FALSE;
944 }
945 }
946
947 // ----------------------------------------------------------------------------
948 // Julian day number conversion and related stuff
949 // ----------------------------------------------------------------------------
950
951 double wxDateTime::GetJulianDayNumber() const
952 {
953 Tm tm(GetTm());
954
955 double result = GetTruncatedJDN(tm.mday, tm.mon, tm.year);
956
957 // add the part GetTruncatedJDN() neglected
958 result += 0.5;
959
960 // and now add the time: 86400 sec = 1 JDN
961 return result + ((double)(60*(60*tm.hour + tm.min) + tm.sec)) / 86400;
962 }
963
964 double wxDateTime::GetRataDie() const
965 {
966 // March 1 of the year 0 is Rata Die day -306 and JDN 1721119.5
967 return GetJulianDayNumber() - 1721119.5 - 306;
968 }
969
970 // ----------------------------------------------------------------------------
971 // timezone stuff
972 // ----------------------------------------------------------------------------
973
974 wxDateTime& wxDateTime::MakeUTC()
975 {
976 return Add(wxTimeSpan::Seconds(GetTimeZone()));
977 }
978
979 wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz)
980 {
981 int minDiff = GetTimeZone() / SECONDS_IN_MINUTE + tz.GetOffset();
982 return Add(wxTimeSpan::Minutes(minDiff));
983 }
984
985 wxDateTime& wxDateTime::MakeLocalTime(const TimeZone& tz)
986 {
987 int minDiff = GetTimeZone() / SECONDS_IN_MINUTE + tz.GetOffset();
988 return Substract(wxTimeSpan::Minutes(minDiff));
989 }
990
991 // ----------------------------------------------------------------------------
992 // wxDateTime to/from text representations
993 // ----------------------------------------------------------------------------
994
995 wxString wxDateTime::Format(const wxChar *format) const
996 {
997 wxCHECK_MSG( format, _T(""), _T("NULL format in wxDateTime::Format") );
998
999 time_t time = GetTicks();
1000 if ( time != (time_t)-1 )
1001 {
1002 // use strftime()
1003 tm *tm = gmtime(&time);
1004
1005 // should never happen
1006 wxCHECK_MSG( tm, _T(""), _T("gmtime() failed") );
1007
1008 return CallStrftime(format, tm);
1009 }
1010 else
1011 {
1012 // use a hack and still use strftime(): make a copy of the format and
1013 // replace all occurences of YEAR in it with some unique string not
1014 // appearing anywhere else in it, then use strftime() to format the
1015 // date in year YEAR and then replace YEAR back by the real year and
1016 // the unique replacement string back with YEAR where YEAR is any year
1017 // in the range supported by strftime() (1970 - 2037) which is equal to
1018 // the real year modulo 28 (so the week days coincide for them)
1019
1020 // find the YEAR
1021 int yearReal = GetYear();
1022 int year = 1970 + yearReal % 28;
1023
1024 wxString strYear;
1025 strYear.Printf(_T("%d"), year);
1026
1027 // find a string not occuring in format (this is surely not optimal way
1028 // of doing it... improvements welcome!)
1029 wxString fmt = format;
1030 wxString replacement = (wxChar)-1;
1031 while ( fmt.Find(replacement) != wxNOT_FOUND )
1032 {
1033 replacement << (wxChar)-1;
1034 }
1035
1036 // replace all occurences of year with it
1037 bool wasReplaced = fmt.Replace(strYear, replacement) > 0;
1038
1039 // use strftime() to format the same date but in supported year
1040 wxDateTime dt(*this);
1041 dt.SetYear(year);
1042 wxString str = dt.Format(format);
1043
1044 // now replace the occurence of 1999 with the real year
1045 wxString strYearReal;
1046 strYearReal.Printf(_T("%d"), yearReal);
1047 str.Replace(strYear, strYearReal);
1048
1049 // and replace back all occurences of replacement string
1050 if ( wasReplaced )
1051 str.Replace(replacement, strYear);
1052
1053 return str;
1054 }
1055 }
1056
1057 // ============================================================================
1058 // wxTimeSpan
1059 // ============================================================================
1060
1061 // not all strftime(3) format specifiers make sense here because, for example,
1062 // a time span doesn't have a year nor a timezone
1063 //
1064 // Here are the ones which are supported (all of them are supported by strftime
1065 // as well):
1066 // %H hour in 24 hour format
1067 // %M minute (00 - 59)
1068 // %S second (00 - 59)
1069 // %% percent sign
1070 //
1071 // Also, for MFC CTimeSpan compatibility, we support
1072 // %D number of days
1073 //
1074 // And, to be better than MFC :-), we also have
1075 // %E number of wEeks
1076 // %l milliseconds (000 - 999)
1077 wxString wxTimeSpan::Format(const wxChar *format) const
1078 {
1079 wxCHECK_MSG( format, _T(""), _T("NULL format in wxTimeSpan::Format") );
1080
1081 wxString str;
1082 str.Alloc(strlen(format));
1083
1084 for ( const wxChar *pch = format; pch; pch++ )
1085 {
1086 wxChar ch = *pch;
1087
1088 if ( ch == '%' )
1089 {
1090 wxString tmp;
1091
1092 ch = *pch++;
1093 switch ( ch )
1094 {
1095 default:
1096 wxFAIL_MSG( _T("invalid format character") );
1097 // fall through
1098
1099 case '%':
1100 // will get to str << ch below
1101 break;
1102
1103 case 'D':
1104 tmp.Printf(_T("%d"), GetDays());
1105 break;
1106
1107 case 'E':
1108 tmp.Printf(_T("%d"), GetWeeks());
1109 break;
1110
1111 case 'H':
1112 tmp.Printf(_T("%02d"), GetHours());
1113 break;
1114
1115 case 'l':
1116 tmp.Printf(_T("%03d"), GetMilliseconds());
1117 break;
1118
1119 case 'M':
1120 tmp.Printf(_T("%02d"), GetMinutes());
1121 break;
1122
1123 case 'S':
1124 tmp.Printf(_T("%02d"), GetSeconds());
1125 break;
1126 }
1127
1128 if ( !!tmp )
1129 {
1130 str += tmp;
1131
1132 // skip str += ch below
1133 continue;
1134 }
1135 }
1136
1137 str += ch;
1138 }
1139
1140 return str;
1141 }