├── .Rbuildignore ├── .covrignore ├── .github ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug.md │ ├── feature.md │ └── question.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── R-CMD-check.yaml │ ├── pkgdown.yaml │ └── test-coverage.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── days_months.R ├── dt_formatters.R ├── dt_replace.R ├── fdf_ftf.R ├── fdt.R ├── fdt_locales.R ├── standard_date_time.R ├── sysdata.rda ├── utils-date_time_parse.R ├── utils.R └── zzz.R ├── README.md ├── _pkgdown.yml ├── bigD.Rproj ├── codecov.yml ├── data-raw ├── X01-tzdb.R ├── X02-dates.R ├── X03-day_periods.R ├── X04-start_of_week.R ├── X05-tz_exemplar.R ├── X06-tz_metazone_names.R ├── X07-tz_metazone_users.R ├── X08-tz_map.R ├── X09-tz_formats.R ├── X10-tz_bcp_id.R ├── X11-tz_name_resolution.R ├── X12-default_locales.R ├── dates.rds ├── day_periods.rds ├── default_locales.rds ├── start_of_week.rds ├── time_zone.csv ├── tz_bcp_id.rds ├── tz_exemplar.rds ├── tz_formats.rds ├── tz_map.rds ├── tz_metazone_names.rds ├── tz_metazone_users.rds ├── tz_name_resolution.csv └── zz_process_datasets_ext.R ├── index.md ├── man ├── fdt.Rd ├── fdt_locales_lst.Rd ├── fdt_locales_vec.Rd ├── figures │ └── logo.svg ├── first_day_of_week.Rd ├── flex_d_lst.Rd ├── flex_d_vec.Rd ├── flex_t12_lst.Rd ├── flex_t12_vec.Rd ├── flex_t24_lst.Rd ├── flex_t24_vec.Rd ├── names_months.Rd ├── names_wkdays.Rd ├── standard_date.Rd ├── standard_date_time.Rd └── standard_time.Rd └── tests ├── testthat.R └── testthat ├── test-dates_times.R ├── test-dt_replace.R ├── test-fdf_ftf.R ├── test-fdt_locales.R ├── test-standard_date_time.R └── test-time_zones.R /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^bigD\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^LICENSE\.md$ 4 | ^README\.Rmd$ 5 | ^README\.md$ 6 | ^index\.md$ 7 | ^\.github$ 8 | ^_pkgdown\.yml$ 9 | ^codecov\.yml$ 10 | ^data-raw$ 11 | ^pkgdown$ 12 | ^docs$ 13 | man/figures/ 14 | tests/testthat/_snaps 15 | tests/testthat/test-dates_times.R 16 | tests/testthat/test-dt_replace.R 17 | tests/testthat/test-fdf_ftf.R 18 | tests/testthat/test-standard_date_time.R 19 | tests/testthat/test-time_zones.R 20 | ^\.covrignore$ 21 | -------------------------------------------------------------------------------- /.covrignore: -------------------------------------------------------------------------------- 1 | R/days_months.R 2 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, caste, color, religion, or sexual 10 | identity and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or advances of 31 | any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email address, 35 | without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at codeofconduct@posit.co. 63 | All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series of 85 | actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or permanent 92 | ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within the 112 | community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.1, available at 118 | . 119 | 120 | Community Impact Guidelines were inspired by 121 | [Mozilla's code of conduct enforcement ladder][https://github.com/mozilla/inclusion]. 122 | 123 | For answers to common questions about this code of conduct, see the FAQ at 124 | . Translations are available at . 125 | 126 | [homepage]: https://www.contributor-covenant.org 127 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Information and Guidelines for Contributing to **bigD** 2 | 3 | There are many ways to contribute to the ongoing development of the **bigD** package. Some contributions can be rather easy to do (e.g., fixing typos, improving documentation, filing issues for feature requests or problems, etc.) whereas other contributions can require more time and patience (like answering questions and submitting pull requests with code changes). Just know that that help provided in any capacity is very much appreciated. :) 4 | 5 | ## Filing Issues 6 | 7 | If you believe you found a bug, create a minimal [reprex](https://reprex.tidyverse.org) for your posting to the [**bigD** issue tracker](https://github.com/rstudio/bigD/issues). Try not to include anything unnecessary, just the minimal amount of code that constitutes the reproducible bug. We will try to verify the bug by running the code in the reprex provided. The quality of the reprex will reduce the amount of back-and-forth communication in trying to understand how to execute the code on our systems. 8 | 9 | ## Answering questions 10 | 11 | A great way to help is by simply answering questions. It's amazing how a little conversation could lead to better insights on a problem. Don't quite know the answer? That's okay too. We're all in this together. 12 | 13 | Where might you answer user questions? Some of the forums for Q&A on **bigD** include the [RStudio community](https://community.rstudio.com), on Twitter (a good search term is `bigD #rstats`), and on [Stack Overflow](https://stackoverflow.com). Good etiquette is key during these interactions: be a good person to all who ask questions. 14 | 15 | ### Making Pull Requests 16 | 17 | Should you consider making a pull request (PR), please file an issue first and explain the problem in some detail. If the PR is an enhancement, detail how the change would make things better for package users. Bugfix PRs also requre some explanation about the bug and how the proposed fix will remove that bug. A great way to illustrate the bug is to include a [reprex](https://reprex.tidyverse.org). While all this upfront work prior to preparing a PR can be time-consuming it opens a line of communication with the package authors and the community, perhaps leading to better enhancement or more effective fixes! 18 | 19 | Once there is consensus that a PR based on the issue would be helpful, adhering to the following process will make things proceed more quickly: 20 | 21 | * Create a separate Git branch for each PR. 22 | * Look at the GitHub Actions build status badges before and after making changes; these badges are available in the package [README](https://github.com/rstudio/bigD). 23 | * The **bigD** package follows the tidyverse [style guide](http://style.tidyverse.org) so please adopt those style guidelines in your submitted code as best as possible. 24 | * The internal documentation uses [roxygen2](https://cran.r-project.org/web/packages/roxygen2/vignettes/roxygen2.html); if your contribution requires new or revised documentation ensure that the roxygen comments are added/modified (do not modify any `.Rd` files in the `man` folder). 25 | * We use [testthat](https://cran.r-project.org/web/packages/testthat/) for code coverage; those contributions with test cases included are helpful easier to accept. 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug 3 | about: Something is wrong with bigD. 4 | title: '' 5 | labels: 'Type: ☹︎ Bug' 6 | assignees: rich-iannone 7 | --- 8 | 9 | ## Prework 10 | 11 | * [ ] Read and agree to the [code of conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html) and [contributing guidelines](https://github.com/rstudio/bigD/blob/main/.github/CONTRIBUTING.md). 12 | * [ ] If there is [already a relevant issue](https://github.com/rstudio/bigD/issues), whether open or closed, comment on the existing thread instead of posting a new issue. 13 | * [ ] Post a [minimal reproducible example](https://www.tidyverse.org/help/) so the maintainer can troubleshoot the problems you identify. A reproducible example is: 14 | * [ ] **Runnable**: post enough R code and data so any onlooker can create the error on their own computer. 15 | * [ ] **Minimal**: reduce runtime wherever possible and remove complicated details that are irrelevant to the issue at hand. 16 | * [ ] **Readable**: format your code according to the [tidyverse style guide](https://style.tidyverse.org/). 17 | 18 | ## Description 19 | 20 | Describe the bug clearly and concisely. 21 | 22 | ## Reproducible example 23 | 24 | Provide a [minimal reproducible example](https://www.tidyverse.org/help/) here so the maintainer can troubleshoot the problems you identify. 25 | 26 | ## Expected result 27 | 28 | What should have happened? Please be as specific as possible. 29 | 30 | ## Session info 31 | 32 | End the reproducible example with a call to `sessionInfo()` in the same session (e.g. `reprex(session_info = TRUE)`) and include the output. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: New feature 3 | about: Suggest a new feature. 4 | title: '' 5 | labels: 'Type: ★ Enhancement' 6 | assignees: rich-iannone 7 | --- 8 | 9 | ## Prework 10 | 11 | - [ ] Read and abide by **bigD**'s [code of conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html) and [contributing guidelines](https://github.com/rstudio/bigD/blob/main/.github/CONTRIBUTING.md). 12 | - [ ] Search for duplicates among the [existing issues](https://github.com/rstudio/bigD/issues) (both open and closed). 13 | 14 | ## Proposal 15 | 16 | Describe the new feature clearly and concisely. If applicable, write a minimal example in R code or pseudo-code to show input, usage, and desired output. 17 | 18 | To help us read any code you include (optional) please try to follow the [tidyverse style guide](https://style.tidyverse.org/). The `style_text()` and `style_file()` functions from the [`styler`](https://github.com/r-lib/styler) package make it easier. 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask a question. 4 | title: '' 5 | labels: 'Type: ⁇ Question' 6 | assignees: '' 7 | --- 8 | 9 | ## Prework 10 | 11 | * [ ] Read and agree to the [code of conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html) and [contributing guidelines](https://github.com/rstudio/bigD/blob/main/.github/CONTRIBUTING.md). 12 | * [ ] If there is [already a relevant issue](https://github.com/rstudio/bigD/issues), whether open or closed, comment on the existing thread instead of posting a new issue. 13 | * [ ] For any problems you identify, a [minimal reproducible example](https://www.tidyverse.org/help/) so the maintainer can troubleshoot. A reproducible example is: 14 | * [ ] **Runnable**: post enough R code and data so any onlooker can create the error on their own computer. 15 | * [ ] **Minimal**: reduce runtime wherever possible and remove complicated details that are irrelevant to the issue at hand. 16 | * [ ] **Readable**: format your code according to the [tidyverse style guide](https://style.tidyverse.org/). 17 | 18 | ## Question 19 | 20 | What would you like to know? 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | Thank you for contributing to **bigD**! To make this process easier for everyone, please explain the context and purpose of your contribution. Also, list the changes made to the existing code or documentation. 4 | 5 | # Related GitHub Issues and PRs 6 | 7 | - Ref: # 8 | 9 | # Checklist 10 | 11 | - [ ] I understand and agree to the [Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html). 12 | - [ ] I have listed any major changes in the [NEWS](https://github.com/rstudio/bigD/blob/main/NEWS.md). 13 | - [ ] I have added [`testthat`](https://github.com/r-lib/testthat) unit tests to [`tests/testthat`](https://github.com/rstudio/bigD/tree/main/tests/testthat) for any new functionality. 14 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: R-CMD-check.yaml 10 | 11 | permissions: read-all 12 | 13 | jobs: 14 | R-CMD-check: 15 | runs-on: ${{ matrix.config.os }} 16 | 17 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | config: 23 | - {os: macos-latest, r: 'release'} 24 | - {os: windows-latest, r: 'release'} 25 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 26 | - {os: ubuntu-latest, r: 'release'} 27 | - {os: ubuntu-latest, r: 'oldrel-1'} 28 | - {os: ubuntu-latest, r: '3.6'} 29 | 30 | env: 31 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 32 | R_KEEP_PKG_SOURCE: yes 33 | 34 | steps: 35 | - uses: actions/checkout@v4 36 | 37 | - uses: r-lib/actions/setup-pandoc@v2 38 | 39 | - uses: r-lib/actions/setup-r@v2 40 | with: 41 | r-version: ${{ matrix.config.r }} 42 | http-user-agent: ${{ matrix.config.http-user-agent }} 43 | use-public-rspm: true 44 | 45 | - uses: r-lib/actions/setup-r-dependencies@v2 46 | with: 47 | extra-packages: any::rcmdcheck 48 | needs: check 49 | 50 | - uses: r-lib/actions/check-r-package@v2 51 | with: 52 | upload-snapshots: true 53 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 54 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown.yaml 13 | 14 | permissions: read-all 15 | 16 | jobs: 17 | pkgdown: 18 | runs-on: ubuntu-latest 19 | # Only restrict concurrency for non-PR jobs 20 | concurrency: 21 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 22 | env: 23 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 24 | permissions: 25 | contents: write 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - uses: r-lib/actions/setup-pandoc@v2 30 | 31 | - uses: r-lib/actions/setup-r@v2 32 | with: 33 | use-public-rspm: true 34 | 35 | - uses: r-lib/actions/setup-r-dependencies@v2 36 | with: 37 | extra-packages: any::pkgdown, local::. 38 | needs: website 39 | 40 | - name: Build site 41 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 42 | shell: Rscript {0} 43 | 44 | - name: Deploy to GitHub pages 🚀 45 | if: github.event_name != 'pull_request' 46 | uses: JamesIves/github-pages-deploy-action@v4.5.0 47 | with: 48 | clean: false 49 | branch: gh-pages 50 | folder: docs 51 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: test-coverage.yaml 10 | 11 | permissions: read-all 12 | 13 | jobs: 14 | test-coverage: 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - uses: r-lib/actions/setup-r@v2 23 | with: 24 | use-public-rspm: true 25 | 26 | - uses: r-lib/actions/setup-r-dependencies@v2 27 | with: 28 | extra-packages: any::covr, any::xml2 29 | needs: coverage 30 | 31 | - name: Test coverage 32 | run: | 33 | cov <- covr::package_coverage( 34 | quiet = FALSE, 35 | clean = FALSE, 36 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") 37 | ) 38 | covr::to_cobertura(cov) 39 | shell: Rscript {0} 40 | 41 | - uses: codecov/codecov-action@v4 42 | with: 43 | fail_ci_if_error: ${{ github.event_name != 'pull_request' && true || false }} 44 | file: ./cobertura.xml 45 | plugin: noop 46 | disable_search: true 47 | token: ${{ secrets.CODECOV_TOKEN }} 48 | 49 | - name: Show testthat output 50 | if: always() 51 | run: | 52 | ## -------------------------------------------------------------------- 53 | find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true 54 | shell: bash 55 | 56 | - name: Upload test results 57 | if: failure() 58 | uses: actions/upload-artifact@v4 59 | with: 60 | name: coverage-test-failures 61 | path: ${{ runner.temp }}/package 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # History files 2 | .Rhistory 3 | .Rapp.history 4 | 5 | # .DS_Store 6 | .DS_Store 7 | 8 | # Session Data files 9 | .RData 10 | 11 | # Example code in package build process 12 | *-Ex.R 13 | 14 | # Output files from R CMD build 15 | /*.tar.gz 16 | 17 | # Output files from R CMD check 18 | /*.Rcheck/ 19 | 20 | # RStudio files 21 | .Rproj.user/ 22 | 23 | # produced vignettes 24 | vignettes/*.html 25 | vignettes/*.pdf 26 | 27 | # OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3 28 | .httr-oauth 29 | 30 | # knitr and R markdown default cache directories 31 | /*_cache/ 32 | /cache/ 33 | 34 | # Temporary files created by R markdown 35 | *.utf8.md 36 | *.knit.md 37 | 38 | # Shiny token, see https://shiny.rstudio.com/articles/shinyapps.html 39 | rsconnect/ 40 | .Rproj.user 41 | docs 42 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Type: Package 2 | Package: bigD 3 | Title: Flexibly Format Dates and Times to a Given Locale 4 | Version: 0.3.1.9000 5 | Description: Format dates and times flexibly and to whichever locales 6 | make sense. Parses dates, times, and date-times in various formats 7 | (including string-based ISO 8601 constructions). The formatting syntax gives 8 | the user many options for formatting the date and time output in a precise 9 | manner. Time zones in the input can be expressed in multiple ways and there 10 | are many options for formatting time zones in the output as well. Several of 11 | the provided helper functions allow for automatic generation of locale-aware 12 | formatting patterns based on date/time skeleton formats and standardized 13 | date/time formats with varying specificity. 14 | Authors@R: c( 15 | person("Richard", "Iannone", , "rich@posit.co", role = c("aut", "cre"), 16 | comment = c(ORCID = "0000-0003-3925-190X")), 17 | person("Olivier", "Roy", role = "ctb"), 18 | person("Posit Software, PBC", role = c("cph", "fnd")) 19 | ) 20 | License: MIT + file LICENSE 21 | URL: https://rstudio.github.io/bigD/, https://github.com/rstudio/bigD 22 | BugReports: https://github.com/rstudio/bigD/issues 23 | Encoding: UTF-8 24 | RoxygenNote: 7.3.2 25 | Depends: 26 | R (>= 3.6.0) 27 | Suggests: 28 | testthat (>= 3.0.0), 29 | vctrs (>= 0.5.0) 30 | Roxygen: list(markdown = TRUE) 31 | Config/testthat/edition: 3 32 | Config/testthat/parallel: true 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2018-2023 2 | COPYRIGHT HOLDER: bigD authors 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2018-2023 bigD authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(fdt) 4 | export(fdt_locales_lst) 5 | export(fdt_locales_vec) 6 | export(first_day_of_week) 7 | export(flex_d_lst) 8 | export(flex_d_vec) 9 | export(flex_t12_lst) 10 | export(flex_t12_vec) 11 | export(flex_t24_lst) 12 | export(flex_t24_vec) 13 | export(names_months) 14 | export(names_wkdays) 15 | export(standard_date) 16 | export(standard_date_time) 17 | export(standard_time) 18 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # bigD (development version) 2 | 3 | # bigD 0.3.1 4 | 5 | * Introduced performance improvements (@olivroy, #15, #16, #17). 6 | 7 | * Internal functions `dt_MM()`, `dt_yyy()` and friends are no longer used, as we 8 | use a new internal structure to avoid duplicate calculations 9 | 10 | # bigD 0.3.0 11 | 12 | * The locale's territory is now better resolved for week in month calculations. 13 | 14 | * Corrected the formatting of localized GMT formats. 15 | 16 | * Depend on R >= 3.3 to use `strrep()` and `startsWith()` 17 | 18 | # bigD 0.2.0 19 | 20 | * New package with everything you need to format dates and times. 21 | -------------------------------------------------------------------------------- /R/days_months.R: -------------------------------------------------------------------------------- 1 | #' Get a vector of all the short weekday names 2 | #' 3 | #' @description 4 | #' The `names_wkdays()` function produces a vector of all short weekday names 5 | #' used by the **bigD** package. 6 | #' 7 | #' @return 8 | #' A character vector of short weekday names. 9 | #' 10 | #' @examples 11 | #' # Let's get all the short weekday names with 12 | #' # the `names_wkdays()` function 13 | #' names_wkdays() 14 | #' 15 | #' @export 16 | names_wkdays <- function() { 17 | c( 18 | "sun", "mon", "tue", "wed", "thu", "fri", "sat" 19 | ) 20 | } 21 | 22 | #' Get a vector of all the short month names 23 | #' 24 | #' @description 25 | #' The `names_months()` function produces a vector of all short month names 26 | #' used by the **bigD** package. 27 | #' 28 | #' @return 29 | #' A character vector of short month names. 30 | #' 31 | #' @examples 32 | #' # Let's get all the short month names with 33 | #' # the `names_months()` function 34 | #' names_months() 35 | #' 36 | #' @export 37 | names_months <- function() { 38 | c( 39 | "jan", "feb", "mar", "apr", "may", "jun", 40 | "jul", "aug", "sep", "oct", "nov", "dec" 41 | ) 42 | } 43 | 44 | #' Get a named vector of all first-day-of-the-week names for different regions 45 | #' 46 | #' @description 47 | #' The `names_months()` function produces a vector of all short month names 48 | #' used by the **bigD** package. 49 | #' 50 | #' @return 51 | #' A character vector of short month names. 52 | #' 53 | #' @examples 54 | #' # Let's get a vector of regions where the 55 | #' # first day of the week is Saturday 56 | #' names(first_day_of_week()[first_day_of_week() == "sat"]) 57 | #' 58 | #' @export 59 | first_day_of_week <- function() { 60 | c( 61 | "001" = "mon", 62 | "AD" = "mon", 63 | "AE" = "sat", 64 | "AF" = "sat", 65 | "AG" = "sun", 66 | "AI" = "mon", 67 | "AL" = "mon", 68 | "AM" = "mon", 69 | "AN" = "mon", 70 | "AR" = "mon", 71 | "AS" = "sun", 72 | "AT" = "mon", 73 | "AU" = "mon", 74 | "AX" = "mon", 75 | "AZ" = "mon", 76 | "BA" = "mon", 77 | "BD" = "sun", 78 | "BE" = "mon", 79 | "BG" = "mon", 80 | "BH" = "sat", 81 | "BM" = "mon", 82 | "BN" = "mon", 83 | "BR" = "sun", 84 | "BS" = "sun", 85 | "BT" = "sun", 86 | "BW" = "sun", 87 | "BY" = "mon", 88 | "BZ" = "sun", 89 | "CA" = "sun", 90 | "CH" = "mon", 91 | "CL" = "mon", 92 | "CM" = "mon", 93 | "CN" = "sun", 94 | "CO" = "sun", 95 | "CR" = "mon", 96 | "CY" = "mon", 97 | "CZ" = "mon", 98 | "DE" = "mon", 99 | "DJ" = "sat", 100 | "DK" = "mon", 101 | "DM" = "sun", 102 | "DO" = "sun", 103 | "DZ" = "sat", 104 | "EC" = "mon", 105 | "EE" = "mon", 106 | "EG" = "sat", 107 | "ES" = "mon", 108 | "ET" = "sun", 109 | "FI" = "mon", 110 | "FJ" = "mon", 111 | "FO" = "mon", 112 | "FR" = "mon", 113 | "GB" = "mon", 114 | "GE" = "mon", 115 | "GF" = "mon", 116 | "GP" = "mon", 117 | "GR" = "mon", 118 | "GT" = "sun", 119 | "GU" = "sun", 120 | "HK" = "sun", 121 | "HN" = "sun", 122 | "HR" = "mon", 123 | "HU" = "mon", 124 | "ID" = "sun", 125 | "IE" = "mon", 126 | "IL" = "sun", 127 | "IN" = "sun", 128 | "IQ" = "sat", 129 | "IR" = "sat", 130 | "IS" = "mon", 131 | "IT" = "mon", 132 | "JM" = "sun", 133 | "JO" = "sat", 134 | "JP" = "sun", 135 | "KE" = "sun", 136 | "KG" = "mon", 137 | "KH" = "sun", 138 | "KR" = "sun", 139 | "KW" = "sat", 140 | "KZ" = "mon", 141 | "LA" = "sun", 142 | "LB" = "mon", 143 | "LI" = "mon", 144 | "LK" = "mon", 145 | "LT" = "mon", 146 | "LU" = "mon", 147 | "LV" = "mon", 148 | "LY" = "sat", 149 | "MC" = "mon", 150 | "MD" = "mon", 151 | "ME" = "mon", 152 | "MH" = "sun", 153 | "MK" = "mon", 154 | "MM" = "sun", 155 | "MN" = "mon", 156 | "MO" = "sun", 157 | "MQ" = "mon", 158 | "MT" = "sun", 159 | "MV" = "fri", 160 | "MX" = "sun", 161 | "MY" = "mon", 162 | "MZ" = "sun", 163 | "NI" = "sun", 164 | "NL" = "mon", 165 | "NO" = "mon", 166 | "NP" = "sun", 167 | "NZ" = "mon", 168 | "OM" = "sat", 169 | "PA" = "sun", 170 | "PE" = "sun", 171 | "PH" = "sun", 172 | "PK" = "sun", 173 | "PL" = "mon", 174 | "PR" = "sun", 175 | "PT" = "sun", 176 | "PY" = "sun", 177 | "QA" = "sat", 178 | "RE" = "mon", 179 | "RO" = "mon", 180 | "RS" = "mon", 181 | "RU" = "mon", 182 | "SA" = "sun", 183 | "SD" = "sat", 184 | "SE" = "mon", 185 | "SG" = "sun", 186 | "SI" = "mon", 187 | "SK" = "mon", 188 | "SM" = "mon", 189 | "SV" = "sun", 190 | "SY" = "sat", 191 | "TH" = "sun", 192 | "TJ" = "mon", 193 | "TM" = "mon", 194 | "TR" = "mon", 195 | "TT" = "sun", 196 | "TW" = "sun", 197 | "UA" = "mon", 198 | "UM" = "sun", 199 | "US" = "sun", 200 | "UY" = "mon", 201 | "UZ" = "mon", 202 | "VA" = "mon", 203 | "VE" = "sun", 204 | "VI" = "sun", 205 | "VN" = "mon", 206 | "WS" = "sun", 207 | "XK" = "mon", 208 | "YE" = "sun", 209 | "ZA" = "sun", 210 | "ZW" = "sun" 211 | ) 212 | } 213 | -------------------------------------------------------------------------------- /R/dt_formatters.R: -------------------------------------------------------------------------------- 1 | zero_pad_to_width <- function(value, width) { 2 | formatC(value, width = width, flag = "0", format = "d") 3 | } 4 | 5 | format_tz_offset_min_sec <- function( 6 | tz_offset, 7 | use_colon = TRUE, 8 | optional_min = TRUE, 9 | use_z = FALSE, 10 | hours_width = 2, 11 | prepend_with = NULL 12 | ) { 13 | 14 | if (use_z && tz_offset == 0) { 15 | return("Z") 16 | } 17 | 18 | hours_val <- zero_pad_to_width(abs(trunc(tz_offset)), hours_width) 19 | minutes_val <- zero_pad_to_width(abs((tz_offset - trunc(tz_offset))) * 60, 2) 20 | 21 | paste0( 22 | prepend_with, # can be NULL (will not print) 23 | ifelse(tz_offset >= 0, "+", "-"), 24 | hours_val, 25 | if (optional_min && minutes_val == "00") { 26 | "" 27 | } else if (use_colon) { 28 | paste0(":", minutes_val) 29 | } else { 30 | minutes_val 31 | } 32 | ) 33 | } 34 | 35 | get_iso_week <- function(input) { 36 | as.integer(strftime(as.Date(input, format = "%Y-%m-%d"), format = "%V")) 37 | } 38 | 39 | format_yearweek <- function(input) { 40 | 41 | date_input <- as.Date(input, format = "%Y-%m-%d") 42 | day_input <- as.integer(format(date_input, format = "%d")) 43 | month_input <- as.integer(format(date_input, format = "%m")) 44 | 45 | yearweek_int <- as.integer(strftime(date_input, format = "%Y%V")) 46 | week_int <- get_iso_week(input = date_input) 47 | 48 | yearweek_int_res <- 49 | ifelse( 50 | day_input > 7 & week_int == 1, 51 | yearweek_int + 100, 52 | ifelse( 53 | as.integer(format(date_input, format = "%m")) == 1 & week_int > 51, 54 | yearweek_int - 100, 55 | yearweek_int 56 | ) 57 | ) 58 | 59 | yearweek_int_res_char <- as.character(yearweek_int_res) 60 | 61 | week_part <- substr(yearweek_int_res_char, nchar(yearweek_int_res_char) - 1, nchar(yearweek_int_res_char)) 62 | year_part <- substr(yearweek_int_res_char, 1, nchar(yearweek_int_res_char) - 2) 63 | 64 | paste0(year_part, "-W", week_part) 65 | } 66 | 67 | format_fractional_seconds <- function(input, digits) { 68 | 69 | frac_s <- gsub("0.", "", as.character(as.POSIXlt(input)$sec %% 1), fixed = TRUE) 70 | 71 | if (frac_s == "0") { 72 | return(strrep("0", digits)) 73 | } 74 | 75 | n_char <- nchar(frac_s) 76 | 77 | if (n_char > 3) { 78 | frac_s <- substr(frac_s, 1, 3) 79 | } 80 | 81 | if (digits < 3) { 82 | frac_s <- substr(frac_s, 1, digits) 83 | } 84 | 85 | if (digits > 3) { 86 | frac_s <- paste0(frac_s, strrep("0", digits - 3)) 87 | } 88 | 89 | frac_s 90 | } 91 | 92 | get_milliseconds_in_day <- function(input) { 93 | 94 | input_lt <- as.POSIXlt(input) 95 | 96 | # Get the number of milliseconds elapsed in day 97 | as.integer( 98 | ( 99 | ( 100 | (input_lt$hour * 3600) + 101 | (input_lt$min * 60) 102 | ) * 1000 103 | ) + 104 | trunc(input_lt$sec * 1000, digits = 0) 105 | ) 106 | } 107 | 108 | get_locale_territory <- function(locale) { 109 | 110 | locale_has_territory <- grepl("^[a-zA-Z_-]+?(-|_)?([A-Z]{2}|001|419|150)", locale) 111 | 112 | if (locale_has_territory) { 113 | 114 | territory <- gsub(".*([A-Z]{2}|001|419|150).*", "\\1", locale) 115 | return(territory) 116 | } 117 | 118 | default_locale_name <- 119 | default_locales[default_locales$base_locale == gsub("_", "-", locale, fixed = TRUE), "default_locale"] 120 | 121 | if (length(default_locale_name) == 0L) { 122 | 123 | default_locale_name <- 124 | default_locales[startsWith(default_locales$base_locale, locale), "default_locale"][1] 125 | } 126 | 127 | territory <- gsub(".*([A-Z]{2}|001|419|150).*", "\\1", default_locale_name) 128 | 129 | if (is.na(territory)) { 130 | territory <- "001" 131 | } 132 | 133 | territory 134 | } 135 | 136 | get_week_in_month <- function(input, locale) { 137 | 138 | date_input <- as.Date(input, format = "%Y-%m-%d") 139 | 140 | territory <- get_locale_territory(locale = locale) 141 | 142 | start_of_week_territory <- start_of_week[start_of_week$territory == territory, "day_of_week"] 143 | 144 | if (length(start_of_week_territory) == 0L) { 145 | start_of_week <- "mon" 146 | } else { 147 | start_of_week <- start_of_week_territory[[1]] 148 | } 149 | 150 | if (start_of_week == "mon") { 151 | week_number <- get_iso_week(input = input) 152 | } else { 153 | week_number <- as.integer(format(date_input, format = "%U")) 154 | } 155 | 156 | min_date_in_month <- as.Date(paste0(format(date_input, "%Y-%m"), "-01")) 157 | week_num_mininmum <- as.integer(format(min_date_in_month, format = "%U")) 158 | 159 | week_number - (week_num_mininmum - 1) 160 | } 161 | 162 | get_dow_n_in_month <- function(input) { 163 | 164 | date_input <- as.Date(input, format = "%Y-%m-%d") 165 | day_input <- as.integer(format(date_input, format = "%d")) 166 | 167 | length(seq(from = day_input, to = 1, by = -7)) 168 | } 169 | 170 | zero_weekday <- function(input) { 171 | as.integer((as.integer(format(input, "%w")) + 6) %% 7 + 1L) 172 | } 173 | 174 | zero_thursday <- function(date) { 175 | date - zero_weekday(input = as.Date(date)) + 3L 176 | } 177 | 178 | week_date_as_datetime <- function(input_str) { 179 | 180 | year <- as.integer(substr(input_str, 1, 4)) 181 | week <- as.integer(substr(input_str, 6, 7)) 182 | 183 | if (nchar(input_str) == 8) { 184 | weekday <- as.integer(substr(input_str, 8, 8)) 185 | } else { 186 | weekday <- 1L 187 | } 188 | 189 | january_fourth <- as.Date(paste(year, "01", "04", sep = "-"), tz = "UTC") 190 | first_thursday <- zero_thursday(date = january_fourth) 191 | nearest_thursday <- first_thursday + 7 * (week - 1) 192 | final_date <- nearest_thursday - 4 + weekday + 1 193 | as.POSIXct(as.character(final_date), tz = "UTC") 194 | } 195 | 196 | month_day_as_datetime <- function(input_str) { 197 | 198 | month_str <- substr(input_str, 1, 2) 199 | day_str <- substr(input_str, 3, 4) 200 | 201 | date_now <- as.character(Sys.Date()) 202 | year_str <- substr(date_now, 1, 4) 203 | 204 | as.POSIXct(paste(year_str, month_str, day_str, sep = "-"), tz = "UTC") 205 | } 206 | 207 | get_modified_julian_day <- function(input) { 208 | 209 | date_input <- as.Date(input, format = "%Y-%m-%d") 210 | day_input <- as.integer(format(date_input, format = "%d")) 211 | month_input <- as.integer(format(date_input, format = "%m")) 212 | year_input <- as.integer(format(date_input, format = "%Y")) 213 | 214 | if (month_input <= 2) { 215 | m_x <- 1 216 | } else { 217 | m_x <- 0 218 | } 219 | 220 | m_m <- (12 * m_x) + month_input 221 | y_x <- year_input - m_x 222 | y_y <- floor(0.01 * y_x) 223 | 224 | floor(365.25 * y_x) + 225 | floor(30.6001 * (1 + m_m)) + 226 | day_input + 227 | 1720995 + 228 | (2 - (y_y - floor(0.25 * y_y))) - 229 | 2400001 230 | } 231 | 232 | get_flexible_day_period <- function(input, locale) { 233 | 234 | locale_in_day_periods_tbl <- locale %in% day_periods[["locale"]] 235 | 236 | # Verify that the supplied locale is defined within the `day_periods` 237 | # table (use the base locale if necessary); return NA if not found 238 | if (!locale_in_day_periods_tbl) { 239 | 240 | locale <- sub("^([a-z]*).*", "\\1", locale) 241 | 242 | if (!locale %in% day_periods[["locale"]]) { 243 | return(NA_character_) 244 | } 245 | } 246 | 247 | input_lt <- as.POSIXlt(input) 248 | 249 | time_str <- 250 | paste0( 251 | zero_pad_to_width( 252 | value = input_lt$hour, 253 | width = 2 254 | ), 255 | ":", 256 | zero_pad_to_width( 257 | value = input_lt$min, 258 | width = 2 259 | ) 260 | ) 261 | 262 | day_periods_locale <- day_periods[day_periods$locale == locale, ] 263 | 264 | if (time_str == "00:00" && "00:00" %in% day_periods_locale$at) { 265 | 266 | period <- 267 | day_periods_locale[ 268 | !is.na(day_periods_locale$at) & 269 | day_periods_locale$at == "00:00", 270 | "period", 271 | drop = TRUE 272 | ] 273 | 274 | } else if (time_str == "12:00" && "12:00" %in% day_periods_locale$at) { 275 | 276 | period <- 277 | day_periods_locale[ 278 | !is.na(day_periods_locale$at) & 279 | day_periods_locale$at == "12:00", "period", drop = TRUE] 280 | 281 | } else { 282 | 283 | day_periods_locale_from_to <- 284 | day_periods_locale[ 285 | !is.na(day_periods_locale$from), c("period", "from", "to") 286 | ] 287 | 288 | to_next_day_idx <- 289 | which(day_periods_locale_from_to$to < day_periods_locale_from_to$from) 290 | 291 | if (length(to_next_day_idx) == 1) { 292 | 293 | row_1 <- row_2 <- day_periods_locale_from_to[to_next_day_idx, ] 294 | 295 | row_1$to <- "24:00" 296 | row_2$from <- "00:00" 297 | 298 | day_periods_locale_from_to <- 299 | rbind( 300 | day_periods_locale_from_to[-to_next_day_idx, ], 301 | row_1, 302 | row_2 303 | ) 304 | } 305 | 306 | period <- 307 | day_periods_locale_from_to[ 308 | day_periods_locale_from_to$from <= time_str & 309 | time_str < day_periods_locale_from_to$to, "period", drop = TRUE] 310 | } 311 | 312 | period 313 | } 314 | 315 | get_noon_midnight_period <- function(input, locale) { 316 | 317 | locale_in_day_periods_tbl <- locale %in% day_periods[["locale"]] 318 | 319 | # Verify that the supplied locale is defined within the `day_periods` 320 | # table (use the base locale if necessary); return NA if not found 321 | if (!locale_in_day_periods_tbl) { 322 | 323 | # Modify locale 324 | locale <- sub("^([a-z]*).*", "\\1", locale) 325 | 326 | if (!locale %in% day_periods[["locale"]]) { 327 | return(NA_character_) 328 | } 329 | } 330 | 331 | input_lt <- as.POSIXlt(input, tz = "UTC") 332 | 333 | time_str <- 334 | paste0( 335 | zero_pad_to_width( 336 | value = input_lt$hour, 337 | width = 2 338 | ), 339 | ":", 340 | zero_pad_to_width( 341 | value = input_lt$min, 342 | width = 2 343 | ) 344 | ) 345 | 346 | day_periods_locale <- day_periods[day_periods$locale == locale, ] 347 | 348 | if (time_str == "00:00" && "00:00" %in% day_periods_locale$at) { 349 | 350 | period <- 351 | day_periods_locale[ 352 | !is.na(day_periods_locale$at) & 353 | day_periods_locale$at == "00:00", "period", drop = TRUE] 354 | 355 | } else if (time_str == "12:00" && "12:00" %in% day_periods_locale$at) { 356 | 357 | period <- 358 | day_periods_locale[ 359 | !is.na(day_periods_locale$at) & 360 | day_periods_locale$at == "12:00", "period", drop = TRUE] 361 | 362 | } else { 363 | 364 | period <- NA_character_ 365 | } 366 | 367 | period 368 | } 369 | 370 | format_quarter <- function(input) { 371 | 372 | date_input <- as.Date(input, format = "%Y-%m-%d") 373 | 374 | month_input <- as.integer(format(date_input, format = "%m")) 375 | year_input <- as.integer(format(date_input, format = "%Y")) 376 | 377 | if (month_input < 4L) { 378 | quarter <- 1 379 | } else if (month_input >= 4L && month_input < 7L) { 380 | quarter <- 2 381 | } else if (month_input >= 7L && month_input < 10L) { 382 | quarter <- 3 383 | } else if (month_input >= 10L) { 384 | quarter <- 4 385 | } 386 | 387 | paste0(year_input, "-Q", quarter) 388 | } 389 | -------------------------------------------------------------------------------- /R/fdf_ftf.R: -------------------------------------------------------------------------------- 1 | #' Get a vector of all flexible date types 2 | #' 3 | #' @description 4 | #' The `flex_d_vec()` function produces a vector of all supported flexible date 5 | #' types in the **bigD** package. These types are essentially identifiers for 6 | #' classes of cross-locale date formatting, so, none of these should be used 7 | #' directly in the `format` argument of the `fdt()` function (use the 8 | #' [flex_d_lst] object for that). 9 | #' 10 | #' @return 11 | #' A character vector of supported flexible date types. 12 | #' 13 | #' @export 14 | flex_d_vec <- function() { 15 | 16 | c( 17 | "yMd", 18 | "yMEd", 19 | "yMMM", 20 | "yMMMM", 21 | "yMMMd", 22 | "yMMMEd", 23 | "GyMd", 24 | "GyMMMd", 25 | "GyMMMEd", 26 | "yM", 27 | "Md", 28 | "MEd", 29 | "MMMd", 30 | "MMMEd", 31 | "MMMMd", 32 | "GyMMM", 33 | "yQQQ", 34 | "yQQQQ", 35 | "Gy", 36 | "y", 37 | "M", 38 | "MMM", 39 | "d", 40 | "Ed", 41 | #"E", # TODO: need to determine local day of week for this to work 42 | "MMMMW", # as "MMMMW-count-*" `one`, `other`, `few`, `many`, `two`, `zero` 43 | "yw" # as "yw-*" `one`, `other`, `few`, `many`, `two`, `zero` 44 | ) 45 | } 46 | 47 | #' Get a vector of all 12-hour flexible time types 48 | #' 49 | #' @description 50 | #' The `flex_t12_vec()` function produces a vector of all supported flexible 51 | #' 12-hour time types in the **bigD** package. These types are essentially 52 | #' identifiers for classes of cross-locale time formatting, so, none of these 53 | #' should be used directly in the `format` argument of the `fdt()` function (use 54 | #' the [flex_t12_lst] object for that). 55 | #' 56 | #' @return 57 | #' A character vector of supported 12-hour flexible time types. 58 | #' 59 | #' @export 60 | flex_t12_vec <- function() { 61 | 62 | c( 63 | "hms", 64 | "hm", 65 | "h", 66 | "Ehm", 67 | "Ehms", 68 | "EBhms", 69 | "Bhms", 70 | "EBhm", 71 | "Bhm", 72 | "Bh", 73 | "hmsv", 74 | "hmv" 75 | ) 76 | } 77 | 78 | #' Get a vector of all 24-hour flexible time types 79 | #' 80 | #' @description 81 | #' The `flex_t24_vec()` function produces a vector of all supported flexible 82 | #' 24-hour time types in the **bigD** package. These types are essentially 83 | #' identifiers for classes of cross-locale time formatting, so, none of these 84 | #' should be used directly in the `format` argument of the `fdt()` function (use 85 | #' the [flex_t24_lst] object for that). 86 | #' 87 | #' @return 88 | #' A character vector of supported 24-hour flexible time types. 89 | #' 90 | #' @export 91 | flex_t24_vec <- function() { 92 | 93 | c( 94 | "Hms", 95 | "Hm", 96 | "H", 97 | "EHm", 98 | "EHms", 99 | "Hmsv", 100 | "Hmv", 101 | "ms" 102 | ) 103 | } 104 | 105 | #' A list of all flexible date types 106 | #' 107 | #' @description 108 | #' The `flex_d_lst` object is a list of widely supported flexible date types. 109 | #' Flexible date types are classes of date formatting which can be translated 110 | #' across locales. There are `r length(flex_d_lst)` flexible date types in 111 | #' `flex_d_lst`. 112 | #' 113 | #' @return A list where each element corresponds to a classifier for a flexible 114 | #' date type. 115 | #' 116 | #' @section Examples: 117 | #' 118 | #' The `flex_d_lst` object can be incredibly useful when you need to get a 119 | #' format for date formatting that works across all locales. You can avoid 120 | #' typing errors by using this list and every flexible date type from this list 121 | #' is guaranteed to work across all supported locales. In this example, we'll 122 | #' use the `"yMMMEd"` flexible date type by accessing it from the `flex_d_lst` 123 | #' object. 124 | #' 125 | #' ```r 126 | #' fdt( 127 | #' input = "2018-07-04 22:05", 128 | #' format = flex_d_lst$yMMMEd, 129 | #' locale = "en" 130 | #' ) 131 | #' ``` 132 | #' ``` 133 | #' #> [1] "Wed, Jul 4, 2018" 134 | #' ``` 135 | #' 136 | #' If we wanted this in a different locale, the locale-specific `format` pattern 137 | #' behind the flexible date identifier would ensure consistency while moving to 138 | #' that locale. Let's use the `fdt_locales_lst` object in the same spirit to 139 | #' specify the French (Canada) locale. 140 | #' 141 | #' ```r 142 | #' fdt( 143 | #' input = "2018-07-04 22:05", 144 | #' format = flex_d_lst$yMMMEd, 145 | #' locale = fdt_locales_lst$fr_CA 146 | #' ) 147 | #' ``` 148 | #' ``` 149 | #' #> [1] "mer. 4 juill. 2018" 150 | #' ``` 151 | #' 152 | #' @export 153 | flex_d_lst <- 154 | lapply( 155 | stats::setNames(as.list(flex_d_vec()), as.list(flex_d_vec())), 156 | FUN = function(x) { 157 | class(x) <- c("date_time_pattern", "flex_d") 158 | x 159 | } 160 | ) 161 | 162 | #' A list of all 12-hour flexible time types 163 | #' 164 | #' @description 165 | #' The `flex_t12_lst` object is a list of the 12-hour flexible time types which 166 | #' are widely supported. Flexible time types are classes of time formatting 167 | #' which can be translated across locales. There are `r length(flex_t12_lst)` 168 | #' flexible time types of the 12-hour variety in `flex_t12_lst`. 169 | #' 170 | #' @return A list where each element corresponds to a classifier for a 12-hour 171 | #' flexible time type. 172 | #' 173 | #' @section Examples: 174 | #' 175 | #' The `flex_t12_lst` object can be incredibly useful when you need to get a 176 | #' format for 12-hour time formatting that works across all locales. You can 177 | #' avoid typing errors by using this list and every flexible time type from this 178 | #' list is guaranteed to work across all supported locales. In this example, 179 | #' we'll use the `"Ehms"` flexible time type by accessing it from the 180 | #' `flex_t12_lst` object. 181 | #' 182 | #' ```r 183 | #' fdt( 184 | #' input = "2018-07-04 22:05", 185 | #' format = flex_t12_lst$Bhms, 186 | #' locale = "en" 187 | #' ) 188 | #' ``` 189 | #' ``` 190 | #' #> [1] "10:05:00 at night" 191 | #' ``` 192 | #' 193 | #' If we wanted this in a different locale, the locale-specific `format` pattern 194 | #' behind the flexible date identifier would ensure consistency while moving to 195 | #' that locale. Let's use the `fdt_locales_lst` object in the same spirit to 196 | #' specify the German (Austria) locale. 197 | #' 198 | #' ```r 199 | #' fdt( 200 | #' input = "2018-07-04 22:05", 201 | #' format = flex_t12_lst$Bhms, 202 | #' locale = fdt_locales_lst$de_AT 203 | #' ) 204 | #' ``` 205 | #' ``` 206 | #' #> [1] "10:05:00 abends" 207 | #' ``` 208 | #' 209 | #' @export 210 | flex_t12_lst <- 211 | lapply( 212 | stats::setNames(as.list(flex_t12_vec()), as.list(flex_t12_vec())), 213 | FUN = function(x) { 214 | class(x) <- c("date_time_pattern", "flex_t12") 215 | x 216 | } 217 | ) 218 | 219 | #' A list of all 24-hour flexible time types 220 | #' 221 | #' @description 222 | #' The `flex_t24_lst` object is a list of the 24-hour flexible time types which 223 | #' are widely supported. Flexible time types are classes of time formatting 224 | #' which can be translated across locales. There are `r length(flex_t24_lst)` 225 | #' flexible time types of the 24-hour variety in `flex_t24_lst`. 226 | #' 227 | #' @return A list where each element corresponds to a classifier for a 24-hour 228 | #' flexible time type. 229 | #' 230 | #' @section Examples: 231 | #' 232 | #' The `flex_t24_lst` object can be incredibly useful when you need to get a 233 | #' format for 24-hour time formatting that works across all locales. You can 234 | #' avoid typing errors by using this list and every flexible time type from this 235 | #' list is guaranteed to work across all supported locales. In this example, 236 | #' we'll use the `"EHms"` flexible time type by accessing it from the 237 | #' `flex_t24_lst` object. 238 | #' 239 | #' ```r 240 | #' fdt( 241 | #' input = "2018-07-04 22:05", 242 | #' format = flex_t24_lst$EHms, 243 | #' locale = "en" 244 | #' ) 245 | #' ``` 246 | #' ``` 247 | #' #> [1] "Wed 22:05:00" 248 | #' ``` 249 | #' 250 | #' If we wanted this in a different locale, the locale-specific `format` pattern 251 | #' behind the flexible date identifier would ensure consistency while moving to 252 | #' that locale. Let's use the `fdt_locales_lst` object in the same spirit to 253 | #' specify the German locale. 254 | #' 255 | #' ```r 256 | #' fdt( 257 | #' input = "2018-07-04 22:05", 258 | #' format = flex_t24_lst$EHms, 259 | #' locale = fdt_locales_lst$de 260 | #' ) 261 | #' ``` 262 | #' ``` 263 | #' #> [1] "Mi, 22:05:00" 264 | #' ``` 265 | #' 266 | #' @export 267 | flex_t24_lst <- 268 | lapply( 269 | stats::setNames(as.list(flex_t24_vec()), as.list(flex_t24_vec())), 270 | FUN = function(x) { 271 | class(x) <- c("date_time_pattern", "flex_t24") 272 | x 273 | } 274 | ) 275 | -------------------------------------------------------------------------------- /R/fdt_locales.R: -------------------------------------------------------------------------------- 1 | #' Get a vector of all supported locales 2 | #' 3 | #' @description 4 | #' The `fdt_locales_vec()` function produces a vector of all supported locale 5 | #' IDs in the **bigD** package. 6 | #' 7 | #' @return 8 | #' A character vector of supported locale IDs. 9 | #' 10 | #' @examples 11 | #' # Let's get all the `ar` locales that exist 12 | #' # in the vector produced by `fdt_locales_vec()` 13 | #' grep("^ar", fdt_locales_vec(), value = TRUE) 14 | #' 15 | #' # Let's get all the locales that pertain to the 16 | #' # `CH` territory in the vector produced by 17 | #' # `fdt_locales_vec()` 18 | #' grep("CH", fdt_locales_vec(), value = TRUE) 19 | #' 20 | #' @export 21 | fdt_locales_vec <- function() { 22 | c( 23 | "af", "af_NA", "agq", "ak", "am", "ar", "ar_AE", "ar_BH", "ar_DJ", 24 | "ar_DZ", "ar_EG", "ar_EH", "ar_ER", "ar_IL", "ar_IQ", "ar_JO", 25 | "ar_KM", "ar_KW", "ar_LB", "ar_LY", "ar_MA", "ar_MR", "ar_OM", 26 | "ar_PS", "ar_QA", "ar_SA", "ar_SD", "ar_SO", "ar_SS", "ar_SY", 27 | "ar_TD", "ar_TN", "ar_YE", "as", "asa", "ast", "az", "az_Cyrl", 28 | "az_Latn", "bas", "be", "be_tarask", "bem", "bez", "bg", "bm", 29 | "bn", "bn_IN", "bo", "bo_IN", "br", "brx", "bs", "bs_Cyrl", "bs_Latn", 30 | "ca", "ca_AD", "ca_ES_valencia", "ca_FR", "ca_IT", "ccp", "ccp_IN", 31 | "ce", "ceb", "cgg", "chr", "ckb", "ckb_IR", "cs", "cy", "da", 32 | "da_GL", "dav", "de", "de_AT", "de_BE", "de_CH", "de_IT", "de_LI", 33 | "de_LU", "dje", "doi", "dsb", "dua", "dyo", "dz", "ebu", "ee", 34 | "ee_TG", "el", "el_CY", "en", "en_001", "en_150", "en_AE", "en_AG", 35 | "en_AI", "en_AS", "en_AT", "en_AU", "en_BB", "en_BE", "en_BI", 36 | "en_BM", "en_BS", "en_BW", "en_BZ", "en_CA", "en_CC", "en_CH", 37 | "en_CK", "en_CM", "en_CX", "en_CY", "en_DE", "en_DG", "en_DK", 38 | "en_DM", "en_ER", "en_FI", "en_FJ", "en_FK", "en_FM", "en_GB", 39 | "en_GD", "en_GG", "en_GH", "en_GI", "en_GM", "en_GU", "en_GY", 40 | "en_HK", "en_IE", "en_IL", "en_IM", "en_IN", "en_IO", "en_JE", 41 | "en_JM", "en_KE", "en_KI", "en_KN", "en_KY", "en_LC", "en_LR", 42 | "en_LS", "en_MG", "en_MH", "en_MO", "en_MP", "en_MS", "en_MT", 43 | "en_MU", "en_MV", "en_MW", "en_MY", "en_NA", "en_NF", "en_NG", 44 | "en_NL", "en_NR", "en_NU", "en_NZ", "en_PG", "en_PH", "en_PK", 45 | "en_PN", "en_PR", "en_PW", "en_RW", "en_SB", "en_SC", "en_SD", 46 | "en_SE", "en_SG", "en_SH", "en_SI", "en_SL", "en_SS", "en_SX", 47 | "en_SZ", "en_TC", "en_TK", "en_TO", "en_TT", "en_TV", "en_TZ", 48 | "en_UG", "en_UM", "en_VC", "en_VG", "en_VI", "en_VU", "en_WS", 49 | "en_ZA", "en_ZM", "en_ZW", "eo", "es", "es_419", "es_AR", "es_BO", 50 | "es_BR", "es_BZ", "es_CL", "es_CO", "es_CR", "es_CU", "es_DO", 51 | "es_EA", "es_EC", "es_GQ", "es_GT", "es_HN", "es_IC", "es_MX", 52 | "es_NI", "es_PA", "es_PE", "es_PH", "es_PR", "es_PY", "es_SV", 53 | "es_US", "es_UY", "es_VE", "et", "eu", "ewo", "fa", "fa_AF", 54 | "ff", "ff_Adlm", "ff_Adlm_BF", "ff_Adlm_CM", "ff_Adlm_GH", "ff_Adlm_GM", 55 | "ff_Adlm_GW", "ff_Adlm_LR", "ff_Adlm_MR", "ff_Adlm_NE", "ff_Adlm_NG", 56 | "ff_Adlm_SL", "ff_Adlm_SN", "ff_Latn", "ff_Latn_BF", "ff_Latn_CM", 57 | "ff_Latn_GH", "ff_Latn_GM", "ff_Latn_GN", "ff_Latn_GW", "ff_Latn_LR", 58 | "ff_Latn_MR", "ff_Latn_NE", "ff_Latn_NG", "ff_Latn_SL", "fi", 59 | "fil", "fo", "fo_DK", "fr", "fr_BE", "fr_BF", "fr_BI", "fr_BJ", 60 | "fr_BL", "fr_CA", "fr_CD", "fr_CF", "fr_CG", "fr_CH", "fr_CI", 61 | "fr_CM", "fr_DJ", "fr_DZ", "fr_GA", "fr_GF", "fr_GN", "fr_GP", 62 | "fr_GQ", "fr_HT", "fr_KM", "fr_LU", "fr_MA", "fr_MC", "fr_MF", 63 | "fr_MG", "fr_ML", "fr_MQ", "fr_MR", "fr_MU", "fr_NC", "fr_NE", 64 | "fr_PF", "fr_PM", "fr_RE", "fr_RW", "fr_SC", "fr_SN", "fr_SY", 65 | "fr_TD", "fr_TG", "fr_TN", "fr_VU", "fr_WF", "fr_YT", "fur", 66 | "fy", "ga", "ga_GB", "gd", "gl", "gsw", "gsw_FR", "gsw_LI", "gu", 67 | "guz", "gv", "ha", "ha_GH", "ha_NE", "haw", "he", "hi", "hi_Latn", 68 | "hr", "hr_BA", "hsb", "hu", "hy", "ia", "id", "ig", "ii", "is", 69 | "it", "it_CH", "it_SM", "it_VA", "ja", "jgo", "jmc", "jv", "ka", 70 | "kab", "kam", "kde", "kea", "kgp", "khq", "ki", "kk", "kkj", 71 | "kl", "kln", "km", "kn", "ko", "ko_KP", "kok", "ks", "ks_Arab", 72 | "ks_Deva", "ksb", "ksf", "ksh", "ku", "kw", "ky", "lag", "lb", 73 | "lg", "lkt", "ln", "ln_AO", "ln_CF", "ln_CG", "lo", "lrc", "lrc_IQ", 74 | "lt", "lu", "luo", "luy", "lv", "mai", "mas", "mas_TZ", "mer", 75 | "mfe", "mg", "mgh", "mgo", "mi", "mk", "ml", "mn", "mni", "mni_Beng", 76 | "mr", "ms", "ms_BN", "ms_ID", "ms_SG", "mt", "mua", "my", "mzn", 77 | "naq", "nb", "nb_SJ", "nd", "nds", "nds_NL", "ne", "ne_IN", "nl", 78 | "nl_AW", "nl_BE", "nl_BQ", "nl_CW", "nl_SR", "nl_SX", "nmg", 79 | "nn", "nnh", "no", "nus", "nyn", "om", "om_KE", "or", "os", "os_RU", 80 | "pa", "pa_Arab", "pa_Guru", "pcm", "pl", "ps", "ps_PK", "pt", 81 | "pt_AO", "pt_CH", "pt_CV", "pt_GQ", "pt_GW", "pt_LU", "pt_MO", 82 | "pt_MZ", "pt_PT", "pt_ST", "pt_TL", "qu", "qu_BO", "qu_EC", "rm", 83 | "rn", "ro", "ro_MD", "rof", "ru", "ru_BY", "ru_KG", "ru_KZ", 84 | "ru_MD", "ru_UA", "rw", "rwk", "sa", "sah", "saq", "sat", "sat_Olck", 85 | "sbp", "sc", "sd", "sd_Arab", "sd_Deva", "se", "se_FI", "se_SE", 86 | "seh", "ses", "sg", "shi", "shi_Latn", "shi_Tfng", "si", "sk", 87 | "sl", "smn", "sn", "so", "so_DJ", "so_ET", "so_KE", "sq", "sq_MK", 88 | "sq_XK", "sr", "sr_Cyrl", "sr_Cyrl_BA", "sr_Cyrl_ME", "sr_Cyrl_XK", 89 | "sr_Latn", "sr_Latn_BA", "sr_Latn_ME", "sr_Latn_XK", "su", "su_Latn", 90 | "sv", "sv_AX", "sv_FI", "sw", "sw_CD", "sw_KE", "sw_UG", "ta", 91 | "ta_LK", "ta_MY", "ta_SG", "te", "teo", "teo_KE", "tg", "th", 92 | "ti", "ti_ER", "tk", "to", "tr", "tr_CY", "tt", "twq", "tzm", 93 | "ug", "uk", "und", "ur", "ur_IN", "uz", "uz_Arab", "uz_Cyrl", 94 | "uz_Latn", "vai", "vai_Latn", "vai_Vaii", "vi", "vun", "wae", 95 | "wo", "xh", "xog", "yav", "yi", "yo", "yo_BJ", "yrl", "yrl_CO", 96 | "yrl_VE", "yue", "yue_Hans", "yue_Hant", "zgh", "zh", "zh_Hans", 97 | "zh_Hans_HK", "zh_Hans_MO", "zh_Hans_SG", "zh_Hant", "zh_Hant_HK", 98 | "zh_Hant_MO", "zu" 99 | ) 100 | } 101 | 102 | #' A list of all supported locales 103 | #' 104 | #' @description 105 | #' The `fdt_locales_lst` object is a list of all supported locales. This is 106 | #' useful when used with the [fdt()] function as the list can be auto-completed 107 | #' with a locale identifier and this generates valid input for the `locale` 108 | #' argument. 109 | #' 110 | #' @return A list where each element corresponds to a supported locale ID. 111 | #' 112 | #' @section Examples: 113 | #' 114 | #' The `fdt_locales_lst` object can be incredibly useful when choosing one of 115 | #' supported locales. You can avoid typing errors and every element of the list 116 | #' is meant to work. In this example, we'll use the `"da"` locale through 117 | #' use of the list. 118 | #' 119 | #' ```r 120 | #' fdt( 121 | #' input = "2018-07-04 22:05", 122 | #' format = "yy-MMMM-d", 123 | #' locale = fdt_locales_lst$da 124 | #' ) 125 | #' ``` 126 | #' ``` 127 | #' #> [1] "18-juli-4" 128 | #' ```` 129 | #' 130 | #' @export 131 | fdt_locales_lst <- 132 | stats::setNames(as.list(fdt_locales_vec()), as.list(fdt_locales_vec())) 133 | -------------------------------------------------------------------------------- /R/standard_date_time.R: -------------------------------------------------------------------------------- 1 | #' Obtain a standard datetime format that works across locales 2 | #' 3 | #' The `standard_date_time()` function can be invoked in the `format` argument 4 | #' of the `fdt()` function to help generate a locale-specific formatting string 5 | #' of a certain 'type' of formatted datetime. The `type` value is a keyword that 6 | #' represents precision and verbosity; the available keywords are `"short"` (the 7 | #' default), `"medium"`, `"long"`, and `"full"`. 8 | #' 9 | #' @param type One of four standardized types for the resulting datetime that 10 | #' range in precision and verbosity. These are `"short"` (the default), 11 | #' `"medium"`, `"long"`, and `"full"`. 12 | #' 13 | #' @return A vector of class `date_time_pattern`. 14 | #' 15 | #' @section Examples: 16 | #' 17 | #' With an input datetime of `"2018-07-04 22:05(America/Vancouver)"`, we can 18 | #' format the date and time in a standardized way with `standard_date_time()` 19 | #' providing the correct formatting string. This function is invoked in the 20 | #' `format` argument of `fdt()`: 21 | #' 22 | #' ```r 23 | #' fdt( 24 | #' input = "2018-07-04 22:05(America/Vancouver)", 25 | #' format = standard_date_time(type = "full") 26 | #' ) 27 | #' ``` 28 | #' ``` 29 | #' #> [1] "Wednesday, July 4, 2018 at 10:05:00 PM Pacific Daylight Time" 30 | #' ``` 31 | #' 32 | #' The locale can be changed and we don't have to worry about the particulars 33 | #' of the formatting string (they are standardized across locales). 34 | #' 35 | #' ```r 36 | #' fdt( 37 | #' input = "2018-07-04 22:05(America/Vancouver)", 38 | #' format = standard_date_time(type = "full"), 39 | #' locale = fdt_locales_lst$nl 40 | #' ) 41 | #' ``` 42 | #' ``` 43 | #' #> [1] "woensdag 4 juli 2018 om 22:05:00 Pacific-zomertijd" 44 | #' ``` 45 | #' 46 | #' We can use different `type` values to control the output datetime string. The 47 | #' default is `"short"`. 48 | #' 49 | #' ```r 50 | #' fdt( 51 | #' input = "2018-07-04 22:05(America/Vancouver)", 52 | #' format = standard_date_time() 53 | #' ) 54 | #' ``` 55 | #' ``` 56 | #' #> [1] "7/4/18, 10:05 PM" 57 | #' ``` 58 | #' 59 | #' After that, it's `"medium"`: 60 | #' 61 | #' ```r 62 | #' fdt( 63 | #' input = "2018-07-04 22:05(America/Vancouver)", 64 | #' format = standard_date_time(type = "medium") 65 | #' ) 66 | #' ``` 67 | #' ``` 68 | #' #> [1] "Jul 4, 2018, 10:05:00 PM" 69 | #' ``` 70 | #' 71 | #' The `"short"` and `"medium"` types don't display time zone information in the 72 | #' output. Beginning with `"long"`, the tz is shown. 73 | #' 74 | #' ```r 75 | #' fdt( 76 | #' input = "2018-07-04 22:05(America/Vancouver)", 77 | #' format = standard_date_time(type = "long") 78 | #' ) 79 | #' ``` 80 | #' ``` 81 | #' #> [1] "July 4, 2018 at 10:05:00 PM PDT" 82 | #' ``` 83 | #' 84 | #' If you don't include time zone information in the input, the `"UTC"` time 85 | #' zone will be assumed: 86 | #' 87 | #' ```r 88 | #' fdt( 89 | #' input = "2018-07-04 22:05", 90 | #' format = standard_date_time(type = "full") 91 | #' ) 92 | #' ``` 93 | #' ``` 94 | #' #> [1] "Wednesday, July 4, 2018 at 10:05:00 PM GMT+00:00" 95 | #' ``` 96 | #' 97 | #' @export 98 | standard_date_time <- function( 99 | type = c("short", "medium", "long", "full") 100 | ) { 101 | type <- match.arg(type) 102 | x <- "sdt" 103 | class(x) <- c("date_time_pattern", "standard", "date_time", type) 104 | x 105 | } 106 | 107 | #' Obtain a standard date format that works across locales 108 | #' 109 | #' The `standard_date()` function can be invoked in the `format` argument of the 110 | #' `fdt()` function to help generate a locale-specific formatting string of a 111 | #' certain 'type' of formatted date. The `type` value is a keyword that 112 | #' represents precision and verbosity; the available keywords are `"short"` (the 113 | #' default), `"medium"`, `"long"`, and `"full"`. 114 | #' 115 | #' @param type One of four standardized types for the resulting date that range 116 | #' in precision and verbosity. These are `"short"` (the default), `"medium"`, 117 | #' `"long"`, and `"full"`. 118 | #' 119 | #' @return A vector of class `date_time_pattern`. 120 | #' 121 | #' @section Examples: 122 | #' 123 | #' With an input datetime of `"2018-07-04 22:05(America/Vancouver)"`, we can 124 | #' format as a date in a standardized way with `standard_date()` providing the 125 | #' correct formatting string. This function is invoked in the `format` argument 126 | #' of `fdt()`: 127 | #' 128 | #' ```r 129 | #' fdt( 130 | #' input = "2018-07-04 22:05(America/Vancouver)", 131 | #' format = standard_date(type = "full") 132 | #' ) 133 | #' ``` 134 | #' ``` 135 | #' #> [1] "Wednesday, July 4, 2018" 136 | #' ``` 137 | #' 138 | #' The locale can be changed and we don't have to worry about the particulars 139 | #' of the formatting string (they are standardized across locales). 140 | #' 141 | #' ```r 142 | #' fdt( 143 | #' input = "2018-07-04 22:05(America/Vancouver)", 144 | #' format = standard_date(type = "full"), 145 | #' locale = fdt_locales_lst$nl 146 | #' ) 147 | #' ``` 148 | #' ``` 149 | #' #> [1] "woensdag 4 juli 2018" 150 | #' ``` 151 | #' 152 | #' We can use different `type` values to control the output date string. The 153 | #' default is `"short"`. 154 | #' 155 | #' ```r 156 | #' fdt( 157 | #' input = "2018-07-04 22:05(America/Vancouver)", 158 | #' format = standard_date() 159 | #' ) 160 | #' ``` 161 | #' ``` 162 | #' #> [1] "7/4/18" 163 | #' ``` 164 | #' 165 | #' After that, it's `"medium"`: 166 | #' 167 | #' ```r 168 | #' fdt( 169 | #' input = "2018-07-04 22:05(America/Vancouver)", 170 | #' format = standard_date(type = "medium") 171 | #' ) 172 | #' ``` 173 | #' ``` 174 | #' #> [1] "Jul 4, 2018" 175 | #' ``` 176 | #' 177 | #' Then, `"long"`: 178 | #' 179 | #' ```r 180 | #' fdt( 181 | #' input = "2018-07-04 22:05(America/Vancouver)", 182 | #' format = standard_date(type = "long") 183 | #' ) 184 | #' ``` 185 | #' ``` 186 | #' #> [1] "July 4, 2018" 187 | #' ``` 188 | #' 189 | #' And finally up to `"full"`, which was demonstrated in the first example. 190 | #' 191 | #' @export 192 | standard_date <- function( 193 | type = c("short", "medium", "long", "full") 194 | ) { 195 | type <- match.arg(type) 196 | x <- "sdt" 197 | class(x) <- c("date_time_pattern", "standard", "date", type) 198 | x 199 | } 200 | 201 | #' Obtain a standard time format that works across locales 202 | #' 203 | #' The `standard_time()` function can be invoked in the `format` argument of the 204 | #' `fdt()` function to help generate a locale-specific formatting string of a 205 | #' certain 'type' of formatted time. The `type` value is a keyword that 206 | #' represents precision and verbosity; the available keywords are `"short"` (the 207 | #' default), `"medium"`, `"long"`, and `"full"`. 208 | #' 209 | #' @param type One of four standardized types for the resulting time that range 210 | #' in precision and verbosity. These are `"short"` (the default), `"medium"`, 211 | #' `"long"`, and `"full"`. 212 | #' 213 | #' @return A vector of class `date_time_pattern`. 214 | #' 215 | #' @section Examples: 216 | #' 217 | #' With an input datetime of `"2018-07-04 22:05(America/Vancouver)"`, we can 218 | #' format as a time in a standardized way with `standard_time()` providing the 219 | #' correct formatting string. This function is invoked in the `format` argument 220 | #' of `fdt()`: 221 | #' 222 | #' ```r 223 | #' fdt( 224 | #' input = "2018-07-04 22:05(America/Vancouver)", 225 | #' format = standard_time(type = "full") 226 | #' ) 227 | #' ``` 228 | #' ``` 229 | #' #> [1] "10:05:00 PM Pacific Daylight Time" 230 | #' ``` 231 | #' 232 | #' The locale can be changed and we don't have to worry about the particulars 233 | #' of the formatting string (they are standardized across locales). 234 | #' 235 | #' ```r 236 | #' fdt( 237 | #' input = "2018-07-04 22:05(America/Vancouver)", 238 | #' format = standard_time(type = "full"), 239 | #' locale = fdt_locales_lst$nl 240 | #' ) 241 | #' ``` 242 | #' ``` 243 | #' #> [1] "22:05:00 Pacific-zomertijd" 244 | #' ``` 245 | #' 246 | #' We can use different `type` values to control the output date string. The 247 | #' default is `"short"`. 248 | #' 249 | #' ```r 250 | #' fdt( 251 | #' input = "2018-07-04 22:05(America/Vancouver)", 252 | #' format = standard_time() 253 | #' ) 254 | #' ``` 255 | #' ``` 256 | #' #> [1] "10:05 PM" 257 | #' ``` 258 | #' 259 | #' After that, it's `"medium"`: 260 | #' 261 | #' ```r 262 | #' fdt( 263 | #' input = "2018-07-04 22:05(America/Vancouver)", 264 | #' format = standard_time(type = "medium") 265 | #' ) 266 | #' ``` 267 | #' ``` 268 | #' #> [1] "10:05:00 PM" 269 | #' ``` 270 | #' 271 | #' Then, `"long"`: 272 | #' 273 | #' ```r 274 | #' fdt( 275 | #' input = "2018-07-04 22:05(America/Vancouver)", 276 | #' format = standard_time(type = "long") 277 | #' ) 278 | #' ``` 279 | #' ``` 280 | #' #> [1] "10:05:00 PM PDT" 281 | #' ``` 282 | #' 283 | #' And finally up to `"full"`, which was demonstrated in the first example. 284 | #' 285 | #' @export 286 | standard_time <- function( 287 | type = c("short", "medium", "long", "full") 288 | ) { 289 | type <- match.arg(type) 290 | x <- "sdt" 291 | class(x) <- c("date_time_pattern", "standard", "time", type) 292 | x 293 | } 294 | -------------------------------------------------------------------------------- /R/sysdata.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/bigD/be131649407343d36f86696b9086319587a2f0e0/R/sysdata.rda -------------------------------------------------------------------------------- /R/utils-date_time_parse.R: -------------------------------------------------------------------------------- 1 | get_date_pattern <- function() { 2 | "([\\+-]?\\d{4}-\\d\\d-\\d\\d)" 3 | } 4 | 5 | get_time_pattern <- function() { 6 | "((T| )((\\d|\\d\\d):\\d\\d[0-9:.]*)|^((\\d|\\d\\d):\\d\\d[0-9:.]*))" 7 | } 8 | 9 | get_tz_pattern <- function() { 10 | "(Z|(\\+|-)\\d\\d|(\\+|-)\\d\\d:\\d\\d|(\\+|-)\\d\\d\\d\\d)" 11 | } 12 | 13 | get_tz_pattern_z <- function() { 14 | paste0(get_time_pattern(), "(Z)") 15 | } 16 | 17 | get_tz_pattern_hh <- function() { 18 | paste0(get_time_pattern(), "((\\+|-)\\d\\d)") 19 | } 20 | 21 | get_tz_pattern_hh_mm <- function() { 22 | paste0(get_time_pattern(), "((\\+|-)\\d\\d:\\d\\d)") 23 | } 24 | 25 | get_tz_pattern_hhmm <- function() { 26 | paste0(get_time_pattern(), "((\\+|-)\\d\\d\\d\\d)") 27 | } 28 | 29 | get_wrapped_iana_pattern <- function() { 30 | "(\\[|\\()([a-zA-Z0-9\\/ -\\+_]*?)(?:\\]|\\))" 31 | } 32 | 33 | get_attached_iana_pattern <- function() { 34 | "(\\s-\\s|\\s|\\/|\\|)([a-zA-Z0-9\\/ -\\+_]*?)" 35 | } 36 | 37 | is_date_present <- function(input) { 38 | grepl(get_date_pattern(), input) 39 | } 40 | 41 | is_time_present <- function(input) { 42 | grepl(get_time_pattern(), input) 43 | } 44 | 45 | # returns a vector of length of input 46 | is_tz_present <- function(input) { 47 | regex <- paste( 48 | get_tz_pattern_z(), 49 | get_tz_pattern_hh(), 50 | get_tz_pattern_hh_mm(), 51 | get_tz_pattern_hhmm(), 52 | sep = "|" 53 | ) 54 | grepl(regex, input) 55 | } 56 | 57 | is_long_tzid_present <- function(input) { 58 | 59 | if (!grepl("^.*\\([^']*\\)$", input)) { 60 | return(FALSE) 61 | } 62 | 63 | long_tzid <- extract_long_tzid(input = input) 64 | 65 | validate_long_tzid(long_tzid = long_tzid) 66 | 67 | TRUE 68 | } 69 | 70 | get_long_tzid_str <- function(input) { 71 | 72 | if (!is_long_tzid_present(input = input)) { 73 | return(NA_character_) 74 | } 75 | 76 | long_tzid <- extract_long_tzid(input = input) 77 | 78 | normalize_long_tzid(long_tzid = long_tzid) 79 | } 80 | 81 | extract_long_tzid <- function(input) { 82 | gsub("^.*\\((.*)\\)$", "\\1", input) 83 | } 84 | 85 | validate_long_tzid <- function(long_tzid) { 86 | 87 | known_long_tzid_vals <- 88 | c(unique(tz_name_resolution$tz_canonical), tz_name_resolution$tz_alt) 89 | 90 | if (!(long_tzid %in% known_long_tzid_vals)) { 91 | stop("The long time zone provided is not valid.", call. = FALSE) 92 | } 93 | 94 | invisible(TRUE) 95 | } 96 | 97 | normalize_long_tzid <- function(long_tzid) { 98 | 99 | if (long_tzid %in% tz_name_resolution$tz_alt) { 100 | 101 | long_tzid <- 102 | tz_name_resolution$tz_canonical[tz_name_resolution$tz_alt == long_tzid] 103 | } 104 | 105 | long_tzid 106 | } 107 | 108 | # A long tzid can be provided in parentheses as `(America/Vancouver)` at the 109 | # end of a datetime string; the `get_long_tzid_str()` function will remove the 110 | # parentheses (and should only be invoked if the datetime input contains a 111 | # long tzid) 112 | # The `long_tzid_to_tz_str` will convert a long tzid to a valid tz string in 113 | # the (+/-)hhmm form 114 | long_tzid_to_tz_str <- function(long_tzid, input_dt) { 115 | 116 | if (startsWith(long_tzid, "Etc/") && long_tzid %in% c("Etc/GMT", "Etc/UTC")) { 117 | return("+0000") 118 | } 119 | 120 | tzdb_entries_tzid <- tzdb[ 121 | tzdb$zone_name == long_tzid, c("date_start", "gmt_offset_h")] 122 | 123 | if (nrow(tzdb_entries_tzid) == 0L) { 124 | return(NULL) 125 | } 126 | 127 | input_date <- as.Date(input_dt) 128 | 129 | tzdb_idx <- rle(tzdb_entries_tzid$date_start >= input_date)$lengths[1] 130 | 131 | tz_offset <- tzdb_entries_tzid[[tzdb_idx, "gmt_offset_h"]] 132 | 133 | minutes <- formatC(round((abs(tz_offset) %% 1) * 60, 0), width = 2, flag = "0") 134 | hours <- formatC(trunc(abs(tz_offset)), width = 2, flag = "0") 135 | sign <- ifelse(tz_offset < 0, "-", "+") 136 | 137 | paste0(sign, hours, minutes) 138 | } 139 | 140 | get_tz_str <- function(input) { 141 | 142 | if (is.null(input) || !is_tz_present(input = input)) { 143 | return("") 144 | } 145 | 146 | if (grepl("+", input, fixed = TRUE)) { 147 | 148 | out <- unlist(strsplit(input, split = "+", fixed = TRUE))[[2]] 149 | out <- paste0("+", out) 150 | 151 | } else if (grepl("Z", input, fixed = TRUE)) { 152 | 153 | out <- "Z" 154 | 155 | } else { 156 | 157 | out <- unlist(strsplit(input, split = "-", fixed = TRUE)) 158 | out <- out[length(out)] 159 | out <- paste0("-", out) 160 | } 161 | 162 | # If there is an attached tzid string then remove it 163 | if (is_long_tzid_present(input = out)) { 164 | out <- gsub("\\s*\\([^']*\\)$", "", out) 165 | } 166 | 167 | out 168 | } 169 | 170 | get_tz_offset_val_from_tz_str <- function(tz_str) { 171 | 172 | if (tz_str == "Z") { 173 | 174 | offset_val <- 0 175 | 176 | } else { 177 | 178 | offset_val <- 179 | as.numeric(substr(tz_str, 2, 3)) + 180 | as.numeric(substr(tz_str, nchar(tz_str) - 1, nchar(tz_str))) / 60 181 | 182 | if (grepl("-", tz_str, fixed = TRUE)) { 183 | offset_val <- offset_val * (-1) 184 | } 185 | } 186 | 187 | offset_val 188 | } 189 | 190 | get_tz_offset_val <- function(input, tz_str = NULL) { 191 | 192 | tz_str <- tz_str %||% get_tz_str(input = input) 193 | 194 | get_tz_offset_val_from_tz_str(tz_str = tz_str) 195 | } 196 | 197 | strip_tz <- function(input) { 198 | 199 | tz_str <- get_tz_str(input = input) 200 | 201 | out <- gsub(tz_str, "", input, fixed = TRUE) 202 | out <- gsub("\\s*\\([^']*\\)$", "", out) 203 | 204 | out 205 | } 206 | 207 | strip_long_tzid <- function(input) { 208 | 209 | long_tzid <- get_long_tzid_str(input = input) 210 | 211 | input_str <- gsub(paste0("(", long_tzid, ")"), "", input, fixed = TRUE) 212 | input_str <- strip_surrounding_whitespace(input = input_str) 213 | 214 | input_str 215 | } 216 | 217 | is_iana_present <- function(input) { 218 | 219 | grepl( 220 | paste0( 221 | get_time_pattern(), get_tz_pattern(), 222 | "(", get_wrapped_iana_pattern(), "|", get_attached_iana_pattern(), ")"), 223 | input 224 | ) 225 | } 226 | 227 | which_tz_pattern <- function(input) { 228 | 229 | if (grepl(get_tz_pattern_z(), input)) { 230 | return("z") 231 | } 232 | 233 | if (grepl(get_tz_pattern_hh_mm(), input)) { 234 | return("hh_mm") 235 | } 236 | 237 | if (grepl(get_tz_pattern_hhmm(), input)) { 238 | return("hhmm") 239 | } 240 | 241 | if (grepl(get_tz_pattern_hh(), input)) { 242 | return("hh") 243 | } 244 | 245 | NA 246 | } 247 | 248 | which_iana_pattern <- function(input) { 249 | 250 | if (grepl(paste0(get_tz_pattern(), get_wrapped_iana_pattern()), input)) { 251 | return("wrapped") 252 | } else if (grepl(paste0(get_tz_pattern(), get_attached_iana_pattern()), input)) { 253 | return("attached") 254 | } 255 | NA_character_ 256 | } 257 | 258 | get_tz_offset <- function(input) { 259 | 260 | tz_pattern <- which_tz_pattern(input) 261 | 262 | switch( 263 | tz_pattern, 264 | z = { 265 | tz_offset <- 0.0 266 | }, 267 | hh = { 268 | iso_tz_component <- gsub(paste0(".*", get_tz_pattern_hh(), ".*"), "\\4", input) 269 | tz_offset <- as.numeric(iso_tz_component) 270 | }, 271 | hh_mm = { 272 | iso_tz_component <- gsub(paste0(".*", get_tz_pattern_hh_mm(), ".*"), "\\4", input) 273 | offset_sign <- ifelse(startsWith(iso_tz_component, "-"), -1L, 1L) 274 | offset_h <- as.numeric(substr(iso_tz_component, 1, 3)) 275 | offset_min <- as.numeric(substr_right(iso_tz_component, 2)) / 60.0 276 | tz_offset <- (abs(offset_h) + offset_min) * offset_sign 277 | }, 278 | hhmm = { 279 | iso_tz_component <- gsub(paste0(".*", get_tz_pattern_hhmm(), ".*"), "\\4", input) 280 | offset_sign <- ifelse(startsWith(iso_tz_component, "-"), -1L, 1L) 281 | offset_h <- as.numeric(substr(iso_tz_component, 1, 3)) 282 | offset_min <- as.numeric(substr_right(iso_tz_component, 2)) / 60.0 283 | tz_offset <- (abs(offset_h) + offset_min) * offset_sign 284 | }) 285 | 286 | tz_offset 287 | } 288 | 289 | get_exemplar_city <- function(long_tzid) { 290 | 291 | exemplar_city <- unlist(strsplit(long_tzid, "/", fixed = TRUE)[[1]])[2] 292 | 293 | exemplar_city 294 | } 295 | 296 | get_localized_exemplar_city <- function( 297 | long_tzid, 298 | locale, 299 | yield_unknown = TRUE 300 | ) { 301 | 302 | exemplar_city <- unlist(strsplit(long_tzid, "/", fixed = TRUE)[[1]])[2] 303 | 304 | # TODO: Resolve links of exemplar cities to a canonical exemplar city 305 | # This will require a separate lookup table 306 | 307 | if (!(exemplar_city %in% colnames(tz_exemplar)[-1])) { 308 | 309 | # Get localized variant of 'Unknown City' 310 | if (yield_unknown) { 311 | return(tz_exemplar[tz_exemplar$locale == locale, "Unknown"]) 312 | } else { 313 | return(NA_character_) 314 | } 315 | } 316 | 317 | exemplar_city_localized <- 318 | tz_exemplar[tz_exemplar$locale == locale, exemplar_city] 319 | 320 | exemplar_city_localized 321 | } 322 | 323 | # The short specific non-location format (e.g., 'PST') from a `long_tzid` 324 | get_tz_short_specific <- function(long_tzid, input_dt) { 325 | 326 | tzdb_entries_tzid <- tzdb[tzdb$zone_name == long_tzid, ] 327 | if (nrow(tzdb_entries_tzid) == 0) { 328 | return(NA_character_) 329 | } 330 | 331 | input_date <- as.Date(input_dt) 332 | 333 | tzdb_idx <- rle(tzdb_entries_tzid$date_start >= input_date)$lengths[1] 334 | 335 | # TODO: add check to ensure that the `abbrev` value is a valid 336 | # short specific non-location time zone 337 | tzdb_entries_tzid[tzdb_idx, "abbrev"] 338 | } 339 | 340 | # The long specific non-location format (e.g., 'Pacific Standard Time') from 341 | # a `long_tzid` 342 | get_tz_long_specific <- function(long_tzid, input_dt, locale) { 343 | 344 | input_date <- as.Date(input_dt) 345 | 346 | tzdb_entries_tzid <- tzdb[tzdb$zone_name == long_tzid, ] 347 | 348 | if (nrow(tzdb_entries_tzid) == 0L) { 349 | return(NA_character_) 350 | } 351 | 352 | tzdb_idx <- rle(tzdb_entries_tzid$date_start >= input_date)$lengths[1] 353 | 354 | tzdb_entries_tzid_ln <- tzdb_entries_tzid[tzdb_idx, ] 355 | 356 | if (tzdb_entries_tzid_ln$dst) { 357 | pattern_col_tz_formats <- "region_format_daylight" 358 | } else { 359 | pattern_col_tz_formats <- "region_format" 360 | } 361 | 362 | tz_long_specific_pattern <- 363 | tz_formats[tz_formats$locale == locale, ][[pattern_col_tz_formats]] 364 | 365 | # Get the metazone in its long ID format 366 | metazone_long_id <- long_tz_id_to_metazone_long_id(long_tzid = long_tzid) 367 | 368 | if (is.na(metazone_long_id)) { 369 | return(NA_character_) 370 | } 371 | 372 | # Get the row of the `tz_metazone_names` table based on the supplied locale 373 | tz_metazone_names_row <- tz_metazone_names[tz_metazone_names$locale == locale, ] 374 | 375 | if (nrow(tz_metazone_names_row) == 0) { 376 | return(NA_character_) 377 | } 378 | 379 | # Get the list entry corresponding to the metazone and the locale 380 | tz_metazone_names_entry <- 381 | unlist( 382 | tz_metazone_names_row[, colnames(tz_metazone_names_row) == metazone_long_id][[1]] 383 | ) 384 | 385 | if (tzdb_entries_tzid_ln$dst) { 386 | daylight_standard <- "daylight" 387 | } else { 388 | daylight_standard <- "standard" 389 | } 390 | 391 | tz_metazone_names_filtered <- 392 | tz_metazone_names_entry[ 393 | grepl(daylight_standard, names(tz_metazone_names_entry))] 394 | 395 | tz_metazone_name <- 396 | unname(tz_metazone_names_filtered[ 397 | grepl("long", names(tz_metazone_names_filtered), fixed = TRUE)]) 398 | 399 | tz_metazone_name 400 | } 401 | 402 | get_tz_bcp_id <- function(long_tzid) { 403 | 404 | # If the supplied `long_tzid` value is NA, return NA 405 | if (is.na(long_tzid)) { 406 | return(NA_character_) 407 | } 408 | 409 | tz_name <- tz_bcp_id[tz_bcp_id$tz_canonical == long_tzid, ][["tz_bcp_id"]] 410 | 411 | tz_name 412 | } 413 | 414 | # Get the non-location formatted time zone names (e.g., 'Pacific Time', 'PT') 415 | # from a `long_tzid` (canonical tz name) 416 | # 417 | # The non-location format reflects "wall time" (what is on 418 | # a clock on the wall). It's used for recurring events, meetings, or anywhere 419 | # people do not want to be overly specific. For example, "10 am Pacific Time" 420 | # will be GMT-8 in the winter, and GMT-7 in the summer. 421 | get_tz_non_location <- function( 422 | long_tzid, 423 | locale, 424 | short_long, 425 | type 426 | ) { 427 | 428 | short_long <- match.arg(short_long, c("long", "short")) 429 | type <- match.arg(type, c("generic", "standard", "daylight")) 430 | 431 | # If the supplied `long_tzid` value is NA, return NA 432 | if (is.na(long_tzid)) { 433 | return(NA_character_) 434 | } 435 | 436 | # Get the metazone in its long ID format 437 | metazone_long_id <- long_tz_id_to_metazone_long_id(long_tzid = long_tzid) 438 | 439 | # Check if metazone is NA and return NA if that is so 440 | if (is.na(metazone_long_id)) { 441 | return(NA_character_) 442 | } 443 | 444 | # Get the row of the `tz_metazone_names` table based on the supplied locale 445 | tz_metazone_names_row <- tz_metazone_names[tz_metazone_names$locale == locale, ] 446 | 447 | # Get the list entry corresponding to the metazone and the locale 448 | tz_metazone_names_entry <- 449 | unlist( 450 | tz_metazone_names_row[, colnames(tz_metazone_names_row) == metazone_long_id][[1]] 451 | ) 452 | 453 | target_item <- paste0(short_long, ".", type) 454 | available_items <- names(tz_metazone_names_entry) 455 | has_long_items <- any(grepl("long", names(tz_metazone_names_entry), fixed = TRUE)) 456 | has_short_items <- any(grepl("short", names(tz_metazone_names_entry), fixed = TRUE)) 457 | 458 | if (length(available_items) == 1) { 459 | tz_name <- tz_metazone_names_entry[[available_items]] 460 | } else if (target_item %in% available_items) { 461 | tz_name <- tz_metazone_names_entry[[target_item]] 462 | } else if (!has_short_items && short_long == "short") { 463 | if (any(grepl(type, available_items))) { 464 | tz_name <- tz_metazone_names_entry[[paste0("long.", type)]] 465 | } else { 466 | tz_name <- tz_metazone_names_entry[["long.standard"]] 467 | } 468 | } else { 469 | tz_name <- tz_metazone_names_entry[[available_items[1]]] 470 | } 471 | 472 | tz_name 473 | } 474 | 475 | long_tz_id_to_metazone_long_id <- function(long_tzid) { 476 | 477 | tzid_in_tz_metazone_users <- long_tzid %in% tz_metazone_users$canonical_tz_name 478 | 479 | if (!tzid_in_tz_metazone_users) { 480 | 481 | if (long_tzid %in% unique(tz_name_resolution$tz_canonical)) { 482 | 483 | alt_names <- 484 | tz_name_resolution$tz_alt[tz_name_resolution$tz_canonical == long_tzid] 485 | 486 | if (!any(alt_names %in% tz_metazone_users$canonical_tz_name)) { 487 | return(NA_character_) 488 | } 489 | long_tzid <- alt_names[1] 490 | 491 | } else { 492 | return(NA_character_) 493 | } 494 | } 495 | 496 | rows <- which(tz_metazone_users$canonical_tz_name == long_tzid) 497 | tz_metazone_users_rows <- 498 | tz_metazone_users[[rows, "metazone_long_id"]] 499 | 500 | # Return NA if number of rows in `tz_metazone_users_rows` is zero 501 | if (length(tz_metazone_users_rows) == 0) { 502 | return(NA_character_) 503 | } 504 | 505 | # TODO: develop routine to further filter multirow `tz_metazone_users_rows` 506 | # to a single row based on `locale`; for now, obtain the first metazone 507 | metazone <- tz_metazone_users_rows[1] 508 | 509 | metazone 510 | } 511 | 512 | get_long_local_gmt <- function(tz_offset) { 513 | 514 | minutes <- formatC(round((abs(tz_offset) %% 1) * 60, 0), width = 2, flag = "0") 515 | hours <- formatC(trunc(abs(tz_offset)), width = 2, flag = "0") 516 | sign <- ifelse(tz_offset < 0, "-", "+") 517 | 518 | paste0("GMT", sign, hours, ":", minutes) 519 | } 520 | 521 | get_short_local_gmt <- function(tz_offset) { 522 | 523 | minutes <- formatC(round((abs(tz_offset) %% 1) * 60, 0), width = 2, flag = "0") 524 | 525 | if (minutes == "00") { 526 | minutes <- "" 527 | } else { 528 | minutes <- paste0(":", minutes) 529 | } 530 | 531 | hours <- as.character(trunc(abs(tz_offset))) 532 | sign <- ifelse(tz_offset < 0, "-", "+") 533 | 534 | paste0("GMT", sign, hours, minutes) 535 | } 536 | 537 | get_date_component <- function(input) { 538 | 539 | if (!is_date_present(input)) { 540 | return(NA_character_) 541 | } 542 | 543 | gsub(paste0(".*", get_date_pattern(), ".*"), "\\1", input) 544 | } 545 | 546 | get_time_component <- function(input) { 547 | 548 | if (!is_time_present(input)) { 549 | return(NA_character_) 550 | } 551 | 552 | gsub(paste0(".*", get_time_pattern(), ".*"), "\\2", input) 553 | } 554 | 555 | get_iana_tz <- function(input) { 556 | 557 | iana_pattern <- which_iana_pattern(input) 558 | 559 | value <- switch( 560 | iana_pattern, 561 | wrapped = { 562 | tz_name <- 563 | gsub( 564 | paste0(".*", get_time_pattern(), get_tz_pattern()), 565 | "", input) 566 | gsub("(\\(|\\[|\\)|\\])", "", tz_name) 567 | }, 568 | attached = gsub( 569 | paste0(".*", get_time_pattern(), get_tz_pattern(), "(\\s-\\s|\\s|\\/|\\|)"), 570 | "", 571 | input 572 | ), 573 | # default 574 | NA_character_ 575 | ) 576 | 577 | tz_name 578 | } 579 | 580 | strip_surrounding_whitespace <- function(input) { 581 | gsub("(^[[:space:]]*)|([[:space:]]*$)", "", input) 582 | } 583 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | 2 | braced <- function(value) { 3 | paste0("{", value, "}") 4 | } 5 | 6 | substr_right <- function(x, n) { 7 | substr(x, nchar(x) - n + 1, nchar(x)) 8 | } 9 | 10 | # 11 | # Taken from the i18n package 12 | # 13 | dates_elements_bigd <- 14 | list( 15 | months_format_abbrev = "months_format_abbrev", 16 | months_format_narrow = "months_format_narrow", 17 | months_format_wide = "months_format_wide", 18 | months_standalone_abbrev = "months_standalone_abbrev", 19 | months_standalone_narrow = "months_standalone_narrow", 20 | months_standalone_wide = "months_standalone_wide", 21 | days_format_abbrev = "days_format_abbrev", 22 | days_format_narrow = "days_format_narrow", 23 | days_format_short = "days_format_short", 24 | days_format_wide = "days_format_wide", 25 | days_standalone_abbrev = "days_standalone_abbrev", 26 | days_standalone_narrow = "days_standalone_narrow", 27 | days_standalone_short = "days_standalone_short", 28 | days_standalone_wide = "days_standalone_wide", 29 | quarters_format_abbrev = "quarters_format_abbrev", 30 | quarters_format_narrow = "quarters_format_narrow", 31 | quarters_format_wide = "quarters_format_wide", 32 | quarters_standalone_abbrev = "quarters_standalone_abbrev", 33 | quarters_standalone_narrow = "quarters_standalone_narrow", 34 | quarters_standalone_wide = "quarters_standalone_wide", 35 | dayperiods_format_abbrev = "dayperiods_format_abbrev", 36 | dayperiods_format_narrow = "dayperiods_format_narrow", 37 | dayperiods_format_wide = "dayperiods_format_wide", 38 | dayperiods_standalone_abbrev = "dayperiods_standalone_abbrev", 39 | dayperiods_standalone_narrow = "dayperiods_standalone_narrow", 40 | dayperiods_standalone_wide = "dayperiods_standalone_wide", 41 | eras_abbrev = "eras_abbrev", 42 | eras_names = "eras_names", 43 | eras_narrow = "eras_narrow", 44 | date_formats = "date_formats", 45 | date_skeletons = "date_skeletons", 46 | time_formats = "time_formats", 47 | time_skeletons = "time_skeletons", 48 | date_time_patterns = "date_time_patterns", 49 | date_time_available_formats = "date_time_available_formats", 50 | date_time_append_items = "date_time_append_items", 51 | date_time_interval_formats = "date_time_interval_formats" 52 | ) 53 | 54 | # 55 | # Taken from the i18n package 56 | # 57 | cldr_dates_bigd <- function(locale = "en", element) { 58 | 59 | rows <- which(dates$locale == locale) 60 | values <- dates[[rows, element]] 61 | values <- unlist(values, use.names = TRUE) 62 | 63 | names(values) <- sub("^value\\.", "", names(values)) 64 | 65 | as.list(values) 66 | } 67 | 68 | # TODO Remove this when depending on R 4.4 69 | `%||%` <- function(x, y) { 70 | if (is.null(x)) y else x 71 | } 72 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | utils::globalVariables( 2 | c( 3 | "dates_elements", 4 | "input_locale", 5 | "zone_name" 6 | ) 7 | ) 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | [![CRAN status](https://www.r-pkg.org/badges/version/bigD)](https://CRAN.R-project.org/package=bigD) 7 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 8 | [![R-CMD-check](https://github.com/rstudio/bigD/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/rstudio/bigD/actions/workflows/R-CMD-check.yaml) 9 | [![Codecov test coverage](https://codecov.io/gh/rstudio/bigD/graph/badge.svg)](https://app.codecov.io/gh/rstudio/bigD) 10 | 11 | [![The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) 12 | [![Monthly Downloads](https://cranlogs.r-pkg.org/badges/bigD)](https://CRAN.R-project.org/package=bigD) 13 | [![Total Downloads](https://cranlogs.r-pkg.org/badges/grand-total/bigD)](https://CRAN.R-project.org/package=bigD) 14 | [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.1%20adopted-ff69b4.svg)](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html) 15 | 16 |
17 |
18 |
19 | 20 | The goal of **bigD** is to provide an easy-to-use solution for formatting dates and times. The main function, `fdt()`, can take dates, times, and datetimes in various formats (including just strings) and it provides a means to format the output in different locales. The formatting syntax is much more powerful than `strptime`-based formatting (plus no need for `%` everywhere). Time zones in the input can be expressed in multiple ways and there's a ton of options for formatting time zones in the output too. 21 | 22 | ### Examples 23 | 24 | Given the ISO-8601 date string `"2018-07-04"`, let's adjust the `format` string to precisely get the date in the form we need. With `"y/M/d"` a nice year/month/day date is returned to us. 25 | 26 | ```r 27 | fdt( 28 | input = "2018-07-04", 29 | format = "y/M/d" 30 | ) 31 | ``` 32 | ``` 33 | #> [1] 2018/7/4 34 | ``` 35 | 36 | With variations of the same time parts, it's possible to get a friendlier version of the same date. 37 | 38 | ```r 39 | fdt( 40 | input = "2018-07-04", 41 | format = "MMMM d, y." 42 | ) 43 | ``` 44 | ``` 45 | #> [1] July 4, 2018. 46 | ``` 47 | 48 | With the `locale` option, we can localize the date. Let's change up the format string and use the German locale (`"de"`). 49 | 50 | ```r 51 | fdt( 52 | input = "2018-07-04", 53 | format = "d. MMMM y (EEEE).", 54 | locale = "de" 55 | ) 56 | ``` 57 | ``` 58 | #> [1] 4. Juli 2018 (Mittwoch). 59 | ``` 60 | 61 | With a datetime string like `"2018-07-24T14:44:22.234343-0800"`, we have more possibilities. This follows the ISO 8601 spec pretty closely and notice that the UTC offset value is added at the end (where it ought to be) to express some time zone information. Let's see a different datetime in French. 62 | 63 | ```r 64 | fdt( 65 | input = "2018-07-24T14:44:22.234343-0800", 66 | format = "MMM dd HH:mm:ss ZZZZ yyyy", 67 | locale = "fr" 68 | ) 69 | ``` 70 | ``` 71 | #> [1] juil. 24 14:44:22 GMT-8:00 2018 72 | ``` 73 | 74 | Next, let's take a look at a slight variation in Finnish. In the above the tz offset was formatted with `"ZZZZ"`. Below, let's use `"XX"` for that. 75 | 76 | ```r 77 | fdt( 78 | input = "2018-07-24T14:44:22.234343-0800", 79 | format = "MMMM dd HH:mm:ss 'yy XX", 80 | locale = "fi" 81 | ) 82 | ``` 83 | ``` 84 | #> [1] heinäkuuta 24 14:44:22 '18 -0800 85 | ``` 86 | 87 | Time zone support is super comprehensive. We can attach a time zone ID, like `"America/Vancouver"` (and there are many others), to a datetime string. We just got to make sure it's wrapped up in parens. 88 | 89 | ``` r 90 | fdt( 91 | input = "2014-06-23T13:24:09.84(America/Vancouver)", 92 | format = "yyyy.MM.dd G, HH:mm:ss zzzz", 93 | locale = "es" 94 | ) 95 | ``` 96 | ``` 97 | #> [1] 2014.06.23 d. C., 13:24:09 hora de verano del Pacífico 98 | ``` 99 | 100 | Just so you know, the time zone ID can alternatively be set to the `use_tz` argument of `fdt()`. Also, POSIXct/POSIXlt/Date times can be used as inputs. Plus, this function is vectorized. 101 | 102 | The formatting syntax has a lot to it but you can learn all about it in the fairly comprehensive documentation. 103 | 104 | ### Installation 105 | 106 | Want to try this out? The **bigD** package is available on **CRAN**: 107 | 108 | ```r 109 | install.packages("bigD") 110 | ``` 111 | 112 | You can also install the development version of **bigD** from **GitHub**: 113 | 114 | ```r 115 | # install.packages("pak") 116 | pak::pak("rstudio/bigD") 117 | ``` 118 | 119 | If you encounter a bug, have usage questions, or want to share ideas to make this package better, feel free to file an [issue](https://github.com/rstudio/bigD/issues). 120 | 121 | ##### Code of Conduct 122 | 123 | Please note that the `rstudio/bigD` project is released with a [contributor code of conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html).
By participating in this project you agree to abide by its terms. 124 | 125 | ##### 📄 License 126 | 127 | **bigD** is licensed under the MIT license. See the [`LICENSE.md`](LICENSE.md) file for more details. 128 | 129 | © Posit Software, PBC. 130 | 131 | ##### 🏛️ Governance 132 | 133 | This project is primarily maintained by [Rich Iannone](https://twitter.com/riannone). Should there also be other authors, they might occasionally assist with some of these duties. 134 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: ~ 2 | 3 | template: 4 | bootstrap: 5 5 | 6 | reference: 7 | - title: 'The Main Function: `fdt()`' 8 | desc: > 9 | The `fdt()` function is the one that takes in your date/time/datetime 10 | (in whichever way you want to provide it) and provides ways to format 11 | that input in a flexible manner. 12 | contents: 13 | - fdt 14 | 15 | - title: Locales 16 | desc: > 17 | You have the option to format dates times according to a specified locale, 18 | and there are hundreds of locales. The vector object `fdt_locales_vec` 19 | contains the locale IDs that accepted by `fdt()` and the `fdt_locales_lst` 20 | list makes selecting a locale in `fdt()` much more convenient (e.g., 21 | `locale = fdt_locales_lst$nl_BE`). 22 | contents: 23 | - fdt_locales_lst 24 | - fdt_locales_vec 25 | 26 | - title: Flexible Dates and Times 27 | desc: > 28 | Flexible dates and times (divided into 12- and 24-hour variants) embody 29 | the idea of canonical forms that are translated faithfully across many 30 | different locales. The vector objects (`*_vec`) show which of these forms 31 | are available and the list objects (`*_lst`) can be directly used to form 32 | an input value for `fdt()`'s `format` argument (e.g., 33 | `format = flex_t24_lst$Hms`). 34 | contents: 35 | - flex_d_lst 36 | - flex_d_vec 37 | - flex_t24_lst 38 | - flex_t24_vec 39 | - flex_t12_lst 40 | - flex_t12_vec 41 | 42 | - title: Standard Dates and Times 43 | desc: > 44 | Standard dates and times follow the convention of four different types 45 | ("short", "medium", "long", and "full") that are specially formulated for 46 | each and every locale. These helper functions provide a simple interface 47 | for choosing a standardized formatting pattern. Invoke any one of them as 48 | an input value for `fdt()`'s `format` argument (e.g., 49 | `format = standard_date_time(type = "medium")`). 50 | contents: 51 | - standard_date_time 52 | - standard_date 53 | - standard_time 54 | 55 | - title: Useful Day and Month Vectors 56 | desc: > 57 | Sometimes you need to know things about days of the week and months (like, 58 | their short names). We can get information like that with the 59 | `names_wkdays()`, `names_months()`, and `first_day_of_week()` functions. 60 | contents: 61 | - names_wkdays 62 | - names_months 63 | - first_day_of_week 64 | -------------------------------------------------------------------------------- /bigD.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace 22 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 1% 9 | informational: true 10 | patch: 11 | default: 12 | target: auto 13 | threshold: 1% 14 | informational: true 15 | -------------------------------------------------------------------------------- /data-raw/X01-tzdb.R: -------------------------------------------------------------------------------- 1 | library(tidyverse) 2 | 3 | tzdb <- 4 | readr::read_csv( 5 | file = "data-raw/time_zone.csv", 6 | col_names = c( 7 | "zone_name", "country_code", "abbreviation", 8 | "time_start", "gmt_offset", "dst" 9 | ), 10 | col_types = cols( 11 | zone_name = col_character(), 12 | country_code = col_character(), 13 | abbreviation = col_character(), 14 | time_start = col_double(), 15 | gmt_offset = col_double(), 16 | dst = col_double() 17 | ) 18 | ) %>% 19 | dplyr::mutate(dst = as.logical(dst)) %>% 20 | dplyr::rename( 21 | abbrev = abbreviation, 22 | gmt_offset_s = gmt_offset 23 | ) %>% 24 | dplyr::mutate( 25 | gmt_offset_h = gmt_offset_s / 3600, 26 | date_start = as.Date("1970-01-01") + (time_start / 86400) 27 | ) %>% 28 | dplyr::select( 29 | zone_name, country_code, abbrev, time_start, date_start, 30 | gmt_offset_s, gmt_offset_h, dst 31 | ) 32 | -------------------------------------------------------------------------------- /data-raw/X02-dates.R: -------------------------------------------------------------------------------- 1 | dates <- readRDS("data-raw/dates.rds") 2 | -------------------------------------------------------------------------------- /data-raw/X03-day_periods.R: -------------------------------------------------------------------------------- 1 | day_periods <- readRDS("data-raw/day_periods.rds") 2 | -------------------------------------------------------------------------------- /data-raw/X04-start_of_week.R: -------------------------------------------------------------------------------- 1 | start_of_week <- readRDS("data-raw/start_of_week.rds") 2 | -------------------------------------------------------------------------------- /data-raw/X05-tz_exemplar.R: -------------------------------------------------------------------------------- 1 | tz_exemplar <- readRDS("data-raw/tz_exemplar.rds") 2 | -------------------------------------------------------------------------------- /data-raw/X06-tz_metazone_names.R: -------------------------------------------------------------------------------- 1 | tz_metazone_names <- readRDS("data-raw/tz_metazone_names.rds") 2 | -------------------------------------------------------------------------------- /data-raw/X07-tz_metazone_users.R: -------------------------------------------------------------------------------- 1 | tz_metazone_users <- readRDS("data-raw/tz_metazone_users.rds") 2 | -------------------------------------------------------------------------------- /data-raw/X08-tz_map.R: -------------------------------------------------------------------------------- 1 | tz_map <- readRDS("data-raw/tz_map.rds") 2 | -------------------------------------------------------------------------------- /data-raw/X09-tz_formats.R: -------------------------------------------------------------------------------- 1 | tz_formats <- readRDS("data-raw/tz_formats.rds") 2 | -------------------------------------------------------------------------------- /data-raw/X10-tz_bcp_id.R: -------------------------------------------------------------------------------- 1 | tz_bcp_id <- readRDS("data-raw/tz_bcp_id.rds") 2 | -------------------------------------------------------------------------------- /data-raw/X11-tz_name_resolution.R: -------------------------------------------------------------------------------- 1 | library(dplyr) 2 | library(readr) 3 | 4 | tz_name_resolution <- 5 | readr::read_csv( 6 | file = "data-raw/tz_name_resolution.csv", 7 | col_types = cols( 8 | tz_canonical = col_character(), 9 | tz_alt = col_character() 10 | ) 11 | ) %>% 12 | dplyr::arrange(tz_canonical) 13 | -------------------------------------------------------------------------------- /data-raw/X12-default_locales.R: -------------------------------------------------------------------------------- 1 | default_locales <- readRDS("data-raw/default_locales.rds") 2 | -------------------------------------------------------------------------------- /data-raw/dates.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/bigD/be131649407343d36f86696b9086319587a2f0e0/data-raw/dates.rds -------------------------------------------------------------------------------- /data-raw/day_periods.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/bigD/be131649407343d36f86696b9086319587a2f0e0/data-raw/day_periods.rds -------------------------------------------------------------------------------- /data-raw/default_locales.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/bigD/be131649407343d36f86696b9086319587a2f0e0/data-raw/default_locales.rds -------------------------------------------------------------------------------- /data-raw/start_of_week.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/bigD/be131649407343d36f86696b9086319587a2f0e0/data-raw/start_of_week.rds -------------------------------------------------------------------------------- /data-raw/tz_bcp_id.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/bigD/be131649407343d36f86696b9086319587a2f0e0/data-raw/tz_bcp_id.rds -------------------------------------------------------------------------------- /data-raw/tz_exemplar.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/bigD/be131649407343d36f86696b9086319587a2f0e0/data-raw/tz_exemplar.rds -------------------------------------------------------------------------------- /data-raw/tz_formats.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/bigD/be131649407343d36f86696b9086319587a2f0e0/data-raw/tz_formats.rds -------------------------------------------------------------------------------- /data-raw/tz_map.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/bigD/be131649407343d36f86696b9086319587a2f0e0/data-raw/tz_map.rds -------------------------------------------------------------------------------- /data-raw/tz_metazone_names.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/bigD/be131649407343d36f86696b9086319587a2f0e0/data-raw/tz_metazone_names.rds -------------------------------------------------------------------------------- /data-raw/tz_metazone_users.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rstudio/bigD/be131649407343d36f86696b9086319587a2f0e0/data-raw/tz_metazone_users.rds -------------------------------------------------------------------------------- /data-raw/tz_name_resolution.csv: -------------------------------------------------------------------------------- 1 | tz_canonical,tz_alt 2 | Africa/Nairobi,Africa/Asmera 3 | America/Adak,America/Atka 4 | America/Argentina/Buenos_Aires,America/Buenos_Aires 5 | America/Argentina/Catamarca,America/Catamarca 6 | America/Argentina/Cordoba,America/Cordoba 7 | America/Indiana/Indianapolis,America/Fort_Wayne 8 | America/Nuuk,America/Godthab 9 | America/Indiana/Indianapolis,America/Indianapolis 10 | America/Argentina/Jujuy,America/Jujuy 11 | America/Indiana/Knox,America/Knox_IN 12 | America/Puerto_Rico,America/Kralendijk 13 | America/Kentucky/Louisville,America/Louisville 14 | America/Puerto_Rico,America/Lower_Princes 15 | America/Puerto_Rico,America/Marigot 16 | America/Argentina/Mendoza,America/Mendoza 17 | America/Rio_Branco,America/Porto_Acre 18 | America/Tijuana,America/Santa_Isabel 19 | America/Denver,America/Shiprock 20 | America/Puerto_Rico,America/St_Barthelemy 21 | America/Puerto_Rico,America/Virgin 22 | Pacific/Auckland,Antarctica/South_Pole 23 | Europe/Oslo,Arctic/Longyearbyen 24 | Asia/Ashgabat,Asia/Ashkhabad 25 | Asia/Kolkata,Asia/Calcutta 26 | Asia/Shanghai,Asia/Chungking 27 | Asia/Dhaka,Asia/Dacca 28 | Europe/Istanbul,Asia/Istanbul 29 | Asia/Kathmandu,Asia/Katmandu 30 | Asia/Macau,Asia/Macao 31 | Asia/Yangon,Asia/Rangoon 32 | Asia/Ho_Chi_Minh,Asia/Saigon 33 | Asia/Thimphu,Asia/Thimbu 34 | Asia/Makassar,Asia/Ujung_Pandang 35 | Asia/Ulaanbaatar,Asia/Ulan_Bator 36 | Atlantic/Faroe,Atlantic/Faeroe 37 | Australia/Sydney,Australia/ACT 38 | Australia/Sydney,Australia/Canberra 39 | Australia/Lord_Howe,Australia/LHI 40 | Australia/Darwin,Australia/North 41 | Australia/Sydney,Australia/NSW 42 | Australia/Brisbane,Australia/Queensland 43 | Australia/Adelaide,Australia/South 44 | Australia/Hobart,Australia/Tasmania 45 | Australia/Melbourne,Australia/Victoria 46 | Australia/Perth,Australia/West 47 | Australia/Broken_Hill,Australia/Yancowinna 48 | America/Rio_Branco,Brazil/Acre 49 | America/Noronha,Brazil/DeNoronha 50 | America/Sao_Paulo,Brazil/East 51 | America/Manaus,Brazil/West 52 | America/Halifax,Canada/Atlantic 53 | America/Winnipeg,Canada/Central 54 | America/Toronto,Canada/Eastern 55 | America/Edmonton,Canada/Mountain 56 | America/St_Johns,Canada/Newfoundland 57 | America/Vancouver,Canada/Pacific 58 | America/Regina,Canada/Saskatchewan 59 | America/Whitehorse,Canada/Yukon 60 | America/Santiago,Chile/Continental 61 | Pacific/Easter,Chile/EasterIsland 62 | America/Havana,Cuba 63 | Africa/Cairo,Egypt 64 | Europe/Dublin,Eire 65 | Etc/GMT,Etc/GMT+0 66 | Etc/GMT,Etc/GMT-0 67 | Etc/GMT,Etc/GMT0 68 | Etc/GMT,Etc/Greenwich 69 | Etc/UTC,Etc/UCT 70 | Etc/UTC,Etc/Universal 71 | Etc/UTC,Etc/Zulu 72 | Europe/Prague,Europe/Bratislava 73 | Europe/Zurich,Europe/Busingen 74 | Europe/Helsinki,Europe/Mariehamn 75 | Asia/Nicosia,Europe/Nicosia 76 | Europe/Belgrade,Europe/Podgorica 77 | Europe/Rome,Europe/San_Marino 78 | Europe/Rome,Europe/Vatican 79 | Europe/London,GB 80 | Europe/London,GB-Eire 81 | Etc/GMT,GMT 82 | Etc/GMT,GMT+0 83 | Etc/GMT,GMT-0 84 | Etc/GMT,GMT0 85 | Etc/GMT,Greenwich 86 | Asia/Hong_Kong,Hongkong 87 | Atlantic/Reykjavik,Iceland 88 | Asia/Tehran,Iran 89 | Asia/Jerusalem,Israel 90 | America/Jamaica,Jamaica 91 | Asia/Tokyo,Japan 92 | Pacific/Kwajalein,Kwajalein 93 | Africa/Tripoli,Libya 94 | America/Tijuana,Mexico/BajaNorte 95 | America/Mazatlan,Mexico/BajaSur 96 | America/Mexico_City,Mexico/General 97 | America/Denver,Navajo 98 | Pacific/Auckland,NZ 99 | Pacific/Chatham,NZ-CHAT 100 | Pacific/Pohnpei,Pacific/Ponape 101 | Pacific/Pago_Pago,Pacific/Samoa 102 | Pacific/Chuuk,Pacific/Truk 103 | Pacific/Chuuk,Pacific/Yap 104 | Europe/Warsaw,Poland 105 | Europe/Lisbon,Portugal 106 | Asia/Shanghai,PRC 107 | Asia/Taipei,ROC 108 | Asia/Seoul,ROK 109 | Asia/Singapore,Singapore 110 | Europe/Istanbul,Turkey 111 | Etc/UTC,UCT 112 | Etc/UTC,Universal 113 | America/Anchorage,US/Alaska 114 | America/Adak,US/Aleutian 115 | America/Phoenix,US/Arizona 116 | America/Chicago,US/Central 117 | America/Indiana/Indianapolis,US/East-Indiana 118 | America/New_York,US/Eastern 119 | Pacific/Honolulu,US/Hawaii 120 | America/Indiana/Knox,US/Indiana-Starke 121 | America/Detroit,US/Michigan 122 | America/Denver,US/Mountain 123 | America/Los_Angeles,US/Pacific 124 | Pacific/Pago_Pago,US/Samoa 125 | Etc/UTC,UTC 126 | Europe/Moscow,W-SU 127 | Europe/Moscow,Zulu 128 | Africa/Abidjan,Africa/Accra 129 | Africa/Nairobi,Africa/Addis_Ababa 130 | Africa/Nairobi,Africa/Asmara 131 | Africa/Abidjan,Africa/Bamako 132 | Africa/Lagos,Africa/Bangui 133 | Africa/Abidjan,Africa/Banjul 134 | Africa/Maputo,Africa/Blantyre 135 | Africa/Lagos,Africa/Brazzaville 136 | Africa/Maputo,Africa/Bujumbura 137 | Africa/Abidjan,Africa/Conakry 138 | Africa/Abidjan,Africa/Dakar 139 | Africa/Nairobi,Africa/Dar_es_Salaam 140 | Africa/Nairobi,Africa/Djibouti 141 | Africa/Lagos,Africa/Douala 142 | Africa/Abidjan,Africa/Freetown 143 | Africa/Maputo,Africa/Gaborone 144 | Africa/Maputo,Africa/Harare 145 | Africa/Nairobi,Africa/Kampala 146 | Africa/Maputo,Africa/Kigali 147 | Africa/Lagos,Africa/Kinshasa 148 | Africa/Lagos,Africa/Libreville 149 | Africa/Abidjan,Africa/Lome 150 | Africa/Lagos,Africa/Luanda 151 | Africa/Maputo,Africa/Lubumbashi 152 | Africa/Maputo,Africa/Lusaka 153 | Africa/Lagos,Africa/Malabo 154 | Africa/Johannesburg,Africa/Maseru 155 | Africa/Johannesburg,Africa/Mbabane 156 | Africa/Nairobi,Africa/Mogadishu 157 | Africa/Lagos,Africa/Niamey 158 | Africa/Abidjan,Africa/Nouakchott 159 | Africa/Abidjan,Africa/Ouagadougou 160 | Africa/Lagos,Africa/Porto-Novo 161 | Africa/Abidjan,Africa/Timbuktu 162 | America/Puerto_Rico,America/Anguilla 163 | America/Puerto_Rico,America/Antigua 164 | America/Argentina/Catamarca,America/Argentina/ComodRivadavia 165 | America/Puerto_Rico,America/Aruba 166 | America/Panama,America/Atikokan 167 | America/Puerto_Rico,America/Blanc-Sablon 168 | America/Panama,America/Cayman 169 | America/Panama,America/Coral_Harbour 170 | America/Phoenix,America/Creston 171 | America/Puerto_Rico,America/Curacao 172 | America/Puerto_Rico,America/Dominica 173 | America/Tijuana,America/Ensenada 174 | America/Puerto_Rico,America/Grenada 175 | America/Puerto_Rico,America/Guadeloupe 176 | America/Toronto,America/Montreal 177 | America/Puerto_Rico,America/Montserrat 178 | America/Toronto,America/Nassau 179 | America/Puerto_Rico,America/Port_of_Spain 180 | America/Argentina/Cordoba,America/Rosario 181 | America/Puerto_Rico,America/St_Kitts 182 | America/Puerto_Rico,America/St_Lucia 183 | America/Puerto_Rico,America/St_Thomas 184 | America/Puerto_Rico,America/St_Vincent 185 | America/Puerto_Rico,America/Tortola 186 | Pacific/Port_Moresby,Antarctica/DumontDUrville 187 | Pacific/Auckland,Antarctica/McMurdo 188 | Asia/Riyadh,Antarctica/Syowa 189 | Asia/Riyadh,Asia/Aden 190 | Asia/Qatar,Asia/Bahrain 191 | Asia/Shanghai,Asia/Chongqing 192 | Asia/Shanghai,Asia/Harbin 193 | Asia/Urumqi,Asia/Kashgar 194 | Asia/Riyadh,Asia/Kuwait 195 | Asia/Dubai,Asia/Muscat 196 | Asia/Bangkok,Asia/Phnom_Penh 197 | Asia/Jerusalem,Asia/Tel_Aviv 198 | Asia/Bangkok,Asia/Vientiane 199 | Europe/Oslo,Atlantic/Jan_Mayen 200 | Africa/Abidjan,Atlantic/St_Helena 201 | Australia/Hobart,Australia/Currie 202 | Europe/London,Europe/Belfast 203 | Europe/London,Europe/Guernsey 204 | Europe/London,Europe/Isle_of_Man 205 | Europe/London,Europe/Jersey 206 | Europe/Belgrade,Europe/Ljubljana 207 | Europe/Belgrade,Europe/Sarajevo 208 | Europe/Belgrade,Europe/Skopje 209 | Europe/Chisinau,Europe/Tiraspol 210 | Europe/Zurich,Europe/Vaduz 211 | Europe/Belgrade,Europe/Zagreb 212 | Africa/Nairobi,Indian/Antananarivo 213 | Africa/Nairobi,Indian/Comoro 214 | Africa/Nairobi,Indian/Mayotte 215 | Pacific/Kanton,Pacific/Enderbury 216 | Pacific/Honolulu,Pacific/Johnston 217 | Pacific/Pago_Pago,Pacific/Midway 218 | Pacific/Guam,Pacific/Saipan -------------------------------------------------------------------------------- /data-raw/zz_process_datasets_ext.R: -------------------------------------------------------------------------------- 1 | library(usethis) 2 | 3 | source("data-raw/X01-tzdb.R") 4 | source("data-raw/X02-dates.R") 5 | source("data-raw/X03-day_periods.R") 6 | source("data-raw/X04-start_of_week.R") 7 | source("data-raw/X05-tz_exemplar.R") 8 | source("data-raw/X06-tz_metazone_names.R") 9 | source("data-raw/X07-tz_metazone_users.R") 10 | source("data-raw/X08-tz_map.R") 11 | source("data-raw/X09-tz_formats.R") 12 | source("data-raw/X10-tz_bcp_id.R") 13 | source("data-raw/X11-tz_name_resolution.R") 14 | source("data-raw/X12-default_locales.R") 15 | 16 | # Create external datasets 17 | usethis::use_data( 18 | tzdb, dates, day_periods, start_of_week, 19 | tz_exemplar, tz_metazone_names, tz_metazone_users, 20 | tz_map, tz_formats, tz_bcp_id, tz_name_resolution, default_locales, 21 | internal = TRUE, overwrite = TRUE 22 | ) 23 | -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | # bigD 2 | 3 | 4 | CRAN status 5 | License: MIT 6 | R build status 7 | Coverage status 8 | The project has reached a stable, usable state and is being actively developed. 9 | Monthly Downloads 10 | Total Downloads 11 | Contributor Covenant 12 | 13 | 14 |
15 | 16 | The goal of **bigD** is to give everyone an easy yet flexible solution for formatting dates and times. The main function, `fdt()`, can take dates, times, and datetimes in various formats (including just strings) and it provides a means to format the output in different locales. The formatting syntax is much more powerful than `strptime`-based formatting (plus no need for `%` everywhere). Time zones in the input can be expressed in multiple ways and there's a ton of options for formatting time zones in the output too. 17 | 18 | ### Examples 19 | 20 | Given the ISO-8601 date string `"2018-07-04"`, let's adjust the `format` string to precisely get the date in the form we need. With `"y/M/d"` a nice year/month/day date is returned to us. 21 | 22 | ```r 23 | fdt( 24 | input = "2018-07-04", 25 | format = "y/M/d" 26 | ) 27 | ``` 28 | ``` 29 | #> [1] 2018/7/4 30 | ``` 31 | 32 | With variations of the same time parts, it's possible to get a friendlier version of the same date. 33 | 34 | ```r 35 | fdt( 36 | input = "2018-07-04", 37 | format = "MMMM d, y." 38 | ) 39 | ``` 40 | ``` 41 | #> [1] July 4, 2018. 42 | ``` 43 | 44 | With the `locale` option, we can localize the date. Let's change up the format string and use the German locale (`"de"`). 45 | 46 | ```r 47 | fdt( 48 | input = "2018-07-04", 49 | format = "d. MMMM y (EEEE).", 50 | locale = "de" 51 | ) 52 | ``` 53 | ``` 54 | #> [1] 4. Juli 2018 (Mittwoch). 55 | ``` 56 | 57 | With a datetime string like `"2018-07-24T14:44:22.234343-0800"`, we have more possibilities. This follows the ISO 8601 spec pretty closely and notice that the UTC offset value is added at the end (where it ought to be) to express some time zone information. Let's see a different datetime in French. 58 | 59 | ```r 60 | fdt( 61 | input = "2018-07-24T14:44:22.234343-0800", 62 | format = "MMM dd HH:mm:ss ZZZZ yyyy", 63 | locale = "fr" 64 | ) 65 | ``` 66 | ``` 67 | #> [1] juil. 24 14:44:22 GMT-8:00 2018 68 | ``` 69 | 70 | Next, let's take a look at a slight variation in Finnish. In the above the tz offset was formatted with `"ZZZZ"`. Below, let's use `"XX"` for that. 71 | 72 | ```r 73 | fdt( 74 | input = "2018-07-24T14:44:22.234343-0800", 75 | format = "MMMM dd HH:mm:ss 'yy XX", 76 | locale = "fi" 77 | ) 78 | ``` 79 | ``` 80 | #> [1] heinäkuuta 24 14:44:22 '18 -0800 81 | ``` 82 | 83 | Time zone support is super comprehensive. We can attach a time zone ID, like `"America/Vancouver"` (and there are many others), to a datetime string. We just got to make sure it's wrapped up in parens. 84 | 85 | ``` r 86 | fdt( 87 | input = "2014-06-23T13:24:09.84(America/Vancouver)", 88 | format = "yyyy.MM.dd G, HH:mm:ss zzzz", 89 | locale = "es" 90 | ) 91 | ``` 92 | ``` 93 | #> [1] 2014.06.23 d. C., 13:24:09 hora de verano del Pacífico 94 | ``` 95 | 96 | Just so you know, the time zone ID can alternatively be set to the `use_tz` argument of `fdt()`. Also, POSIXct/POSIXlt/Date times can be used as inputs. Plus, this function is vectorized. 97 | 98 | The formatting syntax has a lot to it but you can learn all about it in the fairly comprehensive documentation. 99 | 100 | ### Installation 101 | 102 | Want to try this out? The **bigD** package is available on **CRAN**: 103 | 104 | ```r 105 | install.packages("bigD") 106 | ``` 107 | 108 | You can also install the development version of **bigD** from **GitHub**: 109 | 110 | ```r 111 | devtools::install_github("rstudio/bigD") 112 | ``` 113 | 114 | If you encounter a bug, have usage questions, or want to share ideas to make this package better, feel free to file an [issue](https://github.com/rstudio/bigD/issues). 115 | 116 | ##### Code of Conduct 117 | 118 | Please note that the `rstudio/bigD` project is released with a [contributor code of conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html).
By participating in this project you agree to abide by its terms. 119 | 120 | ##### 📄 License 121 | 122 | **bigD** is licensed under the MIT license. See the [`LICENSE.md`](LICENSE.md) file for more details. 123 | 124 | © Posit Software, PBC. 125 | 126 | ##### 🏛️ Governance 127 | 128 | This project is primarily maintained by [Rich Iannone](https://twitter.com/riannone). Should there also be other authors, they might occasionally assist with some of these duties. 129 | -------------------------------------------------------------------------------- /man/fdt_locales_lst.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fdt_locales.R 3 | \docType{data} 4 | \name{fdt_locales_lst} 5 | \alias{fdt_locales_lst} 6 | \title{A list of all supported locales} 7 | \format{ 8 | An object of class \code{list} of length 574. 9 | } 10 | \usage{ 11 | fdt_locales_lst 12 | } 13 | \value{ 14 | A list where each element corresponds to a supported locale ID. 15 | } 16 | \description{ 17 | The \code{fdt_locales_lst} object is a list of all supported locales. This is 18 | useful when used with the \code{\link[=fdt]{fdt()}} function as the list can be auto-completed 19 | with a locale identifier and this generates valid input for the \code{locale} 20 | argument. 21 | } 22 | \section{Examples}{ 23 | 24 | 25 | The \code{fdt_locales_lst} object can be incredibly useful when choosing one of 26 | supported locales. You can avoid typing errors and every element of the list 27 | is meant to work. In this example, we'll use the \code{"da"} locale through 28 | use of the list. 29 | 30 | \if{html}{\out{
}}\preformatted{fdt( 31 | input = "2018-07-04 22:05", 32 | format = "yy-MMMM-d", 33 | locale = fdt_locales_lst$da 34 | ) 35 | }\if{html}{\out{
}} 36 | 37 | \if{html}{\out{
}}\preformatted{#> [1] "18-juli-4" 38 | }\if{html}{\out{
}} 39 | } 40 | 41 | \keyword{datasets} 42 | -------------------------------------------------------------------------------- /man/fdt_locales_vec.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fdt_locales.R 3 | \name{fdt_locales_vec} 4 | \alias{fdt_locales_vec} 5 | \title{Get a vector of all supported locales} 6 | \usage{ 7 | fdt_locales_vec() 8 | } 9 | \value{ 10 | A character vector of supported locale IDs. 11 | } 12 | \description{ 13 | The \code{fdt_locales_vec()} function produces a vector of all supported locale 14 | IDs in the \strong{bigD} package. 15 | } 16 | \examples{ 17 | # Let's get all the `ar` locales that exist 18 | # in the vector produced by `fdt_locales_vec()` 19 | grep("^ar", fdt_locales_vec(), value = TRUE) 20 | 21 | # Let's get all the locales that pertain to the 22 | # `CH` territory in the vector produced by 23 | # `fdt_locales_vec()` 24 | grep("CH", fdt_locales_vec(), value = TRUE) 25 | 26 | } 27 | -------------------------------------------------------------------------------- /man/figures/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /man/first_day_of_week.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/days_months.R 3 | \name{first_day_of_week} 4 | \alias{first_day_of_week} 5 | \title{Get a named vector of all first-day-of-the-week names for different regions} 6 | \usage{ 7 | first_day_of_week() 8 | } 9 | \value{ 10 | A character vector of short month names. 11 | } 12 | \description{ 13 | The \code{names_months()} function produces a vector of all short month names 14 | used by the \strong{bigD} package. 15 | } 16 | \examples{ 17 | # Let's get a vector of regions where the 18 | # first day of the week is Saturday 19 | names(first_day_of_week()[first_day_of_week() == "sat"]) 20 | 21 | } 22 | -------------------------------------------------------------------------------- /man/flex_d_lst.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fdf_ftf.R 3 | \docType{data} 4 | \name{flex_d_lst} 5 | \alias{flex_d_lst} 6 | \title{A list of all flexible date types} 7 | \format{ 8 | An object of class \code{list} of length 26. 9 | } 10 | \usage{ 11 | flex_d_lst 12 | } 13 | \value{ 14 | A list where each element corresponds to a classifier for a flexible 15 | date type. 16 | } 17 | \description{ 18 | The \code{flex_d_lst} object is a list of widely supported flexible date types. 19 | Flexible date types are classes of date formatting which can be translated 20 | across locales. There are 26 flexible date types in 21 | \code{flex_d_lst}. 22 | } 23 | \section{Examples}{ 24 | 25 | 26 | The \code{flex_d_lst} object can be incredibly useful when you need to get a 27 | format for date formatting that works across all locales. You can avoid 28 | typing errors by using this list and every flexible date type from this list 29 | is guaranteed to work across all supported locales. In this example, we'll 30 | use the \code{"yMMMEd"} flexible date type by accessing it from the \code{flex_d_lst} 31 | object. 32 | 33 | \if{html}{\out{
}}\preformatted{fdt( 34 | input = "2018-07-04 22:05", 35 | format = flex_d_lst$yMMMEd, 36 | locale = "en" 37 | ) 38 | }\if{html}{\out{
}} 39 | 40 | \if{html}{\out{
}}\preformatted{#> [1] "Wed, Jul 4, 2018" 41 | }\if{html}{\out{
}} 42 | 43 | If we wanted this in a different locale, the locale-specific \code{format} pattern 44 | behind the flexible date identifier would ensure consistency while moving to 45 | that locale. Let's use the \code{fdt_locales_lst} object in the same spirit to 46 | specify the French (Canada) locale. 47 | 48 | \if{html}{\out{
}}\preformatted{fdt( 49 | input = "2018-07-04 22:05", 50 | format = flex_d_lst$yMMMEd, 51 | locale = fdt_locales_lst$fr_CA 52 | ) 53 | }\if{html}{\out{
}} 54 | 55 | \if{html}{\out{
}}\preformatted{#> [1] "mer. 4 juill. 2018" 56 | }\if{html}{\out{
}} 57 | } 58 | 59 | \keyword{datasets} 60 | -------------------------------------------------------------------------------- /man/flex_d_vec.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fdf_ftf.R 3 | \name{flex_d_vec} 4 | \alias{flex_d_vec} 5 | \title{Get a vector of all flexible date types} 6 | \usage{ 7 | flex_d_vec() 8 | } 9 | \value{ 10 | A character vector of supported flexible date types. 11 | } 12 | \description{ 13 | The \code{flex_d_vec()} function produces a vector of all supported flexible date 14 | types in the \strong{bigD} package. These types are essentially identifiers for 15 | classes of cross-locale date formatting, so, none of these should be used 16 | directly in the \code{format} argument of the \code{fdt()} function (use the 17 | \link{flex_d_lst} object for that). 18 | } 19 | -------------------------------------------------------------------------------- /man/flex_t12_lst.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fdf_ftf.R 3 | \docType{data} 4 | \name{flex_t12_lst} 5 | \alias{flex_t12_lst} 6 | \title{A list of all 12-hour flexible time types} 7 | \format{ 8 | An object of class \code{list} of length 12. 9 | } 10 | \usage{ 11 | flex_t12_lst 12 | } 13 | \value{ 14 | A list where each element corresponds to a classifier for a 12-hour 15 | flexible time type. 16 | } 17 | \description{ 18 | The \code{flex_t12_lst} object is a list of the 12-hour flexible time types which 19 | are widely supported. Flexible time types are classes of time formatting 20 | which can be translated across locales. There are 12 21 | flexible time types of the 12-hour variety in \code{flex_t12_lst}. 22 | } 23 | \section{Examples}{ 24 | 25 | 26 | The \code{flex_t12_lst} object can be incredibly useful when you need to get a 27 | format for 12-hour time formatting that works across all locales. You can 28 | avoid typing errors by using this list and every flexible time type from this 29 | list is guaranteed to work across all supported locales. In this example, 30 | we'll use the \code{"Ehms"} flexible time type by accessing it from the 31 | \code{flex_t12_lst} object. 32 | 33 | \if{html}{\out{
}}\preformatted{fdt( 34 | input = "2018-07-04 22:05", 35 | format = flex_t12_lst$Bhms, 36 | locale = "en" 37 | ) 38 | }\if{html}{\out{
}} 39 | 40 | \if{html}{\out{
}}\preformatted{#> [1] "10:05:00 at night" 41 | }\if{html}{\out{
}} 42 | 43 | If we wanted this in a different locale, the locale-specific \code{format} pattern 44 | behind the flexible date identifier would ensure consistency while moving to 45 | that locale. Let's use the \code{fdt_locales_lst} object in the same spirit to 46 | specify the German (Austria) locale. 47 | 48 | \if{html}{\out{
}}\preformatted{fdt( 49 | input = "2018-07-04 22:05", 50 | format = flex_t12_lst$Bhms, 51 | locale = fdt_locales_lst$de_AT 52 | ) 53 | }\if{html}{\out{
}} 54 | 55 | \if{html}{\out{
}}\preformatted{#> [1] "10:05:00 abends" 56 | }\if{html}{\out{
}} 57 | } 58 | 59 | \keyword{datasets} 60 | -------------------------------------------------------------------------------- /man/flex_t12_vec.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fdf_ftf.R 3 | \name{flex_t12_vec} 4 | \alias{flex_t12_vec} 5 | \title{Get a vector of all 12-hour flexible time types} 6 | \usage{ 7 | flex_t12_vec() 8 | } 9 | \value{ 10 | A character vector of supported 12-hour flexible time types. 11 | } 12 | \description{ 13 | The \code{flex_t12_vec()} function produces a vector of all supported flexible 14 | 12-hour time types in the \strong{bigD} package. These types are essentially 15 | identifiers for classes of cross-locale time formatting, so, none of these 16 | should be used directly in the \code{format} argument of the \code{fdt()} function (use 17 | the \link{flex_t12_lst} object for that). 18 | } 19 | -------------------------------------------------------------------------------- /man/flex_t24_lst.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fdf_ftf.R 3 | \docType{data} 4 | \name{flex_t24_lst} 5 | \alias{flex_t24_lst} 6 | \title{A list of all 24-hour flexible time types} 7 | \format{ 8 | An object of class \code{list} of length 8. 9 | } 10 | \usage{ 11 | flex_t24_lst 12 | } 13 | \value{ 14 | A list where each element corresponds to a classifier for a 24-hour 15 | flexible time type. 16 | } 17 | \description{ 18 | The \code{flex_t24_lst} object is a list of the 24-hour flexible time types which 19 | are widely supported. Flexible time types are classes of time formatting 20 | which can be translated across locales. There are 8 21 | flexible time types of the 24-hour variety in \code{flex_t24_lst}. 22 | } 23 | \section{Examples}{ 24 | 25 | 26 | The \code{flex_t24_lst} object can be incredibly useful when you need to get a 27 | format for 24-hour time formatting that works across all locales. You can 28 | avoid typing errors by using this list and every flexible time type from this 29 | list is guaranteed to work across all supported locales. In this example, 30 | we'll use the \code{"EHms"} flexible time type by accessing it from the 31 | \code{flex_t24_lst} object. 32 | 33 | \if{html}{\out{
}}\preformatted{fdt( 34 | input = "2018-07-04 22:05", 35 | format = flex_t24_lst$EHms, 36 | locale = "en" 37 | ) 38 | }\if{html}{\out{
}} 39 | 40 | \if{html}{\out{
}}\preformatted{#> [1] "Wed 22:05:00" 41 | }\if{html}{\out{
}} 42 | 43 | If we wanted this in a different locale, the locale-specific \code{format} pattern 44 | behind the flexible date identifier would ensure consistency while moving to 45 | that locale. Let's use the \code{fdt_locales_lst} object in the same spirit to 46 | specify the German locale. 47 | 48 | \if{html}{\out{
}}\preformatted{fdt( 49 | input = "2018-07-04 22:05", 50 | format = flex_t24_lst$EHms, 51 | locale = fdt_locales_lst$de 52 | ) 53 | }\if{html}{\out{
}} 54 | 55 | \if{html}{\out{
}}\preformatted{#> [1] "Mi, 22:05:00" 56 | }\if{html}{\out{
}} 57 | } 58 | 59 | \keyword{datasets} 60 | -------------------------------------------------------------------------------- /man/flex_t24_vec.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fdf_ftf.R 3 | \name{flex_t24_vec} 4 | \alias{flex_t24_vec} 5 | \title{Get a vector of all 24-hour flexible time types} 6 | \usage{ 7 | flex_t24_vec() 8 | } 9 | \value{ 10 | A character vector of supported 24-hour flexible time types. 11 | } 12 | \description{ 13 | The \code{flex_t24_vec()} function produces a vector of all supported flexible 14 | 24-hour time types in the \strong{bigD} package. These types are essentially 15 | identifiers for classes of cross-locale time formatting, so, none of these 16 | should be used directly in the \code{format} argument of the \code{fdt()} function (use 17 | the \link{flex_t24_lst} object for that). 18 | } 19 | -------------------------------------------------------------------------------- /man/names_months.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/days_months.R 3 | \name{names_months} 4 | \alias{names_months} 5 | \title{Get a vector of all the short month names} 6 | \usage{ 7 | names_months() 8 | } 9 | \value{ 10 | A character vector of short month names. 11 | } 12 | \description{ 13 | The \code{names_months()} function produces a vector of all short month names 14 | used by the \strong{bigD} package. 15 | } 16 | \examples{ 17 | # Let's get all the short month names with 18 | # the `names_months()` function 19 | names_months() 20 | 21 | } 22 | -------------------------------------------------------------------------------- /man/names_wkdays.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/days_months.R 3 | \name{names_wkdays} 4 | \alias{names_wkdays} 5 | \title{Get a vector of all the short weekday names} 6 | \usage{ 7 | names_wkdays() 8 | } 9 | \value{ 10 | A character vector of short weekday names. 11 | } 12 | \description{ 13 | The \code{names_wkdays()} function produces a vector of all short weekday names 14 | used by the \strong{bigD} package. 15 | } 16 | \examples{ 17 | # Let's get all the short weekday names with 18 | # the `names_wkdays()` function 19 | names_wkdays() 20 | 21 | } 22 | -------------------------------------------------------------------------------- /man/standard_date.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/standard_date_time.R 3 | \name{standard_date} 4 | \alias{standard_date} 5 | \title{Obtain a standard date format that works across locales} 6 | \usage{ 7 | standard_date(type = c("short", "medium", "long", "full")) 8 | } 9 | \arguments{ 10 | \item{type}{One of four standardized types for the resulting date that range 11 | in precision and verbosity. These are \code{"short"} (the default), \code{"medium"}, 12 | \code{"long"}, and \code{"full"}.} 13 | } 14 | \value{ 15 | A vector of class \code{date_time_pattern}. 16 | } 17 | \description{ 18 | The \code{standard_date()} function can be invoked in the \code{format} argument of the 19 | \code{fdt()} function to help generate a locale-specific formatting string of a 20 | certain 'type' of formatted date. The \code{type} value is a keyword that 21 | represents precision and verbosity; the available keywords are \code{"short"} (the 22 | default), \code{"medium"}, \code{"long"}, and \code{"full"}. 23 | } 24 | \section{Examples}{ 25 | 26 | 27 | With an input datetime of \code{"2018-07-04 22:05(America/Vancouver)"}, we can 28 | format as a date in a standardized way with \code{standard_date()} providing the 29 | correct formatting string. This function is invoked in the \code{format} argument 30 | of \code{fdt()}: 31 | 32 | \if{html}{\out{
}}\preformatted{fdt( 33 | input = "2018-07-04 22:05(America/Vancouver)", 34 | format = standard_date(type = "full") 35 | ) 36 | }\if{html}{\out{
}} 37 | 38 | \if{html}{\out{
}}\preformatted{#> [1] "Wednesday, July 4, 2018" 39 | }\if{html}{\out{
}} 40 | 41 | The locale can be changed and we don't have to worry about the particulars 42 | of the formatting string (they are standardized across locales). 43 | 44 | \if{html}{\out{
}}\preformatted{fdt( 45 | input = "2018-07-04 22:05(America/Vancouver)", 46 | format = standard_date(type = "full"), 47 | locale = fdt_locales_lst$nl 48 | ) 49 | }\if{html}{\out{
}} 50 | 51 | \if{html}{\out{
}}\preformatted{#> [1] "woensdag 4 juli 2018" 52 | }\if{html}{\out{
}} 53 | 54 | We can use different \code{type} values to control the output date string. The 55 | default is \code{"short"}. 56 | 57 | \if{html}{\out{
}}\preformatted{fdt( 58 | input = "2018-07-04 22:05(America/Vancouver)", 59 | format = standard_date() 60 | ) 61 | }\if{html}{\out{
}} 62 | 63 | \if{html}{\out{
}}\preformatted{#> [1] "7/4/18" 64 | }\if{html}{\out{
}} 65 | 66 | After that, it's \code{"medium"}: 67 | 68 | \if{html}{\out{
}}\preformatted{fdt( 69 | input = "2018-07-04 22:05(America/Vancouver)", 70 | format = standard_date(type = "medium") 71 | ) 72 | }\if{html}{\out{
}} 73 | 74 | \if{html}{\out{
}}\preformatted{#> [1] "Jul 4, 2018" 75 | }\if{html}{\out{
}} 76 | 77 | Then, \code{"long"}: 78 | 79 | \if{html}{\out{
}}\preformatted{fdt( 80 | input = "2018-07-04 22:05(America/Vancouver)", 81 | format = standard_date(type = "long") 82 | ) 83 | }\if{html}{\out{
}} 84 | 85 | \if{html}{\out{
}}\preformatted{#> [1] "July 4, 2018" 86 | }\if{html}{\out{
}} 87 | 88 | And finally up to \code{"full"}, which was demonstrated in the first example. 89 | } 90 | 91 | -------------------------------------------------------------------------------- /man/standard_date_time.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/standard_date_time.R 3 | \name{standard_date_time} 4 | \alias{standard_date_time} 5 | \title{Obtain a standard datetime format that works across locales} 6 | \usage{ 7 | standard_date_time(type = c("short", "medium", "long", "full")) 8 | } 9 | \arguments{ 10 | \item{type}{One of four standardized types for the resulting datetime that 11 | range in precision and verbosity. These are \code{"short"} (the default), 12 | \code{"medium"}, \code{"long"}, and \code{"full"}.} 13 | } 14 | \value{ 15 | A vector of class \code{date_time_pattern}. 16 | } 17 | \description{ 18 | The \code{standard_date_time()} function can be invoked in the \code{format} argument 19 | of the \code{fdt()} function to help generate a locale-specific formatting string 20 | of a certain 'type' of formatted datetime. The \code{type} value is a keyword that 21 | represents precision and verbosity; the available keywords are \code{"short"} (the 22 | default), \code{"medium"}, \code{"long"}, and \code{"full"}. 23 | } 24 | \section{Examples}{ 25 | 26 | 27 | With an input datetime of \code{"2018-07-04 22:05(America/Vancouver)"}, we can 28 | format the date and time in a standardized way with \code{standard_date_time()} 29 | providing the correct formatting string. This function is invoked in the 30 | \code{format} argument of \code{fdt()}: 31 | 32 | \if{html}{\out{
}}\preformatted{fdt( 33 | input = "2018-07-04 22:05(America/Vancouver)", 34 | format = standard_date_time(type = "full") 35 | ) 36 | }\if{html}{\out{
}} 37 | 38 | \if{html}{\out{
}}\preformatted{#> [1] "Wednesday, July 4, 2018 at 10:05:00 PM Pacific Daylight Time" 39 | }\if{html}{\out{
}} 40 | 41 | The locale can be changed and we don't have to worry about the particulars 42 | of the formatting string (they are standardized across locales). 43 | 44 | \if{html}{\out{
}}\preformatted{fdt( 45 | input = "2018-07-04 22:05(America/Vancouver)", 46 | format = standard_date_time(type = "full"), 47 | locale = fdt_locales_lst$nl 48 | ) 49 | }\if{html}{\out{
}} 50 | 51 | \if{html}{\out{
}}\preformatted{#> [1] "woensdag 4 juli 2018 om 22:05:00 Pacific-zomertijd" 52 | }\if{html}{\out{
}} 53 | 54 | We can use different \code{type} values to control the output datetime string. The 55 | default is \code{"short"}. 56 | 57 | \if{html}{\out{
}}\preformatted{fdt( 58 | input = "2018-07-04 22:05(America/Vancouver)", 59 | format = standard_date_time() 60 | ) 61 | }\if{html}{\out{
}} 62 | 63 | \if{html}{\out{
}}\preformatted{#> [1] "7/4/18, 10:05 PM" 64 | }\if{html}{\out{
}} 65 | 66 | After that, it's \code{"medium"}: 67 | 68 | \if{html}{\out{
}}\preformatted{fdt( 69 | input = "2018-07-04 22:05(America/Vancouver)", 70 | format = standard_date_time(type = "medium") 71 | ) 72 | }\if{html}{\out{
}} 73 | 74 | \if{html}{\out{
}}\preformatted{#> [1] "Jul 4, 2018, 10:05:00 PM" 75 | }\if{html}{\out{
}} 76 | 77 | The \code{"short"} and \code{"medium"} types don't display time zone information in the 78 | output. Beginning with \code{"long"}, the tz is shown. 79 | 80 | \if{html}{\out{
}}\preformatted{fdt( 81 | input = "2018-07-04 22:05(America/Vancouver)", 82 | format = standard_date_time(type = "long") 83 | ) 84 | }\if{html}{\out{
}} 85 | 86 | \if{html}{\out{
}}\preformatted{#> [1] "July 4, 2018 at 10:05:00 PM PDT" 87 | }\if{html}{\out{
}} 88 | 89 | If you don't include time zone information in the input, the \code{"UTC"} time 90 | zone will be assumed: 91 | 92 | \if{html}{\out{
}}\preformatted{fdt( 93 | input = "2018-07-04 22:05", 94 | format = standard_date_time(type = "full") 95 | ) 96 | }\if{html}{\out{
}} 97 | 98 | \if{html}{\out{
}}\preformatted{#> [1] "Wednesday, July 4, 2018 at 10:05:00 PM GMT+00:00" 99 | }\if{html}{\out{
}} 100 | } 101 | 102 | -------------------------------------------------------------------------------- /man/standard_time.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/standard_date_time.R 3 | \name{standard_time} 4 | \alias{standard_time} 5 | \title{Obtain a standard time format that works across locales} 6 | \usage{ 7 | standard_time(type = c("short", "medium", "long", "full")) 8 | } 9 | \arguments{ 10 | \item{type}{One of four standardized types for the resulting time that range 11 | in precision and verbosity. These are \code{"short"} (the default), \code{"medium"}, 12 | \code{"long"}, and \code{"full"}.} 13 | } 14 | \value{ 15 | A vector of class \code{date_time_pattern}. 16 | } 17 | \description{ 18 | The \code{standard_time()} function can be invoked in the \code{format} argument of the 19 | \code{fdt()} function to help generate a locale-specific formatting string of a 20 | certain 'type' of formatted time. The \code{type} value is a keyword that 21 | represents precision and verbosity; the available keywords are \code{"short"} (the 22 | default), \code{"medium"}, \code{"long"}, and \code{"full"}. 23 | } 24 | \section{Examples}{ 25 | 26 | 27 | With an input datetime of \code{"2018-07-04 22:05(America/Vancouver)"}, we can 28 | format as a time in a standardized way with \code{standard_time()} providing the 29 | correct formatting string. This function is invoked in the \code{format} argument 30 | of \code{fdt()}: 31 | 32 | \if{html}{\out{
}}\preformatted{fdt( 33 | input = "2018-07-04 22:05(America/Vancouver)", 34 | format = standard_time(type = "full") 35 | ) 36 | }\if{html}{\out{
}} 37 | 38 | \if{html}{\out{
}}\preformatted{#> [1] "10:05:00 PM Pacific Daylight Time" 39 | }\if{html}{\out{
}} 40 | 41 | The locale can be changed and we don't have to worry about the particulars 42 | of the formatting string (they are standardized across locales). 43 | 44 | \if{html}{\out{
}}\preformatted{fdt( 45 | input = "2018-07-04 22:05(America/Vancouver)", 46 | format = standard_time(type = "full"), 47 | locale = fdt_locales_lst$nl 48 | ) 49 | }\if{html}{\out{
}} 50 | 51 | \if{html}{\out{
}}\preformatted{#> [1] "22:05:00 Pacific-zomertijd" 52 | }\if{html}{\out{
}} 53 | 54 | We can use different \code{type} values to control the output date string. The 55 | default is \code{"short"}. 56 | 57 | \if{html}{\out{
}}\preformatted{fdt( 58 | input = "2018-07-04 22:05(America/Vancouver)", 59 | format = standard_time() 60 | ) 61 | }\if{html}{\out{
}} 62 | 63 | \if{html}{\out{
}}\preformatted{#> [1] "10:05 PM" 64 | }\if{html}{\out{
}} 65 | 66 | After that, it's \code{"medium"}: 67 | 68 | \if{html}{\out{
}}\preformatted{fdt( 69 | input = "2018-07-04 22:05(America/Vancouver)", 70 | format = standard_time(type = "medium") 71 | ) 72 | }\if{html}{\out{
}} 73 | 74 | \if{html}{\out{
}}\preformatted{#> [1] "10:05:00 PM" 75 | }\if{html}{\out{
}} 76 | 77 | Then, \code{"long"}: 78 | 79 | \if{html}{\out{
}}\preformatted{fdt( 80 | input = "2018-07-04 22:05(America/Vancouver)", 81 | format = standard_time(type = "long") 82 | ) 83 | }\if{html}{\out{
}} 84 | 85 | \if{html}{\out{
}}\preformatted{#> [1] "10:05:00 PM PDT" 86 | }\if{html}{\out{
}} 87 | 88 | And finally up to \code{"full"}, which was demonstrated in the first example. 89 | } 90 | 91 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(bigD) 3 | 4 | test_check("bigD") 5 | -------------------------------------------------------------------------------- /tests/testthat/test-dates_times.R: -------------------------------------------------------------------------------- 1 | test_that("ISO dates can be parsed from a string and formatted", { 2 | 3 | iso_date_1 <- "2018-07-04" 4 | iso_date_2 <- "2020-01-12" 5 | iso_date_3 <- "2020-01-04" 6 | 7 | # 8 | # Checks with `y`/`yy`, `M`/`MM`/`MMM`/`MMMM`/`MMMMM`, `d`/`dd` 9 | # 10 | 11 | fdt(input = iso_date_1, format = "y/M/d") %>% 12 | expect_equal("2018/7/4") 13 | fdt(input = iso_date_1, format = "yy-MM-dd") %>% 14 | expect_equal("18-07-04") 15 | fdt(input = iso_date_1, format = "y-MMM-dd") %>% 16 | expect_equal("2018-Jul-04") 17 | fdt(input = iso_date_1, format = "y-MMMM-dd") %>% 18 | expect_equal("2018-July-04") 19 | fdt(input = iso_date_1, format = "y-MMMMM-dd") %>% 20 | expect_equal("2018-J-04") 21 | 22 | # 23 | # Checks with `L` and `LL` 24 | # 25 | 26 | fdt(input = iso_date_1, format = "y-L-dd") %>% 27 | expect_equal("2018-7-04") 28 | fdt(input = iso_date_1, format = "y-LL-dd") %>% 29 | expect_equal("2018-07-04") 30 | 31 | # 32 | # Checks with `D`/`DD`/`DDD` 33 | # 34 | 35 | fdt(input = iso_date_1, format = "y/M/D") %>% 36 | expect_equal("2018/7/185") 37 | fdt(input = iso_date_1, format = "y/M/DD") %>% 38 | expect_equal("2018/7/185") 39 | fdt(input = iso_date_1, format = "y/M/DDD") %>% 40 | expect_equal("2018/7/185") 41 | fdt(input = iso_date_2, format = "y/M/D") %>% 42 | expect_equal("2020/1/12") 43 | fdt(input = iso_date_2, format = "y/M/DD") %>% 44 | expect_equal("2020/1/12") 45 | fdt(input = iso_date_2, format = "y/M/DDD") %>% 46 | expect_equal("2020/1/012") 47 | fdt(input = iso_date_3, format = "y/M/D") %>% 48 | expect_equal("2020/1/4") 49 | fdt(input = iso_date_3, format = "y/M/DD") %>% 50 | expect_equal("2020/1/04") 51 | fdt(input = iso_date_3, format = "y/M/DDD") %>% 52 | expect_equal("2020/1/004") 53 | }) 54 | 55 | test_that("Distant years can be accepted", { 56 | 57 | fdt(input = "1001-01-01") %>% 58 | expect_equal("1001-01-01T00:00:00Z") 59 | fdt(input = "0900-01-01") %>% 60 | expect_equal("0900-01-01T00:00:00Z") 61 | fdt(input = "0090-01-01") %>% 62 | expect_equal("0090-01-01T00:00:00Z") 63 | fdt(input = "0009-01-01") %>% 64 | expect_equal("0009-01-01T00:00:00Z") 65 | fdt(input = "0000-01-01") %>% 66 | expect_equal("0000-01-01T00:00:00Z") 67 | fdt(input = "9001-01-01") %>% 68 | expect_equal("9001-01-01T00:00:00Z") 69 | 70 | fdt(input = "1001-01-01", format = "y") %>% 71 | expect_equal("1001") 72 | fdt(input = "0900-01-01", format = "y") %>% 73 | expect_equal("900") 74 | fdt(input = "0090-01-01", format = "y") %>% 75 | expect_equal("90") 76 | fdt(input = "0009-01-01", format = "y") %>% 77 | expect_equal("9") 78 | fdt(input = "0000-01-01", format = "y") %>% 79 | expect_equal("0") 80 | fdt(input = "9001-01-01", format = "y") %>% 81 | expect_equal("9001") 82 | 83 | fdt(input = "1001-01-01", format = "yy") %>% 84 | expect_equal("01") 85 | fdt(input = "0900-01-01", format = "yy") %>% 86 | expect_equal("00") 87 | fdt(input = "0090-01-01", format = "yy") %>% 88 | expect_equal("90") 89 | fdt(input = "0009-01-01", format = "yy") %>% 90 | expect_equal("09") 91 | fdt(input = "0000-01-01", format = "yy") %>% 92 | expect_equal("00") 93 | fdt(input = "9001-01-01", format = "yy") %>% 94 | expect_equal("01") 95 | 96 | fdt(input = "1001-01-01", format = "yyy") %>% 97 | expect_equal("1001") 98 | fdt(input = "0900-01-01", format = "yyy") %>% 99 | expect_equal("900") 100 | fdt(input = "0090-01-01", format = "yyy") %>% 101 | expect_equal("090") 102 | fdt(input = "0009-01-01", format = "yyy") %>% 103 | expect_equal("009") 104 | fdt(input = "0000-01-01", format = "yyy") %>% 105 | expect_equal("000") 106 | fdt(input = "9001-01-01", format = "yyy") %>% 107 | expect_equal("9001") 108 | }) 109 | 110 | test_that("Years by themselves are accepted", { 111 | 112 | fdt(input = "1001") %>% 113 | expect_equal("1001-01-01T00:00:00Z") 114 | fdt(input = "0900") %>% 115 | expect_equal("0900-01-01T00:00:00Z") 116 | fdt(input = "0090") %>% 117 | expect_equal("0090-01-01T00:00:00Z") 118 | fdt(input = "0009") %>% 119 | expect_equal("0009-01-01T00:00:00Z") 120 | fdt(input = "0000") %>% 121 | expect_equal("0000-01-01T00:00:00Z") 122 | fdt(input = "9001") %>% 123 | expect_equal("9001-01-01T00:00:00Z") 124 | 125 | fdt(input = "1001", format = "y") %>% 126 | expect_equal("1001") 127 | fdt(input = "0900", format = "y") %>% 128 | expect_equal("900") 129 | fdt(input = "0090", format = "y") %>% 130 | expect_equal("90") 131 | fdt(input = "0009", format = "y") %>% 132 | expect_equal("9") 133 | fdt(input = "0000", format = "y") %>% 134 | expect_equal("0") 135 | fdt(input = "9001", format = "y") %>% 136 | expect_equal("9001") 137 | 138 | fdt(input = "1001", format = "yy") %>% 139 | expect_equal("01") 140 | fdt(input = "0900", format = "yy") %>% 141 | expect_equal("00") 142 | fdt(input = "0090", format = "yy") %>% 143 | expect_equal("90") 144 | fdt(input = "0009", format = "yy") %>% 145 | expect_equal("09") 146 | fdt(input = "0000", format = "yy") %>% 147 | expect_equal("00") 148 | fdt(input = "9001", format = "yy") %>% 149 | expect_equal("01") 150 | 151 | fdt(input = "1001", format = "yyy") %>% 152 | expect_equal("1001") 153 | fdt(input = "0900", format = "yyy") %>% 154 | expect_equal("900") 155 | fdt(input = "0090", format = "yyy") %>% 156 | expect_equal("090") 157 | fdt(input = "0009", format = "yyy") %>% 158 | expect_equal("009") 159 | fdt(input = "0000", format = "yyy") %>% 160 | expect_equal("000") 161 | fdt(input = "9001", format = "yyy") %>% 162 | expect_equal("9001") 163 | }) 164 | 165 | test_that("The various week date formats are accepted", { 166 | 167 | expect_equal(fdt("2023-W52"), "2023-12-25T00:00:00Z") 168 | expect_equal(fdt("2023-W52"), fdt("2023W52")) 169 | expect_equal(fdt("2023-W52"), fdt("2023-W52-1")) 170 | expect_equal(fdt("2023-W52"), fdt("2023W521")) 171 | expect_equal(fdt("2023-W01"), "2023-01-02T00:00:00Z") 172 | expect_equal(fdt("2023-W01-1"), "2023-01-02T00:00:00Z") 173 | expect_equal(fdt("2023-W01-2"), "2023-01-03T00:00:00Z") 174 | 175 | expect_equal(fdt("2023-W01-3"), "2023-01-04T00:00:00Z") 176 | expect_equal(fdt("2023-W01-4"), "2023-01-05T00:00:00Z") 177 | expect_equal(fdt("2023-W01-5"), "2023-01-06T00:00:00Z") 178 | expect_equal(fdt("2023-W01-6"), "2023-01-07T00:00:00Z") 179 | expect_equal(fdt("2023-W01-7"), "2023-01-08T00:00:00Z") 180 | 181 | expect_equal(fdt("1922-W01"), "1922-01-02T00:00:00Z") 182 | expect_equal(fdt("1963-W01"), "1962-12-31T00:00:00Z") 183 | expect_equal(fdt("1963-W53"), "1963-12-30T00:00:00Z") 184 | }) 185 | 186 | test_that("The --MM-DD and --MMDD formats are accepted", { 187 | 188 | expect_equal(fdt(input = "--03-05", format = "MM-dd"), "03-05") 189 | expect_equal(fdt(input = "--0305", format = "MM-dd"), "03-05") 190 | 191 | expect_equal(fdt(input = "--12-05", format = "MM-dd"), "12-05") 192 | expect_equal(fdt(input = "--1205", format = "MM-dd"), "12-05") 193 | 194 | expect_equal(fdt(input = "--06-30", format = "MM-dd"), "06-30") 195 | expect_equal(fdt(input = "--0630", format = "MM-dd"), "06-30") 196 | }) 197 | 198 | test_that("Years with BC or AD (or variations of these) can be parsed", { 199 | 200 | expect_equal(fdt(input = "5000 BC", format = "y G"), "5000 BC") 201 | expect_equal(fdt(input = "5000 BC", format = "y GGGG"), "5000 Before Christ") 202 | expect_equal(fdt(input = "5000 BC", format = "y GGGGG"), "5000 B") 203 | 204 | expect_equal(fdt(input = "101010 BC", format = "y G"), "101010 BC") 205 | expect_equal(fdt(input = "5101010 AD", format = "y G"), "5101010 AD") 206 | 207 | expect_equal(fdt(input = "500 BCE", format = "y G"), "500 BC") 208 | expect_equal(fdt(input = "500 B.C.E.", format = "y G"), "500 BC") 209 | expect_equal(fdt(input = "20 AD", format = "y G"), "20 AD") 210 | expect_equal(fdt(input = "20 CE", format = "y G"), "20 AD") 211 | expect_equal(fdt(input = "20 C.E.", format = "y G"), "20 AD") 212 | expect_equal(fdt(input = "20 EV", format = "y G"), "20 AD") 213 | 214 | expect_equal(fdt(input = "500 BCE", format = "y G", locale = "es"), "500 a. C.") 215 | expect_equal(fdt(input = "500 AD", format = "y G", locale = "es"), "500 d. C.") 216 | }) 217 | 218 | test_that("The YYYY-MM format is accepted", { 219 | 220 | fdt(input = "1001-02") %>% 221 | expect_equal("1001-02-01T00:00:00Z") 222 | fdt(input = "0900-03") %>% 223 | expect_equal("0900-03-01T00:00:00Z") 224 | fdt(input = "0090-04") %>% 225 | expect_equal("0090-04-01T00:00:00Z") 226 | fdt(input = "0009-05") %>% 227 | expect_equal("0009-05-01T00:00:00Z") 228 | fdt(input = "0000-06") %>% 229 | expect_equal("0000-06-01T00:00:00Z") 230 | fdt(input = "9001-11") %>% 231 | expect_equal("9001-11-01T00:00:00Z") 232 | 233 | fdt(input = "1001-05", format = "y/MM") %>% 234 | expect_equal("1001/05") 235 | fdt(input = "0900-10", format = "y/MM") %>% 236 | expect_equal("900/10") 237 | fdt(input = "0090-05", format = "y/MM") %>% 238 | expect_equal("90/05") 239 | fdt(input = "0009-10", format = "y/MM") %>% 240 | expect_equal("9/10") 241 | fdt(input = "0000-05", format = "y/MM") %>% 242 | expect_equal("0/05") 243 | fdt(input = "9001-10", format = "y/MM") %>% 244 | expect_equal("9001/10") 245 | }) 246 | 247 | test_that("ISO dates can be formatted even with string literals", { 248 | 249 | iso_date_1 <- "2018-07-04" 250 | iso_date_2 <- "2020-01-12" 251 | iso_date_3 <- "2020-01-04" 252 | 253 | fdt(input = iso_date_1, format = "'Day': dd, 'Month': MM (yy)") %>% 254 | expect_equal("Day: 04, Month: 07 (18)") 255 | 256 | fdt(input = iso_date_1, format = "'dd': dd, 'MM': MM ('yy': yy)") %>% 257 | expect_equal("dd: 04, MM: 07 (yy: 18)") 258 | 259 | fdt(input = iso_date_1, format = "'d': dd, 'M': MM ('y': yy)") %>% 260 | expect_equal("d: 04, M: 07 (y: 18)") 261 | 262 | fdt(input = iso_date_1, format = "h 'h' mm 'min' ss 's' B", locale = "fr_CA") %>% 263 | expect_equal("12 h 00 min 00 s minuit") 264 | 265 | fdt(input = iso_date_1, format = "d'th' MMMM y (EEEE).") %>% 266 | expect_equal("4th July 2018 (Wednesday).") 267 | 268 | fdt(input = iso_date_1, format = "'T:' yyyy-MM-dd") %>% 269 | expect_equal("T: 2018-07-04") 270 | 271 | fdt(input = iso_date_1, format = "'T: 'yyyy-MM-dd") %>% 272 | expect_equal("T: 2018-07-04") 273 | 274 | fdt(input = iso_date_1, format = "'' 'T: 'yyyy-MM-dd") %>% 275 | expect_equal("' T: 2018-07-04") 276 | 277 | fdt(input = iso_date_1, format = "'''''''H' 'T: 'yyyy-MM-dd''''''") %>% 278 | expect_equal("'''H T: 2018-07-04'''") 279 | }) 280 | 281 | test_that("ISO datetimes can be parsed from a string and formatted", { 282 | 283 | iso_datetime_1 <- "2018-07-04T22:05" 284 | iso_datetime_2 <- "2018-07-04 22:05" 285 | iso_datetime_3 <- "2018-07-04 22:05:23" 286 | iso_datetime_4 <- "2018-07-04 22:05:00" 287 | iso_datetime_5 <- "2018-10-04T22:05" 288 | iso_datetime_6 <- "2018-10-04 22:05" 289 | iso_datetime_7 <- "2018-10-04 22:05:23" 290 | iso_datetime_8 <- "2018-10-04 22:05:00" 291 | iso_datetime_9 <- "2018-01-04T22:05" 292 | iso_datetime_10 <- "2018-01-04 22:05" 293 | iso_datetime_11 <- "2018-01-04 22:05:23" 294 | iso_datetime_12 <- "2018-01-04 22:05:00" 295 | iso_datetime_13 <- "2018-01-13T22:05" 296 | iso_datetime_14 <- "2018-01-13 22:05" 297 | iso_datetime_15 <- "2018-01-13 22:05:23" 298 | iso_datetime_16 <- "2018-01-13 22:05:00" 299 | iso_datetime_17 <- "2019-01-15 20:15:24.45678" 300 | iso_datetime_18 <- "2019-01-15 20:15:24.00160" 301 | iso_datetime_19 <- "2019-01-15 00:00:00" 302 | 303 | # 304 | # Checks with `y`/`yy`, `M`/`MM`/`MMM`/`MMMM`/`MMMMM`, `d`/`dd` 305 | # 306 | 307 | fdt(input = iso_datetime_1, format = "y/M/d") %>% 308 | expect_equal("2018/7/4") 309 | fdt(input = iso_datetime_1, format = "yy-MM-dd") %>% 310 | expect_equal("18-07-04") 311 | fdt(input = iso_datetime_1, format = "y-MMM-dd") %>% 312 | expect_equal("2018-Jul-04") 313 | fdt(input = iso_datetime_1, format = "y-MMMM-dd") %>% 314 | expect_equal("2018-July-04") 315 | fdt(input = iso_datetime_1, format = "y-MMMMM-dd") %>% 316 | expect_equal("2018-J-04") 317 | fdt(input = iso_datetime_2, format = "y/M/d") %>% 318 | expect_equal("2018/7/4") 319 | fdt(input = iso_datetime_2, format = "yy-MM-dd") %>% 320 | expect_equal("18-07-04") 321 | fdt(input = iso_datetime_2, format = "y-MMM-dd") %>% 322 | expect_equal("2018-Jul-04") 323 | fdt(input = iso_datetime_2, format = "y-MMMM-dd") %>% 324 | expect_equal("2018-July-04") 325 | fdt(input = iso_datetime_2, format = "y-MMMMM-dd") %>% 326 | expect_equal("2018-J-04") 327 | fdt(input = iso_datetime_3, format = "y/M/d") %>% 328 | expect_equal("2018/7/4") 329 | fdt(input = iso_datetime_3, format = "yy-MM-dd") %>% 330 | expect_equal("18-07-04") 331 | fdt(input = iso_datetime_3, format = "y-MMM-dd") %>% 332 | expect_equal("2018-Jul-04") 333 | fdt(input = iso_datetime_3, format = "y-MMMM-dd") %>% 334 | expect_equal("2018-July-04") 335 | fdt(input = iso_datetime_3, format = "y-MMMMM-dd") %>% 336 | expect_equal("2018-J-04") 337 | fdt(input = iso_datetime_4, format = "y/M/d") %>% 338 | expect_equal("2018/7/4") 339 | fdt(input = iso_datetime_4, format = "yy-MM-dd") %>% 340 | expect_equal("18-07-04") 341 | fdt(input = iso_datetime_4, format = "y-MMM-dd") %>% 342 | expect_equal("2018-Jul-04") 343 | fdt(input = iso_datetime_4, format = "y-MMMM-dd") %>% 344 | expect_equal("2018-July-04") 345 | fdt(input = iso_datetime_4, format = "y-MMMMM-dd") %>% 346 | expect_equal("2018-J-04") 347 | 348 | # 349 | # Checks with `L` 350 | # 351 | 352 | fdt(input = iso_datetime_1, format = "y-L-dd") %>% 353 | expect_equal("2018-7-04") 354 | fdt(input = iso_datetime_2, format = "y-L-dd") %>% 355 | expect_equal("2018-7-04") 356 | fdt(input = iso_datetime_3, format = "y-L-dd") %>% 357 | expect_equal("2018-7-04") 358 | fdt(input = iso_datetime_4, format = "y-L-dd") %>% 359 | expect_equal("2018-7-04") 360 | fdt(input = iso_datetime_5, format = "y-L-dd") %>% 361 | expect_equal("2018-10-04") 362 | fdt(input = iso_datetime_6, format = "y-L-dd") %>% 363 | expect_equal("2018-10-04") 364 | fdt(input = iso_datetime_7, format = "y-L-dd") %>% 365 | expect_equal("2018-10-04") 366 | fdt(input = iso_datetime_8, format = "y-L-dd") %>% 367 | expect_equal("2018-10-04") 368 | 369 | # 370 | # Checks with `LL` 371 | # 372 | 373 | fdt(input = iso_datetime_1, format = "y-LL-dd") %>% 374 | expect_equal("2018-07-04") 375 | fdt(input = iso_datetime_2, format = "y-LL-dd") %>% 376 | expect_equal("2018-07-04") 377 | fdt(input = iso_datetime_3, format = "y-LL-dd") %>% 378 | expect_equal("2018-07-04") 379 | fdt(input = iso_datetime_4, format = "y-LL-dd") %>% 380 | expect_equal("2018-07-04") 381 | fdt(input = iso_datetime_5, format = "y-LL-dd") %>% 382 | expect_equal("2018-10-04") 383 | fdt(input = iso_datetime_6, format = "y-LL-dd") %>% 384 | expect_equal("2018-10-04") 385 | fdt(input = iso_datetime_7, format = "y-LL-dd") %>% 386 | expect_equal("2018-10-04") 387 | fdt(input = iso_datetime_8, format = "y-LL-dd") %>% 388 | expect_equal("2018-10-04") 389 | 390 | # 391 | # Checks with `D` 392 | # 393 | 394 | fdt(input = iso_datetime_1, format = "y/M/D") %>% 395 | expect_equal("2018/7/185") 396 | fdt(input = iso_datetime_2, format = "y/M/D") %>% 397 | expect_equal("2018/7/185") 398 | fdt(input = iso_datetime_3, format = "y/M/D") %>% 399 | expect_equal("2018/7/185") 400 | fdt(input = iso_datetime_4, format = "y/M/D") %>% 401 | expect_equal("2018/7/185") 402 | fdt(input = iso_datetime_5, format = "y/M/D") %>% 403 | expect_equal("2018/10/277") 404 | fdt(input = iso_datetime_6, format = "y/M/D") %>% 405 | expect_equal("2018/10/277") 406 | fdt(input = iso_datetime_7, format = "y/M/D") %>% 407 | expect_equal("2018/10/277") 408 | fdt(input = iso_datetime_8, format = "y/M/D") %>% 409 | expect_equal("2018/10/277") 410 | fdt(input = iso_datetime_9, format = "y/M/D") %>% 411 | expect_equal("2018/1/4") 412 | fdt(input = iso_datetime_10, format = "y/M/D") %>% 413 | expect_equal("2018/1/4") 414 | fdt(input = iso_datetime_11, format = "y/M/D") %>% 415 | expect_equal("2018/1/4") 416 | fdt(input = iso_datetime_12, format = "y/M/D") %>% 417 | expect_equal("2018/1/4") 418 | fdt(input = iso_datetime_13, format = "y/M/D") %>% 419 | expect_equal("2018/1/13") 420 | fdt(input = iso_datetime_14, format = "y/M/D") %>% 421 | expect_equal("2018/1/13") 422 | fdt(input = iso_datetime_15, format = "y/M/D") %>% 423 | expect_equal("2018/1/13") 424 | fdt(input = iso_datetime_16, format = "y/M/D") %>% 425 | expect_equal("2018/1/13") 426 | 427 | # 428 | # Checks with `DD` 429 | # 430 | 431 | fdt(input = iso_datetime_1, format = "y/M/DD") %>% 432 | expect_equal("2018/7/185") 433 | fdt(input = iso_datetime_2, format = "y/M/DD") %>% 434 | expect_equal("2018/7/185") 435 | fdt(input = iso_datetime_3, format = "y/M/DD") %>% 436 | expect_equal("2018/7/185") 437 | fdt(input = iso_datetime_4, format = "y/M/DD") %>% 438 | expect_equal("2018/7/185") 439 | fdt(input = iso_datetime_5, format = "y/M/DD") %>% 440 | expect_equal("2018/10/277") 441 | fdt(input = iso_datetime_6, format = "y/M/DD") %>% 442 | expect_equal("2018/10/277") 443 | fdt(input = iso_datetime_7, format = "y/M/DD") %>% 444 | expect_equal("2018/10/277") 445 | fdt(input = iso_datetime_8, format = "y/M/DD") %>% 446 | expect_equal("2018/10/277") 447 | fdt(input = iso_datetime_9, format = "y/M/DD") %>% 448 | expect_equal("2018/1/04") 449 | fdt(input = iso_datetime_10, format = "y/M/DD") %>% 450 | expect_equal("2018/1/04") 451 | fdt(input = iso_datetime_11, format = "y/M/DD") %>% 452 | expect_equal("2018/1/04") 453 | fdt(input = iso_datetime_12, format = "y/M/DD") %>% 454 | expect_equal("2018/1/04") 455 | fdt(input = iso_datetime_13, format = "y/M/DD") %>% 456 | expect_equal("2018/1/13") 457 | fdt(input = iso_datetime_14, format = "y/M/DD") %>% 458 | expect_equal("2018/1/13") 459 | fdt(input = iso_datetime_15, format = "y/M/DD") %>% 460 | expect_equal("2018/1/13") 461 | fdt(input = iso_datetime_16, format = "y/M/DD") %>% 462 | expect_equal("2018/1/13") 463 | 464 | # 465 | # Checks with `DDD` 466 | # 467 | 468 | fdt(input = iso_datetime_1, format = "y/M/DDD") %>% 469 | expect_equal("2018/7/185") 470 | fdt(input = iso_datetime_2, format = "y/M/DDD") %>% 471 | expect_equal("2018/7/185") 472 | fdt(input = iso_datetime_3, format = "y/M/DDD") %>% 473 | expect_equal("2018/7/185") 474 | fdt(input = iso_datetime_4, format = "y/M/DDD") %>% 475 | expect_equal("2018/7/185") 476 | fdt(input = iso_datetime_5, format = "y/M/DDD") %>% 477 | expect_equal("2018/10/277") 478 | fdt(input = iso_datetime_6, format = "y/M/DDD") %>% 479 | expect_equal("2018/10/277") 480 | fdt(input = iso_datetime_7, format = "y/M/DDD") %>% 481 | expect_equal("2018/10/277") 482 | fdt(input = iso_datetime_8, format = "y/M/DDD") %>% 483 | expect_equal("2018/10/277") 484 | fdt(input = iso_datetime_9, format = "y/M/DDD") %>% 485 | expect_equal("2018/1/004") 486 | fdt(input = iso_datetime_10, format = "y/M/DDD") %>% 487 | expect_equal("2018/1/004") 488 | fdt(input = iso_datetime_11, format = "y/M/DDD") %>% 489 | expect_equal("2018/1/004") 490 | fdt(input = iso_datetime_12, format = "y/M/DDD") %>% 491 | expect_equal("2018/1/004") 492 | fdt(input = iso_datetime_13, format = "y/M/DDD") %>% 493 | expect_equal("2018/1/013") 494 | fdt(input = iso_datetime_14, format = "y/M/DDD") %>% 495 | expect_equal("2018/1/013") 496 | fdt(input = iso_datetime_15, format = "y/M/DDD") %>% 497 | expect_equal("2018/1/013") 498 | fdt(input = iso_datetime_16, format = "y/M/DDD") %>% 499 | expect_equal("2018/1/013") 500 | 501 | # 502 | # Checks with 'S+' 503 | # 504 | 505 | fdt(input = iso_datetime_16, format = "y/MM/DD HH:mm:ss.S") %>% 506 | expect_equal("2018/01/13 22:05:00.0") 507 | fdt(input = iso_datetime_16, format = "y/MM/DD HH:mm:ss.SS") %>% 508 | expect_equal("2018/01/13 22:05:00.00") 509 | fdt(input = iso_datetime_16, format = "y/MM/DD HH:mm:ss.SSS") %>% 510 | expect_equal("2018/01/13 22:05:00.000") 511 | 512 | fdt(input = iso_datetime_17, format = "y/MM/DD HH:mm:ss.S") %>% 513 | expect_equal("2019/01/15 20:15:24.4") 514 | fdt(input = iso_datetime_17, format = "y/MM/DD HH:mm:ss.SS") %>% 515 | expect_equal("2019/01/15 20:15:24.45") 516 | fdt(input = iso_datetime_17, format = "y/MM/DD HH:mm:ss.SSS") %>% 517 | expect_equal("2019/01/15 20:15:24.456") 518 | fdt(input = iso_datetime_17, format = "y/MM/DD HH:mm:ss.SSSS") %>% 519 | expect_equal("2019/01/15 20:15:24.4560") 520 | fdt(input = iso_datetime_17, format = "y/MM/DD HH:mm:ss.SSSSS") %>% 521 | expect_equal("2019/01/15 20:15:24.45600") 522 | fdt(input = iso_datetime_17, format = "y/MM/DD HH:mm:ss.SSSSSS") %>% 523 | expect_equal("2019/01/15 20:15:24.456000") 524 | fdt(input = iso_datetime_17, format = "y/MM/DD HH:mm:ss.SSSSSSS") %>% 525 | expect_equal("2019/01/15 20:15:24.4560000") 526 | fdt(input = iso_datetime_17, format = "y/MM/DD HH:mm:ss.SSSSSSSS") %>% 527 | expect_equal("2019/01/15 20:15:24.45600000") 528 | fdt(input = iso_datetime_17, format = "y/MM/DD HH:mm:ss.SSSSSSSSS") %>% 529 | expect_equal("2019/01/15 20:15:24.456000000") 530 | 531 | fdt(input = iso_datetime_18, format = "y/MM/DD HH:mm:ss.S") %>% 532 | expect_equal("2019/01/15 20:15:24.0") 533 | fdt(input = iso_datetime_18, format = "y/MM/DD HH:mm:ss.SS") %>% 534 | expect_equal("2019/01/15 20:15:24.00") 535 | fdt(input = iso_datetime_18, format = "y/MM/DD HH:mm:ss.SSS") %>% 536 | expect_equal("2019/01/15 20:15:24.001") 537 | fdt(input = iso_datetime_18, format = "y/MM/DD HH:mm:ss.SSSS") %>% 538 | expect_equal("2019/01/15 20:15:24.0010") 539 | fdt(input = iso_datetime_18, format = "y/MM/DD HH:mm:ss.SSSSS") %>% 540 | expect_equal("2019/01/15 20:15:24.00100") 541 | fdt(input = iso_datetime_18, format = "y/MM/DD HH:mm:ss.SSSSSS") %>% 542 | expect_equal("2019/01/15 20:15:24.001000") 543 | fdt(input = iso_datetime_18, format = "y/MM/DD HH:mm:ss.SSSSSSS") %>% 544 | expect_equal("2019/01/15 20:15:24.0010000") 545 | fdt(input = iso_datetime_18, format = "y/MM/DD HH:mm:ss.SSSSSSSS") %>% 546 | expect_equal("2019/01/15 20:15:24.00100000") 547 | fdt(input = iso_datetime_18, format = "y/MM/DD HH:mm:ss.SSSSSSSSS") %>% 548 | expect_equal("2019/01/15 20:15:24.001000000") 549 | 550 | # 551 | # Checks with 'A+' 552 | # 553 | 554 | fdt(input = iso_datetime_18, format = "y/MM/DD HH:mm:ss (A)") %>% 555 | expect_equal("2019/01/15 20:15:24 (72924001)") 556 | fdt(input = iso_datetime_18, format = "y/MM/DD HH:mm:ss (AA)") %>% 557 | expect_equal("2019/01/15 20:15:24 (72924001)") 558 | fdt(input = iso_datetime_18, format = "y/MM/DD HH:mm:ss (AAA)") %>% 559 | expect_equal("2019/01/15 20:15:24 (72924001)") 560 | fdt(input = iso_datetime_18, format = "y/MM/DD HH:mm:ss (AAAA)") %>% 561 | expect_equal("2019/01/15 20:15:24 (72924001)") 562 | fdt(input = iso_datetime_18, format = "y/MM/DD HH:mm:ss (AAAAA)") %>% 563 | expect_equal("2019/01/15 20:15:24 (72924001)") 564 | fdt(input = iso_datetime_18, format = "y/MM/DD HH:mm:ss (AAAAAA)") %>% 565 | expect_equal("2019/01/15 20:15:24 (72924001)") 566 | fdt(input = iso_datetime_18, format = "y/MM/DD HH:mm:ss (AAAAAAA)") %>% 567 | expect_equal("2019/01/15 20:15:24 (72924001)") 568 | fdt(input = iso_datetime_18, format = "y/MM/DD HH:mm:ss (AAAAAAAA)") %>% 569 | expect_equal("2019/01/15 20:15:24 (72924001)") 570 | fdt(input = iso_datetime_18, format = "y/MM/DD HH:mm:ss (AAAAAAAAA)") %>% 571 | expect_equal("2019/01/15 20:15:24 (072924001)") 572 | 573 | fdt(input = iso_datetime_19, format = "y/MM/DD HH:mm:ss (A)") %>% 574 | expect_equal("2019/01/15 00:00:00 (0)") 575 | fdt(input = iso_datetime_19, format = "y/MM/DD HH:mm:ss (AA)") %>% 576 | expect_equal("2019/01/15 00:00:00 (00)") 577 | fdt(input = iso_datetime_19, format = "y/MM/DD HH:mm:ss (AAA)") %>% 578 | expect_equal("2019/01/15 00:00:00 (000)") 579 | fdt(input = iso_datetime_19, format = "y/MM/DD HH:mm:ss (AAAA)") %>% 580 | expect_equal("2019/01/15 00:00:00 (0000)") 581 | fdt(input = iso_datetime_19, format = "y/MM/DD HH:mm:ss (AAAAA)") %>% 582 | expect_equal("2019/01/15 00:00:00 (00000)") 583 | fdt(input = iso_datetime_19, format = "y/MM/DD HH:mm:ss (AAAAAA)") %>% 584 | expect_equal("2019/01/15 00:00:00 (000000)") 585 | fdt(input = iso_datetime_19, format = "y/MM/DD HH:mm:ss (AAAAAAA)") %>% 586 | expect_equal("2019/01/15 00:00:00 (0000000)") 587 | fdt(input = iso_datetime_19, format = "y/MM/DD HH:mm:ss (AAAAAAAA)") %>% 588 | expect_equal("2019/01/15 00:00:00 (00000000)") 589 | fdt(input = iso_datetime_19, format = "y/MM/DD HH:mm:ss (AAAAAAAAA)") %>% 590 | expect_equal("2019/01/15 00:00:00 (000000000)") 591 | }) 592 | -------------------------------------------------------------------------------- /tests/testthat/test-dt_replace.R: -------------------------------------------------------------------------------- 1 | # Just testing internal functions to show examples of how they work 2 | test_that("dt_replace() works", { 3 | skip_on_cran() # not sure how robust this is. 4 | result <- dt_replace( 5 | dt = "{yyyy}-{MM}-{dd}'1'{HH}:{mm}:{ss}{XXX}", 6 | input_dt = as.POSIXct("2015-06-28 20:49:46", tz = "UTC"), 7 | dt_lett = c("y", "M", "d", "H", "m", "s", "X"), 8 | locale = "en", 9 | tz_info = list( 10 | tz_str = "GMT", 11 | tz_offset = 0L, 12 | long_tzid = "UTC", 13 | tz_short_specific = NA_character_, 14 | tz_long_specific = NA_character_ 15 | ) 16 | ) 17 | expect_equal( 18 | result, 19 | "2015-06-28'1'20:49:46Z" 20 | ) 21 | }) 22 | 23 | test_that("The dt_* helpers work as expected (#16)", { 24 | expect_equal(dt_year[["{y}"]](2016), "2016") 25 | 26 | }) 27 | -------------------------------------------------------------------------------- /tests/testthat/test-fdf_ftf.R: -------------------------------------------------------------------------------- 1 | test_that("Vectors can be obtained from the `*_vec()` functions", { 2 | 3 | expect_vector(flex_d_vec()) 4 | expect_vector(flex_t12_vec()) 5 | expect_vector(flex_t24_vec()) 6 | 7 | expect_type(flex_d_vec(), "character") 8 | expect_type(flex_t12_vec(), "character") 9 | expect_type(flex_t24_vec(), "character") 10 | 11 | expect_length(flex_d_vec(), 26) 12 | expect_length(flex_t12_vec(), 12) 13 | expect_length(flex_t24_vec(), 8) 14 | }) 15 | 16 | test_that("The `*_list` objects have a particular structure", { 17 | 18 | expect_type(flex_d_lst, "list") 19 | expect_type(flex_t12_lst, "list") 20 | expect_type(flex_t24_lst, "list") 21 | 22 | expect_named(flex_d_lst, flex_d_vec()) 23 | expect_named(flex_t12_lst, flex_t12_vec()) 24 | expect_named(flex_t24_lst, flex_t24_vec()) 25 | 26 | expect_equal(unlist(flex_d_lst, use.names = FALSE), flex_d_vec()) 27 | expect_equal(unlist(flex_t12_lst, use.names = FALSE), flex_t12_vec()) 28 | expect_equal(unlist(flex_t24_lst, use.names = FALSE), flex_t24_vec()) 29 | 30 | lapply( 31 | flex_d_lst, FUN = function(x) { 32 | expect_s3_class(x, c("date_time_pattern", "flex_d")) 33 | expect_type(x, "character") 34 | } 35 | ) 36 | 37 | lapply( 38 | flex_t12_lst, FUN = function(x) { 39 | expect_s3_class(x, c("date_time_pattern", "flex_t12")) 40 | expect_type(x, "character") 41 | } 42 | ) 43 | 44 | lapply( 45 | flex_t24_lst, FUN = function(x) { 46 | expect_s3_class(x, c("date_time_pattern", "flex_t24")) 47 | expect_type(x, "character") 48 | } 49 | ) 50 | }) 51 | 52 | test_that("`flex_*_lst` can be used in `fdt()`", { 53 | 54 | expect_equal( 55 | fdt( 56 | input = "2018-07-04 22:05", 57 | format = flex_d_lst$yMd 58 | ), 59 | "7/4/2018" 60 | ) 61 | expect_equal( 62 | fdt( 63 | input = "2018-07-04 22:05", 64 | format = flex_d_lst[[1]] 65 | ), 66 | "7/4/2018" 67 | ) 68 | expect_equal( 69 | fdt( 70 | input = "2018-07-04 22:05", 71 | format = flex_d_lst[["yMd"]] 72 | ), 73 | "7/4/2018" 74 | ) 75 | 76 | expect_equal( 77 | vapply( 78 | flex_d_vec(), 79 | FUN.VALUE = character(1), 80 | USE.NAMES = FALSE, 81 | FUN = function(x) { 82 | fdt( 83 | input = "2018-07-04 22:05", 84 | format = flex_d_lst[[x]] 85 | ) 86 | } 87 | ), 88 | c( 89 | "7/4/2018", "Wed, 7/4/2018", "Jul 2018", "July 2018", "Jul 4, 2018", 90 | "Wed, Jul 4, 2018", "7/4/2018 A", "Jul 4, 2018 AD", "Wed, Jul 4, 2018 AD", 91 | "7/2018", "7/4", "Wed, 7/4", "Jul 4", "Wed, Jul 4", "July 4", 92 | "Jul 2018 AD", "Q3 2018", "3rd quarter 2018", "2018 AD", "2018", 93 | "7", "Jul", "4", "4 Wed", "week 1 of July", "week 27 of 2018" 94 | ) 95 | ) 96 | 97 | expect_equal( 98 | vapply( 99 | flex_d_vec(), 100 | FUN.VALUE = character(1), 101 | USE.NAMES = FALSE, 102 | FUN = function(x) { 103 | fdt( 104 | input = "2014-01-03 08:09:06 (America/New_York)", 105 | format = flex_d_lst[[x]] 106 | ) 107 | } 108 | ), 109 | c( 110 | "1/3/2014", "Fri, 1/3/2014", "Jan 2014", "January 2014", "Jan 3, 2014", 111 | "Fri, Jan 3, 2014", "1/3/2014 A", "Jan 3, 2014 AD", "Fri, Jan 3, 2014 AD", 112 | "1/2014", "1/3", "Fri, 1/3", "Jan 3", "Fri, Jan 3", "January 3", 113 | "Jan 2014 AD", "Q1 2014", "1st quarter 2014", "2014 AD", "2014", 114 | "1", "Jan", "3", "3 Fri", "week 1 of January", "week 1 of 2014" 115 | ) 116 | ) 117 | 118 | select_locales <- c("pt", "fr", "en") 119 | 120 | for (i in seq_along(select_locales)) { 121 | for (j in seq_along(flex_d_vec())) { 122 | 123 | fmt_val_d <- 124 | fdt( 125 | input = "2018-07-04 22:05", 126 | format = flex_d_lst[[j]], 127 | locale = select_locales[i] 128 | ) 129 | 130 | expect_vector(fmt_val_d) 131 | expect_length(fmt_val_d, 1) 132 | expect_type(fmt_val_d, "character") 133 | } 134 | } 135 | 136 | for (i in seq_along(select_locales)) { 137 | for (j in seq_along(flex_t12_vec())) { 138 | 139 | fmt_val_t12 <- 140 | fdt( 141 | input = "2018-07-04 22:05", 142 | format = flex_t12_lst[[j]], 143 | locale = select_locales[i] 144 | ) 145 | 146 | expect_vector(fmt_val_t12) 147 | expect_length(fmt_val_t12, 1) 148 | expect_type(fmt_val_t12, "character") 149 | } 150 | } 151 | 152 | for (i in seq_along(select_locales)) { 153 | for (j in seq_along(flex_t24_vec())) { 154 | 155 | fmt_val_t24 <- 156 | fdt( 157 | input = "2018-07-04 22:05", 158 | format = flex_t24_lst[[j]], 159 | locale = select_locales[i] 160 | ) 161 | 162 | expect_vector(fmt_val_t24) 163 | expect_length(fmt_val_t24, 1) 164 | expect_type(fmt_val_t24, "character") 165 | } 166 | } 167 | 168 | expect_equal( 169 | vapply( 170 | flex_d_vec(), 171 | FUN.VALUE = character(1), 172 | USE.NAMES = FALSE, 173 | FUN = function(x) { 174 | fdt( 175 | input = "2018-07-04 22:05", 176 | format = list(flex_d_lst[[x]], flex_t12_lst[[1]]) 177 | ) 178 | } 179 | ), 180 | c( 181 | "7/4/2018, 10:05:00 PM", "Wed, 7/4/2018, 10:05:00 PM", 182 | "Jul 2018, 10:05:00 PM", "July 2018, 10:05:00 PM", 183 | "Jul 4, 2018, 10:05:00 PM", "Wed, Jul 4, 2018, 10:05:00 PM", 184 | "7/4/2018 A, 10:05:00 PM", "Jul 4, 2018 AD, 10:05:00 PM", 185 | "Wed, Jul 4, 2018 AD, 10:05:00 PM", "7/2018, 10:05:00 PM", 186 | "7/4, 10:05:00 PM", "Wed, 7/4, 10:05:00 PM", "Jul 4, 10:05:00 PM", 187 | "Wed, Jul 4, 10:05:00 PM", "July 4, 10:05:00 PM", 188 | "Jul 2018 AD, 10:05:00 PM", "Q3 2018, 10:05:00 PM", 189 | "3rd quarter 2018, 10:05:00 PM", "2018 AD, 10:05:00 PM", 190 | "2018, 10:05:00 PM", "7, 10:05:00 PM", "Jul, 10:05:00 PM", 191 | "4, 10:05:00 PM", "4 Wed, 10:05:00 PM", "week 1 of July, 10:05:00 PM", 192 | "week 27 of 2018, 10:05:00 PM" 193 | ) 194 | ) 195 | }) 196 | 197 | test_that("fdt() works in all contexts", { 198 | expect_equal( 199 | fdt("2024-03-01", format = "GyMMMEd", use_tz = "America/Toronto"), 200 | "AD2024MarFri1" 201 | ) 202 | # Copy of a test in gt 203 | expect_equal( 204 | fdt(c( 205 | "1970-01-01 15:35:00", "1970-01-01 16:36:00", "1970-01-01 17:37:00", 206 | "1970-01-01 18:38:00", "1970-01-01 19:39:00" 207 | ), 208 | format = "E h:m:ss B" 209 | ), 210 | c( 211 | "Thu 3:35:00 in the afternoon", "Thu 4:36:00 in the afternoon", 212 | "Thu 5:37:00 in the afternoon", "Thu 6:38:00 in the evening", 213 | "Thu 7:39:00 in the evening" 214 | ) 215 | ) 216 | 217 | # Copy of a test in gt 218 | expect_equal( 219 | fdt(c( 220 | "1970-01-01 15:35:00", "1970-01-01 16:36:00", "1970-01-01 17:37:00", 221 | "1970-01-01 18:38:00", "1970-01-01 19:39:00" 222 | ), 223 | format = "'Q'q k:m" 224 | ), 225 | c( 226 | "Q1 16:35", "Q1 17:36", 227 | "Q1 18:37", "Q1 19:38", 228 | "Q1 20:39" 229 | ) 230 | ) 231 | 232 | expect_equal( 233 | fdt(c( 234 | "1970-01-01 15:35:00", "1970-01-01 16:36:00", "1970-01-01 17:37:00", 235 | "1970-01-01 18:38:00", "1970-01-01 19:39:00" 236 | ), 237 | format = "QQQQ k:m" 238 | ), 239 | c( 240 | "1st quarter 16:35", "1st quarter 17:36", 241 | "1st quarter 18:37", "1st quarter 19:38", 242 | "1st quarter 20:39" 243 | ) 244 | ) 245 | }) 246 | 247 | -------------------------------------------------------------------------------- /tests/testthat/test-fdt_locales.R: -------------------------------------------------------------------------------- 1 | test_that("A vector can be obtained from the `fdt_locales_vec()` function", { 2 | 3 | expect_vector(fdt_locales_vec()) 4 | expect_type(fdt_locales_vec(), "character") 5 | expect_length(fdt_locales_vec(), 574) 6 | }) 7 | 8 | test_that("The `fdt_locales_lst` object has a particular structure", { 9 | 10 | expect_type(fdt_locales_lst, "list") 11 | expect_named(fdt_locales_lst, fdt_locales_vec()) 12 | expect_equal(unlist(fdt_locales_lst, use.names = FALSE), fdt_locales_vec()) 13 | }) 14 | 15 | test_that("The `fdt_locales_lst` object can be used with `fdt()`", { 16 | 17 | for (i in seq_along(fdt_locales_vec())) { 18 | 19 | fmt_val <- 20 | fdt( 21 | input = "2018-07-04 22:05", 22 | locale = fdt_locales_lst[[i]] 23 | ) 24 | 25 | expect_vector(fmt_val) 26 | expect_length(fmt_val, 1) 27 | expect_type(fmt_val, "character") 28 | } 29 | }) 30 | -------------------------------------------------------------------------------- /tests/testthat/test-standard_date_time.R: -------------------------------------------------------------------------------- 1 | test_that("The `standard_*()` functions produce very specific objects", { 2 | 3 | expect_vector(standard_date_time()) 4 | expect_type(standard_date_time(), "character") 5 | expect_length(standard_date_time(), 1) 6 | expect_s3_class( 7 | standard_date_time(), 8 | c("date_time_pattern", "standard", "date_time", "short") 9 | ) 10 | expect_s3_class(standard_date_time(type = "medium"), "medium") 11 | expect_s3_class(standard_date_time(type = "long"), "long") 12 | expect_s3_class(standard_date_time(type = "full"), "full") 13 | 14 | expect_vector(standard_date()) 15 | expect_type(standard_date(), "character") 16 | expect_length(standard_date(), 1) 17 | expect_s3_class( 18 | standard_date(), 19 | c("date_time_pattern", "standard", "date", "short") 20 | ) 21 | expect_s3_class(standard_date(type = "medium"), "medium") 22 | expect_s3_class(standard_date(type = "long"), "long") 23 | expect_s3_class(standard_date(type = "full"), "full") 24 | 25 | expect_vector(standard_time()) 26 | expect_type(standard_time(), "character") 27 | expect_length(standard_time(), 1) 28 | expect_s3_class( 29 | standard_time(), 30 | c("date_time_pattern", "standard", "time", "short") 31 | ) 32 | expect_s3_class(standard_time(type = "medium"), "medium") 33 | expect_s3_class(standard_time(type = "long"), "long") 34 | expect_s3_class(standard_time(type = "full"), "full") 35 | }) 36 | 37 | test_that("The `standard_*()` functions can be used in `fdt()`", { 38 | 39 | expect_equal( 40 | vapply( 41 | c("short", "medium", "long", "full"), 42 | FUN.VALUE = character(1), 43 | USE.NAMES = FALSE, 44 | FUN = function(x) { 45 | fdt( 46 | input = "2018-07-04 22:05 (America/Los_Angeles)", 47 | format = standard_date_time(type = x) 48 | ) 49 | } 50 | ), 51 | c( 52 | "7/4/18, 10:05 PM", 53 | "Jul 4, 2018, 10:05:00 PM", 54 | "July 4, 2018 at 10:05:00 PM PDT", 55 | "Wednesday, July 4, 2018 at 10:05:00 PM Pacific Daylight Time" 56 | ) 57 | ) 58 | 59 | expect_equal( 60 | vapply( 61 | c("short", "medium", "long", "full"), 62 | FUN.VALUE = character(1), 63 | USE.NAMES = FALSE, 64 | FUN = function(x) { 65 | fdt( 66 | input = "2018-07-04 22:05 (America/Los_Angeles)", 67 | format = standard_date(type = x) 68 | ) 69 | } 70 | ), 71 | c( 72 | "7/4/18", 73 | "Jul 4, 2018", 74 | "July 4, 2018", 75 | "Wednesday, July 4, 2018" 76 | ) 77 | ) 78 | 79 | expect_equal( 80 | vapply( 81 | c("short", "medium", "long", "full"), 82 | FUN.VALUE = character(1), 83 | USE.NAMES = FALSE, 84 | FUN = function(x) { 85 | fdt( 86 | input = "2018-07-04 22:05 (America/Los_Angeles)", 87 | format = standard_time(type = x) 88 | ) 89 | } 90 | ), 91 | c( 92 | "10:05 PM", 93 | "10:05:00 PM", 94 | "10:05:00 PM PDT", 95 | "10:05:00 PM Pacific Daylight Time" 96 | ) 97 | ) 98 | 99 | select_locales <- c("pt", "fr", "en") 100 | 101 | for (i in seq_along(select_locales)) { 102 | for (j in c("short", "medium", "long", "full")) { 103 | 104 | fmt_val_dt <- 105 | fdt( 106 | input = "2018-07-04 22:05 (America/Los_Angeles)", 107 | format = standard_time(type = j), 108 | locale = select_locales[i] 109 | ) 110 | 111 | expect_vector(fmt_val_dt) 112 | expect_length(fmt_val_dt, 1) 113 | expect_type(fmt_val_dt, "character") 114 | } 115 | } 116 | }) 117 | --------------------------------------------------------------------------------