parser.h 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. #pragma once
  2. // probably you do not need to include this file directly, use "util/datetime/base.h"
  3. #include "base.h"
  4. struct TDateTimeFields {
  5. TDateTimeFields() {
  6. Zero(*this);
  7. ZoneOffsetMinutes = 0;
  8. Hour = 0;
  9. }
  10. ui32 Year;
  11. ui32 Month; // 1..12
  12. ui32 Day; // 1 .. 31
  13. ui32 Hour; // 0 .. 23
  14. ui32 Minute; // 0 .. 59
  15. ui32 Second; // 0 .. 60
  16. ui32 MicroSecond; // 0 .. 999999
  17. i32 ZoneOffsetMinutes;
  18. void SetLooseYear(ui32 year) {
  19. if (year < 60)
  20. year += 100;
  21. if (year < 160)
  22. year += 1900;
  23. Year = year;
  24. }
  25. bool IsOk() const noexcept {
  26. if (Year < 1970)
  27. return false;
  28. if (Month < 1 || Month > 12)
  29. return false;
  30. unsigned int maxMonthDay = 31;
  31. if (Month == 4 || Month == 6 || Month == 9 || Month == 11) {
  32. maxMonthDay = 30;
  33. } else if (Month == 2) {
  34. if (Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0))
  35. // leap year
  36. maxMonthDay = 29;
  37. else
  38. maxMonthDay = 28;
  39. }
  40. if (Day > maxMonthDay)
  41. return false;
  42. if (Hour > 23)
  43. return false;
  44. if (Minute > 59)
  45. return false;
  46. // handle leap second which is explicitly allowed by ISO 8601:2004(E) $2.2.2
  47. // https://datatracker.ietf.org/doc/html/rfc3339#section-5.6
  48. if (Second > 60)
  49. return false;
  50. if (MicroSecond > 999999)
  51. return false;
  52. if (Year == 1970 && Month == 1 && Day == 1) {
  53. if ((i64)(3600 * Hour + 60 * Minute + Second) < (60 * ZoneOffsetMinutes))
  54. return false;
  55. }
  56. return true;
  57. }
  58. TInstant ToInstant(TInstant defaultValue) const {
  59. time_t tt = ToTimeT(-1);
  60. if (tt == -1)
  61. return defaultValue;
  62. return TInstant::Seconds(tt) + TDuration::MicroSeconds(MicroSecond);
  63. }
  64. time_t ToTimeT(time_t defaultValue) const {
  65. if (!IsOk())
  66. return defaultValue;
  67. struct tm tm;
  68. Zero(tm);
  69. tm.tm_year = Year - 1900;
  70. tm.tm_mon = Month - 1;
  71. tm.tm_mday = Day;
  72. tm.tm_hour = Hour;
  73. tm.tm_min = Minute;
  74. tm.tm_sec = Second;
  75. time_t tt = TimeGM(&tm);
  76. if (tt == -1)
  77. return defaultValue;
  78. return tt - ZoneOffsetMinutes * 60;
  79. }
  80. };
  81. class TDateTimeParserBase {
  82. public:
  83. const TDateTimeFields& GetDateTimeFields() const {
  84. return DateTimeFields;
  85. }
  86. protected:
  87. TDateTimeFields DateTimeFields;
  88. int cs; // for ragel
  89. int Sign;
  90. int I;
  91. int Dc;
  92. protected:
  93. TDateTimeParserBase()
  94. : DateTimeFields()
  95. , cs(0)
  96. , Sign(0)
  97. , I(0xDEADBEEF) // to guarantee unittest break if ragel code is incorrect
  98. , Dc(0xDEADBEEF)
  99. {
  100. }
  101. inline TInstant GetResult(int firstFinalState, TInstant defaultValue) const {
  102. if (cs < firstFinalState)
  103. return defaultValue;
  104. return DateTimeFields.ToInstant(defaultValue);
  105. }
  106. };
  107. #define DECLARE_PARSER(CLASS) \
  108. struct CLASS: public TDateTimeParserBase { \
  109. CLASS(); \
  110. bool ParsePart(const char* input, size_t len); \
  111. TInstant GetResult(TInstant defaultValue) const; \
  112. };
  113. DECLARE_PARSER(TIso8601DateTimeParser)
  114. DECLARE_PARSER(TRfc822DateTimeParser)
  115. DECLARE_PARSER(THttpDateTimeParser)
  116. DECLARE_PARSER(TX509ValidityDateTimeParser)
  117. DECLARE_PARSER(TX509Validity4yDateTimeParser)
  118. #undef DECLARE_PARSER
  119. struct TDurationParser {
  120. int cs;
  121. ui64 I;
  122. ui32 Dc;
  123. i32 MultiplierPower; // 6 for seconds, 0 for microseconds, -3 for nanoseconds
  124. i32 Multiplier;
  125. ui64 IntegerPart;
  126. ui32 FractionPart;
  127. ui32 FractionDigits;
  128. TDurationParser();
  129. bool ParsePart(const char* input, size_t len);
  130. TDuration GetResult(TDuration defaultValue) const;
  131. };
  132. /**
  133. Depcrecated cause of default hour offset (+4 hours)
  134. @see IGNIETFERRO-823
  135. */
  136. struct TDateTimeFieldsDeprecated {
  137. TDateTimeFieldsDeprecated() {
  138. Zero(*this);
  139. ZoneOffsetMinutes = (i32)TDuration::Hours(4).Minutes(); // legacy code
  140. Hour = 11;
  141. }
  142. ui32 Year;
  143. ui32 Month; // 1..12
  144. ui32 Day; // 1 .. 31
  145. ui32 Hour; // 0 .. 23
  146. ui32 Minute; // 0 .. 59
  147. ui32 Second; // 0 .. 60
  148. ui32 MicroSecond; // 0 .. 999999
  149. i32 ZoneOffsetMinutes;
  150. void SetLooseYear(ui32 year) {
  151. if (year < 60)
  152. year += 100;
  153. if (year < 160)
  154. year += 1900;
  155. Year = year;
  156. }
  157. bool IsOk() const noexcept {
  158. if (Year < 1970)
  159. return false;
  160. if (Month < 1 || Month > 12)
  161. return false;
  162. unsigned int maxMonthDay = 31;
  163. if (Month == 4 || Month == 6 || Month == 9 || Month == 11) {
  164. maxMonthDay = 30;
  165. } else if (Month == 2) {
  166. if (Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0))
  167. // leap year
  168. maxMonthDay = 29;
  169. else
  170. maxMonthDay = 28;
  171. }
  172. if (Day > maxMonthDay)
  173. return false;
  174. if (Hour > 23)
  175. return false;
  176. if (Minute > 59)
  177. return false;
  178. if (Second > 60)
  179. return false;
  180. if (MicroSecond > 999999)
  181. return false;
  182. if (Year == 1970 && Month == 1 && Day == 1) {
  183. if ((i64)(3600 * Hour + 60 * Minute + Second) < (60 * ZoneOffsetMinutes))
  184. return false;
  185. }
  186. return true;
  187. }
  188. TInstant ToInstant(TInstant defaultValue) const {
  189. time_t tt = ToTimeT(-1);
  190. if (tt == -1)
  191. return defaultValue;
  192. return TInstant::Seconds(tt) + TDuration::MicroSeconds(MicroSecond);
  193. }
  194. time_t ToTimeT(time_t defaultValue) const {
  195. if (!IsOk())
  196. return defaultValue;
  197. struct tm tm;
  198. Zero(tm);
  199. tm.tm_year = Year - 1900;
  200. tm.tm_mon = Month - 1;
  201. tm.tm_mday = Day;
  202. tm.tm_hour = Hour;
  203. tm.tm_min = Minute;
  204. tm.tm_sec = Second;
  205. time_t tt = TimeGM(&tm);
  206. if (tt == -1)
  207. return defaultValue;
  208. return tt - ZoneOffsetMinutes * 60;
  209. }
  210. };
  211. class TDateTimeParserBaseDeprecated {
  212. public:
  213. const TDateTimeFieldsDeprecated& GetDateTimeFields() const {
  214. return DateTimeFields;
  215. }
  216. protected:
  217. TDateTimeFieldsDeprecated DateTimeFields;
  218. int cs; // for ragel
  219. int Sign;
  220. int I;
  221. int Dc;
  222. protected:
  223. TDateTimeParserBaseDeprecated()
  224. : DateTimeFields()
  225. , cs(0)
  226. , Sign(0)
  227. , I(0xDEADBEEF) // to guarantee unittest break if ragel code is incorrect
  228. , Dc(0xDEADBEEF)
  229. {
  230. }
  231. inline TInstant GetResult(int firstFinalState, TInstant defaultValue) const {
  232. if (cs < firstFinalState)
  233. return defaultValue;
  234. return DateTimeFields.ToInstant(defaultValue);
  235. }
  236. };
  237. #define DECLARE_PARSER(CLASS) \
  238. struct CLASS: public TDateTimeParserBaseDeprecated { \
  239. CLASS(); \
  240. bool ParsePart(const char* input, size_t len); \
  241. TInstant GetResult(TInstant defaultValue) const; \
  242. };
  243. DECLARE_PARSER(TIso8601DateTimeParserDeprecated)
  244. DECLARE_PARSER(TRfc822DateTimeParserDeprecated)
  245. DECLARE_PARSER(THttpDateTimeParserDeprecated)
  246. DECLARE_PARSER(TX509ValidityDateTimeParserDeprecated)
  247. DECLARE_PARSER(TX509Validity4yDateTimeParserDeprecated)
  248. #undef DECLARE_PARSER