1 #include "vctrs.h"
2 #include "owned.h"
3 #include "utils.h"
4
5
6 static SEXP new_date(SEXP x);
7 static SEXP new_datetime(SEXP x, SEXP tzone);
8 static SEXP new_empty_datetime(SEXP tzone);
9
10 static SEXP date_validate(SEXP x);
11 static SEXP datetime_validate(SEXP x);
12 static SEXP datetime_validate_tzone(SEXP x);
13 static SEXP datetime_validate_type(SEXP x);
14
15 static SEXP datetime_rezone(SEXP x, SEXP tzone);
16
17 static SEXP tzone_get(SEXP x);
18 static SEXP tzone_union(SEXP x_tzone, SEXP y_tzone);
19 static bool tzone_equal(SEXP x_tzone, SEXP y_tzone);
20
21 static SEXP r_as_date(SEXP x);
22 static SEXP r_as_posixct(SEXP x, SEXP tzone);
23 static SEXP r_as_posixlt(SEXP x, SEXP tzone);
24 static SEXP r_date_as_character(SEXP x);
25 static SEXP r_chr_date_as_posixct(SEXP x, SEXP tzone);
26 static SEXP r_chr_date_as_posixlt(SEXP x, SEXP tzone);
27
28 static SEXP posixlt_as_posixct_impl(SEXP x, SEXP tzone);
29 static SEXP posixct_as_posixlt_impl(SEXP x, SEXP tzone);
30
31 // -----------------------------------------------------------------------------
32 // ptype2
33
34 // [[ include("vctrs.h") ]]
date_datetime_ptype2(SEXP x,SEXP y)35 SEXP date_datetime_ptype2(SEXP x, SEXP y) {
36 SEXP x_class = PROTECT(Rf_getAttrib(x, R_ClassSymbol));
37 SEXP x_first_class = STRING_ELT(x_class, 0);
38
39 SEXP tzone = (x_first_class == strings_date) ? tzone_get(y) : tzone_get(x);
40 PROTECT(tzone);
41
42 SEXP out = new_empty_datetime(tzone);
43
44 UNPROTECT(2);
45 return out;
46 }
47
48
49 // [[ include("vctrs.h") ]]
datetime_datetime_ptype2(SEXP x,SEXP y)50 SEXP datetime_datetime_ptype2(SEXP x, SEXP y) {
51 SEXP x_tzone = PROTECT(tzone_get(x));
52 SEXP y_tzone = PROTECT(tzone_get(y));
53
54 // Never allocates
55 SEXP tzone = tzone_union(x_tzone, y_tzone);
56
57 SEXP out = new_empty_datetime(tzone);
58
59 UNPROTECT(2);
60 return out;
61 }
62
63 // -----------------------------------------------------------------------------
64 // cast
65
66 // [[ include("vctrs.h") ]]
date_as_date(SEXP x)67 SEXP date_as_date(SEXP x) {
68 return date_validate(x);
69 }
70
71
72 // [[ include("vctrs.h") ]]
date_as_posixct(SEXP x,SEXP to)73 SEXP date_as_posixct(SEXP x, SEXP to) {
74 SEXP tzone = PROTECT(tzone_get(to));
75
76 // Date -> character -> POSIXct
77 // This is the only way to retain the same clock time
78 SEXP out = PROTECT(r_date_as_character(x));
79 out = PROTECT(r_chr_date_as_posixct(out, tzone));
80
81 UNPROTECT(3);
82 return out;
83 }
84
85
86 // [[ include("vctrs.h") ]]
date_as_posixlt(SEXP x,SEXP to)87 SEXP date_as_posixlt(SEXP x, SEXP to) {
88 SEXP tzone = PROTECT(tzone_get(to));
89
90 // Date -> character -> POSIXlt
91 // This is the only way to retain the same clock time
92 SEXP out = PROTECT(r_date_as_character(x));
93 out = PROTECT(r_chr_date_as_posixlt(out, tzone));
94
95 UNPROTECT(3);
96 return out;
97 }
98
99
100 static SEXP posixt_as_date(SEXP ct, SEXP lt, bool* lossy);
101
102 // [[ include("vctrs.h") ]]
posixct_as_date(SEXP x,bool * lossy)103 SEXP posixct_as_date(SEXP x, bool* lossy) {
104 SEXP ct = PROTECT(datetime_validate(x));
105
106 SEXP tzone = PROTECT(tzone_get(ct));
107 SEXP lt = PROTECT(posixct_as_posixlt_impl(ct, tzone));
108
109 SEXP out = posixt_as_date(ct, lt, lossy);
110
111 UNPROTECT(3);
112 return out;
113 }
114
115
116 // [[ include("vctrs.h") ]]
posixlt_as_date(SEXP x,bool * lossy)117 SEXP posixlt_as_date(SEXP x, bool* lossy) {
118 SEXP lt = x;
119
120 SEXP tzone = PROTECT(tzone_get(lt));
121 SEXP ct = PROTECT(posixlt_as_posixct_impl(lt, tzone));
122
123 SEXP out = posixt_as_date(ct, lt, lossy);
124
125 UNPROTECT(2);
126 return out;
127 }
128
129 // POSIXct is required for lossy checking.
130 // POSIXlt is required for converting to Date.
131 // `as.Date.POSIXct()` must go through `as.POSIXlt()`, so the POSIXct
132 // time alone is not enough.
posixt_as_date(SEXP ct,SEXP lt,bool * lossy)133 static SEXP posixt_as_date(SEXP ct, SEXP lt, bool* lossy) {
134 ct = PROTECT(datetime_validate(ct));
135 const double* p_ct = REAL(ct);
136
137 SEXP out = PROTECT(r_as_date(lt));
138
139 SEXP roundtrip = PROTECT(date_as_posixct(out, ct));
140 const double* p_roundtrip = REAL(roundtrip);
141
142 const R_len_t size = Rf_length(out);
143
144 for (R_len_t i = 0; i < size; ++i) {
145 const double ct_elt = p_ct[i];
146
147 // `NaN` and `NA` always convert without issue
148 if (isnan(ct_elt)) {
149 continue;
150 }
151
152 const double roundtrip_elt = p_roundtrip[i];
153
154 if (ct_elt != roundtrip_elt) {
155 *lossy = true;
156 UNPROTECT(3);
157 return R_NilValue;
158 }
159 }
160
161 UNPROTECT(3);
162 return out;
163 }
164
165
166 static SEXP posixct_as_posixct_impl(SEXP x, SEXP tzone);
167
168 // [[ include("vctrs.h") ]]
posixct_as_posixct(SEXP x,SEXP to)169 SEXP posixct_as_posixct(SEXP x, SEXP to) {
170 SEXP tzone = PROTECT(tzone_get(to));
171 SEXP out = posixct_as_posixct_impl(x, tzone);
172 UNPROTECT(1);
173 return out;
174 }
175
posixct_as_posixct_impl(SEXP x,SEXP tzone)176 static SEXP posixct_as_posixct_impl(SEXP x, SEXP tzone) {
177 x = PROTECT(datetime_validate(x));
178 SEXP out = datetime_rezone(x, tzone);
179 UNPROTECT(1);
180 return out;
181 }
182
183
184 // [[ include("vctrs.h") ]]
posixlt_as_posixct(SEXP x,SEXP to)185 SEXP posixlt_as_posixct(SEXP x, SEXP to) {
186 SEXP tzone = PROTECT(tzone_get(to));
187 SEXP out = posixlt_as_posixct_impl(x, tzone);
188 UNPROTECT(1);
189 return out;
190 }
191
posixlt_as_posixct_impl(SEXP x,SEXP tzone)192 static SEXP posixlt_as_posixct_impl(SEXP x, SEXP tzone) {
193 SEXP x_tzone = PROTECT(tzone_get(x));
194 x = PROTECT(r_as_posixct(x, x_tzone));
195
196 SEXP out = posixct_as_posixct_impl(x, tzone);
197
198 UNPROTECT(2);
199 return out;
200 }
201
202
203 // [[ include("vctrs.h") ]]
posixct_as_posixlt(SEXP x,SEXP to)204 SEXP posixct_as_posixlt(SEXP x, SEXP to) {
205 SEXP tzone = PROTECT(tzone_get(to));
206 SEXP out = posixct_as_posixlt_impl(x, tzone);
207 UNPROTECT(1);
208 return out;
209 }
210
posixct_as_posixlt_impl(SEXP x,SEXP tzone)211 static SEXP posixct_as_posixlt_impl(SEXP x, SEXP tzone) {
212 return r_as_posixlt(x, tzone);
213 }
214
215
216 // [[ include("vctrs.h") ]]
posixlt_as_posixlt(SEXP x,SEXP to)217 SEXP posixlt_as_posixlt(SEXP x, SEXP to) {
218 SEXP x_tzone = PROTECT(tzone_get(x));
219 SEXP to_tzone = PROTECT(tzone_get(to));
220
221 if (tzone_equal(x_tzone, to_tzone)) {
222 UNPROTECT(2);
223 return x;
224 }
225
226 SEXP out = x;
227
228 // `as.POSIXlt.default()` doesn't respect `tz` so we have to do:
229 // POSIXlt<x-tzone> -> POSIXct<x-tzone> -> POSIXct<to-tzone> -> POSIXlt<to-tzone>
230 out = PROTECT(posixlt_as_posixct_impl(out, x_tzone));
231 out = PROTECT(posixct_as_posixct_impl(out, to_tzone));
232 out = PROTECT(posixct_as_posixlt_impl(out, to_tzone));
233
234 UNPROTECT(5);
235 return out;
236 }
237
238 // -----------------------------------------------------------------------------
239 // restore
240
241 // [[ include("vctrs.h") ]]
vec_date_restore(SEXP x,SEXP to,const enum vctrs_owned owned)242 SEXP vec_date_restore(SEXP x, SEXP to, const enum vctrs_owned owned) {
243 SEXP out = PROTECT(vec_restore_default(x, to, owned));
244 out = date_validate(out);
245 UNPROTECT(1);
246 return out;
247 }
248
249 // [[ include("vctrs.h") ]]
vec_posixct_restore(SEXP x,SEXP to,const enum vctrs_owned owned)250 SEXP vec_posixct_restore(SEXP x, SEXP to, const enum vctrs_owned owned) {
251 SEXP out = PROTECT(vec_restore_default(x, to, owned));
252 out = datetime_validate(out);
253 UNPROTECT(1);
254 return out;
255 }
256
257 // [[ include("vctrs.h") ]]
vec_posixlt_restore(SEXP x,SEXP to,const enum vctrs_owned owned)258 SEXP vec_posixlt_restore(SEXP x, SEXP to, const enum vctrs_owned owned) {
259 SEXP out = PROTECT(vec_restore_default(x, to, owned));
260 out = datetime_validate_tzone(out);
261 UNPROTECT(1);
262 return out;
263 }
264
265 // -----------------------------------------------------------------------------
266
267 // [[ register() ]]
vctrs_new_date(SEXP x)268 SEXP vctrs_new_date(SEXP x) {
269 return new_date(x);
270 }
271
new_date(SEXP x)272 static SEXP new_date(SEXP x) {
273 if (TYPEOF(x) != REALSXP) {
274 Rf_errorcall(R_NilValue, "`x` must be a double vector.");
275 }
276
277 SEXP names = PROTECT(r_names(x));
278
279 SEXP out = PROTECT(r_clone_referenced(x));
280
281 SET_ATTRIB(out, R_NilValue);
282
283 r_poke_names(out, names);
284 r_poke_class(out, classes_date);
285
286 UNPROTECT(2);
287 return out;
288 }
289
290
291 // [[ register() ]]
vctrs_new_datetime(SEXP x,SEXP tzone)292 SEXP vctrs_new_datetime(SEXP x, SEXP tzone) {
293 return new_datetime(x, tzone);
294 }
295
new_datetime(SEXP x,SEXP tzone)296 static SEXP new_datetime(SEXP x, SEXP tzone) {
297 if (TYPEOF(x) != REALSXP) {
298 Rf_errorcall(R_NilValue, "`x` must be a double vector.");
299 }
300
301 // Convenience special case where we allow a
302 // null `tzone` to represent local time
303 if (tzone == R_NilValue) {
304 tzone = chrs_empty;
305 }
306
307 if (TYPEOF(tzone) != STRSXP) {
308 Rf_errorcall(R_NilValue, "`tzone` must be a character vector or `NULL`.");
309 }
310
311 SEXP names = PROTECT(r_names(x));
312
313 SEXP out = PROTECT(r_clone_referenced(x));
314
315 SET_ATTRIB(out, R_NilValue);
316
317 r_poke_names(out, names);
318 r_poke_class(out, classes_posixct);
319 Rf_setAttrib(out, syms_tzone, tzone);
320
321 UNPROTECT(2);
322 return out;
323 }
324
325
new_empty_datetime(SEXP tzone)326 static SEXP new_empty_datetime(SEXP tzone) {
327 return new_datetime(vctrs_shared_empty_dbl, tzone);
328 }
329
330 // -----------------------------------------------------------------------------
331
332 // [[ register() ]]
vctrs_date_validate(SEXP x)333 SEXP vctrs_date_validate(SEXP x) {
334 return date_validate(x);
335 }
336
337 // Ensure that a `Date` is internally stored as a double vector
date_validate(SEXP x)338 static SEXP date_validate(SEXP x) {
339 switch (TYPEOF(x)) {
340 case REALSXP:
341 return x;
342 case INTSXP:
343 // Keeps attributes
344 return Rf_coerceVector(x, REALSXP);
345 default:
346 stop_internal("date_validate",
347 "Corrupt `Date` with unknown type %s.",
348 Rf_type2char(TYPEOF(x)));
349 }
350 }
351
352
353 // [[ register() ]]
vctrs_datetime_validate(SEXP x)354 SEXP vctrs_datetime_validate(SEXP x) {
355 return datetime_validate(x);
356 }
357
358 // Ensure that a `POSIXct` is internally stored as a double vector.
359 // Also checks that the `tzone` attribute is non-NULL.
datetime_validate(SEXP x)360 static SEXP datetime_validate(SEXP x) {
361 x = PROTECT(datetime_validate_tzone(x));
362 x = PROTECT(datetime_validate_type(x));
363 UNPROTECT(2);
364 return x;
365 }
366
datetime_validate_tzone(SEXP x)367 static SEXP datetime_validate_tzone(SEXP x) {
368 SEXP tzone = Rf_getAttrib(x, syms_tzone);
369
370 if (tzone != R_NilValue) {
371 return x;
372 }
373
374 x = PROTECT(r_clone_referenced(x));
375
376 Rf_setAttrib(x, syms_tzone, chrs_empty);
377
378 UNPROTECT(1);
379 return x;
380 }
381
datetime_validate_type(SEXP x)382 static SEXP datetime_validate_type(SEXP x) {
383 switch (TYPEOF(x)) {
384 case REALSXP:
385 return x;
386 case INTSXP:
387 // Keeps attributes
388 return Rf_coerceVector(x, REALSXP);
389 default:
390 stop_internal("datetime_validate_type",
391 "Corrupt `POSIXct` with unknown type %s.",
392 Rf_type2char(TYPEOF(x)));
393 }
394
395 never_reached("datetime_validate_type");
396 }
397
398 // -----------------------------------------------------------------------------
399
400 // Same underlying numeric representation, different `tzone`
datetime_rezone(SEXP x,SEXP tzone)401 static SEXP datetime_rezone(SEXP x, SEXP tzone) {
402 SEXP x_tzone = PROTECT(tzone_get(x));
403
404 if (tzone_equal(x_tzone, tzone)) {
405 UNPROTECT(1);
406 return x;
407 }
408
409 SEXP out = PROTECT(r_clone_referenced(x));
410
411 Rf_setAttrib(out, syms_tzone, tzone);
412
413 UNPROTECT(2);
414 return out;
415 }
416
417 // -----------------------------------------------------------------------------
418 // Time zone utilities
419
tzone_get(SEXP x)420 static SEXP tzone_get(SEXP x) {
421 SEXP tzone = PROTECT(Rf_getAttrib(x, syms_tzone));
422
423 if (tzone == R_NilValue) {
424 UNPROTECT(1);
425 return chrs_empty;
426 }
427
428 R_len_t size = Rf_length(tzone);
429
430 if (size == 1) {
431 UNPROTECT(1);
432 return tzone;
433 }
434
435 if (size == 0) {
436 Rf_errorcall(R_NilValue, "Corrupt datetime with 0-length `tzone` attribute");
437 }
438
439 // If there are multiple, only take the first
440 SEXP out = PROTECT(Rf_allocVector(STRSXP, 1));
441 SET_STRING_ELT(out, 0, STRING_ELT(tzone, 0));
442
443 UNPROTECT(2);
444 return out;
445 }
446
447 // `tzone_get()` is guaranteed to return 1 element
tzone_is_local(SEXP tzone)448 static inline bool tzone_is_local(SEXP tzone) {
449 return STRING_ELT(tzone, 0) == strings_empty;
450 }
451
tzone_union(SEXP x_tzone,SEXP y_tzone)452 static SEXP tzone_union(SEXP x_tzone, SEXP y_tzone) {
453 if (tzone_is_local(x_tzone)) {
454 return y_tzone;
455 } else {
456 return x_tzone;
457 }
458 }
459
460 // `tzone_get()` is guaranteed to return 1 element
tzone_equal(SEXP x_tzone,SEXP y_tzone)461 static bool tzone_equal(SEXP x_tzone, SEXP y_tzone) {
462 // Equal objects?
463 if (x_tzone == y_tzone) {
464 return true;
465 }
466
467 // Equal CHARSXPs?
468 SEXP x_string = STRING_ELT(x_tzone, 0);
469 SEXP y_string = STRING_ELT(y_tzone, 0);
470
471 if (x_string == y_string) {
472 return true;
473 }
474
475 // Equal C char?
476 const char* x_tzone_char = CHAR(x_string);
477 const char* y_tzone_char = CHAR(y_string);
478
479 return !strcmp(x_tzone_char, y_tzone_char);
480 }
481
482 // -----------------------------------------------------------------------------
483
484 static SEXP syms_tz = NULL;
485
486 static SEXP syms_as_date = NULL;
487 static SEXP fns_as_date = NULL;
488
r_as_date(SEXP x)489 static SEXP r_as_date(SEXP x) {
490 return vctrs_dispatch1(syms_as_date, fns_as_date, syms_x, x);
491 }
492
493 static SEXP syms_as_posixct = NULL;
494 static SEXP fns_as_posixct = NULL;
495
r_as_posixct(SEXP x,SEXP tzone)496 static SEXP r_as_posixct(SEXP x, SEXP tzone) {
497 return vctrs_dispatch2(syms_as_posixct, fns_as_posixct, syms_x, x, syms_tz, tzone);
498 }
499
500 static SEXP syms_as_posixlt = NULL;
501 static SEXP fns_as_posixlt = NULL;
502
r_as_posixlt(SEXP x,SEXP tzone)503 static SEXP r_as_posixlt(SEXP x, SEXP tzone) {
504 return vctrs_dispatch2(syms_as_posixlt, fns_as_posixlt, syms_x, x, syms_tz, tzone);
505 }
506
507 static SEXP syms_date_as_character = NULL;
508 static SEXP fns_date_as_character = NULL;
509
r_date_as_character(SEXP x)510 static SEXP r_date_as_character(SEXP x) {
511 return vctrs_dispatch1(syms_date_as_character, fns_date_as_character, syms_x, x);
512 }
513
514 static SEXP syms_chr_date_as_posixct = NULL;
515 static SEXP fns_chr_date_as_posixct = NULL;
516
r_chr_date_as_posixct(SEXP x,SEXP tzone)517 static SEXP r_chr_date_as_posixct(SEXP x, SEXP tzone) {
518 return vctrs_dispatch2(syms_chr_date_as_posixct, fns_chr_date_as_posixct, syms_x, x, syms_tzone, tzone);
519 }
520
521 static SEXP syms_chr_date_as_posixlt = NULL;
522 static SEXP fns_chr_date_as_posixlt = NULL;
523
r_chr_date_as_posixlt(SEXP x,SEXP tzone)524 static SEXP r_chr_date_as_posixlt(SEXP x, SEXP tzone) {
525 return vctrs_dispatch2(syms_chr_date_as_posixlt, fns_chr_date_as_posixlt, syms_x, x, syms_tzone, tzone);
526 }
527
528 // -----------------------------------------------------------------------------
529
vctrs_init_type_date_time(SEXP ns)530 void vctrs_init_type_date_time(SEXP ns) {
531 syms_tz = Rf_install("tz");
532
533 syms_as_date = Rf_install("as.Date");
534 syms_as_posixct = Rf_install("as.POSIXct");
535 syms_as_posixlt = Rf_install("as.POSIXlt");
536 syms_date_as_character = Rf_install("date_as_character");
537 syms_chr_date_as_posixct = Rf_install("chr_date_as_posixct");
538 syms_chr_date_as_posixlt = Rf_install("chr_date_as_posixlt");
539
540 fns_as_date = r_env_get(R_BaseEnv, syms_as_date);
541 fns_as_posixct = r_env_get(R_BaseEnv, syms_as_posixct);
542 fns_as_posixlt = r_env_get(R_BaseEnv, syms_as_posixlt);
543 fns_date_as_character = r_env_get(ns, syms_date_as_character);
544 fns_chr_date_as_posixct = r_env_get(ns, syms_chr_date_as_posixct);
545 fns_chr_date_as_posixlt = r_env_get(ns, syms_chr_date_as_posixlt);
546 }
547