Skip to content

erlsci/iso8601

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

154 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

iso8601

Build Status Coverage Tag Erlang Version Downloads

An ISO 8601 date formatting and parsing library for Erlang

iso8601 project logo

Contents

About

The erlang_iso8601 library was originally created by Sean Sawyer in 2011. In 2016, Sean handed off maintenance of the library to the erlsci Github org at which point the project (and repo) was renamed to simply iso8601, matching its Erlang app name:

  • git clone https://github.com/erlsci/iso8601.git

Thanks to Github's forwarding for project renames and moves, the following still work:

  • git clone https://github.com/seansawyer/erlang_iso8601.git
  • git clone https://github.com/erlsci/erlang_iso8601.git

New in 1.4

  • Time interval support. New parse_interval/1, interval_bounds/1, and format_interval/1 functions handle all four ISO 8601 interval forms (start/end, start/duration, duration/end, and duration-only), accept both the / and -- separators, support signed durations, and resolve abbreviated end points by inheriting the missing leading components from the start (e.g. 2007-11-13/15). See the interval examples in Usage.
  • Expanded year representation. parse/1 and parse_exact/1 now accept a leading + and more than four digits (e.g. +10000-01-01), staying calendar-compatible. Negative (astronomical) years — for paleontological and geological timescales — are available through the opt-in parse_expanded/1 and format_expanded/1, which return an expanded_datetime() with an integer() year, leaving parse/1's contract unchanged.
  • Correctness fixes. apply_duration/2 now honors a negative sign (a duration like -P1Y is subtracted rather than added); year arithmetic on a leap day (e.g. adding a year to Feb 29) now clamps to a valid date instead of producing an impossible one; and stray debug output during ordinal-date parsing has been removed.
  • Microsecond precision is available via parse_exact/1, which preserves fractional seconds as a float. Note that floats cannot represent every decimal fraction exactly, so values are reliable to microsecond precision via rounding rather than being bit-exact.

Usage

Add it to your rebar.config deps:

{iso8601, "~> 1.4"}

Format a timestamp or calendar datetime tuple:

1> iso8601:format(now()).
<<"2012-02-16T01:06:19Z">>
2> iso8601:format(calendar:universal_time()).
<<"2012-02-16T01:06:48Z">>

Parse a date string or binary:

3> iso8601:parse(<<"2012-02-16T01:06:48Z">>).
{{2012,2,16},{1,6,48}}
4> iso8601:parse("2012-02-16T01:06:48Z").
{{2012,2,16},{1,6,48}}

Add 1 hour, 2 minutes and 3 seconds to a datetime tuple:

5> Datetime = iso8601:parse(<<"2012-02-16T01:06:48Z">>).
{{2012,2,16},{1,6,48}}
6> iso8601:add_time(Datetime, 1, 2, 3).
{{2012,2,16},{2,8,51}}

Subtract 1 hour, 2 minutes and 3 seconds from a datetime tuple:

5> Datetime = iso8601:parse(<<"2012-02-16T01:06:48Z">>).
{{2012,2,16},{1,6,48}}
6> iso8601:subtract_time(Datetime, 1, 2, 3).
{{2012,2,16},{0,4,45}}

Fractional times:

7> iso8601:parse("20120203T040506.50").
{{2012,2,3},{4,5,7}}
8> iso8601:parse_exact("20120203T040506.50").
{{2012,2,3},{4,5,6.50}}

Parse durations:

9> iso8601:parse_duration("+P6Y3M1DT1H1M1.1S").
[{sign, "+"}, {years, 6}, {months, 3}, {days, 1}, {hours, 1}, {minutes, 1}, {seconds, 1}]
10> iso8601:parse_duration("PT6M").
[{sign, []}, {years, 0}, {months, 0}, {days, 0},{hours, 0}, {minutes, 6}, {seconds, 0}]

Parse a time interval. parse_interval/1 preserves which of the four forms was given, tagging the result so it stays matchable:

11> iso8601:parse_interval("2007-03-01T13:00:00Z/2008-05-11T15:30:00Z").
{interval,start_end,{{2007,3,1},{13,0,0}},{{2008,5,11},{15,30,0}}}
12> iso8601:parse_interval("2007-03-01T13:00:00Z/P1Y2M10DT2H30M").
{interval,start_duration,{{2007,3,1},{13,0,0}},
          [{sign,[]},{years,1},{months,2},{days,10},
           {hours,2},{minutes,30},{seconds,0}]}
13> iso8601:parse_interval("P1Y2M10DT2H30M/2008-05-11T15:30:00Z").
{interval,duration_end,
          [{sign,[]},{years,1},{months,2},{days,10},
           {hours,2},{minutes,30},{seconds,0}],
          {{2008,5,11},{15,30,0}}}
14> iso8601:parse_interval("P1Y2M10DT2H30M").
{interval,duration,
          [{sign,[]},{years,1},{months,2},{days,10},
           {hours,2},{minutes,30},{seconds,0}]}

An abbreviated end inherits the missing leading components from the start, and the -- separator is accepted as well as /:

15> iso8601:parse_interval("2007-11-13/15").
{interval,start_end,{{2007,11,13},{0,0,0}},{{2007,11,15},{0,0,0}}}
16> iso8601:parse_interval("2000--2002").
{interval,start_end,{{2000,1,1},{0,0,0}},{{2002,1,1},{0,0,0}}}

Resolve an interval to concrete {Start, End} datetimes with interval_bounds/1 (the start/duration and duration/end forms are computed from the duration; a duration-only interval has no anchor and raises badarg):

17> iso8601:interval_bounds(iso8601:parse_interval("2007-03-01T13:00:00Z/P1Y2M10DT2H30M")).
{{{2007,3,1},{13,0,0}},{{2008,5,11},{15,30,0}}}

Format an interval back to a binary with format_interval/1:

18> iso8601:format_interval(iso8601:parse_interval("2007-03-01T13:00:00Z/P1Y2M10DT2H30M")).
<<"2007-03-01T13:00:00Z/P1Y2M10DT2H30M">>

Parse an expanded-representation year. Positive expanded years (an explicit + and any number of digits, e.g. years beyond 9999) work with parse/1, which stays calendar-compatible:

19> iso8601:parse("+0002007-01-01").
{{2007,1,1},{0,0,0}}
20> iso8601:parse("+10000-01-01").
{{10000,1,1},{0,0,0}}

Negative (astronomical) years — where year 0 is 1 BCE, -1 is 2 BCE, and so on — use parse_expanded/1 / format_expanded/1, which return an expanded_datetime() with an integer() year. parse/1 rejects negative years so its calendar-compatible contract is preserved:

21> iso8601:parse_expanded("-0001-01-01").
{{-1,1,1},{0,0,0.0}}
22> iso8601:format_expanded({{-44,3,15},{0,0,0}}).
<<"-0044-03-15T00:00:00Z">>

parse_expanded/1 preserves fractional seconds (like parse_exact/1), so the seconds field comes back as a float. Negative years are supported in UTC (Z) or offset-free form; combining a negative year with a non-zero UTC offset or a week-date raises badarg.

Known Deficiencies

  • Does not support repeating intervals (Rn/...).
  • Expanded year representation is supported in extended (hyphenated) format only, not in basic format (e.g. +0002007-01-01, not +00020070101).

See the open issues for more info.

Donating

A donation account for supporting development on this project has been set up on Liberapay here:

You can learn more about Liberapay on its Wikipedia entry or on the service's "About" page.

License

The MIT License (MIT)

Copyright © 2011-2014, Sean Sawyer
Copyright © 2012, Tristan Sloughter
Copyright © 2016-2026, Erlang-Aided Enrichment Center

About

An ISO 8601 date formating and parsing library for Erlang

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors