///////////////////////////////////////////////////////////////////////////////
-// Name: wx/datetime.h
+// Name: src/common/datetime.cpp
// Purpose: implementation of time/date related classes
// Author: Vadim Zeitlin
// Modified by:
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
-// TODO: for $DEITY sake, someone please fix the #ifdef __WXWINCE__ everywhere,
-// the proper way to do it is to implement (subset of) wxStrftime() for
-// CE instead of this horror!!
-
/*
* Implementation notes:
*
// headers
// ----------------------------------------------------------------------------
-#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
- #pragma implementation "datetime.h"
-#endif
-
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
#ifndef WX_PRECOMP
+ #ifdef __WXMSW__
+ #include "wx/msw/wrapwin.h"
+ #endif
#include "wx/string.h"
#include "wx/log.h"
+ #include "wx/intl.h"
+ #include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
+ #include "wx/module.h"
+ #include "wx/crt.h"
#endif // WX_PRECOMP
-#include "wx/intl.h"
#include "wx/thread.h"
#include "wx/tokenzr.h"
-#include "wx/module.h"
#include <ctype.h>
#ifdef __WINDOWS__
- #include "wx/msw/wrapwin.h"
#include <winnls.h>
#ifndef __WXWINCE__
#include <locale.h>
#endif
#include "wx/datetime.h"
-#include "wx/stopwatch.h" // for wxGetLocalTimeMillis()
const long wxDateTime::TIME_T_FACTOR = 1000l;
template<> void wxStringReadValue(const wxString &s , wxDateTime &data )
{
- data.ParseFormat(s,wxT("%Y-%m-%d %H:%M:%S")) ;
+ data.ParseFormat(s,"%Y-%m-%d %H:%M:%S", NULL);
}
template<> void wxStringWriteValue(wxString &s , const wxDateTime &data )
{
- s = data.Format(wxT("%Y-%m-%d %H:%M:%S")) ;
+ s = data.Format("%Y-%m-%d %H:%M:%S");
}
wxCUSTOM_TYPE_INFO(wxDateTime, wxToStringConverter<wxDateTime> , wxFromStringConverter<wxDateTime>)
-#endif
+#endif // wxUSE_EXTENDED_RTTI
//
// ----------------------------------------------------------------------------
#undef HAVE_STRPTIME
#endif // broken strptime()
+#if defined(HAVE_STRPTIME) && defined(__DARWIN__) && defined(_MSL_USING_MW_C_HEADERS) && _MSL_USING_MW_C_HEADERS
+ // configure detects strptime as linkable because it's in the OS X
+ // System library but MSL headers don't declare it.
+
+// char *strptime(const char *, const char *, struct tm *);
+ // However, we DON'T want to just provide it here because we would
+ // crash and/or overwrite data when strptime from OS X tries
+ // to fill in MW's struct tm which is two fields shorter (no TZ stuff)
+ // So for now let's just say we don't have strptime
+ #undef HAVE_STRPTIME
+#endif
+
#if defined(__MWERKS__) && wxUSE_UNICODE
#include <wtime.h>
#endif
#define WX_TIMEZONE wxGetTimeZone()
#elif defined(__DARWIN__)
#define WX_GMTOFF_IN_TM
+ #elif defined(__WXWINCE__) && defined(__VISUALC8__)
+ // _timezone is not present in dynamic run-time library
+ #if 0
+ // Solution (1): use the function equivalent of _timezone
+ static long wxGetTimeZone()
+ {
+ static long s_Timezone = MAXLONG; // invalid timezone
+ if (s_Timezone == MAXLONG)
+ {
+ int t;
+ _get_timezone(& t);
+ s_Timezone = (long) t;
+ }
+ return s_Timezone;
+ }
+ #define WX_TIMEZONE wxGetTimeZone()
+ #elif 1
+ // Solution (2): using GetTimeZoneInformation
+ static long wxGetTimeZone()
+ {
+ static long timezone = MAXLONG; // invalid timezone
+ if (timezone == MAXLONG)
+ {
+ TIME_ZONE_INFORMATION tzi;
+ ::GetTimeZoneInformation(&tzi);
+ timezone = tzi.Bias;
+ }
+ return timezone;
+ }
+ #define WX_TIMEZONE wxGetTimeZone()
+ #else
+ // Old method using _timezone: this symbol doesn't exist in the dynamic run-time library (i.e. using /MD)
+ #define WX_TIMEZONE _timezone
+ #endif
#else // unknown platform - try timezone
#define WX_TIMEZONE timezone
#endif
#endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
+// everyone has strftime except Win CE unless VC8 is used
+#if !defined(__WXWINCE__) || defined(__VISUALC8__)
+ #define HAVE_STRFTIME
+#endif
+
+// NB: VC8 safe time functions could/should be used for wxMSW as well probably
+#if defined(__WXWINCE__) && defined(__VISUALC8__)
+
+struct tm *wxLocaltime_r(const time_t *t, struct tm* tm)
+{
+ __time64_t t64 = *t;
+ return _localtime64_s(tm, &t64) == 0 ? tm : NULL;
+}
+
+struct tm *wxGmtime_r(const time_t* t, struct tm* tm)
+{
+ __time64_t t64 = *t;
+ return _gmtime64_s(tm, &t64) == 0 ? tm : NULL;
+}
+
+#else // !wxWinCE with VC8
+
+#if (!defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)) && wxUSE_THREADS && !defined(__WINDOWS__)
+static wxMutex timeLock;
+#endif
+
+#ifndef HAVE_LOCALTIME_R
+struct tm *wxLocaltime_r(const time_t* ticks, struct tm* temp)
+{
+#if wxUSE_THREADS && !defined(__WINDOWS__)
+ // No need to waste time with a mutex on windows since it's using
+ // thread local storage for localtime anyway.
+ wxMutexLocker locker(timeLock);
+#endif
+
+ // Borland CRT crashes when passed 0 ticks for some reason, see SF bug 1704438
+#ifdef __BORLANDC__
+ if ( !*ticks )
+ return NULL;
+#endif
+
+ const tm * const t = localtime(ticks);
+ if ( !t )
+ return NULL;
+
+ memcpy(temp, t, sizeof(struct tm));
+ return temp;
+}
+#endif // !HAVE_LOCALTIME_R
+
+#ifndef HAVE_GMTIME_R
+struct tm *wxGmtime_r(const time_t* ticks, struct tm* temp)
+{
+#if wxUSE_THREADS && !defined(__WINDOWS__)
+ // No need to waste time with a mutex on windows since it's
+ // using thread local storage for gmtime anyway.
+ wxMutexLocker locker(timeLock);
+#endif
+
+#ifdef __BORLANDC__
+ if ( !*ticks )
+ return NULL;
+#endif
+
+ const tm * const t = gmtime(ticks);
+ if ( !t )
+ return NULL;
+
+ memcpy(temp, gmtime(ticks), sizeof(struct tm));
+ return temp;
+}
+#endif // !HAVE_GMTIME_R
+
+#endif // wxWinCE with VC8/other platforms
+
// ----------------------------------------------------------------------------
// macros
// ----------------------------------------------------------------------------
// debugging helper: just a convenient replacement of wxCHECK()
-#define wxDATETIME_CHECK(expr, msg) \
- if ( !(expr) ) \
- { \
- wxFAIL_MSG(msg); \
- *this = wxInvalidDateTime; \
- return *this; \
- }
+#define wxDATETIME_CHECK(expr, msg) \
+ wxCHECK2_MSG(expr, *this = wxInvalidDateTime; return *this, msg)
// ----------------------------------------------------------------------------
// private classes
// (i.e. JDN(Jan 1, 1970) = 2440587.5)
static const long EPOCH_JDN = 2440587l;
+// used only in asserts
+#ifdef __WXDEBUG__
// the date of JDN -0.5 (as we don't work with fractional parts, this is the
// reference date for us) is Nov 24, 4714BC
static const int JDN_0_YEAR = -4713;
static const int JDN_0_MONTH = wxDateTime::Nov;
static const int JDN_0_DAY = 24;
+#endif // __WXDEBUG__
// the constants used for JDN calculations
static const long JDN_OFFSET = 32046l;
// global data
// ----------------------------------------------------------------------------
-const wxChar * wxDefaultDateTimeFormat = wxT("%c");
-const wxChar * wxDefaultTimeSpanFormat = wxT("%H:%M:%S");
+const char *wxDefaultDateTimeFormat = "%c";
+const char *wxDefaultTimeSpanFormat = "%H:%M:%S";
// in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
// indicate an invalid wxDateTime object
// debugger helper: shows what the date really is
#ifdef __WXDEBUG__
-extern const wxChar *wxDumpDate(const wxDateTime* dt)
+extern const char *wxDumpDate(const wxDateTime* dt)
{
- static wxChar buf[128];
+ static char buf[128];
- wxStrcpy(buf, dt->Format(_T("%Y-%m-%d (%a) %H:%M:%S")));
+ wxString fmt(dt->Format("%Y-%m-%d (%a) %H:%M:%S"));
+ wxStrncpy(buf, fmt + " (" + dt->GetValue().ToString() + " ticks)",
+ WXSIZEOF(buf));
return buf;
}
// (in seconds)
static int GetTimeZone()
{
-#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
- // ensure that the timezone variable is set by calling localtime
+ // ensure that the timezone variable is set by calling wxLocaltime_r
if ( !s_timezoneSet )
{
- // just call localtime() instead of figuring out whether this system
- // supports tzset(), _tzset() or something else
+ // just call wxLocaltime_r() instead of figuring out whether this
+ // system supports tzset(), _tzset() or something else
time_t t = 0;
- struct tm *tm;
+ struct tm tm;
- tm = localtime(&t);
+ wxLocaltime_r(&t, &tm);
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;
+ gmtoffset = -tm.tm_gmtoff;
+#else // !WX_GMTOFF_IN_TM
+ gmtoffset = WX_TIMEZONE;
+#endif // WX_GMTOFF_IN_TM/!WX_GMTOFF_IN_TM
}
return (int)gmtoffset;
-#else // !WX_GMTOFF_IN_TM
- return (int)WX_TIMEZONE;
-#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;
}
-#ifndef __WXWINCE__
+#ifdef HAVE_STRFTIME
+
// this function is a wrapper around strftime(3) adding error checking
-static wxString CallStrftime(const wxChar *format, const tm* tm)
+static wxString CallStrftime(const wxString& format, const tm* tm)
{
wxChar buf[4096];
+ // Create temp wxString here to work around mingw/cygwin bug 1046059
+ // http://sourceforge.net/tracker/?func=detail&atid=102435&aid=1046059&group_id=2435
+ wxString s;
+
if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) )
{
- // buffer is too small?
+ // if the format is valid, buffer must be too small?
wxFAIL_MSG(_T("strftime() failed"));
+
+ buf[0] = '\0';
}
- return wxString(buf);
+ s = buf;
+ return s;
}
-#endif
+
+#endif // HAVE_STRFTIME
#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)
+#if wxUSE_UNIX && !defined(HAVE_STRPTIME_DECL)
+ // configure detected that we had strptime() but not its declaration,
+ // provide it ourselves
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)
+static const wxStringCharType *
+CallStrptime(const wxStringCharType *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
+#if wxUSE_UNICODE_WCHAR
wxCharBuffer inputMB(wxConvertWX2MB(input));
#else // ASCII
const char * const inputMB = input;
if ( !result )
return NULL;
-#if wxUSE_UNICODE
+#if wxUSE_UNICODE_WCHAR
// FIXME: this is wrong in presence of surrogates &c
return input + (result - inputMB.data());
#else // ASCII
wxDateTime::Month *month)
{
struct tm *tmNow = NULL;
+ struct tm tmstruct;
if ( *year == wxDateTime::Inv_Year )
{
- tmNow = wxDateTime::GetTmNow();
+ tmNow = wxDateTime::GetTmNow(&tmstruct);
*year = 1900 + tmNow->tm_year;
}
if ( *month == wxDateTime::Inv_Month )
{
if ( !tmNow )
- tmNow = wxDateTime::GetTmNow();
+ tmNow = wxDateTime::GetTmNow(&tmstruct);
*month = (wxDateTime::Month)tmNow->tm_mon;
}
return wd;
}
+/* static */
+struct tm *wxDateTime::GetTmNow(struct tm *tmstruct)
+{
+ time_t t = GetTimeNow();
+ return wxLocaltime_r(&t, tmstruct);
+}
+
// scans all digits (but no more than len) and returns the resulting number
-static bool GetNumericToken(size_t len, const wxChar*& p, unsigned long *number)
+static bool GetNumericToken(size_t len,
+ const wxStringCharType*& p,
+ unsigned long *number)
{
size_t n = 1;
wxString s;
}
// scans all alphabetic characters and returns the resulting string
-static wxString GetAlphaToken(const wxChar*& p)
+static wxString GetAlphaToken(const wxStringCharType*& p)
{
wxString s;
while ( wxIsalpha(*p) )
// compute the week day from day/month/year: we use the dumbest algorithm
// possible: just compute our JDN and then use the (simple to derive)
// formula: weekday = (JDN + 1.5) % 7
- wday = (wxDateTime::wxDateTime_t)((wxDateTime::WeekDay)(GetTruncatedJDN(mday, mon, year) + 2) % 7);
+ wday = (wxDateTime::wxDateTime_t)((GetTruncatedJDN(mday, mon, year) + 2) % 7);
}
void wxDateTime::Tm::AddMonths(int monDiff)
case wxDateTime::GMT10:
case wxDateTime::GMT11:
case wxDateTime::GMT12:
+ case wxDateTime::GMT13:
m_offset = 3600*(tz - wxDateTime::GMT0);
break;
case wxDateTime::A_CST:
// Central Standard Time in use in Australia = UTC + 9.5
- m_offset = 60l*(9*60 + 30);
+ m_offset = 60l*(9*MIN_PER_HOUR + MIN_PER_HOUR/2);
break;
default:
wxDateTime::NameFlags flags)
{
wxCHECK_MSG( month != Inv_Month, wxEmptyString, _T("invalid month") );
-#ifndef __WXWINCE__
+#ifdef HAVE_STRFTIME
// notice that we must set all the fields to avoid confusing libc (GNU one
// gets confused to a crash if we don't do this)
tm tm;
tm.tm_mon = month;
return CallStrftime(flags == Name_Abbr ? _T("%b") : _T("%B"), &tm);
-#else
+#else // !HAVE_STRFTIME
wxString ret;
switch(month)
{
break;
}
return ret;
-#endif
+#endif // HAVE_STRFTIME/!HAVE_STRFTIME
}
/* static */
wxDateTime::NameFlags flags)
{
wxCHECK_MSG( wday != Inv_WeekDay, wxEmptyString, _T("invalid weekday") );
-#ifndef __WXWINCE__
+#ifdef HAVE_STRFTIME
// 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!)
// ... and call strftime()
return CallStrftime(flags == Name_Abbr ? _T("%a") : _T("%A"), &tm);
-#else
+#else // !HAVE_STRFTIME
wxString ret;
switch(wday)
{
break;
}
return ret;
-
-#endif
+#endif // HAVE_STRFTIME/!HAVE_STRFTIME
}
/* static */
{
// try to guess from the time zone name
time_t t = time(NULL);
- struct tm *tm = localtime(&t);
+ struct tm tmstruct;
+ struct tm *tm = wxLocaltime_r(&t, &tmstruct);
wxString tz = CallStrftime(_T("%Z"), tm);
if ( tz == _T("WET") || tz == _T("WEST") )
ms_country = USA;
}
}
-#else
+#else // __WXWINCE__
ms_country = USA;
-#endif
+#endif // !__WXWINCE__/__WXWINCE__
return ms_country;
}
}
dt += wxTimeSpan::Hours(1);
-
- // disable DST tests because it could result in an infinite recursion!
- dt.MakeGMT(true);
}
else switch ( country )
{
}
dt += wxTimeSpan::Hours(1);
-
- // disable DST tests because it could result in an infinite recursion!
- dt.MakeGMT(true);
}
else switch ( country )
{
_T("Invalid time in wxDateTime::Set()") );
// get the current date from system
- struct tm *tm = GetTmNow();
+ struct tm tmstruct;
+ struct tm *tm = GetTmNow(&tmstruct);
- wxDATETIME_CHECK( tm, _T("localtime() failed") );
+ wxDATETIME_CHECK( tm, _T("wxLocaltime_r() failed") );
// make a copy so it isn't clobbered by the call to mktime() below
struct tm tm1(*tm);
// EPOCH_JDN + 0.5
jdn -= EPOCH_JDN + 0.5;
- jdn *= MILLISECONDS_PER_DAY;
-
- m_time.Assign(jdn);
-
- // 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;
- }
+ m_time.Assign(jdn*MILLISECONDS_PER_DAY);
- m_time += tzDiff*1000; // tzDiff is in seconds
+ // JDNs always are in UTC, so we don't need any adjustments for time zone
return *this;
}
return *this;
}
+wxDateTime wxDateTime::GetDateOnly() const
+{
+ Tm tm = GetTm();
+ tm.msec =
+ tm.sec =
+ tm.min =
+ tm.hour = 0;
+ return wxDateTime(tm);
+}
+
// ----------------------------------------------------------------------------
// DOS Date and Time Format functions
// ----------------------------------------------------------------------------
{
unsigned long ddt;
time_t ticks = GetTicks();
- struct tm *tm = localtime(&ticks);
+ struct tm tmstruct;
+ struct tm *tm = wxLocaltime_r(&ticks, &tmstruct);
+ wxCHECK_MSG( tm, ULONG_MAX, _T("time can't be represented in DOS format") );
long year = tm->tm_year;
year -= 80;
if ( time != (time_t)-1 )
{
// use C RTL functions
+ struct tm tmstruct;
tm *tm;
if ( tz.GetOffset() == -GetTimeZone() )
{
// we are working with local time
- tm = localtime(&time);
+ tm = wxLocaltime_r(&time, &tmstruct);
// should never happen
- wxCHECK_MSG( tm, Tm(), _T("localtime() failed") );
+ wxCHECK_MSG( tm, Tm(), _T("wxLocaltime_r() failed") );
}
else
{
if ( time >= 0 )
#endif
{
- tm = gmtime(&time);
+ tm = wxGmtime_r(&time, &tmstruct);
// should never happen
- wxCHECK_MSG( tm, Tm(), _T("gmtime() failed") );
+ wxCHECK_MSG( tm, Tm(), _T("wxGmtime_r() failed") );
}
else
{
timeOnly -= tm.msec;
timeOnly /= 1000; // now we have time in seconds
- tm.sec = (wxDateTime_t)(timeOnly % 60);
+ tm.sec = (wxDateTime_t)(timeOnly % SEC_PER_MIN);
timeOnly -= tm.sec;
- timeOnly /= 60; // now we have time in minutes
+ timeOnly /= SEC_PER_MIN; // now we have time in minutes
- tm.min = (wxDateTime_t)(timeOnly % 60);
+ tm.min = (wxDateTime_t)(timeOnly % MIN_PER_HOUR);
timeOnly -= tm.min;
- tm.hour = (wxDateTime_t)(timeOnly / 60);
+ tm.hour = (wxDateTime_t)(timeOnly / MIN_PER_HOUR);
return tm;
}
return dt;
}
+#if WXWIN_COMPATIBILITY_2_6
// use a separate function to avoid warnings about using deprecated
// SetToTheWeek in GetWeek below
static wxDateTime
{
return ::SetToTheWeek(GetYear(), numWeek, weekday, flags);
}
+#endif // WXWIN_COMPATIBILITY_2_6
wxDateTime& wxDateTime::SetToLastMonthDay(Month month,
int year)
double wxDateTime::GetJulianDayNumber() const
{
- // JDN are always expressed for the UTC dates
- Tm tm(ToTimezone(UTC).GetTm(UTC));
-
- double result = GetTruncatedJDN(tm.mday, tm.mon, tm.year);
-
- // add the part GetTruncatedJDN() neglected
- result += 0.5;
-
- // and now add the time: 86400 sec = 1 JDN
- return result + ((double)(60*(60*tm.hour + tm.min) + tm.sec)) / 86400;
+ return m_time.ToDouble() / MILLISECONDS_PER_DAY + EPOCH_JDN + 0.5;
}
double wxDateTime::GetRataDie() const
time_t timet = GetTicks();
if ( timet != (time_t)-1 )
{
- tm *tm = localtime(&timet);
+ struct tm tmstruct;
+ tm *tm = wxLocaltime_r(&timet, &tmstruct);
- wxCHECK_MSG( tm, -1, _T("localtime() failed") );
+ wxCHECK_MSG( tm, -1, _T("wxLocaltime_r() failed") );
return tm->tm_isdst;
}
secDiff -= 3600;
}
+ return Add(wxTimeSpan::Seconds(secDiff));
+}
+
+wxDateTime& wxDateTime::MakeFromTimezone(const TimeZone& tz, bool noDST)
+{
+ long secDiff = GetTimeZone() + tz.GetOffset();
+
+ // 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;
+ }
+
return Subtract(wxTimeSpan::Seconds(secDiff));
}
// wxDateTime to/from text representations
// ----------------------------------------------------------------------------
-wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
+wxString wxDateTime::Format(const wxString& format, const TimeZone& tz) const
{
- wxCHECK_MSG( format, wxEmptyString, _T("NULL format in wxDateTime::Format") );
+ wxCHECK_MSG( !format.empty(), wxEmptyString,
+ _T("NULL format in wxDateTime::Format") );
// we have to use our own implementation if the date is out of range of
// strftime() or if we use non standard specificators
+#ifdef HAVE_STRFTIME
time_t time = GetTicks();
+
if ( (time != (time_t)-1) && !wxStrstr(format, _T("%l")) )
{
// use strftime()
- tm *tm;
+ struct tm tmstruct;
+ struct tm *tm;
if ( tz.GetOffset() == -GetTimeZone() )
{
// we are working with local time
- tm = localtime(&time);
+ tm = wxLocaltime_r(&time, &tmstruct);
// should never happen
- wxCHECK_MSG( tm, wxEmptyString, _T("localtime() failed") );
+ wxCHECK_MSG( tm, wxEmptyString, _T("wxLocaltime_r() failed") );
}
else
{
if ( time >= 0 )
#endif
{
- tm = gmtime(&time);
+ tm = wxGmtime_r(&time, &tmstruct);
// should never happen
- wxCHECK_MSG( tm, wxEmptyString, _T("gmtime() failed") );
+ wxCHECK_MSG( tm, wxEmptyString, _T("wxGmtime_r() failed") );
}
else
{
tm = (struct tm *)NULL;
}
}
-#ifndef __WXWINCE__
- //Windows CE doesn't support strftime or wcsftime, so we use the generic implementation
+
if ( tm )
{
return CallStrftime(format, tm);
}
-#endif
- //else: use generic code below
}
+ //else: use generic code below
+#endif // HAVE_STRFTIME
// we only parse ANSI C format specifications here, no POSIX 2
// complications, no GNU extensions but we do add support for a "%l" format
tmTimeOnly.tm_isdst = 0; // no DST, we adjust for tz ourselves
wxString tmp, res, fmt;
- for ( const wxChar *p = format; *p; p++ )
+ for ( wxString::const_iterator p = format.begin(); p != format.end(); ++p )
{
if ( *p != _T('%') )
{
}
// set the default format
- switch ( *++p )
+ switch ( (*++p).GetValue() )
{
case _T('Y'): // year has 4 digits
fmt = _T("%04d");
restart = false;
// start of the format specification
- switch ( *p )
+ switch ( (*p).GetValue() )
{
case _T('a'): // a weekday name
case _T('A'):
case _T('c'): // locale default date and time representation
case _T('x'): // locale default date representation
-#ifndef __WXWINCE__
+#ifdef HAVE_STRFTIME
//
// the problem: there is no way to know what do these format
// specifications correspond to for the current locale.
// 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
+ // replace all occurrences 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!
+ // string back with YEAR. Notice that "all occurrences of YEAR"
+ // means all occurrences 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
// 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
+ // find the YEAR: normally, for any year X, Jan 1 of 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
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") );
-
- wxString strYear, strYear2;
- strYear.Printf(_T("%d"), year);
- strYear2.Printf(_T("%d"), year % 100);
+ // finally move the year below 2000 so that the 2-digit
+ // year number can never match the month or day of the
+ // month when we do the replacements below
+ if ( year >= 2000 )
+ year -= 28;
- // 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;
- }
+ wxASSERT_MSG( year >= 1970 && year < 2000,
+ _T("logic error in wxDateTime::Format") );
- // 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
: _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);
- }
+ // now replace the replacement year with the real year:
+ // notice that we have to replace the 4 digit year with
+ // a unique string not appearing in strftime() output
+ // first to prevent the 2 digit year from matching any
+ // substring of the 4 digit year (but any day, month,
+ // hours or minutes components should be safe because
+ // they are never in 70-99 range)
+ wxString replacement("|");
+ while ( str.find(replacement) != wxString::npos )
+ replacement += '|';
+
+ str.Replace(wxString::Format("%d", year),
+ replacement);
+ str.Replace(wxString::Format("%d", year % 100),
+ wxString::Format("%d", yearReal % 100));
+ str.Replace(replacement,
+ wxString::Format("%d", yearReal));
res += str;
}
-#else
- //Use "%m/%d/%y %H:%M:%S" format instead
+#else // !HAVE_STRFTIME
+ // Use "%m/%d/%y %H:%M:%S" format instead
res += wxString::Format(wxT("%02d/%02d/%04d %02d:%02d:%02d"),
tm.mon+1,tm.mday, tm.year, tm.hour, tm.min, tm.sec);
-#endif
+#endif // HAVE_STRFTIME/!HAVE_STRFTIME
break;
case _T('d'): // day of a month (01-31)
break;
case _T('p'): // AM or PM string
-#ifndef __WXWINCE__
+#ifdef HAVE_STRFTIME
res += CallStrftime(_T("%p"), &tmTimeOnly);
-#else
+#else // !HAVE_STRFTIME
res += (tmTimeOnly.tm_hour > 12) ? wxT("pm") : wxT("am");
-#endif
+#endif // HAVE_STRFTIME/!HAVE_STRFTIME
break;
case _T('S'): // second as a decimal number (00-61)
case _T('X'): // locale default time representation
// just use strftime() to format the time for us
-#ifndef __WXWINCE__
+#ifdef HAVE_STRFTIME
res += CallStrftime(_T("%X"), &tmTimeOnly);
-#else
+#else // !HAVE_STRFTIME
res += wxString::Format(wxT("%02d:%02d:%02d"),tm.hour, tm.min, tm.sec);
-#endif
+#endif // HAVE_STRFTIME/!HAVE_STRFTIME
break;
case _T('y'): // year without century (00-99)
break;
case _T('Z'): // timezone name
-#ifndef __WXWINCE__
+#ifdef HAVE_STRFTIME
res += CallStrftime(_T("%Z"), &tmTimeOnly);
#endif
break;
// RFC822 time specs.
//
// TODO a great candidate for using reg exps
-const wxChar *wxDateTime::ParseRfc822Date(const wxChar* date)
+const char *
+wxDateTime::ParseRfc822Date(const wxString& date, wxString::const_iterator *end)
{
- wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
-
- const wxChar *p = date;
- const wxChar *comma = wxStrchr(p, _T(','));
+ // TODO: rewrite using iterators instead of wxChar pointers
+ const wxStringCharType *p = date.wx_str();
+ const wxStringCharType *comma = wxStrchr(p, wxS(','));
if ( comma )
{
// the part before comma is the weekday
{
wxLogDebug(_T("no space after weekday in RFC822 time spec"));
- return (wxChar *)NULL;
+ return NULL;
}
p++; // skip space
{
wxLogDebug(_T("day number expected in RFC822 time spec, none found"));
- return (wxChar *)NULL;
+ return NULL;
}
wxDateTime_t day = (wxDateTime_t)(*p++ - _T('0'));
if ( *p++ != _T(' ') )
{
- return (wxChar *)NULL;
+ return NULL;
}
// the following 3 letters specify the month
{
wxLogDebug(_T("Invalid RFC 822 month name '%s'"), monName.c_str());
- return (wxChar *)NULL;
+ return NULL;
}
p += 3;
if ( *p++ != _T(' ') )
{
- return (wxChar *)NULL;
+ return NULL;
}
// next is the year
if ( !wxIsdigit(*p) )
{
// no year?
- return (wxChar *)NULL;
+ return NULL;
}
int year = *p++ - _T('0');
if ( !wxIsdigit(*p) )
{
// should have at least 2 digits in the year
- return (wxChar *)NULL;
+ return NULL;
}
year *= 10;
if ( !wxIsdigit(*p) )
{
// no 3 digit years please
- return (wxChar *)NULL;
+ return NULL;
}
year *= 10;
if ( *p++ != _T(' ') )
{
- return (wxChar *)NULL;
+ return NULL;
}
// time is in the format hh:mm:ss and seconds are optional
if ( !wxIsdigit(*p) )
{
- return (wxChar *)NULL;
+ return NULL;
}
wxDateTime_t hour = (wxDateTime_t)(*p++ - _T('0'));
if ( !wxIsdigit(*p) )
{
- return (wxChar *)NULL;
+ return NULL;
}
hour *= 10;
if ( *p++ != _T(':') )
{
- return (wxChar *)NULL;
+ return NULL;
}
if ( !wxIsdigit(*p) )
{
- return (wxChar *)NULL;
+ return NULL;
}
wxDateTime_t min = (wxDateTime_t)(*p++ - _T('0'));
if ( !wxIsdigit(*p) )
{
- return (wxChar *)NULL;
+ return NULL;
}
min *= 10;
min = (wxDateTime_t)(min + *p++ - _T('0'));
wxDateTime_t sec = 0;
- if ( *p++ == _T(':') )
+ if ( *p == _T(':') )
{
+ p++;
if ( !wxIsdigit(*p) )
{
- return (wxChar *)NULL;
+ return NULL;
}
sec = (wxDateTime_t)(*p++ - _T('0'));
if ( !wxIsdigit(*p) )
{
- return (wxChar *)NULL;
+ return NULL;
}
sec *= 10;
if ( *p++ != _T(' ') )
{
- return (wxChar *)NULL;
+ return NULL;
}
// and now the interesting part: the timezone
if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) )
{
- return (wxChar *)NULL;
+ return NULL;
}
// hours
- offset = 60*(10*(*p - _T('0')) + (*(p + 1) - _T('0')));
+ offset = MIN_PER_HOUR*(10*(*p - _T('0')) + (*(p + 1) - _T('0')));
p += 2;
if ( !wxIsdigit(*p) || !wxIsdigit(*(p + 1)) )
{
- return (wxChar *)NULL;
+ return NULL;
}
// minutes
{
wxLogDebug(_T("Invalid militaty timezone '%c'"), *p);
- return (wxChar *)NULL;
+ return NULL;
}
offset = offsets[*p++ - _T('A')];
{
wxLogDebug(_T("Unknown RFC 822 timezone '%s'"), p);
- return (wxChar *)NULL;
+ return NULL;
}
p += tz.length();
}
// make it minutes
- offset *= 60;
+ offset *= MIN_PER_HOUR;
}
- // the spec was correct
+ // the spec was correct, construct the date from the values we found
Set(day, mon, year, hour, min, sec);
- MakeTimezone((wxDateTime_t)(60*offset));
+ MakeFromTimezone(TimeZone::Make(offset*SEC_PER_MIN));
- return p;
+ const size_t endpos = p - date.wx_str();
+ if ( end )
+ *end = date.begin() + endpos;
+
+ return date.c_str() + endpos;
}
#ifdef __WINDOWS__
-// Get's current locale's date formatting string and stores it in fmt if
-// the locale is set; otherwise or in case of failure, leaves fmt unchanged
-static void GetLocaleDateFormat(wxString *fmt)
+// returns the string containing strftime() format used for short dates in the
+// current locale or an empty string
+static wxString GetLocaleDateFormat()
{
+ wxString fmtWX;
+
// there is no setlocale() under Windows CE, so just always query the
// system there
#ifndef __WXWINCE__
#else
LCID lcid = GetThreadLocale();
#endif
- wxChar delim[5]; // fields deliminer, 4 chars max
- if ( GetLocaleInfo(lcid, LOCALE_SDATE, delim, 5) )
+ // according to MSDN 80 chars is max allowed for short date format
+ wxChar fmt[81];
+ if ( ::GetLocaleInfo(lcid, LOCALE_SSHORTDATE, fmt, WXSIZEOF(fmt)) )
{
- wxChar centurybuf[2]; // use %y or %Y, 1 char max
- wxChar century = 'y';
- if ( GetLocaleInfo(lcid, LOCALE_ICENTURY, centurybuf, 2) )
+ wxChar chLast = _T('\0');
+ size_t lastCount = 0;
+ for ( const wxChar *p = fmt; /* NUL handled inside */; p++ )
{
- if ( centurybuf[0] == _T('1') )
- century = 'Y';
- // else 'y' as above
- }
-
- wxChar order[2]; // order code, 1 char max
- if ( GetLocaleInfo(lcid, LOCALE_IDATE, order, 2) )
- {
- if ( order[0] == _T('0') ) // M-D-Y
- {
- *fmt = wxString::Format(_T("%%m%s%%d%s%%%c"),
- delim, delim, century);
- }
- else if ( order[0] == _T('1') ) // D-M-Y
+ if ( *p == chLast )
{
- *fmt = wxString::Format(_T("%%d%s%%m%s%%%c"),
- delim, delim, century);
- }
- else if ( order[0] == _T('2') ) // Y-M-D
- {
- *fmt = wxString::Format(_T("%%%c%s%%m%s%%d"),
- century, delim, delim);
+ lastCount++;
+ continue;
}
- else
+
+ switch ( *p )
{
- wxFAIL_MSG(_T("unexpected GetLocaleInfo return value"));
+ // these characters come in groups, start counting them
+ case _T('d'):
+ case _T('M'):
+ case _T('y'):
+ case _T('g'):
+ chLast = *p;
+ lastCount = 1;
+ break;
+
+ default:
+ // first deal with any special characters we have had
+ if ( lastCount )
+ {
+ switch ( chLast )
+ {
+ case _T('d'):
+ switch ( lastCount )
+ {
+ case 1: // d
+ case 2: // dd
+ // these two are the same as we
+ // don't distinguish between 1 and
+ // 2 digits for days
+ fmtWX += _T("%d");
+ break;
+
+ case 3: // ddd
+ fmtWX += _T("%a");
+ break;
+
+ case 4: // dddd
+ fmtWX += _T("%A");
+ break;
+
+ default:
+ wxFAIL_MSG( _T("too many 'd's") );
+ }
+ break;
+
+ case _T('M'):
+ switch ( lastCount )
+ {
+ case 1: // M
+ case 2: // MM
+ // as for 'd' and 'dd' above
+ fmtWX += _T("%m");
+ break;
+
+ case 3:
+ fmtWX += _T("%b");
+ break;
+
+ case 4:
+ fmtWX += _T("%B");
+ break;
+
+ default:
+ wxFAIL_MSG( _T("too many 'M's") );
+ }
+ break;
+
+ case _T('y'):
+ switch ( lastCount )
+ {
+ case 1: // y
+ case 2: // yy
+ fmtWX += _T("%y");
+ break;
+
+ case 4: // yyyy
+ fmtWX += _T("%Y");
+ break;
+
+ default:
+ wxFAIL_MSG( _T("wrong number of 'y's") );
+ }
+ break;
+
+ case _T('g'):
+ // strftime() doesn't have era string,
+ // ignore this format
+ wxASSERT_MSG( lastCount <= 2,
+ _T("too many 'g's") );
+ break;
+
+ default:
+ wxFAIL_MSG( _T("unreachable") );
+ }
+
+ chLast = _T('\0');
+ lastCount = 0;
+ }
+
+ // not a special character so must be just a separator,
+ // treat as is
+ if ( *p != _T('\0') )
+ {
+ if ( *p == _T('%') )
+ {
+ // this one needs to be escaped
+ fmtWX += _T('%');
+ }
+
+ fmtWX += *p;
+ }
}
+
+ if ( *p == _T('\0') )
+ break;
}
}
- // if we failed, leave fmtDate value unchanged and
- // try our luck with the default set above
+ //else: GetLocaleInfo() failed, leave fmtDate value unchanged and
+ // try our luck with the default formats
}
+ //else: default C locale, default formats should work
+
+ return fmtWX;
}
#endif // __WINDOWS__
-const wxChar *wxDateTime::ParseFormat(const wxChar *date,
- const wxChar *format,
- const wxDateTime& dateDef)
+const char *
+wxDateTime::ParseFormat(const wxString& date,
+ const wxString& format,
+ const wxDateTime& dateDef,
+ wxString::const_iterator *end)
{
- wxCHECK_MSG( date && format, (wxChar *)NULL,
- _T("NULL pointer in wxDateTime::ParseFormat()") );
+ wxCHECK_MSG( !format.empty(), NULL, "format can't be empty" );
wxString str;
unsigned long num;
wxDateTime::Month mon = Inv_Month;
int year = 0;
- const wxChar *input = date;
- for ( const wxChar *fmt = format; *fmt; fmt++ )
+ const wxStringCharType *input = date.wx_str();
+ for ( wxString::const_iterator fmt = format.begin(); fmt != format.end(); ++fmt )
{
if ( *fmt != _T('%') )
{
if ( *input++ != *fmt )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
}
// the default widths for the various fields
if ( !width )
{
- switch ( *fmt )
+ switch ( (*fmt).GetValue() )
{
case _T('Y'): // year has 4 digits
width = 4;
}
// then the format itself
- switch ( *fmt )
+ switch ( (*fmt).GetValue() )
{
case _T('a'): // a weekday name
case _T('A'):
if ( wday == Inv_WeekDay )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
}
haveWDay = true;
if ( mon == Inv_Month )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
}
haveMon = true;
{
wxDateTime dt;
- // 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 %d %H:%M:%S %Y");
-
- const wxChar *result = dt.ParseFormat(input, fmtCtime);
- if ( !result )
- {
- result = dt.ParseFormat(input, _T("%x %X"));
- }
-
- if ( !result )
- {
- result = dt.ParseFormat(input, _T("%X %x"));
- }
+ const wxString inc(input);
- if ( !result )
+ // try the format which corresponds to ctime() output first
+ wxString::const_iterator endc;
+ if ( !dt.ParseFormat(inc, wxS("%a %b %d %H:%M:%S %Y"), &endc) &&
+ !dt.ParseFormat(inc, wxS("%x %X"), &endc) &&
+ !dt.ParseFormat(inc, wxS("%X %x"), &endc) )
{
// we've tried everything and still no match
- return (wxChar *)NULL;
+ return NULL;
}
Tm tm = dt.GetTm();
mon = tm.mon;
mday = tm.mday;
- input = result;
+ input += endc - inc.begin();
}
break;
(num > 31) || (num < 1) )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
// we can't check whether the day range is correct yet, will
if ( !GetNumericToken(width, input, &num) || (num > 23) )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
haveHour = true;
if ( !GetNumericToken(width, input, &num) || !num || (num > 12) )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
haveHour = true;
if ( !GetNumericToken(width, input, &num) || !num || (num > 366) )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
haveYDay = true;
if ( !GetNumericToken(width, input, &num) || !num || (num > 12) )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
haveMon = true;
if ( !GetNumericToken(width, input, &num) || (num > 59) )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
haveMin = true;
GetAmPmStrings(&am, &pm);
if (am.empty() && pm.empty())
- return (wxChar *)NULL; // no am/pm strings defined
+ return NULL; // no am/pm strings defined
if ( token.CmpNoCase(pm) == 0 )
{
isPM = true;
else if ( token.CmpNoCase(am) != 0 )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
}
break;
case _T('r'): // time as %I:%M:%S %p
{
wxDateTime dt;
- input = dt.ParseFormat(input, _T("%I:%M:%S %p"));
+ input = dt.ParseFormat(input, wxS("%I:%M:%S %p"));
if ( !input )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
haveHour = haveMin = haveSec = true;
case _T('R'): // time as %H:%M
{
wxDateTime dt;
- input = dt.ParseFormat(input, _T("%H:%M"));
+ input = dt.ParseFormat(input, wxS("%H:%M"));
if ( !input )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
haveHour = haveMin = true;
hour = tm.hour;
min = tm.min;
}
+ break;
case _T('S'): // second as a decimal number (00-61)
if ( !GetNumericToken(width, input, &num) || (num > 61) )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
haveSec = true;
if ( !input )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
haveHour = haveMin = haveSec = true;
if ( !GetNumericToken(width, input, &num) || (wday > 6) )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
haveWDay = true;
{
struct tm tm;
- const wxChar *result = CallStrptime(input, "%x", &tm);
+ const wxStringCharType *
+ result = CallStrptime(input, "%x", &tm);
if ( result )
{
input = result;
{
wxDateTime dt;
-
- 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");
- }
+ wxString fmtDate,
+ fmtDateAlt;
#ifdef __WINDOWS__
// The above doesn't work for all locales, try to query
// Windows for the right way of formatting the date:
- GetLocaleDateFormat(&fmtDate);
-#endif
-
- const wxChar *result = dt.ParseFormat(input, fmtDate);
-
- if ( !result )
+ fmtDate = GetLocaleDateFormat();
+ if ( fmtDate.empty() )
+#endif // __WINDOWS__
{
- // ok, be nice and try another one
- result = dt.ParseFormat(input, 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");
+ }
}
- if ( !result )
+ const wxString indate(input);
+ wxString::const_iterator endDate;
+ if ( !dt.ParseFormat(indate, fmtDate, &endDate) )
{
- // bad luck
- return (wxChar *)NULL;
+ // try another one if we have it
+ if ( fmtDateAlt.empty() ||
+ !dt.ParseFormat(indate, fmtDateAlt, &endDate) )
+ {
+ return NULL;
+ }
}
Tm tm = dt.GetTm();
- haveDay = haveMon = haveYear = true;
+ haveDay =
+ haveMon =
+ haveYear = true;
year = tm.year;
mon = tm.mon;
mday = tm.mday;
- input = result;
+ input += endDate - indate.begin();
}
break;
input = CallStrptime(input, "%X", &tm);
if ( !input )
{
- return (wxChar *)NULL;
+ return NULL;
}
haveHour = haveMin = haveSec = true;
// common cases
wxDateTime dt;
- const wxChar *result = dt.ParseFormat(input, _T("%T"));
+ const wxStringCharType *
+ result = dt.ParseFormat(input, wxS("%T"));
if ( !result )
{
- result = dt.ParseFormat(input, _T("%r"));
+ result = dt.ParseFormat(input, wxS("%r"));
}
if ( !result )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
- haveHour = haveMin = haveSec = true;
+ haveHour =
+ haveMin =
+ haveSec = true;
Tm tm = dt.GetTm();
hour = tm.hour;
if ( !GetNumericToken(width, input, &num) || (num > 99) )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
haveYear = true;
if ( !GetNumericToken(width, input, &num) )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
haveYear = true;
if ( *input++ != _T('%') )
{
// no match
- return (wxChar *)NULL;
+ return NULL;
}
break;
// fall through
default: // not a known format spec
- return (wxChar *)NULL;
+ return NULL;
}
}
Tm tm = tmDef;
// set the date
+ if ( haveMon )
+ {
+ tm.mon = mon;
+ }
+
if ( haveYear )
{
tm.year = year;
// TODO we don't check here that the values are consistent, if both year
// day and month/day were found, we just ignore the year day and we
// also always ignore the week day
- if ( haveMon && haveDay )
+ if ( haveDay )
{
- if ( mday > GetNumOfDaysInMonth(tm.year, mon) )
+ if ( mday > GetNumOfDaysInMonth(tm.year, tm.mon) )
{
wxLogDebug(_T("bad month day in wxDateTime::ParseFormat"));
- return (wxChar *)NULL;
+ return NULL;
}
- tm.mon = mon;
tm.mday = mday;
}
else if ( haveYDay )
{
wxLogDebug(_T("bad year day in wxDateTime::ParseFormat"));
- return (wxChar *)NULL;
+ return NULL;
}
Tm tm2 = wxDateTime(1, Jan, tm.year).SetToYearDay(yday).GetTm();
return NULL;
}
- return input;
+ const size_t endpos = input - date.wx_str();
+ if ( end )
+ *end = date.begin() + endpos;
+
+ return date.c_str() + endpos;
}
-const wxChar *wxDateTime::ParseDateTime(const wxChar *date)
+const char *
+wxDateTime::ParseDateTime(const wxString& date, wxString::const_iterator *end)
{
- wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
-
// 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;
+ wxString::const_iterator
+ endTime,
+ endDate,
+ endBoth;
- // 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 )
+ // If we got a date in the beginning, see if there is a time specified
+ // after the date
+ if ( dtDate.ParseDate(date, &endDate) )
{
// Skip spaces, as the ParseTime() function fails on spaces
- while ( wxIsspace(*pchDate) )
- pchDate++;
+ while ( endDate != date.end() && wxIsspace(*endDate) )
+ ++endDate;
+
+ const wxString timestr(endDate, date.end());
+ if ( !dtTime.ParseTime(timestr, &endTime) )
+ return NULL;
- pchTime = dtTime.ParseTime(pchDate);
+ endBoth = endDate + (endTime - timestr.begin());
}
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++;
+ // check if we have a time followed by a date
+ if ( !dtTime.ParseTime(date, &endTime) )
+ return NULL;
- pchDate = dtDate.ParseDate(pchTime);
- }
- }
+ while ( endTime != date.end() && wxIsspace(*endTime) )
+ ++endTime;
- // If we have a date specified, set our own data to the same date
- if ( !pchDate || !pchTime )
- return NULL;
+ const wxString datestr(endTime, date.end());
+ if ( !dtDate.ParseDate(datestr, &endDate) )
+ return NULL;
+
+ endBoth = endTime + (endDate - datestr.begin());
+ }
Set(dtDate.GetDay(), dtDate.GetMonth(), dtDate.GetYear(),
dtTime.GetHour(), dtTime.GetMinute(), dtTime.GetSecond(),
dtTime.GetMillisecond());
// Return endpoint of scan
- return pchDate > pchTime ? pchDate : pchTime;
+ if ( end )
+ *end = endBoth;
+
+ return date.c_str() + (endBoth - date.begin());
}
-const wxChar *wxDateTime::ParseDate(const wxChar *date)
+const char *
+wxDateTime::ParseDate(const wxString& date, wxString::const_iterator *end)
{
// this is a simplified version of ParseDateTime() which understands only
// "today" (for wxDate compatibility) and digits only otherwise (and not
// all esoteric constructions ParseDateTime() knows about)
- wxCHECK_MSG( date, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
-
- const wxChar *p = date;
+ const wxStringCharType *p = date.wx_str();
while ( wxIsspace(*p) )
p++;
// some special cases
static struct
{
- const wxChar *str;
+ const char *str;
int dayDiffFromToday;
} literalDates[] =
{
for ( size_t n = 0; n < WXSIZEOF(literalDates); n++ )
{
- wxString date = wxGetTranslation(literalDates[n].str);
- size_t len = date.length();
+ const wxString dateStr = wxGetTranslation(literalDates[n].str);
+ size_t len = dateStr.length();
if ( wxStrlen(p) >= len )
{
wxString str(p, len);
- if ( str.CmpNoCase(date) == 0 )
+ if ( str.CmpNoCase(dateStr) == 0 )
{
// nothing can follow this, so stop here
p += len;
*this += wxDateSpan::Days(dayDiffFromToday);
}
- return p;
+ const size_t endpos = p - date.wx_str();
+
+ if ( end )
+ *end = date.begin() + endpos;
+ return date.c_str() + endpos;
}
}
}
// tokenize the string
size_t nPosCur = 0;
- static const wxChar *dateDelimiters = _T(".,/-\t\r\n ");
+ static const wxStringCharType *dateDelimiters = wxS(".,/-\t\r\n ");
wxStringTokenizer tok(p, dateDelimiters);
while ( tok.HasMoreTokens() )
{
}
else // may be either day or year
{
+ // use a leap year if we don't have the year yet to allow
+ // dates like 2/29/1976 which would be rejected otherwise
wxDateTime_t max_days = (wxDateTime_t)(
haveMon
- ? GetNumOfDaysInMonth(haveYear ? year : Inv_Year, mon)
+ ? GetNumOfDaysInMonth(haveYear ? year : 1976, mon)
: 31
);
}
else // not a valid month name
{
- wday = GetWeekDayFromName(token, Name_Full | Name_Abbr);
- if ( wday != Inv_WeekDay )
+ WeekDay wday2 = GetWeekDayFromName(token, Name_Full | Name_Abbr);
+ if ( wday2 != Inv_WeekDay )
{
// a week day
if ( haveWDay )
break;
}
+ wday = wday2;
+
haveWDay = true;
}
else // not a valid weekday name
{
// try the ordinals
- static const wxChar *ordinals[] =
+ static const char *ordinals[] =
{
wxTRANSLATE("first"),
wxTRANSLATE("second"),
{
wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
- return (wxChar *)NULL;
+ return NULL;
}
if ( haveWDay && (haveMon || haveYear || haveDay) &&
// without adjectives (which we don't support here) the week day only
// makes sense completely separately or with the full date
// specification (what would "Wed 1999" mean?)
- return (wxChar *)NULL;
+ return NULL;
}
if ( !haveWDay && haveYear && !(haveDay && haveMon) )
// if we give the year, month and day must be given too
wxLogDebug(_T("ParseDate: day and month should be specified if year is."));
- return (wxChar *)NULL;
+ return NULL;
}
}
if ( haveDay )
{
+ // normally we check the day above but the check is optimistic in case
+ // we find the day before its month/year so we have to redo it now
+ if ( day > GetNumOfDaysInMonth(year, mon) )
+ return NULL;
+
Set(day, mon, year);
if ( haveWDay )
// inconsistency detected
wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
- return (wxChar *)NULL;
+ return NULL;
}
}
}
p--;
}
- return p;
+ const size_t endpos = p - date.wx_str();
+ if ( end )
+ *end = date.begin() + endpos;
+
+ return date.c_str() + endpos;
}
-const wxChar *wxDateTime::ParseTime(const wxChar *time)
+const char *
+wxDateTime::ParseTime(const wxString& time, wxString::const_iterator *end)
{
- wxCHECK_MSG( time, (wxChar *)NULL, _T("NULL pointer in wxDateTime::Parse") );
-
// first try some extra things
static const struct
{
- const wxChar *name;
+ const char *name;
wxDateTime_t hour;
} stdTimes[] =
{
// casts required by DigitalMars
Set(stdTimes[n].hour, wxDateTime_t(0), wxDateTime_t(0));
- return time + len;
+ if ( end )
+ *end = time.begin() + len;
+
+ return time.c_str() + len;
}
}
// try all time formats we may think about in the order from longest to
// shortest
-
- // 12hour with AM/PM?
- const wxChar *result = ParseFormat(time, _T("%I:%M:%S %p"));
-
- if ( !result )
- {
- // normally, it's the same, but why not try it?
- result = ParseFormat(time, _T("%H:%M:%S"));
- }
-
- if ( !result )
+ static const char *timeFormats[] =
{
- // 12hour with AM/PM but without seconds?
- result = ParseFormat(time, _T("%I:%M %p"));
- }
+ "%I:%M:%S %p", // 12hour with AM/PM
+ "%H:%M:%S", // could be the same or 24 hour one so try it too
+ "%I:%M %p", // 12hour with AM/PM but without seconds
+ "%H:%M:%S", // and a possibly 24 hour version without seconds
+ "%X", // possibly something from above or maybe something
+ // completely different -- try it last
- if ( !result )
- {
- // without seconds?
- result = ParseFormat(time, _T("%H:%M"));
- }
-
- if ( !result )
- {
- // just the hour and AM/PM?
- result = ParseFormat(time, _T("%I %p"));
- }
-
- if ( !result )
- {
- // just the hour?
- result = ParseFormat(time, _T("%H"));
- }
+ // TODO: parse timezones
+ };
- if ( !result )
+ for ( size_t nFmt = 0; nFmt < WXSIZEOF(timeFormats); nFmt++ )
{
- // parse the standard format: normally it is one of the formats above
- // but it may be set to something completely different by the user
- result = ParseFormat(time, _T("%X"));
+ const char *result = ParseFormat(time, timeFormats[nFmt], end);
+ if ( result )
+ return result;
}
- // TODO: parse timezones
-
- return result;
+ return NULL;
}
// ----------------------------------------------------------------------------
// And, to be better than MFC :-), we also have
// %E number of wEeks
// %l milliseconds (000 - 999)
-wxString wxTimeSpan::Format(const wxChar *format) const
+wxString wxTimeSpan::Format(const wxString& format) const
{
- wxCHECK_MSG( format, wxEmptyString, _T("NULL format in wxTimeSpan::Format") );
+ wxCHECK_MSG( !format.empty(), wxEmptyString,
+ _T("NULL format in wxTimeSpan::Format") );
wxString str;
- str.Alloc(wxStrlen(format));
+ str.Alloc(format.length());
// Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
//
// we remember the most important unit found so far
TimeSpanPart partBiggest = Part_MSec;
- for ( const wxChar *pch = format; *pch; pch++ )
+ for ( wxString::const_iterator pch = format.begin(); pch != format.end(); ++pch )
{
wxChar ch = *pch;
if ( ch == _T('%') )
{
// the start of the format specification of the printf() below
- wxString fmtPrefix = _T('%');
+ wxString fmtPrefix(_T('%'));
// the number
long n;
+ // the number of digits for the format string, 0 if unused
+ unsigned digits = 0;
+
ch = *++pch; // get the format spec char
switch ( ch )
{
n = GetHours();
if ( partBiggest < Part_Hour )
{
+ if ( n < 0 )
+ {
+ // the sign has already been taken into account
+ // when outputting the biggest part
+ n = -n;
+ }
+
n %= HOURS_PER_DAY;
}
else
partBiggest = Part_Hour;
}
- fmtPrefix += _T("02");
+ digits = 2;
break;
case _T('l'):
n = GetMilliseconds().ToLong();
if ( partBiggest < Part_MSec )
{
+ if ( n < 0 )
+ n = -n;
+
n %= 1000;
}
//else: no need to reset partBiggest to Part_MSec, it is
// the least significant one anyhow
- fmtPrefix += _T("03");
+ digits = 3;
break;
case _T('M'):
n = GetMinutes();
if ( partBiggest < Part_Min )
{
+ if ( n < 0 )
+ n = -n;
+
n %= MIN_PER_HOUR;
}
else
partBiggest = Part_Min;
}
- fmtPrefix += _T("02");
+ digits = 2;
break;
case _T('S'):
n = GetSeconds().ToLong();
if ( partBiggest < Part_Sec )
{
+ if ( n < 0 )
+ n = -n;
+
n %= SEC_PER_MIN;
}
else
partBiggest = Part_Sec;
}
- fmtPrefix += _T("02");
+ digits = 2;
break;
}
+ if ( digits )
+ {
+ // negative numbers need one extra position for '-' display
+ if ( n < 0 )
+ digits++;
+
+ fmtPrefix << _T("0") << digits;
+ }
+
str += wxString::Format(fmtPrefix + _T("ld"), n);
}
else
#include "wx/arrimpl.cpp"
-WX_DEFINE_OBJARRAY(wxDateTimeArray);
+WX_DEFINE_OBJARRAY(wxDateTimeArray)
static int wxCMPFUNC_CONV
wxDateTimeCompareFunc(wxDateTime **first, wxDateTime **second)
holidays.Clear();
- size_t count = ms_authorities.size();
- for ( size_t nAuth = 0; nAuth < count; nAuth++ )
+ const size_t countAuth = ms_authorities.size();
+ for ( size_t nAuth = 0; nAuth < countAuth; nAuth++ )
{
ms_authorities[nAuth]->DoGetHolidaysInRange(dtStart, dtEnd, hol);
: (wxDateTime::WeekDay)(wd - 1);
}
+#ifdef __WXMSW__
+
+wxDateTime& wxDateTime::SetFromMSWSysTime(const SYSTEMTIME& st)
+{
+ return Set(st.wDay,
+ static_cast<wxDateTime::Month>(wxDateTime::Jan + st.wMonth - 1),
+ st.wYear,
+ 0, 0, 0);
+}
+
+void wxDateTime::GetAsMSWSysTime(SYSTEMTIME* st) const
+{
+ const wxDateTime::Tm tm(GetTm());
+
+ st->wYear = (WXWORD)tm.year;
+ st->wMonth = (WXWORD)(tm.mon - wxDateTime::Jan + 1);
+ st->wDay = tm.mday;
+
+ st->wDayOfWeek =
+ st->wHour =
+ st->wMinute =
+ st->wSecond =
+ st->wMilliseconds = 0;
+}
+#endif // __WXMSW__
+
#endif // wxUSE_DATETIME