|
1 | 1 | // This code has been automatically generated by aas-core-codegen.
|
2 | 2 | // Do NOT edit or append.
|
3 | 3 |
|
4 |
| -#include "./BigInt.hpp" |
5 | 4 | #include "aas_core/aas_3_0/common.hpp"
|
6 | 5 | #include "aas_core/aas_3_0/constants.hpp"
|
7 | 6 | #include "aas_core/aas_3_0/verification.hpp"
|
@@ -225,74 +224,44 @@ const std::wregex kRegexDatePrefix(
|
225 | 224 | L"^(-?[0-9]+)-(0[1-9]|1[0-2])-(0[0-9]|1[0-9]|2[0-9]|30|31)"
|
226 | 225 | );
|
227 | 226 |
|
228 |
| -template< |
229 |
| - typename T, |
230 |
| - std::enable_if< |
231 |
| - std::is_integral<T>::value |
232 |
| - || std::is_same<T, BigInt>::value |
233 |
| - >* = nullptr |
234 |
| -> |
235 |
| -bool IsLeapYear(T year) { |
236 |
| - // NOTE (mristin): |
237 |
| - // We consider the years B.C. to be one-off. |
238 |
| - // See the note at: https://www.w3.org/TR/xmlschema-2/#dateTime: |
239 |
| - // "'-0001' is the lexical representation of the year 1 Before Common Era |
240 |
| - // (1 BCE, sometimes written "1 BC")." |
241 |
| - // |
242 |
| - // Hence, -1 year in XML is 1 BCE, which is 0 year in astronomical years. |
243 |
| - if (year < 0) { |
244 |
| - year = -year - 1; |
245 |
| - } |
246 |
| - |
247 |
| - // See: See: https://en.wikipedia.org/wiki/Leap_year#Algorithm |
248 |
| - if (year % 4 > 0) |
249 |
| - { |
250 |
| - return false; |
251 |
| - } |
252 |
| - |
253 |
| - if (year % 100 > 0) |
254 |
| - { |
255 |
| - return true; |
256 |
| - } |
257 |
| - |
258 |
| - if (year % 400 > 0) |
259 |
| - { |
260 |
| - return false; |
| 227 | +/** |
| 228 | + * Determine the sign of the given year as text. |
| 229 | + * |
| 230 | + * @param year_str year as text |
| 231 | + * @return -1, 0 or 1; -1 means BC, 1 means AD. 0 means a zero year, |
| 232 | + * even if specified as -0. |
| 233 | + */ |
| 234 | +int DetermineEra(const std::wstring& year_str) { |
| 235 | + #ifdef DEBUG |
| 236 | + if (year_str.empty()) { |
| 237 | + throw std::invalid_argument( |
| 238 | + "Expected a valid year string, but got an empty string" |
| 239 | + ); |
261 | 240 | }
|
| 241 | + #endif |
262 | 242 |
|
263 |
| - return true; |
264 |
| -} |
265 |
| - |
| 243 | + const int sign = (year_str[0] == L'-') ? -1 : 1; |
266 | 244 |
|
267 |
| -bool IsLeapYear(long long year) { |
268 |
| - // NOTE (mristin): |
269 |
| - // We consider the years B.C. to be one-off. |
270 |
| - // See the note at: https://www.w3.org/TR/xmlschema-2/#dateTime: |
271 |
| - // "'-0001' is the lexical representation of the year 1 Before Common Era |
272 |
| - // (1 BCE, sometimes written "1 BC")." |
273 |
| - // |
274 |
| - // Hence, -1 year in XML is 1 BCE, which is 0 year in astronomical years. |
275 |
| - if (year < 0) { |
276 |
| - year = -year - 1; |
277 |
| - } |
278 |
| - |
279 |
| - // See: See: https://en.wikipedia.org/wiki/Leap_year#Algorithm |
280 |
| - if (year % 4 > 0) |
281 |
| - { |
282 |
| - return false; |
| 245 | + size_t cursor = 0; |
| 246 | + if (sign < 0) { |
| 247 | + // NOTE (mristin): |
| 248 | + // We skip the minus sign as prefix, including the edge case "-0". |
| 249 | + ++cursor; |
283 | 250 | }
|
284 | 251 |
|
285 |
| - if (year % 100 > 0) |
286 |
| - { |
287 |
| - return true; |
| 252 | + bool is_zero = true; |
| 253 | + for (; cursor < year_str.size(); ++cursor) { |
| 254 | + if (year_str[cursor] != L'0') { |
| 255 | + is_zero = false; |
| 256 | + break; |
| 257 | + } |
288 | 258 | }
|
289 | 259 |
|
290 |
| - if (year % 400 > 0) |
291 |
| - { |
292 |
| - return false; |
| 260 | + if (is_zero) { |
| 261 | + return 0; |
293 | 262 | }
|
294 | 263 |
|
295 |
| - return true; |
| 264 | + return sign; |
296 | 265 | }
|
297 | 266 |
|
298 | 267 | const std::map<int, int> kDaysInMonth = {
|
@@ -340,41 +309,62 @@ bool IsXsDateWithoutOffset(const std::wstring& text) {
|
340 | 309 | // difficult. Hence, we sacrifice the efficiency a bit for the clearer code & code
|
341 | 310 | // generation.
|
342 | 311 |
|
343 |
| - bool is_zero_year; |
344 |
| - bool is_leap_year; |
345 |
| - |
346 |
| - try { |
347 |
| - const long long year = std::stoll(match[1].str()); |
348 |
| - |
349 |
| - is_zero_year = year == 0; |
350 |
| - is_leap_year = IsLeapYear<long long>(year); |
351 |
| - } catch (const std::invalid_argument&) { |
352 |
| - std::wstringstream wss; |
353 |
| - wss |
354 |
| - << "The year matched the regex, but could not be parsed as integer: " |
355 |
| - << match[1].str(); |
| 312 | + // NOTE (mristin): |
| 313 | + // The year can be arbitrarily large in `xs:date` and `xs:dateTime`. Instead of |
| 314 | + // using a BigInt implementation -- and having to maintain another dependency -- |
| 315 | + // we simply clip the year to the last four relevant digits for the computation of |
| 316 | + // leap years. |
356 | 317 |
|
357 |
| - throw std::logic_error( |
358 |
| - common::WstringToUtf8(wss.str()) |
359 |
| - ); |
360 |
| - } catch (const std::out_of_range&) { |
361 |
| - const BigInt year( |
362 |
| - common::WstringToUtf8(match[1].str()) |
363 |
| - ); |
364 |
| - is_zero_year = year == 0; |
365 |
| - is_leap_year = IsLeapYear<BigInt>(std::move(year)); |
366 |
| - } |
| 318 | + const std::wstring year_str = match[1].str(); |
367 | 319 |
|
368 |
| - const int month = std::stoi(match[2].str()); |
369 |
| - const int day = std::stoi(match[3].str()); |
| 320 | + const int era = DetermineEra(year_str); |
370 | 321 |
|
371 | 322 | // NOTE (mristin):
|
372 | 323 | // We do not accept year zero, see the note at:
|
373 | 324 | // https://www.w3.org/TR/xmlschema-2/#dateTime
|
374 |
| - if (is_zero_year) { |
| 325 | + if (era == 0) { |
375 | 326 | return false;
|
376 | 327 | }
|
377 | 328 |
|
| 329 | + std::wstring last_four_year_digits; |
| 330 | + |
| 331 | + const size_t year_start = (era < 0) ? 1 : 0; |
| 332 | + const size_t end = year_str.size(); |
| 333 | + size_t start = end - 4; |
| 334 | + if (start < year_start) { |
| 335 | + start = year_start; |
| 336 | + } |
| 337 | + |
| 338 | + const std::wstring at_most_last_four_year_digits( |
| 339 | + year_str.substr(start, 4) |
| 340 | + ); |
| 341 | + |
| 342 | + int year_suffix = era * std::stoi(at_most_last_four_year_digits); |
| 343 | + |
| 344 | + // NOTE (mristin): |
| 345 | + // We consider the years B.C. to be one-off. |
| 346 | + // See the note at: https://www.w3.org/TR/xmlschema-2/#dateTime: |
| 347 | + // "'-0001' is the lexical representation of the year 1 Before Common Era |
| 348 | + // (1 BCE, sometimes written "1 BC")." |
| 349 | + // |
| 350 | + // Hence, -1 year in XML is 1 BCE, which is 0 year in astronomical years. |
| 351 | + if (year_suffix < 0) { |
| 352 | + year_suffix = -year_suffix - 1; |
| 353 | + } |
| 354 | + |
| 355 | + bool is_leap_year = true; |
| 356 | + |
| 357 | + if (year_suffix % 4 > 0) { |
| 358 | + is_leap_year = false; |
| 359 | + } else if (year_suffix % 100 > 0) { |
| 360 | + is_leap_year = true; |
| 361 | + } else if (year_suffix % 400 > 0) { |
| 362 | + is_leap_year = false; |
| 363 | + } |
| 364 | + |
| 365 | + const int month = std::stoi(match[2].str()); |
| 366 | + const int day = std::stoi(match[3].str()); |
| 367 | + |
378 | 368 | if (day <= 0) {
|
379 | 369 | return false;
|
380 | 370 | }
|
|
0 commit comments