#include <ctype.h>
#include "wx/datetime.h"
+#include "wx/timer.h" // for wxGetLocalTimeMillis()
// ----------------------------------------------------------------------------
// conditional compilation
#if defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
#define WX_TIMEZONE _timezone
#elif defined(__MWERKS__)
- long wxmw_timezone = 28800;
- #define WX_TIMEZONE wxmw_timezone ;
+ long wxmw_timezone = 28800;
+ #define WX_TIMEZONE wxmw_timezone;
#else // unknown platform - try timezone
#define WX_TIMEZONE timezone
#endif
return wd;
}
-// scans all digits and returns the resulting number
-static bool GetNumericToken(const wxChar*& p, unsigned long *number)
+// 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)
{
+ size_t n = 1;
wxString s;
while ( wxIsdigit(*p) )
{
s += *p++;
+
+ if ( len && ++n > len )
+ break;
}
return !!s && s.ToULong(number);
// constructors and assignment operators
// ----------------------------------------------------------------------------
+// return the current time with ms precision
+/* static */ wxDateTime wxDateTime::UNow()
+{
+ return wxDateTime(wxGetLocalTimeMillis());
+}
+
// the values in the tm structure contain the local time
wxDateTime& wxDateTime::Set(const struct tm& tm)
{
tm = localtime(&time);
// should never happen
- wxCHECK_MSG( tm, Tm(), _T("gmtime() failed") );
+ wxCHECK_MSG( tm, Tm(), _T("localtime() failed") );
}
else
{
if ( tm )
{
- return Tm(*tm, tz);
+ // adjust the milliseconds
+ Tm tm2(*tm, tz);
+ long timeOnly = (m_time % MILLISECONDS_PER_DAY).ToLong();
+ tm2.msec = (wxDateTime_t)(timeOnly % 1000);
+ return tm2;
}
//else: use generic code below
}
}
else if ( weekday < wdayThis )
{
- return Substract(wxDateSpan::Days(wdayThis - weekday));
+ return Subtract(wxDateSpan::Days(wdayThis - weekday));
}
else // weekday > wdayThis
{
diff = wdayThis - weekday;
}
- return Substract(wxDateSpan::Days(diff));
+ return Subtract(wxDateSpan::Days(diff));
}
bool wxDateTime::SetToWeekDay(WeekDay weekday,
secDiff -= 3600;
}
- return Substract(wxTimeSpan::Seconds(secDiff));
+ return Subtract(wxTimeSpan::Seconds(secDiff));
}
// ----------------------------------------------------------------------------
{
wxCHECK_MSG( format, _T(""), _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
time_t time = GetTicks();
- if ( time != (time_t)-1 )
+ if ( (time != (time_t)-1) && !wxStrstr(format, _T("%l")) )
{
// use strftime()
tm *tm;
}
// we only parse ANSI C format specifications here, no POSIX 2
- // complications, no GNU extensions
+ // complications, no GNU extensions but we do add support for a "%l" format
+ // specifier allowing to get the number of milliseconds
Tm tm = GetTm(tz);
// used for calls to strftime() when we only deal with time
break;
case _T('j'): // day of year has 3 digits
+ case _T('l'): // milliseconds have 3 digits
fmt = _T("%03d");
break;
res += wxString::Format(fmt, GetDayOfYear(tz));
break;
+ case _T('l'): // milliseconds (NOT STANDARD)
+ res += wxString::Format(fmt, GetMillisecond(tz));
+ break;
+
case _T('m'): // month as a number (01-12)
res += wxString::Format(fmt, tm.mon + 1);
break;
}
// start of a format specification
- switch ( *++fmt )
+
+ // parse the optional width
+ size_t width = 0;
+ while ( isdigit(*++fmt) )
+ {
+ width *= 10;
+ width += *fmt - _T('0');
+ }
+
+ // then the format itself
+ switch ( *fmt )
{
case _T('a'): // a weekday name
case _T('A'):
break;
case _T('d'): // day of a month (01-31)
- if ( !GetNumericToken(input, &num) || (num > 31) || (num < 1) )
+ if ( !GetNumericToken(width, input, &num) ||
+ (num > 31) || (num < 1) )
{
// no match
return (wxChar *)NULL;
break;
case _T('H'): // hour in 24h format (00-23)
- if ( !GetNumericToken(input, &num) || (num > 23) )
+ if ( !GetNumericToken(width, input, &num) || (num > 23) )
{
// no match
return (wxChar *)NULL;
break;
case _T('I'): // hour in 12h format (01-12)
- if ( !GetNumericToken(input, &num) || !num || (num > 12) )
+ if ( !GetNumericToken(width, input, &num) || !num || (num > 12) )
{
// no match
return (wxChar *)NULL;
break;
case _T('j'): // day of the year
- if ( !GetNumericToken(input, &num) || !num || (num > 366) )
+ if ( !GetNumericToken(width, input, &num) || !num || (num > 366) )
{
// no match
return (wxChar *)NULL;
break;
case _T('m'): // month as a number (01-12)
- if ( !GetNumericToken(input, &num) || !num || (num > 12) )
+ if ( !GetNumericToken(width, input, &num) || !num || (num > 12) )
{
// no match
return (wxChar *)NULL;
break;
case _T('M'): // minute as a decimal number (00-59)
- if ( !GetNumericToken(input, &num) || (num > 59) )
+ if ( !GetNumericToken(width, input, &num) || (num > 59) )
{
// no match
return (wxChar *)NULL;
}
case _T('S'): // second as a decimal number (00-61)
- if ( !GetNumericToken(input, &num) || (num > 61) )
+ if ( !GetNumericToken(width, input, &num) || (num > 61) )
{
// no match
return (wxChar *)NULL;
break;
case _T('w'): // weekday as a number (0-6), Sunday = 0
- if ( !GetNumericToken(input, &num) || (wday > 6) )
+ if ( !GetNumericToken(width, input, &num) || (wday > 6) )
{
// no match
return (wxChar *)NULL;
break;
case _T('y'): // year without century (00-99)
- if ( !GetNumericToken(input, &num) || (num > 99) )
+ if ( !GetNumericToken(width, input, &num) || (num > 99) )
{
// no match
return (wxChar *)NULL;
}
haveYear = TRUE;
- year = 1900 + (wxDateTime_t)num;
+
+ // TODO should have an option for roll over date instead of
+ // hard coding it here
+ year = (num > 30 ? 1900 : 2000) + (wxDateTime_t)num;
break;
case _T('Y'): // year with century
- if ( !GetNumericToken(input, &num) )
+ if ( !GetNumericToken(width, input, &num) )
{
// no match
return (wxChar *)NULL;
// take this date as default
tmDef = dateDef.GetTm();
}
+#ifdef __WIN16__
else if ( m_time != 0 )
+#else
+ else if ( m_time != wxLongLong(0) )
+#endif
{
// if this date is valid, don't change it
tmDef = GetTm();
{
wxString date = wxGetTranslation(literalDates[n].str);
size_t len = date.length();
- if ( wxString(p, len).CmpNoCase(date) == 0 )
+ if ( wxStrlen(p) >= len && (wxString(p, len).CmpNoCase(date) == 0) )
{
// nothing can follow this, so stop here
p += len;
int year = 0;
// tokenize the string
- wxStringTokenizer tok(p, _T(",/-\t\n "));
+ size_t nPosCur = 0;
+ static const wxChar *dateDelimiters = _T(".,/-\t\n ");
+ wxStringTokenizer tok(p, dateDelimiters);
while ( tok.HasMoreTokens() )
{
wxString token = tok.GetNextToken();
haveMon = TRUE;
- mon = (wxDateTime::Month)val;
+ // month 1 is Jan
+ mon = (wxDateTime::Month)(val - 1);
}
else
{
if ( (day <= 12) && !haveMon )
{
// exchange day and month
- mon = (wxDateTime::Month)day;
+ mon = (wxDateTime::Month)(day - 1);
haveMon = TRUE;
}
}
else // not a number
{
- mon = GetMonthFromName(token, Name_Full | Name_Abbr);
- if ( mon != Inv_Month )
+ // be careful not to overwrite the current mon value
+ Month mon2 = GetMonthFromName(token, Name_Full | Name_Abbr);
+ if ( mon2 != Inv_Month )
{
// it's a month
if ( haveMon )
break;
}
+ mon = mon2;
+
haveMon = TRUE;
}
else
}
}
}
+
+ nPosCur = tok.GetPosition();
}
// either no more tokens or the scan was stopped by something we couldn't
// parse - in any case, see if we can construct a date from what we have
if ( !haveDay && !haveWDay )
{
- wxLogDebug(_T("no day, no weekday hence no date."));
+ wxLogDebug(_T("ParseDate: no day, no weekday hence no date."));
return (wxChar *)NULL;
}
if ( haveWDay && (haveMon || haveYear || haveDay) &&
- !(haveMon && haveMon && haveYear) )
+ !(haveDay && haveMon && haveYear) )
{
// without adjectives (which we don't support here) the week day only
// makes sense completely separately or with the full date
return (wxChar *)NULL;
}
+ if ( !haveWDay && haveYear && !(haveDay && haveMon) )
+ {
+ // may be we have month and day instead of day and year?
+ if ( haveDay && !haveMon )
+ {
+ if ( day <= 12 )
+ {
+ // exchange day and month
+ mon = (wxDateTime::Month)(day - 1);
+
+ // we're in the current year then
+ if ( year <= GetNumOfDaysInMonth(Inv_Year, mon) )
+ {
+ day = year;
+
+ haveMon = TRUE;
+ haveYear = FALSE;
+ }
+ //else: no, can't exchange, leave haveMon == FALSE
+ }
+ }
+
+ if ( !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;
+ }
+ }
+
if ( !haveMon )
{
mon = GetCurrentMonth();
if ( GetWeekDay() != wday )
{
// inconsistency detected
+ wxLogDebug(_T("ParseDate: inconsistent day/weekday."));
+
return (wxChar *)NULL;
}
}
SetToWeekDayInSameWeek(wday);
}
- // return the pointer to the next char
- return p + wxStrlen(p) - wxStrlen(tok.GetString());
+ // return the pointer to the first unparsed char
+ p += nPosCur;
+ if ( nPosCur && wxStrchr(dateDelimiters, *(p - 1)) )
+ {
+ // if we couldn't parse the token after the delimiter, put back the
+ // delimiter as well
+ p--;
+ }
+
+ return p;
}
const wxChar *wxDateTime::ParseTime(const wxChar *time)
wxString str;
str.Alloc(wxStrlen(format));
- for ( const wxChar *pch = format; pch; pch++ )
+ for ( const wxChar *pch = format; *pch; pch++ )
{
wxChar ch = *pch;
- if ( ch == '%' )
+ if ( ch == _T('%') )
{
wxString tmp;
- ch = *pch++;
+ ch = *++pch; // get the format spec char
switch ( ch )
{
default:
wxFAIL_MSG( _T("invalid format character") );
// fall through
- case '%':
+ case _T('%'):
// will get to str << ch below
break;
- case 'D':
+ case _T('D'):
tmp.Printf(_T("%d"), GetDays());
break;
- case 'E':
+ case _T('E'):
tmp.Printf(_T("%d"), GetWeeks());
break;
- case 'H':
+ case _T('H'):
tmp.Printf(_T("%02d"), GetHours());
break;
- case 'l':
+ case _T('l'):
tmp.Printf(_T("%03ld"), GetMilliseconds().ToLong());
break;
- case 'M':
+ case _T('M'):
tmp.Printf(_T("%02d"), GetMinutes());
break;
- case 'S':
+ case _T('S'):
tmp.Printf(_T("%02ld"), GetSeconds().ToLong());
break;
}
#include "wx/arrimpl.cpp"
-WX_DEFINE_OBJARRAY(wxDateTimeArray)
+WX_DEFINE_OBJARRAY(wxDateTimeArray);
static int wxCMPFUNC_CONV
wxDateTimeCompareFunc(wxDateTime **first, wxDateTime **second)