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