summaryrefslogtreecommitdiffstats
path: root/util/datetime/parser.h
diff options
context:
space:
mode:
authorDevtools Arcadia <[email protected]>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <[email protected]>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /util/datetime/parser.h
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'util/datetime/parser.h')
-rw-r--r--util/datetime/parser.h294
1 files changed, 294 insertions, 0 deletions
diff --git a/util/datetime/parser.h b/util/datetime/parser.h
new file mode 100644
index 00000000000..f0c1b4a0c78
--- /dev/null
+++ b/util/datetime/parser.h
@@ -0,0 +1,294 @@
+#pragma once
+
+// probably you do not need to include this file directly, use "util/datetime/base.h"
+
+#include "base.h"
+
+struct TDateTimeFields {
+ TDateTimeFields() {
+ Zero(*this);
+ ZoneOffsetMinutes = 0;
+ Hour = 0;
+ }
+
+ ui32 Year;
+ ui32 Month; // 1..12
+ ui32 Day; // 1 .. 31
+ ui32 Hour; // 0 .. 23
+ ui32 Minute; // 0 .. 59
+ ui32 Second; // 0 .. 60
+ ui32 MicroSecond; // 0 .. 999999
+ i32 ZoneOffsetMinutes;
+
+ void SetLooseYear(ui32 year) {
+ if (year < 60)
+ year += 100;
+ if (year < 160)
+ year += 1900;
+ Year = year;
+ }
+
+ bool IsOk() const noexcept {
+ if (Year < 1970)
+ return false;
+ if (Month < 1 || Month > 12)
+ return false;
+
+ unsigned int maxMonthDay = 31;
+ if (Month == 4 || Month == 6 || Month == 9 || Month == 11) {
+ maxMonthDay = 30;
+ } else if (Month == 2) {
+ if (Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0))
+ // leap year
+ maxMonthDay = 29;
+ else
+ maxMonthDay = 28;
+ }
+ if (Day > maxMonthDay)
+ return false;
+
+ if (Hour > 23)
+ return false;
+
+ if (Minute > 59)
+ return false;
+
+ // handle leap second which is explicitly allowed by ISO 8601:2004(E) $2.2.2
+ // https://datatracker.ietf.org/doc/html/rfc3339#section-5.6
+ if (Second > 60)
+ return false;
+
+ if (MicroSecond > 999999)
+ return false;
+
+ if (Year == 1970 && Month == 1 && Day == 1) {
+ if ((i64)(3600 * Hour + 60 * Minute + Second) < (60 * ZoneOffsetMinutes))
+ return false;
+ }
+
+ return true;
+ }
+
+ TInstant ToInstant(TInstant defaultValue) const {
+ time_t tt = ToTimeT(-1);
+ if (tt == -1)
+ return defaultValue;
+ return TInstant::Seconds(tt) + TDuration::MicroSeconds(MicroSecond);
+ }
+
+ time_t ToTimeT(time_t defaultValue) const {
+ if (!IsOk())
+ return defaultValue;
+ struct tm tm;
+ Zero(tm);
+ tm.tm_year = Year - 1900;
+ tm.tm_mon = Month - 1;
+ tm.tm_mday = Day;
+ tm.tm_hour = Hour;
+ tm.tm_min = Minute;
+ tm.tm_sec = Second;
+ time_t tt = TimeGM(&tm);
+ if (tt == -1)
+ return defaultValue;
+ return tt - ZoneOffsetMinutes * 60;
+ }
+};
+
+class TDateTimeParserBase {
+public:
+ const TDateTimeFields& GetDateTimeFields() const {
+ return DateTimeFields;
+ }
+
+protected:
+ TDateTimeFields DateTimeFields;
+ int cs; //for ragel
+ int Sign;
+ int I;
+ int Dc;
+
+protected:
+ TDateTimeParserBase()
+ : DateTimeFields()
+ , cs(0)
+ , Sign(0)
+ , I(0xDEADBEEF) // to guarantee unittest break if ragel code is incorrect
+ , Dc(0xDEADBEEF)
+ {
+ }
+
+ inline TInstant GetResult(int firstFinalState, TInstant defaultValue) const {
+ if (cs < firstFinalState)
+ return defaultValue;
+ return DateTimeFields.ToInstant(defaultValue);
+ }
+};
+
+#define DECLARE_PARSER(CLASS) \
+ struct CLASS: public TDateTimeParserBase { \
+ CLASS(); \
+ bool ParsePart(const char* input, size_t len); \
+ TInstant GetResult(TInstant defaultValue) const; \
+ };
+
+DECLARE_PARSER(TIso8601DateTimeParser)
+DECLARE_PARSER(TRfc822DateTimeParser)
+DECLARE_PARSER(THttpDateTimeParser)
+DECLARE_PARSER(TX509ValidityDateTimeParser)
+DECLARE_PARSER(TX509Validity4yDateTimeParser)
+
+#undef DECLARE_PARSER
+
+struct TDurationParser {
+ int cs;
+
+ ui64 I;
+ ui32 Dc;
+
+ i32 MultiplierPower; // 6 for seconds, 0 for microseconds, -3 for nanoseconds
+ i32 Multiplier;
+ ui64 IntegerPart;
+ ui32 FractionPart;
+ ui32 FractionDigits;
+
+ TDurationParser();
+ bool ParsePart(const char* input, size_t len);
+ TDuration GetResult(TDuration defaultValue) const;
+};
+
+/**
+Depcrecated cause of default hour offset (+4 hours)
+@see IGNIETFERRO-823
+*/
+struct TDateTimeFieldsDeprecated {
+ TDateTimeFieldsDeprecated() {
+ Zero(*this);
+ ZoneOffsetMinutes = (i32)TDuration::Hours(4).Minutes(); // legacy code
+ Hour = 11;
+ }
+
+ ui32 Year;
+ ui32 Month; // 1..12
+ ui32 Day; // 1 .. 31
+ ui32 Hour; // 0 .. 23
+ ui32 Minute; // 0 .. 59
+ ui32 Second; // 0 .. 60
+ ui32 MicroSecond; // 0 .. 999999
+ i32 ZoneOffsetMinutes;
+
+ void SetLooseYear(ui32 year) {
+ if (year < 60)
+ year += 100;
+ if (year < 160)
+ year += 1900;
+ Year = year;
+ }
+
+ bool IsOk() const noexcept {
+ if (Year < 1970)
+ return false;
+ if (Month < 1 || Month > 12)
+ return false;
+
+ unsigned int maxMonthDay = 31;
+ if (Month == 4 || Month == 6 || Month == 9 || Month == 11) {
+ maxMonthDay = 30;
+ } else if (Month == 2) {
+ if (Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0))
+ // leap year
+ maxMonthDay = 29;
+ else
+ maxMonthDay = 28;
+ }
+ if (Day > maxMonthDay)
+ return false;
+
+ if (Hour > 23)
+ return false;
+
+ if (Minute > 59)
+ return false;
+
+ if (Second > 60)
+ return false;
+
+ if (MicroSecond > 999999)
+ return false;
+
+ if (Year == 1970 && Month == 1 && Day == 1) {
+ if ((i64)(3600 * Hour + 60 * Minute + Second) < (60 * ZoneOffsetMinutes))
+ return false;
+ }
+
+ return true;
+ }
+
+ TInstant ToInstant(TInstant defaultValue) const {
+ time_t tt = ToTimeT(-1);
+ if (tt == -1)
+ return defaultValue;
+ return TInstant::Seconds(tt) + TDuration::MicroSeconds(MicroSecond);
+ }
+
+ time_t ToTimeT(time_t defaultValue) const {
+ if (!IsOk())
+ return defaultValue;
+ struct tm tm;
+ Zero(tm);
+ tm.tm_year = Year - 1900;
+ tm.tm_mon = Month - 1;
+ tm.tm_mday = Day;
+ tm.tm_hour = Hour;
+ tm.tm_min = Minute;
+ tm.tm_sec = Second;
+ time_t tt = TimeGM(&tm);
+ if (tt == -1)
+ return defaultValue;
+ return tt - ZoneOffsetMinutes * 60;
+ }
+};
+
+class TDateTimeParserBaseDeprecated {
+public:
+ const TDateTimeFieldsDeprecated& GetDateTimeFields() const {
+ return DateTimeFields;
+ }
+
+protected:
+ TDateTimeFieldsDeprecated DateTimeFields;
+ int cs; //for ragel
+ int Sign;
+ int I;
+ int Dc;
+
+protected:
+ TDateTimeParserBaseDeprecated()
+ : DateTimeFields()
+ , cs(0)
+ , Sign(0)
+ , I(0xDEADBEEF) // to guarantee unittest break if ragel code is incorrect
+ , Dc(0xDEADBEEF)
+ {
+ }
+
+ inline TInstant GetResult(int firstFinalState, TInstant defaultValue) const {
+ if (cs < firstFinalState)
+ return defaultValue;
+ return DateTimeFields.ToInstant(defaultValue);
+ }
+};
+
+#define DECLARE_PARSER(CLASS) \
+ struct CLASS: public TDateTimeParserBaseDeprecated { \
+ CLASS(); \
+ bool ParsePart(const char* input, size_t len); \
+ TInstant GetResult(TInstant defaultValue) const; \
+ };
+
+DECLARE_PARSER(TIso8601DateTimeParserDeprecated)
+DECLARE_PARSER(TRfc822DateTimeParserDeprecated)
+DECLARE_PARSER(THttpDateTimeParserDeprecated)
+DECLARE_PARSER(TX509ValidityDateTimeParserDeprecated)
+DECLARE_PARSER(TX509Validity4yDateTimeParserDeprecated)
+
+#undef DECLARE_PARSER