#17 Yday

Open Davis Vaughan DavisVaughan

No flags found

Use flags to group coverage reports by test type, project and/or folders.
Then setup custom commit statuses and notifications for each flag.

e.g., #unittest #integration

#production #enterprise

#frontend #backend

Learn more about Codecov Flags here.


@@ -48,15 +48,20 @@
Loading
48 48
#' the month specified for the `origin` will be used as the month to start
49 49
#' counting from to generate the 3 month quarter.
50 50
#'
51 -
#' The `period` value of `"week"` is computed in the same way as
52 -
#' `lubridate::week()`. Week groups are defined as complete 7 day periods,
53 -
#' with the 7 day counter resetting every January 1st. To mimic the behavior
54 -
#' of `lubridate::floor_date()`, use `period = "day"` and multiply `every` by 7.
55 -
#' To mimic the `week_start` argument of `floor_date()`, set `origin` to a date
51 +
#' To mimic the behavior of `lubridate::floor_date()`, use `period = "week"`.
52 +
#' Internally this is just `period = "day", every = every * 7`. To mimic the
53 +
#' `week_start` argument of `floor_date()`, set `origin` to a date
56 54
#' with a week day identical to the one you want the week to start from. For
57 55
#' example, the default origin of `1970-01-01` is a Thursday, so this would be
58 56
#' generate groups identical to `floor_date(week_start = 4)`.
59 57
#'
58 +
#' The `period` value of `"yday"` is computed as complete `every`-day periods
59 +
#' from the `origin`, with a forced reset of the `every`-day counter every
60 +
#' time you hit the month-day value of the `origin`. `"yweek"` is built on top
61 +
#' of this internally as `period = "yday", every = every * 7`. This ends up
62 +
#' using an algorithm very similar to `lubridate::week()`, with the added
63 +
#' benefit of being able to control the `origin` date.
64 +
#'
60 65
#' @section Precision:
61 66
#'
62 67
#' With `POSIXct`, the limit of precision is approximately the microsecond
@@ -76,8 +81,8 @@
Loading
76 81
#'
77 82
#'   A string defining the period to group by. Valid inputs are:
78 83
#'
79 -
#'   `"year"`, `"quarter"`, `"month"`, `"week"`, `"day"`, `"hour"`, `"minute"`,
80 -
#'   `"second"`, `"millisecond"`
84 +
#'   `"year"`, `"quarter"`, `"month"`, `"week"`, `"yweek"`, `"day"`, `"yday"`,
85 +
#'   `"hour"`, `"minute"`, `"second"`, `"millisecond"`
81 86
#'
82 87
#' @param every `[positive integer(1)]`
83 88
#'
@@ -130,6 +135,9 @@
Loading
130 135
#' origin <- as.POSIXct("1970-01-01 00:00:01", "UTC")
131 136
#' warp_distance(y, "second", every = 5, origin = origin)
132 137
#'
138 +
#' # ---------------------------------------------------------------------------
139 +
#' # Time zones
140 +
#'
133 141
#' # When `x` is not UTC and `origin` is left as `NULL`, the origin is set as
134 142
#' # 1970-01-01 00:00:00 in the time zone of `x`. This seems to be the most
135 143
#' # practically useful default.
@@ -149,6 +157,23 @@
Loading
149 157
#' origin <- as.POSIXct("1970-01-01 00:00:00", tz = "UTC")
150 158
#' warp_distance(z, "year", origin = origin)
151 159
#' warp_distance(z_in_nyc, "year", origin = origin)
160 +
#'
161 +
#' # ---------------------------------------------------------------------------
162 +
#' # `period = "yweek"`
163 +
#'
164 +
#' x <- as.Date("2019-12-23") + 0:16
165 +
#' origin <- as.Date("1970-01-01")
166 +
#'
167 +
#' # `"week"` counts the number of 7 day periods from the `origin`
168 +
#' # `"yweek"` restarts the 7 day counter every time you hit the month-day
169 +
#' # value of the `origin`. Notice how, for the `yweek` column, only 1 day was
170 +
#' # in the week starting with `2019-12-31`. This is because the next day is
171 +
#' # `2020-01-01`, which aligns with the month-day value of the `origin`.
172 +
#' data.frame(
173 +
#'   x = x,
174 +
#'   week = warp_distance(x, "week", origin = origin),
175 +
#'   yweek = warp_distance(x, "yweek", origin = origin)
176 +
#' )
152 177
warp_distance <- function(x, period = "year", every = 1L, origin = NULL) {
153 178
  .Call(warp_warp_distance, x, period, every, origin)
154 179
}

@@ -23,13 +23,13 @@
Loading
23 23
  never_reached("as_datetime");
24 24
}
25 25
26 -
#define AS_DATETIME_FROM_DATE_LOOP(CTYPE, CONST_DEREF, NA_VALUE) {   \
26 +
#define AS_DATETIME_FROM_DATE_LOOP(CTYPE, CONST_DEREF, NA_CHECK) {   \
27 27
  const CTYPE* p_x = CONST_DEREF(x);                                 \
28 28
                                                                     \
29 29
  for (R_xlen_t i = 0; i < x_size; ++i) {                            \
30 30
    const CTYPE elt = p_x[i];                                        \
31 31
                                                                     \
32 -
    if (elt == NA_VALUE) {                                           \
32 +
    if (NA_CHECK) {                                                  \
33 33
      p_out[i] = NA_REAL;                                            \
34 34
      continue;                                                      \
35 35
    }                                                                \
@@ -45,8 +45,8 @@
Loading
45 45
  double* p_out = REAL(out);
46 46
47 47
  switch (TYPEOF(x)) {
48 -
  case INTSXP: AS_DATETIME_FROM_DATE_LOOP(int, INTEGER_RO, NA_INTEGER); break;
49 -
  case REALSXP: AS_DATETIME_FROM_DATE_LOOP(double, REAL_RO, NA_REAL); break;
48 +
  case INTSXP: AS_DATETIME_FROM_DATE_LOOP(int, INTEGER_RO, elt == NA_INTEGER); break;
49 +
  case REALSXP: AS_DATETIME_FROM_DATE_LOOP(double, REAL_RO, !R_FINITE(elt)); break;
50 50
  default: Rf_errorcall(R_NilValue, "Unknown `Date` type %s.", Rf_type2char(TYPEOF(x)));
51 51
  }
52 52

@@ -11,28 +11,24 @@
Loading
11 11
extern SEXP warp_class_type(SEXP);
12 12
extern SEXP warp_date_get_year_offset(SEXP);
13 13
extern SEXP warp_date_get_month_offset(SEXP);
14 -
extern SEXP warp_convert_days_to_components(SEXP);
15 14
extern SEXP warp_divmod(SEXP, SEXP);
16 15
extern SEXP warp_div(SEXP, SEXP);
17 16
extern SEXP warp_warp_is_sorted(SEXP, SEXP, SEXP, SEXP);
18 -
extern SEXP warp_get_origin_epoch_in_time_zone(SEXP);
19 17
20 18
// Defined below
21 19
SEXP warp_init_library(SEXP);
22 20
23 21
static const R_CallMethodDef CallEntries[] = {
24 -
  {"warp_warp_distance",                 (DL_FUNC) &warp_warp_distance, 4},
25 -
  {"warp_warp_change",                   (DL_FUNC) &warp_warp_change, 4},
26 -
  {"warp_warp_boundary",                 (DL_FUNC) &warp_warp_boundary, 4},
27 -
  {"warp_class_type",                    (DL_FUNC) &warp_class_type, 1},
28 -
  {"warp_date_get_year_offset",          (DL_FUNC) &warp_date_get_year_offset, 1},
29 -
  {"warp_date_get_month_offset",         (DL_FUNC) &warp_date_get_month_offset, 1},
30 -
  {"warp_convert_days_to_components",    (DL_FUNC) &warp_convert_days_to_components, 1},
31 -
  {"warp_divmod",                        (DL_FUNC) &warp_divmod, 2},
32 -
  {"warp_div",                           (DL_FUNC) &warp_div, 2},
33 -
  {"warp_warp_is_sorted",                (DL_FUNC) &warp_warp_is_sorted, 4},
34 -
  {"warp_get_origin_epoch_in_time_zone", (DL_FUNC) &warp_get_origin_epoch_in_time_zone, 1},
35 -
  {"warp_init_library",                  (DL_FUNC) &warp_init_library, 1},
22 +
  {"warp_warp_distance",         (DL_FUNC) &warp_warp_distance, 4},
23 +
  {"warp_warp_change",           (DL_FUNC) &warp_warp_change, 4},
24 +
  {"warp_warp_boundary",         (DL_FUNC) &warp_warp_boundary, 4},
25 +
  {"warp_class_type",            (DL_FUNC) &warp_class_type, 1},
26 +
  {"warp_date_get_year_offset",  (DL_FUNC) &warp_date_get_year_offset, 1},
27 +
  {"warp_date_get_month_offset", (DL_FUNC) &warp_date_get_month_offset, 1},
28 +
  {"warp_divmod",                (DL_FUNC) &warp_divmod, 2},
29 +
  {"warp_div",                   (DL_FUNC) &warp_div, 2},
30 +
  {"warp_warp_is_sorted",        (DL_FUNC) &warp_warp_is_sorted, 4},
31 +
  {"warp_init_library",          (DL_FUNC) &warp_init_library, 1},
36 32
  {NULL, NULL, 0}
37 33
};
38 34

@@ -7,15 +7,3 @@
Loading
7 7
date_get_month_offset <- function(x) {
8 8
  .Call(warp_date_get_month_offset, x)
9 9
}
10 -
11 -
# nocov start
12 -
13 -
divmod <- function(x, y) {
14 -
  .Call(warp_divmod, x, y)
15 -
}
16 -
17 -
div <- function(x, y) {
18 -
  .Call(warp_div, x, y)
19 -
}
20 -
21 -
# nocov end

@@ -8,25 +8,6 @@
Loading
8 8
 * components. It is both much faster and highly memory efficient.
9 9
 */
10 10
11 -
/*
12 -
 * @member year
13 -
 *   The year offset. The number of years since 1970.
14 -
 * @member month
15 -
 *   The month. Mapped to the range of 0-11, where 0 is January.
16 -
 * @member day
17 -
 *   The day of month. Mapped to the range of 0-30.
18 -
 * @member yday
19 -
 *   The day of the year. Mapped to the range of 0-365.
20 -
 */
21 -
struct warp_components {
22 -
  int year;
23 -
  int month;
24 -
  int day;
25 -
  int yday;
26 -
};
27 -
28 -
static struct warp_components convert_days_to_components(int n);
29 -
30 11
// -----------------------------------------------------------------------------
31 12
32 13
static SEXP int_date_get_year_offset(SEXP x);
@@ -64,7 +45,7 @@
Loading
64 45
65 46
    struct warp_components components = convert_days_to_components(elt);
66 47
67 -
    p_out[i] = components.year;
48 +
    p_out[i] = components.year_offset;
68 49
  }
69 50
70 51
  UNPROTECT(1);
@@ -92,7 +73,7 @@
Loading
92 73
93 74
    struct warp_components components = convert_days_to_components(elt);
94 75
95 -
    p_out[i] = components.year;
76 +
    p_out[i] = components.year_offset;
96 77
  }
97 78
98 79
  UNPROTECT(1);
@@ -136,7 +117,7 @@
Loading
136 117
137 118
    struct warp_components components = convert_days_to_components(elt);
138 119
139 -
    p_out[i] = components.year * 12 + components.month;
120 +
    p_out[i] = components.year_offset * 12 + components.month;
140 121
  }
141 122
142 123
  UNPROTECT(1);
@@ -164,7 +145,7 @@
Loading
164 145
165 146
    struct warp_components components = convert_days_to_components(elt);
166 147
167 -
    p_out[i] = components.year * 12 + components.month;
148 +
    p_out[i] = components.year_offset * 12 + components.month;
168 149
  }
169 150
170 151
  UNPROTECT(1);
@@ -173,83 +154,61 @@
Loading
173 154
174 155
// -----------------------------------------------------------------------------
175 156
176 -
static SEXP int_date_get_week_offset(SEXP x);
177 -
static SEXP dbl_date_get_week_offset(SEXP x);
157 +
static struct warp_yday_components int_date_get_origin_yday_components(SEXP origin);
158 +
static struct warp_yday_components dbl_date_get_origin_yday_components(SEXP origin);
178 159
179 160
// [[ include("utils.h") ]]
180 -
SEXP date_get_week_offset(SEXP x) {
181 -
  switch (TYPEOF(x)) {
182 -
  case INTSXP: return int_date_get_week_offset(x);
183 -
  case REALSXP: return dbl_date_get_week_offset(x);
184 -
  default: r_error("date_get_week_offset", "Unknown `Date` type %s.", Rf_type2char(TYPEOF(x)));
161 +
struct warp_yday_components date_get_origin_yday_components(SEXP origin) {
162 +
  switch (TYPEOF(origin)) {
163 +
  case INTSXP: return int_date_get_origin_yday_components(origin);
164 +
  case REALSXP: return dbl_date_get_origin_yday_components(origin);
165 +
  default: r_error("date_get_origin_yday_components", "Unknown `Date` type %s.", Rf_type2char(TYPEOF(origin)));
185 166
  }
186 167
}
187 168
188 -
// [[ register() ]]
189 -
SEXP warp_date_get_week_offset(SEXP x) {
190 -
  return date_get_week_offset(x);
191 -
}
192 -
193 -
// In non leap years there are 52 weeks + 1 day
194 -
// In leap years there are 52 weeks + 2 days
195 -
// So there are always 53 week groups
196 -
#define WEEKS_IN_YEAR 53
169 +
static struct warp_yday_components int_date_get_origin_yday_components(SEXP origin) {
170 +
  int elt = INTEGER(origin)[0];
197 171
198 -
static SEXP int_date_get_week_offset(SEXP x) {
199 -
  int* p_x = INTEGER(x);
200 -
201 -
  R_xlen_t size = Rf_xlength(x);
202 -
203 -
  SEXP out = PROTECT(Rf_allocVector(INTSXP, size));
204 -
  int* p_out = INTEGER(out);
205 -
206 -
  for (R_xlen_t i = 0; i < size; ++i) {
207 -
    int elt = p_x[i];
172 +
  if (elt == NA_INTEGER) {
173 +
    r_error(
174 +
      "int_date_get_origin_yday_components",
175 +
      "The `origin` cannot be `NA`."
176 +
    );
177 +
  }
208 178
209 -
    if (elt == NA_INTEGER) {
210 -
      p_out[i] = NA_INTEGER;
211 -
      continue;
212 -
    }
179 +
  struct warp_components components = convert_days_to_components(elt);
213 180
214 -
    struct warp_components components = convert_days_to_components(elt);
181 +
  struct warp_yday_components out;
215 182
216 -
    p_out[i] = components.year * WEEKS_IN_YEAR + components.yday / 7;
217 -
  }
183 +
  out.year_offset = components.year_offset;
184 +
  out.yday = components.yday;
218 185
219 -
  UNPROTECT(1);
220 186
  return out;
221 187
}
222 188
223 -
static SEXP dbl_date_get_week_offset(SEXP x) {
224 -
  double* p_x = REAL(x);
225 -
226 -
  R_xlen_t size = Rf_xlength(x);
227 -
228 -
  SEXP out = PROTECT(Rf_allocVector(INTSXP, size));
229 -
  int* p_out = INTEGER(out);
189 +
static struct warp_yday_components dbl_date_get_origin_yday_components(SEXP origin) {
190 +
  double origin_elt = REAL(origin)[0];
230 191
231 -
  for (R_xlen_t i = 0; i < size; ++i) {
232 -
    double x_elt = p_x[i];
192 +
  if (!R_FINITE(origin_elt)) {
193 +
    r_error(
194 +
      "dbl_date_get_origin_yday_components",
195 +
      "The `origin` must be finite."
196 +
    );
197 +
  }
233 198
234 -
    if (!R_FINITE(x_elt)) {
235 -
      p_out[i] = NA_INTEGER;
236 -
      continue;
237 -
    }
199 +
  // Drop fractional part
200 +
  int elt = origin_elt;
238 201
239 -
    // Truncate fractional pieces towards 0
240 -
    int elt = x_elt;
202 +
  struct warp_components components = convert_days_to_components(elt);
241 203
242 -
    struct warp_components components = convert_days_to_components(elt);
204 +
  struct warp_yday_components out;
243 205
244 -
    p_out[i] = components.year * WEEKS_IN_YEAR + components.yday / 7;
245 -
  }
206 +
  out.year_offset = components.year_offset;
207 +
  out.yday = components.yday;
246 208
247 -
  UNPROTECT(1);
248 209
  return out;
249 210
}
250 211
251 -
#undef WEEKS_IN_YEAR
252 -
253 212
// -----------------------------------------------------------------------------
254 213
255 214
static const int DAYS_IN_MONTH[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
@@ -312,7 +271,7 @@
Loading
312 271
 * far, we adjust it back by 1 month.
313 272
 */
314 273
315 -
static struct warp_components convert_days_to_components(int n) {
274 +
struct warp_components convert_days_to_components(int n) {
316 275
  struct warp_components components;
317 276
318 277
  int n_1_year_cycles;
@@ -339,8 +298,6 @@
Loading
339 298
  divmod(n, DAYS_IN_4_YEAR_CYCLE, &n_4_year_cycles, &n);
340 299
  divmod(n, DAYS_IN_1_YEAR_CYCLE, &n_1_year_cycles, &n);
341 300
342 -
  components.yday = n;
343 -
344 301
  int year = 1 +
345 302
    n_400_year_cycles * 400 +
346 303
    n_100_year_cycles * 100 +
@@ -350,12 +307,15 @@
Loading
350 307
  // Edge case adjustment required if we are on the border of a
351 308
  // 4 year or 400 year cycle boundary (i.e. `n = -1L`)
352 309
  if (n_1_year_cycles == 4 || n_100_year_cycles == 4) {
353 -
    components.year = (year - 1) + YEAR_OFFSET_FROM_EPOCH;
310 +
    components.year_offset = (year - 1) + YEAR_OFFSET_FROM_EPOCH;
354 311
    components.month = 12 + MONTH_ADJUSTMENT_TO_0_TO_11_RANGE;
355 312
    components.day = 31 + DAY_ADJUSTMENT_TO_0_TO_30_RANGE;
313 +
    components.yday = 365;
356 314
    return components;
357 315
  }
358 316
317 +
  components.yday = n;
318 +
359 319
  bool is_leap_year = (n_1_year_cycles == 3) &&
360 320
    (n_4_year_cycles != 24 || n_100_year_cycles == 3);
361 321
@@ -377,7 +337,7 @@
Loading
377 337
  // It will be 0-30 based already
378 338
  n -= preceding;
379 339
380 -
  components.year = year + YEAR_OFFSET_FROM_EPOCH;
340 +
  components.year_offset = year + YEAR_OFFSET_FROM_EPOCH;
381 341
  components.month = month + MONTH_ADJUSTMENT_TO_0_TO_11_RANGE;
382 342
  components.day = n;
383 343
@@ -397,22 +357,3 @@
Loading
397 357
#undef DAYS_IN_4_YEAR_CYCLE
398 358
#undef DAYS_IN_100_YEAR_CYCLE
399 359
#undef DAYS_IN_400_YEAR_CYCLE
400 -
401 -
// -----------------------------------------------------------------------------
402 -
403 -
// [[ export() ]]
404 -
SEXP warp_convert_days_to_components(SEXP n) {
405 -
  int n_ = INTEGER(n)[0];
406 -
407 -
  struct warp_components components = convert_days_to_components(n_);
408 -
409 -
  SEXP out = PROTECT(Rf_allocVector(INTSXP, 4));
410 -
411 -
  INTEGER(out)[0] = components.year;
412 -
  INTEGER(out)[1] = components.month;
413 -
  INTEGER(out)[2] = components.day;
414 -
  INTEGER(out)[3] = components.yday;
415 -
416 -
  UNPROTECT(1);
417 -
  return out;
418 -
}

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Learn more Showing 3 files with coverage changes found.

New file R/divmod.R
New
Loading file...
Changes in src/coercion.c
-13
+13
Loading file...
Changes in src/divmod.c
-19
+19
Loading file...

19 Commits

Hiding 5 contexual commits
+1 Files
-6
+58
-64
-13
-13
Hiding 1 contexual commits
Hiding 1 contexual commits
-8
-6
-2
Hiding 6 contexual commits
+122
+88
+34
Pull Request Base Commit
Files Coverage
R +2.22% 67.74%
src +3.24% 95.12%
Project Totals (18 files) 94.61%
Loading