2012-05-30 21:04:18 +02:00
|
|
|
/*
|
|
|
|
* This file is a part of Winix
|
2014-10-04 20:04:03 +02:00
|
|
|
* and is distributed under the 2-Clause BSD licence.
|
|
|
|
* Author: Tomasz Sowa <t.sowa@ttmath.org>
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2021-04-09 17:50:58 +02:00
|
|
|
* Copyright (c) 2012-2021, Tomasz Sowa
|
2012-05-30 21:04:18 +02:00
|
|
|
* All rights reserved.
|
2014-10-04 20:04:03 +02:00
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
*
|
|
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer.
|
|
|
|
*
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
2012-05-30 21:04:18 +02:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "timezone.h"
|
2012-06-27 01:19:19 +02:00
|
|
|
#include "misc.h"
|
|
|
|
|
|
|
|
|
2014-02-12 17:30:49 +01:00
|
|
|
namespace Winix
|
|
|
|
{
|
|
|
|
|
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
|
|
|
|
|
|
|
|
TimeZone::Dst::Dst()
|
|
|
|
{
|
|
|
|
Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TimeZone::Dst::Clear()
|
|
|
|
{
|
|
|
|
has_dst = false;
|
|
|
|
start.Clear();
|
|
|
|
end.Clear();
|
|
|
|
offset = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-20 20:59:12 +02:00
|
|
|
bool TimeZone::Dst::IsDstUsed(const pt::Date & date) const
|
2012-06-27 01:19:19 +02:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-20 20:59:12 +02:00
|
|
|
int TimeZone::Dst::Compare(const pt::Date & date1, const pt::Date & date2) const
|
2012-06-27 01:19:19 +02:00
|
|
|
{
|
|
|
|
// 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;
|
|
|
|
}
|
2012-05-30 21:04:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TimeZone::TimeZone()
|
|
|
|
{
|
|
|
|
Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void TimeZone::Clear()
|
|
|
|
{
|
2012-06-27 01:19:19 +02:00
|
|
|
name.clear();
|
|
|
|
id = 0;
|
|
|
|
offset = 0;
|
|
|
|
dst_map.clear();
|
2012-05-30 21:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
TimeZone::Dst * TimeZone::FindDst(int year)
|
2012-05-30 21:04:18 +02:00
|
|
|
{
|
2012-06-27 01:19:19 +02:00
|
|
|
if( dst_map.empty() )
|
|
|
|
return 0;
|
2012-05-30 21:04:18 +02:00
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
DstMap::iterator i = dst_map.lower_bound(year);
|
2012-05-30 21:04:18 +02:00
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
if( i == dst_map.begin() && i->first > year )
|
|
|
|
return 0;
|
2012-05-30 21:04:18 +02:00
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
if( i == dst_map.end() )
|
|
|
|
return &(--i)->second;
|
2012-05-30 21:04:18 +02:00
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
if( i != dst_map.begin() && i->first > year )
|
|
|
|
return &(--i)->second;
|
2012-05-30 21:04:18 +02:00
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
return &i->second;
|
|
|
|
}
|
2012-05-30 21:04:18 +02:00
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
|
|
|
|
|
2021-05-20 20:59:12 +02:00
|
|
|
time_t TimeZone::CalcLocalOffset(const pt::Date & utc_date)
|
2012-06-27 01:19:19 +02:00
|
|
|
{
|
|
|
|
time_t dst_offset = 0;
|
|
|
|
|
|
|
|
Dst * dst = FindDst(utc_date.year);
|
|
|
|
|
|
|
|
if( dst && dst->IsDstUsed(utc_date) )
|
|
|
|
dst_offset = dst->offset;
|
|
|
|
|
|
|
|
return offset + dst_offset;
|
2012-05-30 21:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
time_t TimeZone::ToLocal(time_t utc_time)
|
|
|
|
{
|
2021-05-20 20:59:12 +02:00
|
|
|
time_t offset = CalcLocalOffset(pt::Date(utc_time));
|
2012-05-30 21:04:18 +02:00
|
|
|
|
|
|
|
return utc_time + offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-20 20:59:12 +02:00
|
|
|
pt::Date TimeZone::ToLocal(const pt::Date & utc_date)
|
2012-05-30 21:04:18 +02:00
|
|
|
{
|
2021-05-20 20:59:12 +02:00
|
|
|
pt::Date local(utc_date);
|
2012-05-30 21:04:18 +02:00
|
|
|
local += CalcLocalOffset(utc_date);
|
|
|
|
|
|
|
|
return local;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-20 20:59:12 +02:00
|
|
|
time_t TimeZone::CalcUTCOffset(const pt::Date & local_date)
|
2012-05-30 21:04:18 +02:00
|
|
|
{
|
2012-06-27 01:19:19 +02:00
|
|
|
time_t dst_offset = 0;
|
2012-05-30 21:04:18 +02:00
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
Dst * dst = FindDst(local_date.year);
|
2012-05-30 21:04:18 +02:00
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
if( dst && dst->has_dst )
|
2012-05-30 21:04:18 +02:00
|
|
|
{
|
2012-06-27 01:19:19 +02:00
|
|
|
// dst date ranges we have in UTC
|
2021-05-20 20:59:12 +02:00
|
|
|
pt::Date utc(local_date);
|
2012-06-27 01:19:19 +02:00
|
|
|
utc -= (offset + dst->offset);
|
2012-05-30 21:04:18 +02:00
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
if( dst->IsDstUsed(utc) )
|
|
|
|
dst_offset = dst->offset;
|
2012-05-30 21:04:18 +02:00
|
|
|
}
|
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
return offset + dst_offset;
|
2012-05-30 21:04:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
|
2012-05-30 21:04:18 +02:00
|
|
|
time_t TimeZone::ToUTC(time_t local_time)
|
|
|
|
{
|
2021-05-20 20:59:12 +02:00
|
|
|
time_t offset = CalcUTCOffset(pt::Date(local_time));
|
2012-05-30 21:04:18 +02:00
|
|
|
|
|
|
|
return local_time - offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-05-20 20:59:12 +02:00
|
|
|
pt::Date TimeZone::ToUTC(const pt::Date & local_date)
|
2012-05-30 21:04:18 +02:00
|
|
|
{
|
|
|
|
time_t offset;
|
2021-05-20 20:59:12 +02:00
|
|
|
pt::Date utc(local_date);
|
2012-05-30 21:04:18 +02:00
|
|
|
|
|
|
|
offset = CalcUTCOffset(local_date);
|
|
|
|
utc -= offset;
|
|
|
|
|
|
|
|
return utc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
time_t TimeZone::ParseStrOffset(const wchar_t * str)
|
|
|
|
{
|
2021-05-20 20:59:12 +02:00
|
|
|
pt::Date date;
|
2012-06-27 01:19:19 +02:00
|
|
|
bool is_sign = false;
|
|
|
|
time_t offset = 0;
|
|
|
|
|
|
|
|
str = SkipWhite(str);
|
|
|
|
|
|
|
|
if( *str == '-' )
|
|
|
|
{
|
|
|
|
is_sign = true;
|
|
|
|
str += 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if( *str == '+' )
|
|
|
|
{
|
|
|
|
str += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( date.ParseTime(str) )
|
|
|
|
{
|
|
|
|
offset = date.hour * 60 * 60 + date.min * 60 + date.sec;
|
|
|
|
|
|
|
|
if( is_sign )
|
|
|
|
offset = -offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-20 20:59:12 +02:00
|
|
|
time_t TimeZone::GetOffset(pt::Space & space)
|
2012-05-30 21:04:18 +02:00
|
|
|
{
|
2021-04-09 17:50:58 +02:00
|
|
|
std::wstring * offset_str = space.get_wstr(L"offset_str");
|
2012-06-27 01:19:19 +02:00
|
|
|
|
|
|
|
if( offset_str )
|
|
|
|
return ParseStrOffset(offset_str->c_str());
|
2012-05-30 21:04:18 +02:00
|
|
|
|
2021-04-09 17:50:58 +02:00
|
|
|
return space.to_long(L"offset");
|
2012-06-27 01:19:19 +02:00
|
|
|
}
|
2012-05-30 21:04:18 +02:00
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
|
2021-05-20 20:59:12 +02:00
|
|
|
bool TimeZone::SetTzDst(pt::Space & year)
|
2012-06-27 01:19:19 +02:00
|
|
|
{
|
2012-05-30 21:04:18 +02:00
|
|
|
time_t h24 = 60 * 60 * 24; // 24 hours
|
2012-06-27 01:19:19 +02:00
|
|
|
bool result = true;
|
|
|
|
Dst dst;
|
2012-05-30 21:04:18 +02:00
|
|
|
|
2021-04-09 17:50:58 +02:00
|
|
|
int year_int = 0;
|
2021-05-21 17:17:17 +02:00
|
|
|
std::wstring * year_name = year.find_child_space_name();
|
2021-04-09 17:50:58 +02:00
|
|
|
|
2021-05-21 17:17:17 +02:00
|
|
|
if( year_name )
|
|
|
|
year_int = Toi(*year_name);
|
2012-06-27 01:19:19 +02:00
|
|
|
|
2021-02-17 18:36:50 +01:00
|
|
|
if( year_int < 1970 || year_int > 10000 )
|
2012-06-27 01:19:19 +02:00
|
|
|
return false;
|
|
|
|
|
2021-04-09 17:50:58 +02:00
|
|
|
dst.has_dst = year.to_bool(L"has_dst", false);
|
2012-05-30 21:04:18 +02:00
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
if( dst.has_dst )
|
2012-05-30 21:04:18 +02:00
|
|
|
{
|
2012-06-27 01:19:19 +02:00
|
|
|
dst.start.year = year_int;
|
|
|
|
dst.end.year = year_int;
|
2012-05-30 21:04:18 +02:00
|
|
|
|
2021-04-09 17:50:58 +02:00
|
|
|
if( !dst.start.ParseMonthDayTime(year.to_wstr(L"start")) )
|
2012-05-30 21:04:18 +02:00
|
|
|
result = false;
|
|
|
|
|
2021-04-09 17:50:58 +02:00
|
|
|
if( !dst.end.ParseMonthDayTime(year.to_wstr(L"end")) )
|
2012-05-30 21:04:18 +02:00
|
|
|
result = false;
|
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
dst.offset = GetOffset(year);
|
2012-05-30 21:04:18 +02:00
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
if( dst.offset < -h24 || dst.offset > h24 )
|
2012-05-30 21:04:18 +02:00
|
|
|
result = false;
|
|
|
|
}
|
2012-06-27 01:19:19 +02:00
|
|
|
|
|
|
|
if( result )
|
|
|
|
dst_map[year_int] = dst;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-20 20:59:12 +02:00
|
|
|
bool TimeZone::SetTz(pt::Space & space)
|
2012-06-27 01:19:19 +02:00
|
|
|
{
|
|
|
|
bool result = true;
|
2021-04-09 17:50:58 +02:00
|
|
|
name.clear();
|
|
|
|
|
2021-05-21 17:17:17 +02:00
|
|
|
std::wstring * space_name = space.find_child_space_name();
|
|
|
|
|
|
|
|
if( space_name )
|
|
|
|
name = *space_name;
|
2021-04-09 17:50:58 +02:00
|
|
|
|
|
|
|
id = space.to_int(L"id", -1);
|
2012-06-27 01:19:19 +02:00
|
|
|
offset = GetOffset(space);
|
|
|
|
time_t h24 = 60 * 60 * 24; // 24 hours
|
|
|
|
|
|
|
|
if( offset < -h24 || offset > h24 )
|
|
|
|
result = false;
|
|
|
|
|
2021-05-20 20:59:12 +02:00
|
|
|
pt::Space * dst = space.find_child_space(L"dst");
|
2012-06-27 01:19:19 +02:00
|
|
|
|
2021-05-21 17:17:17 +02:00
|
|
|
if( dst )
|
2012-05-30 21:04:18 +02:00
|
|
|
{
|
2021-05-21 17:17:17 +02:00
|
|
|
pt::Space::TableType * child_table = dst->find_child_space_table();
|
2021-04-09 17:50:58 +02:00
|
|
|
|
2021-05-21 17:17:17 +02:00
|
|
|
if( child_table )
|
|
|
|
{
|
|
|
|
for(pt::Space * year : *child_table)
|
2021-04-09 17:50:58 +02:00
|
|
|
{
|
2021-05-21 17:17:17 +02:00
|
|
|
if( !SetTzDst(*year) )
|
|
|
|
{
|
|
|
|
result = false;
|
|
|
|
break;
|
|
|
|
}
|
2021-04-09 17:50:58 +02:00
|
|
|
}
|
2012-06-27 01:19:19 +02:00
|
|
|
}
|
|
|
|
}
|
2012-05-30 21:04:18 +02:00
|
|
|
|
2021-05-21 17:17:17 +02:00
|
|
|
return result;
|
2012-05-30 21:04:18 +02:00
|
|
|
}
|
|
|
|
|
2012-06-27 01:19:19 +02:00
|
|
|
|
2014-02-12 17:30:49 +01:00
|
|
|
|
|
|
|
} // namespace Winix
|
|
|
|
|
|
|
|
|