File: as-zoned-time-naive-time.Rd

package info (click to toggle)
r-cran-clock 0.7.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,856 kB
  • sloc: cpp: 19,564; sh: 17; makefile: 2
file content (256 lines) | stat: -rw-r--r-- 10,735 bytes parent folder | download | duplicates (2)
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/naive-time.R
\name{as-zoned-time-naive-time}
\alias{as-zoned-time-naive-time}
\alias{as_zoned_time.clock_naive_time}
\title{Convert to a zoned-time from a naive-time}
\usage{
\method{as_zoned_time}{clock_naive_time}(x, zone, ..., nonexistent = NULL, ambiguous = NULL)
}
\arguments{
\item{x}{\verb{[clock_naive_time]}

A naive-time to convert to a zoned-time.}

\item{zone}{\verb{[character(1)]}

The zone to convert to.}

\item{...}{These dots are for future extensions and must be empty.}

\item{nonexistent}{\verb{[character / NULL]}

One of the following nonexistent time resolution strategies, allowed to be
either length 1, or the same length as the input:
\itemize{
\item \code{"roll-forward"}: The next valid instant in time.
\item \code{"roll-backward"}: The previous valid instant in time.
\item \code{"shift-forward"}: Shift the nonexistent time forward by the size of
the daylight saving time gap.
\item \verb{"shift-backward}: Shift the nonexistent time backward by the size of
the daylight saving time gap.
\item \code{"NA"}: Replace nonexistent times with \code{NA}.
\item \code{"error"}: Error on nonexistent times.
}

Using either \code{"roll-forward"} or \code{"roll-backward"} is generally
recommended over shifting, as these two strategies maintain the
\emph{relative ordering} between elements of the input.

If \code{NULL}, defaults to \code{"error"}.

If \code{getOption("clock.strict")} is \code{TRUE}, \code{nonexistent} must be supplied
and cannot be \code{NULL}. This is a convenient way to make production code
robust to nonexistent times.}

\item{ambiguous}{\verb{[character / zoned_time / POSIXct / list(2) / NULL]}

One of the following ambiguous time resolution strategies, allowed to be
either length 1, or the same length as the input:
\itemize{
\item \code{"earliest"}: Of the two possible times, choose the earliest one.
\item \code{"latest"}: Of the two possible times, choose the latest one.
\item \code{"NA"}: Replace ambiguous times with \code{NA}.
\item \code{"error"}: Error on ambiguous times.
}

Alternatively, \code{ambiguous} is allowed to be a zoned_time (or POSIXct) that
is either length 1, or the same length as the input. If an ambiguous time
is encountered, the zoned_time is consulted. If the zoned_time corresponds
to a naive_time that is also ambiguous \emph{and} uses the same daylight saving
time transition point as the original ambiguous time, then the offset of
the zoned_time is used to resolve the ambiguity. If the ambiguity cannot be
resolved by consulting the zoned_time, then this method falls back to
\code{NULL}.

Finally, \code{ambiguous} is allowed to be a list of size 2, where the first
element of the list is a zoned_time (as described above), and the second
element of the list is an ambiguous time resolution strategy to use when
the ambiguous time cannot be resolved by consulting the zoned_time.
Specifying a zoned_time on its own is identical to \verb{list(<zoned_time>, NULL)}.

If \code{NULL}, defaults to \code{"error"}.

If \code{getOption("clock.strict")} is \code{TRUE}, \code{ambiguous} must be supplied and
cannot be \code{NULL}. Additionally, \code{ambiguous} cannot be specified as a
zoned_time on its own, as this implies \code{NULL} for ambiguous times that the
zoned_time cannot resolve. Instead, it must be specified as a list
alongside an ambiguous time resolution strategy as described above. This is
a convenient way to make production code robust to ambiguous times.}
}
\value{
A zoned-time vector.
}
\description{
This is a naive-time method for the \code{\link[=as_zoned_time]{as_zoned_time()}} generic.

Converting to a zoned-time from a naive-time retains the printed time,
but changes the underlying duration, depending on the \code{zone} that you choose.

Naive-times are time points with a yet-to-be-determined time zone. By
converting them to a zoned-time, all you are doing is specifying that
time zone while attempting to keep all other printed information the
same (if possible).

If you want to retain the underlying duration, try converting to a zoned-time
\link[=as-zoned-time-sys-time]{from a sys-time}, which is a time point
interpreted as having a UTC time zone.
}
\section{Daylight Saving Time}{


Converting from a naive-time to a zoned-time is not always possible due to
daylight saving time issues. There are two types of these issues:

\emph{Nonexistent} times are the result of daylight saving time "gaps".
For example, in the America/New_York time zone, there was a daylight
saving time gap 1 second after \code{"2020-03-08 01:59:59"}, where the clocks
changed from \code{01:59:59 -> 03:00:00}, completely skipping the 2 o'clock hour.
This means that if you had a naive time of \code{"2020-03-08 02:30:00"}, you
couldn't convert that straight into a zoned-time with this time zone. To
resolve these issues, the \code{nonexistent} argument can be used to specify
one of many nonexistent time resolution strategies.

\emph{Ambiguous} times are the result of daylight saving time "fallbacks".
For example, in the America/New_York time zone, there was a daylight
saving time fallback 1 second after \code{"2020-11-01 01:59:59 EDT"}, at which
point the clocks "fell backwards" by 1 hour, resulting in a printed time of
\code{"2020-11-01 01:00:00 EST"} (note the EDT->EST shift). This resulted in two
1 o'clock hours for this day, so if you had a naive time of
\code{"2020-11-01 01:30:00"}, you wouldn't be able to convert that directly
into a zoned-time with this time zone, as there is no way for clock to know
which of the two ambiguous times you wanted. To resolve these issues,
the \code{ambiguous} argument can be used to specify one of many ambiguous
time resolution strategies.
}

\examples{
library(magrittr)

x <- as_naive_time(year_month_day(2019, 1, 1))

# Converting a naive-time to a zoned-time generally retains the
# printed time, while changing the underlying duration.
as_zoned_time(x, "America/New_York")
as_zoned_time(x, "America/Los_Angeles")

# ---------------------------------------------------------------------------
# Nonexistent time:

new_york <- "America/New_York"

# There was a daylight saving gap in the America/New_York time zone on
# 2020-03-08 01:59:59 -> 03:00:00, which means that one of these
# naive-times don't exist in that time zone. By default, attempting to
# convert it to a zoned time will result in an error.
nonexistent_time <- year_month_day(2020, 03, 08, c(02, 03), c(45, 30), 00)
nonexistent_time <- as_naive_time(nonexistent_time)
try(as_zoned_time(nonexistent_time, new_york))

# Resolve this by specifying a nonexistent time resolution strategy
as_zoned_time(nonexistent_time, new_york, nonexistent = "roll-forward")
as_zoned_time(nonexistent_time, new_york, nonexistent = "roll-backward")

# Note that rolling backwards will choose the last possible moment in
# time at the current precision of the input
nonexistent_nanotime <- time_point_cast(nonexistent_time, "nanosecond")
nonexistent_nanotime
as_zoned_time(nonexistent_nanotime, new_york, nonexistent = "roll-backward")

# A word of caution - Shifting does not guarantee that the relative ordering
# of the input is maintained
shifted <- as_zoned_time(
  nonexistent_time,
  new_york,
  nonexistent = "shift-forward"
)
shifted

# 02:45:00 < 03:30:00
nonexistent_time[1] < nonexistent_time[2]
# 03:45:00 > 03:30:00 (relative ordering is lost)
shifted[1] < shifted[2]

# ---------------------------------------------------------------------------
# Ambiguous time:

new_york <- "America/New_York"

# There was a daylight saving time fallback in the America/New_York time
# zone on 2020-11-01 01:59:59 EDT -> 2020-11-01 01:00:00 EST, resulting
# in two 1 o'clock hours. This means that the following naive time is
# ambiguous since we don't know which of the two 1 o'clocks it belongs to.
# By default, attempting to convert it to a zoned time will result in an
# error.
ambiguous_time <- year_month_day(2020, 11, 01, 01, 30, 00)
ambiguous_time <- as_naive_time(ambiguous_time)
try(as_zoned_time(ambiguous_time, new_york))

# Resolve this by specifying an ambiguous time resolution strategy
earliest <- as_zoned_time(ambiguous_time, new_york, ambiguous = "earliest")
latest <- as_zoned_time(ambiguous_time, new_york, ambiguous = "latest")
na <- as_zoned_time(ambiguous_time, new_york, ambiguous = "NA")
earliest
latest
na

# Now assume that you were given the following zoned-times, i.e.,
# you didn't build them from scratch so you already know their otherwise
# ambiguous offsets
x <- c(earliest, latest)
x

# To set the seconds to 5 in both, you might try:
x_naive <- x \%>\%
  as_naive_time() \%>\%
  as_year_month_day() \%>\%
  set_second(5) \%>\%
  as_naive_time()

x_naive

# But this fails because you've "lost" the information about which
# offsets these ambiguous times started in
try(as_zoned_time(x_naive, zoned_time_zone(x)))

# To get around this, you can use that information by specifying
# `ambiguous = x`, which will use the offset from `x` to resolve the
# ambiguity in `x_naive` as long as `x` is also an ambiguous time with the
# same daylight saving time transition point as `x_naive` (i.e. here
# everything has a transition point of `"2020-11-01 01:00:00 EST"`).
as_zoned_time(x_naive, zoned_time_zone(x), ambiguous = x)

# Say you added one more time to `x` that would not be considered ambiguous
# in naive-time
x <- c(x, as_zoned_time(as_sys_time(latest) + 3600, zoned_time_zone(latest)))
x

# Imagine you want to floor this vector to a multiple of 2 hours, with
# an origin of 1am that day. You can do this by subtracting the origin,
# flooring, then adding it back
origin <- year_month_day(2019, 11, 01, 01, 00, 00) \%>\%
  as_naive_time() \%>\%
  as_duration()

x_naive <- x \%>\%
  as_naive_time() \%>\%
  add_seconds(-origin) \%>\%
  time_point_floor("hour", n = 2) \%>\%
  add_seconds(origin)

x_naive

# You again have ambiguous naive-time points, so you might try using
# `ambiguous = x`. It looks like this took care of the first two problems,
# but we have an issue at location 3.
try(as_zoned_time(x_naive, zoned_time_zone(x), ambiguous = x))

# When we floored from 02:30:00 -> 01:00:00, we went from being
# unambiguous -> ambiguous. In clock, this is something you must handle
# explicitly, and cannot be handled by using information from `x`. You can
# handle this while still retaining the behavior for the other two
# time points that were ambiguous before and after the floor by passing a
# list containing `x` and an ambiguous time resolution strategy to use
# when information from `x` can't resolve ambiguities:
as_zoned_time(x_naive, zoned_time_zone(x), ambiguous = list(x, "latest"))
}