[Tarantool-patches] [PATCH v3 2/2] lua, datetime: introduce ctime, strftime wrappers
Safin Timur
tsafin at tarantool.org
Fri Aug 6 03:30:05 MSK 2021
This change is now part of patch 2/9 lua:built-in module datetime, and
there is no platform specific Lua code in the module.
Timur
On 04.08.2021 0:23, Timur Safin wrote:
> 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
>
>
More information about the Tarantool-patches
mailing list