• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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