/* * This file is a part of Winix * and is distributed under the 2-Clause BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2012-2021, Tomasz Sowa * All rights reserved. * * 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. * */ #include "timezone.h" #include "misc.h" namespace Winix { TimeZone::Dst::Dst() { Clear(); } void TimeZone::Dst::Clear() { has_dst = false; start.Clear(); end.Clear(); offset = 0; } bool TimeZone::Dst::IsDstUsed(const PT::Date & 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; } TimeZone::TimeZone() { Clear(); } void TimeZone::Clear() { name.clear(); id = 0; offset = 0; dst_map.clear(); } TimeZone::Dst * TimeZone::FindDst(int year) { if( dst_map.empty() ) return 0; DstMap::iterator i = dst_map.lower_bound(year); if( i == dst_map.begin() && i->first > year ) return 0; if( i == dst_map.end() ) return &(--i)->second; if( i != dst_map.begin() && i->first > year ) return &(--i)->second; return &i->second; } time_t TimeZone::CalcLocalOffset(const PT::Date & utc_date) { 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; } time_t TimeZone::ToLocal(time_t utc_time) { time_t offset = CalcLocalOffset(PT::Date(utc_time)); return utc_time + offset; } PT::Date TimeZone::ToLocal(const PT::Date & utc_date) { PT::Date local(utc_date); local += CalcLocalOffset(utc_date); return local; } time_t TimeZone::CalcUTCOffset(const PT::Date & local_date) { time_t dst_offset = 0; Dst * dst = FindDst(local_date.year); if( dst && dst->has_dst ) { // dst date ranges we have in UTC PT::Date utc(local_date); utc -= (offset + dst->offset); if( dst->IsDstUsed(utc) ) dst_offset = dst->offset; } return offset + dst_offset; } time_t TimeZone::ToUTC(time_t local_time) { time_t offset = CalcUTCOffset(PT::Date(local_time)); return local_time - offset; } PT::Date TimeZone::ToUTC(const PT::Date & local_date) { time_t offset; PT::Date utc(local_date); offset = CalcUTCOffset(local_date); utc -= offset; return utc; } time_t TimeZone::ParseStrOffset(const wchar_t * str) { PT::Date date; 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; } time_t TimeZone::GetOffset(PT::Space & space) { std::wstring * offset_str = space.get_wstr(L"offset_str"); if( offset_str ) return ParseStrOffset(offset_str->c_str()); return space.to_long(L"offset"); } bool TimeZone::SetTzDst(PT::Space & year) { time_t h24 = 60 * 60 * 24; // 24 hours bool result = true; Dst dst; int year_int = 0; if( year.name ) year_int = Toi(*year.name); if( year_int < 1970 || year_int > 10000 ) return false; dst.has_dst = year.to_bool(L"has_dst", false); if( dst.has_dst ) { 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.offset = GetOffset(year); if( dst.offset < -h24 || dst.offset > h24 ) result = false; } if( result ) dst_map[year_int] = dst; return result; } bool TimeZone::SetTz(PT::Space & space) { bool result = true; name.clear(); if( space.name ) name = *space.name; id = space.to_int(L"id", -1); offset = GetOffset(space); time_t h24 = 60 * 60 * 24; // 24 hours if( offset < -h24 || offset > h24 ) result = false; PT::Space * dst = space.find_child_space(L"dst"); if( dst && dst->child_spaces ) { for(size_t i=0 ; ichild_spaces->size() ; ++i) { PT::Space & year = *(*dst->child_spaces)[i]; if( !SetTzDst(year) ) { result = false; break; } } } return result; } } // namespace Winix