[Tarantool-patches] [RFC PATCH 02/13] lua: built-in module datetime
Timur Safin
tsafin at tarantool.org
Thu Jul 15 11:18:08 MSK 2021
* Added a new Tarantool built-in module `datetime`;
* Register cdef types for this module;
* Export some dt_* functions from c-dt library;
* Datetime parse unit tests;
---
src/CMakeLists.txt | 3 +
src/exports.h | 21 ++
src/lib/core/datetime.h | 61 +++++
src/lua/datetime.c | 64 +++++
src/lua/datetime.h | 53 ++++
src/lua/datetime.lua | 564 ++++++++++++++++++++++++++++++++++++++
src/lua/init.c | 6 +-
test/unit/CMakeLists.txt | 2 +
test/unit/datetime.c | 157 +++++++++++
test/unit/datetime.result | 135 +++++++++
10 files changed, 1065 insertions(+), 1 deletion(-)
create mode 100644 src/lib/core/datetime.h
create mode 100644 src/lua/datetime.c
create mode 100644 src/lua/datetime.h
create mode 100644 src/lua/datetime.lua
create mode 100644 test/unit/datetime.c
create mode 100644 test/unit/datetime.result
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ef6a295d5..9d3da10d9 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -51,6 +51,8 @@ lua_source(lua_sources ../third_party/luafun/fun.lua)
lua_source(lua_sources lua/httpc.lua)
lua_source(lua_sources lua/iconv.lua)
lua_source(lua_sources lua/swim.lua)
+lua_source(lua_sources lua/datetime.lua)
+
# LuaJIT jit.* library
lua_source(lua_sources ${LUAJIT_SOURCE_ROOT}/src/jit/bc.lua)
lua_source(lua_sources ${LUAJIT_SOURCE_ROOT}/src/jit/bcsave.lua)
@@ -136,6 +138,7 @@ set (server_sources
lua/string.c
lua/swim.c
lua/decimal.c
+ lua/datetime.c
${lua_sources}
${PROJECT_SOURCE_DIR}/third_party/lua-yaml/lyaml.cc
${PROJECT_SOURCE_DIR}/third_party/lua-yaml/b64.c
diff --git a/src/exports.h b/src/exports.h
index 5bb3e6a2b..db40c03a4 100644
--- a/src/exports.h
+++ b/src/exports.h
@@ -531,3 +531,24 @@ EXPORT(uri_format)
EXPORT(uri_parse)
EXPORT(uuid_nil)
EXPORT(uuid_unpack)
+EXPORT(dt_from_rdn)
+EXPORT(dt_from_yd)
+EXPORT(dt_from_ymd)
+EXPORT(dt_from_yqd)
+EXPORT(dt_from_ywd)
+EXPORT(dt_to_yd)
+EXPORT(dt_to_ymd)
+EXPORT(dt_to_yqd)
+EXPORT(dt_to_ywd)
+EXPORT(dt_rdn)
+EXPORT(dt_dow)
+EXPORT(dt_parse_iso_date)
+EXPORT(dt_parse_iso_time)
+EXPORT(dt_parse_iso_time_basic)
+EXPORT(dt_parse_iso_time_extended)
+EXPORT(dt_parse_iso_zone)
+EXPORT(dt_parse_iso_zone_basic)
+EXPORT(dt_parse_iso_zone_extended)
+EXPORT(dt_parse_iso_zone_lenient)
+EXPORT(dt_from_struct_tm)
+EXPORT(dt_to_struct_tm)
diff --git a/src/lib/core/datetime.h b/src/lib/core/datetime.h
new file mode 100644
index 000000000..837ed346c
--- /dev/null
+++ b/src/lib/core/datetime.h
@@ -0,0 +1,61 @@
+#pragma once
+/*
+ * Copyright 2021, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <c-dt/dt_core.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+/**
+ * datetime structure consisting of:
+ */
+struct t_datetime_tz {
+ int secs; ///< seconds since epoch
+ int nsec; ///< nanoseconds if any
+ int offset; ///< offset in minutes from GMT
+};
+
+/**
+ * Date/time delta structure
+ */
+struct t_datetime_duration {
+ int secs; ///< relative seconds delta
+ int nsec; ///< nanoseconds delta
+};
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
+
diff --git a/src/lua/datetime.c b/src/lua/datetime.c
new file mode 100644
index 000000000..37a8bc020
--- /dev/null
+++ b/src/lua/datetime.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2021, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "datetime.h"
+
+#include <assert.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+uint32_t CTID_DATETIME_TZ = 0;
+uint32_t CTID_DURATION = 0;
+
+void
+tarantool_lua_datetime_init(struct lua_State *L)
+{
+ int rc = luaL_cdef(L, "struct t_datetime_tz {"
+ "int secs;"
+ "int nsec;"
+ "int offset;"
+ "};");
+ assert(rc == 0);
+ (void) rc;
+ CTID_DATETIME_TZ = luaL_ctypeid(L, "struct t_datetime_tz");
+ assert(CTID_DATETIME_TZ != 0);
+
+
+ rc = luaL_cdef(L, "struct t_datetime_duration {"
+ "int secs;"
+ "int nsec;"
+ "};");
+ assert(rc == 0);
+ (void) rc;
+ CTID_DURATION = luaL_ctypeid(L, "struct t_datetime_duration");
+ assert(CTID_DURATION != 0);
+}
diff --git a/src/lua/datetime.h b/src/lua/datetime.h
new file mode 100644
index 000000000..d290a6d13
--- /dev/null
+++ b/src/lua/datetime.h
@@ -0,0 +1,53 @@
+#pragma once
+/*
+ * Copyright 2021, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "lib/core/datetime.h"
+#include "utils.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+extern uint32_t CTID_DATETIME_TZ;
+extern uint32_t CTID_DURATION;
+
+struct lua_State;
+
+struct t_datetime_tz*
+lua_pushdatetime(struct lua_State *L);
+
+void
+tarantool_lua_datetime_init(struct lua_State *L);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
diff --git a/src/lua/datetime.lua b/src/lua/datetime.lua
new file mode 100644
index 000000000..cffa38cd5
--- /dev/null
+++ b/src/lua/datetime.lua
@@ -0,0 +1,564 @@
+local ffi = require('ffi')
+local cdt = ffi.C
+
+ffi.cdef [[
+
+ typedef int dt_t;
+
+ // dt_core.h
+ typedef enum {
+ DT_MON = 1,
+ DT_MONDAY = 1,
+ DT_TUE = 2,
+ DT_TUESDAY = 2,
+ DT_WED = 3,
+ DT_WEDNESDAY = 3,
+ DT_THU = 4,
+ DT_THURSDAY = 4,
+ DT_FRI = 5,
+ DT_FRIDAY = 5,
+ DT_SAT = 6,
+ DT_SATURDAY = 6,
+ DT_SUN = 7,
+ DT_SUNDAY = 7,
+ } dt_dow_t;
+
+ dt_t dt_from_rdn (int n);
+ dt_t dt_from_yd (int y, int d);
+ dt_t dt_from_ymd (int y, int m, int d);
+ dt_t dt_from_yqd (int y, int q, int d);
+ dt_t dt_from_ywd (int y, int w, int d);
+
+ void dt_to_yd (dt_t dt, int *y, int *d);
+ void dt_to_ymd (dt_t dt, int *y, int *m, int *d);
+ void dt_to_yqd (dt_t dt, int *y, int *q, int *d);
+ void dt_to_ywd (dt_t dt, int *y, int *w, int *d);
+
+ int dt_rdn (dt_t dt);
+ dt_dow_t dt_dow (dt_t dt);
+
+ // dt_parse_iso.h
+ size_t dt_parse_iso_date (const char *str, size_t len, dt_t *dt);
+
+ size_t dt_parse_iso_time (const char *str, size_t len, int *sod, int *nsec);
+ size_t dt_parse_iso_time_basic (const char *str, size_t len, int *sod, int *nsec);
+ size_t dt_parse_iso_time_extended (const char *str, size_t len, int *sod, int *nsec);
+
+ size_t dt_parse_iso_zone (const char *str, size_t len, int *offset);
+ size_t dt_parse_iso_zone_basic (const char *str, size_t len, int *offset);
+ size_t dt_parse_iso_zone_extended (const char *str, size_t len, int *offset);
+ size_t dt_parse_iso_zone_lenient (const char *str, size_t len, int *offset);
+
+ // dt_tm.h
+ dt_t dt_from_struct_tm (const struct tm *tm);
+ void dt_to_struct_tm (dt_t dt, struct tm *tm);
+
+ // <asm-generic/posix_types.h>
+ typedef long __kernel_long_t;
+ typedef unsigned long __kernel_ulong_t;
+ // /usr/include/x86_64-linux-gnu/bits/types/time_t.h
+ typedef long time_t;
+
+
+ // <time.h>
+ typedef __kernel_long_t __kernel_time_t;
+ typedef __kernel_long_t __kernel_suseconds_t;
+
+ struct timespec {
+ __kernel_time_t tv_sec; /* seconds */
+ long tv_nsec; /* nanoseconds */
+ };
+
+ struct timeval {
+ __kernel_time_t tv_sec; /* seconds */
+ __kernel_suseconds_t tv_usec; /* microseconds */
+ };
+
+ struct timezone {
+ int tz_minuteswest; /* minutes west of Greenwich */
+ int tz_dsttime; /* type of dst correction */
+ };
+
+ // /usr/include/x86_64-linux-gnu/sys/time.h
+ typedef struct timezone * __timezone_ptr_t;
+
+ /* Get the current time of day and timezone information,
+ putting it into *TV and *TZ. If TZ is NULL, *TZ is not filled.
+ Returns 0 on success, -1 on errors.
+
+ NOTE: This form of timezone information is obsolete.
+ Use the functions and variables declared in <time.h> instead. */
+ int gettimeofday (struct timeval *__tv, struct timezone * __tz);
+
+ // /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h
+ /* ISO C `broken-down time' structure. */
+ struct tm
+ {
+ int tm_sec; /* Seconds. [0-60] (1 leap second) */
+ int tm_min; /* Minutes. [0-59] */
+ int tm_hour; /* Hours. [0-23] */
+ int tm_mday; /* Day. [1-31] */
+ int tm_mon; /* Month. [0-11] */
+ int tm_year; /* Year - 1900. */
+ int tm_wday; /* Day of week. [0-6] */
+ int tm_yday; /* Days in year.[0-365] */
+ int tm_isdst; /* DST. [-1/0/1]*/
+
+ long int tm_gmtoff; /* Seconds east of UTC. */
+ const char *tm_zone;/* Timezone abbreviation. */
+ };
+
+ // <time.h>
+ /* Return the current time and put it in *TIMER if TIMER is not NULL. */
+ time_t time (time_t *__timer);
+
+ /* Format TP into S according to FORMAT.
+ Write no more than MAXSIZE characters and return the number
+ of characters written, or 0 if it would exceed MAXSIZE. */
+ size_t strftime (char * __s, size_t __maxsize, const char * __format,
+ const struct tm * __tp);
+
+ /* Parse S according to FORMAT and store binary time information in TP.
+ The return value is a pointer to the first unparsed character in S. */
+ char *strptime (const char * __s, const char * __fmt, struct tm *__tp);
+
+ /* Return the `struct tm' representation of *TIMER in UTC,
+ using *TP to store the result. */
+ struct tm *gmtime_r (const time_t * __timer, struct tm * __tp);
+
+ /* Return the `struct tm' representation of *TIMER in local time,
+ using *TP to store the result. */
+ struct tm *localtime_r (const time_t * __timer, struct tm * __tp);
+
+ /* Return a string of the form "Day Mon dd hh:mm:ss yyyy\n"
+ that is the representation of TP in this format. */
+ char *asctime (const struct tm *__tp);
+
+ /* Equivalent to `asctime (localtime (timer))'. */
+ char *ctime (const time_t *__timer);
+
+]]
+
+local native = ffi.C
+
+local SECS_PER_DAY = 86400
+local NANOS_PER_SEC = 1000000000LL
+
+-- c-dt/dt_config.h
+
+-- Unix, January 1, 1970, Thursday
+local DT_EPOCH_1970_OFFSET = 719163LL
+
+
+local datetime_t = ffi.typeof('struct t_datetime_tz')
+local duration_t = ffi.typeof('struct t_datetime_duration')
+
+local function duration_new()
+ local delta = ffi.new(duration_t)
+ return delta
+end
+
+local function adjusted_secs(dt)
+ return dt.secs - dt.offset * 60
+end
+
+local function datetime_sub(lhs, rhs)
+ local s1 = adjusted_secs(lhs)
+ local s2 = adjusted_secs(rhs)
+ local d = duration_new()
+ d.secs = s2 - s1
+ d.nsec = rhs.nsec - lhs.nsec
+ if d.nsec < 0 then
+ d.secs = d.secs - 1
+ d.nsec = d.nsec + NANOS_PER_SEC
+ end
+ return d
+end
+
+local function datetime_add(lhs, rhs)
+ local s1 = adjusted_secs(lhs)
+ local s2 = adjusted_secs(rhs)
+ local d = duration_new()
+ d.secs = s2 + s1
+ d.nsec = rhs.nsec + lhs.nsec
+ if d.nsec >= NANOS_PER_SEC then
+ d.secs = d.secs + 1
+ d.nsec = d.nsec - NANOS_PER_SEC
+ end
+ return d
+end
+
+local function datetime_eq(lhs, rhs)
+ -- we usually don't need to check nullness
+ -- but older tarantool console will call us checking for equality to nil
+ if rhs == nil then
+ return false
+ end
+ return (lhs.secs == rhs.secs) and (lhs.nsec == rhs.nsec)
+end
+
+
+local function datetime_lt(lhs, rhs)
+ return (lhs.secs < rhs.secs) or
+ (lhs.secs == rhs.secs and lhs.nsec < rhs.nsec)
+end
+
+local function datetime_le(lhs, rhs)
+ return (lhs.secs <= rhs.secs) or
+ (lhs.secs == rhs.secs and lhs.nsec <= rhs.nsec)
+end
+
+local function datetime_serialize(self)
+ -- Allow YAML, MsgPack and JSON to dump objects with sockets
+ return { secs = self.secs, nsec = self.nsec, tz = self.offset }
+end
+
+local function duration_serialize(self)
+ -- Allow YAML and JSON to dump objects with sockets
+ return { secs = self.secs, nsec = self.nsec }
+end
+
+local datetime_mt = {
+ -- __tostring = datetime_tostring,
+ __serialize = datetime_serialize,
+ __eq = datetime_eq,
+ __lt = datetime_lt,
+ __le = datetime_le,
+ __sub = datetime_sub,
+ __add = datetime_add,
+
+ nanoseconds = function(self)
+ return tonumber(self.secs*NANOS_PER_SEC + self.nsec)
+ end,
+ microseconds = function(self)
+ return tonumber(self.secs*1e6 + self.nsec*1e3)
+ end,
+ seconds = function(self)
+ return tonumber(self.secs + self.nsec*1e3)
+ end,
+ minutes = function(self)
+ return tonumber((self._ticks/(1e6*60))%60)
+ end,
+ hours = function(self)
+ return tonumber(self._ticks/(1e6*60*60))
+ end,
+
+}
+
+local duration_mt = {
+ -- __tostring = duration_tostring,
+ __serialize = duration_serialize,
+ __eq = datetime_eq,
+ __lt = datetime_lt,
+ __le = datetime_le,
+}
+
+local function datetime_new_raw(secs, nsec, offset)
+ local dt_obj = ffi.new(datetime_t)
+ dt_obj.secs = secs
+ dt_obj.nsec = nsec
+ dt_obj.offset = offset
+ return dt_obj
+end
+
+local function local_rd(o)
+ return math.floor(o.secs / SECS_PER_DAY) + DT_EPOCH_1970_OFFSET
+end
+
+local function local_dt(o)
+ return cdt.dt_from_rdn(local_rd(o))
+end
+
+local function mk_timestamp(dt, sp, fp, offset)
+ local epochV = dt ~= nil and (cdt.dt_rdn(dt) - DT_EPOCH_1970_OFFSET) * SECS_PER_DAY or 0
+ local spV = sp ~= nil and sp or 0
+ local fpV = fp ~= nil and fp or 0
+ local ofsV = offset ~= nil and offset or 0
+ return datetime_new_raw (epochV + spV - ofsV * 60, fpV, ofsV)
+end
+
+-- create @datetime_t given object @o fields
+local function datetime_new(o)
+ if o == nil then
+ return datetime_new_raw(0, 0, 0)
+ end
+ local secs = 0
+ local nsec = 0
+ local offset = 0
+ local easy_way = false
+ local y, M, d, ymd
+ y, M, d, ymd = 0, 0, 0, false
+
+ local h, m, s, frac, hms
+ h, m, s, frac, hms = 0, 0, 0, 0, false
+
+ local dt = 0
+
+ for key, value in pairs(o) do
+ local handlers = {
+ secs = function(v)
+ secs = v
+ easy_way = true
+ end,
+
+ nsec = function(v)
+ nsec = v
+ easy_way = true
+ end,
+
+ offset = function (v)
+ offset = v
+ easy_way = true
+ end,
+
+ year = function(v)
+ assert(v > 0 and v < 10000)
+ y = v
+ ymd = true
+ end,
+
+ month = function(v)
+ assert(v > 0 and v < 12 )
+ M = v
+ ymd = true
+ end,
+
+ day = function(v)
+ assert(v > 0 and v < 32)
+ d = v
+ ymd = true
+ end,
+
+ hour = function(v)
+ assert(v >= 0 and v < 24)
+ h = v
+ hms = true
+ end,
+
+ minute = function(v)
+ assert(v >= 0 and v < 60)
+ m = v
+ hms = true
+ end,
+
+ second = function(v)
+ assert(v >= 0 and v < 61)
+ frac = v % 1
+ if frac then
+ s = v - (v % 1)
+ else
+ s = v
+ end
+ hms = true
+ end,
+
+ -- tz offset in minutes
+ tz = function(v)
+ assert(v >= 0 and v <= 720)
+ offset = v
+ end
+ }
+ handlers[key](value)
+ end
+
+ -- .sec, .nsec, .offset
+ if easy_way then
+ return datetime_new_raw(secs, nsec, offset)
+ end
+
+ -- .year, .month, .day
+ if ymd then
+ dt = dt + cdt.dt_from_ymd(y, M, d)
+ end
+
+ -- .hour, .minute, .second
+ if hms then
+ secs = h * 3600 + m * 60 + s
+ end
+
+ return mk_timestamp(dt, secs, frac, offset)
+end
+
+
+-- simple parse functions:
+-- parse_date/parse_time/parse_zone
+
+--[[
+ Basic Extended
+ 20121224 2012-12-24 Calendar date (ISO 8601)
+ 2012359 2012-359 Ordinal date (ISO 8601)
+ 2012W521 2012-W52-1 Week date (ISO 8601)
+ 2012Q485 2012-Q4-85 Quarter date
+]]
+
+local function parse_date(str)
+ local dt = ffi.new('dt_t[1]')
+ local rc = cdt.dt_parse_iso_date(str, #str, dt)
+ assert(rc > 0)
+ return mk_timestamp(dt[0])
+end
+
+--[[
+ Basic Extended
+ T12 N/A
+ T1230 T12:30
+ T123045 T12:30:45
+ T123045.123456789 T12:30:45.123456789
+ T123045,123456789 T12:30:45,123456789
+
+ The time designator [T] may be omitted.
+]]
+local function parse_time(str)
+ local sp = ffi.new('int[1]')
+ local fp = ffi.new('int[1]')
+ local rc = cdt.dt_parse_iso_time(str, #str, sp, fp)
+ assert(rc > 0)
+ return mk_timestamp(nil, sp[0], fp[0])
+end
+
+--[[
+ Basic Extended
+ Z N/A
+ ±hh N/A
+ ±hhmm ±hh:mm
+]]
+local function parse_zone(str)
+ local offset = ffi.new('int[1]')
+ local rc = cdt.dt_parse_iso_zone(str, #str, offset)
+ assert(rc > 0)
+ return mk_timestamp(nil, nil, nil, offset[0])
+end
+
+
+--[[
+ aggregated parse functions
+ assumes to deal with date T time time_zone
+ at once
+
+ date [T] time [ ] time_zone
+]]
+local function parse_str(str)
+ local dt = ffi.new('dt_t[1]')
+ local len = #str
+ local n = cdt.dt_parse_iso_date(str, len, dt)
+ local dt_ = dt[0]
+ if n == 0 or len == n then
+ return mk_timestamp(dt_)
+ end
+
+ str = str:sub(tonumber(n) + 1)
+
+ local ch = str:sub(1,1)
+ if ch ~= 't' and ch ~= 'T' and ch ~= ' ' then
+ return mk_timestamp(dt_)
+ end
+
+ str = str:sub(2)
+ len = #str
+
+ local sp = ffi.new('int[1]')
+ local fp = ffi.new('int[1]')
+ local n = cdt.dt_parse_iso_time(str, len, sp, fp)
+ if n == 0 then
+ return mk_timestamp(dt_)
+ end
+ local sp_ = sp[0]
+ local fp_ = fp[0]
+ if len == n then
+ return mk_timestamp(dt_, sp_, fp_)
+ end
+
+ str = str:sub(tonumber(n) + 1)
+
+ if str:sub(1,1) == ' ' then
+ str = str:sub(2)
+ end
+
+ len = #str
+
+ local offset = ffi.new('int[1]')
+ n = cdt.dt_parse_iso_zone(str, len, offset)
+ if n == 0 then
+ return mk_timestamp(dt_, sp_, fp_)
+ end
+ return mk_timestamp(dt_, sp_, fp_, offset[0])
+end
+
+local function datetime_from(o)
+ if o == nil or type(o) == 'table' then
+ return datetime_new(o)
+ elseif type(o) == 'string' then
+ return parse_str(o)
+ end
+end
+
+local function local_now()
+ local p_tv = ffi.new ' struct timeval [1] '
+ local rc = native.gettimeofday(p_tv, nil)
+ assert(rc == 0)
+
+ local secs = p_tv[0].tv_sec
+ local nsec = p_tv[0].tv_usec * 1000
+
+ local p_time = ffi.new 'time_t[1]'
+ local p_tm = ffi.new 'struct tm[1]'
+ native.time(p_time)
+ native.localtime_r(p_time, p_tm)
+ -- local dt = cdt.dt_from_struct_tm(p_tm)
+ local ofs = p_tm[0].tm_gmtoff / 60 -- convert seconds to minutes
+
+ return datetime_new_raw(secs, nsec, ofs) -- FIXME
+end
+
+local function asctime(o)
+ assert(ffi.typeof(o) == datetime_t)
+ local p_tm = ffi.new 'struct tm[1]'
+ cdt.dt_to_struct_tm(local_dt(o), p_tm)
+ return ffi.string(native.asctime(p_tm))
+end
+
+local function ctime(o)
+ assert(ffi.typeof(o) == datetime_t)
+ local p_time = ffi.new 'time_t[1]'
+ p_time[0] = o.secs
+ return ffi.string(native.ctime(p_time))
+end
+
+local function strftime(fmt, o)
+ assert(ffi.typeof(o) == datetime_t)
+ local sz = 50
+ local buff = ffi.new('char[?]', sz)
+ local p_tm = ffi.new 'struct tm[1]'
+ cdt.dt_to_struct_tm(local_dt(o), p_tm)
+ native.strftime(buff, sz, fmt, p_tm)
+ return ffi.string(buff)
+end
+
+-- strftime may be redirected to datetime:fmt("format")
+local function datetime_fmt()
+end
+
+
+ffi.metatype(duration_t, duration_mt)
+ffi.metatype(datetime_t, datetime_mt)
+
+return setmetatable(
+ {
+ datetime = datetime_new,
+ delta = duration_new,
+
+ parse = parse_str,
+ parse_date = parse_date,
+ parse_time = parse_time,
+ parse_zone = parse_zone,
+ fmt = datetime_fmt,
+
+ now = local_now,
+ -- strptime = strptime;
+ strftime = strftime,
+ asctime = asctime,
+ ctime = ctime,
+ }, {
+ __call = function(self, ...) return datetime_from(...) end
+ }
+)
diff --git a/src/lua/init.c b/src/lua/init.c
index f9738025d..89b070310 100644
--- a/src/lua/init.c
+++ b/src/lua/init.c
@@ -62,6 +62,7 @@
#include "lua/utf8.h"
#include "lua/swim.h"
#include "lua/decimal.h"
+#include "lua/datetime.h"
#include "digest.h"
#include "errinj.h"
#include <small/ibuf.h>
@@ -129,7 +130,8 @@ extern char strict_lua[],
parse_lua[],
process_lua[],
humanize_lua[],
- memprof_lua[]
+ memprof_lua[],
+ datetime_lua[]
;
static const char *lua_modules[] = {
@@ -184,6 +186,7 @@ static const char *lua_modules[] = {
"memprof.process", process_lua,
"memprof.humanize", humanize_lua,
"memprof", memprof_lua,
+ "datetime", datetime_lua,
NULL
};
@@ -479,6 +482,7 @@ tarantool_lua_init(const char *tarantool_bin, int argc, char **argv)
tarantool_lua_serializer_init(L);
tarantool_lua_swim_init(L);
tarantool_lua_decimal_init(L);
+ tarantool_lua_datetime_init(L);
luaopen_http_client_driver(L);
lua_pop(L, 1);
luaopen_msgpack(L);
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index 5bb7cd6e7..f8320aebd 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -56,6 +56,8 @@ add_executable(uuid.test uuid.c core_test_utils.c)
target_link_libraries(uuid.test uuid unit)
add_executable(random.test random.c core_test_utils.c)
target_link_libraries(random.test core unit)
+add_executable(datetime.test datetime.c)
+target_link_libraries(datetime.test cdt unit)
add_executable(bps_tree.test bps_tree.cc)
target_link_libraries(bps_tree.test small misc)
diff --git a/test/unit/datetime.c b/test/unit/datetime.c
new file mode 100644
index 000000000..25c9c1f1a
--- /dev/null
+++ b/test/unit/datetime.c
@@ -0,0 +1,157 @@
+#include "dt.h"
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "unit.h"
+
+const char sample[] = "2012-12-24T15:30Z";
+
+#define S(s) {s, sizeof(s) - 1}
+struct {
+ const char * sz;
+ size_t len;
+} tests[] = {
+ S("2012-12-24 15:30Z"),
+ S("2012-12-24 15:30z"),
+ S("2012-12-24 16:30+01:00"),
+ S("2012-12-24 16:30+0100"),
+ S("2012-12-24 16:30+01"),
+ S("2012-12-24 14:30-01:00"),
+ S("2012-12-24 14:30-0100"),
+ S("2012-12-24 14:30-01"),
+ S("2012-12-24 15:30:00Z"),
+ S("2012-12-24 15:30:00z"),
+ S("2012-12-24 16:30:00+01:00"),
+ S("2012-12-24 16:30:00+0100"),
+ S("2012-12-24 14:30:00-01:00"),
+ S("2012-12-24 14:30:00-0100"),
+ S("2012-12-24 15:30:00.123456Z"),
+ S("2012-12-24 15:30:00.123456z"),
+ S("2012-12-24 16:30:00.123456+01:00"),
+ S("2012-12-24 16:30:00.123456+01"),
+ S("2012-12-24 14:30:00.123456-01:00"),
+ S("2012-12-24 14:30:00.123456-01"),
+ S("2012-12-24t15:30Z"),
+ S("2012-12-24t15:30z"),
+ S("2012-12-24t16:30+01:00"),
+ S("2012-12-24t16:30+0100"),
+ S("2012-12-24t14:30-01:00"),
+ S("2012-12-24t14:30-0100"),
+ S("2012-12-24t15:30:00Z"),
+ S("2012-12-24t15:30:00z"),
+ S("2012-12-24t16:30:00+01:00"),
+ S("2012-12-24t16:30:00+0100"),
+ S("2012-12-24t14:30:00-01:00"),
+ S("2012-12-24t14:30:00-0100"),
+ S("2012-12-24t15:30:00.123456Z"),
+ S("2012-12-24t15:30:00.123456z"),
+ S("2012-12-24t16:30:00.123456+01:00"),
+ S("2012-12-24t14:30:00.123456-01:00"),
+ S("2012-12-24 16:30 +01:00"),
+ S("2012-12-24 14:30 -01:00"),
+ S("2012-12-24 15:30 UTC"),
+ S("2012-12-24 16:30 UTC+1"),
+ S("2012-12-24 16:30 UTC+01"),
+ S("2012-12-24 16:30 UTC+0100"),
+ S("2012-12-24 16:30 UTC+01:00"),
+ S("2012-12-24 14:30 UTC-1"),
+ S("2012-12-24 14:30 UTC-01"),
+ S("2012-12-24 14:30 UTC-01:00"),
+ S("2012-12-24 14:30 UTC-0100"),
+ S("2012-12-24 15:30 GMT"),
+ S("2012-12-24 16:30 GMT+1"),
+ S("2012-12-24 16:30 GMT+01"),
+ S("2012-12-24 16:30 GMT+0100"),
+ S("2012-12-24 16:30 GMT+01:00"),
+ S("2012-12-24 14:30 GMT-1"),
+ S("2012-12-24 14:30 GMT-01"),
+ S("2012-12-24 14:30 GMT-01:00"),
+ S("2012-12-24 14:30 GMT-0100"),
+ S("2012-12-24 14:30 -01:00"),
+ S("2012-12-24 16:30:00 +01:00"),
+ S("2012-12-24 14:30:00 -01:00"),
+ S("2012-12-24 16:30:00.123456 +01:00"),
+ S("2012-12-24 14:30:00.123456 -01:00"),
+ S("2012-12-24 15:30:00.123456 -00:00"),
+ S("20121224T1630+01:00"),
+ S("2012-12-24T1630+01:00"),
+ S("20121224T16:30+01"),
+ S("20121224T16:30 +01"),
+};
+#undef S
+
+#define DIM(a) (sizeof(a) / sizeof(a[0]))
+
+// p5-time/moment/src/moment_parse.c: parse_string_lenient()
+static int
+parse_datetime(const char *str, size_t len, int64_t *sp, int64_t *np,
+ int64_t *op)
+{
+ size_t n;
+ dt_t dt;
+ char c;
+ int sod, nanosecond, offset;
+
+ n = dt_parse_iso_date(str, len, &dt);
+ if (!n || n == len)
+ return 1;
+
+ c = str[n++];
+ if (!(c == 'T' || c == 't' || c == ' '))
+ return 1;
+
+ str += n;
+ len -= n;
+
+ n = dt_parse_iso_time(str, len, &sod, &nanosecond);
+ if (!n || n == len)
+ return 1;
+
+ if (str[n] == ' ')
+ n++;
+
+ str += n;
+ len -= n;
+
+ n = dt_parse_iso_zone_lenient(str, len, &offset);
+ if (!n || n != len)
+ return 1;
+
+ *sp = ((int64_t)dt_rdn(dt) - 719163) * 86400 + sod - offset * 60;
+ *np = nanosecond;
+ *op = offset;
+
+ return 0;
+}
+
+static void datetime_test(void)
+{
+ size_t index;
+ int64_t secs_expected;
+ int64_t nanosecs;
+ int64_t ofs;
+
+ plan(132);
+ parse_datetime(sample, sizeof(sample) - 1,
+ &secs_expected, &nanosecs, &ofs);
+
+ for (index = 0; index < DIM(tests); index++) {
+ int64_t secs;
+ int rc = parse_datetime(tests[index].sz, tests[index].len,
+ &secs, &nanosecs, &ofs);
+ is(rc, 0, "correct parse_datetime return value for '%s'",
+ tests[index].sz);
+ is(secs, secs_expected, "correct parse_datetime output "
+ "seconds for '%s", tests[index].sz);
+ }
+}
+
+int
+main(void)
+{
+ plan(1);
+ datetime_test();
+
+ return check_plan();
+}
diff --git a/test/unit/datetime.result b/test/unit/datetime.result
new file mode 100644
index 000000000..5cd68dea5
--- /dev/null
+++ b/test/unit/datetime.result
@@ -0,0 +1,135 @@
+1..1
+ 1..132
+ ok 1 - correct parse_datetime return value for '2012-12-24 15:30Z'
+ ok 2 - correct parse_datetime output seconds for '2012-12-24 15:30Z
+ ok 3 - correct parse_datetime return value for '2012-12-24 15:30z'
+ ok 4 - correct parse_datetime output seconds for '2012-12-24 15:30z
+ ok 5 - correct parse_datetime return value for '2012-12-24 16:30+01:00'
+ ok 6 - correct parse_datetime output seconds for '2012-12-24 16:30+01:00
+ ok 7 - correct parse_datetime return value for '2012-12-24 16:30+0100'
+ ok 8 - correct parse_datetime output seconds for '2012-12-24 16:30+0100
+ ok 9 - correct parse_datetime return value for '2012-12-24 16:30+01'
+ ok 10 - correct parse_datetime output seconds for '2012-12-24 16:30+01
+ ok 11 - correct parse_datetime return value for '2012-12-24 14:30-01:00'
+ ok 12 - correct parse_datetime output seconds for '2012-12-24 14:30-01:00
+ ok 13 - correct parse_datetime return value for '2012-12-24 14:30-0100'
+ ok 14 - correct parse_datetime output seconds for '2012-12-24 14:30-0100
+ ok 15 - correct parse_datetime return value for '2012-12-24 14:30-01'
+ ok 16 - correct parse_datetime output seconds for '2012-12-24 14:30-01
+ ok 17 - correct parse_datetime return value for '2012-12-24 15:30:00Z'
+ ok 18 - correct parse_datetime output seconds for '2012-12-24 15:30:00Z
+ ok 19 - correct parse_datetime return value for '2012-12-24 15:30:00z'
+ ok 20 - correct parse_datetime output seconds for '2012-12-24 15:30:00z
+ ok 21 - correct parse_datetime return value for '2012-12-24 16:30:00+01:00'
+ ok 22 - correct parse_datetime output seconds for '2012-12-24 16:30:00+01:00
+ ok 23 - correct parse_datetime return value for '2012-12-24 16:30:00+0100'
+ ok 24 - correct parse_datetime output seconds for '2012-12-24 16:30:00+0100
+ ok 25 - correct parse_datetime return value for '2012-12-24 14:30:00-01:00'
+ ok 26 - correct parse_datetime output seconds for '2012-12-24 14:30:00-01:00
+ ok 27 - correct parse_datetime return value for '2012-12-24 14:30:00-0100'
+ ok 28 - correct parse_datetime output seconds for '2012-12-24 14:30:00-0100
+ ok 29 - correct parse_datetime return value for '2012-12-24 15:30:00.123456Z'
+ ok 30 - correct parse_datetime output seconds for '2012-12-24 15:30:00.123456Z
+ ok 31 - correct parse_datetime return value for '2012-12-24 15:30:00.123456z'
+ ok 32 - correct parse_datetime output seconds for '2012-12-24 15:30:00.123456z
+ ok 33 - correct parse_datetime return value for '2012-12-24 16:30:00.123456+01:00'
+ ok 34 - correct parse_datetime output seconds for '2012-12-24 16:30:00.123456+01:00
+ ok 35 - correct parse_datetime return value for '2012-12-24 16:30:00.123456+01'
+ ok 36 - correct parse_datetime output seconds for '2012-12-24 16:30:00.123456+01
+ ok 37 - correct parse_datetime return value for '2012-12-24 14:30:00.123456-01:00'
+ ok 38 - correct parse_datetime output seconds for '2012-12-24 14:30:00.123456-01:00
+ ok 39 - correct parse_datetime return value for '2012-12-24 14:30:00.123456-01'
+ ok 40 - correct parse_datetime output seconds for '2012-12-24 14:30:00.123456-01
+ ok 41 - correct parse_datetime return value for '2012-12-24t15:30Z'
+ ok 42 - correct parse_datetime output seconds for '2012-12-24t15:30Z
+ ok 43 - correct parse_datetime return value for '2012-12-24t15:30z'
+ ok 44 - correct parse_datetime output seconds for '2012-12-24t15:30z
+ ok 45 - correct parse_datetime return value for '2012-12-24t16:30+01:00'
+ ok 46 - correct parse_datetime output seconds for '2012-12-24t16:30+01:00
+ ok 47 - correct parse_datetime return value for '2012-12-24t16:30+0100'
+ ok 48 - correct parse_datetime output seconds for '2012-12-24t16:30+0100
+ ok 49 - correct parse_datetime return value for '2012-12-24t14:30-01:00'
+ ok 50 - correct parse_datetime output seconds for '2012-12-24t14:30-01:00
+ ok 51 - correct parse_datetime return value for '2012-12-24t14:30-0100'
+ ok 52 - correct parse_datetime output seconds for '2012-12-24t14:30-0100
+ ok 53 - correct parse_datetime return value for '2012-12-24t15:30:00Z'
+ ok 54 - correct parse_datetime output seconds for '2012-12-24t15:30:00Z
+ ok 55 - correct parse_datetime return value for '2012-12-24t15:30:00z'
+ ok 56 - correct parse_datetime output seconds for '2012-12-24t15:30:00z
+ ok 57 - correct parse_datetime return value for '2012-12-24t16:30:00+01:00'
+ ok 58 - correct parse_datetime output seconds for '2012-12-24t16:30:00+01:00
+ ok 59 - correct parse_datetime return value for '2012-12-24t16:30:00+0100'
+ ok 60 - correct parse_datetime output seconds for '2012-12-24t16:30:00+0100
+ ok 61 - correct parse_datetime return value for '2012-12-24t14:30:00-01:00'
+ ok 62 - correct parse_datetime output seconds for '2012-12-24t14:30:00-01:00
+ ok 63 - correct parse_datetime return value for '2012-12-24t14:30:00-0100'
+ ok 64 - correct parse_datetime output seconds for '2012-12-24t14:30:00-0100
+ ok 65 - correct parse_datetime return value for '2012-12-24t15:30:00.123456Z'
+ ok 66 - correct parse_datetime output seconds for '2012-12-24t15:30:00.123456Z
+ ok 67 - correct parse_datetime return value for '2012-12-24t15:30:00.123456z'
+ ok 68 - correct parse_datetime output seconds for '2012-12-24t15:30:00.123456z
+ ok 69 - correct parse_datetime return value for '2012-12-24t16:30:00.123456+01:00'
+ ok 70 - correct parse_datetime output seconds for '2012-12-24t16:30:00.123456+01:00
+ ok 71 - correct parse_datetime return value for '2012-12-24t14:30:00.123456-01:00'
+ ok 72 - correct parse_datetime output seconds for '2012-12-24t14:30:00.123456-01:00
+ ok 73 - correct parse_datetime return value for '2012-12-24 16:30 +01:00'
+ ok 74 - correct parse_datetime output seconds for '2012-12-24 16:30 +01:00
+ ok 75 - correct parse_datetime return value for '2012-12-24 14:30 -01:00'
+ ok 76 - correct parse_datetime output seconds for '2012-12-24 14:30 -01:00
+ ok 77 - correct parse_datetime return value for '2012-12-24 15:30 UTC'
+ ok 78 - correct parse_datetime output seconds for '2012-12-24 15:30 UTC
+ ok 79 - correct parse_datetime return value for '2012-12-24 16:30 UTC+1'
+ ok 80 - correct parse_datetime output seconds for '2012-12-24 16:30 UTC+1
+ ok 81 - correct parse_datetime return value for '2012-12-24 16:30 UTC+01'
+ ok 82 - correct parse_datetime output seconds for '2012-12-24 16:30 UTC+01
+ ok 83 - correct parse_datetime return value for '2012-12-24 16:30 UTC+0100'
+ ok 84 - correct parse_datetime output seconds for '2012-12-24 16:30 UTC+0100
+ ok 85 - correct parse_datetime return value for '2012-12-24 16:30 UTC+01:00'
+ ok 86 - correct parse_datetime output seconds for '2012-12-24 16:30 UTC+01:00
+ ok 87 - correct parse_datetime return value for '2012-12-24 14:30 UTC-1'
+ ok 88 - correct parse_datetime output seconds for '2012-12-24 14:30 UTC-1
+ ok 89 - correct parse_datetime return value for '2012-12-24 14:30 UTC-01'
+ ok 90 - correct parse_datetime output seconds for '2012-12-24 14:30 UTC-01
+ ok 91 - correct parse_datetime return value for '2012-12-24 14:30 UTC-01:00'
+ ok 92 - correct parse_datetime output seconds for '2012-12-24 14:30 UTC-01:00
+ ok 93 - correct parse_datetime return value for '2012-12-24 14:30 UTC-0100'
+ ok 94 - correct parse_datetime output seconds for '2012-12-24 14:30 UTC-0100
+ ok 95 - correct parse_datetime return value for '2012-12-24 15:30 GMT'
+ ok 96 - correct parse_datetime output seconds for '2012-12-24 15:30 GMT
+ ok 97 - correct parse_datetime return value for '2012-12-24 16:30 GMT+1'
+ ok 98 - correct parse_datetime output seconds for '2012-12-24 16:30 GMT+1
+ ok 99 - correct parse_datetime return value for '2012-12-24 16:30 GMT+01'
+ ok 100 - correct parse_datetime output seconds for '2012-12-24 16:30 GMT+01
+ ok 101 - correct parse_datetime return value for '2012-12-24 16:30 GMT+0100'
+ ok 102 - correct parse_datetime output seconds for '2012-12-24 16:30 GMT+0100
+ ok 103 - correct parse_datetime return value for '2012-12-24 16:30 GMT+01:00'
+ ok 104 - correct parse_datetime output seconds for '2012-12-24 16:30 GMT+01:00
+ ok 105 - correct parse_datetime return value for '2012-12-24 14:30 GMT-1'
+ ok 106 - correct parse_datetime output seconds for '2012-12-24 14:30 GMT-1
+ ok 107 - correct parse_datetime return value for '2012-12-24 14:30 GMT-01'
+ ok 108 - correct parse_datetime output seconds for '2012-12-24 14:30 GMT-01
+ ok 109 - correct parse_datetime return value for '2012-12-24 14:30 GMT-01:00'
+ ok 110 - correct parse_datetime output seconds for '2012-12-24 14:30 GMT-01:00
+ ok 111 - correct parse_datetime return value for '2012-12-24 14:30 GMT-0100'
+ ok 112 - correct parse_datetime output seconds for '2012-12-24 14:30 GMT-0100
+ ok 113 - correct parse_datetime return value for '2012-12-24 14:30 -01:00'
+ ok 114 - correct parse_datetime output seconds for '2012-12-24 14:30 -01:00
+ ok 115 - correct parse_datetime return value for '2012-12-24 16:30:00 +01:00'
+ ok 116 - correct parse_datetime output seconds for '2012-12-24 16:30:00 +01:00
+ ok 117 - correct parse_datetime return value for '2012-12-24 14:30:00 -01:00'
+ ok 118 - correct parse_datetime output seconds for '2012-12-24 14:30:00 -01:00
+ ok 119 - correct parse_datetime return value for '2012-12-24 16:30:00.123456 +01:00'
+ ok 120 - correct parse_datetime output seconds for '2012-12-24 16:30:00.123456 +01:00
+ ok 121 - correct parse_datetime return value for '2012-12-24 14:30:00.123456 -01:00'
+ ok 122 - correct parse_datetime output seconds for '2012-12-24 14:30:00.123456 -01:00
+ ok 123 - correct parse_datetime return value for '2012-12-24 15:30:00.123456 -00:00'
+ ok 124 - correct parse_datetime output seconds for '2012-12-24 15:30:00.123456 -00:00
+ ok 125 - correct parse_datetime return value for '20121224T1630+01:00'
+ ok 126 - correct parse_datetime output seconds for '20121224T1630+01:00
+ ok 127 - correct parse_datetime return value for '2012-12-24T1630+01:00'
+ ok 128 - correct parse_datetime output seconds for '2012-12-24T1630+01:00
+ ok 129 - correct parse_datetime return value for '20121224T16:30+01'
+ ok 130 - correct parse_datetime output seconds for '20121224T16:30+01
+ ok 131 - correct parse_datetime return value for '20121224T16:30 +01'
+ ok 132 - correct parse_datetime output seconds for '20121224T16:30 +01
+ok 1 - subtests
--
2.29.2
More information about the Tarantool-patches
mailing list