1 // 2 // Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) 3 // 4 // Distributed under the Boost Software License, Version 1.0. (See 5 // accompanying file LICENSE_1_0.txt or copy at 6 // http://www.boost.org/LICENSE_1_0.txt) 7 // 8 #define BOOST_LOCALE_SOURCE 9 #include <boost/locale/date_time_facet.hpp> 10 #include <boost/locale/date_time.hpp> 11 #include <boost/locale/formatting.hpp> 12 #include <boost/locale/hold_ptr.hpp> 13 #include "all_generator.hpp" 14 15 #include <boost/thread.hpp> 16 #include <unicode/calendar.h> 17 #include <unicode/gregocal.h> 18 #include <unicode/utypes.h> 19 20 #include <memory> 21 #include <math.h> 22 23 #include "cdata.hpp" 24 #include "uconv.hpp" 25 #include "time_zone.hpp" 26 27 #include <iostream> 28 29 30 namespace boost { 31 namespace locale { 32 namespace impl_icu { 33 check_and_throw_dt(UErrorCode & e)34 static void check_and_throw_dt(UErrorCode &e) 35 { 36 if(U_FAILURE(e)) { 37 throw date_time_error(u_errorName(e)); 38 } 39 } 40 using period::marks::period_mark; 41 to_icu(period::marks::period_mark f)42 static UCalendarDateFields to_icu(period::marks::period_mark f) 43 { 44 using namespace period::marks; 45 46 switch(f) { 47 case era: return UCAL_ERA; 48 case year: return UCAL_YEAR; 49 case extended_year: return UCAL_EXTENDED_YEAR; 50 case month: return UCAL_MONTH; 51 case day: return UCAL_DATE; 52 case day_of_year: return UCAL_DAY_OF_YEAR; 53 case day_of_week: return UCAL_DAY_OF_WEEK; 54 case day_of_week_in_month: return UCAL_DAY_OF_WEEK_IN_MONTH; 55 case day_of_week_local: return UCAL_DOW_LOCAL; 56 case hour: return UCAL_HOUR_OF_DAY; 57 case hour_12: return UCAL_HOUR; 58 case am_pm: return UCAL_AM_PM; 59 case minute: return UCAL_MINUTE; 60 case second: return UCAL_SECOND; 61 case week_of_year: return UCAL_WEEK_OF_YEAR; 62 case week_of_month: return UCAL_WEEK_OF_MONTH; 63 default: 64 throw std::invalid_argument("Invalid date_time period type"); 65 } 66 } 67 68 69 class calendar_impl : public abstract_calendar { 70 public: 71 calendar_impl(cdata const & dat)72 calendar_impl(cdata const &dat) 73 { 74 UErrorCode err=U_ZERO_ERROR; 75 calendar_.reset(icu::Calendar::createInstance(dat.locale,err)); 76 check_and_throw_dt(err); 77 #if U_ICU_VERSION_MAJOR_NUM*100 + U_ICU_VERSION_MINOR_NUM < 402 78 // workaround old/invalid data, it should be 4 in general 79 calendar_->setMinimalDaysInFirstWeek(4); 80 #endif 81 encoding_ = dat.encoding; 82 } calendar_impl(calendar_impl const & other)83 calendar_impl(calendar_impl const &other) 84 { 85 calendar_.reset(other.calendar_->clone()); 86 encoding_ = other.encoding_; 87 } 88 clone() const89 calendar_impl *clone() const 90 { 91 return new calendar_impl(*this); 92 } 93 set_value(period::marks::period_mark p,int value)94 void set_value(period::marks::period_mark p,int value) 95 { 96 calendar_->set(to_icu(p),int32_t(value)); 97 } 98 get_value(period::marks::period_mark p,value_type type) const99 int get_value(period::marks::period_mark p,value_type type) const 100 { 101 UErrorCode err=U_ZERO_ERROR; 102 int v=0; 103 if(p==period::marks::first_day_of_week) { 104 guard l(lock_); 105 v=calendar_->getFirstDayOfWeek(err); 106 } 107 else { 108 UCalendarDateFields uper=to_icu(p); 109 guard l(lock_); 110 switch(type) { 111 case absolute_minimum: 112 v=calendar_->getMinimum(uper); 113 break; 114 case actual_minimum: 115 v=calendar_->getActualMinimum(uper,err); 116 break; 117 case greatest_minimum: 118 v=calendar_->getGreatestMinimum(uper); 119 break; 120 case current: 121 v=calendar_->get(uper,err); 122 break; 123 case least_maximum: 124 v=calendar_->getLeastMaximum(uper); 125 break; 126 case actual_maximum: 127 v=calendar_->getActualMaximum(uper,err); 128 break; 129 case absolute_maximum: 130 v=calendar_->getMaximum(uper); 131 break; 132 } 133 } 134 check_and_throw_dt(err); 135 return v; 136 } 137 set_time(posix_time const & p)138 virtual void set_time(posix_time const &p) 139 { 140 double utime = p.seconds * 1000.0 + p.nanoseconds / 1000000.0; 141 UErrorCode code=U_ZERO_ERROR; 142 calendar_->setTime(utime,code); 143 check_and_throw_dt(code); 144 } normalize()145 virtual void normalize() 146 { 147 // Can't call complete() explicitly (protected) 148 // calling get wich calls complete 149 UErrorCode code=U_ZERO_ERROR; 150 calendar_->get(UCAL_YEAR,code); 151 check_and_throw_dt(code); 152 } get_time() const153 virtual posix_time get_time() const 154 { 155 156 UErrorCode code=U_ZERO_ERROR; 157 double rtime = 0; 158 { 159 guard l(lock_); 160 rtime = calendar_->getTime(code); 161 } 162 check_and_throw_dt(code); 163 rtime/=1000.0; 164 double secs = floor(rtime); 165 posix_time res; 166 res.seconds = static_cast<int64_t>(secs); 167 res.nanoseconds = static_cast<uint32_t>((rtime - secs) / 1e9); 168 if(res.nanoseconds > 999999999) 169 res.nanoseconds = 999999999; 170 return res; 171 } set_option(calendar_option_type opt,int)172 virtual void set_option(calendar_option_type opt,int /*v*/) 173 { 174 switch(opt) { 175 case is_gregorian: 176 throw date_time_error("is_gregorian is not settable options for calendar"); 177 case is_dst: 178 throw date_time_error("is_dst is not settable options for calendar"); 179 default: 180 ; 181 } 182 } get_option(calendar_option_type opt) const183 virtual int get_option(calendar_option_type opt) const 184 { 185 switch(opt) { 186 case is_gregorian: 187 return dynamic_cast<icu::GregorianCalendar const *>(calendar_.get())!=0; 188 case is_dst: 189 { 190 guard l(lock_); 191 UErrorCode err = U_ZERO_ERROR; 192 bool res = ( calendar_->inDaylightTime(err) != 0 ); 193 check_and_throw_dt(err); 194 return res; 195 } 196 default: 197 return 0; 198 } 199 } adjust_value(period::marks::period_mark p,update_type u,int difference)200 virtual void adjust_value(period::marks::period_mark p,update_type u,int difference) 201 { 202 UErrorCode err=U_ZERO_ERROR; 203 switch(u) { 204 case move: 205 calendar_->add(to_icu(p),difference,err); 206 break; 207 case roll: 208 calendar_->roll(to_icu(p),difference,err); 209 break; 210 } 211 check_and_throw_dt(err); 212 } difference(abstract_calendar const * other_ptr,period::marks::period_mark p) const213 virtual int difference(abstract_calendar const *other_ptr,period::marks::period_mark p) const 214 { 215 UErrorCode err=U_ZERO_ERROR; 216 double other_time = 0; 217 // 218 // fieldDifference has side effect of moving calendar (WTF?) 219 // So we clone it for performing this operation 220 // 221 hold_ptr<icu::Calendar> self(calendar_->clone()); 222 223 calendar_impl const *other_cal=dynamic_cast<calendar_impl const *>(other_ptr); 224 if(other_cal){ 225 guard l(other_cal->lock_); 226 other_time = other_cal->calendar_->getTime(err); 227 check_and_throw_dt(err); 228 } 229 else { 230 posix_time p = other_ptr->get_time(); 231 other_time = p.seconds * 1000.0 + p.nanoseconds / 1000000.0; 232 } 233 234 int diff = self->fieldDifference(other_time,to_icu(p),err); 235 236 check_and_throw_dt(err); 237 return diff; 238 } set_timezone(std::string const & tz)239 virtual void set_timezone(std::string const &tz) 240 { 241 calendar_->adoptTimeZone(get_time_zone(tz)); 242 } get_timezone() const243 virtual std::string get_timezone() const 244 { 245 icu::UnicodeString tz; 246 calendar_->getTimeZone().getID(tz); 247 icu_std_converter<char> cvt(encoding_); 248 return cvt.std(tz); 249 } same(abstract_calendar const * other) const250 virtual bool same(abstract_calendar const *other) const 251 { 252 calendar_impl const *oc=dynamic_cast<calendar_impl const *>(other); 253 if(!oc) 254 return false; 255 return calendar_->isEquivalentTo(*oc->calendar_)!=0; 256 } 257 258 private: 259 typedef boost::unique_lock<boost::mutex> guard; 260 mutable boost::mutex lock_; 261 std::string encoding_; 262 hold_ptr<icu::Calendar> calendar_; 263 }; 264 265 class icu_calendar_facet : public calendar_facet { 266 public: icu_calendar_facet(cdata const & d,size_t refs=0)267 icu_calendar_facet(cdata const &d,size_t refs = 0) : 268 calendar_facet(refs), 269 data_(d) 270 { 271 } create_calendar() const272 virtual abstract_calendar *create_calendar() const 273 { 274 return new calendar_impl(data_); 275 } 276 private: 277 cdata data_; 278 }; 279 create_calendar(std::locale const & in,cdata const & d)280 std::locale create_calendar(std::locale const &in,cdata const &d) 281 { 282 return std::locale(in,new icu_calendar_facet(d)); 283 } 284 285 } // impl_icu 286 } // locale 287 } // boost 288 289 290 291 // vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 292 293