|
6 | 6 | //
|
7 | 7 | // Identification: src/function/date_functions.cpp
|
8 | 8 | //
|
9 |
| -// Copyright (c) 2015-2017, Carnegie Mellon University Database Group |
| 9 | +// Copyright (c) 2015-2018, Carnegie Mellon University Database Group |
10 | 10 | //
|
11 | 11 | //===----------------------------------------------------------------------===//
|
12 | 12 |
|
13 | 13 | #include "function/date_functions.h"
|
14 | 14 |
|
15 | 15 | #include <date/date.h>
|
16 | 16 | #include <date/iso_week.h>
|
17 |
| -#include <inttypes.h> |
18 |
| -#include <time.h> |
19 | 17 | #include <sys/time.h>
|
20 | 18 |
|
| 19 | +#include "codegen/runtime_functions.h" |
21 | 20 | #include "common/internal_types.h"
|
22 | 21 | #include "type/value.h"
|
23 | 22 | #include "type/value_factory.h"
|
@@ -63,10 +62,156 @@ type::Value DateFunctions::_Now(
|
63 | 62 | return type::ValueFactory::GetTimestampValue(Now());
|
64 | 63 | }
|
65 | 64 |
|
| 65 | +int32_t DateFunctions::DateToJulian(int32_t year, int32_t month, int32_t day) { |
| 66 | + // From Postgres date2j() |
| 67 | + |
| 68 | + if (month > 2) { |
| 69 | + month += 1; |
| 70 | + year += 4800; |
| 71 | + } else { |
| 72 | + month += 13; |
| 73 | + year += 4799; |
| 74 | + } |
| 75 | + |
| 76 | + int32_t century = year / 100; |
| 77 | + |
| 78 | + int32_t julian = year * 365 - 32167; |
| 79 | + julian += year / 4 - century + century / 4; |
| 80 | + julian += 7834 * month / 256 + day; |
| 81 | + |
| 82 | + return julian; |
| 83 | +} |
| 84 | + |
| 85 | +void DateFunctions::JulianToDate(int32_t julian_date, int32_t &year, int32_t &month, |
| 86 | + int32_t &day) { |
| 87 | + // From Postgres j2date() |
| 88 | + |
| 89 | + uint32_t julian = static_cast<uint32_t>(julian_date); |
| 90 | + julian += 32044; |
| 91 | + |
| 92 | + uint32_t quad = julian / 146097; |
| 93 | + |
| 94 | + uint32_t extra = (julian - quad * 146097) * 4 + 3; |
| 95 | + julian += 60 + quad * 3 + extra / 146097; |
| 96 | + quad = julian / 1461; |
| 97 | + julian -= quad * 1461; |
| 98 | + |
| 99 | + int32_t y = julian * 4 / 1461; |
| 100 | + julian = ((y != 0) ? (julian + 305) % 365 : (julian + 306) % 366) + 123; |
| 101 | + y += quad * 4; |
| 102 | + |
| 103 | + // Set year |
| 104 | + year = static_cast<uint32_t>(y - 4800); |
| 105 | + quad = julian * 2141 / 65536; |
| 106 | + |
| 107 | + // Set day |
| 108 | + day = julian - 7834 * quad / 256; |
| 109 | + |
| 110 | + // Set month |
| 111 | + month = (quad + 10) % 12 + 1; |
| 112 | +} |
| 113 | + |
| 114 | +namespace { |
| 115 | + |
| 116 | +template <typename T> |
| 117 | +bool TryParseInt(const char *&data, const char *end, T &out) { |
| 118 | + static_assert(std::is_integral<T>::value, |
| 119 | + "ParseInt() must only be called with integer types"); |
| 120 | + |
| 121 | + // Initialize |
| 122 | + out = 0; |
| 123 | + |
| 124 | + // Trim leading whitespace |
| 125 | + while (*data == ' ') { |
| 126 | + data++; |
| 127 | + } |
| 128 | + |
| 129 | + // Return if no more data |
| 130 | + if (data == end) { |
| 131 | + return false; |
| 132 | + } |
| 133 | + |
| 134 | + const char *snapshot = data; |
| 135 | + while (data != end) { |
| 136 | + if (*data < '0' || *data > '9') { |
| 137 | + // Not a valid integer, stop |
| 138 | + break; |
| 139 | + } |
| 140 | + |
| 141 | + // Update running sum |
| 142 | + out = (out * 10) + (*data - '0'); |
| 143 | + |
| 144 | + // Move along |
| 145 | + data++; |
| 146 | + } |
| 147 | + |
| 148 | + return snapshot != data; |
| 149 | +} |
| 150 | + |
| 151 | +} // namespace |
| 152 | + |
66 | 153 | int32_t DateFunctions::InputDate(
|
67 |
| - UNUSED_ATTRIBUTE const codegen::type::Type &type, |
68 |
| - UNUSED_ATTRIBUTE const char *data, UNUSED_ATTRIBUTE uint32_t len) { |
69 |
| - return 0; |
| 154 | + UNUSED_ATTRIBUTE const codegen::type::Type &type, const char *data, |
| 155 | + uint32_t len) { |
| 156 | + // Okay, Postgres supports a crap-tonne of different date-time and timestamp |
| 157 | + // formats. I don't want to spend time implementing them all. For now, let's |
| 158 | + // cover the most common formats: yyyy-mm-dd |
| 159 | + |
| 160 | + const char *curr_ptr = data; |
| 161 | + const char *end = data + len; |
| 162 | + |
| 163 | + uint32_t nums[3] = {0, 0, 0}; |
| 164 | + uint32_t year, month, day; |
| 165 | + |
| 166 | + for (uint32_t i = 0; i < 3; i++) { |
| 167 | + bool parsed = TryParseInt(curr_ptr, end, nums[i]); |
| 168 | + |
| 169 | + bool unexpected_next_char = (*curr_ptr != '-' && *curr_ptr != '/'); |
| 170 | + if (!parsed || (i != 2 && unexpected_next_char)) { |
| 171 | + goto unsupported; |
| 172 | + } |
| 173 | + |
| 174 | + curr_ptr++; |
| 175 | + } |
| 176 | + |
| 177 | + // Looks okay ... let's check the components. |
| 178 | + year = nums[0], month = nums[1], day = nums[2]; |
| 179 | + |
| 180 | + if (month == 0 || month > 12 || day == 0 || day > 31) { |
| 181 | + goto unsupported; |
| 182 | + } |
| 183 | + |
| 184 | + switch (month) { |
| 185 | + case 2: { |
| 186 | + uint32_t days_in_feb = |
| 187 | + ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) ? 29 : 28; |
| 188 | + if (day > days_in_feb) { |
| 189 | + goto unsupported; |
| 190 | + } |
| 191 | + break; |
| 192 | + } |
| 193 | + case 4: |
| 194 | + case 6: |
| 195 | + case 9: |
| 196 | + case 11: { |
| 197 | + if (day > 30) { |
| 198 | + goto unsupported; |
| 199 | + } |
| 200 | + break; |
| 201 | + } |
| 202 | + default: { |
| 203 | + if (day > 31) { |
| 204 | + goto unsupported; |
| 205 | + } |
| 206 | + break; |
| 207 | + } |
| 208 | + } |
| 209 | + |
| 210 | + return DateToJulian(year, month, day); |
| 211 | + |
| 212 | +unsupported: |
| 213 | + codegen::RuntimeFunctions::ThrowInvalidInputStringException(); |
| 214 | + __builtin_unreachable(); |
70 | 215 | }
|
71 | 216 |
|
72 | 217 | } // namespace expression
|
|
0 commit comments