fix: remember the first/last weekday flags from a timezone DST
If a date is given in a relative form with first/last parameters, we must remember them in order to select the appropriate day of the week from the appropriate year.
This commit is contained in:
parent
0cebb2dc52
commit
09dc6782c8
|
@ -40,6 +40,85 @@ namespace Winix
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
TimeZone::DstDate::DstDate()
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TimeZone::DstDate::Clear()
|
||||||
|
{
|
||||||
|
date_type = ordinary_date;
|
||||||
|
weekday = 0;
|
||||||
|
date.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int TimeZone::DstDate::Compare(const pt::Date & utc_date) const
|
||||||
|
{
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
if( date_type == ordinary_date )
|
||||||
|
{
|
||||||
|
res = date.Compare(utc_date, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if( date_type == first_weekday )
|
||||||
|
{
|
||||||
|
pt::Date tmp_date = CalculateRelativeDate(utc_date.year, date.month, true, weekday);
|
||||||
|
res = tmp_date.Compare(utc_date, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if( date_type == last_weekday )
|
||||||
|
{
|
||||||
|
pt::Date tmp_date = CalculateRelativeDate(utc_date.year, date.month, false, weekday);
|
||||||
|
res = tmp_date.Compare(utc_date, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pt::Date TimeZone::DstDate::CalculateRelativeDate(int year, int month, bool is_first, int weekday)
|
||||||
|
{
|
||||||
|
pt::Date new_date;
|
||||||
|
new_date.year = year;
|
||||||
|
new_date.month = month;
|
||||||
|
|
||||||
|
if( is_first )
|
||||||
|
{
|
||||||
|
new_date.day = 1;
|
||||||
|
int w = new_date.WeekDay();
|
||||||
|
|
||||||
|
if( w < weekday )
|
||||||
|
{
|
||||||
|
new_date.day += (weekday - w);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if( w > weekday )
|
||||||
|
{
|
||||||
|
new_date.day += (weekday - w) + 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
new_date.day = pt::Date::MonthLen(new_date.year, new_date.month);
|
||||||
|
int w = new_date.WeekDay();
|
||||||
|
|
||||||
|
if( w > weekday )
|
||||||
|
{
|
||||||
|
new_date.day -= (w - weekday);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if( w < weekday )
|
||||||
|
{
|
||||||
|
new_date.day -= (w - weekday) + 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_date;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TimeZone::Dst::Dst()
|
TimeZone::Dst::Dst()
|
||||||
|
@ -57,9 +136,12 @@ void TimeZone::Dst::Clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool TimeZone::Dst::IsDstUsed(const pt::Date & utc_date) const
|
bool TimeZone::Dst::IsDstUsed(const pt::Date & utc_date) const
|
||||||
{
|
{
|
||||||
return has_dst && start.Compare(utc_date, true) <= 0 && utc_date.Compare(end) < 0;
|
return has_dst && start.Compare(utc_date) <= 0 && end.Compare(utc_date) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -309,42 +391,17 @@ int TimeZone::ParseDstDateWeekday(const wchar_t * date_str, const wchar_t ** aft
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TimeZone::ParseDstDateDay(bool is_first, int weekday, pt::Date & date)
|
bool TimeZone::ParseDstDate(int year, const wchar_t * date_str, DstDate & dst_date)
|
||||||
{
|
|
||||||
int len = 1;
|
|
||||||
int adder = 1;
|
|
||||||
|
|
||||||
if( is_first )
|
|
||||||
{
|
|
||||||
len = pt::Date::MonthLen(date.year, date.month);
|
|
||||||
date.day = 1;
|
|
||||||
adder = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
len = pt::Date::MonthLen(date.year, date.month);
|
|
||||||
date.day = len;
|
|
||||||
adder = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i=0 ; i < len && date.WeekDay() != weekday ; ++i)
|
|
||||||
{
|
|
||||||
date.day += adder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool TimeZone::ParseDstDate(int year, const wchar_t * date_str, pt::Date & date)
|
|
||||||
{
|
{
|
||||||
bool was_overflow = false;
|
bool was_overflow = false;
|
||||||
bool is_ok = false;
|
bool is_ok = false;
|
||||||
const wchar_t * after_date_str;
|
const wchar_t * after_date_str;
|
||||||
|
|
||||||
date.Clear();
|
dst_date.Clear();
|
||||||
date.year = year;
|
dst_date.date.year = year;
|
||||||
|
|
||||||
date_str = pt::skip_white(date_str, false, false);
|
date_str = pt::skip_white(date_str, false, false);
|
||||||
date.month = pt::to_i(date_str, 10, &after_date_str, &was_overflow, true);
|
dst_date.date.month = pt::to_i(date_str, 10, &after_date_str, &was_overflow, true);
|
||||||
|
|
||||||
if( !was_overflow && after_date_str != date_str )
|
if( !was_overflow && after_date_str != date_str )
|
||||||
{
|
{
|
||||||
|
@ -353,35 +410,30 @@ bool TimeZone::ParseDstDate(int year, const wchar_t * date_str, pt::Date & date)
|
||||||
if( pt::is_substr(L"first", date_str) )
|
if( pt::is_substr(L"first", date_str) )
|
||||||
{
|
{
|
||||||
date_str += 5; // length of "first"
|
date_str += 5; // length of "first"
|
||||||
int weekday = ParseDstDateWeekday(date_str, &date_str);
|
dst_date.date_type = DstDate::first_weekday;
|
||||||
|
dst_date.weekday = ParseDstDateWeekday(date_str, &date_str);
|
||||||
if( weekday >= 0 )
|
dst_date.date.day = 1; // to return true from IsCorrectDate() at the end of this method, will not be used in the future
|
||||||
{
|
is_ok = (dst_date.weekday >= 0);
|
||||||
ParseDstDateDay(true, weekday, date);
|
|
||||||
is_ok = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if( pt::is_substr(L"last", date_str) )
|
if( pt::is_substr(L"last", date_str) )
|
||||||
{
|
{
|
||||||
date_str += 4; // length of "last"
|
date_str += 4; // length of "last"
|
||||||
int weekday = ParseDstDateWeekday(date_str, &date_str);
|
dst_date.date_type = DstDate::last_weekday;
|
||||||
|
dst_date.weekday = ParseDstDateWeekday(date_str, &date_str);
|
||||||
if( weekday >= 0 )
|
dst_date.date.day = 1;
|
||||||
{
|
is_ok = (dst_date.weekday >= 0);
|
||||||
ParseDstDateDay(false, weekday, date);
|
|
||||||
is_ok = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
date.day = pt::to_i(date_str, 10, &after_date_str, &was_overflow, true);
|
dst_date.date_type = DstDate::ordinary_date;
|
||||||
|
dst_date.date.day = pt::to_i(date_str, 10, &after_date_str, &was_overflow, true);
|
||||||
is_ok = !was_overflow && after_date_str != date_str;
|
is_ok = !was_overflow && after_date_str != date_str;
|
||||||
date_str = after_date_str;
|
date_str = after_date_str;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return is_ok && date.ParseTime(date_str) && date.IsCorrectDate();
|
return is_ok && dst_date.date.ParseTime(date_str) && dst_date.date.IsCorrectDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -53,37 +53,73 @@ class TimeZone
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
||||||
|
struct DstDate
|
||||||
|
{
|
||||||
|
enum DateType {
|
||||||
|
ordinary_date = 0,
|
||||||
|
first_weekday,
|
||||||
|
last_weekday,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* for ordinary_date
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
DateType date_type;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* a week day (0-sunday, 1-monday, ..., 6-saturday)
|
||||||
|
* used when date_type is first_weekday or last_weekday
|
||||||
|
*/
|
||||||
|
int weekday;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if date_type=ordinary_date the date is used as normal date represented in UTC time
|
||||||
|
* if date_type=first_weekday we are looking for a first weekday in the date.month (year will be changed)
|
||||||
|
* if date_type=last_weekday we are looking for the last weekady in the date.month (year will be changed)
|
||||||
|
*/
|
||||||
|
pt::Date date;
|
||||||
|
|
||||||
|
|
||||||
|
DstDate();
|
||||||
|
void Clear();
|
||||||
|
int Compare(const pt::Date & utc_date) const;
|
||||||
|
static pt::Date CalculateRelativeDate(int year, int month, bool is_first, int weekday);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct Dst
|
struct Dst
|
||||||
{
|
{
|
||||||
// true if a time zone has daylight saving time
|
/*
|
||||||
|
* true if a time zone has daylight saving time
|
||||||
|
*/
|
||||||
bool has_dst;
|
bool has_dst;
|
||||||
|
|
||||||
// time zone daylight saving time (used if has_dst is true)
|
/*
|
||||||
// the 'year' field is the same in 'start' and 'end'
|
* time zone daylight saving time (used if has_dst is true)
|
||||||
// start and end are represented in UTC time
|
*/
|
||||||
pt::Date start, end;
|
DstDate start, end;
|
||||||
|
|
||||||
// time zone daylight saving time offset
|
/*
|
||||||
// used when has_dst is true and the date is whithin start and end
|
* time zone daylight saving time offset
|
||||||
// this offset should be added to time zone offset
|
* used when has_dst is true and the date is whithin start and end
|
||||||
|
* this offset should be added to time zone offset
|
||||||
|
*/
|
||||||
time_t offset;
|
time_t offset;
|
||||||
|
|
||||||
Dst();
|
Dst();
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
// checking whether specified 'date' is in the range of <start, end>
|
/*
|
||||||
// the year field in date, start and end is ignored
|
* checking whether specified 'date' is in the range of Dst
|
||||||
// has_dst must be true
|
* has_dst must be true
|
||||||
|
*/
|
||||||
bool IsDstUsed(const pt::Date & utc_date) const;
|
bool IsDstUsed(const pt::Date & utc_date) const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
TimeZone();
|
TimeZone();
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
|
|
||||||
|
@ -148,9 +184,8 @@ private:
|
||||||
time_t ParseStrOffset(const wchar_t * str);
|
time_t ParseStrOffset(const wchar_t * str);
|
||||||
time_t GetOffset(pt::Space & space);
|
time_t GetOffset(pt::Space & space);
|
||||||
int ParseDstDateWeekday(const wchar_t * date_str, const wchar_t ** after_date_str);
|
int ParseDstDateWeekday(const wchar_t * date_str, const wchar_t ** after_date_str);
|
||||||
void ParseDstDateDay(bool is_first, int weekday, pt::Date & date);
|
|
||||||
const wchar_t * SkipDstDateSeparator(const wchar_t * date_str);
|
const wchar_t * SkipDstDateSeparator(const wchar_t * date_str);
|
||||||
bool ParseDstDate(int year, const wchar_t * date_str, pt::Date & date);
|
bool ParseDstDate(int year, const wchar_t * date_str, DstDate & dst_date);
|
||||||
bool SetTzDst(pt::Space & year);
|
bool SetTzDst(pt::Space & year);
|
||||||
static void PrintOffsetPart(long val, pt::Stream & str);
|
static void PrintOffsetPart(long val, pt::Stream & str);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue