RcppCCTZ/0000755000176200001440000000000014726464450011656 5ustar liggesusersRcppCCTZ/tests/0000755000176200001440000000000013521272406013006 5ustar liggesusersRcppCCTZ/tests/tinytest.R0000644000176200001440000000212513521272406015014 0ustar liggesusers # RcppCCTZ: Rcpp-based R bindings for CCTZ # # Copyright (C) 2019 Dirk Eddelbuettel # # This file is part of RcppCCTZ. # # RcppCCTZ 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. # # RcppCCTZ 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 RcppCCTZ. If not, see . if (requireNamespace("tinytest", quietly=TRUE)) { ## Set a seed to make the test deterministic set.seed(42) ## there are several more granular ways to test files in a ## tinytest directory, see its package vignette; tests can also ## run once the package is installed using the same command tinytest::test_package("RcppCCTZ") } RcppCCTZ/MD50000644000176200001440000000515214726464450012171 0ustar liggesusers61d3850e035c04669a8205b8a7b7de15 *ChangeLog 7e2d76d95050343cb08c59ce62846aa2 *DESCRIPTION bdfe89628809034bb2aa6fafe2592d04 *NAMESPACE ec6e931dfb406121352ecc96621e7f99 *R/RcppExports.R d94d737785e7fdd2a6b0f60136b2d25d *R/init.R 53b99a6e02c193f8991c5acf43e0a555 *README.md 89936c36d9e3e87ade958c90bb42c0b7 *cleanup a57b7b5c2a241a9ee61fc2393db4c2dd *inst/NEWS.Rd 2a1204292bf9baf598eaaf57041cf0cd *inst/include/RcppCCTZ_API.h 2c98ed80d8423e7c4a780a516a726765 *inst/include/RcppCCTZ_types.h 70cef418e3061411c75ffc0e29b44862 *inst/include/cctz/civil_time.h 47439f0b52cafc3be42e3c0ced78b415 *inst/include/cctz/civil_time_detail.h 3dd0d7916d951a093f524dfdba965b14 *inst/include/cctz/time_zone.h 407daaafb5b7e177d79f4e5a876de84a *inst/include/cctz/zone_info_source.h 455a20d2c32291dc8bc47dd3e21fe8c4 *inst/tinytest/test_format.R 97109413c7a0454e4fb1d8bc8aee4558 *inst/tinytest/test_parse.R c550b4a54536330c6dc3a24d44aa58f7 *inst/tinytest/test_tz.R e03aeb8e69c258a12b1f95a2db50d675 *man/RcppCCTZ-package.Rd a848f8f29d54ef1531a1185c47d76838 *man/formatDatetime.Rd 98e74e616dd35f53631fea9ee74a0e39 *man/parseDatetime.Rd a9c53e2ba5af3d54ac5ab64354817ce7 *man/toTz.Rd 22bcff7a2c3dc34dc747b17d67dfae63 *man/tzDiff.Rd e4e12213cf62b2315fbaaed4c2c1c5cc *src/Makevars 13af8e362f1e392c08e22d0bd9f93b08 *src/Makevars.ucrt e4e12213cf62b2315fbaaed4c2c1c5cc *src/Makevars.win cb3a0102a75ec491e4e2e579d07a2590 *src/RcppExports.cpp d0bc8bdbc1efe3b52812f915854b0d97 *src/RcppExports_snippet.h 8e48937ed5ce673434f4679db99d99a6 *src/civil_time_detail.cc 6cb528d1aac2b05edfbecb0b824a43ec *src/examples.cpp 2e379d00fe16be1165130445d7d4cdc9 *src/get_time.h cdc9f7fcb5152c4b41906c969b28eae9 *src/time_tool.cc 19a5f0db02339633b061cd24b3cc2281 *src/time_zone_fixed.cc a71187d54c274cc9bfbb48d44cc89195 *src/time_zone_fixed.h fc6f636d19521e12fe56e8f97e5452a2 *src/time_zone_format.cc 1b9d6bc859ad8d8ccb9e258a6ba0b98f *src/time_zone_if.cc b4b0e820dd19fe36313705820b70c75a *src/time_zone_if.h 7c8889f3d1777a0ed4b2e427f690e0d6 *src/time_zone_impl.cc b27b5e15323d9e86fb1e78ccc6cb2a23 *src/time_zone_impl.h 9f910919ab3eef981a54e47efe9e08ac *src/time_zone_info.cc 7c8b8f35a25aab53fb76c356eb1fd17c *src/time_zone_info.h 8a1ef8211946acad2fe5eb69659a431c *src/time_zone_libc.cc 290ef8776251333c8516b88f213db785 *src/time_zone_libc.h 0784513ef3c20d3a9ffdd5fea4728c14 *src/time_zone_lookup.cc 46d9e073cfce772f8d0582134dfd4372 *src/time_zone_posix.cc 38903f4cf6c42636e9f42207b5e435d0 *src/time_zone_posix.h 0c64775d321d4487b269110006acf2a3 *src/tzfile.h 2cff1693aa06404093e1f16a5fd4dde1 *src/utilities.cpp b902b20040c55efdc292e307cf712491 *src/zone_info_source.cc cad3d97e1ff5fce87a342264b245bf1d *tests/tinytest.R RcppCCTZ/R/0000755000176200001440000000000013722071251012043 5ustar liggesusersRcppCCTZ/R/RcppExports.R0000644000176200001440000001467313722071251014472 0ustar liggesusers# Generated by using Rcpp::compileAttributes() -> do not edit by hand # Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 example0 <- function() { invisible(.Call(`_RcppCCTZ_example0`)) } helloMoon <- function(verbose = FALSE) { .Call(`_RcppCCTZ_helloMoon`, verbose) } example1 <- function() { invisible(.Call(`_RcppCCTZ_example1`)) } example2 <- function() { .Call(`_RcppCCTZ_example2`) } example3 <- function() { invisible(.Call(`_RcppCCTZ_example3`)) } example4 <- function() { invisible(.Call(`_RcppCCTZ_example4`)) } exampleFormat <- function() { invisible(.Call(`_RcppCCTZ_exampleFormat`)) } #' Difference between two given timezones at a specified date. #' #' Time zone offsets vary by date, and this helper function computes #' the difference (in hours) between two time zones for a given date time. #' #' @title Return difference between two time zones at a given date. #' @param tzfrom The first time zone as a character vector. #' @param tzto The second time zone as a character vector. #' @param dt A Datetime object specifying when the difference is to be computed. #' @param verbose A boolean toggle indicating whether more verbose operations #' are desired, default is \code{FALSE}. #' @return A numeric value with the difference (in hours) between the first and #' second time zone at the given date #' @author Dirk Eddelbuettel #' @examples #' \dontrun{ #' # simple call: difference now #' tzDiff("America/New_York", "Europe/London", Sys.time()) #' # tabulate difference for every week of the year #' table(sapply(0:52, function(d) tzDiff("America/New_York", "Europe/London", #' as.POSIXct(as.Date("2016-01-01") + d*7)))) #' } tzDiff <- function(tzfrom, tzto, dt, verbose = FALSE) { .Call(`_RcppCCTZ_tzDiff`, tzfrom, tzto, dt, verbose) } #' Change from one given timezone to another. #' #' Time zone offsets vary by date, and this helper function converts #' a Datetime object from one given timezone to another. #' #' @title Shift datetime object from one timezone to another #' @param dtv A DatetimeVector object specifying when the difference is to be computed. #' @param tzfrom The first time zone as a character vector. #' @param tzto The second time zone as a character vector. #' @param verbose A boolean toggle indicating whether more verbose operations #' are desired, default is \code{FALSE}. #' @return A DatetimeVector object with the given (civil time) determined by the #' incoming object (and its timezone) shifted to the target timezone. #' @author Dirk Eddelbuettel #' @examples #' \dontrun{ #' toTz(Sys.time(), "America/New_York", "Europe/London") #' # this redoes the 'Armstrong on the moon in NYC and Sydney' example #' toTz(ISOdatetime(1969,7,20,22,56,0,tz="UTC"), "America/New_York", "Australia/Sydney", verbose=TRUE) #' # we can also explicitly format for Sydney time #' format(toTz(ISOdatetime(1969,7,20,22,56,0,tz="UTC"), #' "America/New_York", "Australia/Sydney", verbose=TRUE), #' tz="Australia/Sydney") #' } toTz <- function(dtv, tzfrom, tzto, verbose = FALSE) { .Call(`_RcppCCTZ_toTz`, dtv, tzfrom, tzto, verbose) } #' Format a Datetime vector #' #' An alternative to \code{format.POSIXct} based on the CCTZ library. The #' \code{formatDouble} variant uses two vectors for seconds since the epoch #' and fractional nanoseconds, respectively, to provide fuller resolution. #' #' @title Format a Datetime vector as a string vector #' @param dtv A Datetime vector object to be formatted #' @param fmt A string with the format, which is based on \code{strftime} with some #' extensions; see the CCTZ documentation for details. #' @param lcltzstr The local timezone object for creation the CCTZ timepoint #' @param tgttzstr The target timezone for the desired format #' @return A string vector with the requested format of the datetime objects #' @section Note: #' Windows is now supported via the \code{g++-4.9} compiler, but note #' that it provides an \emph{incomplete} C++11 library. This means we had #' to port a time parsing routine, and that string formatting is more #' limited. As one example, CCTZ frequently uses \code{"\%F \%T"} which do #' not work on Windows; one has to use \code{"\%Y-\%m-\%d \%H:\%M:\%S"}. #' @author Dirk Eddelbuettel #' @examples #' \dontrun{ #' now <- Sys.time() #' formatDatetime(now) # current (UTC) time, in full precision RFC3339 #' formatDatetime(now, tgttzstr="America/New_York") # same but in NY #' formatDatetime(now + 0:4) # vectorised #' } formatDatetime <- function(dtv, fmt = "%Y-%m-%dT%H:%M:%E*S%Ez", lcltzstr = "UTC", tgttzstr = "UTC") { .Call(`_RcppCCTZ_formatDatetime`, dtv, fmt, lcltzstr, tgttzstr) } #' Parse a Datetime vector #' #' An alternative to \code{as.POSIXct} based on the CCTZ library #' #' @title Parse a Datetime vector from a string vector #' @param svec A string vector from which a Datetime vector is to be parsed #' @param fmt A string with the format, which is based on \code{strftime} with some #' extensions; see the CCTZ documentation for details. #' @param tzstr The local timezone for the desired format #' @return A Datetime vector object for \code{parseDatetime}, a numeric matrix with #' two columns for seconds and nanoseconds for \code{parseDouble} #' @author Dirk Eddelbuettel #' @examples #' ds <- getOption("digits.secs") #' options(digits.secs=6) # max value #' parseDatetime("2016-12-07 10:11:12", "%Y-%m-%d %H:%M:%S") # full seconds #' parseDatetime("2016-12-07 10:11:12.123456", "%Y-%m-%d %H:%M:%E*S") # fractional seconds #' parseDatetime("2016-12-07T10:11:12.123456-00:00") ## default RFC3339 format #' parseDatetime("20161207 101112.123456", "%E4Y%m%d %H%M%E*S") # fractional seconds #' now <- trunc(Sys.time()) #' parseDatetime(formatDatetime(now + 0:4)) # vectorised #' options(digits.secs=ds) parseDatetime <- function(svec, fmt = "%Y-%m-%dT%H:%M:%E*S%Ez", tzstr = "UTC") { .Call(`_RcppCCTZ_parseDatetime`, svec, fmt, tzstr) } #' @rdname formatDatetime #' @param secv A numeric vector with seconds since the epoch #' @param nanov A numeric vector with nanoseconds since the epoch, #' complementing \code{secv}. formatDouble <- function(secv, nanov, fmt = "%Y-%m-%dT%H:%M:%E*S%Ez", tgttzstr = "UTC") { .Call(`_RcppCCTZ_formatDouble`, secv, nanov, fmt, tgttzstr) } #' @rdname parseDatetime parseDouble <- function(svec, fmt = "%Y-%m-%dT%H:%M:%E*S%Ez", tzstr = "UTC") { .Call(`_RcppCCTZ_parseDouble`, svec, fmt, tzstr) } now <- function() { invisible(.Call(`_RcppCCTZ_now`)) } RcppCCTZ/R/init.R0000755000176200001440000000040413121751234013131 0ustar liggesusers ## On Windows, set TZDIR to the zoneinfo shipped with R .onLoad <- function(libname, pkgname) { if (Sys.info()[["sysname"]] == "Windows" && Sys.getenv("TZDIR") == "") { Sys.setenv("TZDIR"=file.path(R.home(), "share", "zoneinfo")) } } RcppCCTZ/cleanup0000755000176200001440000000006614726436444013237 0ustar liggesusers rm -f src/*.o src/*.so src/*.dylib src/*~ *~ src/*.d RcppCCTZ/src/0000755000176200001440000000000014726436444012447 5ustar liggesusersRcppCCTZ/src/time_zone_posix.cc0000644000176200001440000001102714271014541016153 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "time_zone_posix.h" #include #include #include #include namespace cctz { namespace { const char kDigits[] = "0123456789"; const char* ParseInt(const char* p, int min, int max, int* vp) { int value = 0; const char* op = p; const int kMaxInt = std::numeric_limits::max(); for (; const char* dp = strchr(kDigits, *p); ++p) { int d = static_cast(dp - kDigits); if (d >= 10) break; // '\0' if (value > kMaxInt / 10) return nullptr; value *= 10; if (value > kMaxInt - d) return nullptr; value += d; } if (p == op || value < min || value > max) return nullptr; *vp = value; return p; } // abbr = <.*?> | [^-+,\d]{3,} const char* ParseAbbr(const char* p, std::string* abbr) { const char* op = p; if (*p == '<') { // special zoneinfo <...> form while (*++p != '>') { if (*p == '\0') return nullptr; } abbr->assign(op + 1, static_cast(p - op) - 1); return ++p; } while (*p != '\0') { if (strchr("-+,", *p)) break; if (strchr(kDigits, *p)) break; ++p; } if (p - op < 3) return nullptr; abbr->assign(op, static_cast(p - op)); return p; } // offset = [+|-]hh[:mm[:ss]] (aggregated into single seconds value) const char* ParseOffset(const char* p, int min_hour, int max_hour, int sign, std::int_fast32_t* offset) { if (p == nullptr) return nullptr; if (*p == '+' || *p == '-') { if (*p++ == '-') sign = -sign; } int hours = 0; int minutes = 0; int seconds = 0; p = ParseInt(p, min_hour, max_hour, &hours); if (p == nullptr) return nullptr; if (*p == ':') { p = ParseInt(p + 1, 0, 59, &minutes); if (p == nullptr) return nullptr; if (*p == ':') { p = ParseInt(p + 1, 0, 59, &seconds); if (p == nullptr) return nullptr; } } *offset = sign * ((((hours * 60) + minutes) * 60) + seconds); return p; } // datetime = ( Jn | n | Mm.w.d ) [ / offset ] const char* ParseDateTime(const char* p, PosixTransition* res) { if (p != nullptr && *p == ',') { if (*++p == 'M') { int month = 0; if ((p = ParseInt(p + 1, 1, 12, &month)) != nullptr && *p == '.') { int week = 0; if ((p = ParseInt(p + 1, 1, 5, &week)) != nullptr && *p == '.') { int weekday = 0; if ((p = ParseInt(p + 1, 0, 6, &weekday)) != nullptr) { res->date.fmt = PosixTransition::M; res->date.m.month = static_cast(month); res->date.m.week = static_cast(week); res->date.m.weekday = static_cast(weekday); } } } } else if (*p == 'J') { int day = 0; if ((p = ParseInt(p + 1, 1, 365, &day)) != nullptr) { res->date.fmt = PosixTransition::J; res->date.j.day = static_cast(day); } } else { int day = 0; if ((p = ParseInt(p, 0, 365, &day)) != nullptr) { res->date.fmt = PosixTransition::N; res->date.n.day = static_cast(day); } } } if (p != nullptr) { res->time.offset = 2 * 60 * 60; // default offset is 02:00:00 if (*p == '/') p = ParseOffset(p + 1, -167, 167, 1, &res->time.offset); } return p; } } // namespace // spec = std offset [ dst [ offset ] , datetime , datetime ] bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res) { const char* p = spec.c_str(); if (*p == ':') return false; p = ParseAbbr(p, &res->std_abbr); p = ParseOffset(p, 0, 24, -1, &res->std_offset); if (p == nullptr) return false; if (*p == '\0') return true; p = ParseAbbr(p, &res->dst_abbr); if (p == nullptr) return false; res->dst_offset = res->std_offset + (60 * 60); // default if (*p != ',') p = ParseOffset(p, 0, 24, -1, &res->dst_offset); p = ParseDateTime(p, &res->dst_start); p = ParseDateTime(p, &res->dst_end); return p != nullptr && *p == '\0'; } } // namespace cctz RcppCCTZ/src/RcppExports.cpp0000644000176200001440000001717513722170466015451 0ustar liggesusers// Generated by using Rcpp::compileAttributes() -> do not edit by hand // Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 #include "../inst/include/RcppCCTZ_types.h" #include using namespace Rcpp; // example0 void example0(); RcppExport SEXP _RcppCCTZ_example0() { BEGIN_RCPP Rcpp::RNGScope rcpp_rngScope_gen; example0(); return R_NilValue; END_RCPP } // helloMoon Rcpp::CharacterVector helloMoon(bool verbose); RcppExport SEXP _RcppCCTZ_helloMoon(SEXP verboseSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< bool >::type verbose(verboseSEXP); rcpp_result_gen = Rcpp::wrap(helloMoon(verbose)); return rcpp_result_gen; END_RCPP } // example1 void example1(); RcppExport SEXP _RcppCCTZ_example1() { BEGIN_RCPP Rcpp::RNGScope rcpp_rngScope_gen; example1(); return R_NilValue; END_RCPP } // example2 int example2(); RcppExport SEXP _RcppCCTZ_example2() { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; rcpp_result_gen = Rcpp::wrap(example2()); return rcpp_result_gen; END_RCPP } // example3 void example3(); RcppExport SEXP _RcppCCTZ_example3() { BEGIN_RCPP Rcpp::RNGScope rcpp_rngScope_gen; example3(); return R_NilValue; END_RCPP } // example4 void example4(); RcppExport SEXP _RcppCCTZ_example4() { BEGIN_RCPP Rcpp::RNGScope rcpp_rngScope_gen; example4(); return R_NilValue; END_RCPP } // exampleFormat void exampleFormat(); RcppExport SEXP _RcppCCTZ_exampleFormat() { BEGIN_RCPP Rcpp::RNGScope rcpp_rngScope_gen; exampleFormat(); return R_NilValue; END_RCPP } // tzDiff Rcpp::NumericVector tzDiff(const std::string tzfrom, const std::string tzto, const Rcpp::NumericVector& dt, bool verbose); RcppExport SEXP _RcppCCTZ_tzDiff(SEXP tzfromSEXP, SEXP tztoSEXP, SEXP dtSEXP, SEXP verboseSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< const std::string >::type tzfrom(tzfromSEXP); Rcpp::traits::input_parameter< const std::string >::type tzto(tztoSEXP); Rcpp::traits::input_parameter< const Rcpp::NumericVector& >::type dt(dtSEXP); Rcpp::traits::input_parameter< bool >::type verbose(verboseSEXP); rcpp_result_gen = Rcpp::wrap(tzDiff(tzfrom, tzto, dt, verbose)); return rcpp_result_gen; END_RCPP } // toTz Rcpp::DatetimeVector toTz(Rcpp::DatetimeVector dtv, const std::string tzfrom, const std::string tzto, bool verbose); RcppExport SEXP _RcppCCTZ_toTz(SEXP dtvSEXP, SEXP tzfromSEXP, SEXP tztoSEXP, SEXP verboseSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::DatetimeVector >::type dtv(dtvSEXP); Rcpp::traits::input_parameter< const std::string >::type tzfrom(tzfromSEXP); Rcpp::traits::input_parameter< const std::string >::type tzto(tztoSEXP); Rcpp::traits::input_parameter< bool >::type verbose(verboseSEXP); rcpp_result_gen = Rcpp::wrap(toTz(dtv, tzfrom, tzto, verbose)); return rcpp_result_gen; END_RCPP } // formatDatetime Rcpp::CharacterVector formatDatetime(Rcpp::DatetimeVector dtv, std::string fmt, std::string lcltzstr, std::string tgttzstr); RcppExport SEXP _RcppCCTZ_formatDatetime(SEXP dtvSEXP, SEXP fmtSEXP, SEXP lcltzstrSEXP, SEXP tgttzstrSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::DatetimeVector >::type dtv(dtvSEXP); Rcpp::traits::input_parameter< std::string >::type fmt(fmtSEXP); Rcpp::traits::input_parameter< std::string >::type lcltzstr(lcltzstrSEXP); Rcpp::traits::input_parameter< std::string >::type tgttzstr(tgttzstrSEXP); rcpp_result_gen = Rcpp::wrap(formatDatetime(dtv, fmt, lcltzstr, tgttzstr)); return rcpp_result_gen; END_RCPP } // parseDatetime Rcpp::DatetimeVector parseDatetime(Rcpp::CharacterVector svec, std::string fmt, std::string tzstr); RcppExport SEXP _RcppCCTZ_parseDatetime(SEXP svecSEXP, SEXP fmtSEXP, SEXP tzstrSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::CharacterVector >::type svec(svecSEXP); Rcpp::traits::input_parameter< std::string >::type fmt(fmtSEXP); Rcpp::traits::input_parameter< std::string >::type tzstr(tzstrSEXP); rcpp_result_gen = Rcpp::wrap(parseDatetime(svec, fmt, tzstr)); return rcpp_result_gen; END_RCPP } // formatDouble Rcpp::CharacterVector formatDouble(Rcpp::NumericVector secv, Rcpp::NumericVector nanov, std::string fmt, std::string tgttzstr); RcppExport SEXP _RcppCCTZ_formatDouble(SEXP secvSEXP, SEXP nanovSEXP, SEXP fmtSEXP, SEXP tgttzstrSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::NumericVector >::type secv(secvSEXP); Rcpp::traits::input_parameter< Rcpp::NumericVector >::type nanov(nanovSEXP); Rcpp::traits::input_parameter< std::string >::type fmt(fmtSEXP); Rcpp::traits::input_parameter< std::string >::type tgttzstr(tgttzstrSEXP); rcpp_result_gen = Rcpp::wrap(formatDouble(secv, nanov, fmt, tgttzstr)); return rcpp_result_gen; END_RCPP } // parseDouble Rcpp::NumericMatrix parseDouble(Rcpp::CharacterVector svec, std::string fmt, std::string tzstr); RcppExport SEXP _RcppCCTZ_parseDouble(SEXP svecSEXP, SEXP fmtSEXP, SEXP tzstrSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::CharacterVector >::type svec(svecSEXP); Rcpp::traits::input_parameter< std::string >::type fmt(fmtSEXP); Rcpp::traits::input_parameter< std::string >::type tzstr(tzstrSEXP); rcpp_result_gen = Rcpp::wrap(parseDouble(svec, fmt, tzstr)); return rcpp_result_gen; END_RCPP } // now void now(); RcppExport SEXP _RcppCCTZ_now() { BEGIN_RCPP Rcpp::RNGScope rcpp_rngScope_gen; now(); return R_NilValue; END_RCPP } static const R_CallMethodDef CallEntries[] = { {"_RcppCCTZ_example0", (DL_FUNC) &_RcppCCTZ_example0, 0}, {"_RcppCCTZ_helloMoon", (DL_FUNC) &_RcppCCTZ_helloMoon, 1}, {"_RcppCCTZ_example1", (DL_FUNC) &_RcppCCTZ_example1, 0}, {"_RcppCCTZ_example2", (DL_FUNC) &_RcppCCTZ_example2, 0}, {"_RcppCCTZ_example3", (DL_FUNC) &_RcppCCTZ_example3, 0}, {"_RcppCCTZ_example4", (DL_FUNC) &_RcppCCTZ_example4, 0}, {"_RcppCCTZ_exampleFormat", (DL_FUNC) &_RcppCCTZ_exampleFormat, 0}, {"_RcppCCTZ_tzDiff", (DL_FUNC) &_RcppCCTZ_tzDiff, 4}, {"_RcppCCTZ_toTz", (DL_FUNC) &_RcppCCTZ_toTz, 4}, {"_RcppCCTZ_formatDatetime", (DL_FUNC) &_RcppCCTZ_formatDatetime, 4}, {"_RcppCCTZ_parseDatetime", (DL_FUNC) &_RcppCCTZ_parseDatetime, 3}, {"_RcppCCTZ_formatDouble", (DL_FUNC) &_RcppCCTZ_formatDouble, 4}, {"_RcppCCTZ_parseDouble", (DL_FUNC) &_RcppCCTZ_parseDouble, 3}, {"_RcppCCTZ_now", (DL_FUNC) &_RcppCCTZ_now, 0}, {NULL, NULL, 0} }; RcppExport void R_init_RcppCCTZ(DllInfo *dll) { R_RegisterCCallable("RcppCCTZ", "_RcppCCTZ_getOffset", (DL_FUNC) &_RcppCCTZ_getOffset); R_RegisterCCallable("RcppCCTZ", "_RcppCCTZ_convertToCivilSecond", (DL_FUNC) &_RcppCCTZ_convertToCivilSecond); R_RegisterCCallable("RcppCCTZ", "_RcppCCTZ_convertToTimePoint", (DL_FUNC) &_RcppCCTZ_convertToTimePoint); R_RegisterCCallable("RcppCCTZ", "_RcppCCTZ_getOffset_nothrow", (DL_FUNC) &_RcppCCTZ_getOffset_nothrow); R_RegisterCCallable("RcppCCTZ", "_RcppCCTZ_convertToCivilSecond_nothrow", (DL_FUNC) &_RcppCCTZ_convertToCivilSecond_nothrow); R_RegisterCCallable("RcppCCTZ", "_RcppCCTZ_convertToTimePoint_nothrow", (DL_FUNC) &_RcppCCTZ_convertToTimePoint_nothrow); R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); } RcppCCTZ/src/tzfile.h0000644000176200001440000000763114271014541014105 0ustar liggesusers/* Layout and location of TZif files. */ #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/share/zoneinfo" /* Time zone object file directory */ #endif /* !defined TZDIR */ #ifndef TZDEFAULT #define TZDEFAULT "/etc/localtime" #endif /* !defined TZDEFAULT */ #ifndef TZDEFRULES #define TZDEFRULES "posixrules" #endif /* !defined TZDEFRULES */ /* See Internet RFC 8536 for more details about the following format. */ /* ** Each file begins with. . . */ #define TZ_MAGIC "TZif" struct tzhead { char tzh_magic[4]; /* TZ_MAGIC */ char tzh_version[1]; /* '\0' or '2'-'4' as of 2021 */ char tzh_reserved[15]; /* reserved; must be zero */ char tzh_ttisutcnt[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 UT 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 1, transition ** time is standard time, if 0, ** transition time is local (wall clock) ** time; if absent, transition times are ** assumed to be local time ** tzh_ttisutcnt (char)s indexed by type; if 1, transition ** time is UT, if 0, transition time is ** local time; if absent, transition ** times are assumed to be local time. ** When this is 1, the corresponding ** std/wall indicator must also be 1. */ /* ** 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 2000 #endif /* !defined TZ_MAX_TIMES */ #ifndef TZ_MAX_TYPES /* This must be at least 17 for Europe/Samara and Europe/Vilnius. */ #define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ #endif /* !defined TZ_MAX_TYPES */ #ifndef TZ_MAX_CHARS #define TZ_MAX_CHARS 50 /* 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 */ #endif /* !defined TZFILE_H */ RcppCCTZ/src/time_zone_info.cc0000644000176200001440000011546114271014541015753 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // This file implements the TimeZoneIf interface using the "zoneinfo" // data provided by the IANA Time Zone Database (i.e., the only real game // in town). // // TimeZoneInfo represents the history of UTC-offset changes within a time // zone. Most changes are due to daylight-saving rules, but occasionally // shifts are made to the time-zone's base offset. The database only attempts // to be definitive for times since 1970, so be wary of local-time conversions // before that. Also, rule and zone-boundary changes are made at the whim // of governments, so the conversion of future times needs to be taken with // a grain of salt. // // For more information see tzfile(5), http://www.iana.org/time-zones, or // https://en.wikipedia.org/wiki/Zoneinfo. // // Note that we assume the proleptic Gregorian calendar and 60-second // minutes throughout. #include "time_zone_info.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cctz/civil_time.h" #include "time_zone_fixed.h" #include "time_zone_posix.h" namespace cctz { namespace { inline bool IsLeap(year_t year) { return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0); } // The number of days in non-leap and leap years respectively. const std::int_least32_t kDaysPerYear[2] = {365, 366}; // The day offsets of the beginning of each (1-based) month in non-leap and // leap years respectively (e.g., 335 days before December in a leap year). const std::int_least16_t kMonthOffsets[2][1 + 12 + 1] = { {-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, {-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}, }; // We reject leap-second encoded zoneinfo and so assume 60-second minutes. const std::int_least32_t kSecsPerDay = 24 * 60 * 60; // 400-year chunks always have 146097 days (20871 weeks). const std::int_least64_t kSecsPer400Years = 146097LL * kSecsPerDay; // Like kDaysPerYear[] but scaled up by a factor of kSecsPerDay. const std::int_least32_t kSecsPerYear[2] = { 365 * kSecsPerDay, 366 * kSecsPerDay, }; // Convert a cctz::weekday to a POSIX TZ weekday number (0==Sun, ..., 6=Sat). inline int ToPosixWeekday(weekday wd) { switch (wd) { case weekday::sunday: return 0; case weekday::monday: return 1; case weekday::tuesday: return 2; case weekday::wednesday: return 3; case weekday::thursday: return 4; case weekday::friday: return 5; case weekday::saturday: return 6; } return 0; /*NOTREACHED*/ } // Single-byte, unsigned numeric values are encoded directly. inline std::uint_fast8_t Decode8(const char* cp) { return static_cast(*cp) & 0xff; } // Multi-byte, numeric values are encoded using a MSB first, // twos-complement representation. These helpers decode, from // the given address, 4-byte and 8-byte values respectively. // Note: If int_fastXX_t == intXX_t and this machine is not // twos complement, then there will be at least one input value // we cannot represent. std::int_fast32_t Decode32(const char* cp) { std::uint_fast32_t v = 0; for (int i = 0; i != (32 / 8); ++i) v = (v << 8) | Decode8(cp++); const std::int_fast32_t s32max = 0x7fffffff; const auto s32maxU = static_cast(s32max); if (v <= s32maxU) return static_cast(v); return static_cast(v - s32maxU - 1) - s32max - 1; } std::int_fast64_t Decode64(const char* cp) { std::uint_fast64_t v = 0; for (int i = 0; i != (64 / 8); ++i) v = (v << 8) | Decode8(cp++); const std::int_fast64_t s64max = 0x7fffffffffffffff; const auto s64maxU = static_cast(s64max); if (v <= s64maxU) return static_cast(v); return static_cast(v - s64maxU - 1) - s64max - 1; } // Generate a year-relative offset for a PosixTransition. std::int_fast64_t TransOffset(bool leap_year, int jan1_weekday, const PosixTransition& pt) { std::int_fast64_t days = 0; switch (pt.date.fmt) { case PosixTransition::J: { days = pt.date.j.day; if (!leap_year || days < kMonthOffsets[1][3]) days -= 1; break; } case PosixTransition::N: { days = pt.date.n.day; break; } case PosixTransition::M: { const bool last_week = (pt.date.m.week == 5); days = kMonthOffsets[leap_year][pt.date.m.month + last_week]; const std::int_fast64_t weekday = (jan1_weekday + days) % 7; if (last_week) { days -= (weekday + 7 - 1 - pt.date.m.weekday) % 7 + 1; } else { days += (pt.date.m.weekday + 7 - weekday) % 7; days += (pt.date.m.week - 1) * 7; } break; } } return (days * kSecsPerDay) + pt.time.offset; } inline time_zone::civil_lookup MakeUnique(const time_point& tp) { time_zone::civil_lookup cl; cl.kind = time_zone::civil_lookup::UNIQUE; cl.pre = cl.trans = cl.post = tp; return cl; } inline time_zone::civil_lookup MakeUnique(std::int_fast64_t unix_time) { return MakeUnique(FromUnixSeconds(unix_time)); } inline time_zone::civil_lookup MakeSkipped(const Transition& tr, const civil_second& cs) { time_zone::civil_lookup cl; cl.kind = time_zone::civil_lookup::SKIPPED; cl.pre = FromUnixSeconds(tr.unix_time - 1 + (cs - tr.prev_civil_sec)); cl.trans = FromUnixSeconds(tr.unix_time); cl.post = FromUnixSeconds(tr.unix_time - (tr.civil_sec - cs)); return cl; } inline time_zone::civil_lookup MakeRepeated(const Transition& tr, const civil_second& cs) { time_zone::civil_lookup cl; cl.kind = time_zone::civil_lookup::REPEATED; cl.pre = FromUnixSeconds(tr.unix_time - 1 - (tr.prev_civil_sec - cs)); cl.trans = FromUnixSeconds(tr.unix_time); cl.post = FromUnixSeconds(tr.unix_time + (cs - tr.civil_sec)); return cl; } inline civil_second YearShift(const civil_second& cs, year_t shift) { return civil_second(cs.year() + shift, cs.month(), cs.day(), cs.hour(), cs.minute(), cs.second()); } } // namespace // What (no leap-seconds) UTC+seconds zoneinfo would look like. bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) { transition_types_.resize(1); TransitionType& tt(transition_types_.back()); tt.utc_offset = static_cast(offset.count()); tt.is_dst = false; tt.abbr_index = 0; // We temporarily add some redundant, contemporary (2015 through 2025) // transitions for performance reasons. See TimeZoneInfo::LocalTime(). // TODO: Fix the performance issue and remove the extra transitions. transitions_.clear(); transitions_.reserve(12); for (const std::int_fast64_t unix_time : { -(1LL << 59), // a "first half" transition 1420070400LL, // 2015-01-01T00:00:00+00:00 1451606400LL, // 2016-01-01T00:00:00+00:00 1483228800LL, // 2017-01-01T00:00:00+00:00 1514764800LL, // 2018-01-01T00:00:00+00:00 1546300800LL, // 2019-01-01T00:00:00+00:00 1577836800LL, // 2020-01-01T00:00:00+00:00 1609459200LL, // 2021-01-01T00:00:00+00:00 1640995200LL, // 2022-01-01T00:00:00+00:00 1672531200LL, // 2023-01-01T00:00:00+00:00 1704067200LL, // 2024-01-01T00:00:00+00:00 1735689600LL, // 2025-01-01T00:00:00+00:00 }) { Transition& tr(*transitions_.emplace(transitions_.end())); tr.unix_time = unix_time; tr.type_index = 0; tr.civil_sec = LocalTime(tr.unix_time, tt).cs; tr.prev_civil_sec = tr.civil_sec - 1; } default_transition_type_ = 0; abbreviations_ = FixedOffsetToAbbr(offset); abbreviations_.append(1, '\0'); future_spec_.clear(); // never needed for a fixed-offset zone extended_ = false; tt.civil_max = LocalTime(seconds::max().count(), tt).cs; tt.civil_min = LocalTime(seconds::min().count(), tt).cs; transitions_.shrink_to_fit(); return true; } // Builds the in-memory header using the raw bytes from the file. bool TimeZoneInfo::Header::Build(const tzhead& tzh) { std::int_fast32_t v; if ((v = Decode32(tzh.tzh_timecnt)) < 0) return false; timecnt = static_cast(v); if ((v = Decode32(tzh.tzh_typecnt)) < 0) return false; typecnt = static_cast(v); if ((v = Decode32(tzh.tzh_charcnt)) < 0) return false; charcnt = static_cast(v); if ((v = Decode32(tzh.tzh_leapcnt)) < 0) return false; leapcnt = static_cast(v); if ((v = Decode32(tzh.tzh_ttisstdcnt)) < 0) return false; ttisstdcnt = static_cast(v); if ((v = Decode32(tzh.tzh_ttisutcnt)) < 0) return false; ttisutcnt = static_cast(v); return true; } // How many bytes of data are associated with this header. The result // depends upon whether this is a section with 4-byte or 8-byte times. std::size_t TimeZoneInfo::Header::DataLength(std::size_t time_len) const { std::size_t len = 0; len += (time_len + 1) * timecnt; // unix_time + type_index len += (4 + 1 + 1) * typecnt; // utc_offset + is_dst + abbr_index len += 1 * charcnt; // abbreviations len += (time_len + 4) * leapcnt; // leap-time + TAI-UTC len += 1 * ttisstdcnt; // UTC/local indicators len += 1 * ttisutcnt; // standard/wall indicators return len; } // zic(8) can generate no-op transitions when a zone changes rules at an // instant when there is actually no discontinuity. So we check whether // two transitions have equivalent types (same offset/is_dst/abbr). bool TimeZoneInfo::EquivTransitions(std::uint_fast8_t tt1_index, std::uint_fast8_t tt2_index) const { if (tt1_index == tt2_index) return true; const TransitionType& tt1(transition_types_[tt1_index]); const TransitionType& tt2(transition_types_[tt2_index]); if (tt1.utc_offset != tt2.utc_offset) return false; if (tt1.is_dst != tt2.is_dst) return false; if (tt1.abbr_index != tt2.abbr_index) return false; return true; } // Find/make a transition type with these attributes. bool TimeZoneInfo::GetTransitionType(std::int_fast32_t utc_offset, bool is_dst, const std::string& abbr, std::uint_least8_t* index) { std::size_t type_index = 0; std::size_t abbr_index = abbreviations_.size(); for (; type_index != transition_types_.size(); ++type_index) { const TransitionType& tt(transition_types_[type_index]); const char* tt_abbr = &abbreviations_[tt.abbr_index]; if (tt_abbr == abbr) abbr_index = tt.abbr_index; if (tt.utc_offset == utc_offset && tt.is_dst == is_dst) { if (abbr_index == tt.abbr_index) break; // reuse } } if (type_index > 255 || abbr_index > 255) { // No index space (8 bits) available for a new type or abbreviation. return false; } if (type_index == transition_types_.size()) { TransitionType& tt(*transition_types_.emplace(transition_types_.end())); tt.utc_offset = static_cast(utc_offset); tt.is_dst = is_dst; if (abbr_index == abbreviations_.size()) { abbreviations_.append(abbr); abbreviations_.append(1, '\0'); } tt.abbr_index = static_cast(abbr_index); } *index = static_cast(type_index); return true; } // Use the POSIX-TZ-environment-variable-style string to handle times // in years after the last transition stored in the zoneinfo data. bool TimeZoneInfo::ExtendTransitions() { extended_ = false; if (future_spec_.empty()) return true; // last transition prevails PosixTimeZone posix; if (!ParsePosixSpec(future_spec_, &posix)) return false; // Find transition type for the future std specification. std::uint_least8_t std_ti; if (!GetTransitionType(posix.std_offset, false, posix.std_abbr, &std_ti)) return false; if (posix.dst_abbr.empty()) { // std only // The future specification should match the last transition, and // that means that handling the future will fall out naturally. return EquivTransitions(transitions_.back().type_index, std_ti); } // Find transition type for the future dst specification. std::uint_least8_t dst_ti; if (!GetTransitionType(posix.dst_offset, true, posix.dst_abbr, &dst_ti)) return false; // Extend the transitions for an additional 400 years using the // future specification. Years beyond those can be handled by // mapping back to a cycle-equivalent year within that range. // We may need two additional transitions for the current year. transitions_.reserve(transitions_.size() + 400 * 2 + 2); extended_ = true; const Transition& last(transitions_.back()); const std::int_fast64_t last_time = last.unix_time; const TransitionType& last_tt(transition_types_[last.type_index]); last_year_ = LocalTime(last_time, last_tt).cs.year(); bool leap_year = IsLeap(last_year_); const civil_second jan1(last_year_); std::int_fast64_t jan1_time = jan1 - civil_second(); int jan1_weekday = ToPosixWeekday(get_weekday(jan1)); Transition dst = {0, dst_ti, civil_second(), civil_second()}; Transition std = {0, std_ti, civil_second(), civil_second()}; for (const year_t limit = last_year_ + 400;; ++last_year_) { auto dst_trans_off = TransOffset(leap_year, jan1_weekday, posix.dst_start); auto std_trans_off = TransOffset(leap_year, jan1_weekday, posix.dst_end); dst.unix_time = jan1_time + dst_trans_off - posix.std_offset; std.unix_time = jan1_time + std_trans_off - posix.dst_offset; const auto* ta = dst.unix_time < std.unix_time ? &dst : &std; const auto* tb = dst.unix_time < std.unix_time ? &std : &dst; if (last_time < tb->unix_time) { if (last_time < ta->unix_time) transitions_.push_back(*ta); transitions_.push_back(*tb); } if (last_year_ == limit) break; jan1_time += kSecsPerYear[leap_year]; jan1_weekday = (jan1_weekday + kDaysPerYear[leap_year]) % 7; leap_year = !leap_year && IsLeap(last_year_ + 1); } return true; } bool TimeZoneInfo::Load(ZoneInfoSource* zip) { // Read and validate the header. tzhead tzh; if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false; if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0) return false; Header hdr; if (!hdr.Build(tzh)) return false; std::size_t time_len = 4; if (tzh.tzh_version[0] != '\0') { // Skip the 4-byte data. if (zip->Skip(hdr.DataLength(time_len)) != 0) return false; // Read and validate the header for the 8-byte data. if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false; if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0) return false; if (tzh.tzh_version[0] == '\0') return false; if (!hdr.Build(tzh)) return false; time_len = 8; } if (hdr.typecnt == 0) return false; if (hdr.leapcnt != 0) { // This code assumes 60-second minutes so we do not want // the leap-second encoded zoneinfo. We could reverse the // compensation, but the "right" encoding is rarely used // so currently we simply reject such data. return false; } if (hdr.ttisstdcnt != 0 && hdr.ttisstdcnt != hdr.typecnt) return false; if (hdr.ttisutcnt != 0 && hdr.ttisutcnt != hdr.typecnt) return false; // Read the data into a local buffer. std::size_t len = hdr.DataLength(time_len); std::vector tbuf(len); if (zip->Read(tbuf.data(), len) != len) return false; const char* bp = tbuf.data(); // Decode and validate the transitions. transitions_.reserve(hdr.timecnt + 2); transitions_.resize(hdr.timecnt); for (std::size_t i = 0; i != hdr.timecnt; ++i) { transitions_[i].unix_time = (time_len == 4) ? Decode32(bp) : Decode64(bp); bp += time_len; if (i != 0) { // Check that the transitions are ordered by time (as zic guarantees). if (!Transition::ByUnixTime()(transitions_[i - 1], transitions_[i])) return false; // out of order } } bool seen_type_0 = false; for (std::size_t i = 0; i != hdr.timecnt; ++i) { transitions_[i].type_index = Decode8(bp++); if (transitions_[i].type_index >= hdr.typecnt) return false; if (transitions_[i].type_index == 0) seen_type_0 = true; } // Decode and validate the transition types. transition_types_.reserve(hdr.typecnt + 2); transition_types_.resize(hdr.typecnt); for (std::size_t i = 0; i != hdr.typecnt; ++i) { transition_types_[i].utc_offset = static_cast(Decode32(bp)); if (transition_types_[i].utc_offset >= kSecsPerDay || transition_types_[i].utc_offset <= -kSecsPerDay) return false; bp += 4; transition_types_[i].is_dst = (Decode8(bp++) != 0); transition_types_[i].abbr_index = Decode8(bp++); if (transition_types_[i].abbr_index >= hdr.charcnt) return false; } // Determine the before-first-transition type. default_transition_type_ = 0; if (seen_type_0 && hdr.timecnt != 0) { std::uint_fast8_t index = 0; if (transition_types_[0].is_dst) { index = transitions_[0].type_index; while (index != 0 && transition_types_[index].is_dst) --index; } while (index != hdr.typecnt && transition_types_[index].is_dst) ++index; if (index != hdr.typecnt) default_transition_type_ = index; } // Copy all the abbreviations. abbreviations_.reserve(hdr.charcnt + 10); abbreviations_.assign(bp, hdr.charcnt); bp += hdr.charcnt; // Skip the unused portions. We've already dispensed with leap-second // encoded zoneinfo. The ttisstd/ttisgmt indicators only apply when // interpreting a POSIX spec that does not include start/end rules, and // that isn't the case here (see "zic -p"). bp += (8 + 4) * hdr.leapcnt; // leap-time + TAI-UTC bp += 1 * hdr.ttisstdcnt; // UTC/local indicators bp += 1 * hdr.ttisutcnt; // standard/wall indicators assert(bp == tbuf.data() + tbuf.size()); future_spec_.clear(); if (tzh.tzh_version[0] != '\0') { // Snarf up the NL-enclosed future POSIX spec. Note // that version '3' files utilize an extended format. auto get_char = [](ZoneInfoSource* azip) -> int { unsigned char ch; // all non-EOF results are positive return (azip->Read(&ch, 1) == 1) ? ch : EOF; }; if (get_char(zip) != '\n') return false; for (int c = get_char(zip); c != '\n'; c = get_char(zip)) { if (c == EOF) return false; future_spec_.push_back(static_cast(c)); } } // We don't check for EOF so that we're forwards compatible. // If we did not find version information during the standard loading // process (as of tzh_version '3' that is unsupported), then ask the // ZoneInfoSource for any out-of-bound version string it may be privy to. if (version_.empty()) { version_ = zip->Version(); } // Trim redundant transitions. zic may have added these to work around // differences between the glibc and reference implementations (see // zic.c:dontmerge) and the Qt library (see zic.c:WORK_AROUND_QTBUG_53071). // For us, they just get in the way when we do future_spec_ extension. while (hdr.timecnt > 1) { if (!EquivTransitions(transitions_[hdr.timecnt - 1].type_index, transitions_[hdr.timecnt - 2].type_index)) { break; } hdr.timecnt -= 1; } transitions_.resize(hdr.timecnt); // Ensure that there is always a transition in the first half of the // time line (the second half is handled below) so that the signed // difference between a civil_second and the civil_second of its // previous transition is always representable, without overflow. if (transitions_.empty() || transitions_.front().unix_time >= 0) { Transition& tr(*transitions_.emplace(transitions_.begin())); tr.unix_time = -(1LL << 59); // -18267312070-10-26T17:01:52+00:00 tr.type_index = default_transition_type_; } // Extend the transitions using the future specification. if (!ExtendTransitions()) return false; // Ensure that there is always a transition in the second half of the // time line (the first half is handled above) so that the signed // difference between a civil_second and the civil_second of its // previous transition is always representable, without overflow. const Transition& last(transitions_.back()); if (last.unix_time < 0) { const std::uint_fast8_t type_index = last.type_index; Transition& tr(*transitions_.emplace(transitions_.end())); tr.unix_time = 2147483647; // 2038-01-19T03:14:07+00:00 tr.type_index = type_index; } // Compute the local civil time for each transition and the preceding // second. These will be used for reverse conversions in MakeTime(). const TransitionType* ttp = &transition_types_[default_transition_type_]; for (std::size_t i = 0; i != transitions_.size(); ++i) { Transition& tr(transitions_[i]); tr.prev_civil_sec = LocalTime(tr.unix_time, *ttp).cs - 1; ttp = &transition_types_[tr.type_index]; tr.civil_sec = LocalTime(tr.unix_time, *ttp).cs; if (i != 0) { // Check that the transitions are ordered by civil time. Essentially // this means that an offset change cannot cross another such change. // No one does this in practice, and we depend on it in MakeTime(). if (!Transition::ByCivilTime()(transitions_[i - 1], tr)) return false; // out of order } } // Compute the maximum/minimum civil times that can be converted to a // time_point for each of the zone's transition types. for (auto& tt : transition_types_) { tt.civil_max = LocalTime(seconds::max().count(), tt).cs; tt.civil_min = LocalTime(seconds::min().count(), tt).cs; } transitions_.shrink_to_fit(); return true; } namespace { using FilePtr = std::unique_ptr; // fopen(3) adaptor. inline FilePtr FOpen(const char* path, const char* mode) { #if defined(_MSC_VER) FILE* fp; if (fopen_s(&fp, path, mode) != 0) fp = nullptr; return FilePtr(fp, fclose); #else // TODO: Enable the close-on-exec flag. return FilePtr(fopen(path, mode), fclose); #endif } // A stdio(3)-backed implementation of ZoneInfoSource. class FileZoneInfoSource : public ZoneInfoSource { public: static std::unique_ptr Open(const std::string& name); std::size_t Read(void* ptr, std::size_t size) override { size = std::min(size, len_); std::size_t nread = fread(ptr, 1, size, fp_.get()); len_ -= nread; return nread; } int Skip(std::size_t offset) override { offset = std::min(offset, len_); int rc = fseek(fp_.get(), static_cast(offset), SEEK_CUR); if (rc == 0) len_ -= offset; return rc; } std::string Version() const override { // TODO: It would nice if the zoneinfo data included the tzdb version. return std::string(); } protected: explicit FileZoneInfoSource( FilePtr fp, std::size_t len = std::numeric_limits::max()) : fp_(std::move(fp)), len_(len) {} private: FilePtr fp_; std::size_t len_; }; std::unique_ptr FileZoneInfoSource::Open( const std::string& name) { // Use of the "file:" prefix is intended for testing purposes only. const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0; // Map the time-zone name to a path name. std::string path; if (pos == name.size() || name[pos] != '/') { const char* tzdir = "/usr/share/zoneinfo"; char* tzdir_env = nullptr; #if defined(_MSC_VER) _dupenv_s(&tzdir_env, nullptr, "TZDIR"); #else tzdir_env = std::getenv("TZDIR"); #endif if (tzdir_env && *tzdir_env) tzdir = tzdir_env; path += tzdir; path += '/'; #if defined(_MSC_VER) free(tzdir_env); #endif } path.append(name, pos, std::string::npos); // Open the zoneinfo file. auto fp = FOpen(path.c_str(), "rb"); if (fp == nullptr) return nullptr; return std::unique_ptr(new FileZoneInfoSource(std::move(fp))); } class AndroidZoneInfoSource : public FileZoneInfoSource { public: static std::unique_ptr Open(const std::string& name); std::string Version() const override { return version_; } private: explicit AndroidZoneInfoSource(FilePtr fp, std::size_t len, std::string version) : FileZoneInfoSource(std::move(fp), len), version_(std::move(version)) {} std::string version_; }; std::unique_ptr AndroidZoneInfoSource::Open( const std::string& name) { // Use of the "file:" prefix is intended for testing purposes only. const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0; // See Android's libc/tzcode/bionic.cpp for additional information. for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata", "/system/usr/share/zoneinfo/tzdata"}) { auto fp = FOpen(tzdata, "rb"); if (fp == nullptr) continue; char hbuf[24]; // covers header.zonetab_offset too if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue; if (strncmp(hbuf, "tzdata", 6) != 0) continue; const char* vers = (hbuf[11] == '\0') ? hbuf + 6 : ""; const std::int_fast32_t index_offset = Decode32(hbuf + 12); const std::int_fast32_t data_offset = Decode32(hbuf + 16); if (index_offset < 0 || data_offset < index_offset) continue; if (fseek(fp.get(), static_cast(index_offset), SEEK_SET) != 0) continue; char ebuf[52]; // covers entry.unused too const std::size_t index_size = static_cast(data_offset - index_offset); const std::size_t zonecnt = index_size / sizeof(ebuf); if (zonecnt * sizeof(ebuf) != index_size) continue; for (std::size_t i = 0; i != zonecnt; ++i) { if (fread(ebuf, 1, sizeof(ebuf), fp.get()) != sizeof(ebuf)) break; const std::int_fast32_t start = data_offset + Decode32(ebuf + 40); const std::int_fast32_t length = Decode32(ebuf + 44); if (start < 0 || length < 0) break; ebuf[40] = '\0'; // ensure zone name is NUL terminated if (strcmp(name.c_str() + pos, ebuf) == 0) { if (fseek(fp.get(), static_cast(start), SEEK_SET) != 0) break; return std::unique_ptr(new AndroidZoneInfoSource( std::move(fp), static_cast(length), vers)); } } } return nullptr; } // A zoneinfo source for use inside Fuchsia components. This attempts to // read zoneinfo files from one of several known paths in a component's // incoming namespace. [Config data][1] is preferred, but package-specific // resources are also supported. // // Fuchsia's implementation supports `FileZoneInfoSource::Version()`. // // [1]: https://fuchsia.dev/fuchsia-src/development/components/data#using_config_data_in_your_component class FuchsiaZoneInfoSource : public FileZoneInfoSource { public: static std::unique_ptr Open(const std::string& name); std::string Version() const override { return version_; } private: explicit FuchsiaZoneInfoSource(FilePtr fp, std::string version) : FileZoneInfoSource(std::move(fp)), version_(std::move(version)) {} std::string version_; }; std::unique_ptr FuchsiaZoneInfoSource::Open( const std::string& name) { // Use of the "file:" prefix is intended for testing purposes only. const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0; // Prefixes where a Fuchsia component might find zoneinfo files, // in descending order of preference. const auto kTzdataPrefixes = { "/config/data/tzdata/", "/pkg/data/tzdata/", "/data/tzdata/", }; const auto kEmptyPrefix = {""}; const bool name_absolute = (pos != name.size() && name[pos] == '/'); const auto prefixes = name_absolute ? kEmptyPrefix : kTzdataPrefixes; // Fuchsia builds place zoneinfo files at "". for (const std::string prefix : prefixes) { std::string path = prefix; if (!prefix.empty()) path += "zoneinfo/tzif2/"; // format path.append(name, pos, std::string::npos); auto fp = FOpen(path.c_str(), "rb"); if (fp == nullptr) continue; std::string version; if (!prefix.empty()) { // Fuchsia builds place the version in "revision.txt". std::ifstream version_stream(prefix + "revision.txt"); if (version_stream.is_open()) { // revision.txt should contain no newlines, but to be // defensive we read just the first line. std::getline(version_stream, version); } } return std::unique_ptr( new FuchsiaZoneInfoSource(std::move(fp), std::move(version))); } return nullptr; } } // namespace bool TimeZoneInfo::Load(const std::string& name) { // We can ensure that the loading of UTC or any other fixed-offset // zone never fails because the simple, fixed-offset state can be // internally generated. Note that this depends on our choice to not // accept leap-second encoded ("right") zoneinfo. auto offset = seconds::zero(); if (FixedOffsetFromName(name, &offset)) { return ResetToBuiltinUTC(offset); } // Find and use a ZoneInfoSource to load the named zone. auto zip = cctz_extension::zone_info_source_factory( name, [](const std::string& n) -> std::unique_ptr { if (auto z = FileZoneInfoSource::Open(n)) return z; if (auto z = AndroidZoneInfoSource::Open(n)) return z; if (auto z = FuchsiaZoneInfoSource::Open(n)) return z; return nullptr; }); return zip != nullptr && Load(zip.get()); } // BreakTime() translation for a particular transition type. time_zone::absolute_lookup TimeZoneInfo::LocalTime( std::int_fast64_t unix_time, const TransitionType& tt) const { // A civil time in "+offset" looks like (time+offset) in UTC. // Note: We perform two additions in the civil_second domain to // sidestep the chance of overflow in (unix_time + tt.utc_offset). return {(civil_second() + unix_time) + tt.utc_offset, tt.utc_offset, tt.is_dst, &abbreviations_[tt.abbr_index]}; } // BreakTime() translation for a particular transition. time_zone::absolute_lookup TimeZoneInfo::LocalTime( std::int_fast64_t unix_time, const Transition& tr) const { const TransitionType& tt = transition_types_[tr.type_index]; // Note: (unix_time - tr.unix_time) will never overflow as we // have ensured that there is always a "nearby" transition. return {tr.civil_sec + (unix_time - tr.unix_time), // TODO: Optimize. tt.utc_offset, tt.is_dst, &abbreviations_[tt.abbr_index]}; } // MakeTime() translation with a conversion-preserving +N * 400-year shift. time_zone::civil_lookup TimeZoneInfo::TimeLocal(const civil_second& cs, year_t c4_shift) const { assert(last_year_ - 400 < cs.year() && cs.year() <= last_year_); time_zone::civil_lookup cl = MakeTime(cs); if (c4_shift > seconds::max().count() / kSecsPer400Years) { cl.pre = cl.trans = cl.post = time_point::max(); } else { const auto offset = seconds(c4_shift * kSecsPer400Years); const auto limit = time_point::max() - offset; for (auto* tp : {&cl.pre, &cl.trans, &cl.post}) { if (*tp > limit) { *tp = time_point::max(); } else { *tp += offset; } } } return cl; } time_zone::absolute_lookup TimeZoneInfo::BreakTime( const time_point& tp) const { std::int_fast64_t unix_time = ToUnixSeconds(tp); const std::size_t timecnt = transitions_.size(); assert(timecnt != 0); // We always add a transition. if (unix_time < transitions_[0].unix_time) { return LocalTime(unix_time, transition_types_[default_transition_type_]); } if (unix_time >= transitions_[timecnt - 1].unix_time) { // After the last transition. If we extended the transitions using // future_spec_, shift back to a supported year using the 400-year // cycle of calendaric equivalence and then compensate accordingly. if (extended_) { const std::int_fast64_t diff = unix_time - transitions_[timecnt - 1].unix_time; const year_t shift = diff / kSecsPer400Years + 1; const auto d = seconds(shift * kSecsPer400Years); time_zone::absolute_lookup al = BreakTime(tp - d); al.cs = YearShift(al.cs, shift * 400); return al; } return LocalTime(unix_time, transitions_[timecnt - 1]); } const std::size_t hint = local_time_hint_.load(std::memory_order_relaxed); if (0 < hint && hint < timecnt) { if (transitions_[hint - 1].unix_time <= unix_time) { if (unix_time < transitions_[hint].unix_time) { return LocalTime(unix_time, transitions_[hint - 1]); } } } const Transition target = {unix_time, 0, civil_second(), civil_second()}; const Transition* begin = &transitions_[0]; const Transition* tr = std::upper_bound(begin, begin + timecnt, target, Transition::ByUnixTime()); local_time_hint_.store(static_cast(tr - begin), std::memory_order_relaxed); return LocalTime(unix_time, *--tr); } time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const { const std::size_t timecnt = transitions_.size(); assert(timecnt != 0); // We always add a transition. // Find the first transition after our target civil time. const Transition* tr = nullptr; const Transition* begin = &transitions_[0]; const Transition* end = begin + timecnt; if (cs < begin->civil_sec) { tr = begin; } else if (cs >= transitions_[timecnt - 1].civil_sec) { tr = end; } else { const std::size_t hint = time_local_hint_.load(std::memory_order_relaxed); if (0 < hint && hint < timecnt) { if (transitions_[hint - 1].civil_sec <= cs) { if (cs < transitions_[hint].civil_sec) { tr = begin + hint; } } } if (tr == nullptr) { const Transition target = {0, 0, cs, civil_second()}; tr = std::upper_bound(begin, end, target, Transition::ByCivilTime()); time_local_hint_.store(static_cast(tr - begin), std::memory_order_relaxed); } } if (tr == begin) { if (tr->prev_civil_sec >= cs) { // Before first transition, so use the default offset. const TransitionType& tt(transition_types_[default_transition_type_]); if (cs < tt.civil_min) return MakeUnique(time_point::min()); return MakeUnique(cs - (civil_second() + tt.utc_offset)); } // tr->prev_civil_sec < cs < tr->civil_sec return MakeSkipped(*tr, cs); } if (tr == end) { if (cs > (--tr)->prev_civil_sec) { // After the last transition. If we extended the transitions using // future_spec_, shift back to a supported year using the 400-year // cycle of calendaric equivalence and then compensate accordingly. if (extended_ && cs.year() > last_year_) { const year_t shift = (cs.year() - last_year_ - 1) / 400 + 1; return TimeLocal(YearShift(cs, shift * -400), shift); } const TransitionType& tt(transition_types_[tr->type_index]); if (cs > tt.civil_max) return MakeUnique(time_point::max()); return MakeUnique(tr->unix_time + (cs - tr->civil_sec)); } // tr->civil_sec <= cs <= tr->prev_civil_sec return MakeRepeated(*tr, cs); } if (tr->prev_civil_sec < cs) { // tr->prev_civil_sec < cs < tr->civil_sec return MakeSkipped(*tr, cs); } if (cs <= (--tr)->prev_civil_sec) { // tr->civil_sec <= cs <= tr->prev_civil_sec return MakeRepeated(*tr, cs); } // In between transitions. return MakeUnique(tr->unix_time + (cs - tr->civil_sec)); } std::string TimeZoneInfo::Version() const { return version_; } std::string TimeZoneInfo::Description() const { std::ostringstream oss; oss << "#trans=" << transitions_.size(); oss << " #types=" << transition_types_.size(); oss << " spec='" << future_spec_ << "'"; return oss.str(); } bool TimeZoneInfo::NextTransition(const time_point& tp, time_zone::civil_transition* trans) const { if (transitions_.empty()) return false; const Transition* begin = &transitions_[0]; const Transition* end = begin + transitions_.size(); if (begin->unix_time <= -(1LL << 59)) { // Do not report the BIG_BANG found in some zoneinfo data as it is // really a sentinel, not a transition. See pre-2018f tz/zic.c. ++begin; } std::int_fast64_t unix_time = ToUnixSeconds(tp); const Transition target = {unix_time, 0, civil_second(), civil_second()}; const Transition* tr = std::upper_bound(begin, end, target, Transition::ByUnixTime()); for (; tr != end; ++tr) { // skip no-op transitions std::uint_fast8_t prev_type_index = (tr == begin) ? default_transition_type_ : tr[-1].type_index; if (!EquivTransitions(prev_type_index, tr[0].type_index)) break; } // When tr == end we return false, ignoring future_spec_. if (tr == end) return false; trans->from = tr->prev_civil_sec + 1; trans->to = tr->civil_sec; return true; } bool TimeZoneInfo::PrevTransition(const time_point& tp, time_zone::civil_transition* trans) const { if (transitions_.empty()) return false; const Transition* begin = &transitions_[0]; const Transition* end = begin + transitions_.size(); if (begin->unix_time <= -(1LL << 59)) { // Do not report the BIG_BANG found in some zoneinfo data as it is // really a sentinel, not a transition. See pre-2018f tz/zic.c. ++begin; } std::int_fast64_t unix_time = ToUnixSeconds(tp); if (FromUnixSeconds(unix_time) != tp) { if (unix_time == std::numeric_limits::max()) { if (end == begin) return false; // Ignore future_spec_. trans->from = (--end)->prev_civil_sec + 1; trans->to = end->civil_sec; return true; } unix_time += 1; // ceils } const Transition target = {unix_time, 0, civil_second(), civil_second()}; const Transition* tr = std::lower_bound(begin, end, target, Transition::ByUnixTime()); for (; tr != begin; --tr) { // skip no-op transitions std::uint_fast8_t prev_type_index = (tr - 1 == begin) ? default_transition_type_ : tr[-2].type_index; if (!EquivTransitions(prev_type_index, tr[-1].type_index)) break; } // When tr == end we return the "last" transition, ignoring future_spec_. if (tr == begin) return false; trans->from = (--tr)->prev_civil_sec + 1; trans->to = tr->civil_sec; return true; } } // namespace cctz RcppCCTZ/src/time_zone_if.h0000644000176200001440000000465414271014541015261 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef CCTZ_TIME_ZONE_IF_H_ #define CCTZ_TIME_ZONE_IF_H_ #include #include #include #include #include "cctz/civil_time.h" #include "cctz/time_zone.h" namespace cctz { // A simple interface used to hide time-zone complexities from time_zone::Impl. // Subclasses implement the functions for civil-time conversions in the zone. class TimeZoneIf { public: // A factory function for TimeZoneIf implementations. static std::unique_ptr Load(const std::string& name); virtual ~TimeZoneIf(); virtual time_zone::absolute_lookup BreakTime( const time_point& tp) const = 0; virtual time_zone::civil_lookup MakeTime( const civil_second& cs) const = 0; virtual bool NextTransition(const time_point& tp, time_zone::civil_transition* trans) const = 0; virtual bool PrevTransition(const time_point& tp, time_zone::civil_transition* trans) const = 0; virtual std::string Version() const = 0; virtual std::string Description() const = 0; protected: TimeZoneIf() {} }; // Convert between time_point and a count of seconds since the // Unix epoch. We assume that the std::chrono::system_clock and the // Unix clock are second aligned, and that the results are representable. // (That is, that they share an epoch, which is required since C++20.) inline std::int_fast64_t ToUnixSeconds(const time_point& tp) { return (tp - std::chrono::time_point_cast( std::chrono::system_clock::from_time_t(0))).count(); } inline time_point FromUnixSeconds(std::int_fast64_t t) { return std::chrono::time_point_cast( std::chrono::system_clock::from_time_t(0)) + seconds(t); } } // namespace cctz #endif // CCTZ_TIME_ZONE_IF_H_ RcppCCTZ/src/get_time.h0000644000176200001440000007040613355642476014425 0ustar liggesusers// ============================================================================= // The code in this file has been adapted from code in the locale header from // the LLVM libc++ project and is licensed as follows. // // ============================================================================= // libc++ License // ============================================================================= // // The libc++ library is dual licensed under both the University of Illinois // "BSD-Like" license and the MIT license. As a user of this code you may // choose to use it under either license. As a contributor, you agree to allow // your code to be used under both. // // Full text of the relevant licenses is included below. // // ============================================================================= // // University of Illinois/NCSA // Open Source License // // Copyright (c) 2009-2016 by the contributors listed in CREDITS.TXT // // All rights reserved. // // Developed by: // // LLVM Team // // University of Illinois at Urbana-Champaign // // http://llvm.org // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // with the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimers. // // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimers in the // documentation and/or other materials provided with the distribution. // // * Neither the names of the LLVM Team, University of Illinois at // Urbana-Champaign, nor the names of its contributors may be used to // endorse or promote products derived from this Software without specific // prior written permission. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH // THE SOFTWARE. // // ============================================================================= // // Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #ifndef RCPP_CCTZ_GET_TIME #define RCPP_CCTZ_GET_TIME #include #include #include #include #include #include #include #include namespace std_backport { template class fake_unique_ptr { private: T* p_; public: fake_unique_ptr() : p_(NULL) {} fake_unique_ptr(T *p) : p_(p) {} ~fake_unique_ptr() { if (p_) free(p_); } T *get() { return p_; } void reset(T *p) { if (p_) free(p_); p_ = p; } }; template ForwardIterator scan_keyword(InputIterator& b, InputIterator e, ForwardIterator kb, ForwardIterator ke, const Ctype& ct, std::ios_base::iostate& err, bool case_sensitive = true) { typedef typename std::iterator_traits::value_type CharT; size_t nkw = static_cast(distance(kb, ke)); const unsigned char doesnt_match = '\0'; const unsigned char might_match = '\1'; const unsigned char does_match = '\2'; unsigned char statbuf[100]; unsigned char* status = statbuf; fake_unique_ptr< unsigned char > stat_hold; if (nkw > sizeof(statbuf)) { status = (unsigned char*)malloc(nkw); if (status == 0) { throw std::bad_alloc(); } stat_hold.reset(status); } size_t n_might_match = nkw; // At this point, any keyword might match size_t n_does_match = 0; // but none of them definitely do // Initialize all statuses to might_match, except for "" keywords are does_match unsigned char* st = status; for (ForwardIterator ky = kb; ky != ke; ++ky, (void) ++st) { if (!ky->empty()) *st = might_match; else { *st = does_match; --n_might_match; ++n_does_match; } } // While there might be a match, test keywords against the next CharT for (size_t indx = 0; b != e && n_might_match > 0; ++indx) { // Peek at the next CharT but don't consume it CharT c = *b; if (!case_sensitive) c = ct.toupper(c); bool consume = false; // For each keyword which might match, see if the indx character is c // If a match if found, consume c // If a match is found, and that is the last character in the keyword, // then that keyword matches. // If the keyword doesn't match this character, then change the keyword // to doesn't match st = status; for (ForwardIterator ky = kb; ky != ke; ++ky, (void) ++st) { if (*st == might_match) { CharT kc = (*ky)[indx]; if (!case_sensitive) kc = ct.toupper(kc); if (c == kc) { consume = true; if (ky->size() == indx+1) { *st = does_match; --n_might_match; ++n_does_match; } } else { *st = doesnt_match; --n_might_match; } } } // consume if we matched a character if (consume) { ++b; // If we consumed a character and there might be a matched keyword that // was marked matched on a previous iteration, then such keywords // which are now marked as not matching. if (n_might_match + n_does_match > 1) { st = status; for (ForwardIterator ky = kb; ky != ke; ++ky, (void) ++st) { if (*st == does_match && ky->size() != indx+1) { *st = doesnt_match; --n_does_match; } } } } } // We've exited the loop because we hit eof and/or we have no more "might matches". if (b == e) err |= std::ios_base::eofbit; // Return the first matching result for (st = status; kb != ke; ++kb, (void) ++st) if (*st == does_match) break; if (kb == ke) { err |= std::ios_base::failbit; } return kb; } template int get_up_to_n_digits(InputIterator& b, InputIterator e, std::ios_base::iostate& err, const std::ctype& ct, int n) { // Precondition: n >= 1 if (b == e) { err |= std::ios_base::eofbit | std::ios_base::failbit; return 0; } // get first digit CharT c = *b; if (!ct.is(std::ctype_base::digit, c)) { err |= std::ios_base::failbit; return 0; } int r = ct.narrow(c, 0) - '0'; for (++b, (void) --n; b != e && n > 0; ++b, (void) --n) { // get next digit c = *b; if (!ct.is(std::ctype_base::digit, c)) return r; r = r * 10 + ct.narrow(c, 0) - '0'; } if (b == e) err |= std::ios_base::eofbit; return r; } template class time_get_c_storage { protected: typedef std::basic_string string_type; string_type am_pm_[2]; virtual const string_type* am_pm() const; virtual const string_type& c() const; virtual const string_type& r() const; virtual const string_type& x() const; virtual const string_type& X() const; public: time_get_c_storage(const std::locale& loc); ~time_get_c_storage() {} }; template<> time_get_c_storage::time_get_c_storage(const std::locale& loc) { std::basic_ostringstream os; const std::time_put &tp = std::use_facet< std::time_put >(loc); std::tm tm; tm.tm_sec = 0; tm.tm_min = 0; tm.tm_hour = 11; tm.tm_mday = 0; tm.tm_mon = 0; tm.tm_year = 0; tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_isdst = -1; std::ostreambuf_iterator out(os); tp.put(out, os, ' ', &tm, 'p'); am_pm_[0] = os.str(); os.str(""); std::ostreambuf_iterator out2(os); tm.tm_hour = 13; tp.put(out2, os, ' ', &tm, 'p'); am_pm_[1] = os.str(); } template<> time_get_c_storage::time_get_c_storage(const std::locale& loc) { std::basic_ostringstream os; const std::time_put &tp = std::use_facet< std::time_put< wchar_t > >(loc); std::tm tm; tm.tm_sec = 0; tm.tm_min = 0; tm.tm_hour = 11; tm.tm_mday = 0; tm.tm_mon = 0; tm.tm_year = 0; tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_isdst = -1; std::ostreambuf_iterator out(os); tp.put(out, os, L' ', &tm, L'p'); am_pm_[0] = os.str(); os.str(L""); std::ostreambuf_iterator out2(os); tm.tm_hour = 13; tp.put(out2, os, L' ', &tm, L'p'); am_pm_[1] = os.str(); } template <> const std::string* time_get_c_storage::am_pm() const { return am_pm_; } template <> const std::wstring* time_get_c_storage::am_pm() const { return am_pm_; } template <> const std::string& time_get_c_storage::x() const { static std::string s("%m/%d/%y"); return s; } template <> const std::wstring& time_get_c_storage::x() const { static std::wstring s(L"%m/%d/%y"); return s; } template <> const std::string& time_get_c_storage::X() const { static std::string s("%H:%M:%S"); return s; } template <> const std::wstring& time_get_c_storage::X() const { static std::wstring s(L"%H:%M:%S"); return s; } template <> const std::string& time_get_c_storage::c() const { static std::string s("%a %b %d %H:%M:%S %Y"); return s; } template <> const std::wstring& time_get_c_storage::c() const { static std::wstring s(L"%a %b %d %H:%M:%S %Y"); return s; } template <> const std::string& time_get_c_storage::r() const { static std::string s("%I:%M:%S %p"); return s; } template <> const std::wstring& time_get_c_storage::r() const { static std::wstring s(L"%I:%M:%S %p"); return s; } template class time_parser : public time_get_c_storage { private: const std::time_get &tg_; public: typedef CharT char_type; typedef InputIterator iter_type; typedef std::time_base::dateorder dateorder; typedef std::basic_string string_type; time_parser(const std::locale& loc) : time_get_c_storage(loc), tg_(std::use_facet< std::time_get >(loc)) { } iter_type get(iter_type b, iter_type e, std::ios_base& iob, std::ios_base::iostate& err, tm *tm, char fmt, char mod = 0) const { return do_get(b, e, iob, err, tm, fmt, mod); } iter_type get(iter_type b, iter_type e, std::ios_base& iob, std::ios_base::iostate& err, tm* tm, const char_type* fmtb, const char_type* fmte) const; private: InputIterator do_get(iter_type b, iter_type e, std::ios_base& iob, std::ios_base::iostate& err, tm* tm, char fmt, char) const; void get_white_space(iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const; void get_percent(iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const; void get_day(int& d, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const; void get_month(int& m, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const; void get_year(int& y, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const; void get_year4(int& y, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const; void get_hour(int& d, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const; void get_12_hour(int& h, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const; void get_am_pm(int& h, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const; void get_minute(int& m, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const; void get_second(int& s, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const; void get_weekday(int& w, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const; void get_day_year_num(int& w, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const; }; template void time_parser::get_day(int& d, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const { int t = get_up_to_n_digits(b, e, err, ct, 2); if (!(err & std::ios_base::failbit) && 1 <= t && t <= 31) d = t; else { err |= std::ios_base::failbit; } } template void time_parser::get_month(int& m, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const { int t = get_up_to_n_digits(b, e, err, ct, 2) - 1; if (!(err & std::ios_base::failbit) && t <= 11) m = t; else { err |= std::ios_base::failbit; } } template void time_parser::get_year(int& y, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const { int t = get_up_to_n_digits(b, e, err, ct, 4); if (!(err & std::ios_base::failbit)) { if (t < 69) t += 2000; else if (69 <= t && t <= 99) t += 1900; y = t - 1900; } } template void time_parser::get_year4(int& y, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const { int t = get_up_to_n_digits(b, e, err, ct, 4); if (!(err & std::ios_base::failbit)) y = t - 1900; } template void time_parser::get_hour(int& h, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const { int t = get_up_to_n_digits(b, e, err, ct, 2); if (!(err & std::ios_base::failbit) && t <= 23) h = t; else { err |= std::ios_base::failbit; } } template void time_parser::get_12_hour(int& h, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const { int t = get_up_to_n_digits(b, e, err, ct, 2); if (!(err & std::ios_base::failbit) && 1 <= t && t <= 12) h = t; else { err |= std::ios_base::failbit; } } template void time_parser::get_minute(int& m, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const { int t = get_up_to_n_digits(b, e, err, ct, 2); if (!(err & std::ios_base::failbit) && t <= 59) m = t; else { err |= std::ios_base::failbit; } } template void time_parser::get_second(int& s, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const { int t = get_up_to_n_digits(b, e, err, ct, 2); if (!(err & std::ios_base::failbit) && t <= 60) s = t; else { err |= std::ios_base::failbit; } } template void time_parser::get_weekday(int& w, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const { int t = get_up_to_n_digits(b, e, err, ct, 1); if (!(err & std::ios_base::failbit) && t <= 6) w = t; else err |= std::ios_base::failbit; } template void time_parser::get_day_year_num(int& d, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const { int t = get_up_to_n_digits(b, e, err, ct, 3); if (!(err & std::ios_base::failbit) && t <= 365) d = t; else { err |= std::ios_base::failbit; } } template void time_parser::get_white_space(iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const { for (; b != e && ct.is(std::ctype_base::space, *b); ++b) ; if (b == e) err |= std::ios_base::eofbit; } template void time_parser::get_am_pm(int& h, iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const { const string_type* ap = this->am_pm(); if (ap[0].size() + ap[1].size() == 0) { err |= std::ios_base::failbit; return; } std::ptrdiff_t i = scan_keyword(b, e, ap, ap+2, ct, err, false) - ap; if (i == 0 && h == 12) h = 0; else if (i == 1 && h < 12) h += 12; } template void time_parser::get_percent(iter_type& b, iter_type e, std::ios_base::iostate& err, const std::ctype& ct) const { if (b == e) { err |= std::ios_base::eofbit | std::ios_base::failbit; return; } if (ct.narrow(*b, 0) != '%') { err |= std::ios_base::failbit; } else if(++b == e) { err |= std::ios_base::eofbit; } } template InputIterator time_parser::get(iter_type b, iter_type e, std::ios_base& iob, std::ios_base::iostate& err, tm* tm, const char_type* fmtb, const char_type* fmte) const { const std::ctype& ct = std::use_facet >(iob.getloc()); err = std::ios_base::goodbit; while (fmtb != fmte && err == std::ios_base::goodbit) { if (b == e) { err = std::ios_base::failbit; break; } if (ct.narrow(*fmtb, 0) == '%') { if (++fmtb == fmte) { err = std::ios_base::failbit; break; } char cmd = ct.narrow(*fmtb, 0); char opt = '\0'; if (cmd == 'E' || cmd == '0') { if (++fmtb == fmte) { err = std::ios_base::failbit; break; } opt = cmd; cmd = ct.narrow(*fmtb, 0); } b = do_get(b, e, iob, err, tm, cmd, opt); ++fmtb; } else if (ct.is(std::ctype_base::space, *fmtb)) { for (++fmtb; fmtb != fmte && ct.is(std::ctype_base::space, *fmtb); ++fmtb) ; for ( ; b != e && ct.is(std::ctype_base::space, *b); ++b) ; } else if (ct.toupper(*b) == ct.toupper(*fmtb)) { ++b; ++fmtb; } else { err = std::ios_base::failbit; } } if (b == e) err |= std::ios_base::eofbit; return b; } template InputIterator time_parser::do_get(iter_type b, iter_type e, std::ios_base& iob, std::ios_base::iostate& err, tm* tm, char fmt, char) const { err = std::ios_base::goodbit; const std::ctype& ct = std::use_facet >(iob.getloc()); switch (fmt) { case 'a': case 'A': b = tg_.get_weekday(b, e, iob, err, tm); break; case 'b': case 'B': case 'h': b = tg_.get_monthname(b, e, iob, err, tm); break; case 'c': { const string_type& fm = this->c(); b = get(b, e, iob, err, tm, fm.data(), fm.data() + fm.size()); } break; case 'd': case 'e': get_day(tm->tm_mday, b, e, err, ct); break; case 'D': { const char_type fm[] = {'%', 'm', '/', '%', 'd', '/', '%', 'y'}; b = get(b, e, iob, err, tm, fm, fm + sizeof(fm)/sizeof(fm[0])); } break; case 'F': { const char_type fm[] = {'%', 'Y', '-', '%', 'm', '-', '%', 'd'}; b = get(b, e, iob, err, tm, fm, fm + sizeof(fm)/sizeof(fm[0])); } break; case 'H': get_hour(tm->tm_hour, b, e, err, ct); break; case 'I': get_12_hour(tm->tm_hour, b, e, err, ct); break; case 'j': get_day_year_num(tm->tm_yday, b, e, err, ct); break; case 'm': get_month(tm->tm_mon, b, e, err, ct); break; case 'M': get_minute(tm->tm_min, b, e, err, ct); break; case 'n': case 't': get_white_space(b, e, err, ct); break; case 'p': get_am_pm(tm->tm_hour, b, e, err, ct); break; case 'r': { const char_type fm[] = {'%', 'I', ':', '%', 'M', ':', '%', 'S', ' ', '%', 'p'}; b = get(b, e, iob, err, tm, fm, fm + sizeof(fm)/sizeof(fm[0])); } break; case 'R': { const char_type fm[] = {'%', 'H', ':', '%', 'M'}; b = get(b, e, iob, err, tm, fm, fm + sizeof(fm)/sizeof(fm[0])); } break; case 'S': get_second(tm->tm_sec, b, e, err, ct); break; case 'T': { const char_type fm[] = {'%', 'H', ':', '%', 'M', ':', '%', 'S'}; b = get(b, e, iob, err, tm, fm, fm + sizeof(fm)/sizeof(fm[0])); } break; case 'w': get_weekday(tm->tm_wday, b, e, err, ct); break; case 'x': return tg_.get_date(b, e, iob, err, tm); case 'X': { const string_type& fm = this->X(); b = get(b, e, iob, err, tm, fm.data(), fm.data() + fm.size()); } break; case 'y': get_year(tm->tm_year, b, e, err, ct); break; case 'Y': get_year4(tm->tm_year, b, e, err, ct); break; case '%': get_percent(b, e, err, ct); break; default: err |= std::ios_base::failbit; } return b; } template class iom_t9; template std::basic_istream& operator>>(std::basic_istream& is, const iom_t9& x); template class iom_t9 { tm* tm_; const CharT* fmt_; public: iom_t9(tm* tm, const CharT* fmt) : tm_(tm), fmt_(fmt) {} template friend std::basic_istream<_Cp, Traits>& operator>>(std::basic_istream<_Cp, Traits>& is, const iom_t9<_Cp>& x); }; template std::basic_istream& operator>>(std::basic_istream& is, const iom_t9& x) { typename std::basic_istream::sentry s(is); if (s) { typedef std::istreambuf_iterator _Ip; typedef time_parser _Fp; std::ios_base::iostate err = std::ios_base::goodbit; const _Fp tf(is.getloc()); tf.get(_Ip(is), _Ip(), is, err, x.tm_, x.fmt_, x.fmt_ + Traits::length(x.fmt_)); is.setstate(err); } return is; } template inline iom_t9 get_time(tm* tm, const CharT* fmt) { return iom_t9(tm, fmt); } } #endif RcppCCTZ/src/RcppExports_snippet.h0000644000176200001440000000136213723003175016640 0ustar liggesusers/* The following needs to be preserved in the call to R_init_RcppCCTZ: R_RegisterCCallable("RcppCCTZ", "_RcppCCTZ_getOffset", (DL_FUNC) &_RcppCCTZ_getOffset); R_RegisterCCallable("RcppCCTZ", "_RcppCCTZ_convertToCivilSecond", (DL_FUNC) &_RcppCCTZ_convertToCivilSecond); R_RegisterCCallable("RcppCCTZ", "_RcppCCTZ_convertToTimePoint", (DL_FUNC) &_RcppCCTZ_convertToTimePoint); R_RegisterCCallable("RcppCCTZ", "_RcppCCTZ_getOffset_nothrow", (DL_FUNC) &_RcppCCTZ_getOffset_nothrow); R_RegisterCCallable("RcppCCTZ", "_RcppCCTZ_convertToCivilSecond_nothrow", (DL_FUNC) &_RcppCCTZ_convertToCivilSecond_nothrow); R_RegisterCCallable("RcppCCTZ", "_RcppCCTZ_convertToTimePoint_nothrow", (DL_FUNC) &_RcppCCTZ_convertToTimePoint_nothrow); */ RcppCCTZ/src/Makevars.win0000755000176200001440000000004214412065517014725 0ustar liggesusers PKG_CXXFLAGS = -I../inst/include RcppCCTZ/src/zone_info_source.cc0000644000176200001440000000774714271014541016324 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "cctz/zone_info_source.h" namespace cctz { // Defined out-of-line to avoid emitting a weak vtable in all TUs. ZoneInfoSource::~ZoneInfoSource() {} std::string ZoneInfoSource::Version() const { return std::string(); } } // namespace cctz namespace cctz_extension { namespace { // A default for cctz_extension::zone_info_source_factory, which simply // defers to the fallback factory. std::unique_ptr DefaultFactory( const std::string& name, const std::function( const std::string& name)>& fallback_factory) { return fallback_factory(name); } } // namespace // A "weak" definition for cctz_extension::zone_info_source_factory. // The user may override this with their own "strong" definition (see // zone_info_source.h). #if !defined(__has_attribute) #define __has_attribute(x) 0 #endif // MinGW is GCC on Windows, so while it asserts __has_attribute(weak), the // Windows linker cannot handle that. Nor does the MinGW compiler know how to // pass "#pragma comment(linker, ...)" to the Windows linker. #if (__has_attribute(weak) || defined(__GNUC__)) && !defined(__MINGW32__) ZoneInfoSourceFactory zone_info_source_factory __attribute__((weak)) = DefaultFactory; #elif defined(_MSC_VER) && !defined(__MINGW32__) && !defined(_LIBCPP_VERSION) extern ZoneInfoSourceFactory zone_info_source_factory; extern ZoneInfoSourceFactory default_factory; ZoneInfoSourceFactory default_factory = DefaultFactory; #if defined(_M_IX86) || defined(_M_ARM) #pragma comment( \ linker, \ "/alternatename:?zone_info_source_factory@cctz_extension@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@@U?$default_delete@VZoneInfoSource@cctz@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@3@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@@U?$default_delete@VZoneInfoSource@cctz@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@3@@ZA=?default_factory@cctz_extension@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@@U?$default_delete@VZoneInfoSource@cctz@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@3@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@@U?$default_delete@VZoneInfoSource@cctz@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@3@@ZA") #elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM64) #pragma comment( \ linker, \ "/alternatename:?zone_info_source_factory@cctz_extension@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@@U?$default_delete@VZoneInfoSource@cctz@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@3@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@@U?$default_delete@VZoneInfoSource@cctz@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@3@@ZEA=?default_factory@cctz_extension@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@@U?$default_delete@VZoneInfoSource@cctz@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@3@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@@U?$default_delete@VZoneInfoSource@cctz@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@3@@ZEA") #else #error Unsupported MSVC platform #endif // _M_ #else // Make it a "strong" definition if we have no other choice. ZoneInfoSourceFactory zone_info_source_factory = DefaultFactory; #endif } // namespace cctz_extension RcppCCTZ/src/time_zone_format.cc0000644000176200001440000007652214271014541016314 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #if !defined(HAS_STRPTIME) # if !defined(_MSC_VER) && !defined(__MINGW32__) # define HAS_STRPTIME 1 // assume everyone has strptime() except windows # endif #endif #if defined(HAS_STRPTIME) && HAS_STRPTIME # if !defined(_XOPEN_SOURCE) && !defined(__OpenBSD__) # define _XOPEN_SOURCE // Definedness suffices for strptime. # endif #endif #include "cctz/time_zone.h" // Include time.h directly since, by C++ standards, ctime doesn't have to // declare strptime. #include #include #include #include #include #include #include #include #include #include #if !HAS_STRPTIME #include #include #endif #include "cctz/civil_time.h" #include "time_zone_if.h" namespace cctz { namespace detail { namespace { #if !HAS_STRPTIME // Build a strptime() using C++11's std::get_time(). char* strptime(const char* s, const char* fmt, std::tm* tm) { std::istringstream input(s); input >> std::get_time(tm, fmt); if (input.fail()) return nullptr; return const_cast(s) + (input.eof() ? strlen(s) : static_cast(input.tellg())); } #endif // Convert a cctz::weekday to a tm_wday value (0-6, Sunday = 0). int ToTmWday(weekday wd) { switch (wd) { case weekday::sunday: return 0; case weekday::monday: return 1; case weekday::tuesday: return 2; case weekday::wednesday: return 3; case weekday::thursday: return 4; case weekday::friday: return 5; case weekday::saturday: return 6; } return 0; /*NOTREACHED*/ } // Convert a tm_wday value (0-6, Sunday = 0) to a cctz::weekday. weekday FromTmWday(int tm_wday) { switch (tm_wday) { case 0: return weekday::sunday; case 1: return weekday::monday; case 2: return weekday::tuesday; case 3: return weekday::wednesday; case 4: return weekday::thursday; case 5: return weekday::friday; case 6: return weekday::saturday; } return weekday::sunday; /*NOTREACHED*/ } std::tm ToTM(const time_zone::absolute_lookup& al) { std::tm tm{}; tm.tm_sec = al.cs.second(); tm.tm_min = al.cs.minute(); tm.tm_hour = al.cs.hour(); tm.tm_mday = al.cs.day(); tm.tm_mon = al.cs.month() - 1; // Saturate tm.tm_year is cases of over/underflow. if (al.cs.year() < std::numeric_limits::min() + 1900) { tm.tm_year = std::numeric_limits::min(); } else if (al.cs.year() - 1900 > std::numeric_limits::max()) { tm.tm_year = std::numeric_limits::max(); } else { tm.tm_year = static_cast(al.cs.year() - 1900); } tm.tm_wday = ToTmWday(get_weekday(al.cs)); tm.tm_yday = get_yearday(al.cs) - 1; tm.tm_isdst = al.is_dst ? 1 : 0; return tm; } // Returns the week of the year [0:53] given a civil day and the day on // which weeks are defined to start. int ToWeek(const civil_day& cd, weekday week_start) { const civil_day d(cd.year() % 400, cd.month(), cd.day()); return static_cast((d - prev_weekday(civil_year(d), week_start)) / 7); } const char kDigits[] = "0123456789"; // Formats a 64-bit integer in the given field width. Note that it is up // to the caller of Format64() [and Format02d()/FormatOffset()] to ensure // that there is sufficient space before ep to hold the conversion. char* Format64(char* ep, int width, std::int_fast64_t v) { bool neg = false; if (v < 0) { --width; neg = true; if (v == std::numeric_limits::min()) { // Avoid negating minimum value. std::int_fast64_t last_digit = -(v % 10); v /= 10; if (last_digit < 0) { ++v; last_digit += 10; } --width; *--ep = kDigits[last_digit]; } v = -v; } do { --width; *--ep = kDigits[v % 10]; } while (v /= 10); while (--width >= 0) *--ep = '0'; // zero pad if (neg) *--ep = '-'; return ep; } // Formats [0 .. 99] as %02d. char* Format02d(char* ep, int v) { *--ep = kDigits[v % 10]; *--ep = kDigits[(v / 10) % 10]; return ep; } // Formats a UTC offset, like +00:00. char* FormatOffset(char* ep, int offset, const char* mode) { // TODO: Follow the RFC3339 "Unknown Local Offset Convention" and // generate a "negative zero" when we're formatting a zero offset // as the result of a failed load_time_zone(). char sign = '+'; if (offset < 0) { offset = -offset; // bounded by 24h so no overflow sign = '-'; } const int seconds = offset % 60; const int minutes = (offset /= 60) % 60; const int hours = offset /= 60; const char sep = mode[0]; const bool ext = (sep != '\0' && mode[1] == '*'); const bool ccc = (ext && mode[2] == ':'); if (ext && (!ccc || seconds != 0)) { ep = Format02d(ep, seconds); *--ep = sep; } else { // If we're not rendering seconds, sub-minute negative offsets // should get a positive sign (e.g., offset=-10s => "+00:00"). if (hours == 0 && minutes == 0) sign = '+'; } if (!ccc || minutes != 0 || seconds != 0) { ep = Format02d(ep, minutes); if (sep != '\0') *--ep = sep; } ep = Format02d(ep, hours); *--ep = sign; return ep; } // Formats a std::tm using strftime(3). void FormatTM(std::string* out, const std::string& fmt, const std::tm& tm) { // strftime(3) returns the number of characters placed in the output // array (which may be 0 characters). It also returns 0 to indicate // an error, like the array wasn't large enough. To accommodate this, // the following code grows the buffer size from 2x the format string // length up to 32x. for (std::size_t i = 2; i != 32; i *= 2) { std::size_t buf_size = fmt.size() * i; std::vector buf(buf_size); if (std::size_t len = strftime(&buf[0], buf_size, fmt.c_str(), &tm)) { out->append(&buf[0], len); return; } } } // Used for %E#S/%E#f specifiers and for data values in parse(). template const char* ParseInt(const char* dp, int width, T min, T max, T* vp) { if (dp != nullptr) { const T kmin = std::numeric_limits::min(); bool erange = false; bool neg = false; T value = 0; if (*dp == '-') { neg = true; if (width <= 0 || --width != 0) { ++dp; } else { dp = nullptr; // width was 1 } } if (const char* const bp = dp) { while (const char* cp = strchr(kDigits, *dp)) { int d = static_cast(cp - kDigits); if (d >= 10) break; if (value < kmin / 10) { erange = true; break; } value *= 10; if (value < kmin + d) { erange = true; break; } value -= d; dp += 1; if (width > 0 && --width == 0) break; } if (dp != bp && !erange && (neg || value != kmin)) { if (!neg || value != 0) { if (!neg) value = -value; // make positive if (min <= value && value <= max) { *vp = value; } else { dp = nullptr; } } else { dp = nullptr; } } else { dp = nullptr; } } } return dp; } // The number of base-10 digits that can be represented by a signed 64-bit // integer. That is, 10^kDigits10_64 <= 2^63 - 1 < 10^(kDigits10_64 + 1). const int kDigits10_64 = 18; // 10^n for everything that can be represented by a signed 64-bit integer. const std::int_fast64_t kExp10[kDigits10_64 + 1] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000, 1000000000000000, 10000000000000000, 100000000000000000, 1000000000000000000, }; } // namespace // Uses strftime(3) to format the given Time. The following extended format // specifiers are also supported: // // - %Ez - RFC3339-compatible numeric UTC offset (+hh:mm or -hh:mm) // - %E*z - Full-resolution numeric UTC offset (+hh:mm:ss or -hh:mm:ss) // - %E#S - Seconds with # digits of fractional precision // - %E*S - Seconds with full fractional precision (a literal '*') // - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999) // - %ET - The RFC3339 "date-time" separator "T" // // The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are // handled internally for performance reasons. strftime(3) is slow due to // a POSIX requirement to respect changes to ${TZ}. // // The TZ/GNU %s extension is handled internally because strftime() has // to use mktime() to generate it, and that assumes the local time zone. // // We also handle the %z and %Z specifiers to accommodate platforms that do // not support the tm_gmtoff and tm_zone extensions to std::tm. // // Requires that zero() <= fs < seconds(1). std::string format(const std::string& format, const time_point& tp, const detail::femtoseconds& fs, const time_zone& tz) { std::string result; result.reserve(format.size()); // A reasonable guess for the result size. const time_zone::absolute_lookup al = tz.lookup(tp); const std::tm tm = ToTM(al); // Scratch buffer for internal conversions. char buf[3 + kDigits10_64]; // enough for longest conversion char* const ep = buf + sizeof(buf); char* bp; // works back from ep // Maintain three, disjoint subsequences that span format. // [format.begin() ... pending) : already formatted into result // [pending ... cur) : formatting pending, but no special cases // [cur ... format.end()) : unexamined // Initially, everything is in the unexamined part. const char* pending = format.c_str(); // NUL terminated const char* cur = pending; const char* end = pending + format.length(); while (cur != end) { // while something is unexamined // Moves cur to the next percent sign. const char* start = cur; while (cur != end && *cur != '%') ++cur; // If the new pending text is all ordinary, copy it out. if (cur != start && pending == start) { result.append(pending, static_cast(cur - pending)); pending = start = cur; } // Span the sequential percent signs. const char* percent = cur; while (cur != end && *cur == '%') ++cur; // If the new pending text is all percents, copy out one // percent for every matched pair, then skip those pairs. if (cur != start && pending == start) { std::size_t escaped = static_cast(cur - pending) / 2; result.append(pending, escaped); pending += escaped * 2; // Also copy out a single trailing percent. if (pending != cur && cur == end) { result.push_back(*pending++); } } // Loop unless we have an unescaped percent. if (cur == end || (cur - percent) % 2 == 0) continue; // Simple specifiers that we handle ourselves. if (strchr("YmdeUuWwHMSzZs%", *cur)) { if (cur - 1 != pending) { FormatTM(&result, std::string(pending, cur - 1), tm); } switch (*cur) { case 'Y': // This avoids the tm.tm_year overflow problem for %Y, however // tm.tm_year will still be used by other specifiers like %D. bp = Format64(ep, 0, al.cs.year()); result.append(bp, static_cast(ep - bp)); break; case 'm': bp = Format02d(ep, al.cs.month()); result.append(bp, static_cast(ep - bp)); break; case 'd': case 'e': bp = Format02d(ep, al.cs.day()); if (*cur == 'e' && *bp == '0') *bp = ' '; // for Windows result.append(bp, static_cast(ep - bp)); break; case 'U': bp = Format02d(ep, ToWeek(civil_day(al.cs), weekday::sunday)); result.append(bp, static_cast(ep - bp)); break; case 'u': bp = Format64(ep, 0, tm.tm_wday ? tm.tm_wday : 7); result.append(bp, static_cast(ep - bp)); break; case 'W': bp = Format02d(ep, ToWeek(civil_day(al.cs), weekday::monday)); result.append(bp, static_cast(ep - bp)); break; case 'w': bp = Format64(ep, 0, tm.tm_wday); result.append(bp, static_cast(ep - bp)); break; case 'H': bp = Format02d(ep, al.cs.hour()); result.append(bp, static_cast(ep - bp)); break; case 'M': bp = Format02d(ep, al.cs.minute()); result.append(bp, static_cast(ep - bp)); break; case 'S': bp = Format02d(ep, al.cs.second()); result.append(bp, static_cast(ep - bp)); break; case 'z': bp = FormatOffset(ep, al.offset, ""); result.append(bp, static_cast(ep - bp)); break; case 'Z': result.append(al.abbr); break; case 's': bp = Format64(ep, 0, ToUnixSeconds(tp)); result.append(bp, static_cast(ep - bp)); break; case '%': result.push_back('%'); break; } pending = ++cur; continue; } // More complex specifiers that we handle ourselves. if (*cur == ':' && cur + 1 != end) { if (*(cur + 1) == 'z') { // Formats %:z. if (cur - 1 != pending) { FormatTM(&result, std::string(pending, cur - 1), tm); } bp = FormatOffset(ep, al.offset, ":"); result.append(bp, static_cast(ep - bp)); pending = cur += 2; continue; } if (*(cur + 1) == ':' && cur + 2 != end) { if (*(cur + 2) == 'z') { // Formats %::z. if (cur - 1 != pending) { FormatTM(&result, std::string(pending, cur - 1), tm); } bp = FormatOffset(ep, al.offset, ":*"); result.append(bp, static_cast(ep - bp)); pending = cur += 3; continue; } if (*(cur + 2) == ':' && cur + 3 != end) { if (*(cur + 3) == 'z') { // Formats %:::z. if (cur - 1 != pending) { FormatTM(&result, std::string(pending, cur - 1), tm); } bp = FormatOffset(ep, al.offset, ":*:"); result.append(bp, static_cast(ep - bp)); pending = cur += 4; continue; } } } } // Loop if there is no E modifier. if (*cur != 'E' || ++cur == end) continue; // Format our extensions. if (*cur == 'T') { // Formats %ET. if (cur - 2 != pending) { FormatTM(&result, std::string(pending, cur - 2), tm); } result.append("T"); pending = ++cur; } else if (*cur == 'z') { // Formats %Ez. if (cur - 2 != pending) { FormatTM(&result, std::string(pending, cur - 2), tm); } bp = FormatOffset(ep, al.offset, ":"); result.append(bp, static_cast(ep - bp)); pending = ++cur; } else if (*cur == '*' && cur + 1 != end && *(cur + 1) == 'z') { // Formats %E*z. if (cur - 2 != pending) { FormatTM(&result, std::string(pending, cur - 2), tm); } bp = FormatOffset(ep, al.offset, ":*"); result.append(bp, static_cast(ep - bp)); pending = cur += 2; } else if (*cur == '*' && cur + 1 != end && (*(cur + 1) == 'S' || *(cur + 1) == 'f')) { // Formats %E*S or %E*F. if (cur - 2 != pending) { FormatTM(&result, std::string(pending, cur - 2), tm); } char* cp = ep; bp = Format64(cp, 15, fs.count()); while (cp != bp && cp[-1] == '0') --cp; switch (*(cur + 1)) { case 'S': if (cp != bp) *--bp = '.'; bp = Format02d(bp, al.cs.second()); break; case 'f': if (cp == bp) *--bp = '0'; break; } result.append(bp, static_cast(cp - bp)); pending = cur += 2; } else if (*cur == '4' && cur + 1 != end && *(cur + 1) == 'Y') { // Formats %E4Y. if (cur - 2 != pending) { FormatTM(&result, std::string(pending, cur - 2), tm); } bp = Format64(ep, 4, al.cs.year()); result.append(bp, static_cast(ep - bp)); pending = cur += 2; } else if (std::isdigit(*cur)) { // Possibly found %E#S or %E#f. int n = 0; if (const char* np = ParseInt(cur, 0, 0, 1024, &n)) { if (*np == 'S' || *np == 'f') { // Formats %E#S or %E#f. if (cur - 2 != pending) { FormatTM(&result, std::string(pending, cur - 2), tm); } bp = ep; if (n > 0) { if (n > kDigits10_64) n = kDigits10_64; bp = Format64(bp, n, (n > 15) ? fs.count() * kExp10[n - 15] : fs.count() / kExp10[15 - n]); if (*np == 'S') *--bp = '.'; } if (*np == 'S') bp = Format02d(bp, al.cs.second()); result.append(bp, static_cast(ep - bp)); pending = cur = ++np; } } } } // Formats any remaining data. if (end != pending) { FormatTM(&result, std::string(pending, end), tm); } return result; } namespace { const char* ParseOffset(const char* dp, const char* mode, int* offset) { if (dp != nullptr) { const char first = *dp++; if (first == '+' || first == '-') { char sep = mode[0]; int hours = 0; int minutes = 0; int seconds = 0; const char* ap = ParseInt(dp, 2, 0, 23, &hours); if (ap != nullptr && ap - dp == 2) { dp = ap; if (sep != '\0' && *ap == sep) ++ap; const char* bp = ParseInt(ap, 2, 0, 59, &minutes); if (bp != nullptr && bp - ap == 2) { dp = bp; if (sep != '\0' && *bp == sep) ++bp; const char* cp = ParseInt(bp, 2, 0, 59, &seconds); if (cp != nullptr && cp - bp == 2) dp = cp; } *offset = ((hours * 60 + minutes) * 60) + seconds; if (first == '-') *offset = -*offset; } else { dp = nullptr; } } else if (first == 'Z' || first == 'z') { // Zulu *offset = 0; } else { dp = nullptr; } } return dp; } const char* ParseZone(const char* dp, std::string* zone) { zone->clear(); if (dp != nullptr) { while (*dp != '\0' && !std::isspace(*dp)) zone->push_back(*dp++); if (zone->empty()) dp = nullptr; } return dp; } const char* ParseSubSeconds(const char* dp, detail::femtoseconds* subseconds) { if (dp != nullptr) { std::int_fast64_t v = 0; std::int_fast64_t exp = 0; const char* const bp = dp; while (const char* cp = strchr(kDigits, *dp)) { int d = static_cast(cp - kDigits); if (d >= 10) break; if (exp < 15) { exp += 1; v *= 10; v += d; } ++dp; } if (dp != bp) { v *= kExp10[15 - exp]; *subseconds = detail::femtoseconds(v); } else { dp = nullptr; } } return dp; } // Parses a string into a std::tm using strptime(3). const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) { if (dp != nullptr) { dp = strptime(dp, fmt, tm); } return dp; } // Sets year, tm_mon and tm_mday given the year, week_num, and tm_wday, // and the day on which weeks are defined to start. Returns false if year // would need to move outside its bounds. bool FromWeek(int week_num, weekday week_start, year_t* year, std::tm* tm) { const civil_year y(*year % 400); civil_day cd = prev_weekday(y, week_start); // week 0 cd = next_weekday(cd - 1, FromTmWday(tm->tm_wday)) + (week_num * 7); if (const year_t shift = cd.year() - y.year()) { if (shift > 0) { if (*year > std::numeric_limits::max() - shift) return false; } else { if (*year < std::numeric_limits::min() - shift) return false; } *year += shift; } tm->tm_mon = cd.month() - 1; tm->tm_mday = cd.day(); return true; } } // namespace // Uses strptime(3) to parse the given input. Supports the same extended // format specifiers as format(), although %E#S and %E*S are treated // identically (and similarly for %E#f and %E*f). %Ez and %E*z also accept // the same inputs. %ET accepts either 'T' or 't'. // // The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are // handled internally so that we can normally avoid strptime() altogether // (which is particularly helpful when the native implementation is broken). // // The TZ/GNU %s extension is handled internally because strptime() has to // use localtime_r() to generate it, and that assumes the local time zone. // // We also handle the %z specifier to accommodate platforms that do not // support the tm_gmtoff extension to std::tm. %Z is parsed but ignored. bool parse(const std::string& format, const std::string& input, const time_zone& tz, time_point* sec, detail::femtoseconds* fs, std::string* err) { // The unparsed input. const char* data = input.c_str(); // NUL terminated // Skips leading whitespace. while (std::isspace(*data)) ++data; const year_t kyearmax = std::numeric_limits::max(); const year_t kyearmin = std::numeric_limits::min(); // Sets default values for unspecified fields. bool saw_year = false; year_t year = 1970; std::tm tm{}; tm.tm_year = 1970 - 1900; tm.tm_mon = 1 - 1; // Jan tm.tm_mday = 1; tm.tm_hour = 0; tm.tm_min = 0; tm.tm_sec = 0; tm.tm_wday = 4; // Thu tm.tm_yday = 0; tm.tm_isdst = 0; auto subseconds = detail::femtoseconds::zero(); bool saw_offset = false; int offset = 0; // No offset from passed tz. std::string zone = "UTC"; const char* fmt = format.c_str(); // NUL terminated bool twelve_hour = false; bool afternoon = false; int week_num = -1; weekday week_start = weekday::sunday; bool saw_percent_s = false; std::int_fast64_t percent_s = 0; // Steps through format, one specifier at a time. while (data != nullptr && *fmt != '\0') { if (std::isspace(*fmt)) { while (std::isspace(*data)) ++data; while (std::isspace(*++fmt)) continue; continue; } if (*fmt != '%') { if (*data == *fmt) { ++data; ++fmt; } else { data = nullptr; } continue; } const char* percent = fmt; if (*++fmt == '\0') { data = nullptr; continue; } switch (*fmt++) { case 'Y': // Symmetrically with FormatTime(), directly handing %Y avoids the // tm.tm_year overflow problem. However, tm.tm_year will still be // used by other specifiers like %D. data = ParseInt(data, 0, kyearmin, kyearmax, &year); if (data != nullptr) saw_year = true; continue; case 'm': data = ParseInt(data, 2, 1, 12, &tm.tm_mon); if (data != nullptr) tm.tm_mon -= 1; week_num = -1; continue; case 'd': case 'e': data = ParseInt(data, 2, 1, 31, &tm.tm_mday); week_num = -1; continue; case 'U': data = ParseInt(data, 0, 0, 53, &week_num); week_start = weekday::sunday; continue; case 'W': data = ParseInt(data, 0, 0, 53, &week_num); week_start = weekday::monday; continue; case 'u': data = ParseInt(data, 0, 1, 7, &tm.tm_wday); if (data != nullptr) tm.tm_wday %= 7; continue; case 'w': data = ParseInt(data, 0, 0, 6, &tm.tm_wday); continue; case 'H': data = ParseInt(data, 2, 0, 23, &tm.tm_hour); twelve_hour = false; continue; case 'M': data = ParseInt(data, 2, 0, 59, &tm.tm_min); continue; case 'S': data = ParseInt(data, 2, 0, 60, &tm.tm_sec); continue; case 'I': case 'l': case 'r': // probably uses %I twelve_hour = true; break; case 'R': // uses %H case 'T': // uses %H case 'c': // probably uses %H case 'X': // probably uses %H twelve_hour = false; break; case 'z': data = ParseOffset(data, "", &offset); if (data != nullptr) saw_offset = true; continue; case 'Z': // ignored; zone abbreviations are ambiguous data = ParseZone(data, &zone); continue; case 's': data = ParseInt(data, 0, std::numeric_limits::min(), std::numeric_limits::max(), &percent_s); if (data != nullptr) saw_percent_s = true; continue; case ':': if (fmt[0] == 'z' || (fmt[0] == ':' && (fmt[1] == 'z' || (fmt[1] == ':' && fmt[2] == 'z')))) { data = ParseOffset(data, ":", &offset); if (data != nullptr) saw_offset = true; fmt += (fmt[0] == 'z') ? 1 : (fmt[1] == 'z') ? 2 : 3; continue; } break; case '%': data = (*data == '%' ? data + 1 : nullptr); continue; case 'E': if (fmt[0] == 'T') { if (*data == 'T' || *data == 't') { ++data; ++fmt; } else { data = nullptr; } continue; } if (fmt[0] == 'z' || (fmt[0] == '*' && fmt[1] == 'z')) { data = ParseOffset(data, ":", &offset); if (data != nullptr) saw_offset = true; fmt += (fmt[0] == 'z') ? 1 : 2; continue; } if (fmt[0] == '*' && fmt[1] == 'S') { data = ParseInt(data, 2, 0, 60, &tm.tm_sec); if (data != nullptr && *data == '.') { data = ParseSubSeconds(data + 1, &subseconds); } fmt += 2; continue; } if (fmt[0] == '*' && fmt[1] == 'f') { if (data != nullptr && std::isdigit(*data)) { data = ParseSubSeconds(data, &subseconds); } fmt += 2; continue; } if (fmt[0] == '4' && fmt[1] == 'Y') { const char* bp = data; data = ParseInt(data, 4, year_t{-999}, year_t{9999}, &year); if (data != nullptr) { if (data - bp == 4) { saw_year = true; } else { data = nullptr; // stopped too soon } } fmt += 2; continue; } if (std::isdigit(*fmt)) { int n = 0; // value ignored if (const char* np = ParseInt(fmt, 0, 0, 1024, &n)) { if (*np == 'S') { data = ParseInt(data, 2, 0, 60, &tm.tm_sec); if (data != nullptr && *data == '.') { data = ParseSubSeconds(data + 1, &subseconds); } fmt = ++np; continue; } if (*np == 'f') { if (data != nullptr && std::isdigit(*data)) { data = ParseSubSeconds(data, &subseconds); } fmt = ++np; continue; } } } if (*fmt == 'c') twelve_hour = false; // probably uses %H if (*fmt == 'X') twelve_hour = false; // probably uses %H if (*fmt != '\0') ++fmt; break; case 'O': if (*fmt == 'H') twelve_hour = false; if (*fmt == 'I') twelve_hour = true; if (*fmt != '\0') ++fmt; break; } // Parses the current specifier. const char* orig_data = data; std::string spec(percent, static_cast(fmt - percent)); data = ParseTM(data, spec.c_str(), &tm); // If we successfully parsed %p we need to remember whether the result // was AM or PM so that we can adjust tm_hour before time_zone::lookup(). // So reparse the input with a known AM hour, and check if it is shifted // to a PM hour. if (spec == "%p" && data != nullptr) { std::string test_input = "1"; test_input.append(orig_data, static_cast(data - orig_data)); const char* test_data = test_input.c_str(); std::tm tmp{}; ParseTM(test_data, "%I%p", &tmp); afternoon = (tmp.tm_hour == 13); } } // Adjust a 12-hour tm_hour value if it should be in the afternoon. if (twelve_hour && afternoon && tm.tm_hour < 12) { tm.tm_hour += 12; } if (data == nullptr) { if (err != nullptr) *err = "Failed to parse input"; return false; } // Skip any remaining whitespace. while (std::isspace(*data)) ++data; // parse() must consume the entire input string. if (*data != '\0') { if (err != nullptr) *err = "Illegal trailing data in input string"; return false; } // If we saw %s then we ignore anything else and return that time. if (saw_percent_s) { *sec = FromUnixSeconds(percent_s); *fs = detail::femtoseconds::zero(); return true; } // If we saw %z, %Ez, or %E*z then we want to interpret the parsed fields // in UTC and then shift by that offset. Otherwise we want to interpret // the fields directly in the passed time_zone. time_zone ptz = saw_offset ? utc_time_zone() : tz; // Allows a leap second of 60 to normalize forward to the following ":00". if (tm.tm_sec == 60) { tm.tm_sec -= 1; offset -= 1; subseconds = detail::femtoseconds::zero(); } if (!saw_year) { year = year_t{tm.tm_year}; if (year > kyearmax - 1900) { // Platform-dependent, maybe unreachable. if (err != nullptr) *err = "Out-of-range year"; return false; } year += 1900; } // Compute year, tm.tm_mon and tm.tm_mday if we parsed a week number. if (week_num != -1) { if (!FromWeek(week_num, week_start, &year, &tm)) { if (err != nullptr) *err = "Out-of-range field"; return false; } } const int month = tm.tm_mon + 1; civil_second cs(year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); // parse() should not allow normalization. Due to the restricted field // ranges above (see ParseInt()), the only possibility is for days to roll // into months. That is, parsing "Sep 31" should not produce "Oct 1". if (cs.month() != month || cs.day() != tm.tm_mday) { if (err != nullptr) *err = "Out-of-range field"; return false; } // Accounts for the offset adjustment before converting to absolute time. if ((offset < 0 && cs > civil_second::max() + offset) || (offset > 0 && cs < civil_second::min() + offset)) { if (err != nullptr) *err = "Out-of-range field"; return false; } cs -= offset; const auto tp = ptz.lookup(cs).pre; // Checks for overflow/underflow and returns an error as necessary. if (tp == time_point::max()) { const auto al = ptz.lookup(time_point::max()); if (cs > al.cs) { if (err != nullptr) *err = "Out-of-range field"; return false; } } if (tp == time_point::min()) { const auto al = ptz.lookup(time_point::min()); if (cs < al.cs) { if (err != nullptr) *err = "Out-of-range field"; return false; } } *sec = tp; *fs = subseconds; return true; } } // namespace detail } // namespace cctz RcppCCTZ/src/Makevars.ucrt0000644000176200001440000000010114412065513015072 0ustar liggesusers PKG_CXXFLAGS = -I../inst/include -D_POSIX_THREAD_SAFE_FUNCTIONS RcppCCTZ/src/time_zone_if.cc0000644000176200001440000000252413520655751015423 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "time_zone_if.h" #include "time_zone_info.h" #include "time_zone_libc.h" namespace cctz { std::unique_ptr TimeZoneIf::Load(const std::string& name) { // Support "libc:localtime" and "libc:*" to access the legacy // localtime and UTC support respectively from the C library. if (name.compare(0, 5, "libc:") == 0) { return std::unique_ptr(new TimeZoneLibC(name.substr(5))); } // Otherwise use the "zoneinfo" implementation by default. std::unique_ptr tz(new TimeZoneInfo); if (!tz->Load(name)) tz.reset(); return std::unique_ptr(tz.release()); } // Defined out-of-line to avoid emitting a weak vtable in all TUs. TimeZoneIf::~TimeZoneIf() {} } // namespace cctz RcppCCTZ/src/time_zone_impl.h0000644000176200001440000000576313520655751015640 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef CCTZ_TIME_ZONE_IMPL_H_ #define CCTZ_TIME_ZONE_IMPL_H_ #include #include #include "cctz/civil_time.h" #include "cctz/time_zone.h" #include "time_zone_if.h" #include "time_zone_info.h" namespace cctz { // time_zone::Impl is the internal object referenced by a cctz::time_zone. class time_zone::Impl { public: // The UTC time zone. Also used for other time zones that fail to load. static time_zone UTC(); // Load a named time zone. Returns false if the name is invalid, or if // some other kind of error occurs. Note that loading "UTC" never fails. static bool LoadTimeZone(const std::string& name, time_zone* tz); // Clears the map of cached time zones. Primarily for use in benchmarks // that gauge the performance of loading/parsing the time-zone data. static void ClearTimeZoneMapTestOnly(); // The primary key is the time-zone ID (e.g., "America/New_York"). const std::string& Name() const { // TODO: It would nice if the zoneinfo data included the zone name. return name_; } // Breaks a time_point down to civil-time components in this time zone. time_zone::absolute_lookup BreakTime(const time_point& tp) const { return zone_->BreakTime(tp); } // Converts the civil-time components in this time zone into a time_point. // That is, the opposite of BreakTime(). The requested civil time may be // ambiguous or illegal due to a change of UTC offset. time_zone::civil_lookup MakeTime(const civil_second& cs) const { return zone_->MakeTime(cs); } // Finds the time of the next/previous offset change in this time zone. bool NextTransition(const time_point& tp, time_zone::civil_transition* trans) const { return zone_->NextTransition(tp, trans); } bool PrevTransition(const time_point& tp, time_zone::civil_transition* trans) const { return zone_->PrevTransition(tp, trans); } // Returns an implementation-defined version string for this time zone. std::string Version() const { return zone_->Version(); } // Returns an implementation-defined description of this time zone. std::string Description() const { return zone_->Description(); } private: explicit Impl(const std::string& name); static const Impl* UTCImpl(); const std::string name_; std::unique_ptr zone_; }; } // namespace cctz #endif // CCTZ_TIME_ZONE_IMPL_H_ RcppCCTZ/src/Makevars0000644000176200001440000000004214412065505014123 0ustar liggesusers PKG_CXXFLAGS = -I../inst/include RcppCCTZ/src/civil_time_detail.cc0000644000176200001440000000535213520655751016424 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "cctz/civil_time_detail.h" #include #include #include namespace cctz { namespace detail { // Output stream operators output a format matching YYYY-MM-DDThh:mm:ss, // while omitting fields inferior to the type's alignment. For example, // civil_day is formatted only as YYYY-MM-DD. std::ostream& operator<<(std::ostream& os, const civil_year& y) { std::stringstream ss; ss << y.year(); // No padding. return os << ss.str(); } std::ostream& operator<<(std::ostream& os, const civil_month& m) { std::stringstream ss; ss << civil_year(m) << '-'; ss << std::setfill('0') << std::setw(2) << m.month(); return os << ss.str(); } std::ostream& operator<<(std::ostream& os, const civil_day& d) { std::stringstream ss; ss << civil_month(d) << '-'; ss << std::setfill('0') << std::setw(2) << d.day(); return os << ss.str(); } std::ostream& operator<<(std::ostream& os, const civil_hour& h) { std::stringstream ss; ss << civil_day(h) << 'T'; ss << std::setfill('0') << std::setw(2) << h.hour(); return os << ss.str(); } std::ostream& operator<<(std::ostream& os, const civil_minute& m) { std::stringstream ss; ss << civil_hour(m) << ':'; ss << std::setfill('0') << std::setw(2) << m.minute(); return os << ss.str(); } std::ostream& operator<<(std::ostream& os, const civil_second& s) { std::stringstream ss; ss << civil_minute(s) << ':'; ss << std::setfill('0') << std::setw(2) << s.second(); return os << ss.str(); } //////////////////////////////////////////////////////////////////////// std::ostream& operator<<(std::ostream& os, weekday wd) { switch (wd) { case weekday::monday: return os << "Monday"; case weekday::tuesday: return os << "Tuesday"; case weekday::wednesday: return os << "Wednesday"; case weekday::thursday: return os << "Thursday"; case weekday::friday: return os << "Friday"; case weekday::saturday: return os << "Saturday"; case weekday::sunday: return os << "Sunday"; } return os; // Should never get here, but -Wreturn-type may warn without this. } } // namespace detail } // namespace cctz RcppCCTZ/src/time_zone_posix.h0000644000176200001440000000733013520655751016031 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Parsing of a POSIX zone spec as described in the TZ part of section 8.3 in // http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html. // // The current POSIX spec for America/Los_Angeles is "PST8PDT,M3.2.0,M11.1.0", // which would be broken down as ... // // PosixTimeZone { // std_abbr = "PST" // std_offset = -28800 // dst_abbr = "PDT" // dst_offset = -25200 // dst_start = PosixTransition { // date { // m { // month = 3 // week = 2 // weekday = 0 // } // } // time { // offset = 7200 // } // } // dst_end = PosixTransition { // date { // m { // month = 11 // week = 1 // weekday = 0 // } // } // time { // offset = 7200 // } // } // } #ifndef CCTZ_TIME_ZONE_POSIX_H_ #define CCTZ_TIME_ZONE_POSIX_H_ #include #include namespace cctz { // The date/time of the transition. The date is specified as either: // (J) the Nth day of the year (1 <= N <= 365), excluding leap days, or // (N) the Nth day of the year (0 <= N <= 365), including leap days, or // (M) the Nth weekday of a month (e.g., the 2nd Sunday in March). // The time, specified as a day offset, identifies the particular moment // of the transition, and may be negative or >= 24h, and in which case // it would take us to another day, and perhaps week, or even month. struct PosixTransition { enum DateFormat { J, N, M }; struct Date { struct NonLeapDay { std::int_fast16_t day; // day of non-leap year [1:365] }; struct Day { std::int_fast16_t day; // day of year [0:365] }; struct MonthWeekWeekday { std::int_fast8_t month; // month of year [1:12] std::int_fast8_t week; // week of month [1:5] (5==last) std::int_fast8_t weekday; // 0==Sun, ..., 6=Sat }; DateFormat fmt; union { NonLeapDay j; Day n; MonthWeekWeekday m; }; }; struct Time { std::int_fast32_t offset; // seconds before/after 00:00:00 }; Date date; Time time; }; // The entirety of a POSIX-string specified time-zone rule. The standard // abbreviation and offset are always given. If the time zone includes // daylight saving, then the daylight abbrevation is non-empty and the // remaining fields are also valid. Note that the start/end transitions // are not ordered---in the southern hemisphere the transition to end // daylight time occurs first in any particular year. struct PosixTimeZone { std::string std_abbr; std::int_fast32_t std_offset; std::string dst_abbr; std::int_fast32_t dst_offset; PosixTransition dst_start; PosixTransition dst_end; }; // Breaks down a POSIX time-zone specification into its constituent pieces, // filling in any missing values (DST offset, or start/end transition times) // with the standard-defined defaults. Returns false if the specification // could not be parsed (although some fields of *res may have been altered). bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res); } // namespace cctz #endif // CCTZ_TIME_ZONE_POSIX_H_ RcppCCTZ/src/examples.cpp0000644000176200001440000001011314436155620014755 0ustar liggesusers// -*- mode: C++; c-indent-level: 4; c-basic-offset: 4; indent-tabs-mode: nil; -*- #include #include "cctz/civil_time.h" #include "cctz/time_zone.h" // from examples/classic.cc // std::string Format(const std::string& fmt, const std::tm& tm) { char buf[100]; std::strftime(buf, sizeof(buf), fmt.c_str(), &tm); return buf; } // [[Rcpp::export]] void example0() { const std::time_t now = std::time(nullptr); std::tm tm_utc; gmtime_r(&now, &tm_utc); Rcpp::Rcout << Format("UTC: %Y-%m-%d %H:%M:%S \n", tm_utc);; std::tm tm_local; localtime_r(&now, &tm_local); Rcpp::Rcout << Format("Local: %Y-%m-%d %H:%M:%S \n", tm_local); } // from examples/hello.cc // // [[Rcpp::export]] Rcpp::CharacterVector helloMoon(bool verbose=false) { cctz::time_zone syd, nyc; cctz::load_time_zone("Australia/Sydney", &syd); cctz::load_time_zone("America/New_York", &nyc); // Neil Armstrong first walks on the moon const auto tp = cctz::convert(cctz::civil_second(1969, 7, 20, 22, 56, 0), nyc); const std::string s1 = cctz::format("%Y-%m-%d %H:%M:%S %z", tp, nyc); if (verbose) Rcpp::Rcout << s1 << "\n"; // That time in Sydney const std::string s2 = cctz::format("%Y-%m-%d %H:%M:%S %z", tp, syd); if (verbose) Rcpp::Rcout << s2 << "\n"; return Rcpp::CharacterVector::create(Rcpp::Named("New_York") = s1, Rcpp::Named("Sydney") = s2); } // from examples/example1.cc to examples/example4.cc // [[Rcpp::export]] void example1() { cctz::time_zone lax; load_time_zone("America/Los_Angeles", &lax); // Time Programming Fundamentals @cppcon const auto tp = cctz::convert(cctz::civil_second(2015, 9, 22, 9, 0, 0), lax); cctz::time_zone nyc; load_time_zone("America/New_York", &nyc); Rcpp::Rcout << cctz::format("Talk starts at %H:%M:%S %z (%Z)\n", tp, lax); Rcpp::Rcout << cctz::format("Talk starts at %H:%M:%S %z (%Z)\n", tp, nyc); } // [[Rcpp::export]] int example2() { const std::string civil_string = "2015-09-22 09:35:00"; cctz::time_zone lax; load_time_zone("America/Los_Angeles", &lax); std::chrono::system_clock::time_point tp; const bool ok = cctz::parse("%Y-%m-%d %H:%M:%S", civil_string, lax, &tp); if (!ok) return -1; const auto now = std::chrono::system_clock::now(); const std::string s = now > tp ? "running long!" : "on time!"; Rcpp::Rcout << "Talk " << s << "\n"; return 0; } // [[Rcpp::export]] void example3() { cctz::time_zone lax; load_time_zone("America/Los_Angeles", &lax); const auto now = std::chrono::system_clock::now(); const cctz::civil_second cs = cctz::convert(now, lax); // First day of month, 6 months from now. const auto then = cctz::convert(cctz::civil_month(cs) + 6, lax); Rcpp::Rcout << cctz::format("Now: %Y-%m-%d %H:%M:%S %z\n", now, lax); Rcpp::Rcout << cctz::format("6mo: %Y-%m-%d %H:%M:%S %z\n", then, lax); } template cctz::time_point FloorDay(cctz::time_point tp, cctz::time_zone tz) { return cctz::convert(cctz::civil_day(cctz::convert(tp, tz)), tz); } // [[Rcpp::export]] void example4() { cctz::time_zone lax; load_time_zone("America/Los_Angeles", &lax); const auto now = std::chrono::system_clock::now(); const auto day = FloorDay(now, lax); Rcpp::Rcout << cctz::format("Now: %Y-%m-%d %H:%M:%S %z\n", now, lax); Rcpp::Rcout << cctz::format("Day: %Y-%m-%d %H:%M:%S %z\n", day, lax); } // from time_zone_format_test.cc // // [[Rcpp::export]] void exampleFormat() { const cctz::time_zone tz = cctz::utc_time_zone(); cctz::time_point tp = std::chrono::system_clock::from_time_t(0); tp += std::chrono::hours(3) + std::chrono::minutes(4) + std::chrono::seconds(5); tp += std::chrono::milliseconds(6) + std::chrono::microseconds(7) + std::chrono::nanoseconds(8); std::string txt = cctz::format("%H:%M:%E15S", tp, tz); Rcpp::Rcout << "15 digit precision on subsecond time: " << txt << std::endl; } RcppCCTZ/src/time_tool.cc0000644000176200001440000003701214271014541014735 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // A command-line tool for exercising the CCTZ library. // RcppCCTZ changes: Add Rcpp header, change std::{cout,cerr} to Rcpp::{Rcout,Rcerr} #include #include #include #include #include #include #include #include #include #include #include #include #include "cctz/civil_time.h" #include "cctz/time_zone.h" #include // Pulls in the aliases from cctz for brevity. template using time_point = cctz::time_point; using seconds = cctz::seconds; // parse() specifiers for command-line time arguments. const char* const kFormats[] = { "%Y %m %d %H %M %E*S", "%Y - %m - %d %ET %H : %M : %E*S", "%Y - %m - %d %H : %M : %E*S", "%Y - %m - %d %ET %H : %M", "%Y - %m - %d %H : %M", "%Y - %m - %d", "%a %b %d %H : %M : %E*S %Z %Y", "%a %e %b %Y %H : %M : %E*S", "%a %b %e %Y %H : %M : %E*S", "%e %b %Y %H : %M : %E*S", "%b %e %Y %H : %M : %E*S", "%a %e %b %Y %H : %M", "%a %b %e %Y %H : %M", "%e %b %Y %H : %M", "%b %e %Y %H : %M", "%a %e %b %Y", "%a %b %e %Y", "%e %b %Y", "%b %e %Y", nullptr }; bool ParseTimeSpec(const std::string& args, time_point* when) { const cctz::time_zone ignored{}; for (const char* const* fmt = kFormats; *fmt != nullptr; ++fmt) { const std::string format = std::string(*fmt) + " %E*z"; time_point tp; if (cctz::parse(format, args, ignored, &tp)) { *when = tp; return true; } } return false; } bool ParseCivilSpec(const std::string& args, cctz::civil_second* when) { const cctz::time_zone utc = cctz::utc_time_zone(); for (const char* const* fmt = kFormats; *fmt != nullptr; ++fmt) { time_point tp; if (cctz::parse(*fmt, args, utc, &tp)) { *when = cctz::convert(tp, utc); return true; } } return false; } const char* WeekDayName(cctz::weekday wd) { switch (wd) { case cctz::weekday::monday: return "Mon"; case cctz::weekday::tuesday: return "Tue"; case cctz::weekday::wednesday: return "Wed"; case cctz::weekday::thursday: return "Thu"; case cctz::weekday::friday: return "Fri"; case cctz::weekday::saturday: return "Sat"; case cctz::weekday::sunday: return "Sun"; } return "XXX"; } std::string FormatTimeInZone(const std::string& fmt, time_point when, cctz::time_zone zone) { std::ostringstream oss; oss << std::setw(36) << std::left << cctz::format(fmt, when, zone); cctz::time_zone::absolute_lookup al = zone.lookup(when); oss << " [wd=" << WeekDayName(cctz::get_weekday(al.cs)) << " yd=" << std::setw(3) << std::setfill('0') << std::right << cctz::get_yearday(al.cs) << " dst=" << (al.is_dst ? 'T' : 'F') << " off=" << std::showpos << al.offset << std::noshowpos << "]"; return oss.str(); } void ZoneInfo(const std::string& label, cctz::time_zone tz) { std::string version = tz.version(); if (version.empty()) version = ""; Rcpp::Rcout << label << tz.name() << " [ver=" << version << " " << tz.description() << "]\n"; } void InstantInfo(const std::string& label, const std::string& fmt, time_point when, cctz::time_zone zone) { const cctz::time_zone loc = cctz::local_time_zone(); // might == zone const cctz::time_zone utc = cctz::utc_time_zone(); // might == zone const std::string time_label = "time_t"; const std::string utc_label = "UTC"; const std::string loc_label = "local"; const std::string zone_label = "in-tz"; // perhaps zone.name()? int width = 2 + static_cast( std::max(std::max(time_label.size(), utc_label.size()), std::max(loc_label.size(), zone_label.size()))); Rcpp::Rcout << label << " {\n"; Rcpp::Rcout << std::setw(width) << std::right << time_label << ": "; Rcpp::Rcout << std::setw(10) << cctz::format("%s", when, utc); Rcpp::Rcout << "\n"; Rcpp::Rcout << std::setw(width) << std::right << utc_label << ": "; Rcpp::Rcout << FormatTimeInZone(fmt, when, utc) << "\n"; Rcpp::Rcout << std::setw(width) << std::right << loc_label << ": "; Rcpp::Rcout << FormatTimeInZone(fmt, when, loc) << "\n"; Rcpp::Rcout << std::setw(width) << std::right << zone_label << ": "; Rcpp::Rcout << FormatTimeInZone(fmt, when, zone) << "\n"; Rcpp::Rcout << "}\n"; } // Report everything we know about a cctz::civil_second (YMDHMS). void CivilInfo(const std::string& fmt, const cctz::civil_second cs, cctz::time_zone zone) { ZoneInfo("tz: ", zone); cctz::time_zone::civil_lookup cl = zone.lookup(cs); switch (cl.kind) { case cctz::time_zone::civil_lookup::UNIQUE: { Rcpp::Rcout << "kind: UNIQUE\n"; InstantInfo("when", fmt, cl.pre, zone); break; } case cctz::time_zone::civil_lookup::SKIPPED: { Rcpp::Rcout << "kind: SKIPPED\n"; InstantInfo("post", fmt, cl.post, zone); // might == trans-1 InstantInfo("trans-1", fmt, cl.trans - seconds(1), zone); InstantInfo("trans", fmt, cl.trans, zone); InstantInfo("pre", fmt, cl.pre, zone); // might == trans break; } case cctz::time_zone::civil_lookup::REPEATED: { Rcpp::Rcout << "kind: REPEATED\n"; InstantInfo("pre", fmt, cl.pre, zone); // might == trans-1 InstantInfo("trans-1", fmt, cl.trans - seconds(1), zone); InstantInfo("trans", fmt, cl.trans, zone); InstantInfo("post", fmt, cl.post, zone); // might == trans break; } } } // Report everything we know about a time_point. void TimeInfo(const std::string& fmt, time_point when, cctz::time_zone zone) { ZoneInfo("tz: ", zone); Rcpp::Rcout << "kind: UNIQUE\n"; InstantInfo("when", fmt, when, zone); } // Report everything we know about a time_zone. void ZoneDump(bool zdump, const std::string& fmt, cctz::time_zone zone, cctz::year_t lo_year, cctz::year_t hi_year) { const cctz::time_zone utc = cctz::utc_time_zone(); if (zdump) { Rcpp::Rcout << zone.name() << " " << std::numeric_limits::min() << " = NULL\n"; Rcpp::Rcout << zone.name() << " " << std::numeric_limits::min() + 86400 << " = NULL\n"; } else { ZoneInfo("", zone); } auto tp = cctz::convert(cctz::civil_second(lo_year, 1, 1, 0, 0, -1), zone); cctz::time_zone::civil_transition trans; while (zone.next_transition(tp, &trans)) { if (trans.from.year() >= hi_year && trans.to.year() >= hi_year) break; tp = zone.lookup(trans.to).trans; if (!zdump) Rcpp::Rcout << "\n"; for (int count_down = 1; count_down >= 0; --count_down) { auto ttp = tp - seconds(count_down); if (zdump) { Rcpp::Rcout << zone.name() << " " << cctz::format("%c UT", ttp, utc) << " = " << cctz::format("%c %Z", ttp, zone); } else { Rcpp::Rcout << std::setw(10) << std::chrono::system_clock::to_time_t(ttp); Rcpp::Rcout << " = " << cctz::format(fmt, ttp, utc); Rcpp::Rcout << " = " << cctz::format(fmt, ttp, zone); } auto al = zone.lookup(ttp); if (zdump) { Rcpp::Rcout << " isdst=" << (al.is_dst ? '1' : '0') << " gmtoff=" << al.offset << "\n"; } else { const char* wd = WeekDayName(get_weekday(al.cs)); Rcpp::Rcout << " [wd=" << wd << " dst=" << (al.is_dst ? 'T' : 'F') << " off=" << al.offset << "]\n"; } } } if (zdump) { Rcpp::Rcout << zone.name() << " " << std::numeric_limits::max() - 86400 << " = NULL\n"; Rcpp::Rcout << zone.name() << " " << std::numeric_limits::max() << " = NULL\n"; } } const char* Basename(const char* p) { if (const char* b = std::strrchr(p, '/')) return ++b; return p; } // std::regex doesn't work before gcc 4.9. bool LooksLikeNegOffset(const char* s) { if (s[0] == '-' && std::isdigit(s[1]) && std::isdigit(s[2])) { int i = (s[3] == ':') ? 4 : 3; if (std::isdigit(s[i]) && std::isdigit(s[i + 1])) { return s[i + 2] == '\0'; } } return false; } std::vector StrSplit(char sep, const std::string& s) { std::vector v; // An empty string value corresponds to an empty vector, not a vector // with a single, empty string. if (!s.empty()) { std::string::size_type pos = 0; for (;;) { std::string::size_type spos = s.find(sep, pos); if (spos == std::string::npos) break; v.push_back(s.substr(pos, spos - pos)); pos = spos + 1; } v.push_back(s.substr(pos)); } return v; } // Parses [,]. bool ParseYearRange(bool zdump, const std::string& args, cctz::year_t* lo_year, cctz::year_t* hi_year) { std::size_t pos = 0; std::size_t digit_pos = pos + (args[pos] == '-' ? 1 : 0); if (digit_pos >= args.size() || !std::isdigit(args[digit_pos])) { return false; } const cctz::year_t first = std::stoll(args, &pos); if (pos == args.size()) { *lo_year = (zdump ? -292277022656 : first); *hi_year = (zdump ? first : first + 1); return true; } if (args[pos] != ' ' || ++pos == args.size()) { // Any comma was already converted to a space. return false; } digit_pos = pos + (args[pos] == '-' ? 1 : 0); if (digit_pos >= args.size() || !std::isdigit(args[digit_pos])) { return false; } const std::string rem = args.substr(pos); const cctz::year_t second = std::stoll(rem, &pos); if (pos == rem.size()) { *lo_year = first; *hi_year = (zdump ? second : second + 1); return true; } return false; } int main(int argc, const char** argv) { const char* argv0 = (argc > 0) ? (argc--, *argv++) : (argc = 0, "time_tool"); const std::string prog = Basename(argv0); // Escape arguments that look like negative offsets so that they // don't look like flags. std::vector eargs; for (int i = 0; i < argc; ++i) { if (std::strcmp(argv[i], "--") == 0) break; if (LooksLikeNegOffset(argv[i])) { eargs.push_back(" "); // space will later be ignorned eargs.back().append(argv[i]); argv[i] = eargs.back().c_str(); } } // Determine the time zone and other options. std::string zones = "localtime"; std::string fmt = "%Y-%m-%d %H:%M:%S %E*z (%Z)"; bool zone_dump = (prog == "zone_dump"); bool zdump = false; // Use zdump(8) format. int optind = 0; int opterr = 0; for (; optind < argc && opterr == 0; ++optind) { const char* opt = argv[optind]; if (*opt++ != '-') break; if (*opt != '-') { // short options while (char c = *opt++) { if (c == 'z') { if (*opt != '\0') { zones = opt; break; } if (optind + 1 == argc) { Rcpp::Rcerr << argv0 << ": option requires an argument -- 'z'\n"; ++opterr; break; } zones = argv[++optind]; } else if (c == 'f') { if (*opt != '\0') { fmt = opt; break; } if (optind + 1 == argc) { Rcpp::Rcerr << argv0 << ": option requires an argument -- 'f'\n"; ++opterr; break; } fmt = argv[++optind]; } else if (c == 'D') { zdump = true; } else if (c == 'd') { zone_dump = true; } else { Rcpp::Rcerr << argv0 << ": invalid option -- '" << c << "'\n"; ++opterr; break; } } } else { // long options if (*++opt == '\0') { // "--" ++optind; break; } if (std::strcmp(opt, "tz") == 0) { if (optind + 1 == argc) { Rcpp::Rcerr << argv0 << ": option '--tz' requires an argument\n"; ++opterr; } else { zones = argv[++optind]; } } else if (std::strncmp(opt, "tz=", 3) == 0) { zones = opt + 3; } else if (std::strcmp(opt, "fmt") == 0) { if (optind + 1 == argc) { Rcpp::Rcerr << argv0 << ": option '--fmt' requires an argument\n"; ++opterr; } else { fmt = argv[++optind]; } } else if (std::strncmp(opt, "fmt=", 4) == 0) { fmt = opt + 4; } else if (std::strcmp(opt, "zdump") == 0) { zdump = true; } else if (std::strcmp(opt, "zone_dump") == 0) { zone_dump = true; } else { Rcpp::Rcerr << argv0 << ": unrecognized option '--" << opt << "'\n"; ++opterr; } } } if (opterr != 0) { Rcpp::Rcerr << "Usage: " << prog << " [--tz=[,...]] [--fmt=]"; if (prog == "zone_dump") { Rcpp::Rcerr << " [[,]|]\n"; Rcpp::Rcerr << " Default years are last year and next year," << " respectively.\n"; } else { Rcpp::Rcerr << " []\n"; } Rcpp::Rcerr << " Default is 'now'.\n"; return 1; } std::string args; for (int i = optind; i < argc; ++i) { if (i != optind) args += " "; args += argv[i]; } std::replace(args.begin(), args.end(), ',', ' '); std::replace(args.begin(), args.end(), '/', '-'); // Determine the time point. time_point tp = std::chrono::time_point_cast(std::chrono::system_clock::now()); bool have_time = ParseTimeSpec(args, &tp); if (!have_time && !args.empty()) { std::string spec = args.substr((args[0] == '@') ? 1 : 0); if ((spec.size() > 0 && std::isdigit(spec[0])) || (spec.size() > 1 && spec[0] == '-' && std::isdigit(spec[1]))) { std::size_t end; const time_t t = std::stoll(spec, &end); if (end == spec.size()) { tp = std::chrono::time_point_cast( std::chrono::system_clock::from_time_t(t)); have_time = true; } } } std::string leader = ""; for (const std::string& tz : StrSplit(',', zones)) { Rcpp::Rcout << leader; cctz::time_zone zone; if (tz == "localtime") { zone = cctz::local_time_zone(); } else if (!cctz::load_time_zone(tz, &zone)) { Rcpp::Rcerr << tz << ": Unrecognized time zone\n"; return 1; } cctz::civil_second when = cctz::convert(tp, zone); bool have_civil = !have_time && ParseCivilSpec(args, &when); if (zone_dump || zdump) { cctz::year_t lo_year = (zdump ? -292277026596 : when.year()); cctz::year_t hi_year = (zdump ? 292277026596 : when.year() + 1); if (!args.empty() && !ParseYearRange(zdump, args, &lo_year, &hi_year)) { if (!have_time && !have_civil) { Rcpp::Rcerr << args << ": Malformed year range\n"; return 1; } } ZoneDump(zdump, fmt, zone, lo_year, hi_year); leader = "---\n"; } else { if (!have_civil && !have_time && !args.empty()) { Rcpp::Rcerr << args << ": Malformed time spec\n"; return 1; } if (have_civil) { CivilInfo(fmt, when, zone); } else { TimeInfo(fmt, tp, zone); } leader = "\n"; } } } RcppCCTZ/src/time_zone_fixed.cc0000644000176200001440000001025214271014541016107 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "time_zone_fixed.h" #include #include #include #include #include namespace cctz { namespace { // The prefix used for the internal names of fixed-offset zones. const char kFixedZonePrefix[] = "Fixed/UTC"; const char kDigits[] = "0123456789"; char* Format02d(char* p, int v) { *p++ = kDigits[(v / 10) % 10]; *p++ = kDigits[v % 10]; return p; } int Parse02d(const char* p) { if (const char* ap = std::strchr(kDigits, *p)) { int v = static_cast(ap - kDigits); if (const char* bp = std::strchr(kDigits, *++p)) { return (v * 10) + static_cast(bp - kDigits); } } return -1; } } // namespace bool FixedOffsetFromName(const std::string& name, seconds* offset) { if (name == "UTC" || name == "UTC0") { *offset = seconds::zero(); return true; } const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1; const char* const ep = kFixedZonePrefix + prefix_len; if (name.size() != prefix_len + 9) // +99:99:99 return false; if (!std::equal(kFixedZonePrefix, ep, name.begin())) return false; const char* np = name.data() + prefix_len; if (np[0] != '+' && np[0] != '-') return false; if (np[3] != ':' || np[6] != ':') // see note below about large offsets return false; int hours = Parse02d(np + 1); if (hours == -1) return false; int mins = Parse02d(np + 4); if (mins == -1) return false; int secs = Parse02d(np + 7); if (secs == -1) return false; secs += ((hours * 60) + mins) * 60; if (secs > 24 * 60 * 60) return false; // outside supported offset range *offset = seconds(secs * (np[0] == '-' ? -1 : 1)); // "-" means west return true; } std::string FixedOffsetToName(const seconds& offset) { if (offset == seconds::zero()) return "UTC"; if (offset < std::chrono::hours(-24) || offset > std::chrono::hours(24)) { // We don't support fixed-offset zones more than 24 hours // away from UTC to avoid complications in rendering such // offsets and to (somewhat) limit the total number of zones. return "UTC"; } int offset_seconds = static_cast(offset.count()); const char sign = (offset_seconds < 0 ? '-' : '+'); int offset_minutes = offset_seconds / 60; offset_seconds %= 60; if (sign == '-') { if (offset_seconds > 0) { offset_seconds -= 60; offset_minutes += 1; } offset_seconds = -offset_seconds; offset_minutes = -offset_minutes; } int offset_hours = offset_minutes / 60; offset_minutes %= 60; const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1; char buf[prefix_len + sizeof("-24:00:00")]; char* ep = std::copy(kFixedZonePrefix, kFixedZonePrefix + prefix_len, buf); *ep++ = sign; ep = Format02d(ep, offset_hours); *ep++ = ':'; ep = Format02d(ep, offset_minutes); *ep++ = ':'; ep = Format02d(ep, offset_seconds); *ep++ = '\0'; assert(ep == buf + sizeof(buf)); return buf; } std::string FixedOffsetToAbbr(const seconds& offset) { std::string abbr = FixedOffsetToName(offset); const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1; if (abbr.size() == prefix_len + 9) { // +99:99:99 abbr.erase(0, prefix_len); // +99:99:99 abbr.erase(6, 1); // +99:9999 abbr.erase(3, 1); // +999999 if (abbr[5] == '0' && abbr[6] == '0') { // +999900 abbr.erase(5, 2); // +9999 if (abbr[3] == '0' && abbr[4] == '0') { // +9900 abbr.erase(3, 2); // +99 } } } return abbr; } } // namespace cctz RcppCCTZ/src/time_zone_info.h0000644000176200001440000001214314271014541015606 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef CCTZ_TIME_ZONE_INFO_H_ #define CCTZ_TIME_ZONE_INFO_H_ #include #include #include #include #include #include "cctz/civil_time.h" #include "cctz/time_zone.h" #include "cctz/zone_info_source.h" #include "time_zone_if.h" #include "tzfile.h" namespace cctz { // A transition to a new UTC offset. struct Transition { std::int_least64_t unix_time; // the instant of this transition std::uint_least8_t type_index; // index of the transition type civil_second civil_sec; // local civil time of transition civil_second prev_civil_sec; // local civil time one second earlier struct ByUnixTime { inline bool operator()(const Transition& lhs, const Transition& rhs) const { return lhs.unix_time < rhs.unix_time; } }; struct ByCivilTime { inline bool operator()(const Transition& lhs, const Transition& rhs) const { return lhs.civil_sec < rhs.civil_sec; } }; }; // The characteristics of a particular transition. struct TransitionType { std::int_least32_t utc_offset; // the new prevailing UTC offset civil_second civil_max; // max convertible civil time for offset civil_second civil_min; // min convertible civil time for offset bool is_dst; // did we move into daylight-saving time std::uint_least8_t abbr_index; // index of the new abbreviation }; // A time zone backed by the IANA Time Zone Database (zoneinfo). class TimeZoneInfo : public TimeZoneIf { public: TimeZoneInfo() = default; TimeZoneInfo(const TimeZoneInfo&) = delete; TimeZoneInfo& operator=(const TimeZoneInfo&) = delete; // Loads the zoneinfo for the given name, returning true if successful. bool Load(const std::string& name); // TimeZoneIf implementations. time_zone::absolute_lookup BreakTime( const time_point& tp) const override; time_zone::civil_lookup MakeTime( const civil_second& cs) const override; bool NextTransition(const time_point& tp, time_zone::civil_transition* trans) const override; bool PrevTransition(const time_point& tp, time_zone::civil_transition* trans) const override; std::string Version() const override; std::string Description() const override; private: struct Header { // counts of: std::size_t timecnt; // transition times std::size_t typecnt; // transition types std::size_t charcnt; // zone abbreviation characters std::size_t leapcnt; // leap seconds (we expect none) std::size_t ttisstdcnt; // UTC/local indicators (unused) std::size_t ttisutcnt; // standard/wall indicators (unused) bool Build(const tzhead& tzh); std::size_t DataLength(std::size_t time_len) const; }; bool GetTransitionType(std::int_fast32_t utc_offset, bool is_dst, const std::string& abbr, std::uint_least8_t* index); bool EquivTransitions(std::uint_fast8_t tt1_index, std::uint_fast8_t tt2_index) const; bool ExtendTransitions(); bool ResetToBuiltinUTC(const seconds& offset); bool Load(ZoneInfoSource* zip); // Helpers for BreakTime() and MakeTime(). time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time, const TransitionType& tt) const; time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time, const Transition& tr) const; time_zone::civil_lookup TimeLocal(const civil_second& cs, year_t c4_shift) const; std::vector transitions_; // ordered by unix_time and civil_sec std::vector transition_types_; // distinct transition types std::uint_fast8_t default_transition_type_; // for before first transition std::string abbreviations_; // all the NUL-terminated abbreviations std::string version_; // the tzdata version if available std::string future_spec_; // for after the last zic transition bool extended_; // future_spec_ was used to generate transitions year_t last_year_; // the final year of the generated transitions // We remember the transitions found during the last BreakTime() and // MakeTime() calls. If the next request is for the same transition we // will avoid re-searching. mutable std::atomic local_time_hint_ = {}; // BreakTime() hint mutable std::atomic time_local_hint_ = {}; // MakeTime() hint }; } // namespace cctz #endif // CCTZ_TIME_ZONE_INFO_H_ RcppCCTZ/src/utilities.cpp0000644000176200001440000004050314331026524015152 0ustar liggesusers #include #include "cctz/civil_time.h" #include "cctz/time_zone.h" #include //std::transform namespace sc = std::chrono; // shorthand double tzDiffAtomic(const cctz::time_zone& tz1, const cctz::time_zone& tz2, const Rcpp::Datetime& dt, bool verbose); //' Difference between two given timezones at a specified date. //' //' Time zone offsets vary by date, and this helper function computes //' the difference (in hours) between two time zones for a given date time. //' //' @title Return difference between two time zones at a given date. //' @param tzfrom The first time zone as a character vector. //' @param tzto The second time zone as a character vector. //' @param dt A Datetime object specifying when the difference is to be computed. //' @param verbose A boolean toggle indicating whether more verbose operations //' are desired, default is \code{FALSE}. //' @return A numeric value with the difference (in hours) between the first and //' second time zone at the given date //' @author Dirk Eddelbuettel //' @examples //' \dontrun{ //' # simple call: difference now //' tzDiff("America/New_York", "Europe/London", Sys.time()) //' # tabulate difference for every week of the year //' table(sapply(0:52, function(d) tzDiff("America/New_York", "Europe/London", //' as.POSIXct(as.Date("2016-01-01") + d*7)))) //' } // [[Rcpp::export]] Rcpp::NumericVector tzDiff(const std::string tzfrom, const std::string tzto, const Rcpp::NumericVector& dt, bool verbose=false) { cctz::time_zone tz1, tz2; if (!cctz::load_time_zone(tzfrom, &tz1)) Rcpp::stop("Bad 'from' timezone"); if (!cctz::load_time_zone(tzto, &tz2)) Rcpp::stop("Bad 'to' timezone"); Rcpp::NumericVector res; if (dt.inherits("POSIXct")) { res = Rcpp::NumericVector(dt.size()); std::transform(dt.begin(), dt.end(), res.begin(), [&tz1, &tz2, verbose](double dtval){ Rcpp::Datetime dtt(dtval); return tzDiffAtomic(tz1, tz2, dtt, verbose); }); } else Rcpp::stop("Unhandled date class"); return res; } double tzDiffAtomic(const cctz::time_zone& tz1, const cctz::time_zone& tz2, const Rcpp::Datetime& dt, bool verbose) { const auto tp1 = cctz::convert(cctz::civil_second(dt.getYear(), dt.getMonth(), dt.getDay(), dt.getHours(), dt.getMinutes(), dt.getSeconds()), tz1); if (verbose) Rcpp::Rcout << cctz::format("%Y-%m-%d %H:%M:%S %z", tp1, tz1) << std::endl; const auto tp2 = cctz::convert(cctz::civil_second(dt.getYear(), dt.getMonth(), dt.getDay(), dt.getHours(), dt.getMinutes(), dt.getSeconds()), tz2); if (verbose) Rcpp::Rcout << cctz::format("%Y-%m-%d %H:%M:%S %z", tp2, tz2) << std::endl; sc::hours d = sc::duration_cast(tp1-tp2); if (verbose) Rcpp::Rcout << "Difference: " << d.count() << std::endl; return d.count(); } //' Change from one given timezone to another. //' //' Time zone offsets vary by date, and this helper function converts //' a Datetime object from one given timezone to another. //' //' @title Shift datetime object from one timezone to another //' @param dtv A DatetimeVector object specifying when the difference is to be computed. //' @param tzfrom The first time zone as a character vector. //' @param tzto The second time zone as a character vector. //' @param verbose A boolean toggle indicating whether more verbose operations //' are desired, default is \code{FALSE}. //' @return A DatetimeVector object with the given (civil time) determined by the //' incoming object (and its timezone) shifted to the target timezone. //' @author Dirk Eddelbuettel //' @examples //' \dontrun{ //' toTz(Sys.time(), "America/New_York", "Europe/London") //' # this redoes the 'Armstrong on the moon in NYC and Sydney' example //' toTz(ISOdatetime(1969,7,20,22,56,0,tz="UTC"), "America/New_York", "Australia/Sydney", verbose=TRUE) //' # we can also explicitly format for Sydney time //' format(toTz(ISOdatetime(1969,7,20,22,56,0,tz="UTC"), //' "America/New_York", "Australia/Sydney", verbose=TRUE), //' tz="Australia/Sydney") //' } // [[Rcpp::export]] Rcpp::DatetimeVector toTz(Rcpp::DatetimeVector dtv, const std::string tzfrom, const std::string tzto, bool verbose=false) { size_t n = dtv.size(); Rcpp::DatetimeVector rsv(n, tzto.c_str()); for (size_t i=0; i ntp = cctz::convert(ct, tz2); // time since epoch, with fractional seconds added back in double newdt = ntp.time_since_epoch().count() + remainder; Rcpp::Datetime res = Rcpp::Datetime(newdt); rsv[i] = res; } return rsv; } //' Format a Datetime vector //' //' An alternative to \code{format.POSIXct} based on the CCTZ library. The //' \code{formatDouble} variant uses two vectors for seconds since the epoch //' and fractional nanoseconds, respectively, to provide fuller resolution. //' //' @title Format a Datetime vector as a string vector //' @param dtv A Datetime vector object to be formatted //' @param fmt A string with the format, which is based on \code{strftime} with some //' extensions; see the CCTZ documentation for details. //' @param lcltzstr The local timezone object for creation the CCTZ timepoint //' @param tgttzstr The target timezone for the desired format //' @return A string vector with the requested format of the datetime objects //' @section Note: //' Windows is now supported via the \code{g++-4.9} compiler, but note //' that it provides an \emph{incomplete} C++11 library. This means we had //' to port a time parsing routine, and that string formatting is more //' limited. As one example, CCTZ frequently uses \code{"\%F \%T"} which do //' not work on Windows; one has to use \code{"\%Y-\%m-\%d \%H:\%M:\%S"}. //' @author Dirk Eddelbuettel //' @examples //' \dontrun{ //' now <- Sys.time() //' formatDatetime(now) # current (UTC) time, in full precision RFC3339 //' formatDatetime(now, tgttzstr="America/New_York") # same but in NY //' formatDatetime(now + 0:4) # vectorised //' } // [[Rcpp::export]] Rcpp::CharacterVector formatDatetime(Rcpp::DatetimeVector dtv, std::string fmt = "%Y-%m-%dT%H:%M:%E*S%Ez", std::string lcltzstr = "UTC", std::string tgttzstr = "UTC") { cctz::time_zone tgttz, lcltz; load_time_zone(tgttzstr, &tgttz); load_time_zone(lcltzstr, &lcltz); auto n = dtv.size(); Rcpp::CharacterVector cv(n); for (auto i=0; i tp = cctz::convert(cctz::civil_second(dt.getYear(), dt.getMonth(), dt.getDay(), dt.getHours(), dt.getMinutes(), dt.getSeconds()), lcltz) + sc::microseconds(dt.getMicroSeconds()); std::string res = cctz::format(fmt, tp, tgttz); cv(i) = res; } return cv; } //' Parse a Datetime vector //' //' An alternative to \code{as.POSIXct} based on the CCTZ library //' //' @title Parse a Datetime vector from a string vector //' @param svec A string vector from which a Datetime vector is to be parsed //' @param fmt A string with the format, which is based on \code{strftime} with some //' extensions; see the CCTZ documentation for details. //' @param tzstr The local timezone for the desired format //' @return A Datetime vector object for \code{parseDatetime}, a numeric matrix with //' two columns for seconds and nanoseconds for \code{parseDouble} //' @author Dirk Eddelbuettel //' @examples //' ds <- getOption("digits.secs") //' options(digits.secs=6) # max value //' parseDatetime("2016-12-07 10:11:12", "%Y-%m-%d %H:%M:%S") # full seconds //' parseDatetime("2016-12-07 10:11:12.123456", "%Y-%m-%d %H:%M:%E*S") # fractional seconds //' parseDatetime("2016-12-07T10:11:12.123456-00:00") ## default RFC3339 format //' parseDatetime("20161207 101112.123456", "%E4Y%m%d %H%M%E*S") # fractional seconds //' now <- trunc(Sys.time()) //' parseDatetime(formatDatetime(now + 0:4)) # vectorised //' options(digits.secs=ds) // [[Rcpp::export]] Rcpp::DatetimeVector parseDatetime(Rcpp::CharacterVector svec, std::string fmt = "%Y-%m-%dT%H:%M:%E*S%Ez", std::string tzstr = "UTC") { cctz::time_zone tz; load_time_zone(tzstr, &tz); sc::system_clock::time_point tp; cctz::time_point unix_epoch = sc::time_point_cast(sc::system_clock::from_time_t(0)); // if we wanted a 'start' timer //sc::system_clock::time_point start = sc::high_resolution_clock::now(); auto n = svec.size(); Rcpp::DatetimeVector dv(n, tzstr.c_str()); for (auto i=0; i(tp - unix_epoch).count() * 1.0e-6; // Rcpp::Rcout << "tp: " << cctz::format(fmt, tp, tz) << "\n" // << "unix epoch: " << cctz::format(fmt, unix_epoch, tz) << "\n" // << "(tp - unix.epoch).count(): " << (tp - unix_epoch).count() << "\n" // << "dt: " << dt << std::endl; dv(i) = Rcpp::Datetime(dt); } } // Rcpp::Rcout << "Took: " // << sc::nanoseconds(sc::high_resolution_clock::now() - start).count() // << " nanosec" << " " << std::endl; return dv; } //' @rdname formatDatetime //' @param secv A numeric vector with seconds since the epoch //' @param nanov A numeric vector with nanoseconds since the epoch, //' complementing \code{secv}. // [[Rcpp::export]] Rcpp::CharacterVector formatDouble(Rcpp::NumericVector secv, Rcpp::NumericVector nanov, std::string fmt = "%Y-%m-%dT%H:%M:%E*S%Ez", std::string tgttzstr = "UTC") { cctz::time_zone tgttz; load_time_zone(tgttzstr, &tgttz); auto n = secv.size(); Rcpp::CharacterVector cv(n); for (auto i=0; i(secv(i)); int64_t nanos = static_cast(nanov(i)); cctz::time_point tp = sc::system_clock::from_time_t(0); tp += sc::seconds(secs) + sc::nanoseconds(nanos); std::string res = cctz::format(fmt, tp, tgttz); cv(i) = res; } return cv; } //' @rdname parseDatetime // [[Rcpp::export]] Rcpp::NumericMatrix parseDouble(Rcpp::CharacterVector svec, std::string fmt = "%Y-%m-%dT%H:%M:%E*S%Ez", std::string tzstr = "UTC") { cctz::time_zone tz; load_time_zone(tzstr, &tz); sc::time_point tp; // Rcpp::Rcout << cctz::format(fmt, tp, tz) << std::endl; auto n = svec.size(); Rcpp::NumericMatrix dm(n, 2); for (auto i=0; i(now.time_since_epoch()).count(); Rcpp::Rcout << nanos << std::endl; } // return the offset at a given time-point for a given timezone template using time_point = std::chrono::time_point; using seconds = std::chrono::duration; int _RcppCCTZ_getOffset(std::int_fast64_t s, const char* tzstr) { // timezone caching is done by cctz cctz::time_zone tz; if (!load_time_zone(tzstr, &tz)) { throw std::range_error("Cannot retrieve timezone"); } const auto tp = time_point(seconds(s)); auto abs_lookup = tz.lookup(tp); return abs_lookup.offset; } cctz::civil_second _RcppCCTZ_convertToCivilSecond(const time_point& tp, const char* tzstr) { cctz::time_zone tz; if (!load_time_zone(tzstr, &tz)) { Rcpp::stop("Cannot retrieve timezone '%s'.", tzstr); } return tz.lookup(tp).cs; } time_point _RcppCCTZ_convertToTimePoint(const cctz::civil_second& cs, const char* tzstr) { cctz::time_zone tz; if (!load_time_zone(tzstr, &tz)) { throw std::range_error("Cannot retrieve timezone"); } return cctz::convert(cs, tz); } // The three following functions are as above but they return an error // instead of throwing; this is necessary for use of these function // with 32-bit Windows, where the throw is uncatchable and will // terminate R int _RcppCCTZ_getOffset_nothrow(std::int_fast64_t s, const char* tzstr, int& offset) { // timezone caching is done by cctz cctz::time_zone tz; if (!load_time_zone(tzstr, &tz)) { return -1; } const auto tp = time_point(seconds(s)); auto abs_lookup = tz.lookup(tp); offset = abs_lookup.offset; return 0; } int _RcppCCTZ_convertToCivilSecond_nothrow(const cctz::time_point& tp, const char* tzstr, cctz::civil_second& cs) { cctz::time_zone tz; if (!load_time_zone(tzstr, &tz)) { return -1; } cs = tz.lookup(tp).cs; return 0; } int _RcppCCTZ_convertToTimePoint_nothrow(const cctz::civil_second& cs, const char* tzstr, cctz::time_point& tp) { cctz::time_zone tz; if (!load_time_zone(tzstr, &tz)) { return -1; } tp = cctz::convert(cs, tz); return 0; } RcppCCTZ/src/time_zone_impl.cc0000644000176200001440000000663114271014541015757 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "time_zone_impl.h" #include #include #include #include #include #include #include "time_zone_fixed.h" namespace cctz { namespace { // time_zone::Impls are linked into a map to support fast lookup by name. using TimeZoneImplByName = std::unordered_map; TimeZoneImplByName* time_zone_map = nullptr; // Mutual exclusion for time_zone_map. std::mutex& TimeZoneMutex() { // This mutex is intentionally "leaked" to avoid the static deinitialization // order fiasco (std::mutex's destructor is not trivial on many platforms). static std::mutex* time_zone_mutex = new std::mutex; return *time_zone_mutex; } } // namespace time_zone time_zone::Impl::UTC() { return time_zone(UTCImpl()); } bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) { const Impl* const utc_impl = UTCImpl(); // Check for UTC (which is never a key in time_zone_map). auto offset = seconds::zero(); if (FixedOffsetFromName(name, &offset) && offset == seconds::zero()) { *tz = time_zone(utc_impl); return true; } // Check whether the time zone has already been loaded. { std::lock_guard lock(TimeZoneMutex()); if (time_zone_map != nullptr) { TimeZoneImplByName::const_iterator itr = time_zone_map->find(name); if (itr != time_zone_map->end()) { *tz = time_zone(itr->second); return itr->second != utc_impl; } } } // Load the new time zone (outside the lock). std::unique_ptr new_impl(new Impl(name)); // Add the new time zone to the map. std::lock_guard lock(TimeZoneMutex()); if (time_zone_map == nullptr) time_zone_map = new TimeZoneImplByName; const Impl*& impl = (*time_zone_map)[name]; if (impl == nullptr) { // this thread won any load race impl = new_impl->zone_ ? new_impl.release() : utc_impl; } *tz = time_zone(impl); return impl != utc_impl; } void time_zone::Impl::ClearTimeZoneMapTestOnly() { std::lock_guard lock(TimeZoneMutex()); if (time_zone_map != nullptr) { // Existing time_zone::Impl* entries are in the wild, so we can't delete // them. Instead, we move them to a private container, where they are // logically unreachable but not "leaked". Future requests will result // in reloading the data. static auto* cleared = new std::deque; for (const auto& element : *time_zone_map) { cleared->push_back(element.second); } time_zone_map->clear(); } } time_zone::Impl::Impl(const std::string& name) : name_(name), zone_(TimeZoneIf::Load(name_)) {} const time_zone::Impl* time_zone::Impl::UTCImpl() { static const Impl* utc_impl = new Impl("UTC"); // never fails return utc_impl; } } // namespace cctz RcppCCTZ/src/time_zone_lookup.cc0000644000176200001440000001526514271014541016332 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "cctz/time_zone.h" #if defined(__ANDROID__) #include #if defined(__ANDROID_API__) && __ANDROID_API__ >= 21 #include #endif #endif #if defined(__APPLE__) #include #include #endif #if defined(__Fuchsia__) #include #include #include #include #endif #include #include #include #include "time_zone_fixed.h" #include "time_zone_impl.h" namespace cctz { #if defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__ >= 21 namespace { // Android 'L' removes __system_property_get() from the NDK, however // it is still a hidden symbol in libc so we use dlsym() to access it. // See Chromium's base/sys_info_android.cc for a similar example. using property_get_func = int (*)(const char*, char*); property_get_func LoadSystemPropertyGet() { int flag = RTLD_LAZY | RTLD_GLOBAL; #if defined(RTLD_NOLOAD) flag |= RTLD_NOLOAD; // libc.so should already be resident #endif if (void* handle = dlopen("libc.so", flag)) { void* sym = dlsym(handle, "__system_property_get"); dlclose(handle); return reinterpret_cast(sym); } return nullptr; } int __system_property_get(const char* name, char* value) { static property_get_func system_property_get = LoadSystemPropertyGet(); return system_property_get ? system_property_get(name, value) : -1; } } // namespace #endif std::string time_zone::name() const { return effective_impl().Name(); } time_zone::absolute_lookup time_zone::lookup( const time_point& tp) const { return effective_impl().BreakTime(tp); } time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const { return effective_impl().MakeTime(cs); } bool time_zone::next_transition(const time_point& tp, civil_transition* trans) const { return effective_impl().NextTransition(tp, trans); } bool time_zone::prev_transition(const time_point& tp, civil_transition* trans) const { return effective_impl().PrevTransition(tp, trans); } std::string time_zone::version() const { return effective_impl().Version(); } std::string time_zone::description() const { return effective_impl().Description(); } const time_zone::Impl& time_zone::effective_impl() const { if (impl_ == nullptr) { // Dereferencing an implicit-UTC time_zone is expected to be // rare, so we don't mind paying a small synchronization cost. return *time_zone::Impl::UTC().impl_; } return *impl_; } bool load_time_zone(const std::string& name, time_zone* tz) { return time_zone::Impl::LoadTimeZone(name, tz); } time_zone utc_time_zone() { return time_zone::Impl::UTC(); // avoid name lookup } time_zone fixed_time_zone(const seconds& offset) { time_zone tz; load_time_zone(FixedOffsetToName(offset), &tz); return tz; } time_zone local_time_zone() { const char* zone = ":localtime"; #if defined(__ANDROID__) char sysprop[PROP_VALUE_MAX]; if (__system_property_get("persist.sys.timezone", sysprop) > 0) { zone = sysprop; } #endif #if defined(__APPLE__) std::vector buffer; CFTimeZoneRef tz_default = CFTimeZoneCopyDefault(); if (CFStringRef tz_name = CFTimeZoneGetName(tz_default)) { CFStringEncoding encoding = kCFStringEncodingUTF8; CFIndex length = CFStringGetLength(tz_name); buffer.resize(CFStringGetMaximumSizeForEncoding(length, encoding) + 1); if (CFStringGetCString(tz_name, &buffer[0], buffer.size(), encoding)) { zone = &buffer[0]; } } CFRelease(tz_default); #endif #if defined(__Fuchsia__) std::string primary_tz; [&]() { // Note: We can't use the synchronous FIDL API here because it doesn't // allow timeouts; if the FIDL call failed, local_time_zone() would never // return. const zx::duration kTimeout = zx::msec(500); // Don't attach to the thread because otherwise the thread's dispatcher // would be set to null when the loop is destroyed, causing any other FIDL // code running on the same thread to crash. async::Loop loop(&kAsyncLoopConfigNeverAttachToThread); fuchsia::intl::PropertyProviderHandle handle; zx_status_t status = fdio_service_connect_by_name( fuchsia::intl::PropertyProvider::Name_, handle.NewRequest().TakeChannel().release()); if (status != ZX_OK) { return; } fuchsia::intl::PropertyProviderPtr intl_provider; status = intl_provider.Bind(std::move(handle), loop.dispatcher()); if (status != ZX_OK) { return; } intl_provider->GetProfile( [&loop, &primary_tz](fuchsia::intl::Profile profile) { if (!profile.time_zones().empty()) { primary_tz = profile.time_zones()[0].id; } loop.Quit(); }); loop.Run(zx::deadline_after(kTimeout)); }(); if (!primary_tz.empty()) { zone = primary_tz.c_str(); } #endif // Allow ${TZ} to override to default zone. char* tz_env = nullptr; #if defined(_MSC_VER) _dupenv_s(&tz_env, nullptr, "TZ"); #else tz_env = std::getenv("TZ"); #endif if (tz_env) zone = tz_env; // We only support the "[:]" form. if (*zone == ':') ++zone; // Map "localtime" to a system-specific name, but // allow ${LOCALTIME} to override the default name. char* localtime_env = nullptr; if (strcmp(zone, "localtime") == 0) { #if defined(_MSC_VER) // System-specific default is just "localtime". _dupenv_s(&localtime_env, nullptr, "LOCALTIME"); #else zone = "/etc/localtime"; // System-specific default. localtime_env = std::getenv("LOCALTIME"); #endif if (localtime_env) zone = localtime_env; } const std::string name = zone; #if defined(_MSC_VER) free(localtime_env); free(tz_env); #endif time_zone tz; load_time_zone(name, &tz); // Falls back to UTC. // TODO: Follow the RFC3339 "Unknown Local Offset Convention" and // arrange for %z to generate "-0000" when we don't know the local // offset because the load_time_zone() failed and we're using UTC. return tz; } } // namespace cctz RcppCCTZ/src/time_zone_libc.cc0000644000176200001440000002343114271014541015724 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #if defined(_WIN32) || defined(_WIN64) #define _CRT_SECURE_NO_WARNINGS 1 #endif #include "time_zone_libc.h" #include #include #include #include #include "cctz/civil_time.h" #include "cctz/time_zone.h" #if defined(_AIX) extern "C" { extern long altzone; } #endif namespace cctz { namespace { #if defined(_WIN32) || defined(_WIN64) // Uses the globals: '_timezone', '_dstbias' and '_tzname'. auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + _dstbias) { const bool is_dst = tm.tm_isdst > 0; return _timezone + (is_dst ? _dstbias : 0); } auto tm_zone(const std::tm& tm) -> decltype(_tzname[0]) { const bool is_dst = tm.tm_isdst > 0; return _tzname[is_dst]; } #elif defined(__sun) || defined(_AIX) // Uses the globals: 'timezone', 'altzone' and 'tzname'. auto tm_gmtoff(const std::tm& tm) -> decltype(timezone) { const bool is_dst = tm.tm_isdst > 0; return is_dst ? altzone : timezone; } auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) { const bool is_dst = tm.tm_isdst > 0; return tzname[is_dst]; } #elif defined(__native_client__) || defined(__myriad2__) || \ defined(__EMSCRIPTEN__) // Uses the globals: 'timezone' and 'tzname'. auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + 0) { const bool is_dst = tm.tm_isdst > 0; return _timezone + (is_dst ? 60 * 60 : 0); } auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) { const bool is_dst = tm.tm_isdst > 0; return tzname[is_dst]; } #else // Adapt to different spellings of the struct std::tm extension fields. #if defined(tm_gmtoff) auto tm_gmtoff(const std::tm& tm) -> decltype(tm.tm_gmtoff) { return tm.tm_gmtoff; } #elif defined(__tm_gmtoff) auto tm_gmtoff(const std::tm& tm) -> decltype(tm.__tm_gmtoff) { return tm.__tm_gmtoff; } #else template auto tm_gmtoff(const T& tm) -> decltype(tm.tm_gmtoff) { return tm.tm_gmtoff; } template auto tm_gmtoff(const T& tm) -> decltype(tm.__tm_gmtoff) { return tm.__tm_gmtoff; } #endif // tm_gmtoff #if defined(tm_zone) auto tm_zone(const std::tm& tm) -> decltype(tm.tm_zone) { return tm.tm_zone; } #elif defined(__tm_zone) auto tm_zone(const std::tm& tm) -> decltype(tm.__tm_zone) { return tm.__tm_zone; } #else template auto tm_zone(const T& tm) -> decltype(tm.tm_zone) { return tm.tm_zone; } template auto tm_zone(const T& tm) -> decltype(tm.__tm_zone) { return tm.__tm_zone; } #endif // tm_zone #endif inline std::tm* gm_time(const std::time_t *timep, std::tm *result) { #if defined(_WIN32) || defined(_WIN64) return gmtime_s(result, timep) ? nullptr : result; #else return gmtime_r(timep, result); #endif } inline std::tm* local_time(const std::time_t *timep, std::tm *result) { #if defined(_WIN32) || defined(_WIN64) return localtime_s(result, timep) ? nullptr : result; #else return localtime_r(timep, result); #endif } // Converts a civil second and "dst" flag into a time_t and UTC offset. // Returns false if time_t cannot represent the requested civil second. // Caller must have already checked that cs.year() will fit into a tm_year. bool make_time(const civil_second& cs, int is_dst, std::time_t* t, int* off) { std::tm tm; tm.tm_year = static_cast(cs.year() - year_t{1900}); tm.tm_mon = cs.month() - 1; tm.tm_mday = cs.day(); tm.tm_hour = cs.hour(); tm.tm_min = cs.minute(); tm.tm_sec = cs.second(); tm.tm_isdst = is_dst; *t = std::mktime(&tm); if (*t == std::time_t{-1}) { std::tm tm2; const std::tm* tmp = local_time(t, &tm2); if (tmp == nullptr || tmp->tm_year != tm.tm_year || tmp->tm_mon != tm.tm_mon || tmp->tm_mday != tm.tm_mday || tmp->tm_hour != tm.tm_hour || tmp->tm_min != tm.tm_min || tmp->tm_sec != tm.tm_sec) { // A true error (not just one second before the epoch). return false; } } *off = static_cast(tm_gmtoff(tm)); return true; } // Find the least time_t in [lo:hi] where local time matches offset, given: // (1) lo doesn't match, (2) hi does, and (3) there is only one transition. std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) { std::tm tm; while (lo + 1 != hi) { const std::time_t mid = lo + (hi - lo) / 2; std::tm* tmp = local_time(&mid, &tm); if (tmp != nullptr) { if (tm_gmtoff(*tmp) == offset) { hi = mid; } else { lo = mid; } } else { // If std::tm cannot hold some result we resort to a linear search, // ignoring all failed conversions. Slow, but never really happens. while (++lo != hi) { tmp = local_time(&lo, &tm); if (tmp != nullptr) { if (tm_gmtoff(*tmp) == offset) break; } } return lo; } } return hi; } } // namespace TimeZoneLibC::TimeZoneLibC(const std::string& name) : local_(name == "localtime") {} time_zone::absolute_lookup TimeZoneLibC::BreakTime( const time_point& tp) const { time_zone::absolute_lookup al; al.offset = 0; al.is_dst = false; al.abbr = "-00"; const std::int_fast64_t s = ToUnixSeconds(tp); // If std::time_t cannot hold the input we saturate the output. if (s < std::numeric_limits::min()) { al.cs = civil_second::min(); return al; } if (s > std::numeric_limits::max()) { al.cs = civil_second::max(); return al; } const std::time_t t = static_cast(s); std::tm tm; std::tm* tmp = local_ ? local_time(&t, &tm) : gm_time(&t, &tm); // If std::tm cannot hold the result we saturate the output. if (tmp == nullptr) { al.cs = (s < 0) ? civil_second::min() : civil_second::max(); return al; } const year_t year = tmp->tm_year + year_t{1900}; al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); al.offset = static_cast(tm_gmtoff(*tmp)); al.abbr = local_ ? tm_zone(*tmp) : "UTC"; // as expected by cctz al.is_dst = tmp->tm_isdst > 0; return al; } time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const { if (!local_) { // If time_point cannot hold the result we saturate. static const civil_second min_tp_cs = civil_second() + ToUnixSeconds(time_point::min()); static const civil_second max_tp_cs = civil_second() + ToUnixSeconds(time_point::max()); const time_point tp = (cs < min_tp_cs) ? time_point::min() : (cs > max_tp_cs) ? time_point::max() : FromUnixSeconds(cs - civil_second()); return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; } // If tm_year cannot hold the requested year we saturate the result. if (cs.year() < 0) { if (cs.year() < std::numeric_limits::min() + year_t{1900}) { const time_point tp = time_point::min(); return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; } } else { if (cs.year() - year_t{1900} > std::numeric_limits::max()) { const time_point tp = time_point::max(); return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; } } // We probe with "is_dst" values of 0 and 1 to try to distinguish unique // civil seconds from skipped or repeated ones. This is not always possible // however, as the "dst" flag does not change over some offset transitions. // We are also subject to the vagaries of mktime() implementations. std::time_t t0, t1; int offset0, offset1; if (make_time(cs, 0, &t0, &offset0) && make_time(cs, 1, &t1, &offset1)) { if (t0 == t1) { // The civil time was singular (pre == trans == post). const time_point tp = FromUnixSeconds(t0); return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; } if (t0 > t1) { std::swap(t0, t1); std::swap(offset0, offset1); } const std::time_t tt = find_trans(t0, t1, offset1); const time_point trans = FromUnixSeconds(tt); if (offset0 < offset1) { // The civil time did not exist (pre >= trans > post). const time_point pre = FromUnixSeconds(t1); const time_point post = FromUnixSeconds(t0); return {time_zone::civil_lookup::SKIPPED, pre, trans, post}; } // The civil time was ambiguous (pre < trans <= post). const time_point pre = FromUnixSeconds(t0); const time_point post = FromUnixSeconds(t1); return {time_zone::civil_lookup::REPEATED, pre, trans, post}; } // make_time() failed somehow so we saturate the result. const time_point tp = (cs < civil_second()) ? time_point::min() : time_point::max(); return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; } bool TimeZoneLibC::NextTransition(const time_point&, time_zone::civil_transition*) const { return false; } bool TimeZoneLibC::PrevTransition(const time_point&, time_zone::civil_transition*) const { return false; } std::string TimeZoneLibC::Version() const { return std::string(); // unknown } std::string TimeZoneLibC::Description() const { return local_ ? "localtime" : "UTC"; } } // namespace cctz RcppCCTZ/src/time_zone_fixed.h0000644000176200001440000000345613520655751015773 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef CCTZ_TIME_ZONE_FIXED_H_ #define CCTZ_TIME_ZONE_FIXED_H_ #include #include "cctz/time_zone.h" namespace cctz { // Helper functions for dealing with the names and abbreviations // of time zones that are a fixed offset (seconds east) from UTC. // FixedOffsetFromName() extracts the offset from a valid fixed-offset // name, while FixedOffsetToName() and FixedOffsetToAbbr() generate // the canonical zone name and abbreviation respectively for the given // offset. // // A fixed-offset name looks like "Fixed/UTC<+->::". // Its abbreviation is of the form "UTC(<+->H?H(MM(SS)?)?)?" where the // optional pieces are omitted when their values are zero. (Note that // the sign is the opposite of that used in a POSIX TZ specification.) // // Note: FixedOffsetFromName() fails on syntax errors or when the parsed // offset exceeds 24 hours. FixedOffsetToName() and FixedOffsetToAbbr() // both produce "UTC" when the argument offset exceeds 24 hours. bool FixedOffsetFromName(const std::string& name, seconds* offset); std::string FixedOffsetToName(const seconds& offset); std::string FixedOffsetToAbbr(const seconds& offset); } // namespace cctz #endif // CCTZ_TIME_ZONE_FIXED_H_ RcppCCTZ/src/time_zone_libc.h0000644000176200001440000000323213520655751015575 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef CCTZ_TIME_ZONE_LIBC_H_ #define CCTZ_TIME_ZONE_LIBC_H_ #include #include "time_zone_if.h" namespace cctz { // A time zone backed by gmtime_r(3), localtime_r(3), and mktime(3), // and which therefore only supports UTC and the local time zone. // TODO: Add support for fixed offsets from UTC. class TimeZoneLibC : public TimeZoneIf { public: explicit TimeZoneLibC(const std::string& name); // TimeZoneIf implementations. time_zone::absolute_lookup BreakTime( const time_point& tp) const override; time_zone::civil_lookup MakeTime( const civil_second& cs) const override; bool NextTransition(const time_point& tp, time_zone::civil_transition* trans) const override; bool PrevTransition(const time_point& tp, time_zone::civil_transition* trans) const override; std::string Version() const override; std::string Description() const override; private: const bool local_; // localtime or UTC }; } // namespace cctz #endif // CCTZ_TIME_ZONE_LIBC_H_ RcppCCTZ/ChangeLog0000644000176200001440000004172114726436140013430 0ustar liggesusers2024-12-11 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Release 0.2.13 2024-09-08 Dirk Eddelbuettel * DESCRIPTION (Authors@R): Added 2024-05-20 Dirk Eddelbuettel * README.md: Use tinyverse.netlify.app for dependency badge 2024-03-01 Dirk Eddelbuettel * .github/workflows/ci.yaml (jobs): Update to actions/checkout@v4, add r-ci-setup actions 2023-06-01 Michael Quinn * src/examples.cpp: Qualify call to cctz::format() 2023-04-01 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll micro version * src/Makevars: No longer set CXX_STD to CXX11 * src/Makevars.win: Idem * src/Makevars.ucrt: Idem 2022-11-06 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Release 0.2.12 2022-11-03 Dirk Eddelbuettel * src/utilities.cpp (parseDatetime,parseDouble): Support NA value on incoming data which now return NA values * .editorconfig: Added * .github/workflows/ci.yaml (jobs): Update to actions/checkout@v3 2022-08-06 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Release 0.2.11 2022-08-02 Dirk Eddelbuettel * .github/workflows/ci.yaml (jobs): Use r2u 2022-07-29 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll minor version * inst/include/cctz/*: Synchronized with CCTZ repo * src/*: Ditto 2022-07-28 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll minor version 2022-07-26 Jing Lu * inst/include/RcppCCTZ_API.h: More specific #include files: add R_ext/Rdynload.h for R_GetCCallable, remove umbrella header R.h 2021-12-14 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Release 0.2.10 2021-12-09 Dirk Eddelbuettel * src/Makevars.ucrt: Based on patch by Tomas Kalibera that is part of his changes for the Windows utf8-enhabced ucrt3 builds of R 4.2.0 2021-12-08 Dirk Eddelbuettel * README.md: Remove unused continuous integration artifact and badge 2021-04-19 Dirk Eddelbuettel * DESCRIPTION (URL): Add package page 2021-01-02 Dirk Eddelbuettel * .github/workflows/ci.yaml: Add CI runner using r-ci * README.md: Add new badge 2020-08-30 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Release 0.2.9 * src/RcppExports_snippet.h: Snippet of exported functions needed in RcppExports.cpp (but not added by compileAttributes) 2020-08-28 Dirk Eddelbuettel * src/utilities.cpp: Show parsing example of YYYYMMDD HHMMSS.NNN * R/RcppExports.R: Idem * man/parseDatetime.Rd: Idem 2020-08-22 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll minor version * inst/include/RcppCCTZ_types.h: Additional declarations header 2020-08-18 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll minor version * inst/include/RcppCCTZ_API.h: Add convertToCivilSecond too * src/RcppExports.cpp: Note that one part is 'manual' 2020-08-13 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll minor version * inst/include/RcppCCTZ_API.h: Add API definitions header file 2020-08-03 Leonardo Silvestri * DESCRIPTION (Version, Date): Release 0.2.8 * src/utilities.cpp: No-throw versions of C functions for Win-32 * src/RcppExports.cpp: Idem 2020-05-12 Dirk Eddelbuettel * README.md: Add 'last commit' badge 2020-03-18 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Release 0.2.7 2020-03-08 Leonardo Silvestri * DESCRIPTION (Version, Date): Roll minor version * src/utilities.cpp (_RcppCCTZ_convertToCivilSecond): Throw if timezone not found 2019-11-15 Leonardo Silvestri * DESCRIPTION (Version, Date): Roll minor version * src/utilities.cpp: Added functions _RcppCCTZ_convertToCivilSecond that converts a time point to the number of seconds since epoch, and _RcppCCTZ_convertToTimePoint that converts a number of seconds since epoch into a time point * src/RcppExports.cpp: Export the above functions at C level 2019-09-07 Leonardo Silvestri * DESCRIPTION (Version, Date): Roll minor version * src/utilities.cpp: Added function _RcppCCTZ_getOffset to return the offset at a time-point for a specific timezone * src/RcppExports.cpp: Export the above function at C level 2019-08-03 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Release 0.2.6 * DESCRIPTION (Suggests): Added tinytest * .travis.yml (install): Install tinytest * tests/tinytest.R: New test driver using tinytest * test/doRUnit.R: Removed * inst/tinytest/test_format.R: Converted from RUnit * inst/tinytest/test_parse.R: Idem * inst/tinytest/test_tz.R: Idem * README.md (tinytest): Added dependency badge, add installation paragraph, add continued testing post-installation via tinytest 2019-08-02 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll minor version * src/*: Synchronized with upstream CCTZ release 2.3 * inst/include/cctz/*: Idem * src/time_zone_format.cc: Carry std::get_time patch forward * src/zone_info_source.cc: Carry forward no linking ZoneInfoSourceFactory * local/cctz-20181003/: Renamed from local/cctz to show release date * local/cctz-20190702/: Added 2018-10-14 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Release 0.2.5 2018-10-11 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll minor version * inst/unitTests/runit.Format.R (test.Format.R): Added test for format and parse of dual double vector (at highest precision) * local/cctz/: Added upstream to repo for easier diff comparison with upstream changes; same directory layout as upstream and now here * src/utilities.cpp: Set target timezone in DatetimeVector; relax one \dontrun 2018-10-09 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll minor version * src/utilities.cpp (toTz): Vectorized toTz * man/toTz.Rd: Updated documentation * inst/unitTests/runit.Parse.R (test.Parse): Added test for vectorised parse 2018-10-07 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll minor version * src/utilities.cpp (parseDatetime): Set the timezone when construction a DatetimeVector; correctly scale time since epoch * inst/unitTests/runit.Format.R: No longer condition on Solaris * inst/unitTests/runit.Parse.R: Idem, added two more parseDouble() tests 2018-10-06 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Release 0.2.4 * DESCRIPTION (Suggests): Add RUnit * .travis.yml: Install r-cran-runit * tests/doRUnit.R: Add unit test runner * inst/unitTests/runit.Format.R: Add a few unit tests * inst/unitTests/runit.Parse.R: Idem * inst/unitTests/runit.tz.R: Idem * tests/simpleTests.R: Removed in favour of unit test frameworks * tests/simpleTests.Rout: Idem 2018-10-05 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll minor version * src/time_zone_format.cc: Restore Windows build by also recognising MinGW / Rtools (so that the contributed src/get_time.h is used) * src/zone_info_source.cc (cctz_extension): Idem, and prevent attempt to link ZoneInfoSourceFactory * .Rbuildignore: Ignore (source) tarballs in top-level directory 2018-10-04 Dirk Eddelbuettel * src/*: Synchronized with upstream CCTZ 2018-10-03 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll minor version * tests/simpleTests.R: Condition on Solaris for some tests * src/utilities.cpp: Turn examples to \dontrun{} for Solaris * formatDatetime.Rd: Idem * parseDatetime.Rd: Idem * toTz.Rd: Idem * tzDiff.Rd: Idem 2018-03-17 Elliott Sales de Andrade * src/utilities.cpp: Remove one unnecessary #include 2017-11-29 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll minor version 2017-11-24 anderic1 * src/utilities.cpp: Generalize to vectorized input * man/tzDiff.Rd: Document change 2017-06-20 Dirk Eddelbuettel * src/time_tool.cc: #ifdef away the unused main() * .travis.yml (group): Added as required by Travis CI 2017-06-19 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Release 0.2.3 * R/init.R (.onLoad): Renamed from .onAttach, set TZDIR only if unset, removed the (conditional on intreactive mode) message, * README.md: Use alternate for img.shields.io GPL-2+ badge * src/RcppExports.cpp (R_init_anytime): Registration now here * src/init.c: Deleted as calls now made in RcppExports.cpp * R/RcppExports.R: Updated by Rcpp 0.12.11 as well 2017-05-03 Dirk Eddelbuettel * src/utilities.cpp: The epoch offset is now calculated more directly using a member function 2017-04-20 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Release 0.2.2 * src/init.c (R_init_RcppCCTZ): all R_registerRoutines() and R_useDynamicSymbols() * NAMESPACE: Use .registration=TRUE on useDynLib * src/time_zone_format.cc: Reapply required MinGW conditionals required for Windows build 2017-04-19 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll minor release and date; * src/utilities.cpp (parseDouble): Instantiate time_point explicitly for nanosecond use which seems needed on macOS (thanks, Leonardo!) * inst/include/civil_time_detail.h: Sync'ed with upstream * inst/include/time_zone.h: Idem * inst/include/time_zone_if.h: Idem * inst/include/time_zone_impl.h: Idem * inst/include/time_zone_info.h: Idem * inst/include/time_zone_libc.h: Idem * inst/include/time_zone_posix.h: Idem * src/time_tool.cc: Idem * src/time_zone_format.cc: Idem * src/time_zone_impl.cc: Idem * src/time_zone_info.cc: Idem * src/time_zone_libc.cc: Idem * src/time_zone_lookup.cc: Idem * src/time_zone_posix.cc: Idem * inst/include/time_zone_fixed.h: Added as new upstream file * inst/include/zone_info_source.h: Idem * src/civil_time_detail.cc: Idem * src/time_zone_fixed.cc: Idem * inst/include/src/cctz.h: Removed as removed upstream 2017-02-04 Dirk Eddelbuettel * DESCRIPTION (Date, Version): Release 0.2.1 * inst/NEWS.Rd: Updated 2017-01-31 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll minor release and date; another update on required C++ compiler version * .travis.yml: Switch to using run.sh for Travis CI; no longer need to install g++-4.9 as 'trusty' used; also switches to https fetch 2017-01-29 Dirk Eddelbuettel * DESCRIPTION: Made need for modern C++ compiler more explicit in SystemRequirements 2017-01-27 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll minor release and date * src/utilities.cpp (parseDouble): Correct conversion to seconds and nanoseconds as suggested by Leonardo Silvestri in #12 2017-01-08 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Release 0.2.0 * src/examples.cpp: Reverted all format strings back to the older 'sprintf' style formatters to be compatible with g++-4.9 * src/utilities.cpp: Idem; also updated documentation * man/RcppCCTZ-package.Rd: Documented Windows behaviour of strftime formatters and lack of support for '%F %T' * man/formatDatetime.Rd: Idem 2017-01-07 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll minor version * tests/simpleTests.R: Simple unit tests * tests/simpleTests.Rout.save: Reference output * R/init.R (.onAttach): Only show Windows message in interactive mode * src/examples.cpp: Use Windows format strings on Windows 2017-01-07 Daniel C. Dillon * inst/include/get_time.h: Ported from C++ library to fill in for the missing strptime on Windows with g++ 4.* as used by Rtools * src/time_zone_format.cc: Use ported routine * src/examples.cpp: Use Windows format strings on Windows 2016-12-28 Dirk Eddelbuettel * DESCRIPTION: No longer exclude Windows * src/time_zone_libc.cc (cctz): Define OFFSET() and ABBR() macros for MinGW * src/time_zone_info.cc (cctz): Account for MinGW * src/time_zone_format.cc (cctz): Idem, empty strptime use for now * R/init.R: On Windows set TZDIR to zoneinfo from R * src/Makevars.win: Added 2016-12-17 Dirk Eddelbuettel * src/utilities.cpp (formatDouble): Removed unused argument lcltzstr * src/RcppExports.cpp (formatDouble): Idem * R/RcppExports.R (formatDouble): Idem * man/formatDatetime.Rd: Idem 2016-12-12 Dirk Eddelbuettel * inst/NEWS.Rd: Post-release update for 0.1.0 2016-12-11 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Release 0.1.0 * src/utilities.cpp (formatDouble, parseDouble): Document and export * man/formatDatetime.Rd: Ditto * man/parseDatetime.Rd: Ditto * NAMESPACE: Ditto 2016-12-10 Dirk Eddelbuettel * README.md: Expanded and added examples section * src/utilities.cpp (formatDatetime, parseDatetime): Vectorized * src/utilities.cpp (formatDouble,parseDouble): Altered to work on '128 bit' via two sets of doubles for seconds + nanoseconds 2016-12-09 Dirk Eddelbuettel * src/utilities.cpp (formatDouble,parseDouble): New functions using full std::chrono nanosecond resolution, using doubles 2016-12-07 Dirk Eddelbuettel * src/utilities.cpp (formatDatetime,parseDatetime): New functions using full std::chrono object precision * man/formatDatetime.Rd: New documentation * man/parseDatetime.Rd: Ditto * NAMESPACE: Control more finely what is, or isn't exported 2016-12-02 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll minor version and date * src/examples.cpp (helloMoon): Updates to helloMoon() example * src/utilities.cpp (toTz): Update example * man/toTz.Rd: Ditto 2016-12-01 Dirk Eddelbuettel * inst/include/civil_time_detail.h: Synced with upstream * inst/include/time_zone.h: Ditto * inst/include/time_zone_if.h: Ditto * inst/include/time_zone_impl.h: Ditto * inst/include/time_zone_info.h: Ditto * inst/include/time_zone_libc.h: Ditto * src/time_zone_format.cc: Ditto * src/time_zone_impl.cc: Ditto * src/time_zone_info.cc: Ditto * src/time_zone_libc.cc: Ditto * src/time_zone_lookup.cc: Ditto * src/time_tool.cc: Ditto, also updated again for std::{cout,cerr} * inst/include/src/cctz_if.h: Removed as removed upstream * inst/include/src/cctz_impl.h: Ditto * inst/include/src/cctz_info.h: Ditto * inst/include/src/cctz_libc.h: Ditto * inst/include/src/cctz_posix.h: Ditto 2016-07-11 Dirk Eddelbuettel * inst/include/civil_time_detail.h (cctz): Synced with upstream * src/time_zone_format.cc: Ditto * src/time_zone_info.cc: Ditto * src/time_zone_lookup.cc: Ditto 2016-07-09 Dirk Eddelbuettel * DESCRIPTION (Version): Version 0.0.5 2016-05-27 Dirk Eddelbuettel * src/utilities.cpp (toTz): New utility function to 'transfer' a datetime object from one timezone to another * man/toTz.Rd: Documentation 2016-05-21 Dirk Eddelbuettel * DESCRIPTION (Version): Rolled minor version and date * src/*: Synced with CCTZ upstream * inst/include/src/: Ditto 2016-04-23 Dirk Eddelbuettel * DESCRIPTION (Version): Rolled minor version and date * src/utilities.cpp (tzDiff): New simple utility to compute differences between timezones 2016-04-18 Dirk Eddelbuettel * src/examples.cpp (exampleFormat): Added simple formatting example 2016-04-17 Dirk Eddelbuettel * DESCRIPTION: Version 0.0.4 * inst/NEWS.Rd: Updated * src/*: Updated to CCTZ v2 upstream * inst/include/src/: Ditto * src/time_tool.cc (main): #ifdef'ed out, replace std::cout with Rcpp::Rcout, added #include * src/examples.cpp: Converted to v2 of API 2016-01-17 Dirk Eddelbuettel * DESCRIPTION: Version 0.0.3 * src/*: Updated to CCTZ upstream * inst/include/src/: Ditto 2015-12-02 Dirk Eddelbuettel * DESCRIPTION: Version 0.0.2 * inst/include/src/cctz_libc.h: Applied CCTZ upstream suggestion to permit compilation on Solaris * src/cctz_libc.cc: Idem * src/cctz_if.cc: Idem 2015-11-30 Dirk Eddelbuettel * DESCRIPTION: Version 0.0.1 * inst/include/src/cctz_info.h: Updated to new upstream version which no longer uses 128 byte integers and is therefore suitable for CRAN * src/cctz_fmt.cc: Idem * src/cctz_info.cc: Idem 2015-10-18 Dirk Eddelbuettel * README.md: Added 2015-10-17 Dirk Eddelbuettel * .travis.yml: Added Travis support * src/examples.cpp: Added examples from CCTZ repo 2015-10-16 Dirk Eddelbuettel * src/*: Initial packaging based on CCTZ repo RcppCCTZ/NAMESPACE0000644000176200001440000000057213075713324013072 0ustar liggesusersuseDynLib(RcppCCTZ, .registration=TRUE) importFrom("Rcpp", "evalCpp") #exportPattern("^[[:alpha:]]+") export("toTz", "tzDiff", "formatDatetime", "parseDatetime", "formatDouble", "parseDouble", # "example0", "example1", "example2", "example3", "example4", "helloMoon", "exampleFormat") RcppCCTZ/inst/0000755000176200001440000000000014331764401012622 5ustar liggesusersRcppCCTZ/inst/include/0000755000176200001440000000000014270471402014243 5ustar liggesusersRcppCCTZ/inst/include/RcppCCTZ_types.h0000644000176200001440000000172713720450126017236 0ustar liggesusers #include "cctz/time_zone.h" // export these functions so they can be called at the C level by other packages: template using time_point = std::chrono::time_point; using seconds = std::chrono::duration; // declarations of exported functions int _RcppCCTZ_getOffset(std::int_fast64_t s, const char* tzstr); cctz::civil_second _RcppCCTZ_convertToCivilSecond(const cctz::time_point& tp, const char* tzstr); cctz::time_point _RcppCCTZ_convertToTimePoint(const cctz::civil_second& cs, const char* tzstr); int _RcppCCTZ_getOffset_nothrow(std::int_fast64_t s, const char* tzstr, int& offset); int _RcppCCTZ_convertToCivilSecond_nothrow(const cctz::time_point& tp, const char* tzstr, cctz::civil_second& cs); int _RcppCCTZ_convertToTimePoint_nothrow(const cctz::civil_second& cs, const char* tzstr, cctz::time_point& tp); RcppCCTZ/inst/include/cctz/0000755000176200001440000000000014271014541015204 5ustar liggesusersRcppCCTZ/inst/include/cctz/zone_info_source.h0000644000176200001440000000641513520655751020743 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef CCTZ_ZONE_INFO_SOURCE_H_ #define CCTZ_ZONE_INFO_SOURCE_H_ #include #include #include #include namespace cctz { // A stdio-like interface for providing zoneinfo data for a particular zone. class ZoneInfoSource { public: virtual ~ZoneInfoSource(); virtual std::size_t Read(void* ptr, std::size_t size) = 0; // like fread() virtual int Skip(std::size_t offset) = 0; // like fseek() // Until the zoneinfo data supports versioning information, we provide // a way for a ZoneInfoSource to indicate it out-of-band. The default // implementation returns an empty string. virtual std::string Version() const; }; } // namespace cctz namespace cctz_extension { // A function-pointer type for a factory that returns a ZoneInfoSource // given the name of a time zone and a fallback factory. Returns null // when the data for the named zone cannot be found. using ZoneInfoSourceFactory = std::unique_ptr (*)( const std::string&, const std::function( const std::string&)>&); // The user can control the mapping of zone names to zoneinfo data by // providing a definition for cctz_extension::zone_info_source_factory. // For example, given functions my_factory() and my_other_factory() that // can return a ZoneInfoSource for a named zone, we could inject them into // cctz::load_time_zone() with: // // namespace cctz_extension { // namespace { // std::unique_ptr CustomFactory( // const std::string& name, // const std::function( // const std::string& name)>& fallback_factory) { // if (auto zip = my_factory(name)) return zip; // if (auto zip = fallback_factory(name)) return zip; // if (auto zip = my_other_factory(name)) return zip; // return nullptr; // } // } // namespace // ZoneInfoSourceFactory zone_info_source_factory = CustomFactory; // } // namespace cctz_extension // // This might be used, say, to use zoneinfo data embedded in the program, // or read from a (possibly compressed) file archive, or both. // // cctz_extension::zone_info_source_factory() will be called: // (1) from the same thread as the cctz::load_time_zone() call, // (2) only once for any zone name, and // (3) serially (i.e., no concurrent execution). // // The fallback factory obtains zoneinfo data by reading files in ${TZDIR}, // and it is used automatically when no zone_info_source_factory definition // is linked into the program. extern ZoneInfoSourceFactory zone_info_source_factory; } // namespace cctz_extension #endif // CCTZ_ZONE_INFO_SOURCE_H_ RcppCCTZ/inst/include/cctz/civil_time_detail.h0000644000176200001440000004764514271014541021043 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef CCTZ_CIVIL_TIME_DETAIL_H_ #define CCTZ_CIVIL_TIME_DETAIL_H_ #include #include #include #include // Disable constexpr support unless we are in C++14 mode. #if __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910) #define CONSTEXPR_D constexpr // data #define CONSTEXPR_F constexpr // function #define CONSTEXPR_M constexpr // member #else #define CONSTEXPR_D const #define CONSTEXPR_F inline #define CONSTEXPR_M #endif namespace cctz { // Support years that at least span the range of 64-bit time_t values. using year_t = std::int_fast64_t; // Type alias that indicates an argument is not normalized (e.g., the // constructor parameters and operands/results of addition/subtraction). using diff_t = std::int_fast64_t; namespace detail { // Type aliases that indicate normalized argument values. using month_t = std::int_fast8_t; // [1:12] using day_t = std::int_fast8_t; // [1:31] using hour_t = std::int_fast8_t; // [0:23] using minute_t = std::int_fast8_t; // [0:59] using second_t = std::int_fast8_t; // [0:59] // Normalized civil-time fields: Y-M-D HH:MM:SS. struct fields { CONSTEXPR_M fields(year_t year, month_t month, day_t day, hour_t hour, minute_t minute, second_t second) : y(year), m(month), d(day), hh(hour), mm(minute), ss(second) {} std::int_least64_t y; std::int_least8_t m; std::int_least8_t d; std::int_least8_t hh; std::int_least8_t mm; std::int_least8_t ss; }; struct second_tag {}; struct minute_tag : second_tag {}; struct hour_tag : minute_tag {}; struct day_tag : hour_tag {}; struct month_tag : day_tag {}; struct year_tag : month_tag {}; //////////////////////////////////////////////////////////////////////// // Field normalization (without avoidable overflow). namespace impl { CONSTEXPR_F bool is_leap_year(year_t y) noexcept { return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); } CONSTEXPR_F int year_index(year_t y, month_t m) noexcept { const int yi = static_cast((y + (m > 2)) % 400); return yi < 0 ? yi + 400 : yi; } CONSTEXPR_F int days_per_century(int yi) noexcept { return 36524 + (yi == 0 || yi > 300); } CONSTEXPR_F int days_per_4years(int yi) noexcept { return 1460 + (yi == 0 || yi > 300 || (yi - 1) % 100 < 96); } CONSTEXPR_F int days_per_year(year_t y, month_t m) noexcept { return is_leap_year(y + (m > 2)) ? 366 : 365; } CONSTEXPR_F int days_per_month(year_t y, month_t m) noexcept { CONSTEXPR_D int k_days_per_month[1 + 12] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 // non leap year }; return k_days_per_month[m] + (m == 2 && is_leap_year(y)); } CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd, hour_t hh, minute_t mm, second_t ss) noexcept { year_t ey = y % 400; const year_t oey = ey; ey += (cd / 146097) * 400; cd %= 146097; if (cd < 0) { ey -= 400; cd += 146097; } ey += (d / 146097) * 400; d = d % 146097 + cd; if (d > 0) { if (d > 146097) { ey += 400; d -= 146097; } } else { if (d > -365) { // We often hit the previous year when stepping a civil time backwards, // so special case it to avoid counting up by 100/4/1-year chunks. ey -= 1; d += days_per_year(ey, m); } else { ey -= 400; d += 146097; } } if (d > 365) { int yi = year_index(ey, m); // Index into Gregorian 400 year cycle. for (;;) { int n = days_per_century(yi); if (d <= n) break; d -= n; ey += 100; yi += 100; if (yi >= 400) yi -= 400; } for (;;) { int n = days_per_4years(yi); if (d <= n) break; d -= n; ey += 4; yi += 4; if (yi >= 400) yi -= 400; } for (;;) { int n = days_per_year(ey, m); if (d <= n) break; d -= n; ++ey; } } if (d > 28) { for (;;) { int n = days_per_month(ey, m); if (d <= n) break; d -= n; if (++m > 12) { ++ey; m = 1; } } } return fields(y + (ey - oey), m, static_cast(d), hh, mm, ss); } CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd, hour_t hh, minute_t mm, second_t ss) noexcept { if (m != 12) { y += m / 12; m %= 12; if (m <= 0) { y -= 1; m += 12; } } return n_day(y, static_cast(m), d, cd, hh, mm, ss); } CONSTEXPR_F fields n_hour(year_t y, diff_t m, diff_t d, diff_t cd, diff_t hh, minute_t mm, second_t ss) noexcept { cd += hh / 24; hh %= 24; if (hh < 0) { cd -= 1; hh += 24; } return n_mon(y, m, d, cd, static_cast(hh), mm, ss); } CONSTEXPR_F fields n_min(year_t y, diff_t m, diff_t d, diff_t hh, diff_t ch, diff_t mm, second_t ss) noexcept { ch += mm / 60; mm %= 60; if (mm < 0) { ch -= 1; mm += 60; } return n_hour(y, m, d, hh / 24 + ch / 24, hh % 24 + ch % 24, static_cast(mm), ss); } CONSTEXPR_F fields n_sec(year_t y, diff_t m, diff_t d, diff_t hh, diff_t mm, diff_t ss) noexcept { // Optimization for when (non-constexpr) fields are already normalized. if (0 <= ss && ss < 60) { const second_t nss = static_cast(ss); if (0 <= mm && mm < 60) { const minute_t nmm = static_cast(mm); if (0 <= hh && hh < 24) { const hour_t nhh = static_cast(hh); if (1 <= d && d <= 28 && 1 <= m && m <= 12) { const day_t nd = static_cast(d); const month_t nm = static_cast(m); return fields(y, nm, nd, nhh, nmm, nss); } return n_mon(y, m, d, 0, nhh, nmm, nss); } return n_hour(y, m, d, hh / 24, hh % 24, nmm, nss); } return n_min(y, m, d, hh, mm / 60, mm % 60, nss); } diff_t cm = ss / 60; ss %= 60; if (ss < 0) { cm -= 1; ss += 60; } return n_min(y, m, d, hh, mm / 60 + cm / 60, mm % 60 + cm % 60, static_cast(ss)); } } // namespace impl //////////////////////////////////////////////////////////////////////// // Increments the indicated (normalized) field by "n". CONSTEXPR_F fields step(second_tag, fields f, diff_t n) noexcept { return impl::n_sec(f.y, f.m, f.d, f.hh, f.mm + n / 60, f.ss + n % 60); } CONSTEXPR_F fields step(minute_tag, fields f, diff_t n) noexcept { return impl::n_min(f.y, f.m, f.d, f.hh + n / 60, 0, f.mm + n % 60, f.ss); } CONSTEXPR_F fields step(hour_tag, fields f, diff_t n) noexcept { return impl::n_hour(f.y, f.m, f.d + n / 24, 0, f.hh + n % 24, f.mm, f.ss); } CONSTEXPR_F fields step(day_tag, fields f, diff_t n) noexcept { return impl::n_day(f.y, f.m, f.d, n, f.hh, f.mm, f.ss); } CONSTEXPR_F fields step(month_tag, fields f, diff_t n) noexcept { return impl::n_mon(f.y + n / 12, f.m + n % 12, f.d, 0, f.hh, f.mm, f.ss); } CONSTEXPR_F fields step(year_tag, fields f, diff_t n) noexcept { return fields(f.y + n, f.m, f.d, f.hh, f.mm, f.ss); } //////////////////////////////////////////////////////////////////////// namespace impl { // Returns (v * f + a) but avoiding intermediate overflow when possible. CONSTEXPR_F diff_t scale_add(diff_t v, diff_t f, diff_t a) noexcept { return (v < 0) ? ((v + 1) * f + a) - f : ((v - 1) * f + a) + f; } // Map a (normalized) Y/M/D to the number of days before/after 1970-01-01. // Probably overflows for years outside [-292277022656:292277026595]. CONSTEXPR_F diff_t ymd_ord(year_t y, month_t m, day_t d) noexcept { const diff_t eyear = (m <= 2) ? y - 1 : y; const diff_t era = (eyear >= 0 ? eyear : eyear - 399) / 400; const diff_t yoe = eyear - era * 400; const diff_t doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; const diff_t doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; return era * 146097 + doe - 719468; } // Returns the difference in days between two normalized Y-M-D tuples. // ymd_ord() will encounter integer overflow given extreme year values, // yet the difference between two such extreme values may actually be // small, so we take a little care to avoid overflow when possible by // exploiting the 146097-day cycle. CONSTEXPR_F diff_t day_difference(year_t y1, month_t m1, day_t d1, year_t y2, month_t m2, day_t d2) noexcept { const diff_t a_c4_off = y1 % 400; const diff_t b_c4_off = y2 % 400; diff_t c4_diff = (y1 - a_c4_off) - (y2 - b_c4_off); diff_t delta = ymd_ord(a_c4_off, m1, d1) - ymd_ord(b_c4_off, m2, d2); if (c4_diff > 0 && delta < 0) { delta += 2 * 146097; c4_diff -= 2 * 400; } else if (c4_diff < 0 && delta > 0) { delta -= 2 * 146097; c4_diff += 2 * 400; } return (c4_diff / 400 * 146097) + delta; } } // namespace impl // Returns the difference between fields structs using the indicated unit. CONSTEXPR_F diff_t difference(year_tag, fields f1, fields f2) noexcept { return f1.y - f2.y; } CONSTEXPR_F diff_t difference(month_tag, fields f1, fields f2) noexcept { return impl::scale_add(difference(year_tag{}, f1, f2), 12, (f1.m - f2.m)); } CONSTEXPR_F diff_t difference(day_tag, fields f1, fields f2) noexcept { return impl::day_difference(f1.y, f1.m, f1.d, f2.y, f2.m, f2.d); } CONSTEXPR_F diff_t difference(hour_tag, fields f1, fields f2) noexcept { return impl::scale_add(difference(day_tag{}, f1, f2), 24, (f1.hh - f2.hh)); } CONSTEXPR_F diff_t difference(minute_tag, fields f1, fields f2) noexcept { return impl::scale_add(difference(hour_tag{}, f1, f2), 60, (f1.mm - f2.mm)); } CONSTEXPR_F diff_t difference(second_tag, fields f1, fields f2) noexcept { return impl::scale_add(difference(minute_tag{}, f1, f2), 60, f1.ss - f2.ss); } //////////////////////////////////////////////////////////////////////// // Aligns the (normalized) fields struct to the indicated field. CONSTEXPR_F fields align(second_tag, fields f) noexcept { return f; } CONSTEXPR_F fields align(minute_tag, fields f) noexcept { return fields{f.y, f.m, f.d, f.hh, f.mm, 0}; } CONSTEXPR_F fields align(hour_tag, fields f) noexcept { return fields{f.y, f.m, f.d, f.hh, 0, 0}; } CONSTEXPR_F fields align(day_tag, fields f) noexcept { return fields{f.y, f.m, f.d, 0, 0, 0}; } CONSTEXPR_F fields align(month_tag, fields f) noexcept { return fields{f.y, f.m, 1, 0, 0, 0}; } CONSTEXPR_F fields align(year_tag, fields f) noexcept { return fields{f.y, 1, 1, 0, 0, 0}; } //////////////////////////////////////////////////////////////////////// template class civil_time { public: explicit CONSTEXPR_M civil_time(year_t y, diff_t m = 1, diff_t d = 1, diff_t hh = 0, diff_t mm = 0, diff_t ss = 0) noexcept : civil_time(impl::n_sec(y, m, d, hh, mm, ss)) {} CONSTEXPR_M civil_time() noexcept : f_{1970, 1, 1, 0, 0, 0} {} civil_time(const civil_time&) = default; civil_time& operator=(const civil_time&) = default; // Conversion between civil times of different alignment. Conversion to // a more precise alignment is allowed implicitly (e.g., day -> hour), // but conversion where information is discarded must be explicit // (e.g., second -> minute). template using preserves_data = typename std::enable_if::value>::type; template CONSTEXPR_M civil_time(const civil_time& ct, preserves_data* = nullptr) noexcept : civil_time(ct.f_) {} template explicit CONSTEXPR_M civil_time(const civil_time& ct, preserves_data* = nullptr) noexcept : civil_time(ct.f_) {} // Factories for the maximum/minimum representable civil_time. static CONSTEXPR_F civil_time (max)() { const auto max_year = (std::numeric_limits::max)(); return civil_time(max_year, 12, 31, 23, 59, 59); } static CONSTEXPR_F civil_time (min)() { const auto min_year = (std::numeric_limits::min)(); return civil_time(min_year, 1, 1, 0, 0, 0); } // Field accessors. Note: All but year() return an int. CONSTEXPR_M year_t year() const noexcept { return f_.y; } CONSTEXPR_M int month() const noexcept { return f_.m; } CONSTEXPR_M int day() const noexcept { return f_.d; } CONSTEXPR_M int hour() const noexcept { return f_.hh; } CONSTEXPR_M int minute() const noexcept { return f_.mm; } CONSTEXPR_M int second() const noexcept { return f_.ss; } // Assigning arithmetic. CONSTEXPR_M civil_time& operator+=(diff_t n) noexcept { return *this = *this + n; } CONSTEXPR_M civil_time& operator-=(diff_t n) noexcept { return *this = *this - n; } CONSTEXPR_M civil_time& operator++() noexcept { return *this += 1; } CONSTEXPR_M civil_time operator++(int) noexcept { const civil_time a = *this; ++*this; return a; } CONSTEXPR_M civil_time& operator--() noexcept { return *this -= 1; } CONSTEXPR_M civil_time operator--(int) noexcept { const civil_time a = *this; --*this; return a; } // Binary arithmetic operators. friend CONSTEXPR_F civil_time operator+(civil_time a, diff_t n) noexcept { return civil_time(step(T{}, a.f_, n)); } friend CONSTEXPR_F civil_time operator+(diff_t n, civil_time a) noexcept { return a + n; } friend CONSTEXPR_F civil_time operator-(civil_time a, diff_t n) noexcept { return n != (std::numeric_limits::min)() ? civil_time(step(T{}, a.f_, -n)) : civil_time(step(T{}, step(T{}, a.f_, -(n + 1)), 1)); } friend CONSTEXPR_F diff_t operator-(civil_time lhs, civil_time rhs) noexcept { return difference(T{}, lhs.f_, rhs.f_); } private: // All instantiations of this template are allowed to call the following // private constructor and access the private fields member. template friend class civil_time; // The designated constructor that all others eventually call. explicit CONSTEXPR_M civil_time(fields f) noexcept : f_(align(T{}, f)) {} fields f_; }; // Disallows difference between differently aligned types. // auto n = civil_day(...) - civil_hour(...); // would be confusing. template CONSTEXPR_F diff_t operator-(civil_time, civil_time) = delete; using civil_year = civil_time; using civil_month = civil_time; using civil_day = civil_time; using civil_hour = civil_time; using civil_minute = civil_time; using civil_second = civil_time; //////////////////////////////////////////////////////////////////////// // Relational operators that work with differently aligned objects. // Always compares all six fields. template CONSTEXPR_F bool operator<(const civil_time& lhs, const civil_time& rhs) noexcept { return (lhs.year() < rhs.year() || (lhs.year() == rhs.year() && (lhs.month() < rhs.month() || (lhs.month() == rhs.month() && (lhs.day() < rhs.day() || (lhs.day() == rhs.day() && (lhs.hour() < rhs.hour() || (lhs.hour() == rhs.hour() && (lhs.minute() < rhs.minute() || (lhs.minute() == rhs.minute() && (lhs.second() < rhs.second()))))))))))); } template CONSTEXPR_F bool operator<=(const civil_time& lhs, const civil_time& rhs) noexcept { return !(rhs < lhs); } template CONSTEXPR_F bool operator>=(const civil_time& lhs, const civil_time& rhs) noexcept { return !(lhs < rhs); } template CONSTEXPR_F bool operator>(const civil_time& lhs, const civil_time& rhs) noexcept { return rhs < lhs; } template CONSTEXPR_F bool operator==(const civil_time& lhs, const civil_time& rhs) noexcept { return lhs.year() == rhs.year() && lhs.month() == rhs.month() && lhs.day() == rhs.day() && lhs.hour() == rhs.hour() && lhs.minute() == rhs.minute() && lhs.second() == rhs.second(); } template CONSTEXPR_F bool operator!=(const civil_time& lhs, const civil_time& rhs) noexcept { return !(lhs == rhs); } //////////////////////////////////////////////////////////////////////// enum class weekday { monday, tuesday, wednesday, thursday, friday, saturday, sunday, }; CONSTEXPR_F weekday get_weekday(const civil_second& cs) noexcept { CONSTEXPR_D weekday k_weekday_by_mon_off[13] = { weekday::monday, weekday::tuesday, weekday::wednesday, weekday::thursday, weekday::friday, weekday::saturday, weekday::sunday, weekday::monday, weekday::tuesday, weekday::wednesday, weekday::thursday, weekday::friday, weekday::saturday, }; CONSTEXPR_D int k_weekday_offsets[1 + 12] = { -1, 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4, }; year_t wd = 2400 + (cs.year() % 400) - (cs.month() < 3); wd += wd / 4 - wd / 100 + wd / 400; wd += k_weekday_offsets[cs.month()] + cs.day(); return k_weekday_by_mon_off[wd % 7 + 6]; } //////////////////////////////////////////////////////////////////////// CONSTEXPR_F civil_day next_weekday(civil_day cd, weekday wd) noexcept { CONSTEXPR_D weekday k_weekdays_forw[14] = { weekday::monday, weekday::tuesday, weekday::wednesday, weekday::thursday, weekday::friday, weekday::saturday, weekday::sunday, weekday::monday, weekday::tuesday, weekday::wednesday, weekday::thursday, weekday::friday, weekday::saturday, weekday::sunday, }; weekday base = get_weekday(cd); for (int i = 0;; ++i) { if (base == k_weekdays_forw[i]) { for (int j = i + 1;; ++j) { if (wd == k_weekdays_forw[j]) { return cd + (j - i); } } } } } CONSTEXPR_F civil_day prev_weekday(civil_day cd, weekday wd) noexcept { CONSTEXPR_D weekday k_weekdays_back[14] = { weekday::sunday, weekday::saturday, weekday::friday, weekday::thursday, weekday::wednesday, weekday::tuesday, weekday::monday, weekday::sunday, weekday::saturday, weekday::friday, weekday::thursday, weekday::wednesday, weekday::tuesday, weekday::monday, }; weekday base = get_weekday(cd); for (int i = 0;; ++i) { if (base == k_weekdays_back[i]) { for (int j = i + 1;; ++j) { if (wd == k_weekdays_back[j]) { return cd - (j - i); } } } } } CONSTEXPR_F int get_yearday(const civil_second& cs) noexcept { CONSTEXPR_D int k_month_offsets[1 + 12] = { -1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, }; const int feb29 = (cs.month() > 2 && impl::is_leap_year(cs.year())); return k_month_offsets[cs.month()] + feb29 + cs.day(); } //////////////////////////////////////////////////////////////////////// std::ostream& operator<<(std::ostream& os, const civil_year& y); std::ostream& operator<<(std::ostream& os, const civil_month& m); std::ostream& operator<<(std::ostream& os, const civil_day& d); std::ostream& operator<<(std::ostream& os, const civil_hour& h); std::ostream& operator<<(std::ostream& os, const civil_minute& m); std::ostream& operator<<(std::ostream& os, const civil_second& s); std::ostream& operator<<(std::ostream& os, weekday wd); } // namespace detail } // namespace cctz #undef CONSTEXPR_M #undef CONSTEXPR_F #undef CONSTEXPR_D #endif // CCTZ_CIVIL_TIME_DETAIL_H_ RcppCCTZ/inst/include/cctz/time_zone.h0000644000176200001440000004560614271014541017361 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // A library for translating between absolute times (represented by // std::chrono::time_points of the std::chrono::system_clock) and civil // times (represented by cctz::civil_second) using the rules defined by // a time zone (cctz::time_zone). #ifndef CCTZ_TIME_ZONE_H_ #define CCTZ_TIME_ZONE_H_ #include #include #include #include #include #include "cctz/civil_time.h" namespace cctz { // Convenience aliases. Not intended as public API points. template using time_point = std::chrono::time_point; using seconds = std::chrono::duration; using sys_seconds = seconds; // Deprecated. Use cctz::seconds instead. namespace detail { template std::pair, D> split_seconds(const time_point& tp); std::pair, seconds> split_seconds( const time_point& tp); } // namespace detail // cctz::time_zone is an opaque, small, value-type class representing a // geo-political region within which particular rules are used for mapping // between absolute and civil times. Time zones are named using the TZ // identifiers from the IANA Time Zone Database, such as "America/Los_Angeles" // or "Australia/Sydney". Time zones are created from factory functions such // as load_time_zone(). Note: strings like "PST" and "EDT" are not valid TZ // identifiers. // // Example: // cctz::time_zone utc = cctz::utc_time_zone(); // cctz::time_zone pst = cctz::fixed_time_zone(std::chrono::hours(-8)); // cctz::time_zone loc = cctz::local_time_zone(); // cctz::time_zone lax; // if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... } // // See also: // - http://www.iana.org/time-zones // - https://en.wikipedia.org/wiki/Zoneinfo class time_zone { public: time_zone() : time_zone(nullptr) {} // Equivalent to UTC time_zone(const time_zone&) = default; time_zone& operator=(const time_zone&) = default; std::string name() const; // An absolute_lookup represents the civil time (cctz::civil_second) within // this time_zone at the given absolute time (time_point). There are // additionally a few other fields that may be useful when working with // older APIs, such as std::tm. // // Example: // const cctz::time_zone tz = ... // const auto tp = std::chrono::system_clock::now(); // const cctz::time_zone::absolute_lookup al = tz.lookup(tp); struct absolute_lookup { civil_second cs; // Note: The following fields exist for backward compatibility with older // APIs. Accessing these fields directly is a sign of imprudent logic in // the calling code. Modern time-related code should only access this data // indirectly by way of cctz::format(). int offset; // civil seconds east of UTC bool is_dst; // is offset non-standard? const char* abbr; // time-zone abbreviation (e.g., "PST") }; absolute_lookup lookup(const time_point& tp) const; template absolute_lookup lookup(const time_point& tp) const { return lookup(detail::split_seconds(tp).first); } // A civil_lookup represents the absolute time(s) (time_point) that // correspond to the given civil time (cctz::civil_second) within this // time_zone. Usually the given civil time represents a unique instant // in time, in which case the conversion is unambiguous. However, // within this time zone, the given civil time may be skipped (e.g., // during a positive UTC offset shift), or repeated (e.g., during a // negative UTC offset shift). To account for these possibilities, // civil_lookup is richer than just a single time_point. // // In all cases the civil_lookup::kind enum will indicate the nature // of the given civil-time argument, and the pre, trans, and post // members will give the absolute time answers using the pre-transition // offset, the transition point itself, and the post-transition offset, // respectively (all three times are equal if kind == UNIQUE). If any // of these three absolute times is outside the representable range of a // time_point the field is set to its maximum/minimum value. // // Example: // cctz::time_zone lax; // if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... } // // // A unique civil time. // auto jan01 = lax.lookup(cctz::civil_second(2011, 1, 1, 0, 0, 0)); // // jan01.kind == cctz::time_zone::civil_lookup::UNIQUE // // jan01.pre is 2011/01/01 00:00:00 -0800 // // jan01.trans is 2011/01/01 00:00:00 -0800 // // jan01.post is 2011/01/01 00:00:00 -0800 // // // A Spring DST transition, when there is a gap in civil time. // auto mar13 = lax.lookup(cctz::civil_second(2011, 3, 13, 2, 15, 0)); // // mar13.kind == cctz::time_zone::civil_lookup::SKIPPED // // mar13.pre is 2011/03/13 03:15:00 -0700 // // mar13.trans is 2011/03/13 03:00:00 -0700 // // mar13.post is 2011/03/13 01:15:00 -0800 // // // A Fall DST transition, when civil times are repeated. // auto nov06 = lax.lookup(cctz::civil_second(2011, 11, 6, 1, 15, 0)); // // nov06.kind == cctz::time_zone::civil_lookup::REPEATED // // nov06.pre is 2011/11/06 01:15:00 -0700 // // nov06.trans is 2011/11/06 01:00:00 -0800 // // nov06.post is 2011/11/06 01:15:00 -0800 struct civil_lookup { enum civil_kind { UNIQUE, // the civil time was singular (pre == trans == post) SKIPPED, // the civil time did not exist (pre >= trans > post) REPEATED, // the civil time was ambiguous (pre < trans <= post) } kind; time_point pre; // uses the pre-transition offset time_point trans; // instant of civil-offset change time_point post; // uses the post-transition offset }; civil_lookup lookup(const civil_second& cs) const; // Finds the time of the next/previous offset change in this time zone. // // By definition, next_transition(tp, &trans) returns false when tp has // its maximum value, and prev_transition(tp, &trans) returns false // when tp has its minimum value. If the zone has no transitions, the // result will also be false no matter what the argument. // // Otherwise, when tp has its minimum value, next_transition(tp, &trans) // returns true and sets trans to the first recorded transition. Chains // of calls to next_transition()/prev_transition() will eventually return // false, but it is unspecified exactly when next_transition(tp, &trans) // jumps to false, or what time is set by prev_transition(tp, &trans) for // a very distant tp. // // Note: Enumeration of time-zone transitions is for informational purposes // only. Modern time-related code should not care about when offset changes // occur. // // Example: // cctz::time_zone nyc; // if (!cctz::load_time_zone("America/New_York", &nyc)) { ... } // const auto now = std::chrono::system_clock::now(); // auto tp = cctz::time_point::min(); // cctz::time_zone::civil_transition trans; // while (tp <= now && nyc.next_transition(tp, &trans)) { // // transition: trans.from -> trans.to // tp = nyc.lookup(trans.to).trans; // } struct civil_transition { civil_second from; // the civil time we jump from civil_second to; // the civil time we jump to }; bool next_transition(const time_point& tp, civil_transition* trans) const; template bool next_transition(const time_point& tp, civil_transition* trans) const { return next_transition(detail::split_seconds(tp).first, trans); } bool prev_transition(const time_point& tp, civil_transition* trans) const; template bool prev_transition(const time_point& tp, civil_transition* trans) const { return prev_transition(detail::split_seconds(tp).first, trans); } // version() and description() provide additional information about the // time zone. The content of each of the returned strings is unspecified, // however, when the IANA Time Zone Database is the underlying data source // the version() string will be in the familar form (e.g, "2018e") or // empty when unavailable. // // Note: These functions are for informational or testing purposes only. std::string version() const; // empty when unknown std::string description() const; // Relational operators. friend bool operator==(time_zone lhs, time_zone rhs) { return &lhs.effective_impl() == &rhs.effective_impl(); } friend bool operator!=(time_zone lhs, time_zone rhs) { return !(lhs == rhs); } class Impl; private: explicit time_zone(const Impl* impl) : impl_(impl) {} const Impl& effective_impl() const; // handles implicit UTC const Impl* impl_; }; // Loads the named time zone. May perform I/O on the initial load. // If the name is invalid, or some other kind of error occurs, returns // false and "*tz" is set to the UTC time zone. bool load_time_zone(const std::string& name, time_zone* tz); // Returns a time_zone representing UTC. Cannot fail. time_zone utc_time_zone(); // Returns a time zone that is a fixed offset (seconds east) from UTC. // Note: If the absolute value of the offset is greater than 24 hours // you'll get UTC (i.e., zero offset) instead. time_zone fixed_time_zone(const seconds& offset); // Returns a time zone representing the local time zone. Falls back to UTC. // Note: local_time_zone.name() may only be something like "localtime". time_zone local_time_zone(); // Returns the civil time (cctz::civil_second) within the given time zone at // the given absolute time (time_point). Since the additional fields provided // by the time_zone::absolute_lookup struct should rarely be needed in modern // code, this convert() function is simpler and should be preferred. template inline civil_second convert(const time_point& tp, const time_zone& tz) { return tz.lookup(tp).cs; } // Returns the absolute time (time_point) that corresponds to the given civil // time within the given time zone. If the civil time is not unique (i.e., if // it was either repeated or non-existent), then the returned time_point is // the best estimate that preserves relative order. That is, this function // guarantees that if cs1 < cs2, then convert(cs1, tz) <= convert(cs2, tz). inline time_point convert(const civil_second& cs, const time_zone& tz) { const time_zone::civil_lookup cl = tz.lookup(cs); if (cl.kind == time_zone::civil_lookup::SKIPPED) return cl.trans; return cl.pre; } namespace detail { using femtoseconds = std::chrono::duration; std::string format(const std::string&, const time_point&, const femtoseconds&, const time_zone&); bool parse(const std::string&, const std::string&, const time_zone&, time_point*, femtoseconds*, std::string* err = nullptr); template bool join_seconds( const time_point& sec, const femtoseconds& fs, time_point>>* tpp); template bool join_seconds( const time_point& sec, const femtoseconds& fs, time_point>>* tpp); template bool join_seconds( const time_point& sec, const femtoseconds& fs, time_point>>* tpp); bool join_seconds(const time_point& sec, const femtoseconds&, time_point* tpp); } // namespace detail // Formats the given time_point in the given cctz::time_zone according to // the provided format string. Uses strftime()-like formatting options, // with the following extensions: // // - %Ez - RFC3339-compatible numeric UTC offset (+hh:mm or -hh:mm) // - %E*z - Full-resolution numeric UTC offset (+hh:mm:ss or -hh:mm:ss) // - %E#S - Seconds with # digits of fractional precision // - %E*S - Seconds with full fractional precision (a literal '*') // - %E#f - Fractional seconds with # digits of precision // - %E*f - Fractional seconds with full precision (a literal '*') // - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999) // - %ET - The RFC3339 "date-time" separator "T" // // Note that %E0S behaves like %S, and %E0f produces no characters. In // contrast %E*f always produces at least one digit, which may be '0'. // // Note that %Y produces as many characters as it takes to fully render the // year. A year outside of [-999:9999] when formatted with %E4Y will produce // more than four characters, just like %Y. // // Tip: Format strings should include the UTC offset (e.g., %z, %Ez, or %E*z) // so that the resulting string uniquely identifies an absolute time. // // Example: // cctz::time_zone lax; // if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... } // auto tp = cctz::convert(cctz::civil_second(2013, 1, 2, 3, 4, 5), lax); // std::string f = cctz::format("%H:%M:%S", tp, lax); // "03:04:05" // f = cctz::format("%H:%M:%E3S", tp, lax); // "03:04:05.000" template inline std::string format(const std::string& fmt, const time_point& tp, const time_zone& tz) { const auto p = detail::split_seconds(tp); const auto n = std::chrono::duration_cast(p.second); return detail::format(fmt, p.first, n, tz); } // Parses an input string according to the provided format string and // returns the corresponding time_point. Uses strftime()-like formatting // options, with the same extensions as cctz::format(), but with the // exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez // and %E*z also accept the same inputs, which (along with %z) includes // 'z' and 'Z' as synonyms for +00:00. %ET accepts either 'T' or 't'. // // %Y consumes as many numeric characters as it can, so the matching data // should always be terminated with a non-numeric. %E4Y always consumes // exactly four characters, including any sign. // // Unspecified fields are taken from the default date and time of ... // // "1970-01-01 00:00:00.0 +0000" // // For example, parsing a string of "15:45" (%H:%M) will return a time_point // that represents "1970-01-01 15:45:00.0 +0000". // // Note that parse() returns time instants, so it makes most sense to parse // fully-specified date/time strings that include a UTC offset (%z, %Ez, or // %E*z). // // Note also that parse() only heeds the fields year, month, day, hour, // minute, (fractional) second, and UTC offset. Other fields, like weekday (%a // or %A), while parsed for syntactic validity, are ignored in the conversion. // // Date and time fields that are out-of-range will be treated as errors rather // than normalizing them like cctz::civil_second() would do. For example, it // is an error to parse the date "Oct 32, 2013" because 32 is out of range. // // A second of ":60" is normalized to ":00" of the following minute with // fractional seconds discarded. The following table shows how the given // seconds and subseconds will be parsed: // // "59.x" -> 59.x // exact // "60.x" -> 00.0 // normalized // "00.x" -> 00.x // exact // // Errors are indicated by returning false. // // Example: // const cctz::time_zone tz = ... // std::chrono::system_clock::time_point tp; // if (cctz::parse("%Y-%m-%d", "2015-10-09", tz, &tp)) { // ... // } template inline bool parse(const std::string& fmt, const std::string& input, const time_zone& tz, time_point* tpp) { time_point sec; detail::femtoseconds fs; return detail::parse(fmt, input, tz, &sec, &fs) && detail::join_seconds(sec, fs, tpp); } namespace detail { // Split a time_point into a time_point and a D subseconds. // Undefined behavior if time_point is not of sufficient range. // Note that this means it is UB to call cctz::time_zone::lookup(tp) or // cctz::format(fmt, tp, tz) with a time_point that is outside the range // of a 64-bit std::time_t. template std::pair, D> split_seconds(const time_point& tp) { auto sec = std::chrono::time_point_cast(tp); auto sub = tp - sec; if (sub.count() < 0) { sec -= seconds(1); sub += seconds(1); } return {sec, std::chrono::duration_cast(sub)}; } inline std::pair, seconds> split_seconds( const time_point& tp) { return {tp, seconds::zero()}; } // Join a time_point and femto subseconds into a time_point. // Floors to the resolution of time_point. Returns false if time_point // is not of sufficient range. template bool join_seconds( const time_point& sec, const femtoseconds& fs, time_point>>* tpp) { using D = std::chrono::duration>; // TODO(#199): Return false if result unrepresentable as a time_point. *tpp = std::chrono::time_point_cast(sec); *tpp += std::chrono::duration_cast(fs); return true; } template bool join_seconds( const time_point& sec, const femtoseconds&, time_point>>* tpp) { using D = std::chrono::duration>; auto count = sec.time_since_epoch().count(); if (count >= 0 || count % Num == 0) { count /= Num; } else { count /= Num; count -= 1; } if (count > (std::numeric_limits::max)()) return false; if (count < (std::numeric_limits::min)()) return false; *tpp = time_point() + D{static_cast(count)}; return true; } template bool join_seconds( const time_point& sec, const femtoseconds&, time_point>>* tpp) { using D = std::chrono::duration>; auto count = sec.time_since_epoch().count(); if (count > (std::numeric_limits::max)()) return false; if (count < (std::numeric_limits::min)()) return false; *tpp = time_point() + D{static_cast(count)}; return true; } inline bool join_seconds(const time_point& sec, const femtoseconds&, time_point* tpp) { *tpp = sec; return true; } } // namespace detail } // namespace cctz #endif // CCTZ_TIME_ZONE_H_ RcppCCTZ/inst/include/cctz/civil_time.h0000644000176200001440000003206314271014541017505 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef CCTZ_CIVIL_TIME_H_ #define CCTZ_CIVIL_TIME_H_ #include "cctz/civil_time_detail.h" namespace cctz { // The term "civil time" refers to the legally recognized human-scale time // that is represented by the six fields YYYY-MM-DD hh:mm:ss. Modern-day civil // time follows the Gregorian Calendar and is a time-zone-independent concept. // A "date" is perhaps the most common example of a civil time (represented in // this library as cctz::civil_day). This library provides six classes and a // handful of functions that help with rounding, iterating, and arithmetic on // civil times while avoiding complications like daylight-saving time (DST). // // The following six classes form the core of this civil-time library: // // * civil_second // * civil_minute // * civil_hour // * civil_day // * civil_month // * civil_year // // Each class is a simple value type with the same interface for construction // and the same six accessors for each of the civil fields (year, month, day, // hour, minute, and second, aka YMDHMS). These classes differ only in their // alignment, which is indicated by the type name and specifies the field on // which arithmetic operates. // // Each class can be constructed by passing up to six optional integer // arguments representing the YMDHMS fields (in that order) to the // constructor. Omitted fields are assigned their minimum valid value. Hours, // minutes, and seconds will be set to 0, month and day will be set to 1, and // since there is no minimum valid year, it will be set to 1970. So, a // default-constructed civil-time object will have YMDHMS fields representing // "1970-01-01 00:00:00". Fields that are out-of-range are normalized (e.g., // October 32 -> November 1) so that all civil-time objects represent valid // values. // // Each civil-time class is aligned to the civil-time field indicated in the // class's name after normalization. Alignment is performed by setting all the // inferior fields to their minimum valid value (as described above). The // following are examples of how each of the six types would align the fields // representing November 22, 2015 at 12:34:56 in the afternoon. (Note: the // string format used here is not important; it's just a shorthand way of // showing the six YMDHMS fields.) // // civil_second 2015-11-22 12:34:56 // civil_minute 2015-11-22 12:34:00 // civil_hour 2015-11-22 12:00:00 // civil_day 2015-11-22 00:00:00 // civil_month 2015-11-01 00:00:00 // civil_year 2015-01-01 00:00:00 // // Each civil-time type performs arithmetic on the field to which it is // aligned. This means that adding 1 to a civil_day increments the day field // (normalizing as necessary), and subtracting 7 from a civil_month operates // on the month field (normalizing as necessary). All arithmetic produces a // valid civil time. Difference requires two similarly aligned civil-time // objects and returns the scalar answer in units of the objects' alignment. // For example, the difference between two civil_hour objects will give an // answer in units of civil hours. // // In addition to the six civil-time types just described, there are // a handful of helper functions and algorithms for performing common // calculations. These are described below. // // Note: In C++14 and later, this library is usable in a constexpr context. // // CONSTRUCTION: // // Each of the civil-time types can be constructed in two ways: by directly // passing to the constructor up to six (optional) integers representing the // YMDHMS fields, or by copying the YMDHMS fields from a differently aligned // civil-time type. // // civil_day default_value; // 1970-01-01 00:00:00 // // civil_day a(2015, 2, 3); // 2015-02-03 00:00:00 // civil_day b(2015, 2, 3, 4, 5, 6); // 2015-02-03 00:00:00 // civil_day c(2015); // 2015-01-01 00:00:00 // // civil_second ss(2015, 2, 3, 4, 5, 6); // 2015-02-03 04:05:06 // civil_minute mm(ss); // 2015-02-03 04:05:00 // civil_hour hh(mm); // 2015-02-03 04:00:00 // civil_day d(hh); // 2015-02-03 00:00:00 // civil_month m(d); // 2015-02-01 00:00:00 // civil_year y(m); // 2015-01-01 00:00:00 // // m = civil_month(y); // 2015-01-01 00:00:00 // d = civil_day(m); // 2015-01-01 00:00:00 // hh = civil_hour(d); // 2015-01-01 00:00:00 // mm = civil_minute(hh); // 2015-01-01 00:00:00 // ss = civil_second(mm); // 2015-01-01 00:00:00 // // ALIGNMENT CONVERSION: // // The alignment of a civil-time object cannot change, but the object may be // used to construct a new object with a different alignment. This is referred // to as "realigning". When realigning to a type with the same or more // precision (e.g., civil_day -> civil_second), the conversion may be // performed implicitly since no information is lost. However, if information // could be discarded (e.g., civil_second -> civil_day), the conversion must // be explicit at the call site. // // void fun(const civil_day& day); // // civil_second cs; // fun(cs); // Won't compile because data may be discarded // fun(civil_day(cs)); // OK: explicit conversion // // civil_day cd; // fun(cd); // OK: no conversion needed // // civil_month cm; // fun(cm); // OK: implicit conversion to civil_day // // NORMALIZATION: // // Integer arguments passed to the constructor may be out-of-range, in which // case they are normalized to produce a valid civil-time object. This enables // natural arithmetic on constructor arguments without worrying about the // field's range. Normalization guarantees that there are no invalid // civil-time objects. // // civil_day d(2016, 10, 32); // Out-of-range day; normalized to 2016-11-01 // // Note: If normalization is undesired, you can signal an error by comparing // the constructor arguments to the normalized values returned by the YMDHMS // properties. // // PROPERTIES: // // All civil-time types have accessors for all six of the civil-time fields: // year, month, day, hour, minute, and second. Recall that fields inferior to // the type's alignment will be set to their minimum valid value. // // civil_day d(2015, 6, 28); // // d.year() == 2015 // // d.month() == 6 // // d.day() == 28 // // d.hour() == 0 // // d.minute() == 0 // // d.second() == 0 // // COMPARISON: // // Comparison always considers all six YMDHMS fields, regardless of the type's // alignment. Comparison between differently aligned civil-time types is // allowed. // // civil_day feb_3(2015, 2, 3); // 2015-02-03 00:00:00 // civil_day mar_4(2015, 3, 4); // 2015-03-04 00:00:00 // // feb_3 < mar_4 // // civil_year(feb_3) == civil_year(mar_4) // // civil_second feb_3_noon(2015, 2, 3, 12, 0, 0); // 2015-02-03 12:00:00 // // feb_3 < feb_3_noon // // feb_3 == civil_day(feb_3_noon) // // // Iterates all the days of February 2015. // for (civil_day d(2015, 2, 1); d < civil_month(2015, 3); ++d) { // // ... // } // // STREAMING: // // Each civil-time type may be sent to an output stream using operator<<(). // The output format follows the pattern "YYYY-MM-DDThh:mm:ss" where fields // inferior to the type's alignment are omitted. // // civil_second cs(2015, 2, 3, 4, 5, 6); // std::cout << cs << "\n"; // Outputs: 2015-02-03T04:05:06 // // civil_day cd(cs); // std::cout << cd << "\n"; // Outputs: 2015-02-03 // // civil_year cy(cs); // std::cout << cy << "\n"; // Outputs: 2015 // // ARITHMETIC: // // Civil-time types support natural arithmetic operators such as addition, // subtraction, and difference. Arithmetic operates on the civil-time field // indicated in the type's name. Difference requires arguments with the same // alignment and returns the answer in units of the alignment. // // civil_day a(2015, 2, 3); // ++a; // 2015-02-04 00:00:00 // --a; // 2015-02-03 00:00:00 // civil_day b = a + 1; // 2015-02-04 00:00:00 // civil_day c = 1 + b; // 2015-02-05 00:00:00 // int n = c - a; // n = 2 (civil days) // int m = c - civil_month(c); // Won't compile: different types. // // EXAMPLE: Adding a month to January 31. // // One of the classic questions that arises when considering a civil-time // library (or a date library or a date/time library) is this: "What happens // when you add a month to January 31?" This is an interesting question // because there could be a number of possible answers: // // 1. March 3 (or 2 if a leap year). This may make sense if the operation // wants the equivalent of February 31. // 2. February 28 (or 29 if a leap year). This may make sense if the operation // wants the last day of January to go to the last day of February. // 3. Error. The caller may get some error, an exception, an invalid date // object, or maybe false is returned. This may make sense because there is // no single unambiguously correct answer to the question. // // Practically speaking, any answer that is not what the programmer intended // is the wrong answer. // // This civil-time library avoids the problem by making it impossible to ask // ambiguous questions. All civil-time objects are aligned to a particular // civil-field boundary (such as aligned to a year, month, day, hour, minute, // or second), and arithmetic operates on the field to which the object is // aligned. This means that in order to "add a month" the object must first be // aligned to a month boundary, which is equivalent to the first day of that // month. // // Of course, there are ways to compute an answer the question at hand using // this civil-time library, but they require the programmer to be explicit // about the answer they expect. To illustrate, let's see how to compute all // three of the above possible answers to the question of "Jan 31 plus 1 // month": // // const civil_day d(2015, 1, 31); // // // Answer 1: // // Add 1 to the month field in the constructor, and rely on normalization. // const auto ans_normalized = civil_day(d.year(), d.month() + 1, d.day()); // // ans_normalized == 2015-03-03 (aka Feb 31) // // // Answer 2: // // Add 1 to month field, capping to the end of next month. // const auto next_month = civil_month(d) + 1; // const auto last_day_of_next_month = civil_day(next_month + 1) - 1; // const auto ans_capped = std::min(ans_normalized, last_day_of_next_month); // // ans_capped == 2015-02-28 // // // Answer 3: // // Signal an error if the normalized answer is not in next month. // if (civil_month(ans_normalized) != next_month) { // // error, month overflow // } // using civil_year = detail::civil_year; using civil_month = detail::civil_month; using civil_day = detail::civil_day; using civil_hour = detail::civil_hour; using civil_minute = detail::civil_minute; using civil_second = detail::civil_second; // An enum class with members monday, tuesday, wednesday, thursday, friday, // saturday, and sunday. These enum values may be sent to an output stream // using operator<<(). The result is the full weekday name in English with a // leading capital letter. // // weekday wd = weekday::thursday; // std::cout << wd << "\n"; // Outputs: Thursday // using detail::weekday; // Returns the weekday for the given civil-time value. // // civil_day a(2015, 8, 13); // weekday wd = get_weekday(a); // wd == weekday::thursday // using detail::get_weekday; // Returns the civil_day that strictly follows or precedes the given // civil_day, and that falls on the given weekday. // // For example, given: // // August 2015 // Su Mo Tu We Th Fr Sa // 1 // 2 3 4 5 6 7 8 // 9 10 11 12 13 14 15 // 16 17 18 19 20 21 22 // 23 24 25 26 27 28 29 // 30 31 // // civil_day a(2015, 8, 13); // get_weekday(a) == weekday::thursday // civil_day b = next_weekday(a, weekday::thursday); // b = 2015-08-20 // civil_day c = prev_weekday(a, weekday::thursday); // c = 2015-08-06 // // civil_day d = ... // // Gets the following Thursday if d is not already Thursday // civil_day thurs1 = next_weekday(d - 1, weekday::thursday); // // Gets the previous Thursday if d is not already Thursday // civil_day thurs2 = prev_weekday(d + 1, weekday::thursday); // using detail::next_weekday; using detail::prev_weekday; // Returns the day-of-year for the given civil-time value. // // civil_day a(2015, 1, 1); // int yd_jan_1 = get_yearday(a); // yd_jan_1 = 1 // civil_day b(2015, 12, 31); // int yd_dec_31 = get_yearday(b); // yd_dec_31 = 365 // using detail::get_yearday; } // namespace cctz #endif // CCTZ_CIVIL_TIME_H_ RcppCCTZ/inst/include/RcppCCTZ_API.h0000644000176200001440000000442214270471402016477 0ustar liggesusers #ifndef _RcppCCTZ_API_H #define _RcppCCTZ_API_H #include #include #include "cctz/civil_time.h" #include "cctz/time_zone.h" #ifdef HAVE_VISIBILITY_ATTRIBUTE # define attribute_hidden __attribute__ ((visibility ("hidden"))) #else # define attribute_hidden #endif #ifdef __cplusplus extern "C" { #endif /* provided the interface for the function exported in ../src/init.c via R_RegisterCCallable() */ inline int attribute_hidden RcppCCTZ_getOffset_nothrow(std::int_fast64_t s, const char* tzstr, int& offset) { typedef int GET_OFFSET_FUN(long long, const char*, int&); static GET_OFFSET_FUN *getOffset = (GET_OFFSET_FUN *) R_GetCCallable("RcppCCTZ", "_RcppCCTZ_getOffset_nothrow"); return getOffset(s, tzstr, offset); } inline int attribute_hidden RcppCCTZ_convertToCivilSecond_nothrow(const cctz::time_point& tp, const char* tzstr, cctz::civil_second& cs) { typedef int CONVERT_TO_CIVILSECOND(const cctz::time_point&, const char*, cctz::civil_second&); static CONVERT_TO_CIVILSECOND *convertToCivilSecond = (CONVERT_TO_CIVILSECOND*) R_GetCCallable("RcppCCTZ", "_RcppCCTZ_convertToCivilSecond_nothrow"); return convertToCivilSecond(tp, tzstr, cs); } inline int attribute_hidden RcppCCTZ_convertToTimePoint_nothrow(const cctz::civil_second& cs, const char* tzstr, cctz::time_point& tp) { typedef int CONVERT_TO_TIMEPOINT(const cctz::civil_second&, const char*, cctz::time_point&); static CONVERT_TO_TIMEPOINT *convertToTimePoint = (CONVERT_TO_TIMEPOINT*) R_GetCCallable("RcppCCTZ", "_RcppCCTZ_convertToTimePoint_nothrow"); return convertToTimePoint(cs, tzstr, tp); } #ifdef __cplusplus } // add a namespace for C++ use namespace RcppCCTZ { inline int getOffset(std::int_fast64_t s, const char* tzstr, int& offset) { return RcppCCTZ_getOffset_nothrow(s, tzstr, offset); } inline int convertToCivilSecond(const cctz::time_point& tp, const char* tzstr, cctz::civil_second& cs) { return RcppCCTZ_convertToCivilSecond_nothrow(tp, tzstr, cs); } inline int convertToTimePoint(const cctz::civil_second& cs, const char* tzstr, cctz::time_point& tp) { return RcppCCTZ_convertToTimePoint_nothrow(cs, tzstr, tp); } } #endif #endif /* !_RcppCCTZ_API_H */ RcppCCTZ/inst/tinytest/0000755000176200001440000000000013521272406014504 5ustar liggesusersRcppCCTZ/inst/tinytest/test_tz.R0000644000176200001440000000273313521272406016330 0ustar liggesusers ## Copyright (C) 2018 - 2019 Dirk Eddelbuettel ## ## This file is part of RcppCCTZ. ## ## RcppCCTZ 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. ## ## RcppCCTZ 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 RcppCCTZ. If not, see . library(RcppCCTZ) isSolaris <- Sys.info()[["sysname"]] == "SunOS" timepoint <- ISOdatetime(2010,1,2,3,4,5) if (!isSolaris) { nyc2lon <- toTz(timepoint, "America/New_York", "Europe/London") h <- as.integer(difftime(nyc2lon, timepoint, units="hour")) expect_equal(h, 5L) } moonland <- ISOdatetime(1969,7,20,22,56,0,tz="UTC") if (!isSolaris) { oz <- toTz(moonland, "America/New_York", "Australia/Sydney") txt <- format(oz, tz="Australia/Sydney") expect_equal(txt, "1969-07-21 12:56:00") } timepoint <- ISOdatetime(2010,1,2,3,4,5) if (!isSolaris) { chi2lon <- tzDiff("America/Chicago", "Europe/London", timepoint) expect_equal(chi2lon, 6L) nyc2lon <- tzDiff("America/New_York", "Europe/London", timepoint) expect_equal(nyc2lon, 5L) } RcppCCTZ/inst/tinytest/test_format.R0000644000176200001440000000256013521272406017161 0ustar liggesusers ## Copyright (C) 2018 - 2019 Dirk Eddelbuettel ## ## This file is part of RcppCCTZ. ## ## RcppCCTZ 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. ## ## RcppCCTZ 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 RcppCCTZ. If not, see . library(RcppCCTZ) timepoint <- ISOdatetime(2010,1,2,3,4,5, tz="UTC") txt <- formatDatetime(timepoint, tgttzstr="UTC") expect_equal(txt, "2010-01-02T03:04:05+00:00") txt <- formatDouble(as.numeric(timepoint), 0, tgttzstr="UTC") expect_equal(txt, "2010-01-02T03:04:05+00:00") txt <- formatDouble(as.numeric(timepoint), 123456789, tgttzstr="UTC") expect_equal(txt, "2010-01-02T03:04:05.123456789+00:00") timepoint <- ISOdatetime(2010,1,2,3,4,5, tz="UTC") txt <- RcppCCTZ:::formatDouble(rep(timepoint, 3), c(1001, 2002, 3003)) vals <- RcppCCTZ:::parseDouble(txt) c1 <- rep(1262401445, 3) c2 <- c(1001, 2002, 3003) expect_equal(vals[,1], c1) expect_equal(vals[,2], c2) RcppCCTZ/inst/tinytest/test_parse.R0000644000176200001440000000302413521272406016777 0ustar liggesusers ## Copyright (C) 2018 - 2019 Dirk Eddelbuettel ## ## This file is part of RcppCCTZ. ## ## RcppCCTZ 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. ## ## RcppCCTZ 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 RcppCCTZ. If not, see . library(RcppCCTZ) ds <- getOption("digits.secs") options(digits.secs=6) # max value timepoint <- ISOdatetime(2010,1,2,3,4,5, tz="UTC") pt <- parseDatetime("2010-01-02 03:04:05", "%Y-%m-%d %H:%M:%S", "UTC") expect_equal(timepoint, pt) pt <- parseDouble("2010-01-02 03:04:05", "%Y-%m-%d %H:%M:%S") expect_equal(as.numeric(timepoint), pt[1,1]) timepoint <- ISOdatetime(2010,1,2,3,4,5.123456, tz="UTC") pt <- parseDatetime("2010-01-02 03:04:05.123456", "%Y-%m-%d %H:%M:%E*S", "UTC") expect_equal(timepoint, pt) pt <- parseDouble("2010-01-02 03:04:05.123456", "%Y-%m-%d %H:%M:%E*S") expect_equal(as.numeric(timepoint), pt[1,1]) timepoints <- ISOdatetime(2010,1,2,3,4,5, tz="UTC") + 0:9 tpvec <- format(timepoints) ptvec <- parseDatetime(tpvec, "%Y-%m-%d %H:%M:%S", "UTC") expect_equal(timepoints, ptvec) options(digits.secs=ds) RcppCCTZ/inst/NEWS.Rd0000644000176200001440000001473614726436425013712 0ustar liggesusers\name{NEWS} \title{News for Package \pkg{RcppCCTZ}} \newcommand{\ghpr}{\href{https://github.com/eddelbuettel/rcppcctz/pull/#1}{##1}} \newcommand{\ghit}{\href{https://github.com/eddelbuettel/rcppcctz/issues/#1}{##1}} \section{Changes in version 0.2.13 (2024-12-11)}{ \itemize{ \item No longer set compilation standard as recent R version set a sufficiently high minimum \item Qualify a call to \code{cctz::format} (Michael Quinn in \ghpr{44}) \item Routine updates to continuous integration and badges \item Switch to Authors@R in DESCRIPTION } } \section{Changes in version 0.2.12 (2022-11-06)}{ \itemize{ \item Support NA values in numerical or character input \item GitHub Actions were updated to checkout version 3. } } \section{Changes in version 0.2.11 (2022-08-06)}{ \itemize{ \item More specific includes in RcppCCTZ_API.h (Jing Lu in \ghpr{42} closing \ghit{41}). \item Synchronized with upstream CCTZ (Dirk in \ghpr{43}). \item Switched \href{https://eddelbuettel.github.io/r-ci/}{r-ci} to \href{https://eddelbuettel.github.io/r2u/}{r2u} use. } } \section{Changes in version 0.2.10 (2021-12-14)}{ \itemize{ \item Switch CI use to \href{https://eddelbuettel.github.io/r-ci/}{r-ci} \item Applied patch by Tomas Kalibera for Windows UCRT under the upcoming R 4.2.0 expected for April. } } \section{Changes in version 0.2.9 (2020-08-30)}{ \itemize{ \item Provide a header \code{RcppCCZT_API.h} for client packages. \item Show a simple example of parsing a YYYYMMDD HHMMSS.FFFFFF date. } } \section{Changes in version 0.2.8 (2020-08-04)}{ \itemize{ \item Added three new \code{nothrow} variants (needed for win32) needed by the expanded \pkg{nanotime} package (Leonardo in \ghpr{37}) } } \section{Changes in version 0.2.7 (2020-03-18)}{ \itemize{ \item Added functions \code{_RcppCCTZ_convertToCivilSecond} that converts a time point to the number of seconds since epoch, and \code{_RcppCCTZ_convertToTimePoint} that converts a number of seconds since epoch into a time point; these functions are only callable from C level (Leonardo in \ghpr{34} and \ghpr{35}). \item Added function \code{_RcppCCTZ_getOffset} that returns the offset at a speficied time-point for a specified timezone; this function is only callable from C level (Leonardo in \ghpr{32}). } } \section{Changes in version 0.2.6 (2019-08-03)}{ \itemize{ \item Synchronized with upstream CCTZ release 2.3 plus commits accrued since then (Dirk in \ghpr{30}). \item The package now uses \pkg{tinytest} for unit tests (Dirk in \ghpr{31}). } } \section{Changes in version 0.2.5 (2018-10-14)}{ \itemize{ \item Parsing to \code{Datetime} was corrected on systems that do not have nanosecond support in C++11 chrono (\ghpr{28}). \item \code{DatetimeVector} objects are now created with their timezone attribute when available. \item The \code{toTz} functions is now vectorized (\ghpr{29}). \item More unit tests were added, and some conditioning on Solaris (mostly due to missing timezone info) was removed. } } \section{Changes in version 0.2.4 (2018-10-06)}{ \itemize{ \item An unused \code{main()} in \code{src/time_tool.cc} was \code{#ifdef}'ed away to please another compiler/OS combination. \item The \code{tzDiff} function now supports a vector argument (\ghpr{24}). \item An unnecessary \code{#include} was removed (\ghpr{25}). \item Some tests are not conditioning on Solaris to not fail there (\ghpr{26}). \item The CCTZ code was updated to the newest upstream version (\ghpr{27}). \item Unit tests now use the \pkg{RUnit} package replacing a simpler tests script. } } \section{Changes in version 0.2.3 (2017-06-19)}{ \itemize{ \item On Windows, the \code{TZDIR} environment variable is now set in \code{.onLoad()} \item Replaced \code{init.c} with registration code inside of \code{RcppExports.cpp} thanks to Rcpp 0.12.11. } } \section{Changes in version 0.2.2 (2017-04-20)}{ \itemize{ \item Synchronized with upstream CCTZ \item The \code{time_point} object is instantiated explicitly for nanosecond use which appears to be required on macOS } } \section{Changes in version 0.2.1 (2017-02-04)}{ \itemize{ \item Conversion from timepoint to two \code{double} values now rounds correctly (\ghpr{14} closing \ghit{12}, with thanks to Leonardo) \item The Description was expanded to stress the need for a modern C++11 compiler; g++-4.8 (as on 'trusty' eg in Travis CI) works \item Travis CI is now driven via \code{run.sh} from our fork } } \section{Changes in version 0.2.0 (2017-01-08)}{ \itemize{ \item Windows compilation was enabled by defining \code{OFFSET()} and \code{ABBR()} for MinGW (\ghpr{10} partially addressing \ghit{9}) \item Windows use completed with backport of \code{std::get_time} from LLVM's libc++ to enable \code{strptime} semantics (Dan Dillon in \ghpr{11} completing \ghit{9}) \item Timezone information on Windows is supplied via R's own copy of zoneinfo with \code{TZDIR} set (also \ghpr{10}) \item The interface to \code{formatDouble} was cleaned up } } \section{Changes in version 0.1.0 (2016-12-11)}{ \itemize{ \item Synchronized with \code{CCTZ} upstream. \item New parsing and formating helpers for Datetime vectors \item New parsing and formating helpers for (two) \code{double} vectors representing full \code{std::chrono} nanosecond resolutions \item Updated documentation and examples. } } \section{Changes in version 0.0.5 (2016-07-09)}{ \itemize{ \item New utility example functions \code{toTz()} and \code{tzDiff} \item Synchronized with small upstream change for additional \code{#ifdef} for compiler differentiation } } \section{Changes in version 0.0.4 (2016-04-17)}{ \itemize{ \item Synchronized with \code{CCTZ} v2 upstream. \item Updated \code{examples.cpp} accordingly } } \section{Changes in version 0.0.3 (2016-01-17)}{ \itemize{ \item Synchronized with \code{CCTZ} upstream. } } \section{Changes in version 0.0.2 (2015-12-02)}{ \itemize{ \item Additional \code{#ifdef} statements suggested by Bradley White in \code{CCTZ} ticket #5 permitting compilation on Solaris -- with thanks to Jeroen for testing our branch. } } \section{Changes in version 0.0.1 (2015-12-01)}{ \itemize{ \item Initial CRAN upload. \item Package is functional and provides examples. } } RcppCCTZ/README.md0000644000176200001440000001101014622640027013115 0ustar liggesusers## RcppCCTZ: Rcpp bindings for [CCTZ](https://github.com/google/cctz) [![CI](https://github.com/eddelbuettel/rcppcctz/workflows/ci/badge.svg)](https://github.com/eddelbuettel/rcppcctz/actions?query=workflow%3Aci) [![License](https://eddelbuettel.github.io/badges/GPL2+.svg)](https://www.gnu.org/licenses/gpl-2.0.html) [![CRAN](https://www.r-pkg.org/badges/version/RcppCCTZ)](https://cran.r-project.org/package=RcppCCTZ) [![Dependencies](https://tinyverse.netlify.app/badge/RcppCCTZ)](https://cran.r-project.org/package=RcppCCTZ) [![Downloads](https://cranlogs.r-pkg.org/badges/RcppCCTZ?color=brightgreen)](https://www.r-pkg.org/pkg/RcppCCTZ) [![Last Commit](https://img.shields.io/github/last-commit/eddelbuettel/rcppcctz)](https://github.com/eddelbuettel/rcppcctz) ### What is CCTZ? [CCTZ](https://github.com/google/cctz) (C++ Time Zone) is an excellent (yet small) C++11 library for translating between absolute times and civil times using the rules defined by a time zone. See its [repository](https://github.com/google/cctz) (as well as code) for very detailed documentation. [CCTZ](https://github.com/google/cctz) is being developed by Google but not an officially endorsed product. ### What is RcppCCTZ? This package wraps CCTZ for use by R via [Rcpp](https://dirk.eddelbuettel.com/code/rcpp.html). ### Examples #### Difference between Timezones ```r R> # simple call: difference now R> tzDiff("America/New_York", "Europe/London", Sys.time()) [1] 5 R> # tabulate difference for every week of the year R> table(sapply(0:52, function(d) tzDiff("America/New_York", "Europe/London", + as.POSIXct(as.Date("2016-01-01") + d*7)))) 4 5 3 50 ``` #### Shifting Timezone ```r R> # Given current time in NY what is the time London, UK R> # (running the code locally in Chicago hence CST text format) R> toTz(Sys.time(), "America/New_York", "Europe/London") [1] "2016-12-10 17:15:04.20370 CST" R> # this redoes the 'Armstrong on the moon in NYC and Sydney' example R> # note that the default print method will print the return object in _your local time_ R> toTz(ISOdatetime(1969,7,20,22,56,0,tz="UTC"), "America/New_York", "Australia/Sydney", TRUE) 1969-07-20 22:56:00 -0400 1969-07-21 12:56:00 +1000 [1] "1969-07-20 21:56:00 CDT" R> # whereas explicitly formating for Sydney time does the right thing R> format(toTz(ISOdatetime(1969,7,20,22,56,0,tz="UTC"), + "America/New_York", "Australia/Sydney", verbose=TRUE), + tz="Australia/Sydney") 1969-07-20 22:56:00 -0400 1969-07-21 12:56:00 +1000 [1] "1969-07-21 12:56:00" ``` #### Parsing and Formatting ```r R> now <- Sys.time() R> formatDatetime(now) # current (UTC) time, in full precision RFC3339 [1] "2016-12-10T18:23:03.327956+00:00" R> formatDatetime(now, tgttzstr="America/New_York") # same but in NY [1] "2016-12-10T13:23:03.327956-05:00" R> formatDatetime(now + 0:4) # vectorised [1] "2016-12-10T18:23:03.327956+00:00" "2016-12-10T18:23:04.327956+00:00" [3] "2016-12-10T18:23:05.327956+00:00" "2016-12-10T18:23:06.327956+00:00" [5] "2016-12-10T18:23:07.327956+00:00" R> R> ds <- getOption("digits.secs") R> options(digits.secs=6) # max value R> parseDatetime("2016-12-07 10:11:12", "%Y-%m-%d %H:%M:%S"); # full seconds [1] "2016-12-07 04:11:12 CST" R> parseDatetime("2016-12-07 10:11:12.123456", "%Y-%m-%d %H:%M:%E*S"); # fractional seconds [1] "2016-12-07 04:11:12.123456 CST" R> parseDatetime("2016-12-07T10:11:12.123456-00:00") ## default RFC3339 format [1] "2016-12-07 04:11:12.123456 CST" R> now <- trunc(Sys.time()) R> parseDatetime(formatDatetime(now + 0:4)) # vectorised [1] "2016-12-10 12:24:25 CST" "2016-12-10 12:24:26 CST" "2016-12-10 12:24:27 CST" [4] "2016-12-10 12:24:28 CST" "2016-12-10 12:24:29 CST" R> options(digits.secs=ds) ``` ### Requirements The [CCTZ](https://github.com/google/cctz) library depends on timezone files typically found in `/usr/share/zoneinfo` which requires a Unix-alike OS such as Linux or OS X. Old school Unix variants may work. ### Status On [CRAN](https://cran.r-project.org/package=RcppCCTZ), builds and tests cleanly, and the example functions are accessible from R. ### Installation The package is now on [CRAN](https://cran.r-project.org) and can be installed via a standard ```r install.packages("RcppCCTZ") ``` ### Continued Testing As we rely on the [tinytest](https://cran.r-project.org/package=tinytest) package, the already-installed package can also be verified via ```r tinytest::test_package("RcppCCTZ") ``` at any later point. ### Author Dirk Eddelbuettel ### License GPL (>= 2) RcppCCTZ/man/0000755000176200001440000000000013360652162012421 5ustar liggesusersRcppCCTZ/man/RcppCCTZ-package.Rd0000644000176200001440000000315113034452051015662 0ustar liggesusers\name{RcppCCTZ-package} \alias{RcppCCTZ-package} \alias{RcppCCTZ} \alias{example0} \alias{example1} \alias{example2} \alias{example3} \alias{example4} \alias{helloMoon} \alias{exampleFormat} \docType{package} \title{ A Simple Wrapper to the CCTZ Library for Time Zone Calculations } \description{ CCTZ contains two underlying libraries which build on the C++11 library \code{chrono}. The first covers \emph{civil time} for computing with human-scale time such as dates and time. It is header-only. The second covers time zones and allow translation between absolute time and civil time. RcppCCTZ brings CCTZ to R by means of Rcpp. } \details{ CCTZ requires a valid timezone library as well as recent-enough compiler to cope with C++11. Windows is supported since version 0.2.0 via the \code{g++-4.9} compiler, but note that it provides an \emph{incomplete} C++11 library. The \code{std::get_time} function was ported from libc++ of the LLVM to enable this. However, string formatting is more limited as the libc++ library used by \code{g++-4.9} does not provide complete C++11 semantics. As one example, CCTZ frequently uses \code{"\%F \%T"} which do not work on Windows; one has to use \code{"\%Y-\%m-\%d \%H:\%M:\%S"}. } \author{ Dirk Eddelbuettel wrote the package; Dan Dillon ported \code{std::get_time} from LLVM's libc++; Bradley White and Greg Miller wrote the underlying CCTZ library. Maintainer: Dirk Eddelbuettel } \references{ The CCZT repository at \url{https://github.com/google/cctz} has additional information. } \keyword{ package } \examples{ helloMoon() } RcppCCTZ/man/tzDiff.Rd0000644000176200001440000000234113355133050014130 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/RcppExports.R \name{tzDiff} \alias{tzDiff} \title{Return difference between two time zones at a given date.} \usage{ tzDiff(tzfrom, tzto, dt, verbose = FALSE) } \arguments{ \item{tzfrom}{The first time zone as a character vector.} \item{tzto}{The second time zone as a character vector.} \item{dt}{A Datetime object specifying when the difference is to be computed.} \item{verbose}{A boolean toggle indicating whether more verbose operations are desired, default is \code{FALSE}.} } \value{ A numeric value with the difference (in hours) between the first and second time zone at the given date } \description{ Difference between two given timezones at a specified date. } \details{ Time zone offsets vary by date, and this helper function computes the difference (in hours) between two time zones for a given date time. } \examples{ \dontrun{ # simple call: difference now tzDiff("America/New_York", "Europe/London", Sys.time()) # tabulate difference for every week of the year table(sapply(0:52, function(d) tzDiff("America/New_York", "Europe/London", as.POSIXct(as.Date("2016-01-01") + d*7)))) } } \author{ Dirk Eddelbuettel } RcppCCTZ/man/formatDatetime.Rd0000644000176200001440000000351413355133050015652 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/RcppExports.R \name{formatDatetime} \alias{formatDatetime} \alias{formatDouble} \title{Format a Datetime vector as a string vector} \usage{ formatDatetime(dtv, fmt = "\%Y-\%m-\%dT\%H:\%M:\%E*S\%Ez", lcltzstr = "UTC", tgttzstr = "UTC") formatDouble(secv, nanov, fmt = "\%Y-\%m-\%dT\%H:\%M:\%E*S\%Ez", tgttzstr = "UTC") } \arguments{ \item{dtv}{A Datetime vector object to be formatted} \item{fmt}{A string with the format, which is based on \code{strftime} with some extensions; see the CCTZ documentation for details.} \item{lcltzstr}{The local timezone object for creation the CCTZ timepoint} \item{tgttzstr}{The target timezone for the desired format} \item{secv}{A numeric vector with seconds since the epoch} \item{nanov}{A numeric vector with nanoseconds since the epoch, complementing \code{secv}.} } \value{ A string vector with the requested format of the datetime objects } \description{ Format a Datetime vector } \details{ An alternative to \code{format.POSIXct} based on the CCTZ library. The \code{formatDouble} variant uses two vectors for seconds since the epoch and fractional nanoseconds, respectively, to provide fuller resolution. } \section{Note}{ Windows is now supported via the \code{g++-4.9} compiler, but note that it provides an \emph{incomplete} C++11 library. This means we had to port a time parsing routine, and that string formatting is more limited. As one example, CCTZ frequently uses \code{"\%F \%T"} which do not work on Windows; one has to use \code{"\%Y-\%m-\%d \%H:\%M:\%S"}. } \examples{ \dontrun{ now <- Sys.time() formatDatetime(now) # current (UTC) time, in full precision RFC3339 formatDatetime(now, tgttzstr="America/New_York") # same but in NY formatDatetime(now + 0:4) # vectorised } } \author{ Dirk Eddelbuettel } RcppCCTZ/man/parseDatetime.Rd0000644000176200001440000000270313722071253015477 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/RcppExports.R \name{parseDatetime} \alias{parseDatetime} \alias{parseDouble} \title{Parse a Datetime vector from a string vector} \usage{ parseDatetime(svec, fmt = "\%Y-\%m-\%dT\%H:\%M:\%E*S\%Ez", tzstr = "UTC") parseDouble(svec, fmt = "\%Y-\%m-\%dT\%H:\%M:\%E*S\%Ez", tzstr = "UTC") } \arguments{ \item{svec}{A string vector from which a Datetime vector is to be parsed} \item{fmt}{A string with the format, which is based on \code{strftime} with some extensions; see the CCTZ documentation for details.} \item{tzstr}{The local timezone for the desired format} } \value{ A Datetime vector object for \code{parseDatetime}, a numeric matrix with two columns for seconds and nanoseconds for \code{parseDouble} } \description{ Parse a Datetime vector } \details{ An alternative to \code{as.POSIXct} based on the CCTZ library } \examples{ ds <- getOption("digits.secs") options(digits.secs=6) # max value parseDatetime("2016-12-07 10:11:12", "\%Y-\%m-\%d \%H:\%M:\%S") # full seconds parseDatetime("2016-12-07 10:11:12.123456", "\%Y-\%m-\%d \%H:\%M:\%E*S") # fractional seconds parseDatetime("2016-12-07T10:11:12.123456-00:00") ## default RFC3339 format parseDatetime("20161207 101112.123456", "\%E4Y\%m\%d \%H\%M\%E*S") # fractional seconds now <- trunc(Sys.time()) parseDatetime(formatDatetime(now + 0:4)) # vectorised options(digits.secs=ds) } \author{ Dirk Eddelbuettel } RcppCCTZ/man/toTz.Rd0000644000176200001440000000253613360652162013656 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/RcppExports.R \name{toTz} \alias{toTz} \title{Shift datetime object from one timezone to another} \usage{ toTz(dtv, tzfrom, tzto, verbose = FALSE) } \arguments{ \item{dtv}{A DatetimeVector object specifying when the difference is to be computed.} \item{tzfrom}{The first time zone as a character vector.} \item{tzto}{The second time zone as a character vector.} \item{verbose}{A boolean toggle indicating whether more verbose operations are desired, default is \code{FALSE}.} } \value{ A DatetimeVector object with the given (civil time) determined by the incoming object (and its timezone) shifted to the target timezone. } \description{ Change from one given timezone to another. } \details{ Time zone offsets vary by date, and this helper function converts a Datetime object from one given timezone to another. } \examples{ \dontrun{ toTz(Sys.time(), "America/New_York", "Europe/London") # this redoes the 'Armstrong on the moon in NYC and Sydney' example toTz(ISOdatetime(1969,7,20,22,56,0,tz="UTC"), "America/New_York", "Australia/Sydney", verbose=TRUE) # we can also explicitly format for Sydney time format(toTz(ISOdatetime(1969,7,20,22,56,0,tz="UTC"), "America/New_York", "Australia/Sydney", verbose=TRUE), tz="Australia/Sydney") } } \author{ Dirk Eddelbuettel } RcppCCTZ/DESCRIPTION0000755000176200001440000000316614726464450013375 0ustar liggesusersPackage: RcppCCTZ Type: Package Title: 'Rcpp' Bindings for the 'CCTZ' Library Version: 0.2.13 Date: 2024-12-11 Authors@R: c(person("Dirk", "Eddelbuettel", role = c("aut", "cre"), email = "edd@debian.org", comment = c(ORCID = "0000-0001-6419-907X")), person("Bradley", "White", role = "aut", comment = "Principal author of CCTZ")) Description: 'Rcpp' Access to the 'CCTZ' timezone library is provided. 'CCTZ' is a C++ library for translating between absolute and civil times using the rules of a time zone. The 'CCTZ' source code, released under the Apache 2.0 License, is included in this package. See for more details. License: GPL (>= 2) Imports: Rcpp (>= 0.11.0) Suggests: tinytest LinkingTo: Rcpp SystemRequirements: A 64-bit POSIX OS such as Linux or OS X with IANA time zone data in /usr/share/zoneinfo as well as a recent-enough C++11 compiler (such as g++-4.9 or later which is preferred, g++-4.8 works too). On Windows the zoneinfo included with R is used; and time parsing support is enabled via a backport of std::get_time from the LLVM libc++ library. URL: https://github.com/eddelbuettel/rcppcctz, https://dirk.eddelbuettel.com/code/rcpp.cctz.html BugReports: https://github.com/eddelbuettel/rcppcctz/issues RoxygenNote: 6.0.1 NeedsCompilation: yes Packaged: 2024-12-12 01:32:20 UTC; edd Author: Dirk Eddelbuettel [aut, cre] (), Bradley White [aut] (Principal author of CCTZ) Maintainer: Dirk Eddelbuettel Repository: CRAN Date/Publication: 2024-12-12 04:40:08 UTC