// 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 <http://www.gnu.org/licenses/>.

#define COMPILING_RCPP

#include <R_ext/Boolean.h> // for TRUE,FALSE
#include <Rcpp.h>
#include <time.h>		// for gmtime
#include <Rcpp/exceptions.h>

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 <errno.h>
#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 <unistd.h>		// 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);
    }
}
