summaryrefslogtreecommitdiffstats
path: root/contrib/restricted/boost/locale/src/util/gregorian.cpp
diff options
context:
space:
mode:
authorthegeorg <[email protected]>2025-05-07 11:09:12 +0300
committerthegeorg <[email protected]>2025-05-07 11:25:22 +0300
commit4c98f14a2491da2bc1a9d40fed1d1682c7ec5dd6 (patch)
tree8142081775c27c8780fa0282a0273ee268e13b98 /contrib/restricted/boost/locale/src/util/gregorian.cpp
parent40b86c68b431bb65d67bba51ef9159066a833b68 (diff)
Update contrib/restricted/boost/locale to 1.88.0
commit_hash:87b595328ea79c6ba38cb973580804486ca4a3ff
Diffstat (limited to 'contrib/restricted/boost/locale/src/util/gregorian.cpp')
-rw-r--r--contrib/restricted/boost/locale/src/util/gregorian.cpp710
1 files changed, 710 insertions, 0 deletions
diff --git a/contrib/restricted/boost/locale/src/util/gregorian.cpp b/contrib/restricted/boost/locale/src/util/gregorian.cpp
new file mode 100644
index 00000000000..3183d4ae6cb
--- /dev/null
+++ b/contrib/restricted/boost/locale/src/util/gregorian.cpp
@@ -0,0 +1,710 @@
+//
+// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
+//
+// Distributed under the Boost Software License, Version 1.0.
+// https://www.boost.org/LICENSE_1_0.txt
+
+#include "gregorian.hpp"
+#include <boost/locale/date_time.hpp>
+#include <boost/locale/date_time_facet.hpp>
+#include <boost/locale/hold_ptr.hpp>
+#include "timezone.hpp"
+#include <boost/assert.hpp>
+#include <algorithm>
+#include <cstdlib>
+#include <ctime>
+#include <ios>
+#include <limits>
+#include <locale>
+#include <memory>
+#include <string>
+
+#define BOOST_LOCALE_CASE_INVALID(action) \
+ BOOST_ASSERT_MSG(false, "Shouldn't use 'invalid' value."); \
+ action
+
+namespace boost { namespace locale { namespace util {
+ namespace {
+
+ bool is_leap(int year)
+ {
+ if(year % 400 == 0)
+ return true;
+ if(year % 100 == 0)
+ return false;
+ if(year % 4 == 0)
+ return true;
+ return false;
+ }
+
+ int days_in_month(int year, int month)
+ {
+ constexpr int tbl[2][12] = {{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
+ return tbl[is_leap(year)][month - 1];
+ }
+
+ constexpr int days_from_0_impl(int year_m1)
+ {
+ return 365 * year_m1 + (year_m1 / 400) - (year_m1 / 100) + (year_m1 / 4);
+ }
+ constexpr int days_from_0(int year)
+ {
+ return days_from_0_impl(year - 1);
+ }
+
+ constexpr int days_from_0_to_1970 = days_from_0(1970);
+ constexpr int days_from_1970(int year)
+ {
+ return days_from_0(year) - days_from_0_to_1970;
+ }
+
+ int days_from_1jan(int year, int month, int day)
+ {
+ constexpr int days[2][12] = {{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
+ {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}};
+ return days[is_leap(year)][month - 1] + day - 1;
+ }
+
+ std::time_t internal_timegm(const std::tm* t)
+ {
+ int year = t->tm_year + 1900;
+ int month = t->tm_mon;
+ if(month > 11) {
+ year += month / 12;
+ month %= 12;
+ } else if(month < 0) {
+ int years_diff = (-month + 11) / 12;
+ year -= years_diff;
+ month += 12 * years_diff;
+ }
+ month++;
+ int day = t->tm_mday;
+ int day_of_year = days_from_1jan(year, month, day);
+ int days_since_epoch = days_from_1970(year) + day_of_year;
+
+ std::time_t seconds_in_day = 3600 * 24;
+ std::time_t result = seconds_in_day * days_since_epoch + 3600 * t->tm_hour + 60 * t->tm_min + t->tm_sec;
+
+ return result;
+ }
+
+ } // namespace
+
+ namespace {
+
+ // Locale dependent data
+
+ bool comparator(const char* left, const char* right)
+ {
+ return strcmp(left, right) < 0;
+ }
+
+ // Ref: CLDR 1.9 common/supplemental/supplementalData.xml
+ //
+ // monday - default
+ // fri - MV
+ // sat - AE AF BH DJ DZ EG ER ET IQ IR JO KE KW LY MA OM QA SA SD SO SY TN YE
+ // 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
+
+ int first_day_of_week(const char* terr)
+ {
+ constexpr const char* sat[] = {"AE", "AF", "BH", "DJ", "DZ", "EG", "ER", "ET", "IQ", "IR", "JO", "KE",
+ "KW", "LY", "MA", "OM", "QA", "SA", "SD", "SO", "SY", "TN", "YE"};
+ constexpr const char* sunday[] = {"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"};
+ if(strcmp(terr, "MV") == 0)
+ return 5; // fri
+ if(std::binary_search<const char* const*>(sat, std::end(sat), terr, comparator))
+ return 6; // sat
+ if(std::binary_search<const char* const*>(sunday, std::end(sunday), terr, comparator))
+ return 0; // sun
+ // default
+ return 1; // mon
+ }
+ } // namespace
+
+ class gregorian_calendar : public abstract_calendar {
+ public:
+ gregorian_calendar(const std::string& terr)
+ {
+ first_day_of_week_ = first_day_of_week(terr.c_str());
+ time_ = std::time(nullptr);
+ is_local_ = true;
+ tzoff_ = 0;
+ from_time(time_);
+ }
+
+ /// Make a polymorphic copy of the calendar
+ gregorian_calendar* clone() const override { return new gregorian_calendar(*this); }
+
+ /// Set specific \a value for period \a p, note not all values are settable.
+ void set_value(period::marks::period_mark m, int value) override
+ {
+ using namespace period::marks;
+ switch(m) {
+ case era: ///< Era i.e. AC, BC in Gregorian and Julian calendar, range [0,1]
+ return;
+ case year: ///< Year, it is calendar specific
+ case extended_year: ///< Extended year for Gregorian/Julian calendars, where 1 BC == 0, 2 BC == -1.
+ tm_updated_.tm_year = value - 1900;
+ break;
+ case month: tm_updated_.tm_mon = value; break;
+ case day: tm_updated_.tm_mday = value; break;
+ case hour: ///< 24 clock hour [0..23]
+ tm_updated_.tm_hour = value;
+ break;
+ case hour_12: ///< 12 clock hour [0..11]
+ tm_updated_.tm_hour = tm_updated_.tm_hour / 12 * 12 + value;
+ break;
+ case am_pm: ///< am or pm marker, [0..1]
+ tm_updated_.tm_hour = 12 * value + tm_updated_.tm_hour % 12;
+ break;
+ case minute: ///< minute [0..59]
+ tm_updated_.tm_min = value;
+ break;
+ case second: tm_updated_.tm_sec = value; break;
+ case day_of_year:
+ normalize();
+ tm_updated_.tm_mday += (value - (tm_updated_.tm_yday + 1));
+ break;
+ case day_of_week: ///< Day of week, starting from Sunday, [1..7]
+ if(value < 1) // make sure it is positive
+ value += (-value / 7) * 7 + 7;
+ // convert to local DOW
+ value = (value - 1 - first_day_of_week_ + 14) % 7 + 1;
+ BOOST_FALLTHROUGH;
+ case day_of_week_local: ///< Local day of week, for example in France Monday is 1, in US Sunday is 1,
+ ///< [1..7]
+ normalize();
+ tm_updated_.tm_mday += (value - 1) - (tm_updated_.tm_wday - first_day_of_week_ + 7) % 7;
+ break;
+ case day_of_week_in_month: ///< Original number of the day of the week in month. (1st sunday, 2nd sunday
+ ///< etc)
+ case week_of_year: ///< The week number in the year, 4 is the minimal number of days to be in month
+ case week_of_month: ///< The week number within current month
+ {
+ normalize();
+ int current_week = get_value(m, current);
+ int diff = 7 * (value - current_week);
+ tm_updated_.tm_mday += diff;
+ } break;
+ ///< For example Sunday in US, Monday in France
+ case period::marks::first_day_of_week: // LCOV_EXCL_LINE
+ throw std::invalid_argument("Can't change first day of week"); // LCOV_EXCL_LINE
+ case invalid: BOOST_LOCALE_CASE_INVALID(return ); // LCOV_EXCL_LINE
+ }
+ normalized_ = false;
+ }
+
+ void normalize() override
+ {
+ if(!normalized_) {
+ std::tm val = tm_updated_;
+ val.tm_isdst = -1;
+ val.tm_wday = -1; // indicator of error
+ std::time_t point = -1;
+ if(is_local_) {
+ point = std::mktime(&val);
+ if(point == static_cast<std::time_t>(-1)) {
+#ifndef BOOST_WINDOWS
+ // windows does not handle negative time_t, under other plaforms
+ // it may be actually valid value in 1969-12-31 23:59:59
+ // so we check that a field was updated - does not happen in case of error
+ if(val.tm_wday == -1)
+#endif
+ throw date_time_error("boost::locale::gregorian_calendar: invalid time"); // LCOV_EXCL_LINE
+ }
+ } else {
+ point = internal_timegm(&val);
+#ifdef BOOST_WINDOWS
+ // Windows uses TLS, thread safe
+ std::tm* revert_point = nullptr;
+ if(point < 0 || (revert_point = gmtime(&point)) == nullptr)
+ throw date_time_error("boost::locale::gregorian_calendar time is out of range");
+ val = *revert_point;
+#else
+ if(!gmtime_r(&point, &val))
+ throw date_time_error("boost::locale::gregorian_calendar invalid time");
+#endif
+ }
+
+ time_ = point - tzoff_;
+ tm_ = val;
+ tm_updated_ = val;
+ normalized_ = true;
+ }
+ }
+
+ int get_week_number(int day, int wday) const
+ {
+ /// This is the number of days that are considered within
+ /// period such that the week belongs there
+ constexpr int days_in_full_week = 4;
+
+ // Always use local week start
+ int current_dow = (wday - first_day_of_week_ + 7) % 7;
+ // Calculate local week day of Jan 1st.
+ int first_week_day = (current_dow + 700 - day) % 7;
+ // adding something big dividable by 7
+
+ int start_of_period_in_weeks;
+ if(first_week_day < days_in_full_week)
+ start_of_period_in_weeks = -first_week_day;
+ else
+ start_of_period_in_weeks = 7 - first_week_day;
+ int week_number_in_days = day - start_of_period_in_weeks;
+ if(week_number_in_days < 0)
+ return -1;
+ return week_number_in_days / 7 + 1;
+ }
+
+ /// Get specific value for period \a p according to a value_type \a v
+ int get_value(period::marks::period_mark m, value_type v) const override
+ {
+ using namespace period::marks;
+ switch(m) {
+ case era: return 1;
+ case year:
+ case extended_year:
+ switch(v) {
+ case absolute_minimum:
+ case greatest_minimum:
+ case actual_minimum:
+#ifdef BOOST_WINDOWS
+ return 1970; // Unix epoch windows can't handle negative time_t
+#else
+ if(sizeof(std::time_t) == 4)
+ return 1901; // minimal year with 32 bit time_t
+ else
+ return 1;
+#endif
+ case absolute_maximum:
+ case least_maximum:
+ case actual_maximum:
+ BOOST_LOCALE_START_CONST_CONDITION
+ if(sizeof(std::time_t) == 4)
+ return 2038; // Y2K38 - maximal with 32 bit time_t
+ else
+ return std::numeric_limits<int>::max()
+ / 365; // Reasonably large but we can still get the days without overflowing
+ BOOST_LOCALE_END_CONST_CONDITION
+ case current: return tm_.tm_year + 1900;
+ };
+ throw std::invalid_argument("Invalid abstract_calendar::value_type"); // LCOV_EXCL_LINE
+ case month:
+ switch(v) {
+ case absolute_minimum:
+ case greatest_minimum:
+ case actual_minimum: return 0;
+ case absolute_maximum:
+ case least_maximum:
+ case actual_maximum: return 11;
+ case current: return tm_.tm_mon;
+ };
+ throw std::invalid_argument("Invalid abstract_calendar::value_type"); // LCOV_EXCL_LINE
+ case day:
+ switch(v) {
+ case absolute_minimum:
+ case greatest_minimum:
+ case actual_minimum: return 1;
+ case absolute_maximum: return 31;
+ case least_maximum: return 28;
+ case actual_maximum: return days_in_month(tm_.tm_year + 1900, tm_.tm_mon + 1);
+ case current: return tm_.tm_mday;
+ };
+ throw std::invalid_argument("Invalid abstract_calendar::value_type"); // LCOV_EXCL_LINE
+ case day_of_year: ///< The number of day in year, starting from 1
+ switch(v) {
+ case absolute_minimum:
+ case greatest_minimum:
+ case actual_minimum: return 1;
+ case absolute_maximum: return 366;
+ case least_maximum: return 365;
+ case actual_maximum: return is_leap(tm_.tm_year + 1900) ? 366 : 365;
+ case current: return tm_.tm_yday + 1;
+ }
+ throw std::invalid_argument("Invalid abstract_calendar::value_type"); // LCOV_EXCL_LINE
+ case day_of_week: ///< Day of week, starting from Sunday, [1..7]
+ switch(v) {
+ case absolute_minimum:
+ case greatest_minimum:
+ case actual_minimum: return 1;
+ case absolute_maximum:
+ case least_maximum:
+ case actual_maximum: return 7;
+ case current: return tm_.tm_wday + 1;
+ }
+ throw std::invalid_argument("Invalid abstract_calendar::value_type"); // LCOV_EXCL_LINE
+ case day_of_week_local: ///< Local day of week, for example in France Monday is 1, in US Sunday is 1,
+ ///< [1..7]
+ switch(v) {
+ case absolute_minimum:
+ case greatest_minimum:
+ case actual_minimum: return 1;
+ case absolute_maximum:
+ case least_maximum:
+ case actual_maximum: return 7;
+ case current: return (tm_.tm_wday - first_day_of_week_ + 7) % 7 + 1;
+ }
+ throw std::invalid_argument("Invalid abstract_calendar::value_type"); // LCOV_EXCL_LINE
+ case hour: ///< 24 clock hour [0..23]
+ switch(v) {
+ case absolute_minimum:
+ case greatest_minimum:
+ case actual_minimum: return 0;
+ case absolute_maximum:
+ case least_maximum:
+ case actual_maximum: return 23;
+ case current: return tm_.tm_hour;
+ }
+ throw std::invalid_argument("Invalid abstract_calendar::value_type"); // LCOV_EXCL_LINE
+ case hour_12: ///< 12 clock hour [0..11]
+ switch(v) {
+ case absolute_minimum:
+ case greatest_minimum:
+ case actual_minimum: return 0;
+ case absolute_maximum:
+ case least_maximum:
+ case actual_maximum: return 11;
+ case current: return tm_.tm_hour % 12;
+ }
+ throw std::invalid_argument("Invalid abstract_calendar::value_type"); // LCOV_EXCL_LINE
+ case am_pm: ///< am or pm marker, [0..1]
+ switch(v) {
+ case absolute_minimum:
+ case greatest_minimum:
+ case actual_minimum: return 0;
+ case absolute_maximum:
+ case least_maximum:
+ case actual_maximum: return 1;
+ case current: return tm_.tm_hour >= 12 ? 1 : 0;
+ }
+ throw std::invalid_argument("Invalid abstract_calendar::value_type"); // LCOV_EXCL_LINE
+ case minute: ///< minute [0..59]
+ switch(v) {
+ case absolute_minimum:
+ case greatest_minimum:
+ case actual_minimum: return 0;
+ case absolute_maximum:
+ case least_maximum:
+ case actual_maximum: return 59;
+ case current: return tm_.tm_min;
+ }
+ throw std::invalid_argument("Invalid abstract_calendar::value_type"); // LCOV_EXCL_LINE
+ case second: ///< second [0..59]
+ switch(v) {
+ case absolute_minimum:
+ case greatest_minimum:
+ case actual_minimum: return 0;
+ case absolute_maximum:
+ case least_maximum:
+ case actual_maximum: return 59;
+ case current: return tm_.tm_sec;
+ }
+ throw std::invalid_argument("Invalid abstract_calendar::value_type"); // LCOV_EXCL_LINE
+ case period::marks::first_day_of_week: ///< For example Sunday in US, Monday in France
+ return first_day_of_week_ + 1;
+
+ case week_of_year: ///< The week number in the year
+ switch(v) {
+ case absolute_minimum:
+ case greatest_minimum:
+ case actual_minimum: return 1;
+ case absolute_maximum: return 53;
+ case least_maximum: return 52;
+ case actual_maximum: {
+ int year = tm_.tm_year + 1900;
+ int end_of_year_days = (is_leap(year) ? 366 : 365) - 1;
+ int dow_of_end_of_year = (end_of_year_days - tm_.tm_yday + tm_.tm_wday) % 7;
+ return get_week_number(end_of_year_days, dow_of_end_of_year);
+ }
+ case current: {
+ const int val = get_week_number(tm_.tm_yday, tm_.tm_wday);
+ return (val < 0) ? 53 : val;
+ }
+ } // LCOV_EXCL_LINE
+ throw std::invalid_argument("Invalid abstract_calendar::value_type"); // LCOV_EXCL_LINE
+ case week_of_month: ///< The week number within current month
+ switch(v) {
+ case absolute_minimum:
+ case greatest_minimum:
+ case actual_minimum: return 1;
+ case absolute_maximum: return 5;
+ case least_maximum: return 4;
+ case actual_maximum: {
+ int end_of_month_days = days_in_month(tm_.tm_year + 1900, tm_.tm_mon + 1);
+ int dow_of_end_of_month = (end_of_month_days - tm_.tm_mday + tm_.tm_wday) % 7;
+ return get_week_number(end_of_month_days, dow_of_end_of_month);
+ }
+ case current: {
+ const int val = get_week_number(tm_.tm_mday, tm_.tm_wday);
+ return (val < 0) ? 5 : val;
+ }
+ } // LCOV_EXCL_LINE
+ throw std::invalid_argument("Invalid abstract_calendar::value_type"); // LCOV_EXCL_LINE
+ case day_of_week_in_month: ///< Original number of the day of the week in month.
+ switch(v) {
+ case absolute_minimum:
+ case greatest_minimum:
+ case actual_minimum: return 1;
+ case absolute_maximum: return 5;
+ case least_maximum: return 4;
+ case actual_maximum:
+ if(tm_.tm_mon == 1 && !is_leap(tm_.tm_year + 1900)) {
+ // only february in non leap year is 28 days, the rest
+ // has more then 4 weeks
+ return 4;
+ }
+ return 5;
+ case current: return (tm_.tm_mday - 1) / 7 + 1;
+ }
+ throw std::invalid_argument("Invalid abstract_calendar::value_type"); // LCOV_EXCL_LINE
+ case invalid: BOOST_LOCALE_CASE_INVALID(break); // LCOV_EXCL_LINE
+ }
+ throw std::invalid_argument("Invalid period_mark"); // LCOV_EXCL_LINE
+ }
+
+ /// Set current time point
+ void set_time(const posix_time& p) override
+ {
+ from_time(static_cast<std::time_t>(p.seconds));
+ }
+ posix_time get_time() const override
+ {
+ posix_time pt = {time_, 0};
+ return pt;
+ }
+ double get_time_ms() const override
+ {
+ return time_ * 1e3;
+ }
+
+ /// Set option for calendar, for future use
+ void set_option(calendar_option_type opt, int /*v*/) override
+ {
+ switch(opt) {
+ case is_gregorian: throw date_time_error("is_gregorian is not settable options for calendar");
+ case is_dst: throw date_time_error("is_dst is not settable options for calendar");
+ }
+ throw std::invalid_argument("Invalid option type"); // LCOV_EXCL_LINE
+ }
+ /// Get option for calendar, currently only check if it is Gregorian calendar
+ int get_option(calendar_option_type opt) const override
+ {
+ switch(opt) {
+ case is_gregorian: return 1;
+ case is_dst: return tm_.tm_isdst == 1;
+ }
+ throw std::invalid_argument("Invalid option type"); // LCOV_EXCL_LINE
+ }
+
+ /// Adjust period's \a p value by \a difference items using a update_type \a u.
+ /// Note: not all values are adjustable
+ void adjust_value(period::marks::period_mark m, update_type u, int difference) override
+ {
+ if(u == move) {
+ using namespace period::marks;
+ switch(m) {
+ case year: ///< Year, it is calendar specific
+ case extended_year: ///< Extended year for Gregorian/Julian calendars, where 1 BC == 0, 2 BC == -1
+ tm_updated_.tm_year += difference;
+ break;
+ case month: tm_updated_.tm_mon += difference; break;
+ case day:
+ case day_of_year:
+ case day_of_week: ///< starting from Sunday, [1..7]
+ case day_of_week_local: ///< Local DoW, e.g. in France Monday=1, in US Sunday=1, [1..7]
+ tm_updated_.tm_mday += difference;
+ break;
+ case hour: ///< 24 clock hour [0..23]
+ case hour_12: ///< 12 clock hour [0..11]
+ tm_updated_.tm_hour += difference;
+ break;
+ case am_pm: ///< am or pm marker, [0..1]
+ tm_updated_.tm_hour += 12 * difference;
+ break;
+ case minute: ///< minute [0..59]
+ tm_updated_.tm_min += difference;
+ break;
+ case second: tm_updated_.tm_sec += difference; break;
+ case week_of_year: ///< The week number in the year
+ case week_of_month: ///< The week number within current month
+ case day_of_week_in_month: ///< Original number of the day of the week in month.
+ tm_updated_.tm_mday += difference * 7;
+ break;
+ case era: throw std::invalid_argument("era not adjustable"); // LCOV_EXCL_LINE
+ case period::marks::first_day_of_week: // LCOV_EXCL_LINE
+ throw std::invalid_argument("Can't change first day of week"); // LCOV_EXCL_LINE
+ case invalid: BOOST_LOCALE_CASE_INVALID(return ); // LCOV_EXCL_LINE
+ }
+ normalized_ = false;
+ normalize();
+ } else {
+ BOOST_ASSERT(u == roll);
+ const int cur_min = get_value(m, actual_minimum);
+ const int cur_max = get_value(m, actual_maximum);
+ BOOST_ASSERT(cur_max >= cur_min);
+ const int range = cur_max - cur_min + 1;
+ int value = get_value(m, current) - cur_min;
+ BOOST_ASSERT(value >= 0 && value < range);
+ BOOST_ASSERT_MSG(difference <= std::numeric_limits<int>::max(), "Input is to large");
+ value = (value + difference) % range;
+ // If the sum above was negative the result of the modulo operation "can" be negative too.
+ if(value < 0)
+ value += range;
+ BOOST_ASSERT(value >= 0 && value < range);
+ set_value(m, value + cur_min);
+ normalize();
+ }
+ }
+
+ int get_diff(period::marks::period_mark m, int diff, const gregorian_calendar* other) const
+ {
+ if(diff == 0)
+ return 0;
+ hold_ptr<gregorian_calendar> self(clone());
+ self->adjust_value(m, move, diff);
+ if(diff > 0) {
+ if(self->time_ > other->time_)
+ return diff - 1;
+ else
+ return diff;
+ } else {
+ if(self->time_ < other->time_)
+ return diff + 1;
+ else
+ return diff;
+ }
+ }
+
+ /// Calculate the difference between this calendar and \a other in \a p units
+ int difference(const abstract_calendar& other_cal, period::marks::period_mark m) const override
+ {
+ hold_ptr<gregorian_calendar> keeper;
+ const gregorian_calendar* other = dynamic_cast<const gregorian_calendar*>(&other_cal);
+ if(!other) {
+ keeper.reset(clone());
+ keeper->set_time(other_cal.get_time());
+ other = keeper.get();
+ }
+
+ int factor = 1; // for weeks vs days handling
+
+ using namespace period::marks;
+ switch(m) {
+ case era: return 0;
+ case year:
+ case extended_year: {
+ int diff = other->tm_.tm_year - tm_.tm_year;
+ return get_diff(period::marks::year, diff, other);
+ }
+ case month: {
+ int diff = 12 * (other->tm_.tm_year - tm_.tm_year) + other->tm_.tm_mon - tm_.tm_mon;
+ return get_diff(period::marks::month, diff, other);
+ }
+ case day_of_week_in_month:
+ case week_of_month:
+ case week_of_year: factor = 7; BOOST_FALLTHROUGH;
+ case day:
+ case day_of_year:
+ case day_of_week:
+ case day_of_week_local: {
+ int diff = other->tm_.tm_yday - tm_.tm_yday;
+ if(other->tm_.tm_year != tm_.tm_year)
+ diff += days_from_0(other->tm_.tm_year + 1900) - days_from_0(tm_.tm_year + 1900);
+ return get_diff(period::marks::day, diff, other) / factor;
+ }
+ case am_pm: return static_cast<int>((other->time_ - time_) / (3600 * 12));
+ case hour:
+ case hour_12: return static_cast<int>((other->time_ - time_) / 3600);
+ case minute: return static_cast<int>((other->time_ - time_) / 60);
+ case second: return static_cast<int>(other->time_ - time_);
+ case invalid: // LCOV_EXCL_LINE
+ BOOST_LOCALE_CASE_INVALID(return 0); // LCOV_EXCL_LINE
+ // Not adjustable
+ case period::marks::first_day_of_week: return 0; // LCOV_EXCL_LINE
+ }
+ throw std::invalid_argument("Invalid period_mark"); // LCOV_EXCL_LINE
+ }
+
+ /// Set time zone, empty - use system
+ void set_timezone(const std::string& tz) override
+ {
+ if(tz.empty()) {
+ is_local_ = true;
+ tzoff_ = 0;
+ } else {
+ is_local_ = false;
+ tzoff_ = parse_tz(tz);
+ }
+ from_time(time_);
+ time_zone_name_ = tz;
+ }
+ std::string get_timezone() const override
+ {
+ return time_zone_name_;
+ }
+
+ bool same(const abstract_calendar* other) const override
+ {
+ const gregorian_calendar* gcal = dynamic_cast<const gregorian_calendar*>(other);
+ if(!gcal)
+ return false;
+ return gcal->tzoff_ == tzoff_ && gcal->is_local_ == is_local_
+ && gcal->first_day_of_week_ == first_day_of_week_;
+ }
+
+ private:
+ void from_time(std::time_t point)
+ {
+ std::time_t real_point = point + tzoff_;
+ std::tm* t;
+#ifdef BOOST_WINDOWS
+ // Windows uses TLS, thread safe
+ t = is_local_ ? localtime(&real_point) : gmtime(&real_point);
+#else
+ std::tm tmp_tm;
+ t = is_local_ ? localtime_r(&real_point, &tmp_tm) : gmtime_r(&real_point, &tmp_tm);
+#endif
+ if(!t)
+ throw date_time_error("boost::locale::gregorian_calendar: invalid time point");
+ tm_ = *t;
+ tm_updated_ = *t;
+ normalized_ = true;
+ time_ = point;
+ }
+ int first_day_of_week_;
+ std::time_t time_;
+ std::tm tm_;
+ std::tm tm_updated_;
+ bool normalized_;
+ bool is_local_;
+ int tzoff_;
+ std::string time_zone_name_;
+ };
+
+ abstract_calendar* create_gregorian_calendar(const std::string& terr)
+ {
+ return new gregorian_calendar(terr);
+ }
+
+ class gregorian_facet : public calendar_facet {
+ public:
+ gregorian_facet(const std::string& terr, size_t refs = 0) : calendar_facet(refs), terr_(terr) {}
+ abstract_calendar* create_calendar() const override { return create_gregorian_calendar(terr_); }
+
+ private:
+ std::string terr_;
+ };
+
+ std::locale install_gregorian_calendar(const std::locale& in, const std::string& terr)
+ {
+ return std::locale(in, new gregorian_facet(terr));
+ }
+
+}}} // namespace boost::locale::util
+
+// boostinspect:nominmax