diff --git a/src/lua/datetime.lua b/src/lua/datetime.lua index 7b494fe92bd666b103a7820aa2f9718f848501d3..c581e170d37f3751f0c37f5170f3d71619fae86a 100644 --- a/src/lua/datetime.lua +++ b/src/lua/datetime.lua @@ -42,7 +42,7 @@ typedef enum { } dt_dow_t; dt_t tnt_dt_from_rdn (int n); -dt_t tnt_dt_from_ymd (int y, int m, int d); +bool tnt_dt_from_ymd_checked(int y, int m, int d, dt_t *val); void tnt_dt_to_ymd (dt_t dt, int *y, int *m, int *d); dt_dow_t tnt_dt_dow (dt_t dt); @@ -287,38 +287,16 @@ local function check_dt(dt_v, direction, v, unit) end end -local function check_ymd_range(y, M, d) - -- Fast path. Max/min year is rather theoretical. Nobody is going to - -- actually use them. - if y > MIN_DATE_YEAR and y < MAX_DATE_YEAR then - return - end - -- Slow path. - if y < MIN_DATE_YEAR then - goto min_err - elseif y > MAX_DATE_YEAR then - goto max_err - elseif y == MIN_DATE_YEAR then - if M < MIN_DATE_MONTH then - goto min_err - elseif M == MIN_DATE_MONTH and d < MIN_DATE_DAY then - goto min_err - end - return - -- y == MAX_DATE_YEAR - elseif M > MAX_DATE_MONTH then - goto max_err - elseif M == MAX_DATE_MONTH and d > MAX_DATE_DAY then - goto max_err - else - return +local function dt_from_ymd_checked(y, M, d) + local pdt = date_dt_stash_take() + local is_valid = builtin.tnt_dt_from_ymd_checked(y, M, d, pdt) + if not is_valid then + date_dt_stash_put(pdt) + error(('date %4d-%02d-%02d is invalid'):format(y, M, d)) end -::min_err:: - error(('date %d-%02d-%02d is less than minimum allowed %s'): - format(y, M, d, MIN_DATE_TEXT)) -::max_err:: - error(('date %d-%02d-%02d is greater than maximum allowed %s'): - format(y, M, d, MAX_DATE_TEXT)) + local dt = pdt[0] + date_dt_stash_put(pdt) + return dt end -- check v value against maximum/minimum possible values @@ -619,8 +597,7 @@ local function datetime_new(obj) format(d, M, y), 3) end end - check_ymd_range(y, M, d) - dt = builtin.tnt_dt_from_ymd(y, M, d) + dt = dt_from_ymd_checked(y, M, d) end -- .hour, .minute, .second @@ -1036,7 +1013,7 @@ local function datetime_ymd_update(self, y, M, d, new_offset) format(d, M, y), 3) end end - local dt = builtin.tnt_dt_from_ymd(y, M, d) + local dt = dt_from_ymd_checked(y, M, d) datetime_update_dt(self, dt, new_offset) end @@ -1159,7 +1136,6 @@ local function datetime_set(self, obj) y = y or y0 M = M or M0 d = d or d0 - check_ymd_range(y, M, d) datetime_ymd_update(self, y, M, d, offset) end diff --git a/test/app-tap/datetime.test.lua b/test/app-tap/datetime.test.lua index 6d60aa5dd13300fae4a77b1e418232a278609471..5036499b5f69c4dfa3a73665d6b1581cdc7f6b4e 100755 --- a/test/app-tap/datetime.test.lua +++ b/test/app-tap/datetime.test.lua @@ -15,12 +15,8 @@ test:plan(32) -- minimum supported date - -5879610-06-22 local MIN_DATE_YEAR = -5879610 -local MIN_DATE_MONTH = 6 -local MIN_DATE_DAY = 22 -- maximum supported date - 5879611-07-11 local MAX_DATE_YEAR = 5879611 -local MAX_DATE_MONTH = 7 -local MAX_DATE_DAY = 11 local SECS_PER_DAY = 86400 local AVERAGE_DAYS_YEAR = 365.25 @@ -78,14 +74,8 @@ local function range_check_3_error(name, value, range) format(value, name, range[1], range[2], range[3]) end -local function less_than_min(y, M, d) - return ('date %d-%02d-%02d is less than minimum allowed %d-%02d-%02d'): - format(y, M, d, MIN_DATE_YEAR, MIN_DATE_MONTH, MIN_DATE_DAY) -end - -local function greater_than_max(y, M, d) - return ('date %d-%02d-%02d is greater than maximum allowed %d-%02d-%02d'): - format(y, M, d, MAX_DATE_YEAR, MAX_DATE_MONTH, MAX_DATE_DAY) +local function invalid_date(y, M, d) + return ('date %d-%02d-%02d is invalid'):format(y, M, d) end local function invalid_tz_fmt_error(val) @@ -320,17 +310,17 @@ test:test("Simple date creation by attributes - check failed", function(test) {range_check_3_error('day', 32, {-1, 1, 31}), {year = 2021, month = 6, day = 32}}, {invalid_days_in_mon(31, 6, 2021), { year = 2021, month = 6, day = 31}}, - {less_than_min(-5879610, 6, 21), + {invalid_date(-5879610, 6, 21), {year = -5879610, month = 6, day = 21}}, - {less_than_min(-5879610, 1, 1), + {invalid_date(-5879610, 1, 1), {year = -5879610, month = 1, day = 1}}, {range_check_error('year', -16009610, {MIN_DATE_YEAR, MAX_DATE_YEAR}), {year = -16009610, month = 12, day = 31}}, {range_check_error('year', 16009610, {MIN_DATE_YEAR, MAX_DATE_YEAR}), {year = 16009610, month = 1, day = 1}}, - {greater_than_max(MAX_DATE_YEAR, 9, 1), + {invalid_date(MAX_DATE_YEAR, 9, 1), {year = MAX_DATE_YEAR, month = 9, day = 1}}, - {greater_than_max(MAX_DATE_YEAR, 7, 12), + {invalid_date(MAX_DATE_YEAR, 7, 12), {year = MAX_DATE_YEAR, month = 7, day = 12}}, } for _, row in pairs(specific_errors) do @@ -1610,17 +1600,17 @@ test:test("Time invalid :set{} operations", function(test) {range_check_3_error('day', 32, {-1, 1, 31}), {year = 2021, month = 6, day = 32}}, {invalid_days_in_mon(31, 6, 2021), { month = 6, day = 31}}, - {less_than_min(-5879610, 6, 21), + {invalid_date(-5879610, 6, 21), {year = -5879610, month = 6, day = 21}}, - {less_than_min(-5879610, 1, 1), + {invalid_date(-5879610, 1, 1), {year = -5879610, month = 1, day = 1}}, {range_check_error('year', -16009610, {MIN_DATE_YEAR, MAX_DATE_YEAR}), {year = -16009610, month = 12, day = 31}}, {range_check_error('year', 16009610, {MIN_DATE_YEAR, MAX_DATE_YEAR}), {year = 16009610, month = 1, day = 1}}, - {greater_than_max(MAX_DATE_YEAR, 9, 1), + {invalid_date(MAX_DATE_YEAR, 9, 1), {year = MAX_DATE_YEAR, month = 9, day = 1}}, - {greater_than_max(MAX_DATE_YEAR, 7, 12), + {invalid_date(MAX_DATE_YEAR, 7, 12), {year = MAX_DATE_YEAR, month = 7, day = 12}}, } for _, row in pairs(specific_errors) do