diff --git a/changelogs/unreleased/gh-10391-forbid-non-integers-datetime-new.md b/changelogs/unreleased/gh-10391-forbid-non-integers-datetime-new.md new file mode 100644 index 0000000000000000000000000000000000000000..d7b560a85b279902f56913d383d95ebc01e98e11 --- /dev/null +++ b/changelogs/unreleased/gh-10391-forbid-non-integers-datetime-new.md @@ -0,0 +1,3 @@ +## bugfix/datetime + +- Forbid non-integers in `datetime.new()` (gh-10391). diff --git a/src/lua/datetime.lua b/src/lua/datetime.lua index 024cfb73d187f6eddc572343f91dc228f0e2950d..c3ee775fa2fe22a52c1494e4fdec223adbf0a24f 100644 --- a/src/lua/datetime.lua +++ b/src/lua/datetime.lua @@ -504,6 +504,7 @@ end local function get_timezone(offset, msg) if type(offset) == 'number' then + check_integer(offset, 'tzoffset') return offset elseif type(offset) == 'string' then return parse_tzoffset(offset) @@ -527,31 +528,37 @@ local function datetime_new(obj) local y = obj.year if y ~= nil then check_range(y, MIN_DATE_YEAR, MAX_DATE_YEAR, 'year') + check_integer(y, 'year') ymd = true end local M = obj.month if M ~= nil then check_range(M, 1, 12, 'month') + check_integer(M, 'month') ymd = true end local d = obj.day if d ~= nil then check_range(d, 1, 31, 'day', -1) + check_integer(d, 'day') ymd = true end local h = obj.hour if h ~= nil then check_range(h, 0, 23, 'hour') + check_integer(h, 'hour') hms = true end local m = obj.min if m ~= nil then check_range(m, 0, 59, 'min') + check_integer(m, 'min') hms = true end local s = obj.sec if s ~= nil then check_range(s, 0, 60, 'sec') + check_integer(s, 'sec') hms = true end @@ -565,12 +572,15 @@ local function datetime_new(obj) end if usec ~= nil then check_range(usec, 0, 1e6, 'usec') + check_integer(usec, 'usec') nsec = usec * 1e3 elseif msec ~= nil then check_range(msec, 0, 1e3, 'msec') + check_integer(msec, 'msec') nsec = msec * 1e6 else check_range(nsec, 0, 1e9, 'nsec') + check_integer(nsec, 'nsec') end else nsec = 0 diff --git a/test/app-tap/datetime.test.lua b/test/app-tap/datetime.test.lua index 3301fc39dea0e2f28ba40c0147bf785df80e898a..ed4532efa096e7e85834306bce3bfa86b4c63318 100755 --- a/test/app-tap/datetime.test.lua +++ b/test/app-tap/datetime.test.lua @@ -34,9 +34,11 @@ local MAX_MSEC_RANGE = math.floor(MAX_NSEC_RANGE / 1e6) local incompat_types = 'incompatible types for datetime comparison' local only_integer_ts = 'only integer values allowed in timestamp'.. ' if nsec, usec, or msecs provided' +local only_integer_msg = function(key) + return key .. ': integer value expected, but received number' +end local only_one_of = 'only one of nsec, usec or msecs may be defined'.. ' simultaneously' -local int_ival_exp = 'sec: integer value expected, but received number' local timestamp_and_ymd = 'timestamp is not allowed if year/month/day provided' local timestamp_and_hms = 'timestamp is not allowed if hour/min/sec provided' local str_or_num_exp = 'tzoffset: string or number expected, but received' @@ -271,7 +273,7 @@ test:test("Simple date creation by attributes", function(test) end) test:test("Simple date creation by attributes - check failed", function(test) - test:plan(83) + test:plan(93) local boundary_checks = { {'year', {MIN_DATE_YEAR, MAX_DATE_YEAR}}, @@ -339,6 +341,16 @@ test:test("Simple date creation by attributes - check failed", function(test) {only_one_of, { nsec = 123456, msec = 123}}, {only_one_of, { usec = 123, msec = 123}}, {only_one_of, { nsec = 123456, usec = 123, msec = 123}}, + {only_integer_msg('nsec'), { nsec = 1.1 }}, + {only_integer_msg('msec'), { msec = 1.1 }}, + {only_integer_msg('usec'), { usec = 1.1 }}, + {only_integer_msg('tzoffset'), { tzoffset = 1.1 }}, + {only_integer_msg('year'), { year = 1.1 }}, + {only_integer_msg('month'), { month = 1.1 }}, + {only_integer_msg('day'), { day = 1.1 }}, + {only_integer_msg('hour'), { hour = 1.1 }}, + {only_integer_msg('min'), { min = 1.1 }}, + {only_integer_msg('sec'), { sec = 1.1 }}, {only_integer_ts, { timestamp = 12345.125, usec = 123}}, {only_integer_ts, { timestamp = 12345.125, msec = 123}}, {only_integer_ts, { timestamp = 12345.125, nsec = 123}}, @@ -1169,9 +1181,9 @@ test:test("Time interval operations", function(test) {only_one_of, { nsec = 123456, msec = 123}}, {only_one_of, { usec = 123, msec = 123}}, {only_one_of, { nsec = 123456, usec = 123, msec = 123}}, - {int_ival_exp, { sec = 12345.125, usec = 123}}, - {int_ival_exp, { sec = 12345.125, msec = 123}}, - {int_ival_exp, { sec = 12345.125, nsec = 123}}, + {only_integer_msg('sec'), { sec = 12345.125, usec = 123}}, + {only_integer_msg('sec'), { sec = 12345.125, msec = 123}}, + {only_integer_msg('sec'), { sec = 12345.125, nsec = 123}}, } for _, row in pairs(specific_errors) do local err_msg, obj = unpack(row) @@ -1322,9 +1334,9 @@ test:test("Time intervals creation - range checks", function(test) {only_one_of, { nsec = 123456, msec = 123}}, {only_one_of, { usec = 123, msec = 123}}, {only_one_of, { nsec = 123456, usec = 123, msec = 123}}, - {int_ival_exp, { sec = 12345.125, usec = 123}}, - {int_ival_exp, { sec = 12345.125, msec = 123}}, - {int_ival_exp, { sec = 12345.125, nsec = 123}}, + {only_integer_msg('sec'), { sec = 12345.125, usec = 123}}, + {only_integer_msg('sec'), { sec = 12345.125, msec = 123}}, + {only_integer_msg('sec'), { sec = 12345.125, nsec = 123}}, {table_expected('interval.new()', '2001-01-01'), '2001-01-01'}, {table_expected('interval.new()', 20010101), 20010101}, {range_check_error('year', 1e21, {-MAX_YEAR_RANGE, MAX_YEAR_RANGE}),