// headers
// ----------------------------------------------------------------------------
-#ifdef __GNUG__
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "datetime.h"
#endif
#include <ctype.h>
#include "wx/datetime.h"
-#include "wx/timer.h" // for wxGetLocalTimeMillis()
+#include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
const long wxDateTime::TIME_T_FACTOR = 1000l;
+#if wxUSE_EXTENDED_RTTI
+
+template<> void wxStringReadValue(const wxString &s , wxDateTime &data )
+{
+ data.ParseFormat(s,wxT("%Y-%m-%d %H:%M:%S")) ;
+}
+
+template<> void wxStringWriteValue(wxString &s , const wxDateTime &data )
+{
+ s = data.Format(wxT("%Y-%m-%d %H:%M:%S")) ;
+}
+
+wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter<wxDateTime> , wxFromStringConverter<wxDateTime>)
+
+#endif
+
+//
// ----------------------------------------------------------------------------
// conditional compilation
// ----------------------------------------------------------------------------
{
wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays);
- return TRUE;
+ return true;
}
virtual void OnExit()
// (in seconds)
static int GetTimeZone()
{
- // set to TRUE when the timezone is set
- static bool s_timezoneSet = FALSE;
#ifdef WX_GMTOFF_IN_TM
+ // set to true when the timezone is set
+ static bool s_timezoneSet = false;
static long gmtoffset = LONG_MAX; // invalid timezone
-#endif
// ensure that the timezone variable is set by calling localtime
if ( !s_timezoneSet )
struct tm *tm;
tm = localtime(&t);
- s_timezoneSet = TRUE;
+ s_timezoneSet = true;
-#ifdef WX_GMTOFF_IN_TM
// note that GMT offset is the opposite of time zone and so to return
// consistent results in both WX_GMTOFF_IN_TM and !WX_GMTOFF_IN_TM
// cases we have to negate it
gmtoffset = -tm->tm_gmtoff;
-#endif
}
-#ifdef WX_GMTOFF_IN_TM
return (int)gmtoffset;
-#else
+#else // !WX_GMTOFF_IN_TM
return (int)WX_TIMEZONE;
-#endif
+#endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
}
// return the integral part of the JDN for the midnight of the given date (to
#ifdef HAVE_STRPTIME
+// glibc2 doesn't define this in the headers unless _XOPEN_SOURCE is defined
+// which, unfortunately, wreaks havoc elsewhere
+#if defined(__GLIBC__) && (__GLIBC__ == 2)
+ extern "C" char *strptime(const char *, const char *, struct tm *);
+#endif
+
// Unicode-friendly strptime() wrapper
static const wxChar *
CallStrptime(const wxChar *input, const char *fmt, tm *tm)
break;
}
- return !!s && s.ToULong(number);
+ return !s.IsEmpty() && s.ToULong(number);
}
// scans all alphabetic characters and returns the resulting string
{
wxFAIL_MSG(_T("unknown calendar"));
- return FALSE;
+ return false;
}
}
{
wxCHECK_MSG( wday != Inv_WeekDay, _T(""), _T("invalid weekday") );
- // take some arbitrary Sunday
+ // take some arbitrary Sunday (but notice that the day should be such that
+ // after adding wday to it below we still have a valid date, e.g. don't
+ // take 28 here!)
tm tm;
InitTm(tm);
- tm.tm_mday = 28;
+ tm.tm_mday = 21;
tm.tm_mon = Nov;
tm.tm_year = 99;
{
tm tm;
InitTm(tm);
+ wxChar buffer[64];
+ // @Note: Do not call 'CallStrftime' here! CallStrftime checks the return code
+ // and causes an assertion failed if the buffer is to small (which is good) - OR -
+ // if strftime does not return anything because the format string is invalid - OR -
+ // if there are no 'am' / 'pm' tokens defined for the current locale (which is not good).
+ // wxDateTime::ParseTime will try several different formats to parse the time.
+ // As a result, GetAmPmStrings might get called, even if the current locale
+ // does not define any 'am' / 'pm' tokens. In this case, wxStrftime would
+ // assert, even though it is a perfectly legal use.
if ( am )
{
- *am = CallStrftime(_T("%p"), &tm);
+ if (wxStrftime(buffer, sizeof buffer, _T("%p"), &tm) > 0)
+ *am = wxString(buffer);
+ else
+ *am = wxString();
}
if ( pm )
{
tm.tm_hour = 13;
- *pm = CallStrftime(_T("%p"), &tm);
+ if (wxStrftime(buffer, sizeof buffer, _T("%p"), &tm) > 0)
+ *pm = wxString(buffer);
+ else
+ *pm = wxString();
}
}
dt += wxTimeSpan::Hours(1);
// disable DST tests because it could result in an infinite recursion!
- dt.MakeGMT(TRUE);
+ dt.MakeGMT(true);
}
else switch ( country )
{
dt += wxTimeSpan::Hours(1);
// disable DST tests because it could result in an infinite recursion!
- dt.MakeGMT(TRUE);
+ dt.MakeGMT(true);
}
else switch ( country )
{
jdn *= MILLISECONDS_PER_DAY;
+ // JDNs always suppose an UTC date, so bring it back to local time zone
+ // (also see GetJulianDayNumber() implementation)
+ long tzDiff = GetTimeZone();
+ if ( IsDST() == 1 )
+ {
+ // FIXME: again, we suppose that DST is always one hour
+ tzDiff -= 3600;
+ }
+
+ jdn += tzDiff*1000; // tzDiff is in seconds
+
m_time.Assign(jdn);
return *this;
wxDateTime& wxDateTime::SetFromDOS(unsigned long ddt)
{
struct tm tm;
+ InitTm(tm);
long year = ddt & 0xFE000000;
year >>= 25;
// Weekday and monthday stuff
// ----------------------------------------------------------------------------
-bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek,
- WeekDay weekday,
- WeekFlags flags)
+// convert Sun, Mon, ..., Sat into 6, 0, ..., 5
+static inline int ConvertWeekDayToMondayBase(int wd)
+{
+ return wd == wxDateTime::Sun ? 6 : wd - 1;
+}
+
+/* static */
+wxDateTime
+wxDateTime::SetToWeekOfYear(int year, wxDateTime_t numWeek, WeekDay wd)
{
wxASSERT_MSG( numWeek > 0,
_T("invalid week number: weeks are counted from 1") );
- int year = GetYear();
+ // Jan 4 always lies in the 1st week of the year
+ wxDateTime dt(4, Jan, year);
+ dt.SetToWeekDayInSameWeek(wd);
+ dt += wxDateSpan::Weeks(numWeek - 1);
+
+ return dt;
+}
+// use a separate function to avoid warnings about using deprecated
+// SetToTheWeek in GetWeek below
+static wxDateTime
+SetToTheWeek(int year,
+ wxDateTime::wxDateTime_t numWeek,
+ wxDateTime::WeekDay weekday,
+ wxDateTime::WeekFlags flags)
+{
// Jan 4 always lies in the 1st week of the year
- Set(4, Jan, year);
- SetToWeekDayInSameWeek(weekday, flags) += wxDateSpan::Weeks(numWeek - 1);
+ wxDateTime dt(4, wxDateTime::Jan, year);
+ dt.SetToWeekDayInSameWeek(weekday, flags);
+ dt += wxDateSpan::Weeks(numWeek - 1);
+
+ return dt;
+}
+bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek,
+ WeekDay weekday,
+ WeekFlags flags)
+{
+ int year = GetYear();
+ *this = ::SetToTheWeek(year, numWeek, weekday, flags);
if ( GetYear() != year )
{
// oops... numWeek was too big
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
+}
+
+wxDateTime wxDateTime::GetWeek(wxDateTime_t numWeek,
+ WeekDay weekday,
+ WeekFlags flags) const
+{
+ return ::SetToTheWeek(GetYear(), numWeek, weekday, flags);
}
wxDateTime& wxDateTime::SetToLastMonthDay(Month month,
Month month,
int year)
{
- wxCHECK_MSG( weekday != Inv_WeekDay, FALSE, _T("invalid weekday") );
+ wxCHECK_MSG( weekday != Inv_WeekDay, false, _T("invalid weekday") );
- // we don't check explicitly that -5 <= n <= 5 because we will return FALSE
+ // we don't check explicitly that -5 <= n <= 5 because we will return false
// anyhow in such case - but may be should still give an assert for it?
// take the current month/year if none specified
{
*this = dt;
- return TRUE;
+ return true;
}
else
{
// no such day in this month
- return FALSE;
+ return false;
}
}
-wxDateTime::wxDateTime_t wxDateTime::GetDayOfYear(const TimeZone& tz) const
+static inline
+wxDateTime::wxDateTime_t GetDayOfYearFromTm(const wxDateTime::Tm& tm)
{
- Tm tm(GetTm(tz));
+ return gs_cumulatedDays[wxDateTime::IsLeapYear(tm.year)][tm.mon] + tm.mday;
+}
- return gs_cumulatedDays[IsLeapYear(tm.year)][tm.mon] + tm.mday;
+wxDateTime::wxDateTime_t wxDateTime::GetDayOfYear(const TimeZone& tz) const
+{
+ return GetDayOfYearFromTm(GetTm(tz));
}
-wxDateTime::wxDateTime_t wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags,
- const TimeZone& tz) const
+wxDateTime::wxDateTime_t
+wxDateTime::GetWeekOfYear(wxDateTime::WeekFlags flags, const TimeZone& tz) const
{
if ( flags == Default_First )
{
flags = GetCountry() == USA ? Sunday_First : Monday_First;
}
- wxDateTime_t nDayInYear = GetDayOfYear(tz);
- wxDateTime_t week;
+ Tm tm(GetTm(tz));
+ wxDateTime_t nDayInYear = GetDayOfYearFromTm(tm);
- WeekDay wd = GetWeekDay(tz);
+ int wdTarget = GetWeekDay(tz);
+ int wdYearStart = wxDateTime(1, Jan, GetYear()).GetWeekDay();
+ int week;
if ( flags == Sunday_First )
{
- week = (nDayInYear - wd + 7) / 7;
+ // FIXME: First week is not calculated correctly.
+ week = (nDayInYear - wdTarget + 7) / 7;
+ if ( wdYearStart == Wed || wdYearStart == Thu )
+ week++;
}
- else
+ else // week starts with monday
{
- // have to shift the week days values
- week = (nDayInYear - (wd - 1 + 7) % 7 + 7) / 7;
- }
+ // adjust the weekdays to non-US style.
+ wdYearStart = ConvertWeekDayToMondayBase(wdYearStart);
+ wdTarget = ConvertWeekDayToMondayBase(wdTarget);
- // FIXME some more elegant way??
- WeekDay wdYearStart = wxDateTime(1, Jan, GetYear()).GetWeekDay();
- if ( wdYearStart == Wed || wdYearStart == Thu )
- {
- week++;
+ // quoting from http://www.cl.cam.ac.uk/~mgk25/iso-time.html:
+ //
+ // Week 01 of a year is per definition the first week that has the
+ // Thursday in this year, which is equivalent to the week that
+ // contains the fourth day of January. In other words, the first
+ // week of a new year is the week that has the majority of its
+ // days in the new year. Week 01 might also contain days from the
+ // previous year and the week before week 01 of a year is the last
+ // week (52 or 53) of the previous year even if it contains days
+ // from the new year. A week starts with Monday (day 1) and ends
+ // with Sunday (day 7).
+ //
+
+ // if Jan 1 is Thursday or less, it is in the first week of this year
+ if ( wdYearStart < 4 )
+ {
+ // count the number of entire weeks between Jan 1 and this date
+ week = (nDayInYear + wdYearStart + 6 - wdTarget)/7;
+
+ // be careful to check for overflow in the next year
+ if ( week == 53 && tm.mday - wdTarget > 28 )
+ week = 1;
+ }
+ else // Jan 1 is in the last week of the previous year
+ {
+ // check if we happen to be at the last week of previous year:
+ if ( tm.mon == Jan && tm.mday < 8 - wdYearStart )
+ week = wxDateTime(31, Dec, GetYear()-1).GetWeekOfYear();
+ else
+ week = (nDayInYear + wdYearStart - 1 - wdTarget)/7;
+ }
}
return week;
// for Dec, we can't compare with gs_cumulatedDays[mon + 1], but we
// don't need it neither - because of the CHECK above we know that
// yday lies in December then
- if ( (mon == Dec) || (yday < gs_cumulatedDays[isLeap][mon + 1]) )
+ if ( (mon == Dec) || (yday <= gs_cumulatedDays[isLeap][mon + 1]) )
{
Set(yday - gs_cumulatedDays[isLeap][mon], mon, year);
double wxDateTime::GetJulianDayNumber() const
{
- // JDN are always expressed for the GMT dates
- Tm tm(ToTimezone(GMT0).GetTm(GMT0));
+ // JDN are always expressed for the UTC dates
+ Tm tm(ToTimezone(UTC).GetTm(UTC));
double result = GetTruncatedJDN(tm.mday, tm.mon, tm.year);
fmt = _T("%02d");
}
- bool restart = TRUE;
+ bool restart = true;
while ( restart )
{
- restart = FALSE;
+ restart = false;
// start of the format specification
switch ( *p )
{
case _T('a'): // a weekday name
case _T('A'):
- // second parameter should be TRUE for abbreviated names
+ // second parameter should be true for abbreviated names
res += GetWeekDayName(tm.GetWeekDay(),
*p == _T('a') ? Name_Abbr : Name_Full);
break;
fmt.Prepend(_T('%'));
fmt.Append(_T('d'));
- restart = TRUE;
+ restart = true;
break;
}
unsigned long num;
// what fields have we found?
- bool haveWDay = FALSE,
- haveYDay = FALSE,
- haveDay = FALSE,
- haveMon = FALSE,
- haveYear = FALSE,
- haveHour = FALSE,
- haveMin = FALSE,
- haveSec = FALSE;
-
- bool hourIsIn12hFormat = FALSE, // or in 24h one?
- isPM = FALSE; // AM by default
+ bool haveWDay = false,
+ haveYDay = false,
+ haveDay = false,
+ haveMon = false,
+ haveYear = false,
+ haveHour = false,
+ haveMin = false,
+ haveSec = false;
+
+ bool hourIsIn12hFormat = false, // or in 24h one?
+ isPM = false; // AM by default
// and the value of the items we have (init them to get rid of warnings)
wxDateTime_t sec = 0,
// parse the optional width
size_t width = 0;
- while ( isdigit(*++fmt) )
+ while ( wxIsdigit(*++fmt) )
{
width *= 10;
width += *fmt - _T('0');
return (wxChar *)NULL;
}
}
- haveWDay = TRUE;
+ haveWDay = true;
break;
case _T('b'): // a month name
return (wxChar *)NULL;
}
}
- haveMon = TRUE;
+ haveMon = true;
break;
case _T('c'): // locale default date and time representation
Tm tm = dt.GetTm();
haveDay = haveMon = haveYear =
- haveHour = haveMin = haveSec = TRUE;
+ haveHour = haveMin = haveSec = true;
hour = tm.hour;
min = tm.min;
// we can't check whether the day range is correct yet, will
// do it later - assume ok for now
- haveDay = TRUE;
+ haveDay = true;
mday = (wxDateTime_t)num;
break;
return (wxChar *)NULL;
}
- haveHour = TRUE;
+ haveHour = true;
hour = (wxDateTime_t)num;
break;
return (wxChar *)NULL;
}
- haveHour = TRUE;
- hourIsIn12hFormat = TRUE;
+ haveHour = true;
+ hourIsIn12hFormat = true;
hour = (wxDateTime_t)(num % 12); // 12 should be 0
break;
return (wxChar *)NULL;
}
- haveYDay = TRUE;
+ haveYDay = true;
yday = (wxDateTime_t)num;
break;
return (wxChar *)NULL;
}
- haveMon = TRUE;
+ haveMon = true;
mon = (Month)(num - 1);
break;
return (wxChar *)NULL;
}
- haveMin = TRUE;
+ haveMin = true;
min = (wxDateTime_t)num;
break;
wxString am, pm, token = GetAlphaToken(input);
GetAmPmStrings(&am, &pm);
+ if (am.IsEmpty() && pm.IsEmpty())
+ return (wxChar *)NULL; // no am/pm strings defined
if ( token.CmpNoCase(pm) == 0 )
{
- isPM = TRUE;
+ isPM = true;
}
else if ( token.CmpNoCase(am) != 0 )
{
return (wxChar *)NULL;
}
- haveHour = haveMin = haveSec = TRUE;
+ haveHour = haveMin = haveSec = true;
Tm tm = dt.GetTm();
hour = tm.hour;
return (wxChar *)NULL;
}
- haveHour = haveMin = TRUE;
+ haveHour = haveMin = true;
Tm tm = dt.GetTm();
hour = tm.hour;
return (wxChar *)NULL;
}
- haveSec = TRUE;
+ haveSec = true;
sec = (wxDateTime_t)num;
break;
return (wxChar *)NULL;
}
- haveHour = haveMin = haveSec = TRUE;
+ haveHour = haveMin = haveSec = true;
Tm tm = dt.GetTm();
hour = tm.hour;
return (wxChar *)NULL;
}
- haveWDay = TRUE;
+ haveWDay = true;
wday = (WeekDay)num;
break;
{
input = result;
- haveDay = haveMon = haveYear = TRUE;
+ haveDay = haveMon = haveYear = true;
year = 1900 + tm.tm_year;
mon = (Month)tm.tm_mon;
Tm tm = dt.GetTm();
- haveDay = haveMon = haveYear = TRUE;
+ haveDay = haveMon = haveYear = true;
year = tm.year;
mon = tm.mon;
return (wxChar *)NULL;
}
- haveHour = haveMin = haveSec = TRUE;
+ haveHour = haveMin = haveSec = true;
hour = tm.tm_hour;
min = tm.tm_min;
return (wxChar *)NULL;
}
- haveHour = haveMin = haveSec = TRUE;
+ haveHour = haveMin = haveSec = true;
Tm tm = dt.GetTm();
hour = tm.hour;
return (wxChar *)NULL;
}
- haveYear = TRUE;
+ haveYear = true;
// TODO should have an option for roll over date instead of
// hard coding it here
return (wxChar *)NULL;
}
- haveYear = TRUE;
+ haveYear = true;
year = (wxDateTime_t)num;
break;
Set(tm);
+ // finally check that the week day is consistent -- if we had it
+ if ( haveWDay && GetWeekDay() != wday )
+ {
+ wxLogDebug(_T("inconsistsnet week day in wxDateTime::ParseFormat()"));
+
+ return NULL;
+ }
+
return input;
}
{
wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
- // there is a public domain version of getdate.y, but it only works for
- // English...
- wxFAIL_MSG(_T("TODO"));
+ // Set to current day and hour, so strings like '14:00' becomes today at
+ // 14, not some other random date
+ wxDateTime dtDate = wxDateTime::Today();
+ wxDateTime dtTime = wxDateTime::Today();
+
+ const wxChar* pchTime;
+
+ // Try to parse the beginning of the string as a date
+ const wxChar* pchDate = dtDate.ParseDate(date);
+
+ // We got a date in the beginning, see if there is a time specified after the date
+ if ( pchDate )
+ {
+ // Skip spaces, as the ParseTime() function fails on spaces
+ while ( wxIsspace(*pchDate) )
+ pchDate++;
+
+ pchTime = dtTime.ParseTime(pchDate);
+ }
+ else // no date in the beginning
+ {
+ // check and see if we have a time followed by a date
+ pchTime = dtTime.ParseTime(date);
+ if ( pchTime )
+ {
+ while ( wxIsspace(*pchTime) )
+ pchTime++;
- return (wxChar *)NULL;
+ pchDate = dtDate.ParseDate(pchTime);
+ }
+ }
+
+ // If we have a date specified, set our own data to the same date
+ if ( !pchDate || !pchTime )
+ return NULL;
+
+ Set(dtDate.GetDay(), dtDate.GetMonth(), dtDate.GetYear(),
+ dtTime.GetHour(), dtTime.GetMinute(), dtTime.GetSecond(),
+ dtTime.GetMillisecond());
+
+ // Return endpoint of scan
+ return pchDate > pchTime ? pchDate : pchTime;
}
const wxChar *wxDateTime::ParseDate(const wxChar *date)
// have the ability to back track.
// what do we have?
- bool haveDay = FALSE, // the months day?
- haveWDay = FALSE, // the day of week?
- haveMon = FALSE, // the month?
- haveYear = FALSE; // the year?
+ bool haveDay = false, // the months day?
+ haveWDay = false, // the day of week?
+ haveMon = false, // the month?
+ haveYear = false; // the year?
// and the value of the items we have (init them to get rid of warnings)
WeekDay wday = Inv_WeekDay;
{
// guess what this number is
- bool isDay = FALSE,
- isMonth = FALSE,
- isYear = FALSE;
+ bool isDay = false,
+ isMonth = false,
+ isYear = false;
if ( !haveMon && val > 0 && val <= 12 )
{
// assume it is month
- isMonth = TRUE;
+ isMonth = true;
}
else // not the month
{
- wxDateTime_t maxDays = haveMon
- ? GetNumOfDaysInMonth(haveYear ? year : Inv_Year, mon)
- : 31;
-
- // can it be day?
- if ( (val == 0) || (val > (unsigned long)maxDays) ) // cast to shut up compiler warning in BCC
+ if ( haveDay )
{
- isYear = TRUE;
+ // this can only be the year
+ isYear = true;
}
- else
+ else // may be either day or year
{
- isDay = TRUE;
+ wxDateTime_t maxDays = haveMon
+ ? GetNumOfDaysInMonth(haveYear ? year : Inv_Year, mon)
+ : 31;
+
+ // can it be day?
+ if ( (val == 0) || (val > (unsigned long)maxDays) )
+ {
+ // no
+ isYear = true;
+ }
+ else // yes, suppose it's the day
+ {
+ isDay = true;
+ }
}
}
if ( haveYear )
break;
- haveYear = TRUE;
+ haveYear = true;
year = (wxDateTime_t)val;
}
if ( haveDay )
break;
- haveDay = TRUE;
+ haveDay = true;
day = (wxDateTime_t)val;
}
else if ( isMonth )
{
- haveMon = TRUE;
+ haveMon = true;
mon = (Month)(val - 1);
}
// no need to check in month range as always < 12, but
// the days are counted from 1 unlike the months
day = (wxDateTime_t)mon + 1;
- haveDay = TRUE;
+ haveDay = true;
}
else
{
mon = mon2;
- haveMon = TRUE;
+ haveMon = true;
}
else // not a valid month name
{
break;
}
- haveWDay = TRUE;
+ haveWDay = true;
}
else // not a valid weekday name
{
break;
}
- haveDay = TRUE;
+ haveDay = true;
day = (wxDateTime_t)(n + 1);
}
mon = (wxDateTime::Month)(day - 1);
// we're in the current year then
- if ( (year > 0) &&
- (unsigned)year <= GetNumOfDaysInMonth(Inv_Year, mon) )
+ if ( (year > 0) && (year <= (int)GetNumOfDaysInMonth(Inv_Year, mon)) )
{
day = year;
- haveMon = TRUE;
- haveYear = FALSE;
+ haveMon = true;
+ haveYear = false;
}
- //else: no, can't exchange, leave haveMon == FALSE
+ //else: no, can't exchange, leave haveMon == false
}
}
{
if ( ms_authorities[n]->DoIsHoliday(dt) )
{
- return TRUE;
+ return true;
}
}
- return FALSE;
+ return false;
}
/* static */