Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-15 09:58:33

0001 //
0002 // Copyright (c) 2019-2025 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
0003 //
0004 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0005 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 
0008 #ifndef BOOST_MYSQL_DATE_HPP
0009 #define BOOST_MYSQL_DATE_HPP
0010 
0011 #include <boost/mysql/days.hpp>
0012 
0013 #include <boost/mysql/detail/config.hpp>
0014 #include <boost/mysql/detail/datetime.hpp>
0015 
0016 #include <boost/assert.hpp>
0017 #include <boost/config.hpp>
0018 #include <boost/throw_exception.hpp>
0019 
0020 #include <chrono>
0021 #include <cstdint>
0022 #include <iosfwd>
0023 #include <stdexcept>
0024 
0025 namespace boost {
0026 namespace mysql {
0027 
0028 /**
0029  * \brief Type representing MySQL `DATE` data type.
0030  * \details
0031  * Represents a Gregorian date broken by its year, month and day components, without a time zone.
0032  * \n
0033  * This type is close to the protocol and should not be used as a vocabulary type.
0034  * Instead, cast it to a `std::chrono::time_point` by calling \ref as_time_point,
0035  * \ref get_time_point, \ref as_local_time_point or \ref get_local_time_point.
0036  * \n
0037  * Dates retrieved from MySQL don't include any time zone information. Determining the time zone
0038  * is left to the application. Thus, any time point obtained from this class should be
0039  * interpreted as a local time in an unspecified time zone, like `std::chrono::local_time`.
0040  * For compatibility with older compilers, \ref as_time_point and \ref get_time_point return
0041  * `system_clock` time points. These should be interpreted as local times rather
0042  * than UTC. Prefer using \ref as_local_time_point or \ref get_local_time_point
0043  * if your compiler supports them, as they provide more accurate semantics.
0044  * \n
0045  * As opposed to `time_point`, this type allows representing MySQL invalid and zero dates.
0046  * These values are allowed by MySQL but don't represent real dates.
0047  * \n
0048  * Note: using `std::chrono` time zone functionality under MSVC may cause memory leaks to be reported.
0049  * See <a href="https://github.com/microsoft/STL/issues/2047">this issue</a> for an explanation and
0050  * <a href="https://github.com/microsoft/STL/issues/2504">this other issue</a> for a workaround.
0051  */
0052 class date
0053 {
0054 public:
0055     /**
0056      * \brief A `std::chrono::time_point` that can represent any valid `date`.
0057      * \details
0058      * Time points used by this class are always local times, even if defined
0059      * to use the system clock.
0060      */
0061     using time_point = std::chrono::time_point<std::chrono::system_clock, days>;
0062 
0063     /**
0064      * \brief Constructs a zero date.
0065      * \details
0066      * Results in a date with all of its components set to zero.
0067      * The resulting object has `this->valid() == false`.
0068      *
0069      * \par Exception safety
0070      * No-throw guarantee.
0071      */
0072     constexpr date() noexcept = default;
0073 
0074     /**
0075      * \brief Constructs a date from its year, month and date components.
0076      * \details
0077      * Component values that yield invalid dates (like zero or out-of-range
0078      * values) are allowed, resulting in an object with `this->valid() == false`.
0079      *
0080      * \par Exception safety
0081      * No-throw guarantee.
0082      */
0083     constexpr date(std::uint16_t year, std::uint8_t month, std::uint8_t day) noexcept
0084         : year_(year), month_(month), day_(day)
0085     {
0086     }
0087 
0088     /**
0089      * \brief Constructs a date from a `time_point`.
0090      * \details
0091      * The time point is interpreted as a local time. No time zone conversion is performed.
0092      *
0093      * \par Exception safety
0094      * Strong guarantee. Throws on invalid input.
0095      * \throws std::out_of_range If the resulting `date` would be
0096      * out of the [\ref min_date, \ref max_date] range.
0097      */
0098     BOOST_CXX14_CONSTEXPR explicit date(time_point tp)
0099     {
0100         bool ok = detail::days_to_ymd(tp.time_since_epoch().count(), year_, month_, day_);
0101         if (!ok)
0102             BOOST_THROW_EXCEPTION(std::out_of_range("date::date: time_point was out of range"));
0103     }
0104 
0105 #ifdef BOOST_MYSQL_HAS_LOCAL_TIME
0106     /**
0107      * \brief Constructs a date from a local time point.
0108      * \details
0109      * Equivalent to constructing a `date` from a `time_point` with the same
0110      * `time_since_epoch()` as `tp`.
0111      * \n
0112      * Requires C++20 calendar types.
0113      *
0114      * \par Exception safety
0115      * Strong guarantee. Throws on invalid input.
0116      * \throws std::out_of_range If the resulting `date` would be
0117      * out of the [\ref min_date, \ref max_date] range.
0118      */
0119     constexpr explicit date(std::chrono::local_days tp) : date(time_point(tp.time_since_epoch())) {}
0120 #endif
0121 
0122     /**
0123      * \brief Retrieves the year component.
0124      * \details
0125      * Represents the year number in the Gregorian calendar.
0126      * If `this->valid() == true`, this value is within the `[0, 9999]` range.
0127      *
0128      * \par Exception safety
0129      * No-throw guarantee.
0130      */
0131     constexpr std::uint16_t year() const noexcept { return year_; }
0132 
0133     /**
0134      * \brief Retrieves the month component (1-based).
0135      * \details
0136      * A value of 1 represents January.
0137      * If `this->valid() == true`, this value is within the `[1, 12]` range.
0138      *
0139      * \par Exception safety
0140      * No-throw guarantee.
0141      */
0142     constexpr std::uint8_t month() const noexcept { return month_; }
0143 
0144     /**
0145      * \brief Retrieves the day component (1-based).
0146      * \details
0147      * A value of 1 represents the first day of the month.
0148      * If `this->valid() == true`, this value is within the `[1, last_month_day]` range
0149      * (where `last_month_day` is the last day of the month).
0150      *
0151      * \par Exception safety
0152      * No-throw guarantee.
0153      */
0154     constexpr std::uint8_t day() const noexcept { return day_; }
0155 
0156     /**
0157      * \brief Returns `true` if `*this` represents a valid `time_point`.
0158      * \details If any of the individual components is out of range, the date
0159      * doesn't represent an actual `time_point` (e.g. `date(2020, 2, 30)`) or
0160      * the date is not in the [\ref min_date, \ref max_date] validity range,
0161      * returns `false`. Otherwise, returns `true`.
0162      * \par Exception safety
0163      * No-throw guarantee.
0164      */
0165     constexpr bool valid() const noexcept { return detail::is_valid(year_, month_, day_); }
0166 
0167     /**
0168      * \brief Converts `*this` into a `time_point` (unchecked access).
0169      * \details
0170      * If your compiler supports it, prefer using \ref get_local_time_point,
0171      * as it provides more accurate semantics.
0172      *
0173      * \par Preconditions
0174      * `this->valid() == true` (if violated, results in undefined behavior).
0175      *
0176      * \par Exception safety
0177      * No-throw guarantee.
0178      */
0179     BOOST_CXX14_CONSTEXPR time_point get_time_point() const noexcept
0180     {
0181         BOOST_ASSERT(valid());
0182         return time_point(unch_get_days());
0183     }
0184 
0185     /**
0186      * \brief Converts `*this` into a `time_point` (checked access).
0187      * \details
0188      * If your compiler supports it, prefer using \ref as_local_time_point,
0189      * as it provides more accurate semantics.
0190      *
0191      * \par Exception safety
0192      * Strong guarantee.
0193      * \throws std::invalid_argument If `!this->valid()`.
0194      */
0195     BOOST_CXX14_CONSTEXPR time_point as_time_point() const
0196     {
0197         if (!valid())
0198             BOOST_THROW_EXCEPTION(std::invalid_argument("date::as_time_point: invalid date"));
0199         return time_point(unch_get_days());
0200     }
0201 
0202 #ifdef BOOST_MYSQL_HAS_LOCAL_TIME
0203     /**
0204      * \brief Converts `*this` into a local time point (unchecked access).
0205      * \details
0206      * The returned object has the same `time_since_epoch()` as `this->get_time_point()`,
0207      * but uses the `std::chrono::local_t` pseudo-clock to better represent
0208      * the absence of time zone information.
0209      * \n
0210      * Requires C++20 calendar types.
0211      *
0212      * \par Preconditions
0213      * `this->valid() == true` (if violated, results in undefined behavior).
0214      *
0215      * \par Exception safety
0216      * No-throw guarantee.
0217      */
0218     constexpr std::chrono::local_days get_local_time_point() const noexcept
0219     {
0220         BOOST_ASSERT(valid());
0221         return std::chrono::local_days(unch_get_days());
0222     }
0223 
0224     /**
0225      * \brief Converts `*this` into a local time point (checked access).
0226      * \details
0227      * The returned object has the same `time_since_epoch()` as `this->as_time_point()`,
0228      * but uses the `std::chrono::local_t` pseudo-clock to better represent
0229      * the absence of time zone information.
0230      * \n
0231      * Requires C++20 calendar types.
0232      *
0233      * \par Exception safety
0234      * Strong guarantee.
0235      * \throws std::invalid_argument If `!this->valid()`.
0236      */
0237     constexpr std::chrono::local_days as_local_time_point() const
0238     {
0239         if (!valid())
0240             BOOST_THROW_EXCEPTION(std::invalid_argument("date::as_local_time_point: invalid date"));
0241         return std::chrono::local_days(unch_get_days());
0242     }
0243 #endif
0244 
0245     /**
0246      * \brief Tests for equality.
0247      * \details Two dates are considered equal if all of its individual components
0248      * are equal. This function works for invalid dates, too.
0249      *
0250      * \par Exception safety
0251      * No-throw guarantee.
0252      */
0253     constexpr bool operator==(const date& rhs) const noexcept
0254     {
0255         return year_ == rhs.year_ && month_ == rhs.month_ && day_ == rhs.day_;
0256     }
0257 
0258     /**
0259      * \brief Tests for inequality.
0260      *
0261      * \par Exception safety
0262      * No-throw guarantee.
0263      */
0264     constexpr bool operator!=(const date& rhs) const noexcept { return !(rhs == *this); }
0265 
0266     /**
0267      * \brief Returns the current system time as a date object.
0268      * \par Exception safety
0269      * Strong guarantee. Only throws if obtaining the current time throws.
0270      */
0271     static date now()
0272     {
0273         auto now = time_point::clock::now();
0274         return date(std::chrono::time_point_cast<time_point::duration>(now));
0275     }
0276 
0277 private:
0278     std::uint16_t year_{};
0279     std::uint8_t month_{};
0280     std::uint8_t day_{};
0281 
0282     BOOST_CXX14_CONSTEXPR days unch_get_days() const
0283     {
0284         return days(detail::ymd_to_days(year_, month_, day_));
0285     }
0286 };
0287 
0288 /**
0289  * \relates date
0290  * \brief Streams a date.
0291  * \details This function works for invalid dates, too.
0292  */
0293 BOOST_MYSQL_DECL
0294 std::ostream& operator<<(std::ostream& os, const date& v);
0295 
0296 /// The minimum allowed value for \ref date.
0297 BOOST_INLINE_CONSTEXPR date min_date{0u, 1u, 1u};
0298 
0299 /// The maximum allowed value for \ref date.
0300 BOOST_INLINE_CONSTEXPR date max_date{9999u, 12u, 31u};
0301 
0302 }  // namespace mysql
0303 }  // namespace boost
0304 
0305 #ifdef BOOST_MYSQL_HEADER_ONLY
0306 #include <boost/mysql/impl/date.ipp>
0307 #endif
0308 
0309 #endif