From 9121eccca9ca09ebc45e4ab3a67f9702750257d4 Mon Sep 17 00:00:00 2001
From: Sergey Bronnikov <sergeyb@tarantool.org>
Date: Tue, 30 Jul 2024 14:57:01 +0300
Subject: [PATCH] datetime: support tz field in :totable()

`datetime` module has a function `:totable()` that converts
the information from a datetime object into the table format.
The commit 43e10ed34949 ("build, lua: built-in module datetime")
added `tzoffset` field to the datetime object and to table
produced by `:totable()`. The commit 9ee45289e012
("datetime: datetime.TZ array") added fields `tz` and `tzindex` to
the datetime object, but not to the table produced by
`:totable()`. The patch fixes that. Note, `tzindex` is not added,
because it is an internal field.

```
tarantool> datetime.parse('2004-12-01T00:00 Europe/Moscow'):totable()
---
- tz: Europe/Moscow
  sec: 0
  min: 0
  yday: 336
  day: 1
  nsec: 0
  isdst: false
  wday: 4
  tzoffset: 180
  month: 12
  year: 2004
  hour: 0
...
```

Fixes #10331
Follows up #6751

@TarantoolBot document
Title: Support of tz field in :totable()

In addition to the `tzoffset` in a table produced by `:totable`
we added `tz` field.

```
tarantool> datetime.parse('2004-12-01T00:00 Europe/Moscow'):totable()
---
- tz: Europe/Moscow
  sec: 0
  min: 0
  yday: 336
  day: 1
  nsec: 0
  isdst: false
  wday: 4
  tzoffset: 180
  month: 12
  year: 2004
  hour: 0
...
```

(cherry picked from commit 90552e55e0921405c43ea086ae418a72c9f000e4)
---
 .../unreleased/gh-10331-tz-in-totable.md      |  4 ++
 src/lua/datetime.lua                          |  1 +
 test/app-tap/datetime.test.lua                | 54 +++++++++++++++++--
 test/sql-luatest/datetime_test.lua            |  6 ++-
 4 files changed, 60 insertions(+), 5 deletions(-)
 create mode 100644 changelogs/unreleased/gh-10331-tz-in-totable.md

diff --git a/changelogs/unreleased/gh-10331-tz-in-totable.md b/changelogs/unreleased/gh-10331-tz-in-totable.md
new file mode 100644
index 0000000000..d09b3ea7f5
--- /dev/null
+++ b/changelogs/unreleased/gh-10331-tz-in-totable.md
@@ -0,0 +1,4 @@
+## bugfix/datetime
+
+* Added the `tz` field to a table produced by `:totable()`
+  (gh-10331).
diff --git a/src/lua/datetime.lua b/src/lua/datetime.lua
index c26e1d3aa8..7daecb0761 100644
--- a/src/lua/datetime.lua
+++ b/src/lua/datetime.lua
@@ -973,6 +973,7 @@ local function datetime_totable(self)
         isdst = datetime_isdst(self),
         nsec = self.nsec,
         tzoffset = self.tzoffset,
+        tz = self.tz,
     }
 end
 
diff --git a/test/app-tap/datetime.test.lua b/test/app-tap/datetime.test.lua
index 4cf33d8e06..a7f1c7f5f7 100755
--- a/test/app-tap/datetime.test.lua
+++ b/test/app-tap/datetime.test.lua
@@ -8,7 +8,7 @@ local json = require('json')
 local msgpack = require('msgpack')
 local TZ = date.TZ
 
-test:plan(41)
+test:plan(42)
 
 local INT_MAX = 2147483647
 
@@ -151,7 +151,7 @@ test:test("Datetime API checks", function(test)
     local table_expected = {
         sec =  0, min = 0, wday = 5, day = 1, nsec = 0,
         isdst = false, yday = 1, tzoffset = 0, month = 1,
-        year = 1970, hour = 0
+        year = 1970, hour = 0, tz = ''
     }
     test:is_deeply(local_totable(ts), table_expected, "correct :totable")
     local date_expected = date.new()
@@ -1832,7 +1832,8 @@ test:test("totable{}", function(test)
     test:plan(78)
     local exp = {sec = 0, min = 0, wday = 5, day = 1,
                  nsec = 0, isdst = false, yday = 1,
-                 tzoffset = 0, month = 1, year = 1970, hour = 0}
+                 tzoffset = 0, month = 1, year = 1970, hour = 0,
+                 tz = ''}
     local ts = date.new()
     local totable = ts:totable()
     test:is_deeply(totable, exp, 'date:totable()')
@@ -1867,6 +1868,53 @@ test:test("totable{}", function(test)
     end
 end)
 
+test:test('totable() with timezone', function(test)
+    local DEFAULT_TZOFFSET = 0
+    local DEFAULT_TZ = ''
+
+    local MOSCOW_TZOFFSET = 180
+    local MOSCOW_TZ = 'Europe/Moscow'
+
+    local test_cases = {
+        -- Empty datetime value except the timezone.
+        {
+            dt = {tz = MOSCOW_TZ},
+            expected = {
+                tzoffset = MOSCOW_TZOFFSET,
+                tz = MOSCOW_TZ,
+            }
+        },
+        -- Empty datetime value except the tzoffset.
+        {
+            dt = {tzoffset = MOSCOW_TZOFFSET},
+            expected = {
+                tzoffset = MOSCOW_TZOFFSET,
+                tz = '',
+            }
+        },
+        -- Empty datetime value.
+        {
+            dt = {},
+            expected = {
+                tz = DEFAULT_TZ,
+                tzoffset = DEFAULT_TZOFFSET,
+            },
+        },
+    }
+
+    test:plan(#test_cases * 2)
+
+    for _, tc in pairs(test_cases) do
+        local dtab = date.new(tc.dt):totable()
+        local expected = tc.expected
+        test:is(dtab.tzoffset, expected.tzoffset,
+                ('[tzoffset]: %q == %q'):format(dtab.tzoffset,
+                                                expected.tzoffset))
+        test:is(dtab.tz, expected.tz,
+                ('[tz]: %q == %q'):format(dtab.tz, expected.tz))
+    end
+end)
+
 test:test("Time :set{} operations", function(test)
     test:plan(16)
 
diff --git a/test/sql-luatest/datetime_test.lua b/test/sql-luatest/datetime_test.lua
index 041a809ec3..bd22186a34 100644
--- a/test/sql-luatest/datetime_test.lua
+++ b/test/sql-luatest/datetime_test.lua
@@ -864,7 +864,8 @@ end
 g.test_datetime_18_3 = function()
     g.server:exec(function()
         local dt = require('datetime')
-        local dt1 = dt.new({year = 2001, month = 1, day = 1, hour = 1})
+        local dt1 = dt.new(
+            {year = 2001, month = 1, day = 1, hour = 1, tz = 'Z'})
         local sql = [[SELECT CAST('2001-01-01T01:00:00Z' AS DATETIME);]]
         local res = {{dt1}}
         local rows = box.execute(sql).rows
@@ -2218,7 +2219,8 @@ end
 g.test_datetime_32_1 = function()
     g.server:exec(function()
         local dt = require('datetime')
-        local dt1 = dt.new({year = 2000, month = 2, day = 29, hour = 1})
+        local dt1 = dt.new(
+            {year = 2000, month = 2, day = 29, hour = 1, tz = 'Z'})
         local sql = [[SELECT CAST('2000-02-29T01:00:00Z' AS DATETIME);]]
         local res = {{dt1}}
         local rows = box.execute(sql).rows
-- 
GitLab