// so long as the above copyright and this permission statement
// are retained in all copies.
//
-// Licence: wxWindows license
+// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
/*
// headers
// ----------------------------------------------------------------------------
-#ifdef __GNUG__
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "datetime.h"
#endif
#include "wx/tokenzr.h"
#include "wx/module.h"
-#define wxDEFINE_TIME_CONSTANTS // before including datetime.h
-
#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
// ----------------------------------------------------------------------------
-#if defined(HAVE_STRPTIME) && defined(__LINUX__)
+#if defined(HAVE_STRPTIME) && defined(__GLIBC__) && \
+ ((__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0))
// glibc 2.0.7 strptime() is broken - the following snippet causes it to
// crash (instead of just failing):
//
#undef HAVE_STRPTIME
#endif // broken strptime()
+#if defined(__MWERKS__) && wxUSE_UNICODE
+ #include <wtime.h>
+#endif
+
#if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
#if defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
#define WX_TIMEZONE _timezone
virtual void OnExit()
{
wxDateTimeHolidayAuthority::ClearAllAuthorities();
- wxDateTimeHolidayAuthority::ms_authorities.Clear();
+ wxDateTimeHolidayAuthority::ms_authorities.clear();
}
private:
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
- static wxCriticalSection gs_critsectTimezone;
-#endif // wxUSE_THREADS
-
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
return daysInMonth[wxDateTime::IsLeapYear(year)][month];
}
-// ensure that the timezone variable is set by calling localtime
+// returns the time zone in the C sense, i.e. the difference UTC - local
+// (in seconds)
static int GetTimeZone()
{
+#ifdef WX_GMTOFF_IN_TM
// set to TRUE when the timezone is set
static bool s_timezoneSet = FALSE;
-#ifdef WX_GMTOFF_IN_TM
static long gmtoffset = LONG_MAX; // invalid timezone
-#endif
-
- wxCRIT_SECT_LOCKER(lock, gs_critsectTimezone);
+ // ensure that the timezone variable is set by calling localtime
if ( !s_timezoneSet )
{
// just call localtime() instead of figuring out whether this system
// supports tzset(), _tzset() or something else
- time_t t = 0;
+ time_t t = 0;
struct tm *tm;
tm = localtime(&t);
s_timezoneSet = TRUE;
-#ifdef WX_GMTOFF_IN_TM
- gmtoffset = tm->tm_gmtoff;
-#endif
+
+ // 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;
}
-#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
- JDN_OFFSET;
}
-// this function is a wrapper around strftime(3)
+// this function is a wrapper around strftime(3) adding error checking
static wxString CallStrftime(const wxChar *format, const tm* tm)
{
wxChar buf[4096];
return wxString(buf);
}
+#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)
+{
+ // the problem here is that strptime() returns pointer into the string we
+ // passed to it while we're really interested in the pointer into the
+ // original, Unicode, string so we try to transform the pointer back
+#if wxUSE_UNICODE
+ wxCharBuffer inputMB(wxConvertWX2MB(input));
+#else // ASCII
+ const char * const inputMB = input;
+#endif // Unicode/Ascii
+
+ const char *result = strptime(inputMB, fmt, tm);
+ if ( !result )
+ return NULL;
+
+#if wxUSE_UNICODE
+ // FIXME: this is wrong in presence of surrogates &c
+ return input + (result - inputMB.data());
+#else // ASCII
+ return result;
+#endif // Unicode/Ascii
+}
+
+#endif // HAVE_STRPTIME
+
// if year and/or month have invalid values, replace them with the current ones
static void ReplaceDefaultYearMonthWithCurrent(int *year,
wxDateTime::Month *month)
{
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();
}
}
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;
// 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);
// parse the optional width
size_t width = 0;
- while ( isdigit(*++fmt) )
+ while ( wxIsdigit(*++fmt) )
{
width *= 10;
width += *fmt - _T('0');
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;
case _T('x'): // locale default date representation
#ifdef HAVE_STRPTIME
- // try using strptime() - it may fail even if the input is
+ // try using strptime() -- it may fail even if the input is
// correct but the date is out of range, so we will fall back
- // to our generic code anyhow (FIXME !Unicode friendly)
+ // to our generic code anyhow
{
struct tm tm;
- const wxChar *result = strptime(input, "%x", &tm);
+
+ const wxChar *result = CallStrptime(input, "%x", &tm);
if ( result )
{
input = result;
{
// use strptime() to do it for us (FIXME !Unicode friendly)
struct tm tm;
- input = strptime(input, "%X", &tm);
+ input = CallStrptime(input, "%X", &tm);
if ( !input )
{
return (wxChar *)NULL;
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;
- return (wxChar *)NULL;
+ // 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++;
+
+ 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)
// tokenize the string
size_t nPosCur = 0;
- static const wxChar *dateDelimiters = _T(".,/-\t\n ");
+ static const wxChar *dateDelimiters = _T(".,/-\t\r\n ");
wxStringTokenizer tok(p, dateDelimiters);
while ( tok.HasMoreTokens() )
{
}
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 )
{
+ // 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;
+ }
}
}
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;
size_t len = timeString.length();
if ( timeString.CmpNoCase(wxString(time, len)) == 0 )
{
- Set(stdTimes[n].hour, 0, 0);
+ // casts required by DigitalMars
+ Set(stdTimes[n].hour, wxDateTime_t(0), wxDateTime_t(0));
return time + len;
}
/* static */
bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime& dt)
{
- size_t count = ms_authorities.GetCount();
+ size_t count = ms_authorities.size();
for ( size_t n = 0; n < count; n++ )
{
if ( ms_authorities[n]->DoIsHoliday(dt) )
{
wxDateTimeArray hol;
- holidays.Empty();
+ holidays.Clear();
- size_t count = ms_authorities.GetCount();
+ size_t count = ms_authorities.size();
for ( size_t nAuth = 0; nAuth < count; nAuth++ )
{
ms_authorities[nAuth]->DoGetHolidaysInRange(dtStart, dtEnd, hol);
holidays.Sort(wxDateTimeCompareFunc);
- return holidays.GetCount();
+ return holidays.size();
}
/* static */
/* static */
void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority *auth)
{
- ms_authorities.Add(auth);
+ ms_authorities.push_back(auth);
+}
+
+wxDateTimeHolidayAuthority::~wxDateTimeHolidayAuthority()
+{
+ // nothing to do here
}
// ----------------------------------------------------------------------------