|
| 1 | +#ifndef MY_TEMPORAL_H |
| 2 | +#define MY_TEMPORAL_H |
| 3 | + |
| 4 | +/* Copyright (c) 2025, Oracle and/or its affiliates. |
| 5 | +
|
| 6 | + This program is free software; you can redistribute it and/or modify |
| 7 | + it under the terms of the GNU General Public License, version 2.0, |
| 8 | + as published by the Free Software Foundation. |
| 9 | +
|
| 10 | + This program is also distributed with certain software (including |
| 11 | + but not limited to OpenSSL) that is licensed under separate terms, |
| 12 | + as designated in a particular file or component or in included license |
| 13 | + documentation. The authors of MySQL hereby grant you an additional |
| 14 | + permission to link the program and your derivative works with the |
| 15 | + separately licensed software that they have included with MySQL. |
| 16 | +
|
| 17 | + This program is distributed in the hope that it will be useful, |
| 18 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 19 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 20 | + GNU General Public License, version 2.0, for more details. |
| 21 | +
|
| 22 | + You should have received a copy of the GNU General Public License |
| 23 | + along with this program; if not, write to the Free Software |
| 24 | + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
| 25 | + |
| 26 | +/** |
| 27 | + @ingroup MY_TEMPORAL |
| 28 | + @{ |
| 29 | +
|
| 30 | + @file include/my_temporal.h |
| 31 | +
|
| 32 | + Server classes for temporal handling (DATE, TIME, DATETIME) |
| 33 | +*/ |
| 34 | + |
| 35 | +class Time_val; |
| 36 | +struct Interval; |
| 37 | + |
| 38 | +#include "my_time.h" |
| 39 | +#include "mysql_time.h" |
| 40 | + |
| 41 | +#include <sys/types.h> |
| 42 | +#include <cassert> |
| 43 | +#include <iostream> |
| 44 | +#include <limits> |
| 45 | + |
| 46 | +/** |
| 47 | + Time_val is a temporal type that represents only time. |
| 48 | + It has constructors for creating time values from time components |
| 49 | + (hour, minute, second and microseconds), and from seconds and microseconds. |
| 50 | + It also has a constructor to create a value from a MYSQL_TIME value. |
| 51 | +
|
| 52 | + The range of values supported is from -838:59:59 to +838:59:59. |
| 53 | + The negative values, and the values from 24:00:00 and up are dedicated |
| 54 | + for use as a small-range interval type and should not be taken as a time |
| 55 | + within a day. |
| 56 | +*/ |
| 57 | +class Time_val { |
| 58 | + public: |
| 59 | + Time_val() : m_value() {} |
| 60 | + |
| 61 | + Time_val(bool negative, uint32_t hour, uint32_t minute, uint32_t second, |
| 62 | + uint32_t microsecond) { |
| 63 | + assert(hour <= TIME_MAX_HOUR && minute <= TIME_MAX_MINUTE && |
| 64 | + second <= TIME_MAX_SECOND && microsecond <= TIME_MAX_MICROSEC); |
| 65 | + m_value = (static_cast<uint64_t>(hour) << TIME_SHIFT_HOUR) | |
| 66 | + (static_cast<uint64_t>(minute) << TIME_SHIFT_MINUTE) | |
| 67 | + (static_cast<uint64_t>(second) << TIME_SHIFT_SECOND) | |
| 68 | + microsecond; |
| 69 | + if (negative) { |
| 70 | + assert(hour != 0 || minute != 0 || second != 0 || microsecond != 0); |
| 71 | + // Stored as one's complement, negate value and subtract one |
| 72 | + m_value = (static_cast<uint64_t>(-static_cast<int64_t>(m_value)) & |
| 73 | + (BITS_MICROSEC | BITS_SECOND | BITS_MINUTE | BITS_HOUR)) - |
| 74 | + 1; |
| 75 | + } else { |
| 76 | + m_value |= BITS_SIGN; |
| 77 | + } |
| 78 | + assert(is_valid()); |
| 79 | + } |
| 80 | + |
| 81 | + Time_val(bool negative, uint32_t second, uint32_t microsecond) |
| 82 | + : Time_val(negative, second / SECS_PER_HOUR, |
| 83 | + (second / SECS_PER_MIN) % MINS_PER_HOUR, second % SECS_PER_MIN, |
| 84 | + microsecond) {} |
| 85 | + |
| 86 | + explicit Time_val(const MYSQL_TIME &mtime) |
| 87 | + : Time_val(mtime.neg, mtime.hour, mtime.minute, mtime.second, |
| 88 | + mtime.second_part) { |
| 89 | + assert(mtime.time_type == MYSQL_TIMESTAMP_TIME); |
| 90 | + } |
| 91 | + |
| 92 | + bool is_negative() const { return (m_value & BITS_SIGN) == 0; } |
| 93 | + uint32_t hour() const { |
| 94 | + return is_negative() ? 2047U - ((m_value & BITS_HOUR) >> TIME_SHIFT_HOUR) |
| 95 | + : (m_value & BITS_HOUR) >> TIME_SHIFT_HOUR; |
| 96 | + } |
| 97 | + uint32_t minute() const { |
| 98 | + return is_negative() ? 63U - ((m_value & BITS_MINUTE) >> TIME_SHIFT_MINUTE) |
| 99 | + : (m_value & BITS_MINUTE) >> TIME_SHIFT_MINUTE; |
| 100 | + } |
| 101 | + uint32_t second() const { |
| 102 | + return is_negative() ? 63U - ((m_value & BITS_SECOND) >> TIME_SHIFT_SECOND) |
| 103 | + : (m_value & BITS_SECOND) >> TIME_SHIFT_SECOND; |
| 104 | + } |
| 105 | + uint32_t microsecond() const { |
| 106 | + return is_negative() ? BITS_MICROSEC - (m_value & BITS_MICROSEC) |
| 107 | + : (m_value & BITS_MICROSEC); |
| 108 | + } |
| 109 | + |
| 110 | + /** |
| 111 | + @returns whether the value is less than, equal to or greater than |
| 112 | + the argument value. |
| 113 | + */ |
| 114 | + int compare(const Time_val arg) const { |
| 115 | + return m_value < arg.m_value ? -1 : m_value > arg.m_value ? 1 : 0; |
| 116 | + } |
| 117 | + /// @returns an integer value for comparison purposes |
| 118 | + int64_t for_comparison() const { return m_value; } |
| 119 | + |
| 120 | + /// Check against extreme values |
| 121 | + bool is_extreme_value(bool positive) const { |
| 122 | + return m_value == (positive ? MAX_TIME_VALUE : MIN_TIME_VALUE); |
| 123 | + } |
| 124 | + |
| 125 | + /// Set zero time |
| 126 | + void set_zero() { m_value = BITS_SIGN; } |
| 127 | + |
| 128 | + /// Set extreme value |
| 129 | + void set_extreme_value(bool negative) { |
| 130 | + m_value = negative ? MIN_TIME_VALUE : MAX_TIME_VALUE; |
| 131 | + } |
| 132 | + |
| 133 | + bool operator==(const Time_val rhs) const { return m_value == rhs.m_value; } |
| 134 | + |
| 135 | + /** |
| 136 | + Add a time value to another time value, or subtract it |
| 137 | +
|
| 138 | + @param tv Time value to add or subtract |
| 139 | + @param subtract If true, subtract the time value, otherwise add it. |
| 140 | +
|
| 141 | + @returns false if result is within valid time range, true otherwise. |
| 142 | + */ |
| 143 | + bool add(Time_val tv, bool subtract); |
| 144 | + |
| 145 | + /** |
| 146 | + Add an interval to a time value, or subtract it |
| 147 | +
|
| 148 | + @param iv Interval to add or subtract |
| 149 | + @param subtract If true, subtract the time value, otherwise add it. |
| 150 | +
|
| 151 | + @returns false if result is within valid time range, true otherwise. |
| 152 | + */ |
| 153 | + bool add(Interval &iv, bool subtract); |
| 154 | + |
| 155 | + /// Static functions for creation |
| 156 | + /** |
| 157 | + Creates Time_val with range check. Minute, second and microsecond |
| 158 | + values must be within limits. |
| 159 | +
|
| 160 | + @returns false if valid range, true if outside valid range. |
| 161 | + */ |
| 162 | + static bool make_time(bool negative, uint32_t hour, uint32_t minute, |
| 163 | + uint32_t second, uint32_t microsecond, Time_val *time) { |
| 164 | + if (hour > TIME_MAX_HOUR || (hour == TIME_MAX_HOUR && microsecond != 0)) { |
| 165 | + return true; |
| 166 | + } |
| 167 | + assert(minute <= TIME_MAX_MINUTE && second <= TIME_MAX_SECOND && |
| 168 | + microsecond <= TIME_MAX_MICROSEC); |
| 169 | + *time = Time_val(negative, hour, minute, second, microsecond); |
| 170 | + return false; |
| 171 | + } |
| 172 | + /// Creates a Time_val from a date_time by extracting only the time fields |
| 173 | + static Time_val strip_date(const MYSQL_TIME &mt); |
| 174 | + |
| 175 | + /// Convert time value to the generalized temporal time format. |
| 176 | + explicit operator MYSQL_TIME() const; |
| 177 | + |
| 178 | + /// @returns time value as number of seconds. Fraction seconds are ignored. |
| 179 | + int32_t to_seconds() const { |
| 180 | + return static_cast<int32_t>(unsigned_seconds(hour(), minute(), second())) * |
| 181 | + (is_negative() ? -1 : 1); |
| 182 | + } |
| 183 | + /// @returns time value as number of microseconds. |
| 184 | + int64_t to_microseconds() const { |
| 185 | + return static_cast<int64_t>( |
| 186 | + unsigned_microsec(hour(), minute(), second(), microsecond())) * |
| 187 | + (is_negative() ? -1 : 1); |
| 188 | + } |
| 189 | + /** |
| 190 | + base100 representation without microsecond, but rounded |
| 191 | + '-12:34:56.999999' is returned as -123457 |
| 192 | + */ |
| 193 | + int64_t to_int_rounded() const; |
| 194 | + /** |
| 195 | + base100 representation without microsecond, '-12:34:56.999999' is |
| 196 | + returned as -123456 |
| 197 | + */ |
| 198 | + int64_t to_int_truncated() const; |
| 199 | + /** |
| 200 | + base100 representation with microseconds, returned as double precision float |
| 201 | + */ |
| 202 | + double to_double() const; |
| 203 | + |
| 204 | + /// @returns true if value is adjusted to number of decimals in fraction |
| 205 | + bool is_adjusted(uint32_t decimals) const; |
| 206 | + |
| 207 | + /// @returns actual number of decimals in fraction |
| 208 | + uint32_t actual_decimals() const; |
| 209 | + |
| 210 | + // Mutators |
| 211 | + void adjust_fraction(uint32_t decimals, bool round); |
| 212 | + |
| 213 | + // Add nanoseconds to a time value, with rounding |
| 214 | + bool add_nanoseconds_round(const int64_t nanoseconds) { |
| 215 | + return nanoseconds < 0 ? add_microseconds((nanoseconds - 500) / 1000) |
| 216 | + : add_microseconds((nanoseconds + 500) / 1000); |
| 217 | + } |
| 218 | + |
| 219 | + /** |
| 220 | + Convert server time value to storage engine interface format |
| 221 | +
|
| 222 | + @param [out] ptr The buffer to put value at. |
| 223 | + @param dec Precision. |
| 224 | + */ |
| 225 | + void store_time(uint8_t *ptr, uint32_t dec) const; |
| 226 | + |
| 227 | + /** |
| 228 | + Convert from storage engine interface time format to server time value. |
| 229 | +
|
| 230 | + @param ptr The pointer to read the value at. |
| 231 | + @param dec Precision. |
| 232 | + @param[out] time Returned time value |
| 233 | + */ |
| 234 | + static void load_time(const uint8_t *ptr, uint32_t dec, Time_val *time); |
| 235 | + |
| 236 | + size_t to_string(char *buffer, uint32_t dec) const; |
| 237 | + |
| 238 | + std::string to_string() const; |
| 239 | + |
| 240 | + private: |
| 241 | + explicit Time_val(int64_t val) : m_value(val) {} |
| 242 | + |
| 243 | + /// Set microsecond part of time value |
| 244 | + void set_microsecond(uint32_t fraction) { |
| 245 | + m_value = (m_value & ~BITS_MICROSEC) | |
| 246 | + (is_negative() ? (0xFFFFFFU - fraction) : fraction); |
| 247 | + } |
| 248 | + |
| 249 | + bool add_seconds(int32_t seconds) { |
| 250 | + Time_val tv(seconds < 0, 0, 0, static_cast<uint8_t>(abs(seconds)), 0); |
| 251 | + add(tv, false); |
| 252 | + assert(is_valid()); |
| 253 | + return false; |
| 254 | + } |
| 255 | + |
| 256 | + bool add_microseconds(int64_t mu) { |
| 257 | + int64_t signed_micro = |
| 258 | + unsigned_microsec(hour(), minute(), second(), microsecond()); |
| 259 | + if (is_negative()) signed_micro = -signed_micro; |
| 260 | + signed_micro += mu; |
| 261 | + bool negative = signed_micro < 0; |
| 262 | + uint64_t micro = negative ? -signed_micro : signed_micro; |
| 263 | + if (micro > MAX_TIME_MICROSEC) { |
| 264 | + return true; |
| 265 | + } |
| 266 | + uint32_t seconds = static_cast<uint32_t>(micro / TIME_MULT_SECOND); |
| 267 | + micro %= TIME_MULT_SECOND; |
| 268 | + *this = Time_val(negative, seconds, micro); |
| 269 | + assert(is_valid()); |
| 270 | + return false; |
| 271 | + } |
| 272 | + |
| 273 | + bool is_valid() const { |
| 274 | + return hour() <= TIME_MAX_HOUR && minute() <= TIME_MAX_MINUTE && |
| 275 | + second() <= TIME_MAX_SECOND && microsecond() <= TIME_MAX_MICROSEC && |
| 276 | + m_value <= MAX_TIME_VALUE && m_value >= MIN_TIME_VALUE; |
| 277 | + } |
| 278 | + |
| 279 | + static uint32_t unsigned_seconds(uint32_t hour, uint32_t minute, |
| 280 | + uint32_t second) { |
| 281 | + return (hour * SECS_PER_HOUR) + (minute * SECS_PER_MIN) + second; |
| 282 | + } |
| 283 | + |
| 284 | + static uint64_t unsigned_microsec(uint32_t hour, uint32_t minute, |
| 285 | + uint32_t second, uint32_t microsec) { |
| 286 | + return (hour * TIME_MULT_HOUR) + (minute * TIME_MULT_MINUTE) + |
| 287 | + (second * TIME_MULT_SECOND) + microsec; |
| 288 | + } |
| 289 | + |
| 290 | + static constexpr const uint32_t TIME_MAX_HOUR = 838; |
| 291 | + static constexpr const uint32_t TIME_MAX_MINUTE = 59; |
| 292 | + static constexpr const uint32_t TIME_MAX_SECOND = 59; |
| 293 | + static constexpr const uint32_t TIME_MAX_MICROSEC = 999999; |
| 294 | + |
| 295 | + static constexpr uint64_t TIME_MULT_SECOND = 1000000; |
| 296 | + static constexpr uint64_t TIME_MULT_MINUTE = 60000000; |
| 297 | + static constexpr uint64_t TIME_MULT_HOUR = 3600000000; |
| 298 | + |
| 299 | + static constexpr uint64_t BITS_MICROSEC = 0x0000000000FFFFFF; |
| 300 | + static constexpr uint64_t BITS_SECOND = 0x000000003F000000; |
| 301 | + static constexpr uint64_t BITS_MINUTE = 0x0000000FC0000000; |
| 302 | + static constexpr uint64_t BITS_HOUR = 0x00007FF000000000; |
| 303 | + static constexpr uint64_t BITS_SIGN = 0x0000800000000000; |
| 304 | + static constexpr int TIME_SHIFT_SECOND = 24; |
| 305 | + static constexpr int TIME_SHIFT_MINUTE = 30; |
| 306 | + static constexpr int TIME_SHIFT_HOUR = 36; |
| 307 | + |
| 308 | + static constexpr uint64_t MAX_TIME_VALUE = |
| 309 | + BITS_SIGN | (static_cast<uint64_t>(TIME_MAX_HOUR) << TIME_SHIFT_HOUR) | |
| 310 | + (static_cast<uint64_t>(TIME_MAX_MINUTE) << TIME_SHIFT_MINUTE) | |
| 311 | + (static_cast<uint64_t>(TIME_MAX_SECOND) << TIME_SHIFT_SECOND); |
| 312 | + |
| 313 | + static constexpr uint64_t MIN_TIME_VALUE = |
| 314 | + (static_cast<uint64_t>(-static_cast<int64_t>(MAX_TIME_VALUE)) & |
| 315 | + (BITS_MICROSEC | BITS_SECOND | BITS_MINUTE | BITS_HOUR)) - |
| 316 | + 1; |
| 317 | + |
| 318 | + // 838:59:59.000000 |
| 319 | + static constexpr uint64_t MAX_TIME_MICROSEC = |
| 320 | + (TIME_MAX_HOUR * TIME_MULT_HOUR) + (TIME_MAX_MINUTE * TIME_MULT_MINUTE) + |
| 321 | + (TIME_MAX_SECOND * TIME_MULT_SECOND); |
| 322 | + |
| 323 | + /** |
| 324 | + A TIME value is stored in bit coded fields in a 64 bit unsigned value. |
| 325 | + The format is efficient for comparison, storage, retrieval and movement. |
| 326 | + The fields are stored in two's complement, but with a sign bit set for |
| 327 | + non-negative values. This means that values can be compared using regular |
| 328 | + unsigned integer logic. |
| 329 | + Format: |
| 330 | + Bits 0-23: microseconds (0-999999) |
| 331 | + Bits 24-29: seconds (0-59) |
| 332 | + Bits 30-35: minutes (0-59) |
| 333 | + Bits 36-46: hours (0-838) (Theoretical range up to 2047) |
| 334 | + Bits 47-47: Sign (1 for positive value, 0 for negative value) |
| 335 | + */ |
| 336 | + uint64_t m_value = 0xffffffffffffffff; |
| 337 | +}; |
| 338 | + |
| 339 | +/** |
| 340 | + @} (end of ingroup MY_TEMPORAL) |
| 341 | +*/ |
| 342 | +#endif // MY_TEMPORAL_H |
0 commit comments