summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael G. Schwern <schwern@pobox.com>2008-10-02 16:18:54 -0400
committerRafael Garcia-Suarez <rgarciasuarez@gmail.com>2009-01-03 18:38:56 +0100
commit806a119aef40085e6ee4a5a8bbab77cca98c9d08 (patch)
tree8ed1365a73b59eff53d7a2e2e219d3be5b3f6180
parent7643e68fdb02e6f17d9f2a5801be920285971156 (diff)
downloadperl-806a119aef40085e6ee4a5a8bbab77cca98c9d08.tar.gz
Remove the AIX work around code. Instead it should just set it's LOCALTIME_MAX to 0x7fff573e.
Update from y2038. Use the new TM64 struct so years can go out past y2**31 Defines a Year type to avoid converting years to ints. Remove the TIMGM work around code, using timegm64() is fine and it saves us from having to convert from TM to tm. Make functions private with static rather than the _foo convention. Even faster for distant dates.
-rw-r--r--pp_sys.c55
-rw-r--r--time64.c143
-rw-r--r--time64.h42
3 files changed, 149 insertions, 91 deletions
diff --git a/pp_sys.c b/pp_sys.c
index 788aa7b680..74958ac43f 100644
--- a/pp_sys.c
+++ b/pp_sys.c
@@ -201,15 +201,6 @@ void endservent(void);
#undef PERL_EFF_ACCESS /* EFFective uid/gid ACCESS */
-/* AIX 5.2 and below use mktime for localtime, and defines the edge case
- * for time 0x7fffffff to be valid only in UTC. AIX 5.3 provides localtime64
- * available in the 32bit environment, which could warrant Configure
- * checks in the future.
- */
-#ifdef _AIX
-#define LOCALTIME_EDGECASE_BROKEN
-#endif
-
/* F_OK unused: if stat() cannot find it... */
#if !defined(PERL_EFF_ACCESS) && defined(HAS_ACCESS) && defined(EFF_ONLY_OK) && !defined(NO_EFF_ONLY_OK)
@@ -4404,53 +4395,13 @@ PP(pp_tms)
#endif /* HAS_TIMES */
}
-#ifdef LOCALTIME_EDGECASE_BROKEN
-static struct tm *S_my_localtime (pTHX_ Time_t *tp)
-{
- auto time_t T;
- auto struct tm *P;
-
- /* No workarounds in the valid range */
- if (!tp || *tp < 0x7fff573f || *tp >= 0x80000000)
- return (localtime (tp));
-
- /* This edge case is to workaround the undefined behaviour, where the
- * TIMEZONE makes the time go beyond the defined range.
- * gmtime (0x7fffffff) => 2038-01-19 03:14:07
- * If there is a negative offset in TZ, like MET-1METDST, some broken
- * implementations of localtime () (like AIX 5.2) barf with bogus
- * return values:
- * 0x7fffffff gmtime 2038-01-19 03:14:07
- * 0x7fffffff localtime 1901-12-13 21:45:51
- * 0x7fffffff mylocaltime 2038-01-19 04:14:07
- * 0x3c19137f gmtime 2001-12-13 20:45:51
- * 0x3c19137f localtime 2001-12-13 21:45:51
- * 0x3c19137f mylocaltime 2001-12-13 21:45:51
- * Given that legal timezones are typically between GMT-12 and GMT+12
- * we turn back the clock 23 hours before calling the localtime
- * function, and add those to the return value. This will never cause
- * day wrapping problems, since the edge case is Tue Jan *19*
- */
- T = *tp - 82800; /* 23 hour. allows up to GMT-23 */
- P = localtime (&T);
- P->tm_hour += 23;
- if (P->tm_hour >= 24) {
- P->tm_hour -= 24;
- P->tm_mday++; /* 18 -> 19 */
- P->tm_wday++; /* Mon -> Tue */
- P->tm_yday++; /* 18 -> 19 */
- }
- return (P);
-} /* S_my_localtime */
-#endif
-
PP(pp_gmtime)
{
dVAR;
dSP;
Time64_T when;
- struct tm tmbuf;
- struct tm *err;
+ struct TM tmbuf;
+ struct TM *err;
static const char * const dayname[] =
{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static const char * const monname[] =
@@ -4483,7 +4434,7 @@ PP(pp_gmtime)
if (err == NULL)
RETPUSHUNDEF;
- tsv = Perl_newSVpvf(aTHX_ "%s %s %2d %02d:%02d:%02d %d",
+ tsv = Perl_newSVpvf(aTHX_ "%s %s %2d %02d:%02d:%02d %lld",
dayname[tmbuf.tm_wday],
monname[tmbuf.tm_mon],
tmbuf.tm_mday,
diff --git a/time64.c b/time64.c
index cb74b55000..2cb6ad242a 100644
--- a/time64.c
+++ b/time64.c
@@ -54,11 +54,12 @@ static const int julian_days_by_month[2][12] = {
static const int length_of_year[2] = { 365, 366 };
/* Number of days in a 400 year Gregorian cycle */
-static const int years_in_gregorian_cycle = 400;
+static const Year years_in_gregorian_cycle = 400;
static const int days_in_gregorian_cycle = (365 * 400) + 100 - 4 + 1;
/* 28 year calendar cycle between 2010 and 2037 */
-static const int safe_years[28] = {
+#define SOLAR_CYCLE_LENGTH 28
+static const int safe_years[SOLAR_CYCLE_LENGTH] = {
2016, 2017, 2018, 2019,
2020, 2021, 2022, 2023,
2024, 2025, 2026, 2027,
@@ -68,7 +69,6 @@ static const int safe_years[28] = {
2012, 2013, 2014, 2015
};
-#define SOLAR_CYCLE_LENGTH 28
static const int dow_year_start[SOLAR_CYCLE_LENGTH] = {
5, 0, 1, 2, /* 0 2016 - 2019 */
3, 5, 6, 0, /* 4 */
@@ -102,7 +102,7 @@ static const int dow_year_start[SOLAR_CYCLE_LENGTH] = {
)
-int _is_exception_century(Int64 year)
+static int is_exception_century(Int64 year)
{
int is_exception = ((year % 100 == 0) && !(year % 400 == 0));
/* printf("is_exception_century: %s\n", is_exception ? "yes" : "no"); */
@@ -111,14 +111,7 @@ int _is_exception_century(Int64 year)
}
-/* timegm() is a GNU extension, so emulate it here if we need it */
-#ifdef HAS_TIMEGM
-# define TIMEGM(n) timegm(n);
-#else
-# define TIMEGM(n) ((time_t)timegm64(n));
-#endif
-
-Time64_T timegm64(struct tm *date) {
+Time64_T timegm64(struct TM *date) {
int days = 0;
Int64 seconds = 0;
Int64 year;
@@ -153,7 +146,7 @@ Time64_T timegm64(struct tm *date) {
}
-int _check_tm(struct tm *tm)
+static int check_tm(struct TM *tm)
{
/* Don't forget leap seconds */
assert(tm->tm_sec >= 0);
@@ -189,7 +182,7 @@ int _check_tm(struct tm *tm)
/* The exceptional centuries without leap years cause the cycle to
shift by 16
*/
-Year _cycle_offset(Year year)
+static Year cycle_offset(Year year)
{
const Year start_year = 2000;
Year year_diff = year - start_year;
@@ -226,17 +219,17 @@ Year _cycle_offset(Year year)
It doesn't need the same leap year status since we only care about
January 1st.
*/
-int _safe_year(Year year)
+static int safe_year(Year year)
{
int safe_year;
- Year year_cycle = year + _cycle_offset(year);
+ Year year_cycle = year + cycle_offset(year);
/* Change non-leap xx00 years to an equivalent */
- if( _is_exception_century(year) )
+ if( is_exception_century(year) )
year_cycle += 11;
/* Also xx01 years, since the previous year will be wrong */
- if( _is_exception_century(year - 1) )
+ if( is_exception_century(year - 1) )
year_cycle += 17;
year_cycle %= SOLAR_CYCLE_LENGTH;
@@ -258,6 +251,70 @@ int _safe_year(Year year)
}
+void copy_tm_to_TM(const struct tm *src, struct TM *dest) {
+ if( src == NULL ) {
+ memset(dest, 0, sizeof(*dest));
+ }
+ else {
+# ifdef USE_TM64
+ dest->tm_sec = src->tm_sec;
+ dest->tm_min = src->tm_min;
+ dest->tm_hour = src->tm_hour;
+ dest->tm_mday = src->tm_mday;
+ dest->tm_mon = src->tm_mon;
+ dest->tm_year = (Year)src->tm_year;
+ dest->tm_wday = src->tm_wday;
+ dest->tm_yday = src->tm_yday;
+ dest->tm_isdst = src->tm_isdst;
+
+# ifdef HAS_TM_TM_GMTOFF
+ dest->tm_gmtoff = src->tm_gmtoff;
+# endif
+
+# ifdef HAS_TM_TM_ZONE
+ dest->tm_zone = src->tm_zone;
+# endif
+
+# else
+ /* They're the same type */
+ memcpy(dest, src, sizeof(*dest));
+# endif
+ }
+}
+
+
+void copy_TM_to_tm(const struct TM *src, struct tm *dest) {
+ if( src == NULL ) {
+ memset(dest, 0, sizeof(*dest));
+ }
+ else {
+# ifdef USE_TM64
+ dest->tm_sec = src->tm_sec;
+ dest->tm_min = src->tm_min;
+ dest->tm_hour = src->tm_hour;
+ dest->tm_mday = src->tm_mday;
+ dest->tm_mon = src->tm_mon;
+ dest->tm_year = (int)src->tm_year;
+ dest->tm_wday = src->tm_wday;
+ dest->tm_yday = src->tm_yday;
+ dest->tm_isdst = src->tm_isdst;
+
+# ifdef HAS_TM_TM_GMTOFF
+ dest->tm_gmtoff = src->tm_gmtoff;
+# endif
+
+# ifdef HAS_TM_TM_ZONE
+ dest->tm_zone = src->tm_zone;
+# endif
+
+# else
+ /* They're the same type */
+ memcpy(dest, src, sizeof(*dest));
+# endif
+ }
+}
+
+
/* Simulate localtime_r() to the best of our ability */
struct tm * fake_localtime_r(const time_t *clock, struct tm *result) {
const struct tm *static_result = localtime(clock);
@@ -292,7 +349,7 @@ struct tm * fake_gmtime_r(const time_t *clock, struct tm *result) {
}
-struct tm *gmtime64_r (const Time64_T *in_time, struct tm *p)
+struct TM *gmtime64_r (const Time64_T *in_time, struct TM *p)
{
int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday;
Int64 v_tm_tday;
@@ -300,14 +357,19 @@ struct tm *gmtime64_r (const Time64_T *in_time, struct tm *p)
Int64 m;
Time64_T time = *in_time;
Year year = 70;
+ int cycles = 0;
assert(p != NULL);
/* Use the system gmtime() if time_t is small enough */
if( SHOULD_USE_SYSTEM_GMTIME(*in_time) ) {
time_t safe_time = *in_time;
- GMTIME_R(&safe_time, p);
- assert(_check_tm(p));
+ struct tm safe_date;
+ GMTIME_R(&safe_time, &safe_date);
+
+ copy_tm_to_TM(&safe_date, p);
+ assert(check_tm(p));
+
return p;
}
@@ -344,9 +406,10 @@ struct tm *gmtime64_r (const Time64_T *in_time, struct tm *p)
if (m >= 0) {
/* Gregorian cycles, this is huge optimization for distant times */
- while (m >= (Time64_T) days_in_gregorian_cycle) {
- m -= (Time64_T) days_in_gregorian_cycle;
- year += years_in_gregorian_cycle;
+ cycles = floor(m / (Time64_T) days_in_gregorian_cycle);
+ if( cycles ) {
+ m -= (cycles * (Time64_T) days_in_gregorian_cycle);
+ year += (cycles * years_in_gregorian_cycle);
}
/* Years */
@@ -367,9 +430,10 @@ struct tm *gmtime64_r (const Time64_T *in_time, struct tm *p)
year--;
/* Gregorian cycles */
- while (m < (Time64_T) -days_in_gregorian_cycle) {
- m += (Time64_T) days_in_gregorian_cycle;
- year -= years_in_gregorian_cycle;
+ cycles = ceil(m / (Time64_T) days_in_gregorian_cycle) + 1;
+ if( cycles ) {
+ m -= (cycles * (Time64_T) days_in_gregorian_cycle);
+ year += (cycles * years_in_gregorian_cycle);
}
/* Years */
@@ -402,16 +466,17 @@ struct tm *gmtime64_r (const Time64_T *in_time, struct tm *p)
p->tm_sec = v_tm_sec, p->tm_min = v_tm_min, p->tm_hour = v_tm_hour,
p->tm_mon = v_tm_mon, p->tm_wday = v_tm_wday;
- assert(_check_tm(p));
+ assert(check_tm(p));
return p;
}
-struct tm *localtime64_r (const Time64_T *time, struct tm *local_tm)
+struct TM *localtime64_r (const Time64_T *time, struct TM *local_tm)
{
time_t safe_time;
- struct tm gm_tm;
+ struct tm safe_date;
+ struct TM gm_tm;
Year orig_year;
int month_diff;
@@ -420,8 +485,12 @@ struct tm *localtime64_r (const Time64_T *time, struct tm *local_tm)
/* Use the system localtime() if time_t is small enough */
if( SHOULD_USE_SYSTEM_LOCALTIME(*time) ) {
safe_time = *time;
- LOCALTIME_R(&safe_time, local_tm);
- assert(_check_tm(local_tm));
+
+ LOCALTIME_R(&safe_time, &safe_date);
+
+ copy_tm_to_TM(&safe_date, local_tm);
+ assert(check_tm(local_tm));
+
return local_tm;
}
@@ -434,13 +503,15 @@ struct tm *localtime64_r (const Time64_T *time, struct tm *local_tm)
gm_tm.tm_year < (1902 - 1900)
)
{
- gm_tm.tm_year = _safe_year(gm_tm.tm_year + 1900) - 1900;
+ gm_tm.tm_year = safe_year(gm_tm.tm_year + 1900) - 1900;
}
- safe_time = TIMEGM(&gm_tm);
- if( LOCALTIME_R(&safe_time, local_tm) == NULL )
+ safe_time = timegm64(&gm_tm);
+ if( LOCALTIME_R(&safe_time, &safe_date) == NULL )
return NULL;
+ copy_tm_to_TM(&safe_date, local_tm);
+
local_tm->tm_year = orig_year;
if( local_tm->tm_year != orig_year ) {
#ifdef EOVERFLOW
@@ -475,7 +546,7 @@ struct tm *localtime64_r (const Time64_T *time, struct tm *local_tm)
if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 )
local_tm->tm_yday--;
- assert(_check_tm(local_tm));
+ assert(check_tm(local_tm));
return local_tm;
}
diff --git a/time64.h b/time64.h
index db8ddbf1ea..85e2ff59a6 100644
--- a/time64.h
+++ b/time64.h
@@ -1,3 +1,5 @@
+#include <time.h>
+
#ifndef LOCALTIME64_H
# define LOCALTIME64_H
@@ -29,7 +31,12 @@
USE_SYSTEM_LOCALTIME
USE_SYSTEM_GMTIME
Should we use the system functions if the time is inside their range?
+
+ USE_TM64
+ Should we use a 64 bit safe tm struct which can handle a
+ year range greater than 2 billion?
*/
+
#define SYSTEM_LOCALTIME_MAX LOCALTIME_MAX
#define SYSTEM_LOCALTIME_MIN LOCALTIME_MIN
#define SYSTEM_GMTIME_MAX GMTIME_MAX
@@ -39,15 +46,44 @@
#define USE_SYSTEM_LOCALTIME 1
#define USE_SYSTEM_GMTIME 1
+/* Let's get all the time */
+#define USE_TM64
+
+#ifdef USE_TM64
+#define TM TM64
+#else
+#define TM tm
+#endif
/* 64 bit types. Set as appropriate for your system. */
typedef Quad_t Time64_T;
typedef Quad_t Int64;
typedef Int64 Year;
-struct tm *gmtime64_r (const Time64_T *, struct tm *);
-struct tm *localtime64_r (const Time64_T *, struct tm *);
-Time64_T timegm64 (struct tm *);
+struct TM *gmtime64_r (const Time64_T *, struct TM *);
+struct TM *localtime64_r (const Time64_T *, struct TM *);
+Time64_T timegm64 (struct TM *);
+
+/* 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 HAS_TM_TM_GMTOFF
+ long tm_gmtoff;
+#endif
+
+#ifdef HAS_TM_TM_ZONE
+ char *tm_zone;
+#endif
+};
/* Not everyone has gm/localtime_r() */