#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <ctime>
#include <numeric>
#include <util/datetime/parser.h>
#include <util/generic/ymath.h>
%%{
machine DateTimeParserCommon;
sp = ' ';
action clear_int {
I = 0;
Dc = 0;
}
action update_int {
I = I * 10 + (fc - '0');
++Dc;
}
int = (digit+)
>clear_int
$update_int;
int1 = digit
>clear_int
$update_int;
int2 = (digit digit)
>clear_int
$update_int;
int3 = (digit digit digit)
>clear_int
$update_int;
int4 = (digit digit digit digit)
>clear_int
$update_int;
int12 = (digit digit?)
>clear_int
$update_int;
int24 = ( digit digit ( digit digit )? )
>clear_int
$update_int;
# According to both RFC2822 and RFC2616 dates MUST be case-sensitive,
# but Andrey fomichev@ wants relaxed parser
month3 =
'Jan'i %{ DateTimeFields.Month = 1; }
| 'Feb'i %{ DateTimeFields.Month = 2; }
| 'Mar'i %{ DateTimeFields.Month = 3; }
| 'Apr'i %{ DateTimeFields.Month = 4; }
| 'May'i %{ DateTimeFields.Month = 5; }
| 'Jun'i %{ DateTimeFields.Month = 6; }
| 'Jul'i %{ DateTimeFields.Month = 7; }
| 'Aug'i %{ DateTimeFields.Month = 8; }
| 'Sep'i %{ DateTimeFields.Month = 9; }
| 'Oct'i %{ DateTimeFields.Month = 10; }
| 'Nov'i %{ DateTimeFields.Month = 11; }
| 'Dec'i %{ DateTimeFields.Month = 12; };
wkday = 'Mon'i | 'Tue'i | 'Wed'i | 'Thu'i | 'Fri'i | 'Sat'i | 'Sun'i;
weekday = 'Monday'i | 'Tuesday'i | 'Wednesday'i | 'Thursday'i
| 'Friday'i | 'Saturday'i | 'Sunday'i;
action set_second { DateTimeFields.Second = I; }
action set_minute { DateTimeFields.Minute = I; }
action set_hour { DateTimeFields.Hour = I; }
action set_day { DateTimeFields.Day = I; }
action set_month { DateTimeFields.Month = I; }
action set_year { DateTimeFields.SetLooseYear(I); }
action set_precise_year { DateTimeFields.Year = I; }
action set_zone_utc { DateTimeFields.ZoneOffsetMinutes = 0; }
}%%
%%{
machine RFC822DateParser;
################# RFC 2822 3.3 Full Date ###################
include DateTimeParserCommon;
ws1 = (space+);
ws0 = (space*);
dow_spec = ( wkday ',' )?;
day = int12 %set_day;
year = int24 %set_year;
# actually it must be from 0 to 23
hour = int2 %set_hour;
# actually it must be from 0 to 59
min = int2 %set_minute;
# actually it must be from 0 to 59
sec = int2 %set_second;
sec_spec = ( ':' . sec )?;
# so called "military zone offset". I hardly believe someone uses it now, but we MUST respect RFc822
action set_mil_offset {
char c = (char)toupper(fc);
if (c == 'Z')
DateTimeFields.ZoneOffsetMinutes = 0;
else {
if (c <= 'M') {
// ['A'..'M'] \ 'J'
if (c < 'J')
DateTimeFields.ZoneOffsetMinutes = (i32)TDuration::Hours(c - 'A' + 1).Minutes();
else
DateTimeFields.ZoneOffsetMinutes = (i32)TDuration::Hours(c - 'A').Minutes();
} else {
// ['N'..'Y']
DateTimeFields.ZoneOffsetMinutes = -(i32)TDuration::Hours(c - 'N' + 1).Minutes();
}
}
}
action set_digit_offset {
DateTimeFields.ZoneOffsetMinutes = Sign * (i32)(TDuration::Hours(I / 100) + TDuration::Minutes(I % 100)).Minutes();
}
mil_zone = /[A-IK-Za-ik-z]/ $set_mil_offset;
# actions % were replaced with @ (when the script was migrated to ragel 5.24)
# because ragel 5.24 does not call to the % action if it be called at the very end of string.
# it is a bug in ragel 5 because ragel 6.2 works correctly with % at the end of string.
# see http://www.complang.org/ragel/ChangeLog.
zone = 'UT' @{ DateTimeFields.ZoneOffsetMinutes = 0; }
| 'GMT' @{ DateTimeFields.ZoneOffsetMinutes = 0; }
| 'EST' @{ DateTimeFields.ZoneOffsetMinutes = -(i32)TDuration::Hours(5).Minutes();}
| 'EDT' @{ DateTimeFields.ZoneOffsetMinutes = -(i32)TDuration::Hours(4).Minutes(); }
| 'CST' @{ DateTimeFields.ZoneOffsetMinutes = -(i32)TDuration::Hours(6).Minutes();}
| 'CDT' @{ DateTimeFields.ZoneOffsetMinutes = -(i32)TDuration::Hours(5).Minutes(); }
| 'MST' @{ DateTimeFields.ZoneOffsetMinutes = -(i32)TDuration::Hours(7).Minutes();}
| 'MDT' @{ DateTimeFields.ZoneOffsetMinutes = -(i32)TDuration::Hours(6).Minutes(); }
| 'PST' @{ DateTimeFields.ZoneOffsetMinutes = -(i32)TDuration::Hours(8).Minutes();}
| 'PDT' @{ DateTimeFields.ZoneOffsetMinutes = -(i32)TDuration::Hours(7).Minutes(); };
digit_offset = ('+' | '-') > { Sign = fc == '+' ? 1 : -1; } . int4 @set_digit_offset;
offset = ( zone | mil_zone | digit_offset );
rfc822datetime = ws0 . dow_spec . ws0 . day . ws1 . month3 . ws1 . year . ws1 . hour . ':' . min . sec_spec . ws1 . offset . ws0;
main := rfc822datetime;
write data noerror;
}%%
TRfc822DateTimeParserDeprecated::TRfc822DateTimeParserDeprecated() {
%% write init;
}
bool TRfc822DateTimeParserDeprecated::ParsePart(const char* input, size_t len) {
const char* p = input;
const char* pe = input + len;
%% write exec;
return cs != %%{ write error; }%%;
}
TRfc822DateTimeParser::TRfc822DateTimeParser() {
%% write init;
}
bool TRfc822DateTimeParser::ParsePart(const char* input, size_t len) {
const char* p = input;
const char* pe = input + len;
%% write exec;
return cs != %%{ write error; }%%;
}
%%{
machine ISO8601DateTimeParser;
include DateTimeParserCommon;
year = int4 @set_precise_year;
month = int2 @set_month;
day = int2 @set_day;
hour = int2 @set_hour;
minute = int2 @set_minute;
second = int2 @set_second;
secondFrac = digit {1,6} >clear_int $update_int @{
ui32 us = I;
for (int k = Dc; k < 6; ++k) {
us *= 10;
}
DateTimeFields.MicroSecond = us;
};
secondFracTail = (digit*);
zoneZ = [Zz] @set_zone_utc;
zoneOffset = space? . ('+' | '-') >{ Sign = fc == '+' ? 1 : -1; } . int2 @{ DateTimeFields.ZoneOffsetMinutes = Sign * (i32)TDuration::Hours(I).Minutes(); } . (':')? . (int2 @{ DateTimeFields.ZoneOffsetMinutes += I * Sign; })?;
zone = zoneZ | zoneOffset;
iso8601date = (year . '-' . month . '-' . day) | (year . month . day);
iso8601time = (hour . ':' . minute . (':' . second ('.' secondFrac secondFracTail)?)?) | (hour . minute . second?);
iso8601datetime = iso8601date . ([Tt ] . iso8601time . zone?)?;
main := iso8601datetime;
write data noerror;
}%%
TIso8601DateTimeParserDeprecated::TIso8601DateTimeParserDeprecated() {
%% write init;
}
bool TIso8601DateTimeParserDeprecated::ParsePart(const char* input, size_t len) {
const char* p = input;
const char* pe = input + len;
%% write exec;
return cs != %%{ write error; }%%;
}
TIso8601DateTimeParser::TIso8601DateTimeParser() {
%% write init;
}
bool TIso8601DateTimeParser::ParsePart(const char* input, size_t len) {
const char* p = input;
const char* pe = input + len;
%% write exec;
return cs != %%{ write error; }%%;
}
%%{
machine HttpDateTimeParser;
include DateTimeParserCommon;
################# RFC 2616 3.3.1 Full Date #################
time = int2 %set_hour ':' int2 %set_minute ':' int2 %set_second;
date1 = int2 %set_day ' ' month3 ' ' int4 %set_year;
date2 = int2 %set_day '-' month3 '-' int2 %set_year;
date3 = month3 sp (int2 | sp int1) %set_day;
rfc1123_date = wkday ',' sp date1 sp time sp 'GMT'i;
rfc850_date = weekday ',' sp date2 sp time sp 'GMT'i;
asctime_date = wkday sp date3 sp time sp int4 @set_year;
http_date = (rfc1123_date | rfc850_date | asctime_date) @set_zone_utc;
}%%
%%{
machine HttpDateTimeParserStandalone;
include HttpDateTimeParser;
main := http_date;
write data noerror;
}%%
THttpDateTimeParserDeprecated::THttpDateTimeParserDeprecated() {
%% write init;
}
bool THttpDateTimeParserDeprecated::ParsePart(const char* input, size_t len) {
const char* p = input;
const char* pe = input + len;
%% write exec;
return cs != %%{ write error; }%%;
}
THttpDateTimeParser::THttpDateTimeParser() {
%% write init;
}
bool THttpDateTimeParser::ParsePart(const char* input, size_t len) {
const char* p = input;
const char* pe = input + len;
%% write exec;
return cs != %%{ write error; }%%;
}
%%{
machine X509ValidityDateTimeParser;
include DateTimeParserCommon;
################# X.509 certificate validity time (see rfc5280 4.1.2.5.*) #################
year = int2 @{ DateTimeFields.Year = (I < 50 ? I + 2000 : I + 1900); };
month = int2 @set_month;
day = int2 @set_day;
hour = int2 @set_hour;
minute = int2 @set_minute;
second = int2 @set_second;
zone = 'Z' @set_zone_utc;
main := year . month . day . hour . minute . second . zone;
write data noerror;
}%%
TX509ValidityDateTimeParserDeprecated::TX509ValidityDateTimeParserDeprecated() {
%% write init;
}
bool TX509ValidityDateTimeParserDeprecated::ParsePart(const char *input, size_t len) {
const char *p = input;
const char *pe = input + len;
%% write exec;
return cs != %%{ write error; }%%;
}
TX509ValidityDateTimeParser::TX509ValidityDateTimeParser() {
%% write init;
}
bool TX509ValidityDateTimeParser::ParsePart(const char *input, size_t len) {
const char *p = input;
const char *pe = input + len;
%% write exec;
return cs != %%{ write error; }%%;
}
%%{
machine X509Validity4yDateTimeParser;
include DateTimeParserCommon;
year = int4 @{ DateTimeFields.Year = I; };
month = int2 @set_month;
day = int2 @set_day;
hour = int2 @set_hour;
minute = int2 @set_minute;
second = int2 @set_second;
zone = 'Z' @set_zone_utc;
main := year . month . day . hour . minute . second . zone;
write data noerror;
}%%
TX509Validity4yDateTimeParserDeprecated::TX509Validity4yDateTimeParserDeprecated() {
%% write init;
}
bool TX509Validity4yDateTimeParserDeprecated::ParsePart(const char *input, size_t len) {
const char *p = input;
const char *pe = input + len;
%% write exec;
return cs != %%{ write error; }%%;
}
TX509Validity4yDateTimeParser::TX509Validity4yDateTimeParser() {
%% write init;
}
bool TX509Validity4yDateTimeParser::ParsePart(const char *input, size_t len) {
const char *p = input;
const char *pe = input + len;
%% write exec;
return cs != %%{ write error; }%%;
}
TInstant TIso8601DateTimeParserDeprecated::GetResult(TInstant defaultValue) const {
Y_UNUSED(ISO8601DateTimeParser_en_main);
return TDateTimeParserBaseDeprecated::GetResult(ISO8601DateTimeParser_first_final, defaultValue);
}
TInstant TRfc822DateTimeParserDeprecated::GetResult(TInstant defaultValue) const {
Y_UNUSED(RFC822DateParser_en_main);
return TDateTimeParserBaseDeprecated::GetResult(RFC822DateParser_first_final, defaultValue);
}
TInstant THttpDateTimeParserDeprecated::GetResult(TInstant defaultValue) const {
Y_UNUSED(HttpDateTimeParserStandalone_en_main);
return TDateTimeParserBaseDeprecated::GetResult(HttpDateTimeParserStandalone_first_final, defaultValue);
}
TInstant TX509ValidityDateTimeParserDeprecated::GetResult(TInstant defaultValue) const {
Y_UNUSED(X509ValidityDateTimeParser_en_main);
return TDateTimeParserBaseDeprecated::GetResult(X509ValidityDateTimeParser_first_final, defaultValue);
}
TInstant TX509Validity4yDateTimeParserDeprecated::GetResult(TInstant defaultValue) const {
Y_UNUSED(X509Validity4yDateTimeParser_en_main);
return TDateTimeParserBaseDeprecated::GetResult(X509Validity4yDateTimeParser_first_final, defaultValue);
}
TInstant TIso8601DateTimeParser::GetResult(TInstant defaultValue) const {
Y_UNUSED(ISO8601DateTimeParser_en_main);
return TDateTimeParserBase::GetResult(ISO8601DateTimeParser_first_final, defaultValue);
}
TInstant TRfc822DateTimeParser::GetResult(TInstant defaultValue) const {
Y_UNUSED(RFC822DateParser_en_main);
return TDateTimeParserBase::GetResult(RFC822DateParser_first_final, defaultValue);
}
TInstant THttpDateTimeParser::GetResult(TInstant defaultValue) const {
Y_UNUSED(HttpDateTimeParserStandalone_en_main);
return TDateTimeParserBase::GetResult(HttpDateTimeParserStandalone_first_final, defaultValue);
}
TInstant TX509ValidityDateTimeParser::GetResult(TInstant defaultValue) const {
Y_UNUSED(X509ValidityDateTimeParser_en_main);
return TDateTimeParserBase::GetResult(X509ValidityDateTimeParser_first_final, defaultValue);
}
TInstant TX509Validity4yDateTimeParser::GetResult(TInstant defaultValue) const {
Y_UNUSED(X509Validity4yDateTimeParser_en_main);
return TDateTimeParserBase::GetResult(X509Validity4yDateTimeParser_first_final, defaultValue);
}
template<class TParser, class TResult>
static inline TResult Parse(const char* input, size_t len, TResult defaultValue) {
TParser parser;
if (!parser.ParsePart(input, len))
return defaultValue;
return parser.GetResult(defaultValue);
}
template<class TParser, class TResult, bool ThrowExceptionOnFailure = true>
static inline TResult ParseUnsafe(const char* input, size_t len) {
TResult r = Parse<TParser, TResult>(input, len, TResult::Max());
if (ThrowExceptionOnFailure && r == TResult::Max())
ythrow TDateTimeParseException() << "error in datetime parsing. Input data: " << TStringBuf(input, len);
return r;
}
TInstant TInstant::ParseIso8601Deprecated(const TStringBuf input) {
return ParseUnsafe<TIso8601DateTimeParserDeprecated, TInstant>(input.data(), input.size());
}
TInstant TInstant::ParseRfc822Deprecated(const TStringBuf input) {
return ParseUnsafe<TRfc822DateTimeParserDeprecated, TInstant>(input.data(), input.size());
}
TInstant TInstant::ParseHttpDeprecated(const TStringBuf input) {
return ParseUnsafe<THttpDateTimeParserDeprecated, TInstant>(input.data(), input.size());
}
TInstant TInstant::ParseX509ValidityDeprecated(const TStringBuf input) {
switch (input.size()) {
case 13:
return ParseUnsafe<TX509ValidityDateTimeParserDeprecated, TInstant>(input.data(), 13);
case 15:
return ParseUnsafe<TX509Validity4yDateTimeParserDeprecated, TInstant>(input.data(), 15);
default:
ythrow TDateTimeParseException();
}
}
bool TInstant::TryParseIso8601Deprecated(const TStringBuf input, TInstant& instant) {
const auto parsed = ParseUnsafe<TIso8601DateTimeParserDeprecated, TInstant, false>(input.data(), input.size());
if (TInstant::Max() == parsed) {
return false;
}
instant = parsed;
return true;
}
bool TInstant::TryParseRfc822Deprecated(const TStringBuf input, TInstant& instant) {
const auto parsed = ParseUnsafe<TRfc822DateTimeParserDeprecated, TInstant, false>(input.data(), input.size());
if (TInstant::Max() == parsed) {
return false;
}
instant = parsed;
return true;
}
bool TInstant::TryParseHttpDeprecated(const TStringBuf input, TInstant& instant) {
const auto parsed = ParseUnsafe<THttpDateTimeParserDeprecated, TInstant, false>(input.data(), input.size());
if (TInstant::Max() == parsed) {
return false;
}
instant = parsed;
return true;
}
bool TInstant::TryParseX509Deprecated(const TStringBuf input, TInstant& instant) {
TInstant parsed;
switch (input.size()) {
case 13:
parsed = ParseUnsafe<TX509ValidityDateTimeParserDeprecated, TInstant, false>(input.data(), 13);
break;
case 15:
parsed = ParseUnsafe<TX509Validity4yDateTimeParserDeprecated, TInstant, false>(input.data(), 15);
break;
default:
return false;
}
if (TInstant::Max() == parsed) {
return false;
}
instant = parsed;
return true;
}
TInstant TInstant::ParseIso8601(const TStringBuf input) {
return ParseUnsafe<TIso8601DateTimeParser, TInstant>(input.data(), input.size());
}
TInstant TInstant::ParseRfc822(const TStringBuf input) {
return ParseUnsafe<TRfc822DateTimeParser, TInstant>(input.data(), input.size());
}
TInstant TInstant::ParseHttp(const TStringBuf input) {
return ParseUnsafe<THttpDateTimeParser, TInstant>(input.data(), input.size());
}
TInstant TInstant::ParseX509Validity(const TStringBuf input) {
switch (input.size()) {
case 13:
return ParseUnsafe<TX509ValidityDateTimeParser, TInstant>(input.data(), 13);
case 15:
return ParseUnsafe<TX509Validity4yDateTimeParser, TInstant>(input.data(), 15);
default:
ythrow TDateTimeParseException();
}
}
bool TInstant::TryParseIso8601(const TStringBuf input, TInstant& instant) {
const auto parsed = ParseUnsafe<TIso8601DateTimeParser, TInstant, false>(input.data(), input.size());
if (TInstant::Max() == parsed) {
return false;
}
instant = parsed;
return true;
}
bool TInstant::TryParseRfc822(const TStringBuf input, TInstant& instant) {
const auto parsed = ParseUnsafe<TRfc822DateTimeParser, TInstant, false>(input.data(), input.size());
if (TInstant::Max() == parsed) {
return false;
}
instant = parsed;
return true;
}
bool TInstant::TryParseHttp(const TStringBuf input, TInstant& instant) {
const auto parsed = ParseUnsafe<THttpDateTimeParser, TInstant, false>(input.data(), input.size());
if (TInstant::Max() == parsed) {
return false;
}
instant = parsed;
return true;
}
bool TInstant::TryParseX509(const TStringBuf input, TInstant& instant) {
TInstant parsed;
switch (input.size()) {
case 13:
parsed = ParseUnsafe<TX509ValidityDateTimeParser, TInstant, false>(input.data(), 13);
break;
case 15:
parsed = ParseUnsafe<TX509Validity4yDateTimeParser, TInstant, false>(input.data(), 15);
break;
default:
return false;
}
if (TInstant::Max() == parsed) {
return false;
}
instant = parsed;
return true;
}
bool ParseRFC822DateTimeDeprecated(const char* input, time_t& utcTime) {
return ParseRFC822DateTimeDeprecated(input, strlen(input), utcTime);
}
bool ParseISO8601DateTimeDeprecated(const char* input, time_t& utcTime) {
return ParseISO8601DateTimeDeprecated(input, strlen(input), utcTime);
}
bool ParseHTTPDateTimeDeprecated(const char* input, time_t& utcTime) {
return ParseHTTPDateTimeDeprecated(input, strlen(input), utcTime);
}
bool ParseX509ValidityDateTimeDeprecated(const char* input, time_t& utcTime) {
return ParseX509ValidityDateTimeDeprecated(input, strlen(input), utcTime);
}
bool ParseRFC822DateTimeDeprecated(const char* input, size_t inputLen, time_t& utcTime) {
try {
utcTime = ParseUnsafe<TRfc822DateTimeParserDeprecated, TInstant>(input, inputLen).TimeT();
return true;
} catch (const TDateTimeParseException&) {
return false;
}
}
bool ParseISO8601DateTimeDeprecated(const char* input, size_t inputLen, time_t& utcTime) {
try {
utcTime = ParseUnsafe<TIso8601DateTimeParserDeprecated, TInstant>(input, inputLen).TimeT();
return true;
} catch (const TDateTimeParseException&) {
return false;
}
}
bool ParseHTTPDateTimeDeprecated(const char* input, size_t inputLen, time_t& utcTime) {
try {
utcTime = ParseUnsafe<THttpDateTimeParserDeprecated, TInstant>(input, inputLen).TimeT();
return true;
} catch (const TDateTimeParseException&) {
return false;
}
}
bool ParseX509ValidityDateTimeDeprecated(const char* input, size_t inputLen, time_t& utcTime) {
TInstant r;
switch (inputLen) {
case 13:
r = Parse<TX509ValidityDateTimeParserDeprecated, TInstant>(input, 13, TInstant::Max());
break;
case 15:
r = Parse<TX509Validity4yDateTimeParserDeprecated, TInstant>(input, 15, TInstant::Max());
break;
default:
return false;
}
if (r == TInstant::Max())
return false;
utcTime = r.TimeT();
return true;
}
bool ParseRFC822DateTime(const char* input, time_t& utcTime) {
return ParseRFC822DateTime(input, strlen(input), utcTime);
}
bool ParseISO8601DateTime(const char* input, time_t& utcTime) {
return ParseISO8601DateTime(input, strlen(input), utcTime);
}
bool ParseHTTPDateTime(const char* input, time_t& utcTime) {
return ParseHTTPDateTime(input, strlen(input), utcTime);
}
bool ParseX509ValidityDateTime(const char* input, time_t& utcTime) {
return ParseX509ValidityDateTime(input, strlen(input), utcTime);
}
bool ParseRFC822DateTime(const char* input, size_t inputLen, time_t& utcTime) {
try {
utcTime = ParseUnsafe<TRfc822DateTimeParser, TInstant>(input, inputLen).TimeT();
return true;
} catch (const TDateTimeParseException&) {
return false;
}
}
bool ParseISO8601DateTime(const char* input, size_t inputLen, time_t& utcTime) {
try {
utcTime = ParseUnsafe<TIso8601DateTimeParser, TInstant>(input, inputLen).TimeT();
return true;
} catch (const TDateTimeParseException&) {
return false;
}
}
bool ParseHTTPDateTime(const char* input, size_t inputLen, time_t& utcTime) {
try {
utcTime = ParseUnsafe<THttpDateTimeParser, TInstant>(input, inputLen).TimeT();
return true;
} catch (const TDateTimeParseException&) {
return false;
}
}
bool ParseX509ValidityDateTime(const char* input, size_t inputLen, time_t& utcTime) {
TInstant r;
switch (inputLen) {
case 13:
r = Parse<TX509ValidityDateTimeParser, TInstant>(input, 13, TInstant::Max());
break;
case 15:
r = Parse<TX509Validity4yDateTimeParser, TInstant>(input, 15, TInstant::Max());
break;
default:
return false;
}
if (r == TInstant::Max())
return false;
utcTime = r.TimeT();
return true;
}
%%{
machine TDurationParser;
include DateTimeParserCommon;
multiplier
= '' # >{ MultiplierPower = 6; } # work around Ragel bugs
| 'w' @{ MultiplierPower = 6; Multiplier = 604800; }
| 'd' @{ MultiplierPower = 6; Multiplier = 86400; }
| 'h' @{ MultiplierPower = 6; Multiplier = 3600; }
| 'm' @{ MultiplierPower = 6; Multiplier = 60; }
| 's' @{ MultiplierPower = 6; Multiplier = 1; }
| 'ms' @{ MultiplierPower = 3; Multiplier = 1; }
| 'us' @{ MultiplierPower = 0; Multiplier = 1; }
| 'ns' @{ MultiplierPower = -3; Multiplier = 1; }
;
integer = int @{ IntegerPart = I; };
fraction = '.' digit {1,6} >clear_int $update_int @{ FractionPart = I; FractionDigits = Dc; } digit*;
duration = integer fraction? multiplier;
main := duration;
write data noerror;
}%%
TDurationParser::TDurationParser()
: cs(0)
, I(0)
, Dc(0)
, MultiplierPower(6)
, Multiplier(1)
, IntegerPart(0)
, FractionPart(0)
, FractionDigits(0)
{
Y_UNUSED(TDurationParser_en_main);
%% write init;
}
bool TDurationParser::ParsePart(const char* input, size_t len) {
const char* p = input;
const char* pe = input + len;
%% write exec;
return cs != %%{ write error; }%%;
}
static inline ui64 DecPower(ui64 part, i32 power) {
if (power >= 0)
return part * Power(10, power);
return part / Power(10, -power);
}
TDuration TDurationParser::GetResult(TDuration defaultValue) const {
if (cs < TDurationParser_first_final)
return defaultValue;
ui64 us = 0;
us += Multiplier * DecPower(IntegerPart, MultiplierPower);
us += Multiplier * DecPower(FractionPart, MultiplierPower - FractionDigits);
return TDuration::MicroSeconds(us);
}
bool TDuration::TryParse(const TStringBuf input, TDuration& result) {
TDuration r = ::Parse<TDurationParser, TDuration>(input.data(), input.size(), TDuration::Max());
if (r == TDuration::Max())
return false;
result = r;
return true;
}
TDuration TDuration::Parse(const TStringBuf input) {
return ParseUnsafe<TDurationParser, TDuration>(input.data(), input.size());
}