// Date.cpp: Rcpp R/C++ interface class library -- Date type // // Copyright (C) 2010 - 2023 Dirk Eddelbuettel and Romain Francois // // The mktime00() as well as the gmtime_() replacement function are // Copyright (C) 2000 - 2010 The R Development Core Team. // // gmtime_() etc are from the public domain timezone code dated // 1996-06-05 by Arthur David Olson. // // This file is part of Rcpp. // // Rcpp is free software: you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 2 of the License, or // (at your option) any later version. // // Rcpp is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Rcpp. If not, see . #define COMPILING_RCPP #include // for TRUE,FALSE #include #include // for gmtime #include namespace Rcpp { // Taken (in 2010) from R's src/main/datetime.c and made a member function called with C++ reference // Later, R added the following comment we now (in 2016) add /* There are two implementation paths here. 1) Use the system functions for mktime, gmtime[_r], localtime[_r], strftime. Use the system time_t, struct tm and time-zone tables. 2) Use substitutes from src/extra/tzone for mktime, gmtime, localtime, strftime with a R_ prefix. The system strftime is used for locale-dependent names in R_strptime and R_strftime. This uses the time-zone tables shipped with R and installed into R_HOME/share/zoneinfo . Our own versions of time_t (64-bit) and struct tm (including the BSD-style fields tm_zone and tm_gmtoff) are used. For path 1), the system facilities are used for 1902-2037 and outside those limits where there is a 64-bit time_t and the conversions work (most OSes currently have only 32-bit time-zone tables). Otherwise there is code below to extrapolate from 1902-2037. Path 2) was added for R 3.1.0 and is the only one supported on Windows: it is the default on macOS. The only currently (Jan 2014) known OS with 64-bit time_t and complete tables is Linux. */ // Now, R only ships share/zoneinfo on Windows AFAIK /* Substitute for mktime -- no checking, always in GMT */ // [[Rcpp::register]] double mktime00(struct tm &tm) { static const int days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; static const int year_base = 1900; #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) #define days_in_year(year) (isleap(year) ? 366 : 365) int day = 0; int i, year, year0; double excess = 0.0; day = tm.tm_mday - 1; year0 = year_base + tm.tm_year; /* safety check for unbounded loops */ if (year0 > 3000) { excess = (int)(year0/2000) - 1; // #nocov start year0 -= (int)(excess * 2000); } else if (year0 < 0) { excess = -1 - (int)(-year0/2000); year0 -= (int)(excess * 2000); // #nocov end } for(i = 0; i < tm.tm_mon; i++) day += days_in_month[i]; if (tm.tm_mon > 1 && isleap(year0)) day++; tm.tm_yday = day; if (year0 > 1970) { for (year = 1970; year < year0; year++) day += days_in_year(year); } else if (year0 < 1970) { for (year = 1969; year >= year0; year--) day -= days_in_year(year); } /* weekday: Epoch day was a Thursday */ if ((tm.tm_wday = (day + 4) % 7) < 0) tm.tm_wday += 7; return tm.tm_sec + (tm.tm_min * 60) + (tm.tm_hour * 3600) + (day + excess * 730485) * 86400.0; } #undef isleap #undef days_in_year #include "sys/types.h" /* for time_t */ #include "string.h" #include "limits.h" /* for CHAR_BIT et al. */ #define _NO_OLDNAMES /* avoid tznames */ #include "time.h" #undef _NO_OLDNAMES #include #ifndef EOVERFLOW # define EOVERFLOW 79 #endif #include "stdlib.h" #include "stdint.h" #include "stdio.h" #include "fcntl.h" #include "float.h" /* for FLT_MAX and DBL_MAX */ #include // solaris needs this for read() and close() /* merged from private.h */ #define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) #define TYPE_SIGNED(type) (((type) -1) < 0) #define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5) #define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0) #define GRANDPARENTED "Local time zone must be set--see zic manual page" #define YEARSPERREPEAT 400 /* years before a Gregorian repeat */ #define AVGSECSPERYEAR 31556952L #define SECSPERREPEAT ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR) #define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */ #define is_digit(c) ((unsigned)(c) - '0' <= 9) #define INITIALIZE(x) (x = 0) /* Max and min values of the integer type T, of which only the bottom B bits are used, and where the highest-order used bit is considered to be a sign bit if T is signed. */ #define MAXVAL(t, b) \ ((t) (((t) 1 << ((b) - 1 - TYPE_SIGNED(t))) \ - 1 + ((t) 1 << ((b) - 1 - TYPE_SIGNED(t))))) #define MINVAL(t, b) \ ((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0)) /* The minimum and maximum finite time values. This assumes no padding. */ static time_t const time_t_min = MINVAL(time_t, TYPE_BIT(time_t)); static time_t const time_t_max = MAXVAL(time_t, TYPE_BIT(time_t)); //#include "tzfile.h" // from src/extra/tzone/tzfile.h // BEGIN ------------------------------------------------------------------------------------------ tzfile.h #ifndef TZFILE_H #define TZFILE_H /* ** This file is in the public domain, so clarified as of ** 1996-06-05 by Arthur David Olson. */ /* ** This header is for use ONLY with the time conversion code. ** There is no guarantee that it will remain unchanged, ** or that it will remain at all. ** Do NOT copy it to any system include directory. ** Thank you! */ /* ** Information about time zone files. */ #ifndef TZDIR #define TZDIR "/usr/local/etc/zoneinfo" /* Time zone object file directory */ #endif /* !defined TZDIR */ #ifndef TZDEFAULT #define TZDEFAULT "localtime" // NB this is "UTC" in R, but R also loads tz data #endif /* !defined TZDEFAULT */ #ifndef TZDEFRULES #define TZDEFRULES "America/New_York" #endif /* !defined TZDEFRULES */ /* ** Each file begins with. . . */ #define TZ_MAGIC "TZif" struct tzhead { char tzh_magic[4]; /* TZ_MAGIC */ char tzh_version[1]; /* '\0' or '2' as of 2005 */ char tzh_reserved[15]; /* reserved--must be zero */ char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ char tzh_leapcnt[4]; /* coded number of leap seconds */ char tzh_timecnt[4]; /* coded number of transition times */ char tzh_typecnt[4]; /* coded number of local time types */ char tzh_charcnt[4]; /* coded number of abbr. chars */ }; /* ** . . .followed by. . . ** ** tzh_timecnt (char [4])s coded transition times a la time(2) ** tzh_timecnt (unsigned char)s types of local time starting at above ** tzh_typecnt repetitions of ** one (char [4]) coded UTC offset in seconds ** one (unsigned char) used to set tm_isdst ** one (unsigned char) that's an abbreviation list index ** tzh_charcnt (char)s '\0'-terminated zone abbreviations ** tzh_leapcnt repetitions of ** one (char [4]) coded leap second transition times ** one (char [4]) total correction after above ** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition ** time is standard time, if FALSE, ** transition time is wall clock time ** if absent, transition times are ** assumed to be wall clock time ** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition ** time is UTC, if FALSE, ** transition time is local time ** if absent, transition times are ** assumed to be local time */ /* ** If tzh_version is '2' or greater, the above is followed by a second instance ** of tzhead and a second instance of the data in which each coded transition ** time uses 8 rather than 4 chars, ** then a POSIX-TZ-environment-variable-style string for use in handling ** instants after the last transition time stored in the file ** (with nothing between the newlines if there is no POSIX representation for ** such instants). ** ** If tz_version is '3' or greater, the above is extended as follows. ** First, the POSIX TZ string's hour offset may range from -167 ** through 167 as compared to the POSIX-required 0 through 24. ** Second, its DST start time may be January 1 at 00:00 and its stop ** time December 31 at 24:00 plus the difference between DST and ** standard time, indicating DST all year. */ /* ** In the current implementation, "tzset()" refuses to deal with files that ** exceed any of the limits below. */ #ifndef TZ_MAX_TIMES #define TZ_MAX_TIMES 1200 #endif /* !defined TZ_MAX_TIMES */ #ifndef TZ_MAX_TYPES #ifndef NOSOLAR #define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ #endif /* !defined NOSOLAR */ #ifdef NOSOLAR /* ** Must be at least 14 for Europe/Riga as of Jan 12 1995, ** as noted by Earl Chew. */ #define TZ_MAX_TYPES 20 /* Maximum number of local time types */ #endif /* !defined NOSOLAR */ #endif /* !defined TZ_MAX_TYPES */ // increased from 50, http://mm.icann.org/pipermail/tz/2015-August/022623.html #ifndef TZ_MAX_CHARS #define TZ_MAX_CHARS 100 /* Maximum number of abbreviation characters */ /* (limited by what unsigned chars can hold) */ #endif /* !defined TZ_MAX_CHARS */ #ifndef TZ_MAX_LEAPS #define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ #endif /* !defined TZ_MAX_LEAPS */ #define SECSPERMIN 60 #define MINSPERHOUR 60 #define HOURSPERDAY 24 #define DAYSPERWEEK 7 #define DAYSPERNYEAR 365 #define DAYSPERLYEAR 366 #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) #define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY) #define MONSPERYEAR 12 #define TM_SUNDAY 0 #define TM_MONDAY 1 #define TM_TUESDAY 2 #define TM_WEDNESDAY 3 #define TM_THURSDAY 4 #define TM_FRIDAY 5 #define TM_SATURDAY 6 #define TM_JANUARY 0 #define TM_FEBRUARY 1 #define TM_MARCH 2 #define TM_APRIL 3 #define TM_MAY 4 #define TM_JUNE 5 #define TM_JULY 6 #define TM_AUGUST 7 #define TM_SEPTEMBER 8 #define TM_OCTOBER 9 #define TM_NOVEMBER 10 #define TM_DECEMBER 11 #define TM_YEAR_BASE 1900 #define EPOCH_YEAR 1970 #define EPOCH_WDAY TM_THURSDAY #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) /* ** Since everything in isleap is modulo 400 (or a factor of 400), we know that ** isleap(y) == isleap(y % 400) ** and so ** isleap(a + b) == isleap((a + b) % 400) ** or ** isleap(a + b) == isleap(a % 400 + b % 400) ** This is true even if % means modulo rather than Fortran remainder ** (which is allowed by C89 but not C99). ** We use this to avoid addition overflow problems. */ #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) #endif /* !defined TZFILE_H */ // -------------------------------------------------------------------------------------- END tzfile.h //#include "localtime.c" // from src/extra/tzone/localtime.c // note though that was included is partial as we support only gmtime_() // BEGIN --------------------------------------------------------------------------------- localtime.c #ifdef O_BINARY #define OPEN_MODE (O_RDONLY | O_BINARY) #endif /* defined O_BINARY */ #ifndef O_BINARY #define OPEN_MODE O_RDONLY #endif /* !defined O_BINARY */ static const char gmt[] = "GMT"; /* ** The DST rules to use if TZ has no rules and we can't load TZDEFRULES. ** We default to US rules as of 1999-08-17. ** POSIX 1003.1 section 8.1.1 says that the default DST rules are ** implementation dependent; for historical reasons, US rules are a ** common default. */ #ifndef TZDEFRULESTRING #define TZDEFRULESTRING ",M4.1.0,M10.5.0" #endif /* !defined TZDEFDST */ #define BIGGEST(a, b) (((a) > (b)) ? (a) : (b)) #ifdef TZNAME_MAX #define MY_TZNAME_MAX TZNAME_MAX #endif /* defined TZNAME_MAX */ #ifndef TZNAME_MAX #define MY_TZNAME_MAX 255 #endif /* !defined TZNAME_MAX */ struct ttinfo { /* time type information */ int_fast32_t tt_gmtoff; /* UTC offset in seconds */ int tt_isdst; /* used to set tm_isdst */ int tt_abbrind; /* abbreviation list index */ int tt_ttisstd; /* TRUE if transition is std time */ int tt_ttisgmt; /* TRUE if transition is UTC */ }; struct lsinfo { /* leap second information */ time_t ls_trans; /* transition time */ int_fast64_t ls_corr; /* correction to apply */ }; struct state { int leapcnt; int timecnt; int typecnt; int charcnt; int goback; int goahead; time_t ats[TZ_MAX_TIMES]; unsigned char types[TZ_MAX_TIMES]; struct ttinfo ttis[TZ_MAX_TYPES]; char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt), (2 * (MY_TZNAME_MAX + 1)))]; struct lsinfo lsis[TZ_MAX_LEAPS]; }; struct rule { int r_type; /* type of rule--see below */ int r_day; /* day number of rule */ int r_week; /* week number of rule */ int r_mon; /* month number of rule */ int_fast32_t r_time; /* transition time of rule */ }; #define JULIAN_DAY 0 /* Jn - Julian day */ #define DAY_OF_YEAR 1 /* n - day of year */ #define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */ static const int mon_lengths[2][MONSPERYEAR] = { { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; static const int year_lengths[2] = { DAYSPERNYEAR, DAYSPERLYEAR }; static int gmt_is_set; //static struct state lclmem; static struct state gmtmem; //#define lclptr (&lclmem) #define gmtptr (&gmtmem) static struct tm tm; //extern const char *getTZinfo(void); static int tzparse(const char * name, struct state * sp, int lastditch); static int typesequiv(const struct state * sp, int a, int b); static const char * getsecs(const char * strp, int_fast32_t * secsp); static const char * getnum(const char * strp, int * const nump, const int min, const int max); static const char * getrule(const char * strp, struct rule * const rulep); static int_fast32_t transtime(int year, const struct rule * rulep, int_fast32_t offset); static struct tm * timesub(const time_t *timep, int_fast32_t offset, const struct state *sp, struct tm *tmp); static int leaps_thru_end_of(const int y); /* ** Normalize logic courtesy Paul Eggert. */ static int increment_overflow(int *const ip, int j) { int const i = *ip; /* ** If i >= 0 there can only be overflow if i + j > INT_MAX ** or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow. ** If i < 0 there can only be overflow if i + j < INT_MIN ** or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow. */ if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i)) return TRUE; // #nocov *ip += j; return FALSE; } static int increment_overflow_time(time_t *tp, int_fast32_t j) { // #nocov start /* ** This is like ** 'if (! (time_t_min <= *tp + j && *tp + j <= time_t_max)) ...', ** except that it does the right thing even if *tp + j would overflow. */ if (! (j < 0 ? (TYPE_SIGNED(time_t) ? time_t_min - j <= *tp : -1 - j < *tp) : *tp <= time_t_max - j)) return TRUE; *tp += j; return FALSE; } static int_fast32_t detzcode(const char *const codep) { int_fast32_t result = (codep[0] & 0x80) ? -1 : 0; for (int i = 0; i < 4; ++i) result = (result << 8) | (codep[i] & 0xff); return result; } static int_fast64_t detzcode64(const char *const codep) { int_fast64_t result = (codep[0] & 0x80) ? -1 : 0; for (int i = 0; i < 8; ++i) result = (result << 8) | (codep[i] & 0xff); return result; } static int differ_by_repeat(const time_t t1, const time_t t0) { if (TYPE_INTEGRAL(time_t) && TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS) return 0; /* R change */ return (int_fast64_t)t1 - (int_fast64_t)t0 == SECSPERREPEAT; } static const char * getzname(const char * strp) { char c; while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && c != '+') ++strp; return strp; } static const char * getqzname(const char *strp, const int delim) { int c; while ((c = *strp) != '\0' && c != delim) ++strp; return strp; } static const char * getoffset(const char *strp, int_fast32_t *const offsetp) { int neg = 0; if (*strp == '-') { neg = 1; ++strp; } else if (*strp == '+') ++strp; strp = getsecs(strp, offsetp); if (strp == NULL) return NULL; /* illegal time */ if (neg) *offsetp = -*offsetp; return strp; } static const char * getsecs(const char *strp, int_fast32_t *const secsp) { int num; /* ** 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like ** "M10.4.6/26", which does not conform to Posix, ** but which specifies the equivalent of ** "02:00 on the first Sunday on or after 23 Oct". */ strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1); if (strp == NULL) return NULL; *secsp = num * (int_fast32_t) SECSPERHOUR; if (*strp == ':') { ++strp; strp = getnum(strp, &num, 0, MINSPERHOUR - 1); if (strp == NULL) return NULL; *secsp += num * SECSPERMIN; if (*strp == ':') { ++strp; /* 'SECSPERMIN' allows for leap seconds. */ strp = getnum(strp, &num, 0, SECSPERMIN); if (strp == NULL) return NULL; *secsp += num; } } return strp; } static const char * getnum(const char * strp, int * const nump, const int min, const int max) { char c; int num; if (strp == NULL || !is_digit(c = *strp)) return NULL; num = 0; do { num = num * 10 + (c - '0'); if (num > max) return NULL; /* illegal value */ c = *++strp; } while (is_digit(c)); if (num < min) return NULL; /* illegal value */ *nump = num; return strp; } static const char * getrule(const char * strp, struct rule * const rulep) { if (*strp == 'J') { /* ** Julian day. */ rulep->r_type = JULIAN_DAY; ++strp; strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR); } else if (*strp == 'M') { /* ** Month, week, day. */ rulep->r_type = MONTH_NTH_DAY_OF_WEEK; ++strp; strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR); if (strp == NULL) return NULL; if (*strp++ != '.') return NULL; strp = getnum(strp, &rulep->r_week, 1, 5); if (strp == NULL) return NULL; if (*strp++ != '.') return NULL; strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1); } else if (is_digit(*strp)) { /* ** Day of year. */ rulep->r_type = DAY_OF_YEAR; strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1); } else return NULL; /* invalid format */ if (strp == NULL) return NULL; if (*strp == '/') { /* ** Time specified. */ ++strp; strp = getsecs(strp, &rulep->r_time); } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ return strp; } // this routine modified / simplified / reduced in 2010 static int tzload(const char * name, struct state * const sp, const int doextend) { const char * p; int i; int fid; int stored; int nread; union { struct tzhead tzhead; char buf[2 * sizeof(struct tzhead) + 2 * sizeof *sp + 4 * TZ_MAX_TIMES]; } u; sp->goback = sp->goahead = FALSE; /* if (name == NULL && (name = TZDEFAULT) == NULL) return -1; */ if (name == NULL) { // edd 06 Jul 2010 let's do without getTZinfo() //name = getTZinfo(); //if( strcmp(name, "unknown") == 0 ) name = TZDEFAULT; name = TZDEFAULT; } { int doaccess; /* ** Section 4.9.1 of the C standard says that ** "FILENAME_MAX expands to an integral constant expression ** that is the size needed for an array of char large enough ** to hold the longest file name string that the implementation ** guarantees can be opened." */ char fullname[FILENAME_MAX + 1]; // edd 08 Jul 2010 not currently needed const char *sname = name; if (name[0] == ':') ++name; doaccess = name[0] == '/'; if (!doaccess) { char buf[1000]; p = getenv("TZDIR"); if (p == NULL) { snprintf(buf, 1000, "%s/share/zoneinfo", getenv("R_HOME")); buf[999] = '\0'; p = buf; } /* if ((p = TZDIR) == NULL) return -1; */ if ((strlen(p) + strlen(name) + 1) >= sizeof fullname) return -1; (void) strcpy(fullname, p); (void) strcat(fullname, "/"); (void) strcat(fullname, name); /* ** Set doaccess if '.' (as in "../") shows up in name. */ if (strchr(name, '.') != NULL) doaccess = TRUE; name = fullname; } // edd 16 Jul 2010 comment out whole block //if (doaccess && access(name, R_OK) != 0) { // edd 08 Jul 2010 we use this without TZ for dates only // so no need to warn //Rf_warning("unknown timezone '%s'", sname); //return -1; //} if ((fid = open(name, OPEN_MODE)) == -1) { // edd 08 Jul 2010 we use this without TZ for dates only // so no need to warn //Rf_warning("unknown timezone '%s'", sname); return -1; } } nread = (int)read(fid, u.buf, sizeof u.buf); if (close(fid) < 0 || nread <= 0) return -1; for (stored = 4; stored <= 8; stored *= 2) { int ttisstdcnt; int ttisgmtcnt; ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt); ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt); sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt); sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt); sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt); sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt); p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt; if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS || sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES || sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES || sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS || (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) return -1; if (nread - (p - u.buf) < sp->timecnt * stored + /* ats */ sp->timecnt + /* types */ sp->typecnt * 6 + /* ttinfos */ sp->charcnt + /* chars */ sp->leapcnt * (stored + 4) + /* lsinfos */ ttisstdcnt + /* ttisstds */ ttisgmtcnt) /* ttisgmts */ return -1; for (i = 0; i < sp->timecnt; ++i) { sp->ats[i] = (stored == 4) ? detzcode(p) : detzcode64(p); p += stored; } for (i = 0; i < sp->timecnt; ++i) { sp->types[i] = (unsigned char) *p++; if (sp->types[i] >= sp->typecnt) return -1; } for (i = 0; i < sp->typecnt; ++i) { struct ttinfo * ttisp; ttisp = &sp->ttis[i]; ttisp->tt_gmtoff = detzcode(p); p += 4; ttisp->tt_isdst = (unsigned char) *p++; if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) return -1; ttisp->tt_abbrind = (unsigned char) *p++; if (ttisp->tt_abbrind < 0 || ttisp->tt_abbrind > sp->charcnt) return -1; } for (i = 0; i < sp->charcnt; ++i) sp->chars[i] = *p++; sp->chars[i] = '\0'; /* ensure '\0' at end */ for (i = 0; i < sp->leapcnt; ++i) { struct lsinfo * lsisp; lsisp = &sp->lsis[i]; lsisp->ls_trans = (stored == 4) ? detzcode(p) : detzcode64(p); p += stored; lsisp->ls_corr = detzcode(p); p += 4; } for (i = 0; i < sp->typecnt; ++i) { struct ttinfo * ttisp; ttisp = &sp->ttis[i]; if (ttisstdcnt == 0) ttisp->tt_ttisstd = FALSE; else { ttisp->tt_ttisstd = *p++; if (ttisp->tt_ttisstd != TRUE && ttisp->tt_ttisstd != FALSE) return -1; } } for (i = 0; i < sp->typecnt; ++i) { struct ttinfo * ttisp; ttisp = &sp->ttis[i]; if (ttisgmtcnt == 0) ttisp->tt_ttisgmt = FALSE; else { ttisp->tt_ttisgmt = *p++; if (ttisp->tt_ttisgmt != TRUE && ttisp->tt_ttisgmt != FALSE) return -1; } } /* ** Out-of-sort ats should mean we're running on a ** signed time_t system but using a data file with ** unsigned values (or vice versa). */ for (i = 0; i < sp->timecnt - 2; ++i) if (sp->ats[i] > sp->ats[i + 1]) { ++i; if (TYPE_SIGNED(time_t)) { /* ** Ignore the end (easy). */ sp->timecnt = i; } else { /* ** Ignore the beginning (harder). */ int j; for (j = 0; j + i < sp->timecnt; ++j) { sp->ats[j] = sp->ats[j + i]; sp->types[j] = sp->types[j + i]; } sp->timecnt = j; } break; } /* ** If this is an old file, we're done. */ if (u.tzhead.tzh_version[0] == '\0') break; nread -= p - u.buf; for (i = 0; i < nread; ++i) u.buf[i] = p[i]; /* ** If this is a narrow integer time_t system, we're done. */ if (stored >= (int) sizeof(time_t) && TYPE_INTEGRAL(time_t)) break; } if (doextend && nread > 2 && u.buf[0] == '\n' && u.buf[nread - 1] == '\n' && sp->typecnt + 2 <= TZ_MAX_TYPES) { struct state ts; int result; u.buf[nread - 1] = '\0'; result = tzparse(&u.buf[1], &ts, FALSE); if (result == 0 && ts.typecnt == 2 && sp->charcnt + ts.charcnt <= TZ_MAX_CHARS) { for (i = 0; i < 2; ++i) ts.ttis[i].tt_abbrind += sp->charcnt; for (i = 0; i < ts.charcnt; ++i) sp->chars[sp->charcnt++] = ts.chars[i]; i = 0; while (i < ts.timecnt && ts.ats[i] <= sp->ats[sp->timecnt - 1]) ++i; while (i < ts.timecnt && sp->timecnt < TZ_MAX_TIMES) { sp->ats[sp->timecnt] = ts.ats[i]; sp->types[sp->timecnt] = (unsigned char)sp->typecnt + ts.types[i]; ++sp->timecnt; ++i; } sp->ttis[sp->typecnt++] = ts.ttis[0]; sp->ttis[sp->typecnt++] = ts.ttis[1]; } } i = 2 * YEARSPERREPEAT; sp->goback = sp->goahead = sp->timecnt > i; sp->goback = sp->goback && typesequiv(sp, sp->types[i], sp->types[0]) && differ_by_repeat(sp->ats[i], sp->ats[0]); sp->goahead = sp->goahead && typesequiv(sp, sp->types[sp->timecnt - 1], sp->types[sp->timecnt - 1 - i]) && differ_by_repeat(sp->ats[sp->timecnt - 1], sp->ats[sp->timecnt - 1 - i]); return 0; } /* ** Given a year, a rule, and the offset from UT at the time that rule takes ** effect, calculate the year-relative time that rule takes effect. */ static int_fast32_t transtime(const int year, const struct rule *const rulep, const int_fast32_t offset) { int leapyear; int_fast32_t value; int d, m1, yy0, yy1, yy2, dow; INITIALIZE(value); leapyear = isleap(year); switch (rulep->r_type) { case JULIAN_DAY: /* ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap ** years. ** In non-leap years, or if the day number is 59 or less, just ** add SECSPERDAY times the day number-1 to the time of ** January 1, midnight, to get the day. */ value = (rulep->r_day - 1) * SECSPERDAY; if (leapyear && rulep->r_day >= 60) value += SECSPERDAY; break; case DAY_OF_YEAR: /* ** n - day of year. ** Just add SECSPERDAY times the day number to the time of ** January 1, midnight, to get the day. */ value = rulep->r_day * SECSPERDAY; break; case MONTH_NTH_DAY_OF_WEEK: /* ** Mm.n.d - nth "dth day" of month m. */ /* ** Use Zeller's Congruence to get day-of-week of first day of ** month. */ m1 = (rulep->r_mon + 9) % 12 + 1; yy0 = (rulep->r_mon <= 2) ? (year - 1) : year; yy1 = yy0 / 100; yy2 = yy0 % 100; dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; if (dow < 0) dow += DAYSPERWEEK; /* ** "dow" is the day-of-week of the first day of the month. Get ** the day-of-month (zero-origin) of the first "dow" day of the ** month. */ d = rulep->r_day - dow; if (d < 0) d += DAYSPERWEEK; for (int i = 1; i < rulep->r_week; ++i) { if (d + DAYSPERWEEK >= mon_lengths[leapyear][rulep->r_mon - 1]) break; d += DAYSPERWEEK; } /* ** "d" is the day-of-month (zero-origin) of the day we want. */ value = d * SECSPERDAY; for (int i = 0; i < rulep->r_mon - 1; ++i) value += mon_lengths[leapyear][i] * SECSPERDAY; break; } /* ** "value" is the year-relative time of 00:00:00 UT on the day in ** question. To get the year-relative time of the specified local ** time on that day, add the transition time and the current offset ** from UT. */ return value + rulep->r_time + offset; } /* ** Given a POSIX section 8-style TZ string, fill in the rule tables as ** appropriate. */ static int tzparse(const char * name, struct state * const sp, const int lastditch) { const char * stdname; const char * dstname; size_t stdlen; size_t dstlen; int_fast32_t stdoffset; int_fast32_t dstoffset; char * cp; int load_result; static struct ttinfo zttinfo; INITIALIZE(dstname); stdname = name; if (lastditch) { stdlen = strlen(name); /* length of standard zone name */ name += stdlen; if (stdlen >= sizeof sp->chars) stdlen = (sizeof sp->chars) - 1; stdoffset = 0; } else { if (*name == '<') { name++; stdname = name; name = getqzname(name, '>'); if (*name != '>') return (-1); stdlen = name - stdname; name++; } else { name = getzname(name); stdlen = name - stdname; } if (*name == '\0') return -1; name = getoffset(name, &stdoffset); if (name == NULL) return -1; } load_result = tzload(TZDEFRULES, sp, FALSE); if (load_result != 0) sp->leapcnt = 0; /* so, we're off a little */ if (*name != '\0') { if (*name == '<') { dstname = ++name; name = getqzname(name, '>'); if (*name != '>') return -1; dstlen = name - dstname; name++; } else { dstname = name; name = getzname(name); dstlen = name - dstname; /* length of DST zone name */ } if (*name != '\0' && *name != ',' && *name != ';') { name = getoffset(name, &dstoffset); if (name == NULL) return -1; } else dstoffset = stdoffset - SECSPERHOUR; if (*name == '\0' && load_result != 0) name = TZDEFRULESTRING; if (*name == ',' || *name == ';') { struct rule start; struct rule end; int year; int yearlim; int timecnt; time_t janfirst; ++name; if ((name = getrule(name, &start)) == NULL) return -1; if (*name++ != ',') return -1; if ((name = getrule(name, &end)) == NULL) return -1; if (*name != '\0') return -1; sp->typecnt = 2; /* standard time and DST */ /* ** Two transitions per year, from EPOCH_YEAR forward. */ sp->ttis[0] = sp->ttis[1] = zttinfo; sp->ttis[0].tt_gmtoff = -dstoffset; sp->ttis[0].tt_isdst = 1; sp->ttis[0].tt_abbrind = (int)(stdlen + 1); sp->ttis[1].tt_gmtoff = -stdoffset; sp->ttis[1].tt_isdst = 0; sp->ttis[1].tt_abbrind = 0; timecnt = 0; janfirst = 0; yearlim = EPOCH_YEAR + YEARSPERREPEAT; for (year = EPOCH_YEAR; year < yearlim; year++) { int_fast32_t starttime = transtime(year, &start, stdoffset), endtime = transtime(year, &end, dstoffset); int_fast32_t yearsecs = (year_lengths[isleap(year)] * SECSPERDAY); int reversed = endtime < starttime; if (reversed) { int_fast32_t swap = starttime; starttime = endtime; endtime = swap; } if (reversed || (starttime < endtime && (endtime - starttime < (yearsecs + (stdoffset - dstoffset))))) { if (TZ_MAX_TIMES - 2 < timecnt) break; yearlim = year + YEARSPERREPEAT + 1; sp->ats[timecnt] = janfirst; if (increment_overflow_time (&sp->ats[timecnt], starttime)) break; sp->types[timecnt++] = (unsigned char) reversed; sp->ats[timecnt] = janfirst; if (increment_overflow_time (&sp->ats[timecnt], endtime)) break; sp->types[timecnt++] = !reversed; } if (increment_overflow_time(&janfirst, yearsecs)) break; } sp->timecnt = timecnt; if (!timecnt) sp->typecnt = 1; /* Perpetual DST. */ } else { int_fast32_t theirstdoffset, theirdstoffset, theiroffset; int isdst; if (*name != '\0') return -1; /* ** Initial values of theirstdoffset and theirdstoffset. */ theirstdoffset = 0; for (int i = 0; i < sp->timecnt; ++i) { int j = sp->types[i]; if (!sp->ttis[j].tt_isdst) { theirstdoffset = -sp->ttis[j].tt_gmtoff; break; } } theirdstoffset = 0; for (int i = 0; i < sp->timecnt; ++i) { int j = sp->types[i]; if (sp->ttis[j].tt_isdst) { theirdstoffset = -sp->ttis[j].tt_gmtoff; break; } } /* ** Initially we're assumed to be in standard time. */ isdst = FALSE; theiroffset = theirstdoffset; /* ** Now juggle transition times and types ** tracking offsets as you do. */ for (int i = 0; i < sp->timecnt; ++i) { int j = sp->types[i]; sp->types[i] = (unsigned char)sp->ttis[j].tt_isdst; if (sp->ttis[j].tt_ttisgmt) { /* No adjustment to transition time */ } else { /* ** If summer time is in effect, and the ** transition time was not specified as ** standard time, add the summer time ** offset to the transition time; ** otherwise, add the standard time ** offset to the transition time. */ /* ** Transitions from DST to DDST ** will effectively disappear since ** POSIX provides for only one DST ** offset. */ if (isdst && !sp->ttis[j].tt_ttisstd) { sp->ats[i] += dstoffset - theirdstoffset; } else { sp->ats[i] += stdoffset - theirstdoffset; } } theiroffset = -sp->ttis[j].tt_gmtoff; if (sp->ttis[j].tt_isdst) theirdstoffset = theiroffset; else theirstdoffset = theiroffset; } /* ** Finally, fill in ttis. */ sp->ttis[0] = sp->ttis[1] = zttinfo; sp->ttis[0].tt_gmtoff = -stdoffset; sp->ttis[0].tt_isdst = FALSE; sp->ttis[0].tt_abbrind = 0; sp->ttis[1].tt_gmtoff = -dstoffset; sp->ttis[1].tt_isdst = TRUE; sp->ttis[1].tt_abbrind = (int)(stdlen + 1); sp->typecnt = 2; } } else { dstlen = 0; sp->typecnt = 1; /* only standard time */ sp->timecnt = 0; sp->ttis[0] = zttinfo; sp->ttis[0].tt_gmtoff = -stdoffset; sp->ttis[0].tt_isdst = 0; sp->ttis[0].tt_abbrind = 0; } sp->charcnt = (int)(stdlen + 1); if (dstlen != 0) sp->charcnt += dstlen + 1; if ((size_t) sp->charcnt > sizeof sp->chars) return -1; cp = sp->chars; (void) strncpy(cp, stdname, stdlen); cp += stdlen; *cp++ = '\0'; if (dstlen != 0) { (void) strncpy(cp, dstname, dstlen); *(cp + dstlen) = '\0'; } return 0; } static int typesequiv(const struct state * const sp, const int a, const int b) { int result; if (sp == NULL || a < 0 || a >= sp->typecnt || b < 0 || b >= sp->typecnt) result = FALSE; else { const struct ttinfo * ap = &sp->ttis[a]; const struct ttinfo * bp = &sp->ttis[b]; result = ap->tt_gmtoff == bp->tt_gmtoff && ap->tt_isdst == bp->tt_isdst && ap->tt_ttisstd == bp->tt_ttisstd && ap->tt_ttisgmt == bp->tt_ttisgmt && strcmp(&sp->chars[ap->tt_abbrind], &sp->chars[bp->tt_abbrind]) == 0; } return result; } // #nocov end static int leaps_thru_end_of(const int y) { return (y >= 0) ? (y / 4 - y / 100 + y / 400) : -(leaps_thru_end_of(-(y + 1)) + 1); } static struct tm * timesub(const time_t *const timep, const int_fast32_t offset, const struct state *const sp, struct tm *const tmp) { const struct lsinfo * lp; time_t tdays; int idays; /* unsigned would be so 2003 */ int_fast64_t rem; int y; const int * ip; int_fast64_t corr; int hit; int i; corr = 0; hit = 0; i = sp->leapcnt; while (--i >= 0) { lp = &sp->lsis[i]; // #nocov start if (*timep >= lp->ls_trans) { if (*timep == lp->ls_trans) { hit = ((i == 0 && lp->ls_corr > 0) || lp->ls_corr > sp->lsis[i - 1].ls_corr); if (hit) while (i > 0 && sp->lsis[i].ls_trans == sp->lsis[i - 1].ls_trans + 1 && sp->lsis[i].ls_corr == sp->lsis[i - 1].ls_corr + 1) { ++hit; --i; } } corr = lp->ls_corr; break; // #nocov end } } y = EPOCH_YEAR; tdays = *timep / SECSPERDAY; rem = *timep - tdays * SECSPERDAY; while (tdays < 0 || tdays >= year_lengths[isleap(y)]) { int newy; time_t tdelta; int idelta; int leapdays; tdelta = tdays / DAYSPERLYEAR; if (! ((! TYPE_SIGNED(time_t) || INT_MIN <= tdelta) && tdelta <= INT_MAX)) return NULL; // #nocov idelta = (int)tdelta; if (idelta == 0) idelta = (tdays < 0) ? -1 : 1; newy = y; if (increment_overflow(&newy, idelta)) return NULL; // #nocov leapdays = leaps_thru_end_of(newy - 1) - leaps_thru_end_of(y - 1); tdays -= ((time_t) newy - y) * DAYSPERNYEAR; tdays -= leapdays; y = newy; } { int_fast32_t seconds; seconds = (int_fast32_t)(tdays * SECSPERDAY); tdays = seconds / SECSPERDAY; rem += seconds - tdays * SECSPERDAY; } /* ** Given the range, we can now fearlessly cast... */ idays = (int)tdays; rem += offset - corr; while (rem < 0) { // #nocov start rem += SECSPERDAY; --idays; } while (rem >= SECSPERDAY) { rem -= SECSPERDAY; ++idays; } while (idays < 0) { if (increment_overflow(&y, -1)) return NULL; idays += year_lengths[isleap(y)]; } while (idays >= year_lengths[isleap(y)]) { idays -= year_lengths[isleap(y)]; if (increment_overflow(&y, 1)) return NULL; // #nocov end } // Previously we returned 'year + base', so keep behaviour // It seems like R now returns just 'year - 1900' (as libc does) // But better for continuity to do as before tmp->tm_year = y + TM_YEAR_BASE; if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE)) return NULL; // #nocov tmp->tm_yday = idays; /* ** The "extra" mods below avoid overflow problems. */ tmp->tm_wday = EPOCH_WDAY + ((y - EPOCH_YEAR) % DAYSPERWEEK) * (DAYSPERNYEAR % DAYSPERWEEK) + leaps_thru_end_of(y - 1) - leaps_thru_end_of(EPOCH_YEAR - 1) + idays; tmp->tm_wday %= DAYSPERWEEK; if (tmp->tm_wday < 0) tmp->tm_wday += DAYSPERWEEK; // #nocov tmp->tm_hour = (int) (rem / SECSPERHOUR); rem %= SECSPERHOUR; tmp->tm_min = (int) (rem / SECSPERMIN); /* ** A positive leap second requires a special ** representation. This uses "... ??:59:60" et seq. */ tmp->tm_sec = (int) (rem % SECSPERMIN) + hit; ip = mon_lengths[isleap(y)]; for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon)) idays -= ip[tmp->tm_mon]; tmp->tm_mday = (int) (idays + 1); tmp->tm_isdst = 0; #if ! (defined(__MINGW32__) || defined(__MINGW64__) || defined(__sun) || defined(sun) || defined(_AIX)) //#ifdef HAVE_TM_GMTOFF tmp->tm_gmtoff = offset; #endif return tmp; } static void gmtload(struct state * const sp) { if (tzload(gmt, sp, TRUE) != 0) (void) tzparse(gmt, sp, TRUE); } /* ** gmtsub is to gmtime as localsub is to localtime. */ static struct tm * gmtsub(const time_t *const timep, const int_fast32_t offset, struct tm *const tmp) { struct tm * result; if (!gmt_is_set) { gmt_is_set = TRUE; gmtload(gmtptr); } result = timesub(timep, offset, gmtptr, tmp); return result; } // [[Rcpp::register]] struct tm * gmtime_(const time_t * const timep) { return gmtsub(timep, 0L, &tm); } }