allow to use a first|last weekday name when defining a timezone dst
the format of a 'start' or 'end' field in a dst is:
MM-[first|last]:[monday|tuesday|wednesday|thursday|friday|saturday|sunday] HH:MM:SS
or just as beforehand: MM-DD HH:MM:SS
a sample timezone:
{
name = "tz_+01:00d"
id = "40"
offset_str = "+01:00"
dst = (
{
year = "2001"
has_dst = true
offset_str = "+01:00"
start = "03-last-sunday 01:00"
end = "10-last-sunday 01:00"
}
) # end of dst
}
This commit is contained in:
@@ -57,40 +57,9 @@ void TimeZone::Dst::Clear()
|
||||
}
|
||||
|
||||
|
||||
bool TimeZone::Dst::IsDstUsed(const pt::Date & date) const
|
||||
bool TimeZone::Dst::IsDstUsed(const pt::Date & utc_date) const
|
||||
{
|
||||
if( !has_dst )
|
||||
return false;
|
||||
|
||||
if( Compare(start, date) <= 0 ) // !! CHECK ME <= or < ? (what about the one second?)
|
||||
if( Compare(date, end) < 0 )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int TimeZone::Dst::Compare(const pt::Date & date1, const pt::Date & date2) const
|
||||
{
|
||||
// year is ignored
|
||||
|
||||
if( date1.month != date2.month )
|
||||
return date1.month - date2.month;
|
||||
|
||||
if( date1.day != date2.day )
|
||||
return date1.day - date2.day;
|
||||
|
||||
if( date1.hour != date2.hour )
|
||||
return date1.hour - date2.hour;
|
||||
|
||||
if( date1.min != date2.min )
|
||||
return date1.min - date2.min;
|
||||
|
||||
if( date1.sec != date2.sec )
|
||||
return date1.sec - date2.sec;
|
||||
|
||||
// dates are equal
|
||||
return 0;
|
||||
return has_dst && start.Compare(utc_date, true) <= 0 && utc_date.Compare(end) < 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -290,9 +259,135 @@ time_t TimeZone::GetOffset(pt::Space & space)
|
||||
}
|
||||
|
||||
|
||||
const wchar_t * TimeZone::SkipDstDateSeparator(const wchar_t * date_str)
|
||||
{
|
||||
date_str = pt::skip_white(date_str, false, false);
|
||||
|
||||
// the same separators as in the pikotools library
|
||||
if( *date_str == '-' || *date_str == '/' || *date_str == '.' )
|
||||
{
|
||||
++date_str;
|
||||
}
|
||||
|
||||
return pt::skip_white(date_str, false, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int TimeZone::ParseDstDateWeekday(const wchar_t * date_str, const wchar_t ** after_date_str)
|
||||
{
|
||||
date_str = SkipDstDateSeparator(date_str);
|
||||
int weekday_int = -1;
|
||||
|
||||
struct WeekDay {
|
||||
const wchar_t * name;
|
||||
int weekday;
|
||||
};
|
||||
|
||||
static const WeekDay week_days[] = {
|
||||
{L"sunday", 0},
|
||||
{L"monday", 1},
|
||||
{L"tuesday", 2},
|
||||
{L"wednesday", 3},
|
||||
{L"thursday", 4},
|
||||
{L"friday", 5},
|
||||
{L"saturday", 6},
|
||||
};
|
||||
|
||||
for(const WeekDay & week_day : week_days)
|
||||
{
|
||||
if( pt::is_substr(week_day.name, date_str) )
|
||||
{
|
||||
date_str += wcslen(week_day.name);
|
||||
weekday_int = week_day.weekday;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*after_date_str = date_str;
|
||||
return weekday_int;
|
||||
}
|
||||
|
||||
|
||||
void TimeZone::ParseDstDateDay(bool is_first, int weekday, pt::Date & 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 is_ok = false;
|
||||
const wchar_t * after_date_str;
|
||||
|
||||
date.Clear();
|
||||
date.year = year;
|
||||
|
||||
date_str = pt::skip_white(date_str, false, false);
|
||||
date.month = pt::to_i(date_str, 10, &after_date_str, &was_overflow, true);
|
||||
|
||||
if( !was_overflow && after_date_str != date_str )
|
||||
{
|
||||
date_str = SkipDstDateSeparator(after_date_str);
|
||||
|
||||
if( pt::is_substr(L"first", date_str) )
|
||||
{
|
||||
date_str += 5; // length of "first"
|
||||
int weekday = ParseDstDateWeekday(date_str, &date_str);
|
||||
|
||||
if( weekday >= 0 )
|
||||
{
|
||||
ParseDstDateDay(true, weekday, date);
|
||||
is_ok = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
if( pt::is_substr(L"last", date_str) )
|
||||
{
|
||||
date_str += 4; // length of "last"
|
||||
int weekday = ParseDstDateWeekday(date_str, &date_str);
|
||||
|
||||
if( weekday >= 0 )
|
||||
{
|
||||
ParseDstDateDay(false, weekday, date);
|
||||
is_ok = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
date.day = pt::to_i(date_str, 10, &after_date_str, &was_overflow, true);
|
||||
is_ok = !was_overflow && after_date_str != date_str;
|
||||
date_str = after_date_str;
|
||||
}
|
||||
}
|
||||
|
||||
return is_ok && date.ParseTime(date_str) && date.IsCorrectDate();
|
||||
}
|
||||
|
||||
|
||||
bool TimeZone::SetTzDst(pt::Space & year)
|
||||
{
|
||||
time_t h24 = 60 * 60 * 24; // 24 hours
|
||||
time_t h24 = pt::Date::ONE_DAY;
|
||||
bool result = true;
|
||||
Dst dst;
|
||||
|
||||
@@ -305,19 +400,15 @@ bool TimeZone::SetTzDst(pt::Space & year)
|
||||
if( year_int < 1970 || year_int > 10000 )
|
||||
return false;
|
||||
|
||||
dst.has_dst = year.to_bool(L"has_dst", false);
|
||||
bool has_dst = year.to_bool(L"has_dst", false);
|
||||
std::wstring * dst_start = year.get_wstr(L"start");
|
||||
std::wstring * dst_end = year.get_wstr(L"end");
|
||||
|
||||
if( dst.has_dst )
|
||||
if( has_dst && dst_start && dst_end )
|
||||
{
|
||||
dst.start.year = year_int;
|
||||
dst.end.year = year_int;
|
||||
|
||||
if( !dst.start.ParseMonthDayTime(year.to_wstr(L"start")) )
|
||||
result = false;
|
||||
|
||||
if( !dst.end.ParseMonthDayTime(year.to_wstr(L"end")) )
|
||||
result = false;
|
||||
|
||||
dst.has_dst = true;
|
||||
result = result && ParseDstDate(year_int, dst_start->c_str(), dst.start);
|
||||
result = result && ParseDstDate(year_int, dst_end->c_str(), dst.end);
|
||||
dst.offset = GetOffset(year);
|
||||
|
||||
if( dst.offset < -h24 || dst.offset > h24 )
|
||||
@@ -327,7 +418,7 @@ bool TimeZone::SetTzDst(pt::Space & year)
|
||||
if( result )
|
||||
dst_map[year_int] = dst;
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -343,7 +434,7 @@ bool TimeZone::SetTz(pt::Space & space)
|
||||
|
||||
id = space.to_int(L"id", -1);
|
||||
offset = GetOffset(space);
|
||||
time_t h24 = 60 * 60 * 24; // 24 hours
|
||||
time_t h24 = pt::Date::ONE_DAY;
|
||||
|
||||
if( offset < -h24 || offset > h24 )
|
||||
result = false;
|
||||
|
||||
@@ -74,16 +74,7 @@ public:
|
||||
// checking whether specified 'date' is in the range of <start, end>
|
||||
// the year field in date, start and end is ignored
|
||||
// has_dst must be true
|
||||
bool IsDstUsed(const pt::Date & date) const;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Compare returns zero if date1 and date2 are equal
|
||||
// return value less than zero if date1 is lower than date2
|
||||
// and a value greater than zero if date1 is greater than date2
|
||||
// the year field is ignored
|
||||
int Compare(const pt::Date & date1, const pt::Date & date2) const;
|
||||
bool IsDstUsed(const pt::Date & utc_date) const;
|
||||
|
||||
};
|
||||
|
||||
@@ -156,7 +147,11 @@ private:
|
||||
|
||||
time_t ParseStrOffset(const wchar_t * str);
|
||||
time_t GetOffset(pt::Space & space);
|
||||
bool SetTzDst(pt::Space & year);
|
||||
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);
|
||||
bool ParseDstDate(int year, const wchar_t * date_str, pt::Date & date);
|
||||
bool SetTzDst(pt::Space & year);
|
||||
static void PrintOffsetPart(long val, pt::Stream & str);
|
||||
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user