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