diff --git a/DESCRIPTION b/DESCRIPTION index fd45ce4c..dd4e2282 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,7 +19,10 @@ Authors@R: person(given = "Mitchell", family = "O'Hara-Wild", role = "aut", - comment = c(ORCID = "0000-0001-6729-7695"))) + comment = c(ORCID = "0000-0001-6729-7695")), + person(given = "David", + family = "Holt", + role = "ctb")) Description: Provides a 'tbl_ts' class (the 'tsibble') for temporal data in an data- and model-oriented format. The 'tsibble' provides tools to easily manipulate and analyse temporal data, such as diff --git a/NEWS.md b/NEWS.md index 2d365fd1..00ccd9c4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,7 @@ * Dropped the support of `summarise_all()`, and made `dplyr::across()` compatible with `summarise.tbl_ts()`. * Defunct rolling functions in favour of {slider}. +* Added input support for `%Y M%M` format to `yearmonth()`. (#142) # tsibble 0.9.2 diff --git a/R/yearmonth.R b/R/yearmonth.R index 0baf4503..6c21c7ad 100644 --- a/R/yearmonth.R +++ b/R/yearmonth.R @@ -65,8 +65,27 @@ yearmonth.Date <- function(x) { #' @export yearmonth.character <- function(x) { - assertDate(x) - new_yearmonth(anydate(x)) + key_words <- regmatches(x, gregexpr("[[:alpha:]]+", x)) + search_expr <- "^[[:digit:]]{1~4}(m|mon|month)[[:digit:]]{1~4}$" + if (all(grepl(search_expr, key_words, ignore.case = TRUE))) { + yr_mon <- regmatches(x, gregexpr("[[:digit:]]+", x)) + digits_lgl <- map_lgl(yr_mon, ~ !has_length(.x, 2)) + digits_len <- map_int(yr_mon, ~ sum(nchar(.x))) + digits_ind <- nchar(flatten_chr(yr_mon)) + if (any(digits_lgl) || any(digits_len < 5) || any(digits_len > 6) || 3 == digits_ind) { + abort("Character strings are not in a standard unambiguous format.") + } + yr_lgl <- map(yr_mon, ~ grepl("[[:digit:]]{4}", .x)) + yr <- as.integer(map2_chr(yr_mon, yr_lgl, ~ .x[.y])) + mon <- as.integer(map2_chr(yr_mon, yr_lgl, ~ .x[!.y])) + if (any(mon > 12)) { + abort("Months can't be greater than 12.") + } + yearmonth(12 * (yr - 1970) + mon - 1) + } else { + assertDate(x) + new_yearmonth(anydate(x)) + } } #' @export diff --git a/tests/testthat/test-yearmonth.R b/tests/testthat/test-yearmonth.R index 015e6116..787bf4e0 100644 --- a/tests/testthat/test-yearmonth.R +++ b/tests/testthat/test-yearmonth.R @@ -12,6 +12,17 @@ test_that("input types for yearmonth()", { expect_identical(yearmonth(c(596, 576)), expected) }) +test_that("%Ym%M format for yearmonth()", { + expect_identical(yearmonth("2018 M01"), yearmonth("2018 Jan")) + expect_identical(yearmonth("2018 M1"), yearmonth("2018 Jan")) + expect_identical(yearmonth("2018mon01"), yearmonth("2018 Jan")) + expect_identical(yearmonth("2018 month 12"), yearmonth("2018 Dec")) + expect_error(yearmonth("201M1")) + expect_error(yearmonth("2018M13")) + expect_error(yearmonth("201M811")) + expect_error(yearmonth(c("2018M1", "2018 Feb", "2018M3"))) +}) + test_that("vec_arith() for yearmonth()", { expect_identical(yearmonth(x) + 1:2, yearmonth(c("2019 Oct", "2018 Mar"))) expect_identical(yearmonth(x) - 1, yearmonth(c("2019 Aug", "2017 Dec")))