// so long as the above copyright and this permission statement
// are retained in all copies.
//
-// Licence: wxWindows license
+// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
/*
#pragma hdrstop
#endif
+#if !defined(wxUSE_DATETIME) || wxUSE_DATETIME
+
#ifndef WX_PRECOMP
#include "wx/string.h"
- #include "wx/intl.h"
#include "wx/log.h"
#endif // WX_PRECOMP
+#include "wx/intl.h"
#include "wx/thread.h"
#include "wx/tokenzr.h"
#include "wx/module.h"
#undef HAVE_STRPTIME
#endif // broken strptime()
-#ifndef WX_TIMEZONE
+#if defined(__MWERKS__) && wxUSE_UNICODE
+ #include <wtime.h>
+#endif
+
+#if !defined(WX_TIMEZONE) && !defined(WX_GMTOFF_IN_TM)
#if defined(__BORLANDC__) || defined(__MINGW32__) || defined(__VISAGECPP__)
#define WX_TIMEZONE _timezone
#elif defined(__MWERKS__)
long wxmw_timezone = 28800;
- #define WX_TIMEZONE wxmw_timezone;
+ #define WX_TIMEZONE wxmw_timezone
+ #elif defined(__DJGPP__) || defined(__WINE__)
+ #include <sys/timeb.h>
+ #include <values.h>
+ static long wxGetTimeZone()
+ {
+ static long timezone = MAXLONG; // invalid timezone
+ if (timezone == MAXLONG)
+ {
+ struct timeb tb;
+ ftime(&tb);
+ timezone = tb.timezone;
+ }
+ return timezone;
+ }
+ #define WX_TIMEZONE wxGetTimeZone()
+ #elif defined(__DARWIN__)
+ #define WX_GMTOFF_IN_TM
#else // unknown platform - try timezone
#define WX_TIMEZONE timezone
#endif
-#endif // !WX_TIMEZONE
+#endif // !WX_TIMEZONE && !WX_GMTOFF_IN_TM
// ----------------------------------------------------------------------------
// macros
// some trivial ones
static const int MONTHS_IN_YEAR = 12;
-static const int SECONDS_IN_MINUTE = 60;
+static const int SEC_PER_MIN = 60;
+
+static const int MIN_PER_HOUR = 60;
+
+static const int HOURS_PER_DAY = 24;
static const long SECONDS_PER_DAY = 86400l;
+static const int DAYS_PER_WEEK = 7;
+
static const long MILLISECONDS_PER_DAY = 86400000l;
// this is the integral part of JDN of the midnight of Jan 1, 1970
// in the fine tradition of ANSI C we use our equivalent of (time_t)-1 to
// indicate an invalid wxDateTime object
-static const wxDateTime gs_dtDefault = wxLongLong((long)ULONG_MAX, ULONG_MAX);
-
-const wxDateTime& wxDefaultDateTime = gs_dtDefault;
+const wxDateTime wxDefaultDateTime;
wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown;
return daysInMonth[wxDateTime::IsLeapYear(year)][month];
}
-// ensure that the timezone variable is set by calling localtime
+// returns the time zone in the C sense, i.e. the difference UTC - local
+// (in seconds)
static int GetTimeZone()
{
// set to TRUE when the timezone is set
static bool s_timezoneSet = FALSE;
+#ifdef WX_GMTOFF_IN_TM
+ static long gmtoffset = LONG_MAX; // invalid timezone
+#endif
wxCRIT_SECT_LOCKER(lock, gs_critsectTimezone);
+ // ensure that the timezone variable is set by calling localtime
if ( !s_timezoneSet )
{
// just call localtime() instead of figuring out whether this system
// supports tzset(), _tzset() or something else
- time_t t = 0;
+ time_t t = 0;
+ struct tm *tm;
- (void)localtime(&t);
+ tm = localtime(&t);
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;
+#endif
}
+#ifdef WX_GMTOFF_IN_TM
+ return (int)gmtoffset;
+#else
return (int)WX_TIMEZONE;
+#endif
}
// return the integral part of the JDN for the midnight of the given date (to
// the values in the tm structure contain the local time
wxDateTime& wxDateTime::Set(const struct tm& tm)
{
- wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
-
struct tm tm2(tm);
time_t timet = mktime(&tm2);
wxDateTime_t second,
wxDateTime_t millisec)
{
- wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
-
// we allow seconds to be 61 to account for the leap seconds, even if we
// don't use them really
wxDATETIME_CHECK( hour < 24 &&
wxDateTime_t second,
wxDateTime_t millisec)
{
- wxASSERT_MSG( IsValid(), _T("invalid wxDateTime") );
-
wxDATETIME_CHECK( hour < 24 &&
second < 62 &&
minute < 60 &&
return *this;
}
+// ----------------------------------------------------------------------------
+// DOS Date and Time Format functions
+// ----------------------------------------------------------------------------
+// the dos date and time value is an unsigned 32 bit value in the format:
+// YYYYYYYMMMMDDDDDhhhhhmmmmmmsssss
+//
+// Y = year offset from 1980 (0-127)
+// M = month (1-12)
+// D = day of month (1-31)
+// h = hour (0-23)
+// m = minute (0-59)
+// s = bisecond (0-29) each bisecond indicates two seconds
+// ----------------------------------------------------------------------------
+
+wxDateTime& wxDateTime::SetFromDOS(unsigned long ddt)
+{
+ struct tm tm;
+
+ long year = ddt & 0xFE000000;
+ year >>= 25;
+ year += 80;
+ tm.tm_year = year;
+
+ long month = ddt & 0x1E00000;
+ month >>= 21;
+ month -= 1;
+ tm.tm_mon = month;
+
+ long day = ddt & 0x1F0000;
+ day >>= 16;
+ tm.tm_mday = day;
+
+ long hour = ddt & 0xF800;
+ hour >>= 11;
+ tm.tm_hour = hour;
+
+ long minute = ddt & 0x7E0;
+ minute >>= 5;
+ tm.tm_min = minute;
+
+ long second = ddt & 0x1F;
+ tm.tm_sec = second * 2;
+
+ return Set(mktime(&tm));
+}
+
+unsigned long wxDateTime::GetAsDOS() const
+{
+ unsigned long ddt;
+ time_t ticks = GetTicks();
+ struct tm *tm = localtime(&ticks);
+
+ long year = tm->tm_year;
+ year -= 80;
+ year <<= 25;
+
+ long month = tm->tm_mon;
+ month += 1;
+ month <<= 21;
+
+ long day = tm->tm_mday;
+ day <<= 16;
+
+ long hour = tm->tm_hour;
+ hour <<= 11;
+
+ long minute = tm->tm_min;
+ minute <<= 5;
+
+ long second = tm->tm_sec;
+ second /= 2;
+
+ ddt = year | month | day | hour | minute | second;
+ return ddt;
+}
+
// ----------------------------------------------------------------------------
// time_t <-> broken down time conversions
// ----------------------------------------------------------------------------
else
{
time += (time_t)tz.GetOffset();
-#ifdef __VMS__ // time is unsigned so avoid warning
+#if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
int time2 = (int) time;
if ( time2 >= 0 )
#else
// check that the algorithm gave us something reasonable
wxASSERT_MSG( (0 < month) && (month <= 12), _T("invalid month") );
wxASSERT_MSG( (1 <= day) && (day < 32), _T("invalid day") );
- wxASSERT_MSG( (INT_MIN <= year) && (year <= INT_MAX),
- _T("year range overflow") );
// construct Tm from these values
Tm tm;
// Weekday and monthday stuff
// ----------------------------------------------------------------------------
-bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek, WeekDay weekday)
+bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek,
+ WeekDay weekday,
+ WeekFlags flags)
{
+ wxASSERT_MSG( numWeek > 0,
+ _T("invalid week number: weeks are counted from 1") );
+
int year = GetYear();
// Jan 4 always lies in the 1st week of the year
Set(4, Jan, year);
- SetToWeekDayInSameWeek(weekday) += wxDateSpan::Weeks(numWeek);
+ SetToWeekDayInSameWeek(weekday, flags) += wxDateSpan::Weeks(numWeek - 1);
if ( GetYear() != year )
{
return Set(GetNumOfDaysInMonth(year, month), month, year);
}
-wxDateTime& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday)
+wxDateTime& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday, WeekFlags flags)
{
wxDATETIME_CHECK( weekday != Inv_WeekDay, _T("invalid weekday") );
- WeekDay wdayThis = GetWeekDay();
+ int wdayThis = GetWeekDay();
if ( weekday == wdayThis )
{
// nothing to do
return *this;
}
- else if ( weekday < wdayThis )
+
+ if ( flags == Default_First )
+ {
+ flags = GetCountry() == USA ? Sunday_First : Monday_First;
+ }
+
+ // 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 ( wdayThis == Sun )
+ wdayThis += 7;
+ }
+ //else: Sunday_First, nothing to do
+
+ // go forward or back in time to the day we want
+ if ( weekday < wdayThis )
{
return Subtract(wxDateSpan::Days(wdayThis - weekday));
}
{
time += (int)tz.GetOffset();
-#ifdef __VMS__ // time is unsigned so avoid the warning
+#if defined(__VMS__) || defined(__WATCOMC__) // time is unsigned so avoid warning
int time2 = (int) time;
if ( time2 >= 0 )
#else
fmt = _T("%03d");
break;
+ case _T('w'): // week day as number has only one
+ fmt = _T("%d");
+ break;
+
default:
// it's either another valid format specifier in which case
// the format is "%02d" (for all the rest) or we have the
width += *fmt - _T('0');
}
+ // the default widths for the various fields
+ if ( !width )
+ {
+ switch ( *fmt )
+ {
+ case _T('Y'): // year has 4 digits
+ width = 4;
+ break;
+
+ case _T('j'): // day of year has 3 digits
+ case _T('l'): // milliseconds have 3 digits
+ width = 3;
+ break;
+
+ case _T('w'): // week day as number has only one
+ width = 1;
+ break;
+
+ default:
+ // default for all other fields
+ width = 2;
+ }
+ }
+
// then the format itself
switch ( *fmt )
{
// take this date as default
tmDef = dateDef.GetTm();
}
-#ifdef __WIN16__
- else if ( m_time != 0 )
-#else
- else if ( m_time != wxLongLong(0) )
-#endif
+ else if ( IsValid() )
{
// if this date is valid, don't change it
tmDef = GetTm();
// tokenize the string
size_t nPosCur = 0;
- static const wxChar *dateDelimiters = _T(".,/-\t\n ");
+ static const wxChar *dateDelimiters = _T(".,/-\t\r\n ");
wxStringTokenizer tok(p, dateDelimiters);
while ( tok.HasMoreTokens() )
{
: 31;
// can it be day?
- if ( (val == 0) || (val > maxDays) )
+ if ( (val == 0) || (val > (unsigned long)maxDays) ) // cast to shut up compiler warning in BCC
{
isYear = TRUE;
}
// but we already have a month - maybe we guessed wrong?
if ( !haveDay )
{
- // no need to check in month range as always < 12, but
+ // no need to check in month range as always < 12, but
// the days are counted from 1 unlike the months
day = (wxDateTime_t)mon + 1;
haveDay = TRUE;
// could possible be the year (doesn't the year come
// before the month in the japanese format?) (FIXME)
break;
- }
+ }
}
mon = mon2;
haveDay = TRUE;
- day = n + 1;
+ day = (wxDateTime_t)(n + 1);
}
}
}
mon = (wxDateTime::Month)(day - 1);
// we're in the current year then
- if ( year <= GetNumOfDaysInMonth(Inv_Year, mon) )
+ if ( (year > 0) &&
+ (unsigned)year <= GetNumOfDaysInMonth(Inv_Year, mon) )
{
day = year;
}
}
- // try all time formats we may think about starting with the standard one
- const wxChar *result = ParseFormat(time, _T("%X"));
+ // 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?
if ( !result )
{
- // 12hour with AM/PM?
- result = ParseFormat(time, _T("%I:%M:%S %p"));
+ // 12hour with AM/PM but without seconds?
+ result = ParseFormat(time, _T("%I:%M %p"));
}
if ( !result )
if ( !result )
{
- // 12hour with AM/PM but without seconds?
- result = ParseFormat(time, _T("%I:%M %p"));
+ // just the hour and AM/PM?
+ result = ParseFormat(time, _T("%I %p"));
}
if ( !result )
if ( !result )
{
- // just the hour and AM/PM?
- result = ParseFormat(time, _T("%I %p"));
+ // 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"));
}
// TODO: parse timezones
// wxTimeSpan
// ============================================================================
+// this enum is only used in wxTimeSpan::Format() below but we can't declare
+// it locally to the method as it provokes an internal compiler error in egcs
+// 2.91.60 when building with -O2
+enum TimeSpanPart
+{
+ Part_Week,
+ Part_Day,
+ Part_Hour,
+ Part_Min,
+ Part_Sec,
+ Part_MSec
+};
+
// not all strftime(3) format specifiers make sense here because, for example,
// a time span doesn't have a year nor a timezone
//
wxString str;
str.Alloc(wxStrlen(format));
+ // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
+ //
+ // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
+ // question is what should ts.Format("%S") do? The code here returns "3273"
+ // in this case (i.e. the total number of seconds, not just seconds % 60)
+ // because, for me, this call means "give me entire time interval in
+ // seconds" and not "give me the seconds part of the time interval"
+ //
+ // If we agree that it should behave like this, it is clear that the
+ // interpretation of each format specifier depends on the presence of the
+ // other format specs in the string: if there was "%H" before "%M", we
+ // should use GetMinutes() % 60, otherwise just GetMinutes() &c
+
+ // we remember the most important unit found so far
+ TimeSpanPart partBiggest = Part_MSec;
+
for ( const wxChar *pch = format; *pch; pch++ )
{
wxChar ch = *pch;
if ( ch == _T('%') )
{
- wxString tmp;
+ // the start of the format specification of the printf() below
+ wxString fmtPrefix = _T('%');
+
+ // the number
+ long n;
ch = *++pch; // get the format spec char
switch ( ch )
// fall through
case _T('%'):
- // will get to str << ch below
- break;
+ str += ch;
+
+ // skip the part below switch
+ continue;
case _T('D'):
- tmp.Printf(_T("%d"), GetDays());
+ n = GetDays();
+ if ( partBiggest < Part_Day )
+ {
+ n %= DAYS_PER_WEEK;
+ }
+ else
+ {
+ partBiggest = Part_Day;
+ }
break;
case _T('E'):
- tmp.Printf(_T("%d"), GetWeeks());
+ partBiggest = Part_Week;
+ n = GetWeeks();
break;
case _T('H'):
- tmp.Printf(_T("%02d"), GetHours());
+ n = GetHours();
+ if ( partBiggest < Part_Hour )
+ {
+ n %= HOURS_PER_DAY;
+ }
+ else
+ {
+ partBiggest = Part_Hour;
+ }
+
+ fmtPrefix += _T("02");
break;
case _T('l'):
- tmp.Printf(_T("%03ld"), GetMilliseconds().ToLong());
+ n = GetMilliseconds().ToLong();
+ if ( partBiggest < Part_MSec )
+ {
+ n %= 1000;
+ }
+ //else: no need to reset partBiggest to Part_MSec, it is
+ // the least significant one anyhow
+
+ fmtPrefix += _T("03");
break;
case _T('M'):
- tmp.Printf(_T("%02d"), GetMinutes());
+ n = GetMinutes();
+ if ( partBiggest < Part_Min )
+ {
+ n %= MIN_PER_HOUR;
+ }
+ else
+ {
+ partBiggest = Part_Min;
+ }
+
+ fmtPrefix += _T("02");
break;
case _T('S'):
- tmp.Printf(_T("%02ld"), GetSeconds().ToLong());
+ n = GetSeconds().ToLong();
+ if ( partBiggest < Part_Sec )
+ {
+ n %= SEC_PER_MIN;
+ }
+ else
+ {
+ partBiggest = Part_Sec;
+ }
+
+ fmtPrefix += _T("02");
break;
}
- if ( !!tmp )
- {
- str += tmp;
-
- // skip str += ch below
- continue;
- }
+ str += wxString::Format(fmtPrefix + _T("ld"), n);
+ }
+ else
+ {
+ // normal character, just copy
+ str += ch;
}
-
- str += ch;
}
return str;
return holidays.GetCount();
}
-
+#endif // wxUSE_DATETIME