1. corrected compilation of wxTime/wxDate

2. wxDateTime::Format() seems to work (look at it to see why I'm so happy)


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@5049 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
1999-12-21 15:40:13 +00:00
parent 75399efbb2
commit 68ee7c4730
6 changed files with 367 additions and 79 deletions

View File

@@ -123,7 +123,7 @@ public:
wxDate &Set(long lJulian) wxDate &Set(long lJulian)
{ m_date.Set((double)(lJulian + 0.5)); return (wxDate&)*this; } { m_date.Set((double)(lJulian + 0.5)); return (wxDate&)*this; }
wxDate &Set(int nMonth, int nDay, int nYear) wxDate &Set(int nMonth, int nDay, int nYear)
{ m_date.Set(nDay, (wxDateTime::Month)nMonth, nYear); return (wxDate&)*this; } { m_date.Set(nDay, (wxDateTime::Month)nMonth, nYear); return *this; }
// May also pass neg# to decrement // May also pass neg# to decrement
wxDate &AddWeeks(int nCount = 1) wxDate &AddWeeks(int nCount = 1)

View File

@@ -798,7 +798,7 @@ public:
inline wxDateTime& operator+=(const wxDateSpan& diff); inline wxDateTime& operator+=(const wxDateSpan& diff);
// return the difference of the date with a date span // return the difference of the date with a date span
inline wxDateTime& Substract(const wxDateSpan& diff) const; inline wxDateTime Substract(const wxDateSpan& diff) const;
// substract a date span (positive or negative) // substract a date span (positive or negative)
inline wxDateTime& Substract(const wxDateSpan& diff); inline wxDateTime& Substract(const wxDateSpan& diff);
// substract a date span (positive or negative) // substract a date span (positive or negative)
@@ -896,18 +896,23 @@ public:
// return the timespan for the given number of seconds // return the timespan for the given number of seconds
static wxTimeSpan Seconds(int sec) { return wxTimeSpan(0, 0, sec); } static wxTimeSpan Seconds(int sec) { return wxTimeSpan(0, 0, sec); }
static wxTimeSpan Second() { return Seconds(1); }
// return the timespan for the given number of minutes // return the timespan for the given number of minutes
static wxTimeSpan Minutes(int min) { return wxTimeSpan(0, min, 0 ); } static wxTimeSpan Minutes(int min) { return wxTimeSpan(0, min, 0 ); }
static wxTimeSpan Minute() { return Minutes(1); }
// return the timespan for the given number of hours // return the timespan for the given number of hours
static wxTimeSpan Hours(int hours) { return wxTimeSpan(hours, 0, 0); } static wxTimeSpan Hours(int hours) { return wxTimeSpan(hours, 0, 0); }
static wxTimeSpan Hour() { return Hours(1); }
// return the timespan for the given number of days // return the timespan for the given number of days
static wxTimeSpan Days(int days) { return Hours(24 * days); } static wxTimeSpan Days(int days) { return Hours(24 * days); }
static wxTimeSpan Day() { return Days(1); }
// return the timespan for the given number of weeks // return the timespan for the given number of weeks
static wxTimeSpan Weeks(int days) { return Days(7 * days); } static wxTimeSpan Weeks(int days) { return Days(7 * days); }
static wxTimeSpan Week() { return Weeks(1); }
// default ctor constructs the 0 time span // default ctor constructs the 0 time span
wxTimeSpan() { } wxTimeSpan() { }
@@ -1073,15 +1078,19 @@ public:
// get an object for the given number of days // get an object for the given number of days
static wxDateSpan Days(int days) { return wxDateSpan(0, 0, 0, days); } static wxDateSpan Days(int days) { return wxDateSpan(0, 0, 0, days); }
static wxDateSpan Day() { return Days(1); }
// get an object for the given number of weeks // get an object for the given number of weeks
static wxDateSpan Weeks(int weeks) { return wxDateSpan(0, 0, weeks, 0); } static wxDateSpan Weeks(int weeks) { return wxDateSpan(0, 0, weeks, 0); }
static wxDateSpan Week() { return Weeks(1); }
// get an object for the given number of months // get an object for the given number of months
static wxDateSpan Months(int mon) { return wxDateSpan(0, mon, 0, 0); } static wxDateSpan Months(int mon) { return wxDateSpan(0, mon, 0, 0); }
static wxDateSpan Month() { return Months(1); }
// get an object for the given number of years // get an object for the given number of years
static wxDateSpan Years(int years) { return wxDateSpan(years, 0, 0, 0); } static wxDateSpan Years(int years) { return wxDateSpan(years, 0, 0, 0); }
static wxDateSpan Year() { return Years(1); }
// default copy ctor is ok // default copy ctor is ok

View File

@@ -222,11 +222,21 @@ wxTimeSpan wxDateTime::Substract(const wxDateTime& datetime) const
return wxTimeSpan(datetime.GetValue() - GetValue()); return wxTimeSpan(datetime.GetValue() - GetValue());
} }
wxDateTime wxDateTime::Add(const wxDateSpan& diff) const
{
return wxDateTime(*this).Add(diff);
}
wxDateTime& wxDateTime::Substract(const wxDateSpan& diff) wxDateTime& wxDateTime::Substract(const wxDateSpan& diff)
{ {
return Add(diff.Negate()); return Add(diff.Negate());
} }
wxDateTime wxDateTime::Substract(const wxDateSpan& diff) const
{
return wxDateTime(*this).Substract(diff);
}
wxDateTime& wxDateTime::operator-=(const wxDateSpan& diff) wxDateTime& wxDateTime::operator-=(const wxDateSpan& diff)
{ {
return Substract(diff); return Substract(diff);

View File

@@ -815,6 +815,45 @@ static void TestTimeDST()
} }
} }
// test wxDateTime -> text conversion
static void TestTimeFormat()
{
puts("\n*** wxDateTime formatting test ***");
static const char *formatTestFormats[] =
{
"%c",
"Date is %A, %d of %B, in year %Y",
"Date is %x, time is %X",
"Time is %H:%M:%S or %I:%M:%S %p",
"The day of year: %j, the week of year: %W",
};
static const Date formatTestDates[] =
{
{ }, // unused
{ 29, wxDateTime::May, 1976, 18, 30, 00 },
{ 31, wxDateTime::Dec, 1999, 23, 30, 00 },
{ 29, wxDateTime::May, 2076, 18, 30, 00 },
{ 29, wxDateTime::Feb, 2400, 02, 15, 25 },
{ 01, wxDateTime::Jan, -52, 03, 16, 47 },
};
// an extra test (as it doesn't depend on date, don't do it in the loop)
printf("%s\n", wxDateTime::Now().Format("Our timezone is %Z").c_str());
for ( size_t d = 0; d < WXSIZEOF(formatTestDates); d++ )
{
puts("");
wxDateTime dt = d == 0 ? wxDateTime::Now() : formatTestDates[d].DT();
for ( size_t n = 0; n < WXSIZEOF(formatTestFormats); n++ )
{
printf("%s\n", dt.Format(formatTestFormats[n]).c_str());
}
}
}
// test text -> wxDateTime conversion // test text -> wxDateTime conversion
static void TestTimeParse() static void TestTimeParse()
{ {
@@ -829,7 +868,8 @@ static void TestTimeParse()
static const ParseTestData parseTestDates[] = static const ParseTestData parseTestDates[] =
{ {
"Sat, 18 Dec 1999 00:46:40 +0100", { 18, wxDateTime::Dec, 1999, 00, 46, 40 }, TRUE, { "Sat, 18 Dec 1999 00:46:40 +0100", { 18, wxDateTime::Dec, 1999, 00, 46, 40 }, TRUE },
{ "Wed, 1 Dec 1999 05:17:20 +0300", { 1, wxDateTime::Dec, 1999, 03, 17, 20 }, TRUE },
}; };
for ( size_t n = 0; n < WXSIZEOF(parseTestDates); n++ ) for ( size_t n = 0; n < WXSIZEOF(parseTestDates); n++ )
@@ -868,6 +908,8 @@ static void TestTimeParse()
} }
} }
#if 0
// test compatibility with the old wxDate/wxTime classes // test compatibility with the old wxDate/wxTime classes
static void TestTimeCompatibility() static void TestTimeCompatibility()
{ {
@@ -896,6 +938,8 @@ static void TestTimeCompatibility()
} }
} }
#endif // 0
#endif // TEST_TIME #endif // TEST_TIME
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -1348,7 +1392,8 @@ int main(int argc, char **argv)
TestTimeWNumber(); TestTimeWNumber();
TestTimeParse(); TestTimeParse();
} }
TestTimeCompatibility();
TestTimeFormat();
#endif // TEST_TIME #endif // TEST_TIME
wxUninitialize(); wxUninitialize();

View File

@@ -213,10 +213,10 @@ static long GetTruncatedJDN(wxDateTime::wxDateTime_t day,
// this function is a wrapper around strftime(3) // this function is a wrapper around strftime(3)
static wxString CallStrftime(const wxChar *format, const tm* tm) static wxString CallStrftime(const wxChar *format, const tm* tm)
{ {
wxChar buf[1024]; wxChar buf[4096];
if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) ) if ( !wxStrftime(buf, WXSIZEOF(buf), format, tm) )
{ {
// is ti really possible that 1024 is too short? // buffer is too small?
wxFAIL_MSG(_T("strftime() failed")); wxFAIL_MSG(_T("strftime() failed"));
} }
@@ -574,13 +574,18 @@ wxString wxDateTime::GetMonthName(wxDateTime::Month month, bool abbr)
{ {
wxCHECK_MSG( month != Inv_Month, _T(""), _T("invalid month") ); wxCHECK_MSG( month != Inv_Month, _T(""), _T("invalid month") );
// notice that we must set all the fields to avoid confusing libc (GNU one
// gets confused to a crash if we don't do this)
tm tm; tm tm;
tm.tm_hour = tm.tm_hour =
tm.tm_min = tm.tm_min =
tm.tm_sec = 0; tm.tm_sec =
tm.tm_wday =
tm.tm_yday = 0;
tm.tm_mday = 1; tm.tm_mday = 1;
tm.tm_mon = month; tm.tm_mon = month;
tm.tm_year = 76; // any year will do tm.tm_year = 76; // any year will do
tm.tm_isdst = -1;
return CallStrftime(abbr ? _T("%b") : _T("%B"), &tm); return CallStrftime(abbr ? _T("%b") : _T("%B"), &tm);
} }
@@ -1407,6 +1412,7 @@ wxDateTime::wxDateTime_t wxDateTime::GetDayOfYear(const TimeZone& tz) const
wxDateTime::wxDateTime_t wxDateTime::GetWeekOfYear(const TimeZone& tz) const wxDateTime::wxDateTime_t wxDateTime::GetWeekOfYear(const TimeZone& tz) const
{ {
#if 0
// the first week of the year is the one which contains Jan, 4 (according // the first week of the year is the one which contains Jan, 4 (according
// to ISO standard rule), so the year day N0 = 4 + 7*W always lies in the // to ISO standard rule), so the year day N0 = 4 + 7*W always lies in the
// week W+1. As any day N = 7*W + 4 + (N - 4)%7, it lies in the same week // week W+1. As any day N = 7*W + 4 + (N - 4)%7, it lies in the same week
@@ -1428,6 +1434,25 @@ wxDateTime::wxDateTime_t wxDateTime::GetWeekOfYear(const TimeZone& tz) const
} }
return week; return week;
#else // this seems to be a bit simpler and I believe is also correct
return (WeekDay)((GetDayOfYear() - (GetWeekDay() - 1 + 7) % 7 + 7) / 7);
#endif // 0/1
}
wxDateTime::wxDateTime_t wxDateTime::GetWeekOfMonth(const TimeZone& tz) const
{
size_t nWeek = 0;
wxDateTime dt(*this);
do
{
nWeek++;
dt -= wxTimeSpan::Week();
}
while ( dt.GetMonth(tz) == GetMonth(tz) );
return nWeek;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -1546,45 +1571,123 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
//else: use generic code below //else: use generic code below
} }
// use a hack and still use strftime(): first find the YEAR which is a year // we only parse ANSI C format specifications here, no POSIX 2
// in the strftime() range (1970 - 2038) whose Jan 1 falls on the same week // complications, no GNU extensions
// day as the Jan 1 of the real year. Then make a copy of the format and Tm tm = GetTm(tz);
// replace all occurences of YEAR in it with some unique string not
// appearing anywhere else in it, then use strftime() to format the date in // used for calls to strftime() when we only deal with time
// year YEAR and then replace YEAR back by the real year and the unique struct tm tmTimeOnly;
// replacement string back with YEAR. Notice that "all occurences of YEAR" tmTimeOnly.tm_hour = tm.hour;
tmTimeOnly.tm_min = tm.min;
tmTimeOnly.tm_sec = tm.sec;
tmTimeOnly.tm_wday = 0;
tmTimeOnly.tm_yday = 0;
tmTimeOnly.tm_mday = 1; // any date will do
tmTimeOnly.tm_mon = 0;
tmTimeOnly.tm_year = 76;
tmTimeOnly.tm_isdst = 0; // no DST, we adjust for tz ourselves
wxString tmp, res;
for ( const wxChar *p = format; *p; p++ )
{
if ( *p != _T('%') )
{
// copy as is
res += *p;
continue;
}
// start of the format specification
switch ( *++p )
{
case _T('a'): // a weekday name
case _T('A'):
// second parameter should be TRUE for abbreviated names
res += GetWeekDayName(tm.GetWeekDay(), *p == _T('a'));
break;
case _T('b'): // a month name
case _T('B'):
// second parameter should be TRUE for abbreviated names
res += GetMonthName(tm.mon, *p == _T('b'));
break;
case _T('c'): // locale default date and time representation
case _T('x'): // locale default date representation
//
// the problem: there is no way to know what do these format
// specifications correspond to for the current locale.
//
// the solution: use a hack and still use strftime(): first
// 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
// 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! // means all occurences of 4 digit as well as 2 digit form!
//
// NB: may be it would be simpler to "honestly" reimplement strftime()? // the bugs: we assume that neither of %c nor %x contains any
// fields which may change between the YEAR and real year. For
// find the YEAR: normally, for any year X, Jan 1 or the year X + 28 is the // example, the week number (%U, %W) and the day number (%j)
// same weekday as Jan 1 of X (because the weekday advances by 1 for each // will change if one of these years is leap and the other one
// normal X and by 2 for each leap X, hence by 5 every 4 years or by 35 // is not!
// which is 0 mod 7 every 28 years) but this rule breaks down if there are {
// years between X and Y which are divisible by 4 but not leap (i.e. // find the YEAR: normally, for any year X, Jan 1 or the
// divisible by 100 but not 400), hence the correction. // year X + 28 is the same weekday as Jan 1 of X (because
// the weekday advances by 1 for each normal X and by 2
// for each leap X, hence by 5 every 4 years or by 35
// which is 0 mod 7 every 28 years) but this rule breaks
// down if there are years between X and Y which are
// divisible by 4 but not leap (i.e. divisible by 100 but
// not 400), hence the correction.
int yearReal = GetYear(tz); int yearReal = GetYear(tz);
int year = 1970 + yearReal % 28; int mod28 = yearReal % 28;
int nCenturiesInBetween = (year / 100) - (yearReal / 100); // be careful to not go too far - we risk to leave the
int nLostWeekDays = nCenturiesInBetween - (nCenturiesInBetween / 400); // supported range
int year;
if ( mod28 < 10 )
{
year = 1988 + mod28; // 1988 == 0 (mod 28)
}
else
{
year = 1970 + mod28 - 10; // 1970 == 10 (mod 28)
}
// we have to gain back the "lost" weekdays... int nCentury = year / 100,
nCenturyReal = yearReal / 100;
// need to adjust for the years divisble by 400 which are
// not leap but are counted like leap ones if we just take
// the number of centuries in between for nLostWeekDays
int nLostWeekDays = (nCentury - nCenturyReal) -
(nCentury / 4 - nCenturyReal / 4);
// we have to gain back the "lost" weekdays: note that the
// effect of this loop is to not do anything to
// nLostWeekDays (which we won't use any more), but to
// (indirectly) set the year correctly
while ( (nLostWeekDays % 7) != 0 ) while ( (nLostWeekDays % 7) != 0 )
{ {
nLostWeekDays += year++ % 4 ? 1 : 2; nLostWeekDays += year++ % 4 ? 1 : 2;
} }
// at any rate, we can't go further than 1997 + 28! // at any rate, we couldn't go further than 1988 + 9 + 28!
wxASSERT_MSG( year < 2030, _T("logic error in wxDateTime::Format") ); wxASSERT_MSG( year < 2030,
_T("logic error in wxDateTime::Format") );
wxString strYear, strYear2; wxString strYear, strYear2;
strYear.Printf(_T("%d"), year); strYear.Printf(_T("%d"), year);
strYear2.Printf(_T("%d"), year % 100); strYear2.Printf(_T("%d"), year % 100);
// find two strings not occuring in format (this is surely not optimal way // find two strings not occuring in format (this is surely
// of doing it... improvements welcome!) // not optimal way of doing it... improvements welcome!)
wxString fmt = format; wxString fmt = format;
wxString replacement = (wxChar)-1; wxString replacement = (wxChar)-1;
while ( fmt.Find(replacement) != wxNOT_FOUND ) while ( fmt.Find(replacement) != wxNOT_FOUND )
@@ -1603,10 +1706,26 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
if ( !wasReplaced ) if ( !wasReplaced )
wasReplaced = fmt.Replace(strYear2, replacement2) > 0; wasReplaced = fmt.Replace(strYear2, replacement2) > 0;
// use strftime() to format the same date but in supported year // use strftime() to format the same date but in supported
wxDateTime dt(*this); // year
dt.SetYear(year); //
wxString str = dt.Format(format, tz); // NB: we assume that strftime() doesn't check for the
// date validity and will happily format the date
// corresponding to Feb 29 of a non leap year (which
// may happen if yearReal was leap and year is not)
struct tm tmAdjusted;
tmAdjusted.tm_hour = tm.hour;
tmAdjusted.tm_min = tm.min;
tmAdjusted.tm_sec = tm.sec;
tmAdjusted.tm_wday = tm.GetWeekDay();
tmAdjusted.tm_yday = GetDayOfYear();
tmAdjusted.tm_mday = tm.mday;
tmAdjusted.tm_mon = tm.mon;
tmAdjusted.tm_year = year - 1900;
tmAdjusted.tm_isdst = 0; // no DST, already adjusted
wxString str = CallStrftime(*p == _T('c') ? _T("%c")
: _T("%x"),
&tmAdjusted);
// now replace the occurence of 1999 with the real year // now replace the occurence of 1999 with the real year
wxString strYearReal, strYearReal2; wxString strYearReal, strYearReal2;
@@ -1622,7 +1741,110 @@ wxString wxDateTime::Format(const wxChar *format, const TimeZone& tz) const
str.Replace(replacement, strYear); str.Replace(replacement, strYear);
} }
return str; res += str;
}
break;
case _T('d'): // day of a month (01-31)
tmp.Printf(_T("%02d"), tm.mday);
res += tmp;
break;
case _T('H'): // hour in 24h format (00-23)
tmp.Printf(_T("%02d"), tm.hour);
res += tmp;
break;
case _T('I'): // hour in 12h format (01-12)
{
// 24h -> 12h, 0h -> 12h too
int hour12 = tm.hour > 12 ? tm.hour - 12
: tm.hour ? tm.hour : 12;
tmp.Printf(_T("%02d"), hour12);
res += tmp;
}
break;
case _T('j'): // day of the year
tmp.Printf(_T("%03d"), GetDayOfYear(tz));
res += tmp;
break;
case _T('m'): // month as a number (01-12)
tmp.Printf(_T("%02d"), tm.mon + 1);
res += tmp;
break;
case _T('M'): // minute as a decimal number (00-59)
tmp.Printf(_T("%02d"), tm.min);
res += tmp;
break;
case _T('p'): // AM or PM string
res += CallStrftime(_T("%p"), &tmTimeOnly);
break;
case _T('S'): // second as a decimal number (00-61)
tmp.Printf(_T("%02d"), tm.sec);
res += tmp;
break;
case _T('U'): // week number in the year (Sunday 1st week day)
tmp.Printf(_T("%02d"),
(GetDayOfYear(tz) - tm.GetWeekDay() + 7) / 7);
res += tmp;
break;
case _T('W'): // week number in the year (Monday 1st week day)
tmp.Printf(_T("%02d"), GetWeekOfYear(tz));
res += tmp;
break;
case _T('w'): // weekday as a number (0-6), Sunday = 0
tmp.Printf(_T("%d"), tm.GetWeekDay());
res += tmp;
break;
// case _T('x'): -- handled with "%c"
case _T('X'): // locale default time representation
// just use strftime() to format the time for us
res += CallStrftime(_T("%X"), &tmTimeOnly);
break;
case _T('y'): // year without century (00-99)
tmp.Printf(_T("%02d"), tm.year % 100);
res += tmp;
break;
case _T('Y'): // year with century
tmp.Printf(_T("%04d"), tm.year);
res += tmp;
break;
case _T('Z'): // timezone name
res += CallStrftime(_T("%Z"), &tmTimeOnly);
break;
default:
wxFAIL_MSG(_T("unknown format specificator"));
// fall through and just copy it nevertheless
case _T('%'): // a percent sign
res += *p;
break;
case 0:
wxFAIL_MSG(_T("missing format at the end of string"));
// just put the '%' which was the last char in format
res += _T('%');
break;
}
}
return res;
} }
// this function parses a string in (strict) RFC 822 format: see the section 5 // this function parses a string in (strict) RFC 822 format: see the section 5

View File

@@ -11,6 +11,8 @@
#ifdef __GNUG__ #ifdef __GNUG__
#pragma implementation "variant.h" #pragma implementation "variant.h"
#pragma implementation "time.h"
#pragma implementation "date.h"
#endif #endif
// For compilers that support precompilation, includes "wx/wx.h". // For compilers that support precompilation, includes "wx/wx.h".