Commit 8d34de30 authored by Nikias Bassen's avatar Nikias Bassen

Use time64 implementation by Michael G Schwern to extend allowed date/time range

The main benefit of this is to allow date/time values outside of the 32bit time_t
range which is very important on 32bit platforms. But there are also some other
issues that will be fixed with this, for example on macOS, mktime() will not work
for dates < 1902 despite time_t being 64bit.

In the same run this commit will also use a reentrant version of gmtime64_r that
should help in multithreaded scenarios.

Original code taken from: https://github.com/evalEmpire/y2038
parent 912cb459
......@@ -56,7 +56,7 @@ AC_TYPE_UINT32_T
AC_TYPE_UINT8_T
# Checks for library functions.
AC_CHECK_FUNCS([asprintf strcasecmp strdup strerror strndup stpcpy vasprintf])
AC_CHECK_FUNCS([asprintf strcasecmp strdup strerror strndup stpcpy vasprintf gmtime_r localtime_r timegm])
# Checking endianness
AC_C_BIGENDIAN([AC_DEFINE([__BIG_ENDIAN__], [1], [big endian])],
......@@ -78,6 +78,32 @@ case ${host_os} in
esac
AM_CONDITIONAL(WIN32, test x$win32 = xtrue)
# Check if struct tm has a tm_gmtoff member
AC_CACHE_CHECK(for tm_gmtoff in struct tm, ac_cv_struct_tm_gmtoff,
AC_TRY_COMPILE([
#include <time.h>
], [
struct tm tm;
tm.tm_gmtoff = 1;
], ac_cv_struct_tm_gmtoff=yes, ac_cv_struct_tm_gmtoff=no))
if (test "$ac_cv_struct_tm_gmtoff" = "yes"); then
AC_DEFINE(HAVE_TM_TM_GMTOFF, 1, [Define if struct tm has a tm_gmtoff member])
fi
# Check if struct tm has a tm_zone member
AC_CACHE_CHECK(for tm_zone in struct tm, ac_cv_struct_tm_zone,
AC_TRY_COMPILE([
#include <time.h>
], [
struct tm tm;
tm.tm_zone = 1;
], ac_cv_struct_tm_zone=yes, ac_cv_struct_tm_zone=no))
if (test "$ac_cv_struct_tm_zone" = "yes"); then
AC_DEFINE(HAVE_TM_TM_ZONE, 1, [Define if struct tm has a tm_zone member])
fi
# Cython Python Bindings
AC_ARG_WITH([cython],
[AS_HELP_STRING([--without-cython],
......
......@@ -10,6 +10,7 @@ libplist_la_SOURCES = base64.c base64.h \
bytearray.c bytearray.h \
hashtable.c hashtable.h \
ptrarray.c ptrarray.h \
time64.c time64.h time64_limits.h \
xplist.c \
bplist.c \
plist.c plist.h
......
This diff is collapsed.
#ifndef TIME64_H
# define TIME64_H
#include <time.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* Set our custom types */
typedef long long Int64;
typedef Int64 Time64_T;
typedef Int64 Year;
/* A copy of the tm struct but with a 64 bit year */
struct TM64 {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
Year tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
#ifdef HAVE_TM_TM_GMTOFF
long tm_gmtoff;
#endif
#ifdef HAVE_TM_TM_ZONE
char *tm_zone;
#endif
};
/* Decide which tm struct to use */
#ifdef USE_TM64
#define TM TM64
#else
#define TM tm
#endif
/* Declare public functions */
struct TM *gmtime64_r (const Time64_T *, struct TM *);
struct TM *localtime64_r (const Time64_T *, struct TM *);
struct TM *gmtime64 (const Time64_T *);
struct TM *localtime64 (const Time64_T *);
char *asctime64 (const struct TM *);
char *asctime64_r (const struct TM *, char *);
char *ctime64 (const Time64_T*);
char *ctime64_r (const Time64_T*, char*);
Time64_T timegm64 (const struct TM *);
Time64_T mktime64 (struct TM *);
Time64_T timelocal64 (struct TM *);
/* Not everyone has gm/localtime_r(), provide a replacement */
#ifdef HAVE_LOCALTIME_R
# define LOCALTIME_R(clock, result) localtime_r(clock, result)
#else
# define LOCALTIME_R(clock, result) fake_localtime_r(clock, result)
#endif
#ifdef HAVE_GMTIME_R
# define GMTIME_R(clock, result) gmtime_r(clock, result)
#else
# define GMTIME_R(clock, result) fake_gmtime_r(clock, result)
#endif
/* Use a different asctime format depending on how big the year is */
#ifdef USE_TM64
#define TM64_ASCTIME_FORMAT "%.3s %.3s%3d %.2d:%.2d:%.2d %lld\n"
#else
#define TM64_ASCTIME_FORMAT "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n"
#endif
void copy_tm_to_TM64(const struct tm *src, struct TM *dest);
void copy_TM64_to_tm(const struct TM *src, struct tm *dest);
#endif
/*
Maximum and minimum inputs your system's respective time functions
can correctly handle. time64.h will use your system functions if
the input falls inside these ranges and corresponding USE_SYSTEM_*
constant is defined.
*/
#ifndef TIME64_LIMITS_H
#define TIME64_LIMITS_H
/* Max/min for localtime() */
#define SYSTEM_LOCALTIME_MAX 2147483647
#define SYSTEM_LOCALTIME_MIN -2147483647-1
/* Max/min for gmtime() */
#define SYSTEM_GMTIME_MAX 2147483647
#define SYSTEM_GMTIME_MIN -2147483647-1
/* Max/min for mktime() */
static const struct tm SYSTEM_MKTIME_MAX = {
7,
14,
19,
18,
0,
138,
1,
17,
0
#ifdef HAVE_TM_TM_GMTOFF
,-28800
#endif
#ifdef HAVE_TM_TM_ZONE
,(char*)"PST"
#endif
};
static const struct tm SYSTEM_MKTIME_MIN = {
52,
45,
12,
13,
11,
1,
5,
346,
0
#ifdef HAVE_TM_TM_GMTOFF
,-28800
#endif
#ifdef HAVE_TM_TM_ZONE
,(char*)"PST"
#endif
};
/* Max/min for timegm() */
#ifdef HAVE_TIMEGM
static const struct tm SYSTEM_TIMEGM_MAX = {
7,
14,
3,
19,
0,
138,
2,
18,
0
#ifdef HAVE_TM_TM_GMTOFF
,0
#endif
#ifdef HAVE_TM_TM_ZONE
,(char*)"UTC"
#endif
};
static const struct tm SYSTEM_TIMEGM_MIN = {
52,
45,
20,
13,
11,
1,
5,
346,
0
#ifdef HAVE_TM_TM_GMTOFF
,0
#endif
#ifdef HAVE_TM_TM_ZONE
,(char*)"UTC"
#endif
};
#endif /* HAVE_TIMEGM */
#endif /* TIME64_LIMITS_H */
......@@ -41,6 +41,7 @@
#include "plist.h"
#include "base64.h"
#include "time64.h"
#define XPLIST_TEXT BAD_CAST("text")
#define XPLIST_KEY BAD_CAST("key")
......@@ -258,12 +259,15 @@ static void node_to_xml(node_t* node, void *xml_struct)
case PLIST_DATE:
tag = XPLIST_DATE;
{
time_t timev = (time_t)node_data->realval + MAC_EPOCH;
struct tm *btime = gmtime(&timev);
Time64_T timev = (Time64_T)node_data->realval + MAC_EPOCH;
struct TM _btime;
struct TM *btime = gmtime64_r(&timev, &_btime);
if (btime) {
val = (char*)malloc(24);
memset(val, 0, 24);
if (strftime(val, 24, "%Y-%m-%dT%H:%M:%SZ", btime) <= 0) {
struct tm _tmcopy;
copy_TM64_to_tm(btime, &_tmcopy);
if (strftime(val, 24, "%Y-%m-%dT%H:%M:%SZ", &_tmcopy) <= 0) {
free (val);
val = NULL;
}
......@@ -346,7 +350,7 @@ static void node_to_xml(node_t* node, void *xml_struct)
return;
}
static void parse_date(const char *strval, struct tm *btime)
static void parse_date(const char *strval, struct TM *btime)
{
if (!btime) return;
memset(btime, 0, sizeof(struct tm));
......@@ -354,7 +358,12 @@ static void parse_date(const char *strval, struct tm *btime)
#ifdef strptime
strptime((char*)strval, "%Y-%m-%dT%H:%M:%SZ", btime);
#else
sscanf(strval, "%d-%d-%dT%d:%d:%dZ", &btime->tm_year, &btime->tm_mon, &btime->tm_mday, &btime->tm_hour, &btime->tm_min, &btime->tm_sec);
#ifdef USE_TM64
#define PLIST_SSCANF_FORMAT "%lld-%d-%dT%d:%d:%dZ"
#else
#define PLIST_SSCANF_FORMAT "%d-%d-%dT%d:%d:%dZ"
#endif
sscanf(strval, PLIST_SSCANF_FORMAT, &btime->tm_year, &btime->tm_mon, &btime->tm_mday, &btime->tm_hour, &btime->tm_min, &btime->tm_sec);
btime->tm_year-=1900;
btime->tm_mon--;
#endif
......@@ -453,14 +462,11 @@ static void xml_to_node(xmlNodePtr xml_node, plist_t * plist_node)
if (!xmlStrcmp(node->name, XPLIST_DATE))
{
xmlChar *strval = xmlNodeGetContent(node);
time_t timev = 0;
Time64_T timev = 0;
if (strlen((const char*)strval) >= 11) {
struct tm btime;
struct tm* tm_utc;
struct TM btime;
parse_date((const char*)strval, &btime);
timev = mktime(&btime);
tm_utc = gmtime(&timev);
timev -= (mktime(tm_utc) - timev);
timev = timegm64(&btime);
}
data->realval = (double)(timev - MAC_EPOCH);
data->type = PLIST_DATE;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment