// 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:
*
#ifdef __WINDOWS__
#include "wx/msw/wrapwin.h"
#include <winnls.h>
- #include <locale.h>
+ #ifndef __WXWINCE__
+ #include <locale.h>
+ #endif
#endif
#include "wx/datetime.h"
#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
- JDN_OFFSET;
}
+#ifndef __WXWINCE__
// this function is a wrapper around strftime(3) adding error checking
static wxString CallStrftime(const wxChar *format, const tm* tm)
{
wxChar buf[4096];
- if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) )
+ // 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?
wxFAIL_MSG(_T("strftime() failed"));
}
- return wxString(buf);
+ s = buf;
+ return s;
}
+#endif
#ifdef HAVE_STRPTIME
return CallStrftime(flags == Name_Abbr ? _T("%b") : _T("%B"), &tm);
#else
- wxString ret;
- switch(month)
- {
- case Jan:
- ret = (flags == Name_Abbr ? wxT("Jan"): wxT("January"));
- break;
- case Feb:
- ret = (flags == Name_Abbr ? wxT("Feb"): wxT("Febuary"));
- break;
- case Mar:
- ret = (flags == Name_Abbr ? wxT("Mar"): wxT("March"));
- break;
- case Apr:
- ret = (flags == Name_Abbr ? wxT("Apr"): wxT("April"));
- break;
- case May:
- ret = (flags == Name_Abbr ? wxT("May"): wxT("May"));
- break;
- case Jun:
- ret = (flags == Name_Abbr ? wxT("Jun"): wxT("June"));
- break;
- case Jul:
- ret = (flags == Name_Abbr ? wxT("Jul"): wxT("July"));
- break;
- case Aug:
- ret = (flags == Name_Abbr ? wxT("Aug"): wxT("August"));
- break;
- case Sep:
- ret = (flags == Name_Abbr ? wxT("Sep"): wxT("September"));
- break;
- case Oct:
- ret = (flags == Name_Abbr ? wxT("Oct"): wxT("October"));
- break;
- case Nov:
- ret = (flags == Name_Abbr ? wxT("Nov"): wxT("November"));
- break;
- case Dec:
- ret = (flags == Name_Abbr ? wxT("Dec"): wxT("December"));
- break;
- }
- return ret;
+ wxString ret;
+ switch(month)
+ {
+ case Jan:
+ ret = (flags == Name_Abbr ? wxT("Jan"): wxT("January"));
+ break;
+ case Feb:
+ ret = (flags == Name_Abbr ? wxT("Feb"): wxT("Febuary"));
+ break;
+ case Mar:
+ ret = (flags == Name_Abbr ? wxT("Mar"): wxT("March"));
+ break;
+ case Apr:
+ ret = (flags == Name_Abbr ? wxT("Apr"): wxT("April"));
+ break;
+ case May:
+ ret = (flags == Name_Abbr ? wxT("May"): wxT("May"));
+ break;
+ case Jun:
+ ret = (flags == Name_Abbr ? wxT("Jun"): wxT("June"));
+ break;
+ case Jul:
+ ret = (flags == Name_Abbr ? wxT("Jul"): wxT("July"));
+ break;
+ case Aug:
+ ret = (flags == Name_Abbr ? wxT("Aug"): wxT("August"));
+ break;
+ case Sep:
+ ret = (flags == Name_Abbr ? wxT("Sep"): wxT("September"));
+ break;
+ case Oct:
+ ret = (flags == Name_Abbr ? wxT("Oct"): wxT("October"));
+ break;
+ case Nov:
+ ret = (flags == Name_Abbr ? wxT("Nov"): wxT("November"));
+ break;
+ case Dec:
+ ret = (flags == Name_Abbr ? wxT("Dec"): wxT("December"));
+ break;
+ }
+ return ret;
#endif
}
// ... and call strftime()
return CallStrftime(flags == Name_Abbr ? _T("%a") : _T("%A"), &tm);
#else
- wxString ret;
- switch(wday)
- {
- case Sun:
- ret = (flags == Name_Abbr ? wxT("Sun") : wxT("Sunday"));
- break;
- case Mon:
- ret = (flags == Name_Abbr ? wxT("Mon") : wxT("Monday"));
- break;
- case Tue:
- ret = (flags == Name_Abbr ? wxT("Tue") : wxT("Tuesday"));
- break;
- case Wed:
- ret = (flags == Name_Abbr ? wxT("Wed") : wxT("Wednesday"));
- break;
- case Thu:
- ret = (flags == Name_Abbr ? wxT("Thu") : wxT("Thursday"));
- break;
- case Fri:
- ret = (flags == Name_Abbr ? wxT("Fri") : wxT("Friday"));
- break;
- case Sat:
- ret = (flags == Name_Abbr ? wxT("Sat") : wxT("Saturday"));
- break;
- }
- return ret;
+ wxString ret;
+ switch(wday)
+ {
+ case Sun:
+ ret = (flags == Name_Abbr ? wxT("Sun") : wxT("Sunday"));
+ break;
+ case Mon:
+ ret = (flags == Name_Abbr ? wxT("Mon") : wxT("Monday"));
+ break;
+ case Tue:
+ ret = (flags == Name_Abbr ? wxT("Tue") : wxT("Tuesday"));
+ break;
+ case Wed:
+ ret = (flags == Name_Abbr ? wxT("Wed") : wxT("Wednesday"));
+ break;
+ case Thu:
+ ret = (flags == Name_Abbr ? wxT("Thu") : wxT("Thursday"));
+ break;
+ case Fri:
+ ret = (flags == Name_Abbr ? wxT("Fri") : wxT("Friday"));
+ break;
+ case Sat:
+ ret = (flags == Name_Abbr ? wxT("Sat") : wxT("Saturday"));
+ break;
+ }
+ return ret;
#endif
}
// assert, even though it is a perfectly legal use.
if ( am )
{
- if (wxStrftime(buffer, sizeof buffer, _T("%p"), &tm) > 0)
+ if (wxStrftime(buffer, sizeof(buffer)/sizeof(wxChar), _T("%p"), &tm) > 0)
*am = wxString(buffer);
else
*am = wxString();
if ( pm )
{
tm.tm_hour = 13;
- if (wxStrftime(buffer, sizeof buffer, _T("%p"), &tm) > 0)
+ if (wxStrftime(buffer, sizeof(buffer)/sizeof(wxChar), _T("%p"), &tm) > 0)
*pm = wxString(buffer);
else
*pm = wxString();
wxDATETIME_CHECK( tm, _T("localtime() failed") );
+ // make a copy so it isn't clobbered by the call to mktime() below
+ struct tm tm1(*tm);
+
// adjust the time
- tm->tm_hour = hour;
- tm->tm_min = minute;
- tm->tm_sec = second;
+ tm1.tm_hour = hour;
+ tm1.tm_min = minute;
+ tm1.tm_sec = second;
// and the DST in case it changes on this date
- struct tm tm2(*tm);
+ struct tm tm2(tm1);
mktime(&tm2);
- if ( tm2.tm_isdst != tm->tm_isdst )
- tm->tm_isdst = tm2.tm_isdst;
+ if ( tm2.tm_isdst != tm1.tm_isdst )
+ tm1.tm_isdst = tm2.tm_isdst;
- (void)Set(*tm);
+ (void)Set(tm1);
// and finally adjust milliseconds
return SetMillisecond(millisec);
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();
tzDiff -= 3600;
}
- jdn += tzDiff*1000; // tzDiff is in seconds
-
- m_time.Assign(jdn);
+ m_time += tzDiff*1000; // tzDiff is in seconds
return *this;
}
{
wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
- int wdayThis = GetWeekDay();
- if ( weekday == wdayThis )
+ int wdayDst = weekday,
+ wdayThis = GetWeekDay();
+ if ( wdayDst == wdayThis )
{
// nothing to do
return *this;
// the logic below based on comparing weekday and wdayThis works if Sun (0)
// is the first day in the week, but breaks down for Monday_First case so
// we adjust the week days in this case
- if( flags == Monday_First )
+ if ( flags == Monday_First )
{
if ( wdayThis == Sun )
wdayThis += 7;
+ if ( wdayDst == Sun )
+ wdayDst += 7;
}
//else: Sunday_First, nothing to do
// go forward or back in time to the day we want
- if ( weekday < wdayThis )
+ if ( wdayDst < wdayThis )
{
- return Subtract(wxDateSpan::Days(wdayThis - weekday));
+ return Subtract(wxDateSpan::Days(wdayThis - wdayDst));
}
else // weekday > wdayThis
{
- return Add(wxDateSpan::Days(weekday - wdayThis));
+ return Add(wxDateSpan::Days(wdayDst - wdayThis));
}
}
}
}
#ifndef __WXWINCE__
- //Windows CE doesn't support strftime or wcsftime, so we use the generic implementation
+ //Windows CE doesn't support strftime or wcsftime, so we use the generic implementation
if ( tm )
{
return CallStrftime(format, tm);
// 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
strYear.Printf(_T("%d"), year);
strYear2.Printf(_T("%d"), year % 100);
- // find two strings not occuring in format (this is surely
- // not optimal way of doing it... improvements welcome!)
+ // find two strings not occurring in format (this is surely
+ // not the optimal way of doing it... improvements welcome!)
wxString fmt = format;
wxString replacement = (wxChar)-1;
while ( fmt.Find(replacement) != wxNOT_FOUND )
replacement << (wxChar)-2;
}
- // replace all occurences of year with it
+ // replace all occurrences of year with it
bool wasReplaced = fmt.Replace(strYear, replacement) > 0;
if ( !wasReplaced )
wasReplaced = fmt.Replace(strYear2, replacement2) > 0;
: _T("%x"),
&tmAdjusted);
- // now replace the occurence of 1999 with the real year
+ // now replace the occurrence 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
+ // and replace back all occurrences of replacement string
if ( wasReplaced )
{
str.Replace(replacement2, strYear2);
res += str;
}
#else
- //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);
+ //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
break;
#ifndef __WXWINCE__
res += CallStrftime(_T("%p"), &tmTimeOnly);
#else
- res += (tmTimeOnly.tm_hour > 12) ? wxT("pm") : wxT("am");
+ res += (tmTimeOnly.tm_hour > 12) ? wxT("pm") : wxT("am");
#endif
break;
#ifndef __WXWINCE__
res += CallStrftime(_T("%X"), &tmTimeOnly);
#else
- res += wxString::Format(wxT("%02d:%02d:%02d"),tm.hour, tm.min, tm.sec);
+ res += wxString::Format(wxT("%02d:%02d:%02d"),tm.hour, tm.min, tm.sec);
#endif
break;
}
// and now the interesting part: the timezone
- int offset;
+ int offset wxDUMMY_INITIALIZE(0);
if ( *p == _T('-') || *p == _T('+') )
{
// the explicit offset given: it has the form of hhmm
}
#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
-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__
if ( strcmp(setlocale(LC_ALL, NULL), "C") != 0 )
+#endif
{
// The locale was programatically set to non-C. We assume that this was
// done using wxLocale, in which case thread's current locale is also
// set to correct LCID value and we can use GetLocaleInfo to determine
// the correct formatting string:
+#ifdef __WXWINCE__
+ LCID lcid = LOCALE_USER_DEFAULT;
+#else
LCID lcid = GetThreadLocale();
- wxChar delim[5]; // fields deliminer, 4 chars max
- if ( GetLocaleInfo(lcid, LOCALE_SDATE, delim, 5) )
+#endif
+ // 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,
hour = tm.hour;
min = tm.min;
}
+ break;
case _T('S'): // second as a decimal number (00-61)
if ( !GetNumericToken(width, input, &num) || (num > 61) )
{
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);
+ fmtDate = GetLocaleDateFormat();
+ if ( fmtDate.empty() )
#endif
+ {
+ if ( IsWestEuropeanCountry(GetCountry()) ||
+ GetCountry() == Russia )
+ {
+ fmtDate = _T("%d/%m/%y");
+ fmtDateAlt = _T("%m/%d/%y");
+ }
+ else // assume USA
+ {
+ fmtDate = _T("%m/%d/%y");
+ fmtDateAlt = _T("%d/%m/%y");
+ }
+ }
const wxChar *result = dt.ParseFormat(input, fmtDate);
- if ( !result )
+ if ( !result && !fmtDateAlt.empty() )
{
// ok, be nice and try another one
result = dt.ParseFormat(input, fmtDateAlt);