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/config.hpp>
10 #ifdef BOOST_MSVC
11 #  pragma warning(disable : 4996)
12 #endif
13 #include <locale>
14 #include <string>
15 #include <ios>
16 #include <boost/locale/date_time_facet.hpp>
17 #include <boost/locale/date_time.hpp>
18 #include <boost/locale/hold_ptr.hpp>
19 #include <stdlib.h>
20 #include <ctime>
21 #include <memory>
22 #include <algorithm>
23 #include <limits>
24 
25 #include "timezone.hpp"
26 #include "gregorian.hpp"
27 
28 namespace boost {
29 namespace locale {
30 namespace util {
31     namespace {
32 
is_leap(int year)33         int is_leap(int year)
34         {
35             if(year % 400 == 0)
36                 return 1;
37             if(year % 100 == 0)
38                 return 0;
39             if(year % 4 == 0)
40                 return 1;
41             return 0;
42         }
43 
days_in_month(int year,int month)44         int days_in_month(int year,int month)
45         {
46             static const int tbl[2][12] = {
47                 { 31,28,31,30,31,30,31,31,30,31,30,31 },
48                 { 31,29,31,30,31,30,31,31,30,31,30,31 }
49             };
50             return tbl[is_leap(year)][month - 1];
51         }
52 
days_from_0(int year)53         inline int days_from_0(int year)
54         {
55             year--;
56             return 365 * year + (year / 400) - (year/100) + (year / 4);
57         }
58 
days_from_1970(int year)59         int days_from_1970(int year)
60         {
61             static const int days_from_0_to_1970 = days_from_0(1970);
62             return days_from_0(year) - days_from_0_to_1970;
63         }
64 
days_from_1jan(int year,int month,int day)65         int days_from_1jan(int year,int month,int day)
66         {
67             static const int days[2][12] = {
68                 { 0,31,59,90,120,151,181,212,243,273,304,334 },
69                 { 0,31,60,91,121,152,182,213,244,274,305,335 }
70             };
71             return days[is_leap(year)][month-1] + day - 1;
72         }
73 
internal_timegm(std::tm const * t)74         std::time_t internal_timegm(std::tm const *t)
75         {
76             int year = t->tm_year + 1900;
77             int month = t->tm_mon;
78             if(month > 11) {
79                 year += month/12;
80                 month %= 12;
81             }
82             else if(month < 0) {
83                 int years_diff = (-month + 11)/12;
84                 year -= years_diff;
85                 month+=12 * years_diff;
86             }
87             month++;
88             int day = t->tm_mday;
89             int day_of_year = days_from_1jan(year,month,day);
90             int days_since_epoch = days_from_1970(year) + day_of_year;
91 
92             std::time_t seconds_in_day = 3600 * 24;
93             std::time_t result =  seconds_in_day * days_since_epoch + 3600 * t->tm_hour + 60 * t->tm_min + t->tm_sec;
94 
95             return result;
96         }
97 
98     } // anon
99 
100 
101 
102 
103     namespace {
104 
105         // Locale dependent data
106 
comparator(char const * left,char const * right)107         bool comparator(char const *left,char const *right)
108         {
109             return strcmp(left,right) < 0;
110         }
111 
112         //
113         // Ref: CLDR 1.9 common/supplemental/supplementalData.xml
114         //
115         // monday - default
116         // fri - MV
117         // sat - AE AF BH DJ DZ EG ER ET IQ IR JO KE KW LY MA OM QA SA SD SO SY TN YE
118         // 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
119         //
120 
first_day_of_week(char const * terr)121         int first_day_of_week(char const *terr) {
122             static char const * const sat[] = {
123                 "AE","AF","BH","DJ","DZ","EG","ER","ET","IQ","IR",
124                 "JO","KE","KW","LY","MA","OM","QA","SA","SD","SO",
125                 "SY","TN","YE"
126             };
127             static char const * const sunday[] = {
128                 "AR","AS","AZ","BW","CA","CN","FO","GE","GL","GU",
129                 "HK","IL","IN","JM","JP","KG","KR","LA","MH","MN",
130                 "MO","MP","MT","NZ","PH","PK","SG","TH","TT","TW",
131                 "UM","US","UZ","VI","ZW"
132             };
133             if(strcmp(terr,"MV") == 0)
134                 return 5; // fri
135             if(std::binary_search<char const * const *>(sat,sat+sizeof(sat)/(sizeof(sat[0])),terr,comparator))
136                 return 6; // sat
137             if(std::binary_search<char const * const *>(sunday,sunday+sizeof(sunday)/(sizeof(sunday[0])),terr,comparator))
138                 return 0; // sun
139             // default
140             return 1; // mon
141         }
142     }
143 
144     class gregorian_calendar : public abstract_calendar {
145     public:
146 
gregorian_calendar(std::string const & terr)147             gregorian_calendar(std::string const &terr)
148             {
149                 first_day_of_week_ = first_day_of_week(terr.c_str());
150                 time_ = std::time(0);
151                 is_local_ = true;
152                 tzoff_ = 0;
153                 from_time(time_);
154             }
155 
156             ///
157             /// Make a polymorphic copy of the calendar
158             ///
clone() const159             virtual gregorian_calendar *clone() const
160             {
161                 return new gregorian_calendar(*this);
162             }
163 
164             ///
165             /// Set specific \a value for period \a p, note not all values are settable.
166             ///
set_value(period::marks::period_mark p,int value)167             virtual void set_value(period::marks::period_mark p,int value)
168             {
169                 using namespace period::marks;
170                 switch(p) {
171                 case era:                        ///< Era i.e. AC, BC in Gregorian and Julian calendar, range [0,1]
172                     return;
173                 case year:                       ///< Year, it is calendar specific
174                 case extended_year:              ///< Extended year for Gregorian/Julian calendars, where 1 BC == 0, 2 BC == -1.
175                     tm_updated_.tm_year = value - 1900;
176                     break;
177                 case month:
178                     tm_updated_.tm_mon = value;
179                     break;
180                 case day:
181                     tm_updated_.tm_mday = value;
182                     break;
183                 case hour:                       ///< 24 clock hour [0..23]
184                     tm_updated_.tm_hour = value;
185                     break;
186                 case hour_12:                    ///< 12 clock hour [0..11]
187                     tm_updated_.tm_hour = tm_updated_.tm_hour / 12 * 12 + value;
188                     break;
189                 case am_pm:                      ///< am or pm marker, [0..1]
190                     tm_updated_.tm_hour = 12 * value + tm_updated_.tm_hour % 12;
191                     break;
192                 case minute:                     ///< minute [0..59]
193                     tm_updated_.tm_min = value;
194                     break;
195                 case second:
196                     tm_updated_.tm_sec = value;
197                     break;
198                 case day_of_year:
199                     normalize();
200                     tm_updated_.tm_mday += (value - (tm_updated_.tm_yday + 1));
201                     break;
202                 case day_of_week:           ///< Day of week, starting from Sunday, [1..7]
203                     if(value < 1) // make sure it is positive
204                         value += (-value / 7) * 7 + 7;
205                     // convert to local DOW
206                     value = (value - 1 - first_day_of_week_ + 14) % 7 + 1;
207                     // fall throght
208                 case day_of_week_local:     ///< Local day of week, for example in France Monday is 1, in US Sunday is 1, [1..7]
209                     normalize();
210                     tm_updated_.tm_mday += (value - 1) - (tm_updated_.tm_wday - first_day_of_week_ + 7) % 7;
211                     break;
212                 case day_of_week_in_month:  ///< Original number of the day of the week in month. (1st sunday, 2nd sunday etc)
213                 case week_of_year:          ///< The week number in the year, 4 is the minimal number of days to be in month
214                 case week_of_month:         ///< The week number withing current month
215                     {
216                         normalize();
217                         int current_week = get_value(p,current);
218                         int diff = 7 * (value - current_week);
219                         tm_updated_.tm_mday += diff;
220                     }
221                     break;
222                 case period::marks::first_day_of_week:          ///< For example Sunday in US, Monday in France
223                 default:
224                     return;
225                 }
226                 normalized_ = false;
227             }
228 
normalize()229             void normalize()
230             {
231                 if(!normalized_) {
232                     std::tm val = tm_updated_;
233                     val.tm_isdst = -1;
234                     val.tm_wday = -1; // indecator of error
235                     std::time_t point = -1;
236                     if(is_local_) {
237                         point = std::mktime(&val);
238                         if(point == static_cast<std::time_t>(-1)){
239                             #ifndef BOOST_WINDOWS
240                             // windows does not handle negative time_t, under other plaforms
241                             // it may be actually valid value in  1969-12-31 23:59:59
242                             // so we check that a filed was updated - does not happen in case of error
243                             if(val.tm_wday == -1)
244                             #endif
245                             {
246                                 throw date_time_error("boost::locale::gregorian_calendar: invalid time");
247                             }
248                         }
249                     }
250                     else {
251                         point = internal_timegm(&val);
252                         #ifdef BOOST_WINDOWS
253                         // Windows uses TLS, thread safe
254                         std::tm *revert_point = 0;
255                         if(point < 0  || (revert_point = gmtime(&point)) == 0)
256                             throw date_time_error("boost::locale::gregorian_calendar time is out of range");
257                         val = *revert_point;
258                         #else
259                         if(!gmtime_r(&point,&val))
260                             throw date_time_error("boost::locale::gregorian_calendar invalid time");
261                         #endif
262 
263                     }
264 
265                     time_ = point - tzoff_;
266                     tm_ = val;
267                     tm_updated_ = val;
268                     normalized_ = true;
269                 }
270             }
271 
get_week_number(int day,int wday) const272             int get_week_number(int day,int wday) const
273             {
274                 ///
275                 /// This is the number of days that are considered withing
276                 /// period such that the week belongs there
277                 ///
278                 static const int days_in_full_week = 4;
279 
280 
281                 // Alaways use local week start
282                 int current_dow = (wday - first_day_of_week_ + 7) % 7;
283                 // Calculate local week day of Jan 1st.
284                 int first_week_day = (current_dow + 700 - day) % 7;
285                     // adding something big devidable by 7
286 
287                 int start_of_period_in_weeks;
288                 if(first_week_day < days_in_full_week) {
289                     start_of_period_in_weeks = - first_week_day;
290                 }
291                 else {
292                     start_of_period_in_weeks = 7 - first_week_day;
293                 }
294                 int week_number_in_days = day - start_of_period_in_weeks;
295                 if(week_number_in_days < 0)
296                     return -1;
297                 return week_number_in_days / 7 + 1;
298             }
299 
300             ///
301             /// Get specific value for period \a p according to a value_type \a v
302             ///
get_value(period::marks::period_mark p,value_type v) const303             virtual int get_value(period::marks::period_mark p,value_type v) const
304             {
305                 using namespace period::marks;
306                 switch(p) {
307                 case era:
308                     return 1;
309                 case year:
310                 case extended_year:
311                     switch(v) {
312                     case absolute_minimum:
313                     case greatest_minimum:
314                     case actual_minimum:
315                         #ifdef BOOST_WINDOWS
316                         return 1970; // Unix epoch windows can't handle negative time_t
317                         #else
318                         if(sizeof(std::time_t) == 4)
319                             return 1901; // minimal year with 32 bit time_t
320                         else
321                             return 1;
322                         #endif
323                     case absolute_maximum:
324                     case least_maximum:
325                     case actual_maximum:
326                         if(sizeof(std::time_t) == 4)
327                             return 2038; // Y2K38 - maximal with 32 bit time_t
328                         else
329                             return std::numeric_limits<int>::max();
330                     case current:
331                         return tm_.tm_year + 1900;
332                     };
333                     break;
334                 case month:
335                     switch(v) {
336                     case absolute_minimum:
337                     case greatest_minimum:
338                     case actual_minimum:
339                         return 0;
340                     case absolute_maximum:
341                     case least_maximum:
342                     case actual_maximum:
343                         return 11;
344                     case current:
345                         return tm_.tm_mon;
346                     };
347                     break;
348                 case day:
349                     switch(v) {
350                     case absolute_minimum:
351                     case greatest_minimum:
352                     case actual_minimum:
353                         return 1;
354                     case absolute_maximum:
355                         return 31;
356                     case least_maximum:
357                         return 28;
358                     case actual_maximum:
359                         return days_in_month(tm_.tm_year + 1900,tm_.tm_mon + 1);
360                     case current:
361                         return tm_.tm_mday;
362                     };
363                     break;
364                 case day_of_year:                ///< The number of day in year, starting from 1
365                     switch(v) {
366                     case absolute_minimum:
367                     case greatest_minimum:
368                     case actual_minimum:
369                         return 1;
370                     case absolute_maximum:
371                         return 366;
372                     case least_maximum:
373                         return 365;
374                     case actual_maximum:
375                         return is_leap(tm_.tm_year + 1900) ? 366 : 365;
376                     case current:
377                         return tm_.tm_yday + 1;
378                     }
379                     break;
380                 case day_of_week:                ///< Day of week, starting from Sunday, [1..7]
381                     switch(v) {
382                     case absolute_minimum:
383                     case greatest_minimum:
384                     case actual_minimum:
385                         return 1;
386                     case absolute_maximum:
387                     case least_maximum:
388                     case actual_maximum:
389                         return 7;
390                     case current:
391                         return tm_.tm_wday + 1;
392                     }
393                     break;
394                 case day_of_week_local:     ///< Local day of week, for example in France Monday is 1, in US Sunday is 1, [1..7]
395                     switch(v) {
396                     case absolute_minimum:
397                     case greatest_minimum:
398                     case actual_minimum:
399                         return 1;
400                     case absolute_maximum:
401                     case least_maximum:
402                     case actual_maximum:
403                         return 7;
404                     case current:
405                         return (tm_.tm_wday - first_day_of_week_ + 7) % 7 + 1;
406                     }
407                     break;
408                 case hour:                       ///< 24 clock hour [0..23]
409                     switch(v) {
410                     case absolute_minimum:
411                     case greatest_minimum:
412                     case actual_minimum:
413                         return 0;
414                     case absolute_maximum:
415                     case least_maximum:
416                     case actual_maximum:
417                         return 23;
418                     case current:
419                         return tm_.tm_hour;
420                     }
421                     break;
422                 case hour_12:                    ///< 12 clock hour [0..11]
423                     switch(v) {
424                     case absolute_minimum:
425                     case greatest_minimum:
426                     case actual_minimum:
427                         return 0;
428                     case absolute_maximum:
429                     case least_maximum:
430                     case actual_maximum:
431                         return 11;
432                     case current:
433                         return tm_.tm_hour % 12;
434                     }
435                     break;
436                 case am_pm:                      ///< am or pm marker, [0..1]
437                     switch(v) {
438                     case absolute_minimum:
439                     case greatest_minimum:
440                     case actual_minimum:
441                         return 0;
442                     case absolute_maximum:
443                     case least_maximum:
444                     case actual_maximum:
445                         return 1;
446                     case current:
447                         return tm_.tm_hour >= 12 ? 1 : 0;
448                     }
449                     break;
450                 case minute:                     ///< minute [0..59]
451                     switch(v) {
452                     case absolute_minimum:
453                     case greatest_minimum:
454                     case actual_minimum:
455                         return 0;
456                     case absolute_maximum:
457                     case least_maximum:
458                     case actual_maximum:
459                         return 59;
460                     case current:
461                         return tm_.tm_min;
462                     }
463                     break;
464                 case second:                     ///< second [0..59]
465                     switch(v) {
466                     case absolute_minimum:
467                     case greatest_minimum:
468                     case actual_minimum:
469                         return 0;
470                     case absolute_maximum:
471                     case least_maximum:
472                     case actual_maximum:
473                         return 59;
474                     case current:
475                         return tm_.tm_sec;
476                     }
477                     break;
478                 case period::marks::first_day_of_week:          ///< For example Sunday in US, Monday in France
479                     return first_day_of_week_ + 1;
480 
481                 case week_of_year:               ///< The week number in the year
482                     switch(v) {
483                     case absolute_minimum:
484                     case greatest_minimum:
485                     case actual_minimum:
486                         return 1;
487                     case absolute_maximum:
488                         return 53;
489                     case least_maximum:
490                         return 52;
491                     case actual_maximum:
492                         {
493                             int year = tm_.tm_year + 1900;
494                             int end_of_year_days = (is_leap(year) ? 366 : 365) - 1;
495                             int dow_of_end_of_year = (end_of_year_days - tm_.tm_yday + tm_.tm_wday) % 7;
496                             return get_week_number(end_of_year_days,dow_of_end_of_year);
497                         }
498                     case current:
499                         {
500                             int val = get_week_number(tm_.tm_yday,tm_.tm_wday);
501                             if(val < 0)
502                                 return 53;
503                             return val;
504                         }
505                     }
506                 case week_of_month:              ///< The week number withing current month
507                     switch(v) {
508                     case absolute_minimum:
509                     case greatest_minimum:
510                     case actual_minimum:
511                         return 1;
512                     case absolute_maximum:
513                         return 5;
514                     case least_maximum:
515                         return 4;
516                     case actual_maximum:
517                         {
518                             int end_of_month_days = days_in_month(tm_.tm_year + 1900,tm_.tm_mon + 1);
519                             int dow_of_end_of_month = (end_of_month_days - tm_.tm_mday + tm_.tm_wday) % 7;
520                             return get_week_number(end_of_month_days,dow_of_end_of_month);
521                         }
522                     case current:
523                         {
524                             int val = get_week_number(tm_.tm_mday,tm_.tm_wday);
525                             if(val < 0)
526                                 return 5;
527                             return val;
528                         }
529                     }
530 
531                 case day_of_week_in_month:       ///< Original number of the day of the week in month.
532                     switch(v) {
533                     case absolute_minimum:
534                     case greatest_minimum:
535                     case actual_minimum:
536                         return 1;
537                     case absolute_maximum:
538                         return 5;
539                     case least_maximum:
540                         return 4;
541                     case actual_maximum:
542                         if(tm_.tm_mon == 1 && !is_leap(tm_.tm_year + 1900)) {
543                             // only in february in non leap year is 28 days, the rest
544                             // conver more then 4 weeks
545                             return 4;
546                         }
547                         return 5;
548                     case current:
549                         return (tm_.tm_mday - 1) / 7 + 1;
550                     default:
551                         ;
552                     }
553                 default:
554                     ;
555                 }
556                 return 0;
557 
558             }
559 
560             ///
561             /// Set current time point
562             ///
set_time(posix_time const & p)563             virtual void set_time(posix_time const &p)
564             {
565                 from_time(static_cast<std::time_t>(p.seconds));
566             }
get_time() const567             virtual posix_time get_time() const
568             {
569                 posix_time pt = { time_, 0};
570                 return pt;
571             }
572 
573             ///
574             /// Set option for calendar, for future use
575             ///
set_option(calendar_option_type opt,int)576             virtual void set_option(calendar_option_type opt,int /*v*/)
577             {
578                 switch(opt) {
579                 case is_gregorian:
580                     throw date_time_error("is_gregorian is not settable options for calendar");
581                 case is_dst:
582                     throw date_time_error("is_dst is not settable options for calendar");
583                 default:
584                     ;
585                 }
586             }
587             ///
588             /// Get option for calendar, currently only check if it is Gregorian calendar
589             ///
get_option(calendar_option_type opt) const590             virtual int get_option(calendar_option_type opt) const
591             {
592                 switch(opt) {
593                 case is_gregorian:
594                     return 1;
595                 case is_dst:
596                     return tm_.tm_isdst == 1;
597                 default:
598                     return 0;
599                 };
600             }
601 
602             ///
603             /// Adjust period's \a p value by \a difference items using a update_type \a u.
604             /// Note: not all values are adjustable
605             ///
adjust_value(period::marks::period_mark p,update_type u,int difference)606             virtual void adjust_value(period::marks::period_mark p,update_type u,int difference)
607             {
608                 switch(u) {
609                 case move:
610                     {
611                         using namespace period::marks;
612                         switch(p) {
613                         case year:                       ///< Year, it is calendar specific
614                         case extended_year:              ///< Extended year for Gregorian/Julian calendars, where 1 BC == 0, 2 BC == -1.
615                             tm_updated_.tm_year +=difference;
616                             break;
617                         case month:
618                             tm_updated_.tm_mon +=difference;
619                             break;
620                         case day:
621                         case day_of_year:
622                         case day_of_week:                ///< Day of week, starting from Sunday, [1..7]
623                         case day_of_week_local: ///< Local day of week, for example in France Monday is 1, in US Sunday is 1, [1..7]
624                             tm_updated_.tm_mday +=difference;
625                             break;
626                         case hour:                       ///< 24 clock hour [0..23]
627                         case hour_12:                    ///< 12 clock hour [0..11]
628                             tm_updated_.tm_hour += difference;
629                             break;
630                         case am_pm:                      ///< am or pm marker, [0..1]
631                             tm_updated_.tm_hour += 12 * difference;
632                             break;
633                         case minute:                     ///< minute [0..59]
634                             tm_updated_.tm_min += difference;
635                             break;
636                         case second:
637                             tm_updated_.tm_sec += difference;
638                             break;
639                         case week_of_year:               ///< The week number in the year
640                         case week_of_month:              ///< The week number withing current month
641                         case day_of_week_in_month:       ///< Original number of the day of the week in month.
642                             tm_updated_.tm_mday +=difference * 7;
643                             break;
644                         default:
645                             ; // Not all values are adjustable
646                         }
647                         normalized_ = false;
648                         normalize();
649                     }
650                     break;
651                 case roll:
652                     { // roll
653                         int cur_min = get_value(p,actual_minimum);
654                         int cur_max = get_value(p,actual_maximum);
655                         int max_diff = cur_max - cur_min + 1;
656                         if(max_diff > 0) {
657                             int value = get_value(p,current);
658                             int addon = 0;
659                             if(difference < 0)
660                                 addon = ((-difference/max_diff) + 1) * max_diff;
661                             value = (value - cur_min + difference + addon) % max_diff + cur_min;
662                             set_value(p,value);
663                             normalize();
664                         }
665                     }
666                 default:
667                     ;
668                 }
669             }
670 
get_diff(period::marks::period_mark p,int diff,gregorian_calendar const * other) const671             int get_diff(period::marks::period_mark p,int diff,gregorian_calendar const *other) const
672             {
673                 if(diff == 0)
674                     return 0;
675                 hold_ptr<gregorian_calendar> self(clone());
676                 self->adjust_value(p,move,diff);
677                 if(diff > 0){
678                     if(self->time_ > other->time_)
679                         return diff - 1;
680                     else
681                         return diff;
682                 }
683                 else {
684                     if(self->time_ < other->time_)
685                         return diff + 1;
686                     else
687                         return diff;
688                 }
689              }
690 
691             ///
692             /// Calculate the difference between this calendar  and \a other in \a p units
693             ///
difference(abstract_calendar const * other_cal,period::marks::period_mark p) const694             virtual int difference(abstract_calendar const *other_cal,period::marks::period_mark p) const
695             {
696                 hold_ptr<gregorian_calendar> keeper;
697                 gregorian_calendar const *other = dynamic_cast<gregorian_calendar const *>(other_cal);
698                 if(!other) {
699                     keeper.reset(clone());
700                     keeper->set_time(other_cal->get_time());
701                     other = keeper.get();
702                 }
703 
704                 int factor = 1; // for weeks vs days handling
705 
706                 using namespace period::marks;
707                 switch(p) {
708                 case era:
709                     return 0;
710                 case year:
711                 case extended_year:
712                     {
713                         int diff = other->tm_.tm_year - tm_.tm_year;
714                         return get_diff(period::marks::year,diff,other);
715                     }
716                 case month:
717                     {
718                         int diff = 12 * (other->tm_.tm_year - tm_.tm_year)
719                                     + other->tm_.tm_mon - tm_.tm_mon;
720                         return get_diff(period::marks::month,diff,other);
721                     }
722                 case day_of_week_in_month:
723                 case week_of_month:
724                 case week_of_year:
725                     factor = 7;
726                     // fall
727                 case day:
728                 case day_of_year:
729                 case day_of_week:
730                 case day_of_week_local:
731                     {
732                         int diff = other->tm_.tm_yday - tm_.tm_yday;
733                         if(other->tm_.tm_year != tm_.tm_year) {
734                             diff += days_from_0(other->tm_.tm_year + 1900) -
735                                     days_from_0(tm_.tm_year + 1900);
736                         }
737                         return get_diff(period::marks::day,diff,other) / factor;
738                     }
739                 case am_pm:
740                     return static_cast<int>( (other->time_ - time_) / (3600*12) );
741                 case hour:
742                 case hour_12:
743                     return static_cast<int>( (other->time_ - time_) / 3600 );
744                 case minute:
745                     return static_cast<int>( (other->time_ - time_) / 60 );
746                 case second:
747                     return static_cast<int>( other->time_ - time_  );
748                 default:
749                     return 0;
750                 };
751             }
752 
753             ///
754             /// Set time zone, empty - use system
755             ///
set_timezone(std::string const & tz)756             virtual void set_timezone(std::string const &tz)
757             {
758                 if(tz.empty()) {
759                     is_local_ = true;
760                     tzoff_ = 0;
761                 }
762                 else {
763                     is_local_ = false;
764                     tzoff_ = parse_tz(tz);
765                 }
766                 from_time(time_);
767                 time_zone_name_ = tz;
768             }
get_timezone() const769             virtual std::string get_timezone() const
770             {
771                 return time_zone_name_;
772             }
773 
same(abstract_calendar const * other) const774             virtual bool same(abstract_calendar const *other) const
775             {
776                 gregorian_calendar const *gcal = dynamic_cast<gregorian_calendar const *>(other);
777                 if(!gcal)
778                     return false;
779                 return
780                     gcal->tzoff_ == tzoff_
781                     && gcal->is_local_ == is_local_
782                     && gcal->first_day_of_week_  == first_day_of_week_;
783             }
784 
~gregorian_calendar()785             virtual ~gregorian_calendar()
786             {
787             }
788 
789     private:
790 
from_time(std::time_t point)791         void from_time(std::time_t point)
792         {
793             std::time_t real_point = point + tzoff_;
794             std::tm *t = 0;
795             #ifdef BOOST_WINDOWS
796             // Windows uses TLS, thread safe
797             t = is_local_ ? localtime(&real_point) : gmtime(&real_point);
798             #else
799             std::tm tmp_tm;
800             t = is_local_ ? localtime_r(&real_point,&tmp_tm) : gmtime_r(&real_point,&tmp_tm);
801             #endif
802             if(!t) {
803                 throw date_time_error("boost::locale::gregorian_calendar: invalid time point");
804             }
805             tm_ = *t;
806             tm_updated_ = *t;
807             normalized_ = true;
808             time_ = point;
809         }
810         int first_day_of_week_;
811         std::time_t time_;
812         std::tm tm_;
813         std::tm tm_updated_;
814         bool normalized_;
815         bool is_local_;
816         int tzoff_;
817         std::string time_zone_name_;
818 
819     };
820 
create_gregorian_calendar(std::string const & terr)821     abstract_calendar *create_gregorian_calendar(std::string const &terr)
822     {
823         return new gregorian_calendar(terr);
824     }
825 
826     class gregorian_facet : public calendar_facet {
827     public:
gregorian_facet(std::string const & terr,size_t refs=0)828         gregorian_facet(std::string const &terr,size_t refs = 0) :
829             calendar_facet(refs),
830             terr_(terr)
831         {
832         }
create_calendar() const833         virtual abstract_calendar *create_calendar() const
834         {
835             return create_gregorian_calendar(terr_);
836         }
837     private:
838         std::string terr_;
839     };
840 
install_gregorian_calendar(std::locale const & in,std::string const & terr)841     std::locale install_gregorian_calendar(std::locale const &in,std::string const &terr)
842     {
843         return std::locale(in,new gregorian_facet(terr));
844     }
845 
846 
847 } // util
848 } // locale
849 } //boost
850 
851 
852 // vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
853