summaryrefslogtreecommitdiff
path: root/time64.c
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 /time64.c
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.
Diffstat (limited to 'time64.c')
-rw-r--r--time64.c143
1 files changed, 107 insertions, 36 deletions
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;
}