[Tarantool-patches] [PATCH v3 2/2] lua, datetime: introduce ctime, strftime wrappers

Timur Safin tsafin at tarantool.org
Wed Aug 4 00:23:50 MSK 2021


To make Lua code less platfrom dependent we move
all platform specifics to the C level. Specifically
we want to avoid dependence on `struct tm {}` layout
and GLIBC `strftime` behaviour.
As a bonus we have that after moving of time dependent
code to C we make Lua code significantly simpler and thus
may get rid of all implementation specific details
with numerous typedefs and function declarations.

Part of #5941
---
 src/exports.h           |   4 ++
 src/lib/core/datetime.c |  63 +++++++++++++++++-
 src/lib/core/datetime.h |  23 ++++++-
 src/lua/datetime.lua    | 139 +++++-----------------------------------
 4 files changed, 104 insertions(+), 125 deletions(-)

diff --git a/src/exports.h b/src/exports.h
index 63efe0ec7..f4a6f98d7 100644
--- a/src/exports.h
+++ b/src/exports.h
@@ -218,6 +218,10 @@ EXPORT(curl_version)
 EXPORT(curl_version_info)
 #endif /* EXPORT_LIBCURL_SYMBOLS */
 EXPORT(datetime_pack)
+EXPORT(datetime_asctime)
+EXPORT(datetime_ctime)
+EXPORT(datetime_now)
+EXPORT(datetime_strftime)
 EXPORT(datetime_to_string)
 EXPORT(datetime_unpack)
 EXPORT(decimal_unpack)
diff --git a/src/lib/core/datetime.c b/src/lib/core/datetime.c
index 03facb123..a921cbd11 100755
--- a/src/lib/core/datetime.c
+++ b/src/lib/core/datetime.c
@@ -30,6 +30,7 @@
  */
 
 #include <string.h>
+#include <time.h>
 
 #include "trivia/util.h"
 #include "datetime.h"
@@ -165,12 +166,35 @@ mp_encode_datetime(char *data, const struct datetime *date)
 	return datetime_pack(data, date);
 }
 
+static int
+local_dt(int64_t secs)
+{
+	return dt_from_rdn((int)(secs / SECS_PER_DAY) + DT_EPOCH_1970_OFFSET);
+}
+
+static struct tm *
+datetime_to_tm(const struct datetime *date)
+{
+	static struct tm tm;
+
+	memset(&tm, 0, sizeof(tm));
+	int64_t secs = date->secs;
+	dt_to_struct_tm(local_dt(secs), &tm);
+
+	int seconds_of_day = date->secs % SECS_PER_DAY;
+	tm.tm_hour = (seconds_of_day / 3600) % 24;
+	tm.tm_min = (seconds_of_day / 60) % 60;
+	tm.tm_sec = seconds_of_day % 60;
+
+	return &tm;
+}
+
 int datetime_to_string(const struct datetime *date, char *buf, uint32_t len)
 {
 	char * src = buf;
 	int offset = date->offset;
 	int64_t secs = date->secs + offset * 60;
-	dt_t dt = dt_from_rdn((secs / SECS_PER_DAY) + 719163);
+	dt_t dt = local_dt(secs);
 
 	int year, month, day, sec, ns, sign;
 	dt_to_ymd(dt, &year, &month, &day);
@@ -214,6 +238,43 @@ int datetime_to_string(const struct datetime *date, char *buf, uint32_t len)
 	return (buf - src);
 }
 
+void
+datetime_now(struct datetime * now)
+{
+	struct timeval tv;
+	gettimeofday(&tv, NULL);
+	now->secs = tv.tv_sec;
+	now->nsec = tv.tv_usec * 1000;
+
+	time_t now_seconds;
+	time(&now_seconds);
+	struct tm tm;
+	localtime_r(&now_seconds, &tm);
+	now->offset = tm.tm_gmtoff / 60;
+}
+
+char *
+datetime_asctime(const struct datetime *date)
+{
+	struct tm *p_tm = datetime_to_tm(date);
+	return asctime(p_tm);
+}
+
+char *
+datetime_ctime(const struct datetime *date)
+{
+	time_t time = date->secs;
+	return ctime(&time);
+}
+
+size_t
+datetime_strftime(const struct datetime *date, const char *fmt, char *buf,
+		  uint32_t len)
+{
+	struct tm *p_tm = datetime_to_tm(date);
+	return strftime(buf, len, fmt, p_tm);
+}
+
 int
 mp_snprint_datetime(char *buf, int size, const char **data, uint32_t len)
 {
diff --git a/src/lib/core/datetime.h b/src/lib/core/datetime.h
index 540bd68d9..012fb340d 100644
--- a/src/lib/core/datetime.h
+++ b/src/lib/core/datetime.h
@@ -42,6 +42,7 @@ extern "C"
 
 #ifndef SECS_PER_DAY
 #define SECS_PER_DAY	86400
+#define DT_EPOCH_1970_OFFSET 719163
 #endif
 
 /**
@@ -102,7 +103,27 @@ mp_encode_datetime(char *data, const struct datetime *date);
  * @param buf output character buffer
  * @param len size ofoutput buffer
  */
-int datetime_to_string(const struct datetime *date, char *buf, uint32_t len);
+int
+datetime_to_string(const struct datetime *date, char *buf, uint32_t len);
+
+/**
+ * Convert datetime to string using default asctime format
+ * "Sun Sep 16 01:03:52 1973\n\0"
+ * @param date source datetime value
+ * @sa datetime_ctime
+ */
+char *
+datetime_asctime(const struct datetime *date);
+
+char *
+datetime_ctime(const struct datetime *date);
+
+size_t
+datetime_strftime(const struct datetime *date, const char *fmt, char *buf,
+		  uint32_t len);
+
+void
+datetime_now(struct datetime * now);
 
 int
 mp_snprint_datetime(char *buf, int size, const char **data, uint32_t len);
diff --git a/src/lua/datetime.lua b/src/lua/datetime.lua
index 5cad4e02f..1549735bd 100644
--- a/src/lua/datetime.lua
+++ b/src/lua/datetime.lua
@@ -82,90 +82,18 @@ ffi.cdef [[
     int
     datetime_to_string(const struct datetime * date, char *buf, uint32_t len);
 
+    char *
+    datetime_asctime(const struct datetime *date);
 
-    // <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;
+    char *
+    datetime_ctime(const struct datetime *date);
 
+    size_t
+    datetime_strftime(const struct datetime *date, const char *fmt, char *buf,
+                      uint32_t len);
 
-    // <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);
+    void
+    datetime_now(struct datetime * now);
 
 ]]
 
@@ -921,62 +849,27 @@ local function datetime_from(o)
 end
 
 local function local_now()
-    local p_tv = ffi.new('struct timeval [1]')
-    local rc = builtin.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]')
-    builtin.time(p_time)
-    builtin.localtime_r(p_time, p_tm)
-    local ofs = p_tm[0].tm_gmtoff / 60 -- convert seconds to minutes
-
-    return datetime_new_raw(secs, nsec, ofs)
-end
-
-local function datetime_to_tm_ptr(o)
-    assert(is_datetime(o))
-    local p_tm = ffi.new('struct tm[1]')
-    -- dt_to_struct_tm() fills only date data
-    builtin.dt_to_struct_tm(local_dt(o), p_tm)
-
-    -- calculate the smaller data (hour, minute,
-    -- seconds) using datetime seconds value
-    local seconds_of_day = o.secs % 86400
-    local hour = (seconds_of_day / 3600) % 24
-    local minute = (seconds_of_day / 60) % 60
-    p_tm[0].tm_sec = seconds_of_day % 60
-    p_tm[0].tm_min = minute
-    p_tm[0].tm_hour = hour
-
-    p_tm[0].tm_gmtoff = o.offset * 60
-
-    return p_tm
+    local d = datetime_new_raw(0, 0, 0)
+    builtin.datetime_now(d)
+    return d
 end
 
 local function asctime(o)
     check_date(o, "datetime:asctime()")
 
-    local p_tm = datetime_to_tm_ptr(o)
-    return ffi.string(builtin.asctime(p_tm))
+    return ffi.string(builtin.datetime_asctime(o))
 end
 
 local function ctime(o)
     check_date(o, "datetime:ctime()")
-    local p_time = ffi.new('time_t[1]')
-    p_time[0] = o.secs
-    return ffi.string(builtin.ctime(p_time))
+    return ffi.string(builtin.datetime_ctime(o))
 end
 
 local function strftime(fmt, o)
     check_date(o, "datetime.strftime()")
-    local p_tm = datetime_to_tm_ptr(o)
-    local sz = builtin.strftime(nil, 1024, fmt, p_tm) + 1
+    local sz = 128
     local buff = ffi.new('char[?]', sz)
-    builtin.strftime(buff, sz, fmt, p_tm)
+    builtin.datetime_strftime(o, fmt, buff, sz)
     return ffi.string(buff)
 end
 
-- 
2.29.2



More information about the Tarantool-patches mailing list