MyNixOS website logo
Description

Partial Datetime Handling.

Datetimes and timestamps are invariably an imprecise notation, with any partial representation implying some amount of uncertainty. To handle this, 'parttime' provides classes for embedding partial missingness as a central part of its datetime classes. This central feature allows for more ergonomic use of datetimes for challenging datetime computation, including calculations of overlapping date ranges, imputations, and more thoughtful handling of ambiguity that arises from uncertain time zones. This package was developed first and foremost with pharmaceutical applications in mind, but aims to be agnostic to application to accommodate general use cases just as conveniently.

parttime

CRAN status R-CMD-check Coverage

A package for a partial datetime class and generics

Installation

devtools::install_github("dgkf/parttime")

Quick Start

The parttime package aims to make uncertainty in datetimes a central feature by offering the partial_time datetime class.

This includes:

  • parsing of a wider range of datetime string formats
  • internal representations that captures date component missingness
  • overloading of operators for comparison
  • mechanisms for resolving datetime uncertainty
  • imputation

Overview

partial_times can be parsed from strings. Any missing data is not immediately imputed with a known date. Instead, its uncertainty is preserved as a central part of the partial_time class.

pttms <- as.parttime(c("2023", "2023-02"))

We can access the components of each datetime as though the partial_time is a matrix of datetime fields, or using lubridate-style accessors and assignment functions.

pttms[, "year"]
##    2023 2023-02 
##    2023    2023

pttms[[1, "year"]]
## [1] 2023

year(pttms)  # the first row are names of elements in a named numeric vector
##    2023 2023-02 
##    2023    2023

year(pttms[1])
## [1] 2023

month(pttms[2]) <- 3
pttms
## <partial_time<YMDhms+tz>[2]> 
## [1] "2023"    "2023-03"

month(pttms[1]) <- 3
pttms
## <partial_time<YMDhms+tz>[2]> 
## [1] "2023-03" "2023-03"

month(pttms) <- NA
pttms
## <partial_time<YMDhms+tz>[2]> 
## [1] "2023" "2023"

Because partial_time objects may have uncertainty, comparison between times conveys this uncertainty. As a brief example, if we compare our dates from above we see that it is unclear whether one is greater-than the other.

pttms <- as.parttime(c("2023", "2023-02"))
pttms[1] > pttms[2]
## [1] NA

pttms[2] > pttms[1]
## [1] NA

This is because "2022" could be any date within the calendar year (and even outside the calendar year if the timezone is unknown!, see below). In this sense, there are two other modes of comparison - to determine whether a partial_timepossibly or definitely satisfies a criteria.

definitely(pttms[1] > pttms[2])
## [1] FALSE

possibly(pttms[2] > pttms[1])
## [1] TRUE

As well, a few helper functions are provided to perform imputation. All imputation functions are wrappers around impute_time with varying defaults for default timestamp and resolution to which imputation is performed.

impute_date_max(pttms[2])  # resolve date fields with maximum value
## <partial_time<YMDhms+tz>[1]> 
## [1] "2023-02-28"

impute_time(pttms[1], "1999-06-05T04:03:02")  # arbitrary imputation
## <partial_time<YMDhms+tz>[1]> 
## [1] "2023-06-05 04:03:02"

The partial_time class

partial_times are like any other time, but may include NAs for some of their fields. For example, "1999" only tells us information about a year, the month, day, hour, etc. are still unknown. partial_times should be used for situations when a specific point in time is intended, but exactly when it occurred is unknown.

The timespan class

Similarly, a timespan class is offered, which is meant to represent a range of times, denoted by a starting and ending partial_time. Timespans might represent a range from the start to the end of a day, like a partial_time, but can also represent ranges where the start and end are partial times with different resolution.

Examples

Parsing Incomplete Timestamps

Parse ISO8601 timestampes using the parsedate package’s parser, but retains information about missingness in the timestamp format.

iso8601_dates <- c(
  NA,
  "2001",
  "2002-01-01",
  "2004-245", # yearday
  "2005-W13",  # yearweek
  "2006-W02-5",  # yearweek + weekday
  "2007-10-01T08",
  "2008-09-20T08:35",
  "2009-08-12T08:35.048",  # fractional minute
  "2010-07-22T08:35:32",
  "2011-06-13T08:35:32.123",  # fractional second
  "2012-05-23T08:35:32.123Z",  # Zulu time
  "2013-04-14T08:35:32.123+05",  # time offset from GMT
  "2014-03-24T08:35:32.123+05:30",  # time offset with min from GMT
  "20150101T083532.123+0530"  # condensed form
)

as.parttime(iso8601_dates)
## Warning in warn_repr_data_loss(x, includes = "week", excludes = "weekday"): Date strings including week and excluding weekday can not be fully
## represented. To avoid loss of datetime resolution, such partial dates
## are best represented as timespans. See `?timespan`.
## <partial_time<YMDhms+tz>[15]> 
##  [1] NA                              "2001"                         
##  [3] "2002-01-01"                    "2004-09-01"                   
##  [5] "2005"                          "2006-01-12"                   
##  [7] "2007-10-01 08"                 "2008-09-20 08:35"             
##  [9] "2009-08-12 08:35:02.880"       "2010-07-22 08:35:32"          
## [11] "2011-06-13 08:35:32.123"       "2012-05-23 08:35:32.123"      
## [13] "2013-04-14 08:35:32.123+05:00" "2014-03-24 08:35:32.123+05:30"
## [15] "2015-01-01 08:35:32.123+05:30"

Imputing Timestamps

impute_time("2019", "2000-01-02T03:04:05.006+07:30")
## <partial_time<YMDhms+tz>[1]> 
## [1] "2019-01-02 03:04:05.006"

Partial Datetime Comparisons

Partial timestamps include uncertainty, which means that there is often uncertainty when comparing between timestamps. To help resolve this uncertainty there are two helper functions, possibly and definitely resolving this uncertainty for when the windows of uncertainty overlap, or equal (to a given resolution).

options(parttime.assume_tz_offset = 0)  # assume GMT
parttime(2019) < parttime(2020)
## [1] TRUE

options(parttime.assume_tz_offset = NA)  # don't assume a timezone
parttime(2019) < parttime(2020)
## [1] NA

possibly(parttime(2019) < parttime(2020))
## [1] TRUE

definitely(parttime(2019) < parttime(2020))
## [1] FALSE

Given uncertainty in timestamps, we can’t be sure these are equal. In this situation, == will return NA.

parttime(2019) == parttime(2019)
## [1] NA

options(parttime.assume_tz_offset = 0)
definitely(parttime(2019) == parttime(2019), by = "year")
## [1] TRUE

options(parttime.assume_tz_offset = NA)
definitely(parttime(2019) == parttime(2019), by = "year")
## [1] FALSE

Timespans

Cast a partial time’s missingness to a range of possible values

as.timespan(parttime(2019))
## <timespan[1]>
## [1] [2019 — 2020)

Tidyverse Compatible vctrs

tibble-style formatting makes it easy to see which components of each partial_time are missing.

library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union

tibble(dates = iso8601_dates) %>%
  mutate(
    parttimes = as.parttime(dates),
    imputed_times = impute_time_min(parttimes)
  )
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `parttimes = as.parttime(dates)`.
## Caused by warning in `warn_repr_data_loss()`:
## ! Date strings including week and excluding weekday can not be fully
## represented. To avoid loss of datetime resolution, such partial dates
## are best represented as timespans. See `?timespan`.
## # A tibble: 15 × 3
##    dates            parttimes                     imputed_times                 
##    <chr>            <pttm>                        <pttm>                        
##  1 <NA>             NA                            NA                            
##  2 2001             2001                          2001-01-01 00:00:00+-12:00    
##  3 2002-01-01       2002-01-01                    2002-01-01 00:00:00+-12:00    
##  4 2004-245         2004-09-01                    2004-09-01 00:00:00+-12:00    
##  5 2005-W13         2005                          2005-01-01 00:00:00+-12:00    
##  6 2006-W02-5       2006-01-12                    2006-01-12 00:00:00+-12:00    
##  7 2007-10-01T08    2007-10-01 08                 2007-10-01 08:00:00+-12:00    
##  8 2008-09-20T08:35 2008-09-20 08:35              2008-09-20 08:35:00+-12:00    
##  9 2009-08-12T08:3… 2009-08-12 08:35:02.880       2009-08-12 08:35:02.880+-12:00
## 10 2010-07-22T08:3… 2010-07-22 08:35:32           2010-07-22 08:35:32+-12:00    
## 11 2011-06-13T08:3… 2011-06-13 08:35:32.123       2011-06-13 08:35:32.123+-12:00
## 12 2012-05-23T08:3… 2012-05-23 08:35:32.123-00:00 2012-05-23 08:35:32.123-00:00 
## 13 2013-04-14T08:3… 2013-04-14 08:35:32.123+05:00 2013-04-14 08:35:32.123+05:00 
## 14 2014-03-24T08:3… 2014-03-24 08:35:32.123+05:30 2014-03-24 08:35:32.123+05:30 
## 15 20150101T083532… 2015-01-01 08:35:32.123+05:30 2015-01-01 08:35:32.123+05:30

Roadmap

Summary

The partial_time class is pretty complete. The timespan and partial_difftime classes are still under construction!

In-development :construction:

statusclassfunction/opdescription
:ballot_box_with_check:partial_timeparttimecreate partial_time
:ballot_box_with_check:partial_timeas.parttimecast to partial_time
:ballot_box_with_check:partial_time>,`<`,`<=`,`>=`comparison operators
:ballot_box_with_check:partial_timepossibly,definitelyuncertainty resolvers
:ballot_box_with_check:partial_time==,!=equivalence operators
:ballot_box_with_check:partial_timemin,max,pmin,pmaxpartial time extremes
:ballot_box_with_check:partial_timeimpute_timeimputing partial time
:ballot_box_with_check:partial_timeto_gmtconvert to gmt timezone
:ballot_box_with_check:partial_timeprintprinting
:ballot_box_with_check:partial_timeformatformat as character
:ballot_box_with_check:partial_time<vctrs>misc vctrs functions
:ballot_box_with_check:partial_time<pillar>misc pillar functions
:black_square_button:partial_difftimedifftimecreate partial_difftime
:black_square_button:partial_difftimeas.difftimecast to partial_difftime
:black_square_button:partial_difftime>,`<`,`<=`,`>=`comparison operators
:black_square_button:partial_difftimepossibly,definitelyuncertainty resolvers
:black_square_button:partial_difftime==,!=equivalence operators
:black_square_button:partial_difftimemin,max,pmin,pmaxpartial difftime extremes
:black_square_button:partial_difftimeprintprinting
:black_square_button:partial_difftimeformatformat as character
:black_square_button:partial_difftime<vctrs>misc vctrs functions
:black_square_button:partial_difftime<pillar>misc pillar functions
:black_square_button:`-`(partial_time, partial_difftime)subraction
:black_square_button:`-`(partial_time, partial_time)subraction
:black_square_button:`-`(partial_difftime, partial_difftime)subraction
:black_square_button:`-`(partial_difftime, partial_difftime)addition.
Metadata

Version

0.1.2

License

Unknown

Platforms (75)

    Darwin
    FreeBSD
    Genode
    GHCJS
    Linux
    MMIXware
    NetBSD
    none
    OpenBSD
    Redox
    Solaris
    WASI
    Windows
Show all
  • aarch64-darwin
  • aarch64-genode
  • aarch64-linux
  • aarch64-netbsd
  • aarch64-none
  • aarch64_be-none
  • arm-none
  • armv5tel-linux
  • armv6l-linux
  • armv6l-netbsd
  • armv6l-none
  • armv7a-darwin
  • armv7a-linux
  • armv7a-netbsd
  • armv7l-linux
  • armv7l-netbsd
  • avr-none
  • i686-cygwin
  • i686-darwin
  • i686-freebsd
  • i686-genode
  • i686-linux
  • i686-netbsd
  • i686-none
  • i686-openbsd
  • i686-windows
  • javascript-ghcjs
  • loongarch64-linux
  • m68k-linux
  • m68k-netbsd
  • m68k-none
  • microblaze-linux
  • microblaze-none
  • microblazeel-linux
  • microblazeel-none
  • mips-linux
  • mips-none
  • mips64-linux
  • mips64-none
  • mips64el-linux
  • mipsel-linux
  • mipsel-netbsd
  • mmix-mmixware
  • msp430-none
  • or1k-none
  • powerpc-netbsd
  • powerpc-none
  • powerpc64-linux
  • powerpc64le-linux
  • powerpcle-none
  • riscv32-linux
  • riscv32-netbsd
  • riscv32-none
  • riscv64-linux
  • riscv64-netbsd
  • riscv64-none
  • rx-none
  • s390-linux
  • s390-none
  • s390x-linux
  • s390x-none
  • vc4-none
  • wasm32-wasi
  • wasm64-wasi
  • x86_64-cygwin
  • x86_64-darwin
  • x86_64-freebsd
  • x86_64-genode
  • x86_64-linux
  • x86_64-netbsd
  • x86_64-none
  • x86_64-openbsd
  • x86_64-redox
  • x86_64-solaris
  • x86_64-windows