|
1 | 1 | local t = require('luatest')
|
| 2 | +local compat = require('compat') |
2 | 3 | local dt = require('datetime')
|
3 | 4 |
|
4 | 5 | local SUPPORTED_DATETIME_FORMATS = {
|
@@ -2098,3 +2099,151 @@ for supported_by, standard_cases in pairs(UNSUPPORTED_DATETIME_FORMATS) do
|
2098 | 2099 | end
|
2099 | 2100 | end
|
2100 | 2101 | end
|
| 2102 | + |
| 2103 | +local TIMEZONES = { |
| 2104 | + { |
| 2105 | + tzname = 'Europe/Moscow', |
| 2106 | + tzoffset = 180, |
| 2107 | + }, |
| 2108 | + { |
| 2109 | + tzname = 'Africa/Abidjan', |
| 2110 | + tzoffset = 0, |
| 2111 | + }, |
| 2112 | + { |
| 2113 | + tzname = 'America/Argentina/Buenos_Aires', |
| 2114 | + tzoffset = -180, |
| 2115 | + }, |
| 2116 | + { |
| 2117 | + tzname = 'Asia/Krasnoyarsk', |
| 2118 | + tzoffset = 420, |
| 2119 | + }, |
| 2120 | + { |
| 2121 | + tzname = 'Pacific/Fiji', |
| 2122 | + tzoffset = 720, |
| 2123 | + } |
| 2124 | +} |
| 2125 | + |
| 2126 | +local function tz_behavior_test_case(behavior, create_dt, expected) |
| 2127 | + return function() |
| 2128 | + t.assert_equals(compat.datetime_apply_timezone_action.default, 'old') |
| 2129 | + compat.datetime_apply_timezone_action = behavior |
| 2130 | + |
| 2131 | + local dt_obj = create_dt() |
| 2132 | + |
| 2133 | + t.assert_equals(dt_obj.hour, expected.hour) |
| 2134 | + t.assert_equals(dt_obj.timestamp, expected.timestamp) |
| 2135 | + end |
| 2136 | +end |
| 2137 | + |
| 2138 | +-- Test new/old behavior of applying timezone to datetime objects |
| 2139 | +-- (gh-10363). |
| 2140 | +for _, case in ipairs(TIMEZONES) do |
| 2141 | + local function add_test_case(method, behavior, ...) |
| 2142 | + local testcase_name = ('test_tz_behavior_%s_%s_%s') |
| 2143 | + :format(method, case.tzname:gsub('/', '_'), behavior) |
| 2144 | + pg[testcase_name] = tz_behavior_test_case(behavior, ...) |
| 2145 | + end |
| 2146 | + |
| 2147 | + local now = dt.now() |
| 2148 | + local create_dt = function() |
| 2149 | + return dt.new{timestamp = now.timestamp, tzoffset = case.tzoffset} |
| 2150 | + end |
| 2151 | + |
| 2152 | + -- Note the following when using old behavior. |
| 2153 | + -- |
| 2154 | + -- `datetime.new{timestamp = now.timestamp, |
| 2155 | + -- tzoffset = now.tzoffset}` |
| 2156 | + -- creates a datetime object representing time of day in GMT+0 |
| 2157 | + -- corresponding to `now` time of day within the provided |
| 2158 | + -- timezone instead of creating a complete `now` copy. |
| 2159 | + add_test_case('create_tzoffset', 'old', create_dt, |
| 2160 | + {hour = now.hour - now.tzoffset / 60, |
| 2161 | + timestamp = now.timestamp - case.tzoffset * 60}) |
| 2162 | + add_test_case('create_tzoffset', 'new', create_dt, |
| 2163 | + {hour = (now.hour + (case.tzoffset - now.tzoffset) / 60) % 24, |
| 2164 | + timestamp = now.timestamp}) |
| 2165 | + |
| 2166 | + create_dt = function() |
| 2167 | + return dt.new{timestamp = now.timestamp, tz = case.tzname} |
| 2168 | + end |
| 2169 | + |
| 2170 | + add_test_case('create_tzname', 'old', create_dt, |
| 2171 | + {hour = now.hour - now.tzoffset / 60, |
| 2172 | + timestamp = now.timestamp - case.tzoffset * 60}) |
| 2173 | + add_test_case('create_tzname', 'new', create_dt, |
| 2174 | + {hour = (now.hour + (case.tzoffset - now.tzoffset) / 60) % 24, |
| 2175 | + timestamp = now.timestamp}) |
| 2176 | + |
| 2177 | + create_dt = function() |
| 2178 | + return dt.new{timestamp = now.timestamp}:set{tzoffset = case.tzoffset} |
| 2179 | + end |
| 2180 | + |
| 2181 | + add_test_case('set_tzoffset', 'old', create_dt, |
| 2182 | + {hour = now.hour - now.tzoffset / 60, |
| 2183 | + timestamp = now.timestamp - case.tzoffset * 60}) |
| 2184 | + add_test_case('set_tzoffset', 'new', create_dt, |
| 2185 | + {hour = (now.hour + (case.tzoffset - now.tzoffset) / 60) % 24, |
| 2186 | + timestamp = now.timestamp}) |
| 2187 | + |
| 2188 | + create_dt = function() |
| 2189 | + return dt.new{timestamp = now.timestamp}:set{tz = case.tzname} |
| 2190 | + end |
| 2191 | + |
| 2192 | + add_test_case('set_tzname', 'old', create_dt, |
| 2193 | + {hour = now.hour - now.tzoffset / 60, |
| 2194 | + timestamp = now.timestamp - case.tzoffset * 60}) |
| 2195 | + add_test_case('set_tzname', 'new', create_dt, |
| 2196 | + {hour = (now.hour + (case.tzoffset - now.tzoffset) / 60) % 24, |
| 2197 | + timestamp = now.timestamp}) |
| 2198 | +end |
| 2199 | + |
| 2200 | +-- Test new/old behavior of applying timezone when parsing |
| 2201 | +-- datetime objects. |
| 2202 | +-- Adapted from app-tap/datetime.test.lua (gh-10363). |
| 2203 | +pg.test_new_tz_behavior_parse = function() |
| 2204 | + t.assert_equals(compat.datetime_apply_timezone_action.default, 'old') |
| 2205 | + compat.datetime_apply_timezone_action = 'new' |
| 2206 | + |
| 2207 | + t.assert(dt.parse("1970-01-01T01:00:00Z") == |
| 2208 | + dt.new{year=1970, mon=1, day=1, hour=1, min=0, sec=0}) |
| 2209 | + t.assert(dt.parse("1970-01-01T01:00:00Z", {format = 'iso8601'}) == |
| 2210 | + dt.new{year=1970, mon=1, day=1, hour=1, min=0, sec=0}) |
| 2211 | + t.assert(dt.parse("1970-01-01T01:00:00Z", {format = 'rfc3339'}) == |
| 2212 | + dt.new{year=1970, mon=1, day=1, hour=1, min=0, sec=0}) |
| 2213 | + t.assert(dt.parse("2020-01-01T01:00:00+00:00", {format = 'rfc3339'}) == |
| 2214 | + dt.parse("2020-01-01T01:00:00+00:00", {format = 'iso8601'})) |
| 2215 | + t.assert(dt.parse("1970-01-01T02:00:00+02:00") == |
| 2216 | + dt.new{year=1970, mon=1, day=1, hour=2, min=0, sec=0, tzoffset=120}) |
| 2217 | + |
| 2218 | + t.assert(dt.parse("1970-01-01T01:00:00", {tzoffset = 120}) == |
| 2219 | + dt.new{year=1970, mon=1, day=1, hour=1, min=0, sec=0, tzoffset=120}) |
| 2220 | + t.assert(dt.parse("1970-01-01T01:00:00", {tzoffset = '+0200'}) == |
| 2221 | + dt.new{year=1970, mon=1, day=1, hour=1, min=0, sec=0, tzoffset=120}) |
| 2222 | + t.assert(dt.parse("1970-01-01T01:00:00", {tzoffset = '+02:00'}) == |
| 2223 | + dt.new{year=1970, mon=1, day=1, hour=1, min=0, sec=0, tzoffset=120}) |
| 2224 | + t.assert(dt.parse("1970-01-01T01:00:00Z", {tzoffset = '+02:00'}) == |
| 2225 | + dt.new{year=1970, mon=1, day=1, hour=1, min=0, sec=0, tzoffset=0}) |
| 2226 | + t.assert(dt.parse("1970-01-01T01:00:00+01:00", {tzoffset = '+02:00'}) == |
| 2227 | + dt.new{year=1970, mon=1, day=1, hour=1, min=0, sec=0, tzoffset=60}) |
| 2228 | + t.assert(dt.parse('1998-11-25', { format = '%Y-%m-%d', tzoffset = 180 }) == |
| 2229 | + dt.new({ year = 1998, month = 11, day = 25, tzoffset = 180 })) |
| 2230 | + t.assert(dt.parse('1998', { format = '%Y', tzoffset = '+03:00' }) == |
| 2231 | + dt.new({ year = 1998, tzoffset = 180 })) |
| 2232 | + |
| 2233 | + -- Testcases with override timezone by setting tz. |
| 2234 | + -- Timezone is not specified in a parsed string. |
| 2235 | + t.assert(dt.parse("1970-01-01T01:00:00", {tz = "MSK"}) == |
| 2236 | + dt.new{year = 1970, mon = 1, day = 1, |
| 2237 | + hour = 1, min = 0, sec = 0, tzoffset=180}) |
| 2238 | + -- Timezone is specified in a parsed string as a military timezone. |
| 2239 | + t.assert(dt.parse("1970-01-01T01:00:00Z", {tz = "Europe/Moscow"}) == |
| 2240 | + dt.new{year = 1970, mon = 1, day = 1, |
| 2241 | + hour = 1, min = 0, sec = 0, tzoffset = 0}) |
| 2242 | + -- Timezone is specified in a parsed string as an offset. |
| 2243 | + t.assert(dt.parse("1970-01-01T01:00:00+01:00", {tz = "Asia/Omsk"}) == |
| 2244 | + dt.new{year = 1970, mon = 1, day = 1, |
| 2245 | + hour = 1, min = 0, sec = 0, tzoffset = 60}) |
| 2246 | + -- Timezone is not specified in a parsed string and format is passed. |
| 2247 | + t.assert(dt.parse("1998-11-25", { format = "%Y-%m-%d", tz = "MSK" }) == |
| 2248 | + dt.new{year = 1998, month = 11, day = 25, tzoffset = 180}) |
| 2249 | +end |
0 commit comments