|
|
|||
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_DATETIME_HPP 0009 #define BOOST_MYSQL_DATETIME_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 <ratio> 0024 #include <stdexcept> 0025 0026 namespace boost { 0027 namespace mysql { 0028 0029 /** 0030 * \brief Type representing MySQL `DATETIME` and `TIMESTAMP` data types. 0031 * \details Represents a Gregorian date and time broken by its year, month, day, hour, minute, second and 0032 * microsecond components, without a time zone. 0033 * \n 0034 * This type is close to the protocol and should not be used as a vocabulary type. 0035 * Instead, cast it to a `std::chrono::time_point` by calling \ref as_time_point, 0036 * \ref get_time_point, \ref as_local_time_point or \ref get_local_time_point. 0037 * \n 0038 * Datetimes retrieved from MySQL don't include any time zone information. Determining the time zone 0039 * is left to the application. Thus, any time point obtained from this class should be 0040 * interpreted as a local time in an unspecified time zone, like `std::chrono::local_time`. 0041 * For compatibility with older compilers, \ref as_time_point and \ref get_time_point return 0042 * `system_clock` time points. These should be interpreted as local times rather 0043 * than UTC. Prefer using \ref as_local_time_point or \ref get_local_time_point 0044 * if your compiler supports them, as they provide more accurate semantics. 0045 * \n 0046 * As opposed to `time_point`, this type allows representing MySQL invalid and zero datetimes. 0047 * These values are allowed by MySQL but don't represent real time points. 0048 * \n 0049 * Note: using `std::chrono` time zone functionality under MSVC may cause memory leaks to be reported. 0050 * See <a href="https://github.com/microsoft/STL/issues/2047">this issue</a> for an explanation and 0051 * <a href="https://github.com/microsoft/STL/issues/2504">this other issue</a> for a workaround. 0052 */ 0053 class datetime 0054 { 0055 public: 0056 /** 0057 * \brief A `std::chrono::time_point` that can represent any valid datetime, with microsecond resolution. 0058 * \details 0059 * Time points used by this class are always local times, even if defined 0060 * to use the system clock. Prefer using \ref local_time_point, if your compiler 0061 * supports it. 0062 */ 0063 using time_point = std::chrono:: 0064 time_point<std::chrono::system_clock, std::chrono::duration<std::int64_t, std::micro>>; 0065 0066 #ifdef BOOST_MYSQL_HAS_LOCAL_TIME 0067 0068 /** 0069 * \brief A `std::chrono::local_time` that can represent any valid datetime, with microsecond resolution. 0070 * \details Requires C++20 calendar types. 0071 */ 0072 using local_time_point = std::chrono::local_time<std::chrono::duration<std::int64_t, std::micro>>; 0073 #endif 0074 0075 /** 0076 * \brief Constructs a zero datetime. 0077 * \details Results in a datetime with all of its components set to zero. 0078 * The resulting object has `this->valid() == false`. 0079 * \par Exception safety 0080 * No-throw guarantee. 0081 */ 0082 constexpr datetime() noexcept = default; 0083 0084 /** 0085 * \brief Constructs a datetime from its individual components. 0086 * \details 0087 * Component values that yield invalid datetimes (like zero or out-of-range 0088 * values) are allowed, resulting in an object with `this->valid() == false`. 0089 * \par Exception safety 0090 * No-throw guarantee. 0091 */ 0092 constexpr datetime( 0093 std::uint16_t year, 0094 std::uint8_t month, 0095 std::uint8_t day, 0096 std::uint8_t hour = 0, 0097 std::uint8_t minute = 0, 0098 std::uint8_t second = 0, 0099 std::uint32_t microsecond = 0 0100 ) noexcept 0101 : year_(year), 0102 month_(month), 0103 day_(day), 0104 hour_(hour), 0105 minute_(minute), 0106 second_(second), 0107 microsecond_(microsecond) 0108 { 0109 } 0110 0111 /** 0112 * \brief Constructs a datetime from a `time_point`. 0113 * \par Exception safety 0114 * Strong guarantee. Throws on invalid input. 0115 * \throws std::out_of_range If the resulting `datetime` object would be 0116 * out of the [\ref min_datetime, \ref max_datetime] range. 0117 */ 0118 BOOST_CXX14_CONSTEXPR inline explicit datetime(time_point tp); 0119 0120 #ifdef BOOST_MYSQL_HAS_LOCAL_TIME 0121 /** 0122 * \brief Constructs a datetime from a `local_time_point`. 0123 * \details 0124 * Equivalent to constructing a `date` from a `time_point` with the same 0125 * `time_since_epoch()` as `tp`. 0126 * \n 0127 * Requires C++20 calendar types. 0128 * 0129 * \par Exception safety 0130 * Strong guarantee. Throws on invalid input. 0131 * \throws std::out_of_range If the resulting `datetime` object would be 0132 * out of the [\ref min_datetime, \ref max_datetime] range. 0133 */ 0134 constexpr explicit datetime(local_time_point tp) : datetime(time_point(tp.time_since_epoch())) {} 0135 #endif 0136 0137 /** 0138 * \brief Retrieves the year component. 0139 * \details 0140 * Represents the year number in the Gregorian calendar. 0141 * If `this->valid() == true`, this value is within the `[0, 9999]` range. 0142 * 0143 * \par Exception safety 0144 * No-throw guarantee. 0145 */ 0146 constexpr std::uint16_t year() const noexcept { return year_; } 0147 0148 /** 0149 * \brief Retrieves the month component (1-based). 0150 * \details 0151 * A value of 1 represents January. 0152 * If `this->valid() == true`, this value is within the `[1, 12]` range. 0153 * 0154 * \par Exception safety 0155 * No-throw guarantee. 0156 */ 0157 constexpr std::uint8_t month() const noexcept { return month_; } 0158 0159 /** 0160 * \brief Retrieves the day component (1-based). 0161 * \details 0162 * A value of 1 represents the first day of the month. 0163 * If `this->valid() == true`, this value is within the `[1, last_month_day]` range 0164 * (where `last_month_day` is the last day of the month). 0165 * 0166 * \par Exception safety 0167 * No-throw guarantee. 0168 */ 0169 constexpr std::uint8_t day() const noexcept { return day_; } 0170 0171 /** 0172 * \brief Retrieves the hour component. 0173 * \details If `this->valid() == true`, this value is within the `[0, 23]` range. 0174 * \par Exception safety 0175 * No-throw guarantee. 0176 */ 0177 constexpr std::uint8_t hour() const noexcept { return hour_; } 0178 0179 /** 0180 * \brief Retrieves the minute component. 0181 * \details If `this->valid() == true`, this value is within the `[0, 59]` range. 0182 * \par Exception safety 0183 * No-throw guarantee. 0184 */ 0185 constexpr std::uint8_t minute() const noexcept { return minute_; } 0186 0187 /** 0188 * \brief Retrieves the second component. 0189 * \details If `this->valid() == true`, this value is within the `[0, 59]` range. 0190 * \par Exception safety 0191 * No-throw guarantee. 0192 */ 0193 constexpr std::uint8_t second() const noexcept { return second_; } 0194 0195 /** 0196 * \brief Retrieves the microsecond component. 0197 * \details If `this->valid() == true`, this value is within the `[0, 999999]` range. 0198 * \par Exception safety 0199 * No-throw guarantee. 0200 */ 0201 constexpr std::uint32_t microsecond() const noexcept { return microsecond_; } 0202 0203 /** 0204 * \brief Returns `true` if `*this` represents a valid `time_point`. 0205 * \details If any of the individual components is out of range, the datetime 0206 * doesn't represent an actual `time_point` (e.g. `datetime(2020, 2, 30)`) or 0207 * the datetime is not in the [\ref min_date, \ref max_date] validity range, 0208 * returns `false`. Otherwise, returns `true`. 0209 * 0210 * \par Exception safety 0211 * No-throw guarantee. 0212 */ 0213 constexpr bool valid() const noexcept 0214 { 0215 return detail::is_valid(year_, month_, day_) && hour_ <= detail::max_hour && 0216 minute_ <= detail::max_min && second_ <= detail::max_sec && microsecond_ <= detail::max_micro; 0217 } 0218 0219 /** 0220 * \brief Converts `*this` into a `time_point` (unchecked access). 0221 * \details 0222 * If your compiler supports it, prefer using \ref get_local_time_point, 0223 * as it provides more accurate semantics. 0224 * 0225 * \par Preconditions 0226 * `this->valid() == true` (if violated, results in undefined behavior). 0227 * 0228 * \par Exception safety 0229 * No-throw guarantee. 0230 */ 0231 BOOST_CXX14_CONSTEXPR time_point get_time_point() const noexcept 0232 { 0233 BOOST_ASSERT(valid()); 0234 return time_point(unch_get_micros()); 0235 } 0236 0237 /** 0238 * \brief Converts `*this` into a `time_point` (checked access). 0239 * \details 0240 * If your compiler supports it, prefer using \ref as_local_time_point, 0241 * as it provides more accurate semantics. 0242 * 0243 * \par Exception safety 0244 * Strong guarantee. 0245 * \throws std::invalid_argument If `!this->valid()`. 0246 */ 0247 BOOST_CXX14_CONSTEXPR inline time_point as_time_point() const 0248 { 0249 if (!valid()) 0250 BOOST_THROW_EXCEPTION(std::invalid_argument("datetime::as_time_point: invalid datetime")); 0251 return time_point(unch_get_micros()); 0252 } 0253 0254 #ifdef BOOST_MYSQL_HAS_LOCAL_TIME 0255 /** 0256 * \brief Converts `*this` into a `local_time_point` (unchecked access). 0257 * \details 0258 * The returned object has the same `time_since_epoch()` as `this->get_time_point()`, 0259 * but uses the `std::chrono::local_t` pseudo-clock to better represent 0260 * the absence of time zone information. 0261 * \n 0262 * Requires C++20 calendar types. 0263 * 0264 * \par Preconditions 0265 * `this->valid() == true` (if violated, results in undefined behavior). 0266 * 0267 * \par Exception safety 0268 * No-throw guarantee. 0269 */ 0270 constexpr local_time_point get_local_time_point() const noexcept 0271 { 0272 BOOST_ASSERT(valid()); 0273 return local_time_point(unch_get_micros()); 0274 } 0275 0276 /** 0277 * \brief Converts `*this` into a local time point (checked access). 0278 * \details 0279 * The returned object has the same `time_since_epoch()` as `this->as_time_point()`, 0280 * but uses the `std::chrono::local_t` pseudo-clock to better represent 0281 * the absence of time zone information. 0282 * \n 0283 * Requires C++20 calendar types. 0284 * 0285 * \par Exception safety 0286 * Strong guarantee. 0287 * \throws std::invalid_argument If `!this->valid()`. 0288 */ 0289 constexpr local_time_point as_local_time_point() const 0290 { 0291 if (!valid()) 0292 BOOST_THROW_EXCEPTION(std::invalid_argument("date::as_local_time_point: invalid date")); 0293 return local_time_point(unch_get_micros()); 0294 } 0295 #endif 0296 0297 /** 0298 * \brief Tests for equality. 0299 * \details Two datetimes are considered equal if all of its individual components 0300 * are equal. This function works for invalid datetimes, too. 0301 * 0302 * \par Exception safety 0303 * No-throw guarantee. 0304 */ 0305 constexpr bool operator==(const datetime& rhs) const noexcept 0306 { 0307 return year_ == rhs.year_ && month_ == rhs.month_ && day_ == rhs.day_ && hour_ == rhs.hour_ && 0308 minute_ == rhs.minute_ && second_ == rhs.second_ && microsecond_ == rhs.microsecond_; 0309 } 0310 0311 /** 0312 * \brief Tests for inequality. 0313 * \par Exception safety 0314 * No-throw guarantee. 0315 */ 0316 constexpr bool operator!=(const datetime& rhs) const noexcept { return !(*this == rhs); } 0317 0318 /** 0319 * \brief Returns the current system time as a datetime object. 0320 * \par Exception safety 0321 * Strong guarantee. Only throws if obtaining the current time throws. 0322 */ 0323 static datetime now() 0324 { 0325 auto now = time_point::clock::now(); 0326 return datetime(std::chrono::time_point_cast<time_point::duration>(now)); 0327 } 0328 0329 private: 0330 std::uint16_t year_{}; 0331 std::uint8_t month_{}; 0332 std::uint8_t day_{}; 0333 std::uint8_t hour_{}; 0334 std::uint8_t minute_{}; 0335 std::uint8_t second_{}; 0336 std::uint32_t microsecond_{}; 0337 0338 BOOST_CXX14_CONSTEXPR inline time_point::duration unch_get_micros() const 0339 { 0340 // Doing time of day independently to prevent overflow 0341 days d(detail::ymd_to_days(year_, month_, day_)); 0342 auto time_of_day = std::chrono::hours(hour_) + std::chrono::minutes(minute_) + 0343 std::chrono::seconds(second_) + std::chrono::microseconds(microsecond_); 0344 return time_point::duration(d) + time_of_day; 0345 } 0346 }; 0347 0348 /** 0349 * \relates datetime 0350 * \brief Streams a datetime. 0351 * \details This function works for invalid datetimes, too. 0352 */ 0353 BOOST_MYSQL_DECL 0354 std::ostream& operator<<(std::ostream& os, const datetime& v); 0355 0356 /// The minimum allowed value for \ref datetime. 0357 BOOST_INLINE_CONSTEXPR datetime min_datetime(0u, 1u, 1u); 0358 0359 /// The maximum allowed value for \ref datetime. 0360 BOOST_INLINE_CONSTEXPR datetime max_datetime(9999u, 12u, 31u, 23u, 59u, 59u, 999999u); 0361 0362 } // namespace mysql 0363 } // namespace boost 0364 0365 // Implementations 0366 BOOST_CXX14_CONSTEXPR boost::mysql::datetime::datetime(time_point tp) 0367 { 0368 using std::chrono::duration_cast; 0369 using std::chrono::hours; 0370 using std::chrono::microseconds; 0371 using std::chrono::minutes; 0372 using std::chrono::seconds; 0373 0374 // Avoiding using -= for durations as it's not constexpr until C++17 0375 auto input_dur = tp.time_since_epoch(); 0376 auto rem = input_dur % days(1); 0377 auto num_days = duration_cast<days>(input_dur); 0378 if (rem.count() < 0) 0379 { 0380 rem = rem + days(1); 0381 num_days = num_days - days(1); 0382 } 0383 auto num_hours = duration_cast<hours>(rem); 0384 rem = rem - num_hours; 0385 auto num_minutes = duration_cast<minutes>(rem); 0386 rem = rem - num_minutes; 0387 auto num_seconds = duration_cast<seconds>(rem); 0388 rem = rem - num_seconds; 0389 auto num_microseconds = duration_cast<microseconds>(rem); 0390 0391 BOOST_ASSERT(num_hours.count() >= 0 && num_hours.count() <= detail::max_hour); 0392 BOOST_ASSERT(num_minutes.count() >= 0 && num_minutes.count() <= detail::max_min); 0393 BOOST_ASSERT(num_seconds.count() >= 0 && num_seconds.count() <= detail::max_sec); 0394 BOOST_ASSERT(num_microseconds.count() >= 0 && num_microseconds.count() <= detail::max_micro); 0395 0396 bool ok = detail::days_to_ymd(num_days.count(), year_, month_, day_); 0397 if (!ok) 0398 BOOST_THROW_EXCEPTION(std::out_of_range("datetime::datetime: time_point was out of range")); 0399 0400 microsecond_ = static_cast<std::uint32_t>(num_microseconds.count()); 0401 second_ = static_cast<std::uint8_t>(num_seconds.count()); 0402 minute_ = static_cast<std::uint8_t>(num_minutes.count()); 0403 hour_ = static_cast<std::uint8_t>(num_hours.count()); 0404 } 0405 0406 #ifdef BOOST_MYSQL_HEADER_ONLY 0407 #include <boost/mysql/impl/datetime.ipp> 0408 #endif 0409 0410 #endif
| [ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
|
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
|