# messydates 1.0.0

## Package

- Updated the `{messydates}` logo to better reflect the package's purpose
  and be more brand-consistent with manydata packages
- Removed `purrr` and `dplyr` dependencies (replaced with base R), 
  leaving only `stringi` and `lubridate` as imports
- Updated the cheatsheet to reflect the new time support and other changes
- Declared `anytime` and `clock` in Suggests, since the interoperability
  tests exercise them where installed

## Class

- Added support for ISO 8601-2:2019 **times** of day in the `mdate` class
  - `as_messydate()` now parses an optional date-time separator, 
    `hh`, `hh:mm`, and `hh:mm:ss` (with fractional seconds), am/pm times, the UTC designator
    `Z`, and numeric offsets (e.g. `+02:00`), zero-padding and normalising them
  - Dates and times are separated by a space (e.g. `2019-03-01 14:30:00`)
    for readability (as permitted by ISO 8601-1 sec. 4.3.2 and RFC 3339),
    but a `T` separator can be used on input and is normalised to a space in the output
  - `:` and `_` continue to work as range separators; 
    times are detected and protected first, 
    so `2009-01-01:2019-01-01` is a range while `2019-03-01 14:30:00` is a time
  - Time components can carry the same annotations as dates: 
    approximate (`~`), uncertain (`?`), both (`%`), and unspecified (`X`),
    e.g. `2019-03-01 ~14:30`
  - A time of day can now be given on its own, with no date part 
    (e.g. `as_messydate("2:30pm")` -> `14:30`, `as_messydate("around 2pm")` -> `14:00~`); 
    this requires a clear time signal (a colon-clock or am/pm), so a bare `2019` 
    is still a year, and a bare am/pm hour (`2pm`) fills to `14:00`. 
    A leading "at" (`"at 2:30pm"`, `"at around 2pm"`) is recognised and dropped. 
    `hour()`/`minute()`/`second()`/`tz()`, `precision()`, `expand()`, 
    and `approximate()`/`uncertain()` all handle date-less times
- Renamed `messyduration()` to `make_messyduration()` for consistency with other `make_*()` functions, 
  and renamed resulting class from `messyduration` to `mduration`
- Fixed bug where a messyrange would shift instead of widen upon approximation

## Coercion

- Improved parsing of written dates (closed #52): 
  - Ordinal days (`"4th July 1976"`): either day-first or month-first order 
    (`"Fourth of July 1976"`, `"July 4th 1976"`), "day of" phrasings, 
    and "last day of `<month>`" (leap-year aware for February)
  - Connectives: 
    - `"between the 13th and 15th of Feb 1977"` (or `"from the 13th to the 15th"`) becomes a range
    - `"the 13th or the 15th"` a set
    - plain `"13th and 15th"`, or a comma-separated list of dates, becomes several dates
  - Reduced-precision expressions: 
    - month-and-year (`"February 2004"` -> `2004-02`)
    - decades (`"the 1910s"` -> `191X`)
    - centuries (`"the 19th century"` -> `18XX`)
  - Open ranges: `"before 1910"` -> `..1910` and `"after 1910"` -> `1910..`, 
    where the bound may itself be imprecise (`"before the 1920s"` -> `..192X`)
  - Prose qualifiers: 
    - approximate words (`"around"`, `"circa"`, ...) add `~`
    - uncertain words (`"possibly"`, `"reportedly"`, ...) add `?`
    - both together add `%`, applied to the most specific component
  - Roman numerals (e.g. `"MDCCLXXVI"` becomes `1776`)
  - Roman calendar references, e.g. `"the Ides of March, 44 BC"` becomes `-0044-03-15` 
  (Kalends, Nones, and Ides, with the later Nones/Ides of March, May, July, and October)
- Coercion from `POSIXct`/`POSIXlt` now preserves the time of day (midnight is
  treated as date-only); `as.POSIXct()`/`as.POSIXlt()` restore it
- Improved interoperability with `{lubridate}`: its `as_date()` and 
  `as_datetime()` coercion verbs now work on an `mdate`, honouring the `FUN` 
  resolver (e.g. `as_date(md, FUN = vmax)`), as do `format()` on `mdate` columns 
  in data frames and tibbles
- Fixed `as.POSIXct()`/`as.POSIXlt()` erroring on a vector of two or more 
  `mdate`s (the check for pre-common-era dates was not vectorised)
  
## Annotation

- Renamed `as_approximate()`/`as_uncertain()` to `approximate()`/`uncertain()` 
  for consistency with `on_or_before()`/`on_or_after()` (these aren't coercion functions); 
  old names are defunct and warn
- Improved `approximate()`/`uncertain()` to accept 
  "hour", "minute", "second", and "time" components
- Improved `approximate()`/`uncertain()` to combine annotations as `%` 
  when both are applied to the same component

## Resolution

- Fixed how `median()`/`vmedian()` treat an even number of dates,
  which no longer silently returns `NA` but instead averages the two middle values
- Fixed how `mean()`/`vmean()`/`median()`/`vmedian()` average precise date-times,
  honouring the time of day, instead of miscomputing via `lubridate::as_date()`
  
## Operations

- Improved arithmetic (`+`/`-`) and `seq()` to accept sub-day units 
  ("hours", "minutes", "seconds") and operate on times
  - `messyduration()` keeps sub-day precision
- Improved how arithmetic treats time of day and shifts the calendar components, 
  so `"2012-02-03 14:30" + "1 year"` is `2013-02-03 14:30` (with month-end rollback)
- Fixed bug where adding/subtracting from an open-ended range (`"2012-01-01T09:00.." + "2 hours"` 
  or `"..2012-01-01T09:00" + "1 month"`) dropped `..` marker
- Fixed how `<`/`>`/`<=`/`>=` compare time of day on the same calendar day, 
  no longer silently truncating both sides to a date first and treating them as equal
- Fixed two related bugs (#92) affecting *any* comparison of two `Date`/`POSIXct` 
  objects in a session with `{messydates}` loaded, including in unrelated packages:
  - since `<`/`>`/`<=`/`>=` are registered for `"Date"`/`"POSIXt"` classes
    (so that e.g. `Date < mdate` works), 
    *any* comparison of a zero-length or all-`NA` `Date`/`POSIXct`/`POSIXlt` value
    was passed through `as_messydate()`, 
    where `ifelse()` and `paste0()` silently changed type or length
    and tripped an internal `is.character()` check. 
    This broke loading packages whose `.onLoad` hooks compare timestamps, 
    such as `{httr2}`'s cache pruning, which in turn broke `pkgdown::build_news()`
  - when one side of a comparison had a time of day and the other did not, 
    their numeric bounds were computed in different units (e.g. seconds, days)
    without converting to a common unit, which could silently reverse the result. 
    In particular, `Sys.time() < (Sys.time() + Inf)` -- 
    the pattern `{httr2}` uses to represent an unbounded retry deadline -- 
    incorrectly evaluated to `FALSE`, in turn breaking `httr2::req_perform()` 
    (and so any request made while `{messydates}` is loaded, 
    including `pkgdown`'s GitHub release-timeline lookup)

## Extraction
  
- Added `hour()`, `minute()`, `second()`, and `tz()` time component extractors
- Improved `month()`, `day()`, and `precision()` to be vectorised and 
  no longer call `expand()` more than necessary
- Improved `precision()` to extend below the day: 24 to the hour, 1440 to the
  minute, and 86400 to the second (date-level precision is unchanged)
- `year()`, `month()`, `day()`, `hour()`, `minute()`, `second()`, and `tz()` are 
  now S3 methods on the same-named `{lubridate}` generics rather than plain 
  functions, so the two packages can be loaded together (in either order) 
  without one masking the other: these accessors dispatch to the messy-date 
  logic on an `mdate` but to `{lubridate}`'s own methods on a `Date`/`POSIXct`

## Expand/Contract

- Fixed `expand()` error when `approx_range` was set and the vector contained a 
  reduced-precision value (e.g. a bare year-month) alongside an approximate one
- Improved `expand()` with a `by` argument (default `"day"`)
  - Ranges are enumerated at day granularity to avoid combinatorial explosion;
    set `by` to `"hour"`, `"min"`, or `"sec"` for finer enumeration 
  - Precise date-times keep their time
  
# messydates 0.5.4

## Coercion

- Improved how `as_messydates()` handles text with dates in American format,
e.g. October 10, 2010 (fixes #86)

# messydates 0.5.3

## Components

- Improved `year()` to be faster and work on durations
- Improved `precision()`
  - `precision()` is now a S3 generic, dispatching to `precision.mdate()`
  - `precision.mdate()` now returns the inverse of the previous measure,
  meaning maximising precision makes more sense

# messydates 0.5.2

## Package

- Moved `mreport()` to `{manydata}`
- Consolidated and renamed scripts internally

## Coerce to

- Fixed pkgdown#2855 by fixing how as_messydate methods interpret infinite dates
- Fixed time zone defaults in `as.POSIXct.mdate()` and `as.POSIXlt.mdate()`
- Fixed set bug in `validate_messydate()`

## Coerce from

- Renamed `as.numeric()` to `as.double()` to fix S3 dispatching
- Separated extrema functions into `min.mdate()` and `max.mdate()` for summaries 
and `vmin.mdate()` and `vmax.mdate()` for vector coercion
- Separated tendency functions into `mean.mdate()`, `median.mdate()`, and `modal.mdate()` for summaries 
and `vmean.mdate()`, `vmedian.mdate()`, and `vmodal.mdate()` for vector coercion
- Vector coercion previously in `random.mdate()` now in `vrandom.mdate()`
- Improved how coercion/resolution functions handle BCE dates

## Manipulation

- Fixed how `precision()` calculates precision

# messydates 0.5.1

## Package

- Fixed redirected url in README
- Fixed pkgdown help links

# messydates 0.5.0

## Package

- Dropped the vignettes as they were 'outdated'
- Dropped a number of tests to provide rapid testing framework
- Dropped `{tibble}` dependency by just using `{dplyr}`
- Updated Github workflows
- Updated testthat to version 3, tests now run in parallel
- Updated pkgdown to bootstrap 5
- Updated DESCRIPTION with config packages

## Functions

- Moved from `{stringr}` to `{stringi}` for _speed_
- Added `as.numeric.mdate()` and `as_messydate.numeric()` for coercing between messydates and numbers (closes #85)
- Added `seq.mdate()` for creating sequences from one or two messydates
  - This includes correct sequences for leap years and historical dates including before the common era
- Added `is_bce()` for testing whether dates are from before the common era
- Added `stri_squish()` helper for trimming white space everywhere
- Improved `c.mdate()` so that it will strip class from an `mdate` object, as expected
- Improved `is_uncertain()` and `is_approximate()` so that they also recognise `%` annotations
- Improved `min.mdate()`, `max.mdate()`, and `modal.mdate()` to avoid using `expand()` and consequently run much faster
- Improved `min.mdate()`, `max.mdate()`, `modal.mdate()`, `mean.mdate()`, `median.mdate()`, and `random.mdate()` by adding `recursive` argument for resolving vectors down to a scalar
- Fixed bug in `messyduration.mdate()` where the minimum of an underspecified later date was used
- Fixed bug in `as_messydate()` where zero padding for early dates was not added correctly

# messydates 0.4.1

## Package

* The package now depends on R versions bigger or equal to 4.0 since functions for subsetting and comparing 'mdate' objects rely on functions introduced in that version

## Functions

* Closed #83 by fixing how logical comparisons works for negative and year only dates

# messydates 0.4.0

## Functions

* Closed #46 by adding the `mdates_duration` class that introduces methods to annotate a duration or period with representations of its uncertainty
* Closed #72 by fixing issues with double unspecified components not being contracted correctly
* Closed #73 by fixing bugs with the conversion of dates where month is spelled
* Closed #74 and #82 by adding other logical comparison operators for 'mdate' objects (e.g. `<`, `>`, `<=`, `>=`) (thanks @WerthPADOH)
* Closed #76 by adding proportional operators that calculate the proportion of messy dates meeting logical tests (e.g. `%l%` `%le%`, `%g%`, `%ge%`, `%><%`, `%>=<%`)
* Closed #77 by adding basic vector methods for subsetting and data frames (thanks @WerthPADOH)
* Added alias function `mdate()` for `as_messydate()`
* Renamed set family of functions to work as operators (i.e. `%intersect%` and `%union%`)
* Replaced `is_element()` by `is_subset()` for clarity and consistency
* Closed #80 by updating `make_messydates()` function to also construct ranges of dates

# messydates 0.3.5

## Functions

* Updated how `contract()` function checks if 'mdate' object has been expanded 

# messydates 0.3.4

## Package

* Updated 'battles' internal data
  * Corrected issues with zero padding for certain date ranges
  * Added 'US_party' and 'N_actors' additional variables for replication purposes

## Functions

* Closed #68 by updating `as_messydate()` function
  * Fixed bugs with zero padding for ranges of dates
  * Fixed bugs with the re-ordering of months and day components for incorrectly specified dates
* Closed #69 by updating `contract()` function to 'expand' dates before 'contracting' them
* Updated `expand()` function to handle, and properly convert, date objects that are not 'mdate'

# messydates 0.3.3

## Package

* Moved cheatsheet.pdf to 'inst' folder instead of the 'man' folder

# messydates 0.3.2

## Package

* Closed #64 by updating failing tests to test for other aspects instead of the printing of negative dates across OS
* Closed #65 by updating cheatsheet for new package changes

## Functions

* Closed #62 by adding "resequence" as an argument to `as_messydates()` for explicit date format conversion, if necessary
* Closed #63 by fixing issues with unnecessary white spaces added in date conversion

# messydates 0.3.1

## Package

* Updated README by removing unattractive package startup messages
* Updated `battles` data by adding 'parties' variable

## Functions

* Closed #54 by adding new `mreport()` function to properly report on data containing 'mdate' variables
* Updated `expand()` function
  * Fixed bug with the expansion of approximate dates
  * Removed unnecessary function message

# messydates 0.3.0

## Package

-   Closed #51 by changing object class name to `mdate`
    -   Note that this is a *breaking* change
-   Closed #41 by creating `{skimr}` template for `mdate` class

## Functions

-   Updated coercion to messy dates
    -   Closed #26 by adding "resequence" argument to `as_messydate()` allowing users to choose component order of ambiguous dates
    -   Closed #45 by improving how `as_messydate()` re-orders 6 digit date components if necessary
    -   Closed #48 by adding zero padding incomplete date ranges and sets of dates
    -   Updated `as_messydate()` to also extract dates from text strings
-   Added `is_precise()` function that provides a logical test for precise dates
-   Updated messy dates expansion
    -   Updated `expand()` to allow for the expansion of incomplete date ranges and sets of dates
    -   Closed #49 by updating resolve functions to only expand dates if they are not precise

# messydates 0.2.1

## Package

-   Added a vignette for working with the `{messydates}` package

## Functions

-   Closed #9 by adding arithmetic operations for working with `messydt` objects
    -   Added S3 methods for "+" and "-" operators
    -   Added `add()` and `subtract()` helper functions for arithmetic operations
-   Updated `expand()` function
    -   Closed #31 by updating how approximate dates are expanded to account for leap years
    -   Closed #34 by updating `expand()` to manage negative dates
    -   Added `expand_negative_dates()` helper function for expanding ranges of negative dates
-   Updated functions that coerce from `messydt` objects to `Date` to manage negative dates
    -   Added `negative_dates()` helper function to coerce negative `messydt` dates
-   Closed #39 by updating how resolve mean methods work for negative dates
-   Closed #40 by updating contract function to manage the contraction of negative dates
    -   Added `compact_negative_dates()` helper function to compact negative date ranges
    -   Added `is.sequence()` as a helper function to check if dates are a range
-   Updated resequence script to export `interleave()` function

# messydates 0.2.0

## Package

-   Added PANARCHIC project details to README file
-   Added cheatsheet
-   Added a new CSS style to website and updated functions displayed
-   Addressed workflow actions issues
    -   Updated pushrelease.yml workflow actions file to stop installing `{messydates}` from Github
    -   Updated README file to stop installing `{messydates}` from Github
-   Fixed Codecov test coverage URL on README file for CRAN submission

## Functions

-   Expanded on messydates checks for class validity
-   Fixed bugs for `make_messydate()`
-   Added annotation functions and standardized annotation so that it is consistent with ISO2019E standards.
    -   `on_or_before()`
    -   `on_or_after()`
    -   `as_approximate()`
    -   `as_uncertain()` (includes discrimination between month uncertainty and day and month uncertainty)
-   Updated `as_messydate()` by adding zero padding for month, day or year
-   Updated `resequence()` to work consistently with messydate objects
-   Updated `expand()` function to expand imprecise, unspecified, approximate, uncertain, and negative dates according to approximate ranges and added tests
-   Updated `precision()` to return the lengths of expanded dates
-   Updated `median()` in resolve family of functions to work with changes to `expand()`
-   Added tests for functions
    -   Added tests for `expand()`
    -   Added tests for `contract()`
    -   Added tests for `precision()`
    -   Added tests for `coerce_from_messydate()`

# messydates 0.1.1

## Package

-   Updated README with some more explanation about what the package does/offers
-   Fixed URL to the package website

# messydates 0.1.0

## Package

-   Updated call to `messydt` class in DESCRIPTION file

## Functions

-   Updated documentation for `as_messydate()` functions
-   Updated documentation for `expand()` function
-   Updated documentation for resolve family of functions
-   Updated documentation for coerce from family of functions
-   Updated documentation for coerce to family of functions

# messydates 0.0.1

## Package

-   Setup `{messydates}` package
    -   Added `DESCRIPTION` file
    -   Added `R` folder
    -   Added `LICENSE` file
    -   Added `NAMESPACE` file
    -   Added `NEWS` file
    -   Added `README` files
    -   Added `.github` folder and files
    -   Added `tests` folder and files
-   Setup pkgdown website
-   Added package logo

## Functions

-   Added a new `messydt` class which follows the latest ISO 8601 (2019) standards
    -   Added validation checks for messydt class
    -   Added print methods for messydt class
-   Added `as_messydate()` function to coerce from date objects to messydate
    -   `as_messydate()` standardises date order, separators and ambiguity
    -   Added date class coercion
    -   Added POSIXct class coercion
    -   Added POSIXlt class coercion
    -   Added character class coercion
-   Added functions to coerce from messydate objects to other date classes
    -   Added `as.Date.messydt()` for coercing to date class
    -   Added `as.POSIXct.messydt()` for coercing to POSIXct class
    -   Added `as.POSIXlt.messydt()` for coercing to POSIXlt class
-   Added `expand()` function for expanding ranged and uncertain dates
-   Added functions to resolve expanded dates
    -   Added `min.messydt()` to get minimum value from expanded range
    -   Added `max.messydt()` to get maximum value from expanded range
    -   Added `median.messydt()` to get median value from expanded range
    -   Added `mean.messydt()` to get mean value from expanded range
    -   Added `modal.messydt()` to get mode value from expanded range
-   Added `contract()` function for contracting expanded dates
-   Added extract functions to get particular date components
    -   Added `year()` to extract year from date
    -   Added `month()` to extract month from date
    -   Added `day()` to extract day from date
-   Added `make_messydate()` function to get messy dates from multiple columns
-   Added set functions for operations in sets of messy dates
    -   Added `md_intersect()` to find intersection of sets of messy dates
    -   Added `md_union()` to find union of sets of messy dates
    -   Added `md_multiset()` to join two sets of messy dates
-   Added logical function for various logical tests for messy date objects
    -   Added `is_messydate()` to test for messydt class
    -   Added `is_intersecting()` to test if dates intersect
    -   Added `is_element()` to test for multiple elements in dates
    -   Added `is_similar()` to test for similarities in dates
-   Added tests for new functions
    -   Added tests for messydt class and `às_messydate()` function
    -   Added tests for coerce from messy dates functions
    -   Added tests for coerce to messy dates functions
    -   Added tests for `contract()` function
    -   Added tests for `expand()` function
    -   Added tests for extract functions
    -   Added tests for `make_messydate()` function
    -   Added tests for resolve functions
    -   Added tests for set functions
