Changelog
Source:NEWS.md
messydates 1.1.0
Package
- Improved package architecture documentation in
.github/CONTRIBUTING.mdso that human contributors and coding agents read the same notes - Added a PR metadata job to the PR checks workflow, which verifies that the
DESCRIPTIONversion is bumped, that the PR title names the new version, and that the PR description itemizes changes under subsection titles; these items are consequently dropped from the pull request template - Fixed the website deploy job installing
Config/Needs/checkpackages instead ofConfig/Needs/website
Class
- Improved
validate_messydate()to report which elements failed and what they contained, instead of naming only the rule that was broken- No longer passes a vector of empty strings as valid because a single element somewhere in it happened to contain a digit
- Assigning an unparseable value into an
mdatewith[<-or[[<-now reports what could not be parsed, rather than silently blanking the element - Added
unique()andduplicated()methods formdateobjects (closes #106)- Previously
unique()fell through to the character method and dropped the class, so a deduplicated column silently stopped being anmdate - Both compare the annotated strings rather than the dates they expand to, so
"2012-01"and"2012-01-01..2012-01-31"remain distinct
- Previously
Coercion
- Improved
as_messydate()to validate its input rather than carrying values it cannot interpret:- dates with impossible components are rejected, so
"2019-02-30","2019-06-31"and"2019-01-01 25:00"now error instead of being accepted as written; relatedly,"2019-13-45"is no longer silently reordered into"2019-45-13", since month and day are only swapped where the swap yields a date that could exist - ISO 8601-2 notations that messydates does not represent (week dates, ordinal dates, season codes, significant digits, extended years, the
P-style duration notation, and repeating intervals) now error naming the format, instead of passing through as strings that nothing downstream could expand, resolve or compare. Durations themselves are unaffected: they are written as date ranges and handled bymduration, and the error for"P1Y2M"points there - text naming no date at all still becomes
NA, but now warns, listing what could not be read
- dates with impossible components are rejected, so
- Added
md_problems(), which reports one row per unparseable element of a vector, with the reason for each, for checking a column of dates before coercing it - Added
as_messydate()methods for factors (coerced via their labels, the common case when a column was read in withstringsAsFactors = TRUE) and a default method that names the offending class rather than failing with R’sUseMethodmessage - Improved parsing consistency across scalars and vectors
- a month-first date such as
"July 4 1976"previously lost its day whenever another value shared the vector with it
- a month-first date such as
- Fixed how
[]sets (“one member of”) silently rewrote as{}sets (“all members of”).- Note that the two still expand and resolve alike; giving
[]its own meaning in the resolution functions remains outstanding
- Note that the two still expand and resolve alike; giving
- Fixed BCE/CE prose off-by-one error (closed #94, thanks @njbart) to convert to ISO 8601-2 astronomical year numbering (proleptic Gregorian, in which a year zero exists and equals 1 BCE):
- A historical
N BCEmaps to the astronomical year-(N-1), so"44 BC"becomes-0043and"1 BC"becomes0000(year zero); a signed ISO year such as"-0044"is already astronomical and is left unchanged - Year zero is preserved on input (previously
0000was misread as an unspecified year) and is traversed byseq()andexpand(), so a sequence spanning the BCE/CE boundary now passes through the whole of year0000rather than jumping from-0001straight to0001 - Era markers on input are now resolved for each year in an expression separately, rather than by counting how many markers a string contains (they are, as before, always removed in the parsed
mdate, which records the era in the sign of the year alone). So an input marker written once at the end of a range or set applies to every bound of it:"..200 BC","200..100 BC"and"44, 33 BC"now give..-199,-0199..-0099and{-0043,-0032}, where previously the non-leading bounds silently stayed CE; an input marker written before a date still governs that date, so"{BC2010-10-10,BC2010-10-11}"gives{-2009-10-10,-2009-10-11}; and"200 BC..100 AD"spans the two eras, giving-0199..0100 - Fixed how era markers could be dropped from approximate or uncertain dates given in prose, so
"circa 200 BC"is now~-199rather than0200~
- A historical
Expand/Contract
- Fixed how
expand()handles unspecified years- Previously
"192X"raised'from' must be a finite numberand"18XX"silently returned a single date, even though these are what the prose parser produces for decades and centuries - Now a bare unspecified year expands to the whole span (
"192X"gives every day of the 1920s) - Now an attached month or day picks out that month or day in each candidate year (
"192X-05-04"gives ten dates, not nine years of them) - Now BCE years are bounded the other way round,
-1999being earlier than-1900 - Now a year too vague to enumerate, such as
"XXXX", refused with a message suggestingvmin()/vmax()
- Previously
- Fixed how
expand()applies unspecified-component rules to each member of a set separately, fixing over-expansion of sets whose members had an unspecified month:"{2008-XX-31,2009-XX-31}"gave 671 dates and now gives 24 - Fixed
contract()returning every set in{}notation, so that a[]set no longer became a{}set on a round-trip throughexpand()/contract()(closes #99)- Only applies where
contract()is given anmdate, since a list of dates does not record which kind of set its members came from - Documented what the two set types mean once resolved or operated on, having established that both should continue to expand to the same members (closes #99):
-
?resolve_tendencynow distinguishes a central tendency that describes where several recorded occurrences sit ({}) from one that is a point estimate of a single unknown date ([]), as for a range -
?operate_setand?operate_proportionalnow note that a result for a[]set reads as the candidates that remain possible, or the probability that a comparison holds, rather than a share of recorded occurrences
-
- Only applies where
messydates 1.0.0
CRAN release: 2026-07-16
Package
- Updated the messydates logo to better reflect the package’s purpose and be more brand-consistent with manydata packages
- Removed
purrranddplyrdependencies (replaced with base R), leaving onlystringiandlubridateas imports - Updated the cheatsheet to reflect the new time support and other changes
- Declared
anytimeandclockin Suggests, since the interoperability tests exercise them where installed
Class
- Added support for ISO 8601-2:2019 times of day in the
mdateclass-
as_messydate()now parses an optional date-time separator,hh,hh:mm, andhh:mm:ss(with fractional seconds), am/pm times, the UTC designatorZ, 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 aTseparator 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, so2009-01-01:2019-01-01is a range while2019-03-01 14:30:00is 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 bare2019is still a year, and a bare am/pm hour (2pm) fills to14:00. A leading “at” ("at 2:30pm","at around 2pm") is recognised and dropped.hour()/minute()/second()/tz(),precision(),expand(), andapproximate()/uncertain()all handle date-less times
-
- Renamed
messyduration()tomake_messyduration()for consistency with othermake_*()functions, and renamed resulting class frommessydurationtomduration - 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)
- month-and-year (
- Open ranges:
"before 1910"->..1910and"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
- approximate words (
- Roman numerals (e.g.
"MDCCLXXVI"becomes1776) - 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)
- Ordinal days (
- Coercion from
POSIXct/POSIXltnow preserves the time of day (midnight is treated as date-only);as.POSIXct()/as.POSIXlt()restore it - Improved interoperability with lubridate: its
as_date()andas_datetime()coercion verbs now work on anmdate, honouring theFUNresolver (e.g.as_date(md, FUN = vmax)), as doformat()onmdatecolumns in data frames and tibbles - Fixed
as.POSIXct()/as.POSIXlt()erroring on a vector of two or moremdates (the check for pre-common-era dates was not vectorised)
Annotation
- Renamed
as_approximate()/as_uncertain()toapproximate()/uncertain()for consistency withon_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
Operations
- Improved arithmetic (
+/-) andseq()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"is2013-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/POSIXctobjects in a session with messydates loaded, including in unrelated packages:- since
</>/<=/>=are registered for"Date"/"POSIXt"classes (so that e.g.Date < mdateworks), any comparison of a zero-length or all-NADate/POSIXct/POSIXltvalue was passed throughas_messydate(), whereifelse()andpaste0()silently changed type or length and tripped an internalis.character()check. This broke loading packages whose.onLoadhooks compare timestamps, such as httr2’s cache pruning, which in turn brokepkgdown::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 toFALSE, in turn breakinghttr2::req_perform()(and so any request made while messydates is loaded, includingpkgdown’s GitHub release-timeline lookup)
- since
Extraction
- Added
hour(),minute(),second(), andtz()time component extractors - Improved
month(),day(), andprecision()to be vectorised and no longer callexpand()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(), andtz()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 anmdatebut to lubridate’s own methods on aDate/POSIXct
Expand/Contract
- Fixed
expand()error whenapprox_rangewas set and the vector contained a reduced-precision value (e.g. a bare year-month) alongside an approximate one - Improved
expand()with abyargument (default"day")- Ranges are enumerated at day granularity to avoid combinatorial explosion; set
byto"hour","min", or"sec"for finer enumeration - Precise date-times keep their time
- Ranges are enumerated at day granularity to avoid combinatorial explosion; set
messydates 0.5.4
CRAN release: 2025-06-02
Coercion
- Improved how
as_messydates()handles text with dates in American format, e.g. October 10, 2010 (fixes #86)
messydates 0.5.3
CRAN release: 2025-03-20
Components
- Improved
year()to be faster and work on durations - Improved
precision()-
precision()is now a S3 generic, dispatching toprecision.mdate() -
precision.mdate()now returns the inverse of the previous measure, meaning maximising precision makes more sense
-
messydates 0.5.2
CRAN release: 2025-03-07
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()andas.POSIXlt.mdate() - Fixed set bug in
validate_messydate()
Coerce from
- Renamed
as.numeric()toas.double()to fix S3 dispatching - Separated extrema functions into
min.mdate()andmax.mdate()for summaries andvmin.mdate()andvmax.mdate()for vector coercion - Separated tendency functions into
mean.mdate(),median.mdate(), andmodal.mdate()for summaries andvmean.mdate(),vmedian.mdate(), andvmodal.mdate()for vector coercion - Vector coercion previously in
random.mdate()now invrandom.mdate() - Improved how coercion/resolution functions handle BCE dates
Manipulation
- Fixed how
precision()calculates precision
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()andas_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 anmdateobject, as expected - Improved
is_uncertain()andis_approximate()so that they also recognise%annotations - Improved
min.mdate(),max.mdate(), andmodal.mdate()to avoid usingexpand()and consequently run much faster - Improved
min.mdate(),max.mdate(),modal.mdate(),mean.mdate(),median.mdate(), andrandom.mdate()by addingrecursiveargument 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
CRAN release: 2024-04-19
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_durationclass 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()foras_messydate() - Renamed set family of functions to work as operators (i.e.
%intersect%and%union%) - Replaced
is_element()byis_subset()for clarity and consistency - Closed #80 by updating
make_messydates()function to also construct ranges of dates
messydates 0.3.5
CRAN release: 2023-01-20
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.1
CRAN release: 2022-07-21
messydates 0.3.0
CRAN release: 2022-06-02
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
- Closed #26 by adding “resequence” argument to
- Added
is_precise()function that provides a logical test for precise dates - Updated messy dates expansion
messydates 0.2.1
CRAN release: 2022-02-23
Package
- Added a vignette for working with the messydates package
Functions
- Closed #9 by adding arithmetic operations for working with
messydtobjects- Added S3 methods for “+” and “-” operators
- Added
add()andsubtract()helper functions for arithmetic operations
- Updated
expand()function - Updated functions that coerce from
messydtobjects toDateto manage negative dates- Added
negative_dates()helper function to coerce negativemessydtdates
- Added
- 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
- Added
- Updated resequence script to export
interleave()function
messydates 0.2.0
CRAN release: 2021-11-12
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 toexpand() - Added tests for functions
- Added tests for
expand() - Added tests for
contract() - Added tests for
precision() - Added tests for
coerce_from_messydate()
- Added tests for
messydates 0.1.0
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
DESCRIPTIONfile - Added
Rfolder - Added
LICENSEfile - Added
NAMESPACEfile - Added
NEWSfile - Added
READMEfiles - Added
.githubfolder and files - Added
testsfolder and files
- Added
- Setup pkgdown website
- Added package logo
Functions
- Added a new
messydtclass 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
- 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
- Added
contract()function for contracting expanded dates - Added extract functions to get particular date components
- 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
- 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
- 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
- Added tests for messydt class and