Giter Club home page Giter Club logo

edtf-rs's Introduction

edtf

docs.rs

This crate implements the Extended Date/Time Format 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

[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 crate via the optional features = ["chrono"]. Similarly, provides optional 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 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 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, 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 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.

Example usage of the edtf crate

Level 0

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

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

edtf-rs's People

Contributors

cormacrelf avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

bdarcus

edtf-rs's Issues

Seasonal ranges, and formatting tips?

Hey @cormacrelf.

While it's not formally part of the spec, thought I might suggest including this anyway, per EDTF.js.

inukshuk/edtf.js#12

Also, on a simpler note, can you show how to format the parsed dates, and maybe add it to the usage section?

I'm wanting to implement localized level 1 support in a project I'm working on, and am lost.

Here's the typescript prototype code I'm wanting to convert to Rust:

  formatDate(date: string, options: Intl.DateTimeFormatOptions): string {
    const parsedDate = edtf.default(date);
    const year = parsedDate.year.toString();
    const optKeys = Object.keys(options);
    const useYear = optKeys.includes("year") && optKeys.length === 1;
    const formattedDate = useYear
      ? year
      : edtf.format(parsedDate, "en-US", options);
    return formattedDate;
  }

serde Serialize/Deserialize implementations

It needs these. Pretty easy, just parse to deserialize, and use the Display implementation to serialize. Serde should be an optional dependency, no need for the derive macro.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.