///////////////////////////////////////////////////////////////////////////////
-// Name: wx/datetime.h
+// Name: src/common/datetime.cpp
// Purpose: implementation of time/date related classes
// Author: Vadim Zeitlin
// Modified by:
// 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"
// (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;
static wxString CallStrftime(const wxChar *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?
wxFAIL_MSG(_T("strftime() failed"));
}
- return wxString(buf);
+ s = buf;
+ return s;
}
#endif
#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
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:
// 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;
}
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;
}
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
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));
}
// 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!)
- wxString fmt = format;
+ // find two 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 ( fmt.Find(replacement) != wxNOT_FOUND )
+ while ( fmt2.Find(replacement) != wxNOT_FOUND )
{
replacement << (wxChar)-1;
}
wxString replacement2 = (wxChar)-2;
- while ( fmt.Find(replacement) != wxNOT_FOUND )
+ while ( fmt2.Find(replacement) != wxNOT_FOUND )
{
replacement << (wxChar)-2;
}
- // replace all occurences of year with it
- bool wasReplaced = fmt.Replace(strYear, replacement) > 0;
+ // replace all occurrences of year with it
+ bool wasReplaced = fmt2.Replace(strYear, replacement) > 0;
if ( !wasReplaced )
- wasReplaced = fmt.Replace(strYear2, replacement2) > 0;
+ wasReplaced = fmt2.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
+ // 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);
}
// 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;
}
// 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((wxDateTime_t)(offset*SEC_PER_MIN)));
return p;
}
#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
+ if ( *p == chLast )
{
- *fmt = wxString::Format(_T("%%m%s%%d%s%%%c"),
- delim, delim, century);
- }
- else if ( order[0] == _T('1') ) // D-M-Y
- {
- *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__
{
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);
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;
#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);