///////////////////////////////////////////////////////////////////////////////
-// Name: wx/datetime.h
+// Name: src/common/datetime.cpp
// Purpose: implementation of time/date related classes
// Author: Vadim Zeitlin
// Modified by:
#ifndef WX_PRECOMP
#include "wx/string.h"
#include "wx/log.h"
+ #include "wx/intl.h"
#endif // WX_PRECOMP
-#include "wx/intl.h"
#include "wx/thread.h"
#include "wx/tokenzr.h"
#include "wx/module.h"
#endif
#endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
+#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
+ memcpy(temp, localtime(ticks), sizeof(struct tm));
+ return temp;
+}
+#endif
+
+#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
+ memcpy(temp, gmtime(ticks), sizeof(struct tm));
+ return temp;
+}
+#endif
+
// ----------------------------------------------------------------------------
// 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
// (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
#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
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)
{
{
// 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") )
_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);
{
unsigned long ddt;
time_t ticks = GetTicks();
- struct tm *tm = localtime(&ticks);
+ struct tm tmstruct;
+ struct tm *tm = wxLocaltime_r(&ticks, &tmstruct);
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
{
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)
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;
}
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
{
nLostWeekDays += year++ % 4 ? 1 : 2;
}
+ // Keep year below 2000 so the 2digit year number
+ // can never match the month or day of the month
+ if (year>=2000) year-=28;
// at any rate, we couldn't go further than 1988 + 9 + 28!
wxASSERT_MSG( year < 2030,
_T("logic error in wxDateTime::Format") );
strYear.Printf(_T("%d"), year);
strYear2.Printf(_T("%d"), year % 100);
- // find two strings not occurring in format (this is surely
+ // find four strings not occurring in format (this is surely
// not the optimal way of doing it... improvements welcome!)
wxString fmt2 = format;
- wxString replacement = (wxChar)-1;
- while ( fmt2.Find(replacement) != wxNOT_FOUND )
+ wxString replacement,replacement2,replacement3,replacement4;
+ for (int rnr=1; rnr<5 ; rnr++)
{
- replacement << (wxChar)-1;
- }
+ wxString r = (wxChar)-rnr;
+ while ( fmt2.Find(r) != wxNOT_FOUND )
+ {
+ r << (wxChar)-rnr;
+ }
- wxString replacement2 = (wxChar)-2;
- while ( fmt2.Find(replacement) != wxNOT_FOUND )
- {
- replacement << (wxChar)-2;
+ switch (rnr)
+ {
+ case 1: replacement=r; break;
+ case 2: replacement2=r; break;
+ case 3: replacement3=r; break;
+ case 4: replacement4=r; break;
+ }
}
-
// replace all occurrences of year with it
bool wasReplaced = fmt2.Replace(strYear, replacement) > 0;
- if ( !wasReplaced )
- wasReplaced = fmt2.Replace(strYear2, replacement2) > 0;
+ // evaluation order ensures we always attempt the replacement.
+ wasReplaced = (fmt2.Replace(strYear2, replacement2) > 0) | wasReplaced ;
// use strftime() to format the same date but in supported
// year
&tmAdjusted);
// now replace the occurrence of 1999 with the real year
+ // we do this in two stages to stop the 2 digit year
+ // matching any substring of the 4 digit year.
+ // Any day,month hours and minutes components should be safe due
+ // to ensuring the range of the years.
wxString strYearReal, strYearReal2;
strYearReal.Printf(_T("%04d"), yearReal);
strYearReal2.Printf(_T("%02d"), yearReal % 100);
- str.Replace(strYear, strYearReal);
- str.Replace(strYear2, strYearReal2);
+ str.Replace(strYear, replacement3);
+ str.Replace(strYear2,replacement4);
+ str.Replace(replacement3, strYearReal);
+ str.Replace(replacement4, strYearReal2);
// and replace back all occurrences of replacement string
if ( wasReplaced )
p += tz.length();
}
-
+
// make it minutes
offset *= MIN_PER_HOUR;
}
}
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
);
{
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 )
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