From ed3dee07a23c210ca7f6c0c6df0b5e5336ea1e5a Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 24 Mar 2022 13:57:06 -0500 Subject: [PATCH] Add locale argument to date/time formatters (#330) Fixes #309 --- DESCRIPTION | 1 + NEWS.md | 3 +++ R/label-date.R | 29 +++++++++++++++++++++++------ man/date_format.Rd | 9 +++++++-- man/label_date.Rd | 11 +++++++++-- tests/testthat/test-label-date.R | 6 ++++++ 6 files changed, 49 insertions(+), 10 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 2796723b..10886bbb 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -28,6 +28,7 @@ Suggests: dichromat, ggplot2, hms (>= 0.5.0), + stringi, testthat (>= 3.0.0) Config/Needs/website: tidyverse/tidytemplate Encoding: UTF-8 diff --git a/NEWS.md b/NEWS.md index 73a31ee3..c249f2f2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,8 @@ # scales (development version) +* `label_date()` and `label_time()` gain a `locale` argument that allows you + to set the locale used to generate day and month names (#309). + * `hue_pal()` respects `h.start` once again (#288). * `col_quantile()` no longer errors if data is sufficiently skewed that we diff --git a/R/label-date.R b/R/label-date.R index 684aa8e4..900997c9 100644 --- a/R/label-date.R +++ b/R/label-date.R @@ -15,7 +15,11 @@ #' For `date_short()` a character vector of length 4 giving the format #' components to use for year, month, day, and hour respectively. #' @param tz a time zone name, see [timezones()]. Defaults -#' to UTC +#' to UTC +#' @param locale Locale to use when for day and month names. The default +#' uses the current locale. Setting this argument requires stringi, and you +#' can see a complete list of supported locales with +#' [stringi::stri_locale_list()]. #' @export #' @examples #' date_range <- function(start, days) { @@ -26,6 +30,8 @@ #' two_months <- date_range("2020-05-01", 60) #' demo_datetime(two_months) #' demo_datetime(two_months, labels = date_format("%m/%d")) +#' demo_datetime(two_months, labels = date_format("%e %b", locale = "fr")) +#' demo_datetime(two_months, labels = date_format("%e %B", locale = "es")) #' # ggplot2 provides a short-hand: #' demo_datetime(two_months, date_labels = "%m/%d") #' @@ -35,9 +41,11 @@ #' one_year <- date_range("2020-05-01", 365) #' demo_datetime(one_year, date_breaks = "month") #' demo_datetime(one_year, date_breaks = "month", labels = label_date_short()) -label_date <- function(format = "%Y-%m-%d", tz = "UTC") { - force_all(format, tz) - function(x) format(x, format, tz = tz) # format handles NAs correctly when dealing with dates +label_date <- function(format = "%Y-%m-%d", tz = "UTC", locale = NULL) { + force_all(format, tz, locale) + function(x) { + format_dt(x, format = format, tz = tz, locale = locale) + } } #' @export @@ -93,11 +101,11 @@ append_if <- function(x, cond, value) { #' @export #' @rdname label_date -label_time <- function(format = "%H:%M:%S", tz = "UTC") { +label_time <- function(format = "%H:%M:%S", tz = "UTC", locale = NULL) { force_all(format, tz) function(x) { if (inherits(x, "POSIXt")) { - format(x, format = format, tz = tz) # format handles NAs correctly for times + format_dt(x, format = format, tz = tz, locale = locale) } else if (inherits(x, "difftime")) { format(as.POSIXct(x), format = format, tz = tz) } else { @@ -110,6 +118,15 @@ label_time <- function(format = "%H:%M:%S", tz = "UTC") { } } +format_dt <- function(x, format, tz = "UTC", locale = NULL) { + if (is.null(locale)) { + format(x, format = format, tz = tz) + } else { + check_installed("stringi") + format <- stringi::stri_datetime_fstr(format) + stringi::stri_datetime_format(x, format, tz = tz, locale = locale) + } +} #' Superseded interface to `label_date()`/`label_time()` #' diff --git a/man/date_format.Rd b/man/date_format.Rd index 9a966859..8d3ab9e3 100644 --- a/man/date_format.Rd +++ b/man/date_format.Rd @@ -5,9 +5,9 @@ \alias{time_format} \title{Superseded interface to \code{label_date()}/\code{label_time()}} \usage{ -date_format(format = "\%Y-\%m-\%d", tz = "UTC") +date_format(format = "\%Y-\%m-\%d", tz = "UTC", locale = NULL) -time_format(format = "\%H:\%M:\%S", tz = "UTC") +time_format(format = "\%H:\%M:\%S", tz = "UTC", locale = NULL) } \arguments{ \item{format}{For \code{date_format()} and \code{time_format()} a date/time format @@ -18,6 +18,11 @@ components to use for year, month, day, and hour respectively.} \item{tz}{a time zone name, see \code{\link[=timezones]{timezones()}}. Defaults to UTC} + +\item{locale}{Locale to use when for day and month names. The default +uses the current locale. Setting this argument requires stringi, and you +can see a complete list of supported locales with +\code{\link[stringi:stri_locale_list]{stringi::stri_locale_list()}}.} } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} diff --git a/man/label_date.Rd b/man/label_date.Rd index 810574d6..b6671a58 100644 --- a/man/label_date.Rd +++ b/man/label_date.Rd @@ -6,11 +6,11 @@ \alias{label_time} \title{Label date/times} \usage{ -label_date(format = "\%Y-\%m-\%d", tz = "UTC") +label_date(format = "\%Y-\%m-\%d", tz = "UTC", locale = NULL) label_date_short(format = c("\%Y", "\%b", "\%d", "\%H:\%M"), sep = "\\n") -label_time(format = "\%H:\%M:\%S", tz = "UTC") +label_time(format = "\%H:\%M:\%S", tz = "UTC", locale = NULL) } \arguments{ \item{format}{For \code{date_format()} and \code{time_format()} a date/time format @@ -22,6 +22,11 @@ components to use for year, month, day, and hour respectively.} \item{tz}{a time zone name, see \code{\link[=timezones]{timezones()}}. Defaults to UTC} +\item{locale}{Locale to use when for day and month names. The default +uses the current locale. Setting this argument requires stringi, and you +can see a complete list of supported locales with +\code{\link[stringi:stri_locale_list]{stringi::stri_locale_list()}}.} + \item{sep}{Separator to use when combining date formats into a single string.} } \value{ @@ -52,6 +57,8 @@ date_range <- function(start, days) { two_months <- date_range("2020-05-01", 60) demo_datetime(two_months) demo_datetime(two_months, labels = date_format("\%m/\%d")) +demo_datetime(two_months, labels = date_format("\%e \%b", locale = "fr")) +demo_datetime(two_months, labels = date_format("\%e \%B", locale = "es")) # ggplot2 provides a short-hand: demo_datetime(two_months, date_labels = "\%m/\%d") diff --git a/tests/testthat/test-label-date.R b/tests/testthat/test-label-date.R index 5343a02b..62b56cea 100644 --- a/tests/testthat/test-label-date.R +++ b/tests/testthat/test-label-date.R @@ -18,6 +18,12 @@ test_that("time_format works correctly", { expect_equal(time_format()(na_time), NA_character_) }) +test_that("can set locale", { + x <- ISOdate(2012, 1, 1, 11, tz = "UTC") + expect_equal(date_format("%B", locale = "fr")(x), "janvier") + expect_equal(time_format("%B", locale = "fr")(x), "janvier") +}) + test_that("date_short doesn't change unexpectedly", { expect_snapshot({ dformat <- label_date_short()