Skip to content

Commit 2cea9c0

Browse files
authored
fix: minor improvements to time zone parsing (#400)
1 parent 4a4a810 commit 2cea9c0

File tree

3 files changed

+39
-37
lines changed

3 files changed

+39
-37
lines changed

NEWS.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
## Enhancements and fixes
44

5-
- `get_groups()` now paginates through all results when a `prefix` is provided, if the Connect server API version supports pagination. (#328)
5+
- `get_groups()` now paginates through all results when a `prefix` is provided,
6+
if the Connect server API version supports pagination. (#328)
7+
- Timestamps from the Connect server are now displayed in your local time zone,
8+
rather than in UTC. (#400)
69

710
# connectapi 0.7.0
811

R/parse.R

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -154,35 +154,21 @@ coerce_datetime <- function(x, to, ...) {
154154
# - "2020-01-01T00:02:03-01:00"
155155
# nolint end
156156
parse_connect_rfc3339 <- function(x) {
157-
# Convert any timestamps with offsets to a format recognized by `strptime`.
157+
# Convert timestamps with offsets to a format recognized by `strptime`.
158158
x <- gsub("([+-]\\d\\d):(\\d\\d)$", "\\1\\2", x)
159+
x <- gsub("Z$", "+0000", x)
159160

160-
# `purrr::map2_vec()` converts to POSIXct automatically, but we need
161-
# `as.POSIXct()` in there to account vectors of length 1, which it seems are
162-
# not converted.
163-
#
164-
# Parse with an inner call to `strptime()`; convert the resulting `POSIXlt`
165-
# object to `POSIXct`.
161+
# Parse with an inner call to `strptime()`, which returns a POSIXlt object,
162+
# and convert that to `POSIXct`.
166163
#
167164
# We must specify `tz` in the inner call to correctly compute date math.
168-
# Specifying `tz` when parsing just changes the time zone without doing any
169-
# date math!
165+
# Specifying `tz` when in the outer call just changes the time zone without
166+
# doing any date math!
170167
#
171-
# > xlt
172-
# [1] "2024-08-29 16:36:33 EDT"
173-
# > tzone(xlt)
174-
# [1] "America/New_York"
175-
# > as.POSIXct(xlt, tz = "UTC")
176-
# [1] "2024-08-29 16:36:33 UTC"
177-
purrr::map_vec(x, function(.x) {
178-
# Times with and without offsets require different formats.
179-
format_string <- ifelse(
180-
grepl("Z$", .x),
181-
"%Y-%m-%dT%H:%M:%OSZ",
182-
"%Y-%m-%dT%H:%M:%OS%z"
183-
)
184-
as.POSIXct(strptime(.x, format = format_string, tz = "UTC"))
185-
})
168+
# > xlt [1] "2024-08-29 16:36:33 EDT" tzone(xlt) [1] "America/New_York"
169+
# as.POSIXct(xlt, tz = "UTC") [1] "2024-08-29 16:36:33 UTC"
170+
format_string <- "%Y-%m-%dT%H:%M:%OS%z"
171+
as.POSIXct(x, format = format_string, tz = Sys.timezone())
186172
}
187173

188174
vec_cast.POSIXct.double <- # nolint: object_name_linter

tests/testthat/test-parse.R

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ test_that("coerce_datetime fills the void", {
4747
})
4848

4949
test_that("parse_connect_rfc3339() parses timestamps with offsets as expected", {
50-
withr::defer(Sys.setenv(TZ = Sys.getenv("TZ")))
51-
5250
x_mixed <- c(
5351
"2023-08-22T14:13:14Z",
5452
"2020-01-01T01:02:03Z",
@@ -75,32 +73,47 @@ test_that("parse_connect_rfc3339() parses timestamps with offsets as expected",
7573

7674
single_offset <- "2023-08-22T15:13:14+01:00"
7775

76+
withr::local_envvar(TZ = "America/New_York")
7877
expected <- as.POSIXct(strptime(
7978
c(
8079
"2023-08-22T14:13:14+0000",
8180
"2020-01-01T01:02:03+0000"
8281
),
8382
format = "%Y-%m-%dT%H:%M:%S%z",
84-
tz = "UTC"
83+
tz = Sys.timezone()
8584
))
86-
87-
Sys.setenv(TZ = "America/New_York")
8885
expect_identical(parse_connect_rfc3339(x_mixed), rep(expected, 2))
8986
expect_identical(parse_connect_rfc3339(x_zero_offset), expected)
9087
expect_identical(parse_connect_rfc3339(x_plus_one), expected)
9188
expect_identical(parse_connect_rfc3339(x_minus_one), expected)
9289
expect_identical(parse_connect_rfc3339(single_zero_offset), expected[1])
9390
expect_identical(parse_connect_rfc3339(single_offset), expected[1])
9491

95-
Sys.setenv(TZ = "UTC")
92+
withr::local_envvar(TZ = "UTC")
93+
expected <- as.POSIXct(strptime(
94+
c(
95+
"2023-08-22T14:13:14+0000",
96+
"2020-01-01T01:02:03+0000"
97+
),
98+
format = "%Y-%m-%dT%H:%M:%S%z",
99+
tz = Sys.timezone()
100+
))
96101
expect_identical(parse_connect_rfc3339(x_mixed), rep(expected, 2))
97102
expect_identical(parse_connect_rfc3339(x_zero_offset), expected)
98103
expect_identical(parse_connect_rfc3339(x_plus_one), expected)
99104
expect_identical(parse_connect_rfc3339(x_minus_one), expected)
100105
expect_identical(parse_connect_rfc3339(single_zero_offset), expected[1])
101106
expect_identical(parse_connect_rfc3339(single_offset), expected[1])
102107

103-
Sys.setenv(TZ = "Asia/Tokyo")
108+
withr::local_envvar(TZ = "Asia/Tokyo")
109+
expected <- as.POSIXct(strptime(
110+
c(
111+
"2023-08-22T14:13:14+0000",
112+
"2020-01-01T01:02:03+0000"
113+
),
114+
format = "%Y-%m-%dT%H:%M:%S%z",
115+
tz = Sys.timezone()
116+
))
104117
expect_identical(parse_connect_rfc3339(x_mixed), rep(expected, 2))
105118
expect_identical(parse_connect_rfc3339(x_zero_offset), expected)
106119
expect_identical(parse_connect_rfc3339(x_plus_one), expected)
@@ -109,7 +122,9 @@ test_that("parse_connect_rfc3339() parses timestamps with offsets as expected",
109122
expect_identical(parse_connect_rfc3339(single_offset), expected[1])
110123
})
111124

125+
112126
test_that("parse_connect_rfc3339() handles fractional seconds", {
127+
withr::local_envvar(TZ = "UTC")
113128
expected <- as.POSIXct(strptime(
114129
c(
115130
"2024-12-06T19:09:29.948016766+0000",
@@ -125,8 +140,6 @@ test_that("parse_connect_rfc3339() handles fractional seconds", {
125140
})
126141

127142
test_that("make_timestamp produces expected output", {
128-
withr::defer(Sys.setenv(TZ = Sys.getenv("TZ")))
129-
130143
x_mixed <- c(
131144
"2023-08-22T14:13:14Z",
132145
"2020-01-01T01:02:03Z",
@@ -158,7 +171,7 @@ test_that("make_timestamp produces expected output", {
158171
"2020-01-01T01:02:03Z"
159172
)
160173

161-
Sys.setenv(TZ = "America/New_York")
174+
withr::local_envvar(TZ = "America/New_York")
162175
expect_equal(
163176
make_timestamp(coerce_datetime(x_mixed, NA_datetime_)),
164177
rep(outcome, 2)
@@ -185,7 +198,7 @@ test_that("make_timestamp produces expected output", {
185198
)
186199
expect_equal(make_timestamp(outcome), outcome)
187200

188-
Sys.setenv(TZ = "UTC")
201+
withr::local_envvar(TZ = "UTC")
189202
expect_equal(
190203
make_timestamp(coerce_datetime(x_mixed, NA_datetime_)),
191204
rep(outcome, 2)
@@ -212,7 +225,7 @@ test_that("make_timestamp produces expected output", {
212225
)
213226
expect_equal(make_timestamp(outcome), outcome)
214227

215-
Sys.setenv(TZ = "Asia/Tokyo")
228+
withr::local_envvar(TZ = "Asia/Tokyo")
216229
expect_equal(
217230
make_timestamp(coerce_datetime(x_mixed, NA_datetime_)),
218231
rep(outcome, 2)

0 commit comments

Comments
 (0)