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