diff --git a/DESCRIPTION b/DESCRIPTION index 6b31f66..035c3aa 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -43,4 +43,4 @@ VignetteBuilder: Encoding: UTF-8 LazyData: true Roxygen: list(markdown = TRUE) -RoxygenNote: 7.1.1 +RoxygenNote: 7.3.1 diff --git a/R/ic_dataframe.R b/R/ic_dataframe.R index e874f7d..02b301d 100644 --- a/R/ic_dataframe.R +++ b/R/ic_dataframe.R @@ -19,6 +19,7 @@ ic_dataframe <- function(x) { if(methods::is(object = x, class2 = "data.frame")) { return(x) + } stopifnot(methods::is(object = x, class2 = "character") | methods::is(object = x, class2 = "list")) @@ -35,16 +36,27 @@ ic_dataframe <- function(x) { x_df <- ic_bind_list(x_list_named) date_cols <- grepl(pattern = "VALUE=DATE", x = names(x_df)) + datetime_cols <- names(x_df) %in% grep("^DT[A-Z]+$|CREATED|LAST-MODIFIED", names(x_df), value = TRUE) # include any column starting with DT + tzid_cols <- names(x_df) %in% grep(".*TZID=.*", names(x_df), value = TRUE) # find cols with TZID in name + timezones <- unlist(regmatches(names(x_df), gregexpr("(?<=TZID=).*", names(x_df), perl = TRUE))) # pull all tzones from col names into vector to apply separately to each column + # in case different events have differing tzones although + # think most calendar software only uses single tzone + if(any(date_cols)) { - x_df[date_cols] <- lapply(x_df[, date_cols], ic_date) + x_df[date_cols] <- lapply(x_df[date_cols], ic_date) } - datetime_cols <- names(x_df) %in% c("DTSTART", "DTEND") + if(any(datetime_cols)) { - x_df[datetime_cols] <- lapply(x_df[, datetime_cols], ic_datetime) + if (any(tzid_cols)) { + x_df[tzid_cols] <- Map(function(x, y) ic_datetime(x, tzone = y), x_df[tzid_cols], timezones) # apply timezones to tzid_cols + x_df[tzid_cols] <- lapply(x_df[tzid_cols], function(x) {attr(x, "tzone") <- ""; x}) # change tzid_cols to local time zone + x_df[datetime_cols & !tzid_cols] <- lapply(x_df[datetime_cols & !tzid_cols], ic_datetime) # set time zone on datetime cols without TZID to local ic_datetime() does this by default + } else { + x_df[datetime_cols] <- lapply(x_df[datetime_cols], ic_datetime) + } } - - # names(x_df) <- gsub(pattern = ".VALUE.DATE", replacement = "", names(x_df)) - + names(x_df) <- gsub(pattern = ";VALUE=DATE", replacement = "", names(x_df)) + names(x_df) <- gsub(pattern = ";TZID.*", replacement = "", names(x_df)) x_df } diff --git a/R/ic_date.R b/R/ic_date.R index 7814202..f8fab81 100644 --- a/R/ic_date.R +++ b/R/ic_date.R @@ -1,13 +1,18 @@ #' Convert ical datetime into R datetime -#' Z at the end of an ical stamp stands of Zulu time +#' +#' @description +#' Convert ical datetime into R datetime. Z at the end of an ical stamp stands of Zulu time +#' #' https://en.wikipedia.org/wiki/Coordinated_Universal_Time#Time_zones -#' which is UTC = GMT https://greenwichmeantime.com/info/zulu/ +#' which is UTC = GMT https://greenwichmeantime.com/articles/history/zulu/ #' @inheritParams ic_find +#' @param tzone a character string. Time zone specification to be used for the conversion if datetime not Zulu time. +#' Defaults to `""` which is the current local system time zone. See \link[base]{OlsonNames} for list of time zones. #' @export #' @examples #' ic_datetime("20180809T160000Z") #' ic_date("20120103") -ic_datetime <- function(x) { +ic_datetime <- function(x, tzone = "") { # allow pass through of time zone to as.POSIXct # TODO (LH): regex check x timestamp if(any(!is.na(x) & !(x == "NA") & !grepl("^\\d{8}T\\d{6}Z?$", x))) { @@ -18,13 +23,13 @@ ic_datetime <- function(x) { plain <- gsub("[TZtz]", "", x) - # if time string has a trailing "Z" assign to zulu timezone + # if time string has a trailing "Z" assign to zulu timezone if (any(grepl("Z$", x))) { datetime <- as.POSIXct(plain, tz = "Zulu", format = "%Y%m%d%H%M%S") - attr(datetime, "tzone") <- "" # change tz to "" which defaults to local system timezone; this could be left out if not desired + attr(datetime, "tzone") <- "" # change tz to "" which defaults to local system timezone; this could be left out if not desired # but as.POSIXct() uses tz = "" as standard argument and this is what is used below if not zulu time } else { - datetime <- as.POSIXct(plain, format = "%Y%m%d%H%M%S") + datetime <- as.POSIXct(plain, tz = tzone, format = "%Y%m%d%H%M%S") } datetime } diff --git a/inst/extdata/apple_calendar_test.ics b/inst/extdata/apple_calendar_test.ics new file mode 100644 index 0000000..ca69420 --- /dev/null +++ b/inst/extdata/apple_calendar_test.ics @@ -0,0 +1,36 @@ +BEGIN:VCALENDAR +CALSCALE:GREGORIAN +PRODID:-//Apple Inc.//macOS 13.4.1//EN +VERSION:2.0 +X-WR-CALNAME:Test +BEGIN:VTIMEZONE +TZID:Europe/London +BEGIN:DAYLIGHT +DTSTART:19810329T010000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +TZNAME:BST +TZOFFSETFROM:+0000 +TZOFFSETTO:+0100 +END:DAYLIGHT +BEGIN:STANDARD +DTSTART:19961027T020000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +TZNAME:GMT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0000 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20230320T213321Z +DESCRIPTION:Testing : 00:00\n +DTEND;TZID=Pacific/Auckland:20230630T160000 +DTSTAMP:20230709T221931Z +DTSTART;TZID=Pacific/Auckland:20230630T120000 +LAST-MODIFIED:20230320T213321Z +LOCATION:London +SEQUENCE:0 +SUMMARY:This is test data +TRANSP:OPAQUE +UID:5C15BDCE-C77A-4A67-BBD3-8D5AD233A034 +END:VEVENT +END:VCALENDAR diff --git a/man/calendar.Rd b/man/calendar.Rd index c0512d4..7312a90 100644 --- a/man/calendar.Rd +++ b/man/calendar.Rd @@ -2,6 +2,7 @@ % Please edit documentation in R/package.R \docType{package} \name{calendar} +\alias{calendar-package} \alias{calendar} \title{ics files with R} \description{ @@ -12,3 +13,27 @@ Files adhering to this standard are save as \code{.ics} files. \details{ The \code{ical} package is for interacting with such files } +\seealso{ +Useful links: +\itemize{ + \item \url{https://github.com/atfutures/calendar} + \item \url{https://atfutures.github.io/calendar/} + \item \url{https://github.com/ATFutures/calendar} + \item Report bugs at \url{https://github.com/ATFutures/calendar/issues} +} + +} +\author{ +\strong{Maintainer}: Robin Lovelace \email{rob00x@gmail.com} (\href{https://orcid.org/0000-0001-5679-6536}{ORCID}) + +Authors: +\itemize{ + \item Layik Hama \email{layik.hama@gmail.com} (\href{https://orcid.org/0000-0003-1912-4890}{ORCID}) +} + +Other contributors: +\itemize{ + \item Ollie Lloyd \email{o.lloyd@doctors.org.uk} (\href{https://orcid.org/0000-0002-9385-1634}{ORCID}) [contributor] +} + +} diff --git a/man/ic_attributes_vec.Rd b/man/ic_attributes_vec.Rd index 280b626..ba1612b 100644 --- a/man/ic_attributes_vec.Rd +++ b/man/ic_attributes_vec.Rd @@ -6,8 +6,8 @@ \usage{ ic_attributes_vec( x = NULL, - ic_attributes = c(BEGIN = "VCALENDAR", PRODID = "ATFutures/calendar", VERSION = - "2.0", CALSCALE = "GREGORIAN", METHOD = "PUBLISH") + ic_attributes = c(BEGIN = "VCALENDAR", PRODID = "ATFutures/calendar", VERSION = "2.0", + CALSCALE = "GREGORIAN", METHOD = "PUBLISH") ) } \arguments{ diff --git a/man/ic_datetime.Rd b/man/ic_datetime.Rd index f69ece5..999973e 100644 --- a/man/ic_datetime.Rd +++ b/man/ic_datetime.Rd @@ -2,21 +2,21 @@ % Please edit documentation in R/ic_date.R \name{ic_datetime} \alias{ic_datetime} -\title{Convert ical datetime into R datetime -Z at the end of an ical stamp stands of Zulu time -https://en.wikipedia.org/wiki/Coordinated_Universal_Time#Time_zones -which is UTC = GMT https://greenwichmeantime.com/info/zulu/} +\title{Convert ical datetime into R datetime} \usage{ -ic_datetime(x) +ic_datetime(x, tzone = "") } \arguments{ \item{x}{Lines read-in in from an iCal file} + +\item{tzone}{a character string. Time zone specification to be used for the conversion if datetime not Zulu time. +Defaults to \code{""} which is the current local system time zone. See \link[base]{OlsonNames} for list of time zones.} } \description{ -Convert ical datetime into R datetime -Z at the end of an ical stamp stands of Zulu time +Convert ical datetime into R datetime. Z at the end of an ical stamp stands of Zulu time + https://en.wikipedia.org/wiki/Coordinated_Universal_Time#Time_zones -which is UTC = GMT https://greenwichmeantime.com/info/zulu/ +which is UTC = GMT https://greenwichmeantime.com/articles/history/zulu/ } \examples{ ic_datetime("20180809T160000Z") diff --git a/tests/testthat/test-ic_datetime.R b/tests/testthat/test-ic_datetime.R index b59b67a..25d6cd9 100644 --- a/tests/testthat/test-ic_datetime.R +++ b/tests/testthat/test-ic_datetime.R @@ -13,3 +13,10 @@ test_that("ic_datetime works for 20180809T160000Z:", { test_that("ic_datetime is NA for empty:", { expect_output(ic_datetime(""), NA) }) + +test_that("DTSTART & DTEND time zones equal local time zone ''", { + f <- system.file("extdata", "apple_calendar_test.ics", package = "calendar") # "Pacific/Auckland" chosen as original time zone as different to testers local time zone + ics_df = ic_read(f) + expect_equal(attributes(ics_df$DTSTART)$tzone, "") + expect_equal(attributes(ics_df$DTEND)$tzone, "") +})