Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/cormacrelf/edtf-rs

An implementation of the Extended Date/Time Format (EDTF).
https://github.com/cormacrelf/edtf-rs

chrono date datetime edtf

Last synced: 2 months ago
JSON representation

An implementation of the Extended Date/Time Format (EDTF).

Awesome Lists containing this project

README

        

# edtf

![docs.rs](https://docs.rs/edtf/badge.svg)

This crate implements the [Extended Date/Time
Format](https://www.loc.gov/standards/datetime/) as of the 2019-02
specification. It contains separate implementations for each level 0, 1 (and 2,
but not yet). Notes on the choices made in each level are found in in the
module level documentation.

### Installation

```toml
[dependencies]
edtf = "0.2.0"
```

### Features:

- Lossless. Each parsed Edtf can be formatted again to output exactly the same string.
- Strict. Rejects everything the specification rejects as a parse error. All
the types make it impossible to construct an invalid `Edtf` object, down to the
leap year.
- Integration with the widely used [chrono](https://lib.rs/chrono) crate via
the optional `features = ["chrono"]`. Similarly, provides optional
[serde](https://lib.rs/serde) implementations under the `serde` feature.
- Three implementations, not one, so you can pick your compatibility level.

### Notes on EDTF and the ISO 8601 calendar system

It is probably not stated often enough that EDTF and ISO 8601 support only one
calendar system, a modified [proleptic Gregorian
calendar](https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar) with a
year zero. **⚠️ This means your old historical records may have to be converted
from a different calendar system to be used with EDTF accurately.**

Here's a summary for EDTF use:

- Years written with `BCE` are off by one. **1BCE is year 0000 in EDTF. 100BCE
is -0099 in EDTF.**
- Positive years (aka CE) are correct already.
- Be careful transcribing complete dates written down before the local adoption
of the Gregorian calendar. **You may be unwittingly transcribing a Julian
date**, or some other calendar, which will be reinterpreted by EDTF as a
proleptic Gregorian date and behave incorrectly.
- Check the [local adoption][local-adoption] of the Gregorian calendar where
the historical record was created. Many western jurisdictions adopted it in
1582, but e.g. the UK didn't switch over until 1752-09.
- Note that, especially if you're attempting to use EDTF naïvely as a dumb storage area
for a year/month/day combination, then you may unwittingly write an invalid
ISO date due to the removal of every 25th or so leap year before its
adoption. **This library validates that parsed EDTF dates actually exist in
the ISO calendar.**

Here is an online [Julian \<-\> Gregorian
converter][julian-converter], made by Stephen P. Morse, the man who designed
the Intel 8086 chip. More conversion tools and detailed information about a
selection of calendar systems are available on his awesome
[One Step Webpages][stephen-p-morse] site.

> One useful piece of info on there is that the Julian calendar *year* does not
generally increment on the January 1. In the UK it increments on 25 March,
which used to be the end of the fiscal year there, until they switched to
Gregorian and dropped 11 days in the process. Since then, tax day is April 5!
Julian dates between 1 January and 25 March are often 'dual dated', i.e.:
>
> > ... two years separated by a slash. The first year was the year in the
> > Julian calendar in use in that locality, and the second was the year that
> > it would have been if they had changed the year number on January 1.
>
> E.g. *19 February 1683/4*.

[local-adoption]: https://en.wikipedia.org/wiki/List_of_adoption_dates_of_the_Gregorian_calendar_per_country
[julian-converter]: https://stevemorse.org/jcal/julian.html
[stephen-p-morse]: https://stevemorse.org/#calendar

### Example usage of the `edtf` crate

#### Level 0

```rust
use edtf::level_0::Edtf;
let edtf = Edtf::parse("2019-01-07/2020-01").unwrap();
match edtf {
Edtf::Date(d) => println!("date: {}", d),
Edtf::Interval(from, to) => {
println!("interval: {} to {}", from, to);
println!("year parts: {} to {}", from.year(), to.year());
}
Edtf::DateTime(dt) => println!("datetime: {}", dt),
}
// prints:
// interval: 2019-01-07 to 2020-01
// year parts: 2019 to 2020
```

#### Level 1

```rust
use edtf::level_1::{Date, Edtf, Certainty, Precision, Terminal};
use edtf::{DateTime, Time, DateComplete, TzOffset};

let edtf = Edtf::parse("2021-07-15");
assert_eq!(edtf, Ok(Edtf::Date(Date::from_ymd(2021, 07, 15))));

let edtf = Edtf::parse("2019-01-XX?").unwrap();
match edtf {
Edtf::Date(date) => {
// precision deconstructs a date into variants like
// 2019, 2019-01, 2019-01-01, 20XX, 201X, 2019-XX, 2019-01-XX, 2019-XX-XX.
// certainty is the ? (Uncertain) / ~ (Approximate) / % (Both) value.
assert_eq!(date.precision(), Precision::DayOfMonth(2019, 01));
assert_eq!(date.certainty(), Certainty::Uncertain);
}
_ => panic!("not matched")
}

let edtf = Edtf::parse("2019-XX/..").unwrap();
match edtf {
Edtf::IntervalFrom(from_date, terminal) => {
assert_eq!(from_date.precision(), Precision::MonthOfYear(2019));
assert_eq!(terminal, Terminal::Open);
}
_ => panic!("not matched"),
}

let edtf = Edtf::parse("Y-12000").unwrap();
match edtf {
Edtf::YYear(yy) => assert_eq!(yy.value(), -12000i64),
_ => panic!("not matched"),
}

let edtf = Edtf::parse("2012-11-30T12:04:56Z").unwrap();
match edtf {
Edtf::DateTime(dt) => {
assert_eq!(dt.date(), DateComplete::from_ymd(2012, 11, 30));
assert_eq!(dt.time(), Time::from_hmsz(12, 04, 56, TzOffset::Utc));
},
_ => panic!("not matched"),
}

// Edtf's parsing + Display implementation is lossless, so you can render back
// out the same string.
let string = "2019-01-XX~/2020-XX";
assert_eq!(Edtf::parse(string).unwrap().to_string(), string);
```

License: MPL-2.0