/Src/Dependencies/Boost/boost/date_time/local_time/local_date_time.hpp

http://hadesmem.googlecode.com/ · C++ Header · 528 lines · 385 code · 29 blank · 114 comment · 75 complexity · bd0d50b2058588d7975050debc842392 MD5 · raw file

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