]>
git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/locale/src/util/gregorian.cpp
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/config.hpp>
11 # pragma warning(disable : 4996)
16 #include <boost/locale/date_time_facet.hpp>
17 #include <boost/locale/date_time.hpp>
24 #include "timezone.hpp"
25 #include "gregorian.hpp"
43 int days_in_month(int year
,int month
)
45 static const int tbl
[2][12] = {
46 { 31,28,31,30,31,30,31,31,30,31,30,31 },
47 { 31,29,31,30,31,30,31,31,30,31,30,31 }
49 return tbl
[is_leap(year
)][month
- 1];
52 inline int days_from_0(int year
)
55 return 365 * year
+ (year
/ 400) - (year
/100) + (year
/ 4);
58 int days_from_1970(int year
)
60 static const int days_from_0_to_1970
= days_from_0(1970);
61 return days_from_0(year
) - days_from_0_to_1970
;
64 int days_from_1jan(int year
,int month
,int day
)
66 static const int days
[2][12] = {
67 { 0,31,59,90,120,151,181,212,243,273,304,334 },
68 { 0,31,60,91,121,152,182,213,244,274,305,335 }
70 return days
[is_leap(year
)][month
-1] + day
- 1;
73 std::time_t internal_timegm(std::tm
const *t
)
75 int year
= t
->tm_year
+ 1900;
76 int month
= t
->tm_mon
;
82 int years_diff
= (-month
+ 11)/12;
84 month
+=12 * years_diff
;
88 int day_of_year
= days_from_1jan(year
,month
,day
);
89 int days_since_epoch
= days_from_1970(year
) + day_of_year
;
91 std::time_t seconds_in_day
= 3600 * 24;
92 std::time_t result
= seconds_in_day
* days_since_epoch
+ 3600 * t
->tm_hour
+ 60 * t
->tm_min
+ t
->tm_sec
;
104 // Locale dependent data
106 bool comparator(char const *left
,char const *right
)
108 return strcmp(left
,right
) < 0;
112 // Ref: CLDR 1.9 common/supplemental/supplementalData.xml
116 // sat - AE AF BH DJ DZ EG ER ET IQ IR JO KE KW LY MA OM QA SA SD SO SY TN YE
117 // sun - AR AS AZ BW CA CN FO GE GL GU HK IL IN JM JP KG KR LA MH MN MO MP MT NZ PH PK SG TH TT TW UM US UZ VI ZW
120 int first_day_of_week(char const *terr
) {
121 static char const * const sat
[] = {
122 "AE","AF","BH","DJ","DZ","EG","ER","ET","IQ","IR",
123 "JO","KE","KW","LY","MA","OM","QA","SA","SD","SO",
126 static char const * const sunday
[] = {
127 "AR","AS","AZ","BW","CA","CN","FO","GE","GL","GU",
128 "HK","IL","IN","JM","JP","KG","KR","LA","MH","MN",
129 "MO","MP","MT","NZ","PH","PK","SG","TH","TT","TW",
130 "UM","US","UZ","VI","ZW"
132 if(strcmp(terr
,"MV") == 0)
134 if(std::binary_search
<char const * const *>(sat
,sat
+sizeof(sat
)/(sizeof(sat
[0])),terr
,comparator
))
136 if(std::binary_search
<char const * const *>(sunday
,sunday
+sizeof(sunday
)/(sizeof(sunday
[0])),terr
,comparator
))
143 class gregorian_calendar
: public abstract_calendar
{
146 gregorian_calendar(std::string
const &terr
)
148 first_day_of_week_
= first_day_of_week(terr
.c_str());
149 time_
= std::time(0);
156 /// Make a polymorphic copy of the calendar
158 virtual gregorian_calendar
*clone() const
160 return new gregorian_calendar(*this);
164 /// Set specific \a value for period \a p, note not all values are settable.
166 virtual void set_value(period::marks::period_mark p
,int value
)
168 using namespace period::marks
;
170 case era
: ///< Era i.e. AC, BC in Gregorian and Julian calendar, range [0,1]
172 case year
: ///< Year, it is calendar specific
173 case extended_year
: ///< Extended year for Gregorian/Julian calendars, where 1 BC == 0, 2 BC == -1.
174 tm_updated_
.tm_year
= value
- 1900;
177 tm_updated_
.tm_mon
= value
;
180 tm_updated_
.tm_mday
= value
;
182 case hour
: ///< 24 clock hour [0..23]
183 tm_updated_
.tm_hour
= value
;
185 case hour_12
: ///< 12 clock hour [0..11]
186 tm_updated_
.tm_hour
= tm_updated_
.tm_hour
/ 12 * 12 + value
;
188 case am_pm
: ///< am or pm marker, [0..1]
189 tm_updated_
.tm_hour
= 12 * value
+ tm_updated_
.tm_hour
% 12;
191 case minute
: ///< minute [0..59]
192 tm_updated_
.tm_min
= value
;
195 tm_updated_
.tm_sec
= value
;
199 tm_updated_
.tm_mday
+= (value
- (tm_updated_
.tm_yday
+ 1));
201 case day_of_week
: ///< Day of week, starting from Sunday, [1..7]
202 if(value
< 1) // make sure it is positive
203 value
+= (-value
/ 7) * 7 + 7;
204 // convert to local DOW
205 value
= (value
- 1 - first_day_of_week_
+ 14) % 7 + 1;
207 case day_of_week_local
: ///< Local day of week, for example in France Monday is 1, in US Sunday is 1, [1..7]
209 tm_updated_
.tm_mday
+= (value
- 1) - (tm_updated_
.tm_wday
- first_day_of_week_
+ 7) % 7;
211 case day_of_week_in_month
: ///< Original number of the day of the week in month. (1st sunday, 2nd sunday etc)
212 case week_of_year
: ///< The week number in the year, 4 is the minimal number of days to be in month
213 case week_of_month
: ///< The week number withing current month
216 int current_week
= get_value(p
,current
);
217 int diff
= 7 * (value
- current_week
);
218 tm_updated_
.tm_mday
+= diff
;
221 case period::marks::first_day_of_week
: ///< For example Sunday in US, Monday in France
231 std::tm val
= tm_updated_
;
233 val
.tm_wday
= -1; // indecator of error
234 std::time_t point
= -1;
236 point
= std::mktime(&val
);
237 if(point
== static_cast<std::time_t>(-1)){
238 #ifndef BOOST_WINDOWS
239 // windows does not handle negative time_t, under other plaforms
240 // it may be actually valid value in 1969-12-31 23:59:59
241 // so we check that a filed was updated - does not happen in case of error
242 if(val
.tm_wday
== -1)
245 throw date_time_error("boost::locale::gregorian_calendar: invalid time");
250 point
= internal_timegm(&val
);
252 // Windows uses TLS, thread safe
253 std::tm
*revert_point
= 0;
254 if(point
< 0 || (revert_point
= gmtime(&point
)) == 0)
255 throw date_time_error("boost::locale::gregorian_calendar time is out of range");
258 if(!gmtime_r(&point
,&val
))
259 throw date_time_error("boost::locale::gregorian_calendar invalid time");
264 time_
= point
- tzoff_
;
271 int get_week_number(int day
,int wday
) const
274 /// This is the number of days that are considered withing
275 /// period such that the week belongs there
277 static const int days_in_full_week
= 4;
280 // Alaways use local week start
281 int current_dow
= (wday
- first_day_of_week_
+ 7) % 7;
282 // Calculate local week day of Jan 1st.
283 int first_week_day
= (current_dow
+ 700 - day
) % 7;
284 // adding something big devidable by 7
286 int start_of_period_in_weeks
;
287 if(first_week_day
< days_in_full_week
) {
288 start_of_period_in_weeks
= - first_week_day
;
291 start_of_period_in_weeks
= 7 - first_week_day
;
293 int week_number_in_days
= day
- start_of_period_in_weeks
;
294 if(week_number_in_days
< 0)
296 return week_number_in_days
/ 7 + 1;
300 /// Get specific value for period \a p according to a value_type \a v
302 virtual int get_value(period::marks::period_mark p
,value_type v
) const
304 using namespace period::marks
;
311 case absolute_minimum
:
312 case greatest_minimum
:
315 return 1970; // Unix epoch windows can't handle negative time_t
317 if(sizeof(std::time_t) == 4)
318 return 1901; // minimal year with 32 bit time_t
322 case absolute_maximum
:
325 if(sizeof(std::time_t) == 4)
326 return 2038; // Y2K38 - maximal with 32 bit time_t
328 return std::numeric_limits
<int>::max();
330 return tm_
.tm_year
+ 1900;
335 case absolute_minimum
:
336 case greatest_minimum
:
339 case absolute_maximum
:
349 case absolute_minimum
:
350 case greatest_minimum
:
353 case absolute_maximum
:
358 return days_in_month(tm_
.tm_year
+ 1900,tm_
.tm_mon
+ 1);
363 case day_of_year
: ///< The number of day in year, starting from 1
365 case absolute_minimum
:
366 case greatest_minimum
:
369 case absolute_maximum
:
374 return is_leap(tm_
.tm_year
+ 1900) ? 366 : 365;
376 return tm_
.tm_yday
+ 1;
379 case day_of_week
: ///< Day of week, starting from Sunday, [1..7]
381 case absolute_minimum
:
382 case greatest_minimum
:
385 case absolute_maximum
:
390 return tm_
.tm_wday
+ 1;
393 case day_of_week_local
: ///< Local day of week, for example in France Monday is 1, in US Sunday is 1, [1..7]
395 case absolute_minimum
:
396 case greatest_minimum
:
399 case absolute_maximum
:
404 return (tm_
.tm_wday
- first_day_of_week_
+ 7) % 7 + 1;
407 case hour
: ///< 24 clock hour [0..23]
409 case absolute_minimum
:
410 case greatest_minimum
:
413 case absolute_maximum
:
421 case hour_12
: ///< 12 clock hour [0..11]
423 case absolute_minimum
:
424 case greatest_minimum
:
427 case absolute_maximum
:
432 return tm_
.tm_hour
% 12;
435 case am_pm
: ///< am or pm marker, [0..1]
437 case absolute_minimum
:
438 case greatest_minimum
:
441 case absolute_maximum
:
446 return tm_
.tm_hour
>= 12 ? 1 : 0;
449 case minute
: ///< minute [0..59]
451 case absolute_minimum
:
452 case greatest_minimum
:
455 case absolute_maximum
:
463 case second
: ///< second [0..59]
465 case absolute_minimum
:
466 case greatest_minimum
:
469 case absolute_maximum
:
477 case period::marks::first_day_of_week
: ///< For example Sunday in US, Monday in France
478 return first_day_of_week_
+ 1;
480 case week_of_year
: ///< The week number in the year
482 case absolute_minimum
:
483 case greatest_minimum
:
486 case absolute_maximum
:
492 int year
= tm_
.tm_year
+ 1900;
493 int end_of_year_days
= (is_leap(year
) ? 366 : 365) - 1;
494 int dow_of_end_of_year
= (end_of_year_days
- tm_
.tm_yday
+ tm_
.tm_wday
) % 7;
495 return get_week_number(end_of_year_days
,dow_of_end_of_year
);
499 int val
= get_week_number(tm_
.tm_yday
,tm_
.tm_wday
);
505 case week_of_month
: ///< The week number withing current month
507 case absolute_minimum
:
508 case greatest_minimum
:
511 case absolute_maximum
:
517 int end_of_month_days
= days_in_month(tm_
.tm_year
+ 1900,tm_
.tm_mon
+ 1);
518 int dow_of_end_of_month
= (end_of_month_days
- tm_
.tm_mday
+ tm_
.tm_wday
) % 7;
519 return get_week_number(end_of_month_days
,dow_of_end_of_month
);
523 int val
= get_week_number(tm_
.tm_mday
,tm_
.tm_wday
);
530 case day_of_week_in_month
: ///< Original number of the day of the week in month.
532 case absolute_minimum
:
533 case greatest_minimum
:
536 case absolute_maximum
:
541 if(tm_
.tm_mon
== 1 && !is_leap(tm_
.tm_year
+ 1900)) {
542 // only in february in non leap year is 28 days, the rest
543 // conver more then 4 weeks
548 return (tm_
.tm_mday
- 1) / 7 + 1;
560 /// Set current time point
562 virtual void set_time(posix_time
const &p
)
564 from_time(static_cast<std::time_t>(p
.seconds
));
566 virtual posix_time
get_time() const
568 posix_time pt
= { time_
, 0};
573 /// Set option for calendar, for future use
575 virtual void set_option(calendar_option_type opt
,int /*v*/)
579 throw date_time_error("is_gregorian is not settable options for calendar");
581 throw date_time_error("is_dst is not settable options for calendar");
587 /// Get option for calendar, currently only check if it is Gregorian calendar
589 virtual int get_option(calendar_option_type opt
) const
595 return tm_
.tm_isdst
== 1;
602 /// Adjust period's \a p value by \a difference items using a update_type \a u.
603 /// Note: not all values are adjustable
605 virtual void adjust_value(period::marks::period_mark p
,update_type u
,int difference
)
610 using namespace period::marks
;
612 case year
: ///< Year, it is calendar specific
613 case extended_year
: ///< Extended year for Gregorian/Julian calendars, where 1 BC == 0, 2 BC == -1.
614 tm_updated_
.tm_year
+=difference
;
617 tm_updated_
.tm_mon
+=difference
;
621 case day_of_week
: ///< Day of week, starting from Sunday, [1..7]
622 case day_of_week_local
: ///< Local day of week, for example in France Monday is 1, in US Sunday is 1, [1..7]
623 tm_updated_
.tm_mday
+=difference
;
625 case hour
: ///< 24 clock hour [0..23]
626 case hour_12
: ///< 12 clock hour [0..11]
627 tm_updated_
.tm_hour
+= difference
;
629 case am_pm
: ///< am or pm marker, [0..1]
630 tm_updated_
.tm_hour
+= 12 * difference
;
632 case minute
: ///< minute [0..59]
633 tm_updated_
.tm_min
+= difference
;
636 tm_updated_
.tm_sec
+= difference
;
638 case week_of_year
: ///< The week number in the year
639 case week_of_month
: ///< The week number withing current month
640 case day_of_week_in_month
: ///< Original number of the day of the week in month.
641 tm_updated_
.tm_mday
+=difference
* 7;
644 ; // Not all values are adjustable
652 int cur_min
= get_value(p
,actual_minimum
);
653 int cur_max
= get_value(p
,actual_maximum
);
654 int max_diff
= cur_max
- cur_min
+ 1;
656 int value
= get_value(p
,current
);
659 addon
= ((-difference
/max_diff
) + 1) * max_diff
;
660 value
= (value
- cur_min
+ difference
+ addon
) % max_diff
+ cur_min
;
670 int get_diff(period::marks::period_mark p
,int diff
,gregorian_calendar
const *other
) const
674 std::auto_ptr
<gregorian_calendar
> self(clone());
675 self
->adjust_value(p
,move
,diff
);
677 if(self
->time_
> other
->time_
)
683 if(self
->time_
< other
->time_
)
691 /// Calculate the difference between this calendar and \a other in \a p units
693 virtual int difference(abstract_calendar
const *other_cal
,period::marks::period_mark p
) const
695 std::auto_ptr
<gregorian_calendar
> keeper
;
696 gregorian_calendar
const *other
= dynamic_cast<gregorian_calendar
const *>(other_cal
);
698 keeper
.reset(clone());
699 keeper
->set_time(other_cal
->get_time());
700 other
= keeper
.get();
703 int factor
= 1; // for weeks vs days handling
705 using namespace period::marks
;
712 int diff
= other
->tm_
.tm_year
- tm_
.tm_year
;
713 return get_diff(period::marks::year
,diff
,other
);
717 int diff
= 12 * (other
->tm_
.tm_year
- tm_
.tm_year
)
718 + other
->tm_
.tm_mon
- tm_
.tm_mon
;
719 return get_diff(period::marks::month
,diff
,other
);
721 case day_of_week_in_month
:
729 case day_of_week_local
:
731 int diff
= other
->tm_
.tm_yday
- tm_
.tm_yday
;
732 if(other
->tm_
.tm_year
!= tm_
.tm_year
) {
733 diff
+= days_from_0(other
->tm_
.tm_year
+ 1900) -
734 days_from_0(tm_
.tm_year
+ 1900);
736 return get_diff(period::marks::day
,diff
,other
) / factor
;
739 return static_cast<int>( (other
->time_
- time_
) / (3600*12) );
742 return static_cast<int>( (other
->time_
- time_
) / 3600 );
744 return static_cast<int>( (other
->time_
- time_
) / 60 );
746 return static_cast<int>( other
->time_
- time_
);
753 /// Set time zone, empty - use system
755 virtual void set_timezone(std::string
const &tz
)
763 tzoff_
= parse_tz(tz
);
766 time_zone_name_
= tz
;
768 virtual std::string
get_timezone() const
770 return time_zone_name_
;
773 virtual bool same(abstract_calendar
const *other
) const
775 gregorian_calendar
const *gcal
= dynamic_cast<gregorian_calendar
const *>(other
);
779 gcal
->tzoff_
== tzoff_
780 && gcal
->is_local_
== is_local_
781 && gcal
->first_day_of_week_
== first_day_of_week_
;
784 virtual ~gregorian_calendar()
790 void from_time(std::time_t point
)
792 std::time_t real_point
= point
+ tzoff_
;
795 // Windows uses TLS, thread safe
796 t
= is_local_
? localtime(&real_point
) : gmtime(&real_point
);
799 t
= is_local_
? localtime_r(&real_point
,&tmp_tm
) : gmtime_r(&real_point
,&tmp_tm
);
802 throw date_time_error("boost::locale::gregorian_calendar: invalid time point");
809 int first_day_of_week_
;
816 std::string time_zone_name_
;
820 abstract_calendar
*create_gregorian_calendar(std::string
const &terr
)
822 return new gregorian_calendar(terr
);
825 class gregorian_facet
: public calendar_facet
{
827 gregorian_facet(std::string
const &terr
,size_t refs
= 0) :
828 calendar_facet(refs
),
832 virtual abstract_calendar
*create_calendar() const
834 return create_gregorian_calendar(terr_
);
840 std::locale
install_gregorian_calendar(std::locale
const &in
,std::string
const &terr
)
842 return std::locale(in
,new gregorian_facet(terr
));
851 // vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4