2 // Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
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)
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"
14 #include <boost/thread.hpp>
15 #include <unicode/calendar.h>
16 #include <unicode/gregocal.h>
17 #include <unicode/utypes.h>
24 #include "time_zone.hpp"
33 static void check_and_throw_dt(UErrorCode
&e
)
36 date_time_error(u_errorName(e
));
39 using period::marks::period_mark
;
41 static UCalendarDateFields
to_icu(period::marks::period_mark f
)
43 using namespace period::marks
;
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
;
63 throw std::invalid_argument("Invalid date_time period type");
68 class calendar_impl
: public abstract_calendar
{
71 calendar_impl(cdata
const &dat
)
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);
80 encoding_
= dat
.encoding
;
82 calendar_impl(calendar_impl
const &other
)
84 calendar_
.reset(other
.calendar_
->clone());
85 encoding_
= other
.encoding_
;
88 calendar_impl
*clone() const
90 return new calendar_impl(*this);
93 void set_value(period::marks::period_mark p
,int value
)
95 calendar_
->set(to_icu(p
),int32_t(value
));
98 int get_value(period::marks::period_mark p
,value_type type
) const
100 UErrorCode err
=U_ZERO_ERROR
;
102 if(p
==period::marks::first_day_of_week
) {
104 v
=calendar_
->getFirstDayOfWeek(err
);
107 UCalendarDateFields uper
=to_icu(p
);
110 case absolute_minimum
:
111 v
=calendar_
->getMinimum(uper
);
114 v
=calendar_
->getActualMinimum(uper
,err
);
116 case greatest_minimum
:
117 v
=calendar_
->getGreatestMinimum(uper
);
120 v
=calendar_
->get(uper
,err
);
123 v
=calendar_
->getLeastMaximum(uper
);
126 v
=calendar_
->getActualMaximum(uper
,err
);
128 case absolute_maximum
:
129 v
=calendar_
->getMaximum(uper
);
133 check_and_throw_dt(err
);
137 virtual void set_time(posix_time
const &p
)
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
);
144 virtual void normalize()
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
);
152 virtual posix_time
get_time() const
155 UErrorCode code
=U_ZERO_ERROR
;
159 rtime
= calendar_
->getTime(code
);
161 check_and_throw_dt(code
);
163 double secs
= floor(rtime
);
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;
171 virtual void set_option(calendar_option_type opt
,int /*v*/)
175 throw date_time_error("is_gregorian is not settable options for calendar");
177 throw date_time_error("is_dst is not settable options for calendar");
182 virtual int get_option(calendar_option_type opt
) const
186 return dynamic_cast<icu::GregorianCalendar
const *>(calendar_
.get())!=0;
190 UErrorCode err
= U_ZERO_ERROR
;
191 bool res
= ( calendar_
->inDaylightTime(err
) != 0 );
192 check_and_throw_dt(err
);
199 virtual void adjust_value(period::marks::period_mark p
,update_type u
,int difference
)
201 UErrorCode err
=U_ZERO_ERROR
;
204 calendar_
->add(to_icu(p
),difference
,err
);
207 calendar_
->roll(to_icu(p
),difference
,err
);
210 check_and_throw_dt(err
);
212 virtual int difference(abstract_calendar
const *other_ptr
,period::marks::period_mark p
) const
214 UErrorCode err
=U_ZERO_ERROR
;
215 double other_time
= 0;
217 // fieldDifference has side effect of moving calendar (WTF?)
218 // So we clone it for performing this operation
220 std::auto_ptr
<icu::Calendar
> self(calendar_
->clone());
222 calendar_impl
const *other_cal
=dynamic_cast<calendar_impl
const *>(other_ptr
);
224 guard
l(other_cal
->lock_
);
225 other_time
= other_cal
->calendar_
->getTime(err
);
226 check_and_throw_dt(err
);
229 posix_time p
= other_ptr
->get_time();
230 other_time
= p
.seconds
* 1000.0 + p
.nanoseconds
/ 1000000.0;
233 int diff
= self
->fieldDifference(other_time
,to_icu(p
),err
);
235 check_and_throw_dt(err
);
238 virtual void set_timezone(std::string
const &tz
)
240 calendar_
->adoptTimeZone(get_time_zone(tz
));
242 virtual std::string
get_timezone() const
244 icu::UnicodeString tz
;
245 calendar_
->getTimeZone().getID(tz
);
246 icu_std_converter
<char> cvt(encoding_
);
249 virtual bool same(abstract_calendar
const *other
) const
251 calendar_impl
const *oc
=dynamic_cast<calendar_impl
const *>(other
);
254 return calendar_
->isEquivalentTo(*oc
->calendar_
)!=0;
258 typedef boost::unique_lock
<boost::mutex
> guard
;
259 mutable boost::mutex lock_
;
260 std::string encoding_
;
261 hold_ptr
<icu::Calendar
> calendar_
;
264 class icu_calendar_facet
: public calendar_facet
{
266 icu_calendar_facet(cdata
const &d
,size_t refs
= 0) :
267 calendar_facet(refs
),
271 virtual abstract_calendar
*create_calendar() const
273 return new calendar_impl(data_
);
279 std::locale
create_calendar(std::locale
const &in
,cdata
const &d
)
281 return std::locale(in
,new icu_calendar_facet(d
));
290 // vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4