Chris@16: #ifndef DATE_TIME_LOCAL_TIME_ADJUSTOR_HPP__ Chris@16: #define DATE_TIME_LOCAL_TIME_ADJUSTOR_HPP__ Chris@16: Chris@16: /* Copyright (c) 2002,2003 CrystalClear Software, Inc. Chris@16: * Use, modification and distribution is subject to the Chris@16: * Boost Software License, Version 1.0. (See accompanying Chris@16: * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) Chris@16: * Author: Jeff Garland Chris@101: * $Date$ Chris@16: */ Chris@16: Chris@16: /*! @file local_time_adjustor.hpp Chris@16: Time adjustment calculations for local times Chris@16: */ Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include // boost::date_time::dst_flags Chris@16: #include // not_a_date_time Chris@16: Chris@16: namespace boost { Chris@16: namespace date_time { Chris@16: Chris@16: Chris@16: //! Provides a base offset adjustment from utc Chris@16: template Chris@16: class utc_adjustment Chris@16: { Chris@16: public: Chris@16: static time_duration_type local_to_utc_base_offset() Chris@16: { Chris@16: time_duration_type td(hours,minutes,0); Chris@16: return td.invert_sign(); Chris@16: } Chris@16: static time_duration_type utc_to_local_base_offset() Chris@16: { Chris@16: return time_duration_type(hours,minutes,0); Chris@16: } Chris@16: }; Chris@16: Chris@16: Chris@16: Chris@16: //! Allow sliding utc adjustment with fixed dst rules Chris@16: template Chris@16: class dynamic_local_time_adjustor : public dst_rules Chris@16: { Chris@16: public: Chris@16: typedef typename time_type::time_duration_type time_duration_type; Chris@16: typedef typename time_type::date_type date_type; Chris@16: Chris@16: dynamic_local_time_adjustor(time_duration_type utc_offset) : Chris@16: utc_offset_(utc_offset) Chris@16: {} Chris@16: Chris@16: //! Presumes local time Chris@16: time_duration_type utc_offset(bool is_dst) Chris@16: { Chris@16: if (is_dst) { Chris@16: return utc_offset_ + this->dst_offset(); Chris@16: } Chris@16: else { Chris@16: return utc_offset_; Chris@16: } Chris@16: Chris@16: } Chris@16: private: Chris@16: time_duration_type utc_offset_; Chris@16: Chris@16: }; Chris@16: Chris@16: Chris@16: Chris@16: //! Embed the rules for local time adjustments at compile time Chris@16: template Chris@16: class static_local_time_adjustor: public dst_rules, public utc_offset_rules Chris@16: { Chris@16: public: Chris@16: typedef typename time_type::time_duration_type time_duration_type; Chris@16: typedef typename time_type::date_type date_type; Chris@16: Chris@16: //! Calculates the offset from a utc time to local based on dst and utc offset Chris@16: /*! @param t UTC time to calculate offset to local time Chris@16: * This adjustment depends on the following observations about the Chris@16: * workings of the DST boundary offset. Since UTC time labels are Chris@16: * monotonically increasing we can determine if a given local time Chris@16: * is in DST or not and therefore adjust the offset appropriately. Chris@16: * Chris@16: * The logic is as follows. Starting with UTC time use the offset to Chris@16: * create a label for an non-dst adjusted local time. Then call Chris@16: * dst_rules::local_is_dst with the non adjust local time. The Chris@16: * results of this function will either unabiguously decide that Chris@16: * the initial local time is in dst or return an illegal or Chris@16: * ambiguous result. An illegal result only occurs at the end Chris@16: * of dst (where labels are skipped) and indicates that dst has Chris@16: * ended. An ambiguous result means that we need to recheck by Chris@16: * making a dst adjustment and then rechecking. If the dst offset Chris@16: * is added to the utc time and the recheck proves non-ambiguous Chris@16: * then we are past the boundary. If it is still ambiguous then Chris@16: * we are ahead of the boundary and dst is still in effect. Chris@16: * Chris@16: * TODO -- check if all dst offsets are positive. If not then Chris@16: * the algorithm needs to check for this and reverse the Chris@16: * illegal/ambiguous logic. Chris@16: */ Chris@16: static time_duration_type utc_to_local_offset(const time_type& t) Chris@16: { Chris@16: //get initial local time guess by applying utc offset Chris@16: time_type initial = t + utc_offset_rules::utc_to_local_base_offset(); Chris@16: time_is_dst_result dst_flag = Chris@16: dst_rules::local_is_dst(initial.date(), initial.time_of_day()); Chris@16: switch(dst_flag) { Chris@16: case is_in_dst: return utc_offset_rules::utc_to_local_base_offset() + dst_rules::dst_offset(); Chris@16: case is_not_in_dst: return utc_offset_rules::utc_to_local_base_offset(); Chris@16: case invalid_time_label:return utc_offset_rules::utc_to_local_base_offset() + dst_rules::dst_offset(); Chris@16: case ambiguous: { Chris@16: time_type retry = initial + dst_rules::dst_offset(); Chris@16: dst_flag = dst_rules::local_is_dst(retry.date(), retry.time_of_day()); Chris@16: //if still ambibuous then the utc time still translates to a dst time Chris@16: if (dst_flag == ambiguous) { Chris@16: return utc_offset_rules::utc_to_local_base_offset() + dst_rules::dst_offset(); Chris@16: } Chris@16: // we are past the dst boundary Chris@16: else { Chris@16: return utc_offset_rules::utc_to_local_base_offset(); Chris@16: } Chris@16: } Chris@16: }//case Chris@16: //TODO better exception type Chris@16: boost::throw_exception(std::out_of_range("Unreachable case")); Chris@16: BOOST_DATE_TIME_UNREACHABLE_EXPRESSION(return time_duration_type(not_a_date_time)); // should never reach Chris@16: } Chris@16: Chris@16: //! Get the offset to UTC given a local time Chris@16: static time_duration_type local_to_utc_offset(const time_type& t, Chris@16: date_time::dst_flags dst=date_time::calculate) Chris@16: { Chris@16: switch (dst) { Chris@16: case is_dst: Chris@16: return utc_offset_rules::local_to_utc_base_offset() - dst_rules::dst_offset(); Chris@16: case not_dst: Chris@16: return utc_offset_rules::local_to_utc_base_offset(); Chris@16: case calculate: Chris@16: time_is_dst_result res = Chris@16: dst_rules::local_is_dst(t.date(), t.time_of_day()); Chris@16: switch(res) { Chris@16: case is_in_dst: return utc_offset_rules::local_to_utc_base_offset() - dst_rules::dst_offset(); Chris@16: case is_not_in_dst: return utc_offset_rules::local_to_utc_base_offset(); Chris@16: case ambiguous: return utc_offset_rules::local_to_utc_base_offset(); Chris@16: case invalid_time_label: break; Chris@16: } Chris@16: } Chris@16: boost::throw_exception(std::out_of_range("Time label invalid")); Chris@16: BOOST_DATE_TIME_UNREACHABLE_EXPRESSION(return time_duration_type(not_a_date_time)); // should never reach Chris@16: } Chris@16: Chris@16: Chris@16: private: Chris@16: Chris@16: }; Chris@16: Chris@16: void dummy_to_prevent_msvc6_ice(); //why ask why? Chris@16: Chris@16: //! Template that simplifies the creation of local time calculator Chris@16: /*! Use this template to create the timezone to utc convertors as required. Chris@16: * Chris@16: * This class will also work for other regions that don't use dst and Chris@16: * have a utc offset which is an integral number of hours. Chris@16: * Chris@16: * Template Parameters Chris@16: * -time_type -- Time class to use Chris@16: * -utc_offset -- Number hours local time is adjust from utc Chris@16: * -use_dst -- true (default) if region uses dst, false otherwise Chris@16: * For example: Chris@16: * @code Chris@16: * //eastern timezone is utc-5 Chris@16: typedef date_time::local_adjustor us_eastern; Chris@16: typedef date_time::local_adjustor us_central; Chris@16: typedef date_time::local_adjustor us_mountain; Chris@16: typedef date_time::local_adjustor us_pacific; Chris@16: typedef date_time::local_adjustor us_arizona; Chris@16: @endcode Chris@16: Chris@16: */ Chris@16: template Chris@16: class local_adjustor Chris@16: { Chris@16: public: Chris@16: typedef typename time_type::time_duration_type time_duration_type; Chris@16: typedef typename time_type::date_type date_type; Chris@16: typedef static_local_time_adjustor > dst_adjustor; Chris@16: //! Convert a utc time to local time Chris@16: static time_type utc_to_local(const time_type& t) Chris@16: { Chris@16: time_duration_type td = dst_adjustor::utc_to_local_offset(t); Chris@16: return t + td; Chris@16: } Chris@16: //! Convert a local time to utc Chris@16: static time_type local_to_utc(const time_type& t, Chris@16: date_time::dst_flags dst=date_time::calculate) Chris@16: { Chris@16: time_duration_type td = dst_adjustor::local_to_utc_offset(t, dst); Chris@16: return t + td; Chris@16: } Chris@16: }; Chris@16: Chris@16: Chris@16: } } //namespace date_time Chris@16: Chris@16: Chris@16: Chris@16: #endif