local_date_time.hpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. #ifndef LOCAL_TIME_LOCAL_DATE_TIME_HPP__
  2. #define LOCAL_TIME_LOCAL_DATE_TIME_HPP__
  3. /* Copyright (c) 2003-2005 CrystalClear Software, Inc.
  4. * Subject to the Boost Software License, Version 1.0.
  5. * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
  6. * Author: Jeff Garland, Bart Garst
  7. * $Date$
  8. */
  9. #include <string>
  10. #include <iomanip>
  11. #include <sstream>
  12. #include <stdexcept>
  13. #include <boost/shared_ptr.hpp>
  14. #include <boost/throw_exception.hpp>
  15. #include <boost/date_time/time.hpp>
  16. #include <boost/date_time/posix_time/posix_time.hpp> //todo remove?
  17. #include <boost/date_time/compiler_config.hpp>
  18. #include <boost/date_time/dst_rules.hpp>
  19. #include <boost/date_time/time_zone_base.hpp>
  20. #include <boost/date_time/special_defs.hpp>
  21. #include <boost/date_time/time_resolution_traits.hpp> // absolute_value
  22. namespace boost {
  23. namespace local_time {
  24. //! simple exception for reporting when STD or DST cannot be determined
  25. struct BOOST_SYMBOL_VISIBLE ambiguous_result : public std::logic_error
  26. {
  27. ambiguous_result (std::string const& msg = std::string()) :
  28. std::logic_error(std::string("Daylight Savings Results are ambiguous: " + msg)) {}
  29. };
  30. //! simple exception for when time label given cannot exist
  31. struct BOOST_SYMBOL_VISIBLE time_label_invalid : public std::logic_error
  32. {
  33. time_label_invalid (std::string const& msg = std::string()) :
  34. std::logic_error(std::string("Time label given is invalid: " + msg)) {}
  35. };
  36. struct BOOST_SYMBOL_VISIBLE dst_not_valid: public std::logic_error
  37. {
  38. dst_not_valid(std::string const& msg = std::string()) :
  39. std::logic_error(std::string("is_dst flag does not match resulting dst for time label given: " + msg)) {}
  40. };
  41. //TODO: I think these should be in local_date_time_base and not
  42. // necessarily brought into the namespace
  43. using date_time::time_is_dst_result;
  44. using date_time::is_in_dst;
  45. using date_time::is_not_in_dst;
  46. using date_time::ambiguous;
  47. using date_time::invalid_time_label;
  48. //! Representation of "wall-clock" time in a particular time zone
  49. /*! Representation of "wall-clock" time in a particular time zone
  50. * Local_date_time_base holds a time value (date and time offset from 00:00)
  51. * along with a time zone. The time value is stored as UTC and conversions
  52. * to wall clock time are made as needed. This approach allows for
  53. * operations between wall-clock times in different time zones, and
  54. * daylight savings time considerations, to be made. Time zones are
  55. * required to be in the form of a boost::shared_ptr<time_zone_base>.
  56. */
  57. template<class utc_time_=posix_time::ptime,
  58. class tz_type=date_time::time_zone_base<utc_time_,char> >
  59. class BOOST_SYMBOL_VISIBLE local_date_time_base : public date_time::base_time<utc_time_,
  60. boost::posix_time::posix_time_system> {
  61. public:
  62. typedef utc_time_ utc_time_type;
  63. typedef typename utc_time_type::time_duration_type time_duration_type;
  64. typedef typename utc_time_type::date_type date_type;
  65. typedef typename date_type::duration_type date_duration_type;
  66. typedef typename utc_time_type::time_system_type time_system_type;
  67. /*! This constructor interprets the passed time as a UTC time.
  68. * So, for example, if the passed timezone is UTC-5 then the
  69. * time will be adjusted back 5 hours. The time zone allows for
  70. * automatic calculation of whether the particular time is adjusted for
  71. * daylight savings, etc.
  72. * If the time zone shared pointer is null then time stays unadjusted.
  73. *@param t A UTC time
  74. *@param tz Timezone for to adjust the UTC time to.
  75. */
  76. local_date_time_base(utc_time_type t,
  77. boost::shared_ptr<tz_type> tz) :
  78. date_time::base_time<utc_time_type, time_system_type>(t),
  79. zone_(tz)
  80. {
  81. // param was already utc so nothing more to do
  82. }
  83. /*! This constructs a local time -- the passed time information
  84. * understood to be in the passed tz. The DST flag must be passed
  85. * to indicate whether the time is in daylight savings or not.
  86. * @throws -- time_label_invalid if the time passed does not exist in
  87. * the given locale. The non-existent case occurs typically
  88. * during the shift-back from daylight savings time. When
  89. * the clock is shifted forward a range of times
  90. * (2 am to 3 am in the US) is skipped and hence is invalid.
  91. * @throws -- dst_not_valid if the DST flag is passed for a period
  92. * where DST is not active.
  93. */
  94. local_date_time_base(date_type d,
  95. time_duration_type td,
  96. boost::shared_ptr<tz_type> tz,
  97. bool dst_flag) : //necessary for constr_adj()
  98. date_time::base_time<utc_time_type,time_system_type>(construction_adjustment(utc_time_type(d, td), tz, dst_flag)),
  99. zone_(tz)
  100. {
  101. if(tz != boost::shared_ptr<tz_type>() && tz->has_dst()){
  102. // d & td are already local so we use them
  103. time_is_dst_result result = check_dst(d, td, tz);
  104. bool in_dst = (result == is_in_dst); // less processing than is_dst()
  105. // ambig occurs at end, invalid at start
  106. if(result == invalid_time_label){
  107. // Ex: 2:15am local on trans-in day in nyc, dst_flag irrelevant
  108. std::ostringstream ss;
  109. ss << "time given: " << d << ' ' << td;
  110. boost::throw_exception(time_label_invalid(ss.str()));
  111. }
  112. else if(result != ambiguous && in_dst != dst_flag){
  113. // is dst_flag accurate?
  114. // Ex: false flag in NYC in June
  115. std::ostringstream ss;
  116. ss.setf(std::ios_base::boolalpha);
  117. ss << "flag given: dst=" << dst_flag << ", dst calculated: dst=" << in_dst;
  118. boost::throw_exception(dst_not_valid(ss.str()));
  119. }
  120. // everything checks out and conversion to utc already done
  121. }
  122. }
  123. //TODO maybe not the right set...Ignore the last 2 for now...
  124. enum DST_CALC_OPTIONS { EXCEPTION_ON_ERROR, NOT_DATE_TIME_ON_ERROR };
  125. //ASSUME_DST_ON_ERROR, ASSUME_NOT_DST_ON_ERROR };
  126. /*! This constructs a local time -- the passed time information
  127. * understood to be in the passed tz. The DST flag is calculated
  128. * according to the specified rule.
  129. */
  130. local_date_time_base(date_type d,
  131. time_duration_type td,
  132. boost::shared_ptr<tz_type> tz,
  133. DST_CALC_OPTIONS calc_option) :
  134. // dummy value - time_ is set in constructor code
  135. date_time::base_time<utc_time_type,time_system_type>(utc_time_type(d,td)),
  136. zone_(tz)
  137. {
  138. time_is_dst_result result = check_dst(d, td, tz);
  139. if(result == ambiguous) {
  140. if(calc_option == EXCEPTION_ON_ERROR){
  141. std::ostringstream ss;
  142. ss << "time given: " << d << ' ' << td;
  143. boost::throw_exception(ambiguous_result(ss.str()));
  144. }
  145. else{ // NADT on error
  146. this->time_ = posix_time::posix_time_system::get_time_rep(date_type(date_time::not_a_date_time), time_duration_type(date_time::not_a_date_time));
  147. }
  148. }
  149. else if(result == invalid_time_label){
  150. if(calc_option == EXCEPTION_ON_ERROR){
  151. std::ostringstream ss;
  152. ss << "time given: " << d << ' ' << td;
  153. boost::throw_exception(time_label_invalid(ss.str()));
  154. }
  155. else{ // NADT on error
  156. this->time_ = posix_time::posix_time_system::get_time_rep(date_type(date_time::not_a_date_time), time_duration_type(date_time::not_a_date_time));
  157. }
  158. }
  159. else if(result == is_in_dst){
  160. utc_time_type t =
  161. construction_adjustment(utc_time_type(d, td), tz, true);
  162. this->time_ = posix_time::posix_time_system::get_time_rep(t.date(),
  163. t.time_of_day());
  164. }
  165. else{
  166. utc_time_type t =
  167. construction_adjustment(utc_time_type(d, td), tz, false);
  168. this->time_ = posix_time::posix_time_system::get_time_rep(t.date(),
  169. t.time_of_day());
  170. }
  171. }
  172. //! Determines if given time label is in daylight savings for given zone
  173. /*! Determines if given time label is in daylight savings for given zone.
  174. * Takes a date and time_duration representing a local time, along
  175. * with time zone, and returns a time_is_dst_result object as result.
  176. */
  177. static time_is_dst_result check_dst(date_type d,
  178. time_duration_type td,
  179. boost::shared_ptr<tz_type> tz)
  180. {
  181. if(tz != boost::shared_ptr<tz_type>() && tz->has_dst()) {
  182. typedef typename date_time::dst_calculator<date_type, time_duration_type> dst_calculator;
  183. return dst_calculator::local_is_dst(
  184. d, td,
  185. tz->dst_local_start_time(d.year()).date(),
  186. tz->dst_local_start_time(d.year()).time_of_day(),
  187. tz->dst_local_end_time(d.year()).date(),
  188. tz->dst_local_end_time(d.year()).time_of_day(),
  189. tz->dst_offset()
  190. );
  191. }
  192. else{
  193. return is_not_in_dst;
  194. }
  195. }
  196. //! Simple destructor, releases time zone if last referrer
  197. ~local_date_time_base() {}
  198. //! Copy constructor
  199. local_date_time_base(const local_date_time_base& rhs) :
  200. date_time::base_time<utc_time_type, time_system_type>(rhs),
  201. zone_(rhs.zone_)
  202. {}
  203. //! Special values constructor
  204. explicit local_date_time_base(const boost::date_time::special_values sv,
  205. boost::shared_ptr<tz_type> tz = boost::shared_ptr<tz_type>()) :
  206. date_time::base_time<utc_time_type, time_system_type>(utc_time_type(sv)),
  207. zone_(tz)
  208. {}
  209. //! returns time zone associated with calling instance
  210. boost::shared_ptr<tz_type> zone() const
  211. {
  212. return zone_;
  213. }
  214. //! returns false is time_zone is NULL and if time value is a special_value
  215. bool is_dst() const
  216. {
  217. if(zone_ != boost::shared_ptr<tz_type>() && zone_->has_dst() && !this->is_special()) {
  218. // check_dst takes a local time, *this is utc
  219. utc_time_type lt(this->time_);
  220. lt += zone_->base_utc_offset();
  221. // dst_offset only needs to be considered with ambiguous time labels
  222. // make that adjustment there
  223. switch(check_dst(lt.date(), lt.time_of_day(), zone_)){
  224. case is_not_in_dst:
  225. return false;
  226. case is_in_dst:
  227. return true;
  228. case ambiguous:
  229. if(lt + zone_->dst_offset() < zone_->dst_local_end_time(lt.date().year())) {
  230. return true;
  231. }
  232. break;
  233. case invalid_time_label:
  234. if(lt >= zone_->dst_local_start_time(lt.date().year())) {
  235. return true;
  236. }
  237. break;
  238. }
  239. }
  240. return false;
  241. }
  242. //! Returns object's time value as a utc representation
  243. utc_time_type utc_time() const
  244. {
  245. return utc_time_type(this->time_);
  246. }
  247. //! Returns object's time value as a local representation
  248. utc_time_type local_time() const
  249. {
  250. if(zone_ != boost::shared_ptr<tz_type>()){
  251. utc_time_type lt = this->utc_time() + zone_->base_utc_offset();
  252. if (is_dst()) {
  253. lt += zone_->dst_offset();
  254. }
  255. return lt;
  256. }
  257. return utc_time_type(this->time_);
  258. }
  259. //! Returns string in the form "2003-Aug-20 05:00:00 EDT"
  260. /*! Returns string in the form "2003-Aug-20 05:00:00 EDT". If
  261. * time_zone is NULL the time zone abbreviation will be "UTC". The time
  262. * zone abbrev will not be included if calling object is a special_value*/
  263. std::string to_string() const
  264. {
  265. //TODO is this a temporary function ???
  266. std::ostringstream ss;
  267. if(this->is_special()){
  268. ss << utc_time();
  269. return ss.str();
  270. }
  271. if(zone_ == boost::shared_ptr<tz_type>()) {
  272. ss << utc_time() << " UTC";
  273. return ss.str();
  274. }
  275. bool is_dst_ = is_dst();
  276. utc_time_type lt = this->utc_time() + zone_->base_utc_offset();
  277. if (is_dst_) {
  278. lt += zone_->dst_offset();
  279. }
  280. ss << local_time() << " ";
  281. if (is_dst()) {
  282. ss << zone_->dst_zone_abbrev();
  283. }
  284. else {
  285. ss << zone_->std_zone_abbrev();
  286. }
  287. return ss.str();
  288. }
  289. /*! returns a local_date_time_base in the given time zone with the
  290. * optional time_duration added. */
  291. local_date_time_base local_time_in(boost::shared_ptr<tz_type> new_tz,
  292. time_duration_type td=time_duration_type(0,0,0)) const
  293. {
  294. return local_date_time_base(utc_time_type(this->time_) + td, new_tz);
  295. }
  296. //! Returns name of associated time zone or "Coordinated Universal Time".
  297. /*! Optional bool parameter will return time zone as an offset
  298. * (ie "+07:00" extended iso format). Empty string is returned for
  299. * classes that do not use a time_zone */
  300. std::string zone_name(bool as_offset=false) const
  301. {
  302. if(zone_ == boost::shared_ptr<tz_type>()) {
  303. if(as_offset) {
  304. return std::string("Z");
  305. }
  306. else {
  307. return std::string("Coordinated Universal Time");
  308. }
  309. }
  310. if (is_dst()) {
  311. if(as_offset) {
  312. time_duration_type td = zone_->base_utc_offset();
  313. td += zone_->dst_offset();
  314. return zone_as_offset(td, ":");
  315. }
  316. else {
  317. return zone_->dst_zone_name();
  318. }
  319. }
  320. else {
  321. if(as_offset) {
  322. time_duration_type td = zone_->base_utc_offset();
  323. return zone_as_offset(td, ":");
  324. }
  325. else {
  326. return zone_->std_zone_name();
  327. }
  328. }
  329. }
  330. //! Returns abbreviation of associated time zone or "UTC".
  331. /*! Optional bool parameter will return time zone as an offset
  332. * (ie "+0700" iso format). Empty string is returned for classes
  333. * that do not use a time_zone */
  334. std::string zone_abbrev(bool as_offset=false) const
  335. {
  336. if(zone_ == boost::shared_ptr<tz_type>()) {
  337. if(as_offset) {
  338. return std::string("Z");
  339. }
  340. else {
  341. return std::string("UTC");
  342. }
  343. }
  344. if (is_dst()) {
  345. if(as_offset) {
  346. time_duration_type td = zone_->base_utc_offset();
  347. td += zone_->dst_offset();
  348. return zone_as_offset(td, "");
  349. }
  350. else {
  351. return zone_->dst_zone_abbrev();
  352. }
  353. }
  354. else {
  355. if(as_offset) {
  356. time_duration_type td = zone_->base_utc_offset();
  357. return zone_as_offset(td, "");
  358. }
  359. else {
  360. return zone_->std_zone_abbrev();
  361. }
  362. }
  363. }
  364. //! returns a posix_time_zone string for the associated time_zone. If no time_zone, "UTC+00" is returned.
  365. std::string zone_as_posix_string() const
  366. {
  367. if(zone_ == shared_ptr<tz_type>()) {
  368. return std::string("UTC+00");
  369. }
  370. return zone_->to_posix_string();
  371. }
  372. //! Equality comparison operator
  373. /*bool operator==(const date_time::base_time<boost::posix_time::ptime,boost::posix_time::posix_time_system>& rhs) const
  374. { // fails due to rhs.time_ being protected
  375. return date_time::base_time<boost::posix_time::ptime,boost::posix_time::posix_time_system>::operator==(rhs);
  376. //return this->time_ == rhs.time_;
  377. }*/
  378. //! Equality comparison operator
  379. bool operator==(const local_date_time_base& rhs) const
  380. {
  381. return time_system_type::is_equal(this->time_, rhs.time_);
  382. }
  383. //! Non-Equality comparison operator
  384. bool operator!=(const local_date_time_base& rhs) const
  385. {
  386. return !(*this == rhs);
  387. }
  388. //! Less than comparison operator
  389. bool operator<(const local_date_time_base& rhs) const
  390. {
  391. return time_system_type::is_less(this->time_, rhs.time_);
  392. }
  393. //! Less than or equal to comparison operator
  394. bool operator<=(const local_date_time_base& rhs) const
  395. {
  396. return (*this < rhs || *this == rhs);
  397. }
  398. //! Greater than comparison operator
  399. bool operator>(const local_date_time_base& rhs) const
  400. {
  401. return !(*this <= rhs);
  402. }
  403. //! Greater than or equal to comparison operator
  404. bool operator>=(const local_date_time_base& rhs) const
  405. {
  406. return (*this > rhs || *this == rhs);
  407. }
  408. //! Local_date_time + date_duration
  409. local_date_time_base operator+(const date_duration_type& dd) const
  410. {
  411. return local_date_time_base(time_system_type::add_days(this->time_,dd), zone_);
  412. }
  413. //! Local_date_time += date_duration
  414. local_date_time_base operator+=(const date_duration_type& dd)
  415. {
  416. this->time_ = time_system_type::add_days(this->time_,dd);
  417. return *this;
  418. }
  419. //! Local_date_time - date_duration
  420. local_date_time_base operator-(const date_duration_type& dd) const
  421. {
  422. return local_date_time_base(time_system_type::subtract_days(this->time_,dd), zone_);
  423. }
  424. //! Local_date_time -= date_duration
  425. local_date_time_base operator-=(const date_duration_type& dd)
  426. {
  427. this->time_ = time_system_type::subtract_days(this->time_,dd);
  428. return *this;
  429. }
  430. //! Local_date_time + time_duration
  431. local_date_time_base operator+(const time_duration_type& td) const
  432. {
  433. return local_date_time_base(time_system_type::add_time_duration(this->time_,td), zone_);
  434. }
  435. //! Local_date_time += time_duration
  436. local_date_time_base operator+=(const time_duration_type& td)
  437. {
  438. this->time_ = time_system_type::add_time_duration(this->time_,td);
  439. return *this;
  440. }
  441. //! Local_date_time - time_duration
  442. local_date_time_base operator-(const time_duration_type& td) const
  443. {
  444. return local_date_time_base(time_system_type::subtract_time_duration(this->time_,td), zone_);
  445. }
  446. //! Local_date_time -= time_duration
  447. local_date_time_base operator-=(const time_duration_type& td)
  448. {
  449. this->time_ = time_system_type::subtract_time_duration(this->time_,td);
  450. return *this;
  451. }
  452. //! local_date_time -= local_date_time --> time_duration_type
  453. time_duration_type operator-(const local_date_time_base& rhs) const
  454. {
  455. return utc_time_type(this->time_) - utc_time_type(rhs.time_);
  456. }
  457. private:
  458. boost::shared_ptr<tz_type> zone_;
  459. //bool is_dst_;
  460. /*! Adjust the passed in time to UTC?
  461. */
  462. utc_time_type construction_adjustment(utc_time_type t,
  463. boost::shared_ptr<tz_type> z,
  464. bool dst_flag)
  465. {
  466. if(z != boost::shared_ptr<tz_type>()) {
  467. if(dst_flag && z->has_dst()) {
  468. t -= z->dst_offset();
  469. } // else no adjust
  470. t -= z->base_utc_offset();
  471. }
  472. return t;
  473. }
  474. /*! Simple formatting code -- todo remove this?
  475. */
  476. std::string zone_as_offset(const time_duration_type& td,
  477. const std::string& separator) const
  478. {
  479. std::ostringstream ss;
  480. if(td.is_negative()) {
  481. // a negative duration is represented as "-[h]h:mm"
  482. // we require two digits for the hour. A positive duration
  483. // with the %H flag will always give two digits
  484. ss << "-";
  485. }
  486. else {
  487. ss << "+";
  488. }
  489. ss << std::setw(2) << std::setfill('0')
  490. << date_time::absolute_value(td.hours())
  491. << separator
  492. << std::setw(2) << std::setfill('0')
  493. << date_time::absolute_value(td.minutes());
  494. return ss.str();
  495. }
  496. };
  497. //!Use the default parameters to define local_date_time
  498. typedef local_date_time_base<> local_date_time;
  499. } }
  500. #endif