blob: 43b6df8da22cdbdc0edda1c2de9d1e6e0a3153dd (
plain) (
tree)
|
|
#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
|