#include "wx/thread.h"
#include "wx/tokenzr.h"
+#include "wx/module.h"
#define wxDEFINE_TIME_CONSTANTS // before including datetime.h
+#include <ctype.h>
+
#include "wx/datetime.h"
// ----------------------------------------------------------------------------
#ifndef WX_TIMEZONE
#if defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
#define WX_TIMEZONE _timezone
+ #elif defined(__MWERKS__)
+ long wxmw_timezone = 28800;
+ #define WX_TIMEZONE wxmw_timezone ;
#else // unknown platform - try timezone
#define WX_TIMEZONE timezone
#endif
#endif // !WX_TIMEZONE
+// ----------------------------------------------------------------------------
+// private classes
+// ----------------------------------------------------------------------------
+
+class wxDateTimeHolidaysModule : public wxModule
+{
+public:
+ virtual bool OnInit()
+ {
+ wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays);
+
+ return TRUE;
+ }
+
+ virtual void OnExit()
+ {
+ wxDateTimeHolidayAuthority::ClearAllAuthorities();
+ wxDateTimeHolidayAuthority::ms_authorities.Clear();
+ }
+
+private:
+ DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule)
+};
+
+IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule, wxModule)
+
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
};
// ----------------------------------------------------------------------------
-// globals
+// global data
+// ----------------------------------------------------------------------------
+
+static wxDateTime gs_dtDefault;
+
+wxDateTime& wxDefaultDateTime = gs_dtDefault;
+
+wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown;
+
+// ----------------------------------------------------------------------------
+// private globals
// ----------------------------------------------------------------------------
// a critical section is needed to protect GetTimeZone() static
// variable in MT case
#if wxUSE_THREADS
- wxCriticalSection gs_critsectTimezone;
+ static wxCriticalSection gs_critsectTimezone;
#endif // wxUSE_THREADS
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
+// debugger helper: shows what the date really is
+#ifdef __WXDEBUG__
+extern const wxChar *wxDumpDate(const wxDateTime* dt)
+{
+ static wxChar buf[128];
+
+ wxStrcpy(buf, dt->Format(_T("%Y-%m-%d (%a) %H:%M:%S")));
+
+ return buf;
+}
+#endif // Debug
+
// get the number of days in the given month of the given year
static inline
wxDateTime::wxDateTime_t GetNumOfDaysInMonth(int year, wxDateTime::Month month)
// implementation of wxDateTime
// ============================================================================
-// ----------------------------------------------------------------------------
-// static data
-// ----------------------------------------------------------------------------
-
-wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown;
-wxDateTime wxDateTime::ms_InvDateTime;
-
// ----------------------------------------------------------------------------
// struct Tm
// ----------------------------------------------------------------------------
monDiff += MONTHS_IN_YEAR;
}
- while ( monDiff + mon > MONTHS_IN_YEAR )
+ while ( monDiff + mon >= MONTHS_IN_YEAR )
{
year++;
mon = (wxDateTime::Month)(mon + monDiff);
wxASSERT_MSG( mon >= 0 && mon < MONTHS_IN_YEAR, _T("logic error") );
+
+ // NB: we don't check here that the resulting date is valid, this function
+ // is private and the caller must check it if needed
}
void wxDateTime::Tm::AddDays(int dayDiff)
{
// normalize the days field
- mday += dayDiff;
- while ( mday < 1 )
+ while ( dayDiff + mday < 1 )
{
AddMonths(-1);
- mday += GetNumOfDaysInMonth(year, mon);
+ dayDiff += GetNumOfDaysInMonth(year, mon);
}
+ mday += dayDiff;
while ( mday > GetNumOfDaysInMonth(year, mon) )
{
mday -= GetNumOfDaysInMonth(year, mon);
{
case Gregorian:
return Now().GetMonth();
- break;
case Julian:
wxFAIL_MSG(_T("TODO"));
case Gregorian:
case Julian:
return IsLeapYear(year) ? 366 : 365;
- break;
default:
wxFAIL_MSG(_T("unsupported calendar"));
if ( !IsDSTApplicable(year, country) )
{
- return ms_InvDateTime;
+ return wxInvalidDateTime;
}
wxDateTime dt;
dt += wxTimeSpan::Hours(1);
- dt.MakeGMT();
+ // disable DST tests because it could result in an infinite recursion!
+ dt.MakeGMT(TRUE);
}
else switch ( country )
{
if ( !IsDSTApplicable(year, country) )
{
- return ms_InvDateTime;
+ return wxInvalidDateTime;
}
wxDateTime dt;
dt += wxTimeSpan::Hours(1);
- dt.MakeGMT();
+ // disable DST tests because it could result in an infinite recursion!
+ dt.MakeGMT(TRUE);
}
else switch ( country )
{
wxFAIL_MSG( _T("mktime() failed") );
- return ms_InvDateTime;
+ return wxInvalidDateTime;
}
else
{
// we allow seconds to be 61 to account for the leap seconds, even if we
// don't use them really
wxCHECK_MSG( hour < 24 && second < 62 && minute < 60 && millisec < 1000,
- ms_InvDateTime,
+ wxInvalidDateTime,
_T("Invalid time in wxDateTime::Set()") );
// get the current date from system
- time_t timet = GetTimeNow();
- struct tm *tm = localtime(&timet);
+ struct tm *tm = GetTmNow();
- wxCHECK_MSG( tm, ms_InvDateTime, _T("localtime() failed") );
+ wxCHECK_MSG( tm, wxInvalidDateTime, _T("localtime() failed") );
// adjust the time
tm->tm_hour = hour;
wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
wxCHECK_MSG( hour < 24 && second < 62 && minute < 60 && millisec < 1000,
- ms_InvDateTime,
+ wxInvalidDateTime,
_T("Invalid time in wxDateTime::Set()") );
ReplaceDefaultYearMonthWithCurrent(&year, &month);
wxCHECK_MSG( (0 < day) && (day <= GetNumberOfDays(month, year)),
- ms_InvDateTime,
+ wxInvalidDateTime,
_T("Invalid date in wxDateTime::Set()") );
// the range of time_t type (inclusive)
jdn *= MILLISECONDS_PER_DAY;
- m_time = jdn;
+ m_time.Assign(jdn);
+
+ return *this;
+}
+
+wxDateTime& wxDateTime::ResetTime()
+{
+ Tm tm = GetTm();
+
+ if ( tm.hour || tm.min || tm.sec || tm.msec )
+ {
+ tm.msec =
+ tm.sec =
+ tm.min =
+ tm.hour = 0;
+
+ Set(tm);
+ }
return *this;
}
wxDateTime::Tm wxDateTime::GetTm(const TimeZone& tz) const
{
-#ifdef __VMS__
- int time2;
-#endif
wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
time_t time = GetTicks();
}
else
{
- time += tz.GetOffset();
-#ifdef __VMS__ /* time is unsigned so VMS gives a warning on the original */
- time2 = (int) time;
- if ( time2 >= 0 )
+ time += (time_t)tz.GetOffset();
+#ifdef __VMS__ // time is unsigned so avoid warning
+ int time2 = (int) time;
+ if ( time2 >= 0 )
#else
- if ( time >= 0 )
+ if ( time >= 0 )
#endif
{
tm = gmtime(&time);
wxASSERT_MSG( jdn > -2, _T("JDN out of range") );
// calculate the century
- int temp = (jdn + JDN_OFFSET) * 4 - 1;
- int century = temp / DAYS_PER_400_YEARS;
+ long temp = (jdn + JDN_OFFSET) * 4 - 1;
+ long century = temp / DAYS_PER_400_YEARS;
// then the year and day of year (1 <= dayOfYear <= 366)
temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
- int year = (century * 100) + (temp / DAYS_PER_4_YEARS);
- int dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
+ long year = (century * 100) + (temp / DAYS_PER_4_YEARS);
+ long dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
// and finally the month and day of the month
temp = dayOfYear * 5 - 3;
- int month = temp / DAYS_PER_5_MONTHS;
- int day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
+ long month = temp / DAYS_PER_5_MONTHS;
+ long day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
// month is counted from March - convert to normal
if ( month < 10 )
tm.year = (int)year;
tm.mon = (Month)(month - 1); // algorithm yields 1 for January, not 0
tm.mday = (wxDateTime_t)day;
- tm.msec = timeOnly % 1000;
+ tm.msec = (wxDateTime_t)(timeOnly % 1000);
timeOnly -= tm.msec;
timeOnly /= 1000; // now we have time in seconds
- tm.sec = timeOnly % 60;
+ tm.sec = (wxDateTime_t)(timeOnly % 60);
timeOnly -= tm.sec;
timeOnly /= 60; // now we have time in minutes
- tm.min = timeOnly % 60;
+ tm.min = (wxDateTime_t)(timeOnly % 60);
timeOnly -= tm.min;
- tm.hour = timeOnly / 60;
+ tm.hour = (wxDateTime_t)(timeOnly / 60);
return tm;
}
tm.year += diff.GetYears();
tm.AddMonths(diff.GetMonths());
+
+ // check that the resulting date is valid
+ if ( tm.mday > GetNumOfDaysInMonth(tm.year, tm.mon) )
+ {
+ // We suppose that when adding one month to Jan 31 we want to get Feb
+ // 28 (or 29), i.e. adding a month to the last day of the month should
+ // give the last day of the next month which is quite logical.
+ //
+ // Unfortunately, there is no logic way to understand what should
+ // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)?
+ // We make it Feb 28 (last day too), but it is highly questionable.
+ tm.mday = GetNumOfDaysInMonth(tm.year, tm.mon);
+ }
+
tm.AddDays(diff.GetTotalDays());
Set(tm);
+ wxASSERT_MSG( IsSameTime(tm),
+ _T("Add(wxDateSpan) shouldn't modify time") );
+
return *this;
}
// Weekday and monthday stuff
// ----------------------------------------------------------------------------
+bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek, WeekDay weekday)
+{
+ int year = GetYear();
+
+ // Jan 4 always lies in the 1st week of the year
+ Set(4, Jan, year);
+ SetToWeekDayInSameWeek(weekday) += wxDateSpan::Weeks(numWeek);
+
+ if ( GetYear() != year )
+ {
+ // oops... numWeek was too big
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
wxDateTime& wxDateTime::SetToLastMonthDay(Month month,
int year)
{
// take the current month/year if none specified
- ReplaceDefaultYearMonthWithCurrent(&year, &month);
+ if ( year == Inv_Year )
+ year = GetYear();
+ if ( month == Inv_Month )
+ month = GetMonth();
return Set(GetNumOfDaysInMonth(year, month), month, year);
}
wxDateTime& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday)
{
- wxCHECK_MSG( weekday != Inv_WeekDay, ms_InvDateTime, _T("invalid weekday") );
+ wxCHECK_MSG( weekday != Inv_WeekDay, wxInvalidDateTime, _T("invalid weekday") );
WeekDay wdayThis = GetWeekDay();
if ( weekday == wdayThis )
}
else if ( weekday < wdayThis )
{
- return Substract(wxTimeSpan::Days(wdayThis - weekday));
+ return Substract(wxDateSpan::Days(wdayThis - weekday));
}
else // weekday > wdayThis
{
- return Add(wxTimeSpan::Days(weekday - wdayThis));
+ return Add(wxDateSpan::Days(weekday - wdayThis));
}
}
wxDateTime& wxDateTime::SetToNextWeekDay(WeekDay weekday)
{
- wxCHECK_MSG( weekday != Inv_WeekDay, ms_InvDateTime, _T("invalid weekday") );
+ wxCHECK_MSG( weekday != Inv_WeekDay, wxInvalidDateTime, _T("invalid weekday") );
int diff;
WeekDay wdayThis = GetWeekDay();
diff = weekday - wdayThis;
}
- return Add(wxTimeSpan::Days(diff));
+ return Add(wxDateSpan::Days(diff));
}
wxDateTime& wxDateTime::SetToPrevWeekDay(WeekDay weekday)
{
- wxCHECK_MSG( weekday != Inv_WeekDay, ms_InvDateTime, _T("invalid weekday") );
+ wxCHECK_MSG( weekday != Inv_WeekDay, wxInvalidDateTime, _T("invalid weekday") );
int diff;
WeekDay wdayThis = GetWeekDay();
diff = wdayThis - weekday;
}
- return Substract(wxTimeSpan::Days(diff));
+ return Substract(wxDateSpan::Days(diff));
}
bool wxDateTime::SetToWeekDay(WeekDay weekday,
return gs_cumulatedDays[IsLeapYear(tm.year)][tm.mon] + tm.mday;
}
-wxDateTime::wxDateTime_t wxDateTime::GetWeekOfYear(const TimeZone& tz) const
+wxDateTime::wxDateTime_t wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags,
+ const TimeZone& tz) const
{
-#if 0
- // the first week of the year is the one which contains Jan, 4 (according
- // to ISO standard rule), so the year day N0 = 4 + 7*W always lies in the
- // week W+1. As any day N = 7*W + 4 + (N - 4)%7, it lies in the same week
- // as N0 or in the next one.
-
- // TODO this surely may be optimized - I got confused while writing it
+ if ( flags == Default_First )
+ {
+ flags = GetCountry() == USA ? Sunday_First : Monday_First;
+ }
wxDateTime_t nDayInYear = GetDayOfYear(tz);
+ wxDateTime_t week;
- // the week day of the day lying in the first week
- WeekDay wdayStart = wxDateTime(4, Jan, GetYear()).GetWeekDay();
-
- wxDateTime_t week = (nDayInYear - 4) / 7 + 1;
+ WeekDay wd = GetWeekDay(tz);
+ if ( flags == Sunday_First )
+ {
+ week = (nDayInYear - wd + 7) / 7;
+ }
+ else
+ {
+ // have to shift the week days values
+ week = (nDayInYear - (wd - 1 + 7) % 7 + 7) / 7;
+ }
- // notice that Sunday shoould be counted as 7, not 0 here!
- if ( ((nDayInYear - 4) % 7) + (!wdayStart ? 7 : wdayStart) > 7 )
+ // FIXME some more elegant way??
+ WeekDay wdYearStart = wxDateTime(1, Jan, GetYear()).GetWeekDay();
+ if ( wdYearStart == Wed || wdYearStart == Thu )
{
week++;
}
return week;
-#else // this seems to be a bit simpler and I believe is also correct
- return (WeekDay)((GetDayOfYear() - (GetWeekDay() - 1 + 7) % 7 + 7) / 7);
-#endif // 0/1
}
-wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(const TimeZone& tz) const
+wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(wxDateTime::WeekFlags flags,
+ const TimeZone& tz) const
{
- size_t nWeek = 0;
-
- wxDateTime dt(*this);
- do
+ Tm tm = GetTm(tz);
+ wxDateTime dtMonthStart = wxDateTime(1, tm.mon, tm.year);
+ int nWeek = GetWeekOfYear(flags) - dtMonthStart.GetWeekOfYear(flags) + 1;
+ if ( nWeek < 0 )
{
- nWeek++;
-
- dt -= wxTimeSpan::Week();
+ // this may happen for January when Jan, 1 is the last week of the
+ // previous year
+ nWeek += IsLeapYear(tm.year - 1) ? 53 : 52;
}
- while ( dt.GetMonth(tz) == GetMonth(tz) );
- return nWeek;
+ return (wxDateTime::wxDateTime_t)nWeek;
}
wxDateTime& wxDateTime::SetToYearDay(wxDateTime::wxDateTime_t yday)
{
int year = GetYear();
wxCHECK_MSG( (0 < yday) && (yday <= GetNumberOfDays(year)),
- ms_InvDateTime, _T("invalid year day") );
+ wxInvalidDateTime, _T("invalid year day") );
bool isLeap = IsLeapYear(year);
for ( Month mon = Jan; mon < Inv_Month; wxNextMonth(mon) )
}
}
-wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz)
+wxDateTime& wxDateTime::MakeTimezone(const TimeZone& tz, bool noDST)
{
- int secDiff = GetTimeZone() + tz.GetOffset();
+ long secDiff = GetTimeZone() + tz.GetOffset();
- // we need to know whether DST is or not in effect for this date
- if ( IsDST() == 1 )
+ // we need to know whether DST is or not in effect for this date unless
+ // the test disabled by the caller
+ if ( !noDST && (IsDST() == 1) )
{
// FIXME we assume that the DST is always shifted by 1 hour
secDiff -= 3600;
wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
{
-#ifdef __VMS__
- int time2;
-#endif
wxCHECK_MSG( format, _T(""), _T("NULL format in wxDateTime::Format") );
time_t time = GetTicks();
}
else
{
- time += tz.GetOffset();
+ time += (int)tz.GetOffset();
-#ifdef __VMS__ /* time is unsigned so VMS gives a warning on the original */
- time2 = (int) time;
- if ( time2 >= 0 )
+#ifdef __VMS__ // time is unsigned so avoid the warning
+ int time2 = (int) time;
+ if ( time2 >= 0 )
#else
- if ( time >= 0 )
+ if ( time >= 0 )
#endif
{
tm = gmtime(&time);
fmt = _T("%02d");
}
-restart:
- // start of the format specification
- switch ( *p )
+ bool restart = TRUE;
+ while ( restart )
{
- case _T('a'): // a weekday name
- case _T('A'):
- // second parameter should be TRUE for abbreviated names
- res += GetWeekDayName(tm.GetWeekDay(),
- *p == _T('a') ? Name_Abbr : Name_Full);
- break;
+ restart = FALSE;
- case _T('b'): // a month name
- case _T('B'):
- res += GetMonthName(tm.mon,
- *p == _T('b') ? Name_Abbr : Name_Full);
- break;
+ // start of the format specification
+ switch ( *p )
+ {
+ case _T('a'): // a weekday name
+ case _T('A'):
+ // second parameter should be TRUE for abbreviated names
+ res += GetWeekDayName(tm.GetWeekDay(),
+ *p == _T('a') ? Name_Abbr : Name_Full);
+ break;
- case _T('c'): // locale default date and time representation
- case _T('x'): // locale default date representation
- //
- // the problem: there is no way to know what do these format
- // specifications correspond to for the current locale.
- //
- // the solution: use a hack and still use strftime(): first
- // find the YEAR which is a year in the strftime() range (1970
- // - 2038) whose Jan 1 falls on the same week day as the Jan 1
- // of the real year. Then make a copy of the format and
- // replace all occurences of YEAR in it with some unique
- // string not appearing anywhere else in it, then use
- // strftime() to format the date in year YEAR and then replace
- // YEAR back by the real year and the unique replacement
- // string back with YEAR. Notice that "all occurences of YEAR"
- // means all occurences of 4 digit as well as 2 digit form!
- //
- // the bugs: we assume that neither of %c nor %x contains any
- // fields which may change between the YEAR and real year. For
- // example, the week number (%U, %W) and the day number (%j)
- // will change if one of these years is leap and the other one
- // is not!
- {
- // find the YEAR: normally, for any year X, Jan 1 or the
- // year X + 28 is the same weekday as Jan 1 of X (because
- // the weekday advances by 1 for each normal X and by 2
- // for each leap X, hence by 5 every 4 years or by 35
- // which is 0 mod 7 every 28 years) but this rule breaks
- // down if there are years between X and Y which are
- // divisible by 4 but not leap (i.e. divisible by 100 but
- // not 400), hence the correction.
-
- int yearReal = GetYear(tz);
- int mod28 = yearReal % 28;
-
- // be careful to not go too far - we risk to leave the
- // supported range
- int year;
- if ( mod28 < 10 )
- {
- year = 1988 + mod28; // 1988 == 0 (mod 28)
- }
- else
+ case _T('b'): // a month name
+ case _T('B'):
+ res += GetMonthName(tm.mon,
+ *p == _T('b') ? Name_Abbr : Name_Full);
+ break;
+
+ case _T('c'): // locale default date and time representation
+ case _T('x'): // locale default date representation
+ //
+ // the problem: there is no way to know what do these format
+ // specifications correspond to for the current locale.
+ //
+ // the solution: use a hack and still use strftime(): first
+ // find the YEAR which is a year in the strftime() range (1970
+ // - 2038) whose Jan 1 falls on the same week day as the Jan 1
+ // of the real year. Then make a copy of the format and
+ // replace all occurences of YEAR in it with some unique
+ // string not appearing anywhere else in it, then use
+ // strftime() to format the date in year YEAR and then replace
+ // YEAR back by the real year and the unique replacement
+ // string back with YEAR. Notice that "all occurences of YEAR"
+ // means all occurences of 4 digit as well as 2 digit form!
+ //
+ // the bugs: we assume that neither of %c nor %x contains any
+ // fields which may change between the YEAR and real year. For
+ // example, the week number (%U, %W) and the day number (%j)
+ // will change if one of these years is leap and the other one
+ // is not!
{
- year = 1970 + mod28 - 10; // 1970 == 10 (mod 28)
- }
+ // find the YEAR: normally, for any year X, Jan 1 or the
+ // year X + 28 is the same weekday as Jan 1 of X (because
+ // the weekday advances by 1 for each normal X and by 2
+ // for each leap X, hence by 5 every 4 years or by 35
+ // which is 0 mod 7 every 28 years) but this rule breaks
+ // down if there are years between X and Y which are
+ // divisible by 4 but not leap (i.e. divisible by 100 but
+ // not 400), hence the correction.
+
+ int yearReal = GetYear(tz);
+ int mod28 = yearReal % 28;
+
+ // be careful to not go too far - we risk to leave the
+ // supported range
+ int year;
+ if ( mod28 < 10 )
+ {
+ year = 1988 + mod28; // 1988 == 0 (mod 28)
+ }
+ else
+ {
+ year = 1970 + mod28 - 10; // 1970 == 10 (mod 28)
+ }
- int nCentury = year / 100,
- nCenturyReal = yearReal / 100;
+ int nCentury = year / 100,
+ nCenturyReal = yearReal / 100;
- // need to adjust for the years divisble by 400 which are
- // not leap but are counted like leap ones if we just take
- // the number of centuries in between for nLostWeekDays
- int nLostWeekDays = (nCentury - nCenturyReal) -
- (nCentury / 4 - nCenturyReal / 4);
+ // need to adjust for the years divisble by 400 which are
+ // not leap but are counted like leap ones if we just take
+ // the number of centuries in between for nLostWeekDays
+ int nLostWeekDays = (nCentury - nCenturyReal) -
+ (nCentury / 4 - nCenturyReal / 4);
- // we have to gain back the "lost" weekdays: note that the
- // effect of this loop is to not do anything to
- // nLostWeekDays (which we won't use any more), but to
- // (indirectly) set the year correctly
- while ( (nLostWeekDays % 7) != 0 )
- {
- nLostWeekDays += year++ % 4 ? 1 : 2;
- }
+ // we have to gain back the "lost" weekdays: note that the
+ // effect of this loop is to not do anything to
+ // nLostWeekDays (which we won't use any more), but to
+ // (indirectly) set the year correctly
+ while ( (nLostWeekDays % 7) != 0 )
+ {
+ nLostWeekDays += year++ % 4 ? 1 : 2;
+ }
- // at any rate, we couldn't go further than 1988 + 9 + 28!
- wxASSERT_MSG( year < 2030,
- _T("logic error in wxDateTime::Format") );
+ // at any rate, we couldn't go further than 1988 + 9 + 28!
+ wxASSERT_MSG( year < 2030,
+ _T("logic error in wxDateTime::Format") );
- wxString strYear, strYear2;
- strYear.Printf(_T("%d"), year);
- strYear2.Printf(_T("%d"), year % 100);
+ wxString strYear, strYear2;
+ strYear.Printf(_T("%d"), year);
+ strYear2.Printf(_T("%d"), year % 100);
- // find two strings not occuring in format (this is surely
- // not optimal way of doing it... improvements welcome!)
- wxString fmt = format;
- wxString replacement = (wxChar)-1;
- while ( fmt.Find(replacement) != wxNOT_FOUND )
- {
- replacement << (wxChar)-1;
- }
+ // find two strings not occuring in format (this is surely
+ // not optimal way of doing it... improvements welcome!)
+ wxString fmt = format;
+ wxString replacement = (wxChar)-1;
+ while ( fmt.Find(replacement) != wxNOT_FOUND )
+ {
+ replacement << (wxChar)-1;
+ }
- wxString replacement2 = (wxChar)-2;
- while ( fmt.Find(replacement) != wxNOT_FOUND )
- {
- replacement << (wxChar)-2;
- }
+ wxString replacement2 = (wxChar)-2;
+ while ( fmt.Find(replacement) != wxNOT_FOUND )
+ {
+ replacement << (wxChar)-2;
+ }
- // replace all occurences of year with it
- bool wasReplaced = fmt.Replace(strYear, replacement) > 0;
- if ( !wasReplaced )
- wasReplaced = fmt.Replace(strYear2, replacement2) > 0;
+ // replace all occurences of year with it
+ bool wasReplaced = fmt.Replace(strYear, replacement) > 0;
+ if ( !wasReplaced )
+ wasReplaced = fmt.Replace(strYear2, replacement2) > 0;
+
+ // use strftime() to format the same date but in supported
+ // year
+ //
+ // NB: we assume that strftime() doesn't check for the
+ // date validity and will happily format the date
+ // corresponding to Feb 29 of a non leap year (which
+ // may happen if yearReal was leap and year is not)
+ struct tm tmAdjusted;
+ InitTm(tmAdjusted);
+ tmAdjusted.tm_hour = tm.hour;
+ tmAdjusted.tm_min = tm.min;
+ tmAdjusted.tm_sec = tm.sec;
+ tmAdjusted.tm_wday = tm.GetWeekDay();
+ tmAdjusted.tm_yday = GetDayOfYear();
+ tmAdjusted.tm_mday = tm.mday;
+ tmAdjusted.tm_mon = tm.mon;
+ tmAdjusted.tm_year = year - 1900;
+ tmAdjusted.tm_isdst = 0; // no DST, already adjusted
+ wxString str = CallStrftime(*p == _T('c') ? _T("%c")
+ : _T("%x"),
+ &tmAdjusted);
+
+ // now replace the occurence of 1999 with the real year
+ wxString strYearReal, strYearReal2;
+ strYearReal.Printf(_T("%04d"), yearReal);
+ strYearReal2.Printf(_T("%02d"), yearReal % 100);
+ str.Replace(strYear, strYearReal);
+ str.Replace(strYear2, strYearReal2);
+
+ // and replace back all occurences of replacement string
+ if ( wasReplaced )
+ {
+ str.Replace(replacement2, strYear2);
+ str.Replace(replacement, strYear);
+ }
- // use strftime() to format the same date but in supported
- // year
- //
- // NB: we assume that strftime() doesn't check for the
- // date validity and will happily format the date
- // corresponding to Feb 29 of a non leap year (which
- // may happen if yearReal was leap and year is not)
- struct tm tmAdjusted;
- InitTm(tmAdjusted);
- tmAdjusted.tm_hour = tm.hour;
- tmAdjusted.tm_min = tm.min;
- tmAdjusted.tm_sec = tm.sec;
- tmAdjusted.tm_wday = tm.GetWeekDay();
- tmAdjusted.tm_yday = GetDayOfYear();
- tmAdjusted.tm_mday = tm.mday;
- tmAdjusted.tm_mon = tm.mon;
- tmAdjusted.tm_year = year - 1900;
- tmAdjusted.tm_isdst = 0; // no DST, already adjusted
- wxString str = CallStrftime(*p == _T('c') ? _T("%c")
- : _T("%x"),
- &tmAdjusted);
-
- // now replace the occurence of 1999 with the real year
- wxString strYearReal, strYearReal2;
- strYearReal.Printf(_T("%04d"), yearReal);
- strYearReal2.Printf(_T("%02d"), yearReal % 100);
- str.Replace(strYear, strYearReal);
- str.Replace(strYear2, strYearReal2);
-
- // and replace back all occurences of replacement string
- if ( wasReplaced )
- {
- str.Replace(replacement2, strYear2);
- str.Replace(replacement, strYear);
+ res += str;
}
+ break;
- res += str;
- }
- break;
+ case _T('d'): // day of a month (01-31)
+ res += wxString::Format(fmt, tm.mday);
+ break;
- case _T('d'): // day of a month (01-31)
- res += wxString::Format(fmt, tm.mday);
- break;
+ case _T('H'): // hour in 24h format (00-23)
+ res += wxString::Format(fmt, tm.hour);
+ break;
- case _T('H'): // hour in 24h format (00-23)
- res += wxString::Format(fmt, tm.hour);
- break;
+ case _T('I'): // hour in 12h format (01-12)
+ {
+ // 24h -> 12h, 0h -> 12h too
+ int hour12 = tm.hour > 12 ? tm.hour - 12
+ : tm.hour ? tm.hour : 12;
+ res += wxString::Format(fmt, hour12);
+ }
+ break;
- case _T('I'): // hour in 12h format (01-12)
- {
- // 24h -> 12h, 0h -> 12h too
- int hour12 = tm.hour > 12 ? tm.hour - 12
- : tm.hour ? tm.hour : 12;
- res += wxString::Format(fmt, hour12);
- }
- break;
+ case _T('j'): // day of the year
+ res += wxString::Format(fmt, GetDayOfYear(tz));
+ break;
- case _T('j'): // day of the year
- res += wxString::Format(fmt, GetDayOfYear(tz));
- break;
+ case _T('m'): // month as a number (01-12)
+ res += wxString::Format(fmt, tm.mon + 1);
+ break;
- case _T('m'): // month as a number (01-12)
- res += wxString::Format(fmt, tm.mon + 1);
- break;
+ case _T('M'): // minute as a decimal number (00-59)
+ res += wxString::Format(fmt, tm.min);
+ break;
- case _T('M'): // minute as a decimal number (00-59)
- res += wxString::Format(fmt, tm.min);
- break;
+ case _T('p'): // AM or PM string
+ res += CallStrftime(_T("%p"), &tmTimeOnly);
+ break;
- case _T('p'): // AM or PM string
- res += CallStrftime(_T("%p"), &tmTimeOnly);
- break;
+ case _T('S'): // second as a decimal number (00-61)
+ res += wxString::Format(fmt, tm.sec);
+ break;
- case _T('S'): // second as a decimal number (00-61)
- res += wxString::Format(fmt, tm.sec);
- break;
+ case _T('U'): // week number in the year (Sunday 1st week day)
+ res += wxString::Format(fmt, GetWeekOfYear(Sunday_First, tz));
+ break;
- case _T('U'): // week number in the year (Sunday 1st week day)
- {
- int week = (GetDayOfYear(tz) - tm.GetWeekDay() + 7) / 7;
- res += wxString::Format(fmt, week);
- }
- break;
+ case _T('W'): // week number in the year (Monday 1st week day)
+ res += wxString::Format(fmt, GetWeekOfYear(Monday_First, tz));
+ break;
- case _T('W'): // week number in the year (Monday 1st week day)
- res += wxString::Format(fmt, GetWeekOfYear(tz));
- break;
+ case _T('w'): // weekday as a number (0-6), Sunday = 0
+ res += wxString::Format(fmt, tm.GetWeekDay());
+ break;
- case _T('w'): // weekday as a number (0-6), Sunday = 0
- res += wxString::Format(fmt, tm.GetWeekDay());
- break;
+ // case _T('x'): -- handled with "%c"
- // case _T('x'): -- handled with "%c"
+ case _T('X'): // locale default time representation
+ // just use strftime() to format the time for us
+ res += CallStrftime(_T("%X"), &tmTimeOnly);
+ break;
- case _T('X'): // locale default time representation
- // just use strftime() to format the time for us
- res += CallStrftime(_T("%X"), &tmTimeOnly);
- break;
+ case _T('y'): // year without century (00-99)
+ res += wxString::Format(fmt, tm.year % 100);
+ break;
- case _T('y'): // year without century (00-99)
- res += wxString::Format(fmt, tm.year % 100);
- break;
+ case _T('Y'): // year with century
+ res += wxString::Format(fmt, tm.year);
+ break;
- case _T('Y'): // year with century
- res += wxString::Format(fmt, tm.year);
- break;
+ case _T('Z'): // timezone name
+ res += CallStrftime(_T("%Z"), &tmTimeOnly);
+ break;
- case _T('Z'): // timezone name
- res += CallStrftime(_T("%Z"), &tmTimeOnly);
- break;
+ default:
+ // is it the format width?
+ fmt.Empty();
+ while ( *p == _T('-') || *p == _T('+') ||
+ *p == _T(' ') || wxIsdigit(*p) )
+ {
+ fmt += *p;
+ }
- default:
- // is it the format width?
- fmt.Empty();
- while ( *p == _T('-') || *p == _T('+') ||
- *p == _T(' ') || wxIsdigit(*p) )
- {
- fmt += *p;
- }
+ if ( !fmt.IsEmpty() )
+ {
+ // we've only got the flags and width so far in fmt
+ fmt.Prepend(_T('%'));
+ fmt.Append(_T('d'));
- if ( !fmt.IsEmpty() )
- {
- // we've only got the flags and width so far in fmt
- fmt.Prepend(_T('%'));
- fmt.Append(_T('d'));
+ restart = TRUE;
- goto restart;
- }
+ break;
+ }
- // no, it wasn't the width
- wxFAIL_MSG(_T("unknown format specificator"));
+ // no, it wasn't the width
+ wxFAIL_MSG(_T("unknown format specificator"));
- // fall through and just copy it nevertheless
+ // fall through and just copy it nevertheless
- case _T('%'): // a percent sign
- res += *p;
- break;
+ case _T('%'): // a percent sign
+ res += *p;
+ break;
- case 0: // the end of string
- wxFAIL_MSG(_T("missing format at the end of string"));
+ case 0: // the end of string
+ wxFAIL_MSG(_T("missing format at the end of string"));
- // just put the '%' which was the last char in format
- res += _T('%');
- break;
+ // just put the '%' which was the last char in format
+ res += _T('%');
+ break;
+ }
}
}
// the spec was correct
Set(day, mon, year, hour, min, sec);
- MakeTimezone(60*(wxDateTime_t)offset);
+ MakeTimezone((wxDateTime_t)(60*offset));
return p;
}
// this is the format which corresponds to ctime() output
// and strptime("%c") should parse it, so try it first
- static const wxChar *fmtCtime = _T("%a %b %e %H:%M:%S %Y");
+ static const wxChar *fmtCtime = _T("%a %b %d %H:%M:%S %Y");
const wxChar *result = dt.ParseFormat(input, fmtCtime);
if ( !result )
break;
case _T('d'): // day of a month (01-31)
- if ( !GetNumericToken(input, &num) || (num > 31) )
+ if ( !GetNumericToken(input, &num) || (num > 31) || (num < 1) )
{
// no match
return (wxChar *)NULL;
haveHour = TRUE;
hourIsIn12hFormat = TRUE;
- hour = num % 12; // 12 should be 0
+ hour = (wxDateTime_t)(num % 12); // 12 should be 0
break;
case _T('j'): // day of the year
{
wxDateTime dt;
- wxString fmtDate;
+ wxString fmtDate, fmtDateAlt;
if ( IsWestEuropeanCountry(GetCountry()) ||
GetCountry() == Russia )
{
fmtDate = _T("%d/%m/%y");
+ fmtDateAlt = _T("%m/%d/%y");
}
else // assume USA
{
fmtDate = _T("%m/%d/%y");
+ fmtDateAlt = _T("%d/%m/%y");
}
const wxChar *result = dt.ParseFormat(input, fmtDate);
+
+ if ( !result )
+ {
+ // ok, be nice and try another one
+ result = dt.ParseFormat(input, fmtDateAlt);
+ }
+
if ( !result )
{
// bad luck
}
haveYear = TRUE;
- year = 1900 + num;
+ year = 1900 + (wxDateTime_t)num;
break;
case _T('Y'): // year with century
while ( wxIsspace(*p) )
p++;
- wxString today = _T("today");
- size_t len = today.length();
- if ( wxString(p, len).CmpNoCase(today) == 0 )
+ // some special cases
+ static struct
{
- // nothing can follow this, so stop here
- p += len;
+ const wxChar *str;
+ int dayDiffFromToday;
+ } literalDates[] =
+ {
+ { wxTRANSLATE("today"), 0 },
+ { wxTRANSLATE("yesterday"), -1 },
+ { wxTRANSLATE("tomorrow"), 1 },
+ };
- *this = Today();
+ for ( size_t n = 0; n < WXSIZEOF(literalDates); n++ )
+ {
+ wxString date = wxGetTranslation(literalDates[n].str);
+ size_t len = date.length();
+ if ( wxString(p, len).CmpNoCase(date) == 0 )
+ {
+ // nothing can follow this, so stop here
+ p += len;
+
+ int dayDiffFromToday = literalDates[n].dayDiffFromToday;
+ *this = Today();
+ if ( dayDiffFromToday )
+ {
+ *this += wxDateSpan::Days(dayDiffFromToday);
+ }
- return p;
+ return p;
+ }
}
// what do we have?
int year = 0;
// tokenize the string
- wxStringTokenizer tok(p, _T(",/-\t "));
+ wxStringTokenizer tok(p, _T(",/-\t\n "));
while ( tok.HasMoreTokens() )
{
wxString token = tok.GetNextToken();
haveYear = TRUE;
// no roll over - 99 means 99, not 1999 for us
- year = val;
+ year = (wxDateTime_t)val;
}
else if ( isMonth )
{
haveDay = TRUE;
- day = val;
+ day = (wxDateTime_t)val;
}
}
else // not a number
return result;
}
+// ----------------------------------------------------------------------------
+// Workdays and holidays support
+// ----------------------------------------------------------------------------
+
+bool wxDateTime::IsWorkDay(Country WXUNUSED(country)) const
+{
+ return !wxDateTimeHolidayAuthority::IsHoliday(*this);
+}
+
// ============================================================================
// wxTimeSpan
// ============================================================================
wxCHECK_MSG( format, _T(""), _T("NULL format in wxTimeSpan::Format") );
wxString str;
- str.Alloc(strlen(format));
+ str.Alloc(wxStrlen(format));
for ( const wxChar *pch = format; pch; pch++ )
{
return str;
}
+
+// ============================================================================
+// wxDateTimeHolidayAuthority and related classes
+// ============================================================================
+
+#include "wx/arrimpl.cpp"
+
+WX_DEFINE_OBJARRAY(wxDateTimeArray)
+
+static int wxCMPFUNC_CONV
+wxDateTimeCompareFunc(wxDateTime **first, wxDateTime **second)
+{
+ wxDateTime dt1 = **first,
+ dt2 = **second;
+
+ return dt1 == dt2 ? 0 : dt1 < dt2 ? -1 : +1;
+}
+
+// ----------------------------------------------------------------------------
+// wxDateTimeHolidayAuthority
+// ----------------------------------------------------------------------------
+
+wxHolidayAuthoritiesArray wxDateTimeHolidayAuthority::ms_authorities;
+
+/* static */
+bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime& dt)
+{
+ size_t count = ms_authorities.GetCount();
+ for ( size_t n = 0; n < count; n++ )
+ {
+ if ( ms_authorities[n]->DoIsHoliday(dt) )
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* static */
+size_t
+wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime& dtStart,
+ const wxDateTime& dtEnd,
+ wxDateTimeArray& holidays)
+{
+ wxDateTimeArray hol;
+
+ holidays.Empty();
+
+ size_t count = ms_authorities.GetCount();
+ for ( size_t nAuth = 0; nAuth < count; nAuth++ )
+ {
+ ms_authorities[nAuth]->DoGetHolidaysInRange(dtStart, dtEnd, hol);
+
+ WX_APPEND_ARRAY(holidays, hol);
+ }
+
+ holidays.Sort(wxDateTimeCompareFunc);
+
+ return holidays.GetCount();
+}
+
+/* static */
+void wxDateTimeHolidayAuthority::ClearAllAuthorities()
+{
+ WX_CLEAR_ARRAY(ms_authorities);
+}
+
+/* static */
+void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority *auth)
+{
+ ms_authorities.Add(auth);
+}
+
+// ----------------------------------------------------------------------------
+// wxDateTimeWorkDays
+// ----------------------------------------------------------------------------
+
+bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime& dt) const
+{
+ wxDateTime::WeekDay wd = dt.GetWeekDay();
+
+ return (wd == wxDateTime::Sun) || (wd == wxDateTime::Sat);
+}
+
+size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime& dtStart,
+ const wxDateTime& dtEnd,
+ wxDateTimeArray& holidays) const
+{
+ if ( dtStart > dtEnd )
+ {
+ wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") );
+
+ return 0u;
+ }
+
+ holidays.Empty();
+
+ // instead of checking all days, start with the first Sat after dtStart and
+ // end with the last Sun before dtEnd
+ wxDateTime dtSatFirst = dtStart.GetNextWeekDay(wxDateTime::Sat),
+ dtSatLast = dtEnd.GetPrevWeekDay(wxDateTime::Sat),
+ dtSunFirst = dtStart.GetNextWeekDay(wxDateTime::Sun),
+ dtSunLast = dtEnd.GetPrevWeekDay(wxDateTime::Sun),
+ dt;
+
+ for ( dt = dtSatFirst; dt <= dtSatLast; dt += wxDateSpan::Week() )
+ {
+ holidays.Add(dt);
+ }
+
+ for ( dt = dtSunFirst; dt <= dtSunLast; dt += wxDateSpan::Week() )
+ {
+ holidays.Add(dt);
+ }
+
+ return holidays.GetCount();
+}
+
+