All Projects → inukshuk → edtf.js

inukshuk / edtf.js

Licence: BSD-2-Clause License
Extended Date Time Format (ISO 8601-2 / EDTF) Parser for JavaScript

Programming Languages

javascript
184084 projects - #8 most used programming language
Nearley
35 projects

Projects that are alternatives of or similar to edtf.js

duration
Parse iso8601 duration strings, and use to shift dates/times.
Stars: ✭ 51 (+15.91%)
Mutual labels:  time, date, iso8601
iso8601
A fast ISO8601 date parser for Go
Stars: ✭ 122 (+177.27%)
Mutual labels:  time, date, iso8601
relative.time.parser
Moment.js Plugin for parsing Relative Time Strings
Stars: ✭ 13 (-70.45%)
Mutual labels:  time, date
popoPicker
popoPicker是一个移动端3D滚轮日期时间和单项的选择器,支持无限循环滚动,不依赖第三方库
Stars: ✭ 26 (-40.91%)
Mutual labels:  time, date
rescript-date
📆 Date manipulation in ReScript.
Stars: ✭ 101 (+129.55%)
Mutual labels:  time, date
nepali-datetime
Python's core datetime inspired nepali datetime (BS date & NPT) package 🇳🇵
Stars: ✭ 36 (-18.18%)
Mutual labels:  time, date
dt
Go's missing DateTime package
Stars: ✭ 34 (-22.73%)
Mutual labels:  time, date
chronos
One library to rule the time
Stars: ✭ 17 (-61.36%)
Mutual labels:  time, date
rutimeparser
Recognize date and time in russian text and return datetime.datetime.
Stars: ✭ 17 (-61.36%)
Mutual labels:  time, date
isoweek
Go package for calculating a start date and time of ISO 8601 week. (golang)
Stars: ✭ 32 (-27.27%)
Mutual labels:  date, iso8601
moment-cache
⏱ Simple utility to cache moment.js results and speed up moment calls.
Stars: ✭ 29 (-34.09%)
Mutual labels:  time, date
date4j
Lightweight alternative to Java's built-in date-time classes. Android-friendly. Compiles under JDK 1.5.
Stars: ✭ 36 (-18.18%)
Mutual labels:  time, date
vue-translated
Internationalization (i18n) and localization (l10n) library for Vue.js v2.
Stars: ✭ 19 (-56.82%)
Mutual labels:  time, date
hs-hourglass
efficient and simpler time API for haskell
Stars: ✭ 43 (-2.27%)
Mutual labels:  time, date
shamsi date
A Flutter and Dart package for using Jalali (Shamsi, Solar, Persian or Jalaali) calendar. You can convert, format and manipulate Jalali and Gregorian (Miladi) date and times.
Stars: ✭ 59 (+34.09%)
Mutual labels:  time, date
go-systemd-time
📅 Go implementation of systemd relative time adjustments
Stars: ✭ 21 (-52.27%)
Mutual labels:  time, date
time-formater
在javascript中显示日期。
Stars: ✭ 44 (+0%)
Mutual labels:  time, date
date-php
这是一个Javascript模仿PHP日期时间格式化函数,使用方法和PHP非常类似,有丰富的模板字符,并在原来的基础上增加了一些模板字符。 This is a date function that implement PHP in Javascript. It is very similar to PHP, has rich template characters, and enhances some template characters on the basis of the original.
Stars: ✭ 24 (-45.45%)
Mutual labels:  time, date
ElevenClock
ElevenClock: Customize Windows 11 taskbar clock
Stars: ✭ 1,494 (+3295.45%)
Mutual labels:  time, date
qrono
🕥 Just right date time library
Stars: ✭ 111 (+152.27%)
Mutual labels:  time, date

EDTF.js

Build Status codecov NPM version

An Extended Date Time Format (EDTF) / ISO 8601-2 parser and toolkit for date/time hackers and enthusiasts.

Compatibility

EDTF / ISO 8601-2

EDTF.js fully implements EDTF levels 0, 1, and 2 as specified by ISO 8601-2 with the following exceptions:

  1. Seasons in intervals are supported at the experimental/non-standard level 3.

ESM

Since version 4.0 EDTF.js uses Nodes.js native ESM module implementation. Since version 4.1 EDTF.js is published as a dual-package.

Installation

$ npm install edtf

Manual

EDTF.js exports a top-level function which takes either a string (with optional parser constraints), a parse result, a regular or any of the extended date objects and returns a new, extended date object as appropriate.

import edtf, { Date, Interval } from 'edtf'

edtf('2016-XX')          #-> returns a EDTF Date
edtf(new Date())         #-> returns a EDTF Date
edtf('2016-04~/2016-05') #-> returns a EDTF Interval

For a list of all types supported by EDTF.js see:

import * as types from 'edtf/types'
Object.keys(types)
#-> ['Date', 'Year', 'Decade', 'Century', 'Season', 'Interval', 'List', 'Set']

Each type provides at least the following properties: the date's corresponding EDTF string, its minimum and maximum numeric value, its type, as well as its date part values.

edtf('2016?').edtf         #-> '2016?'

edtf('2016-02').min        #-> 1454284800000, i.e. 2016-02-01T00:00:00Z
edtf('2016-02').max        #-> 1456790399999, i.e. 2016-02-29T23:59:59Z

edtf('2016-02-2X').min     #-> 1455926400000, i.e. 2016-02-20T00:00:00Z
edtf('2016-02-2X').max     #-> 1456790399999, i.e. 2016-02-29T23:59:59Z

edtf('[..2016,2017]').min  #-> -Infinity
edtf('[..2016,2017]').max  #-> 1514764799999, i.e. 2017-12-31T23:59:59Z

edtf('2016-34').min        #-> 1459468800000, i.e. 2016-04-01T00:00:00Z
edtf('2016-34').max        #-> 1467331199999, i.e. 2016-06-30T23:59:59Z

edtf('2016?-02').values    #-> [2016, 1]
edtf('2016-05').values     #-> [2016, 4]

A date's min value is also used as its primitive value for numeric coercion. Because this is the case for all EDTF.js classes, comparison semantics tend to align well with common-sense expectations -- but be careful, as always, when type coercion is at play.

Unspecified, uncertain, and approximate dates

EDTF.js keeps track of qualified dates using bitmasks. If you are interested in binary yes or no, you can always convert a bitmask's value to boolean. For more fine-grained information, the Bitmask class provides a convenient interface for accessing these states:

 edtf('2016-05?').uncertain.value        #-> 63 / yes
 edtf('2016-05?').approximate.value      #-> 0  / no

 edtf('2016-05?').uncertain.is('year')   #-> 15 / yes
 edtf('2016-05?').uncertain.is('month')  #-> 48 / yes
 edtf('2016-05?').uncertain.is('day')    #-> 0  / no

 edtf('2016-?05').uncertain.is('year')   #-> 0  / no
 edtf('2016-?05').uncertain.is('month')  #-> 48 / yes

 edtf('201X-XX').unspecified.value       #-> 56 / yes
 edtf('201X-XX').unspecified.is('year')  #-> 8  / yes
 edtf('201X-XX').unspecified.is('month') #-> 48 / yes
 edtf('201X-XX').unspecified.is('day')   #-> 0  / no

Instead of year, month, and day, you can also query a string-based representation of the bitmask:

 edtf('201X-XX').unspecified.test('XXYYMMDD') #-> 0  / no
 edtf('201X-XX').unspecified.test('YYYXMMDD') #-> 8  / yes

When printing qualified dates, EDTF.js will try to find an optimal rendtition of qualification symbols. For that reason, you can use EDTF.js to normalize EDTF strings:

 edtf('?2016-?05-31').edtf   #-> 2016-05?-31
 edtf('?2016-?05~-31').edtf  #-> 2016-05%-31

Because every extended date object has a min and max value, you can always test if one date covers another one:

 edtf('2016-06/2016-09').covers(edtf('2016-08-24')) #-> true
 edtf('2016-06/2016-09').covers(edtf('2016-05-31')) #-> false

Iterable dates offer an includes() test as well which returns true only if a given date is part of the iteration. For example:

 edtf('2016-06/2016-09').includes(edtf('2016-08-24')) #-> false

August 24, 2016 is covered by the interval '2016-06/2016-09' but is not included in it, because the interval has month precision and can be iterated as:

 [...edtf('2016-06/2016-09')]
 #-> [2016-06, 2016-07, 2016-08, 2016-09]

Enumerating dates

EDTF.js dates offer iterators to help measure the duration between two dates. These iterators are dependent on a date's precision:

 edtf('2016').next()        #-> 2017
 edtf('2016-05').next()     #-> 2016-06
 edtf('2016-02-28').next()  #-> 2016-02-29
 edtf('2017-02-28').next()  #-> 2017-03-01

Careful, if your date has no precision, next will return the next second!

The generator *between() returns all the dates, by precision, between two dates; similarly, *until() returns all dates in between and includes the first of the two dates; to generate the full range inluding both dates, use *through().

 [...edtf('2016-05').between(edtf('2016-07'))]
 #-> [2016-06]

 [...edtf('2016-05').until(edtf('2016-07'))]
 #-> [2016-05, 2016-06]

 [...edtf('2016-05').through(edtf('2016-07'))]
 #-> [2016-05, 2016-06, 2016-07]

Note, that all range generators also work in reverse order:

 [...edtf('2016-07').through(edtf('2016-05'))]
 #-> [2016-07, 2016-06, 2016-05]

 [...edtf('2016-07').until(edtf('2016-05'))]
 #-> [2016-07, 2016-06]

 [...edtf('2016-07').between(edtf('2016-05'))]
 #-> [2016-06]

Iterators

The EDTF.js classes Date, Interval, List, and Set (lists model EDTF's 'multiple dates', while sets model 'one of a set') are iterable. Dates are iterable.

 [...edtf('2015/2018')]
 #-> [2015, 2016, 2017, 2018]

Note that this also works with varying precisions:

 [...edtf('2015-10/2016')]
  #-> [2015-10, 2015-11, 2015-12, 2016-01, 2016-02, 2016]

Consecutive dates in lists and sets are expanded during an iteration:

 [...edtf('{2015,2018..2020}')]
 #-> [2015, 2018, 2019, 2020]

Parser

To use EDTF.js' date parser directly, call parse() with an input string and optional parser constraints. The parser will always return plain JavaScript objects which you can pass to edtf() for conversion to extended date object, or to your own post-processing.

import { parse } from 'edtf'
parse('2016-02')
#-> { type: 'Date', level: 0, values: [2016, 1] }

As you can see, the parser output includes the compatibility level of the date parsed; the values array contains the individual date parts in a format compatible with JavaScript's Date semantics (months are a zero-based index).

Unspecified, uncertain, or approximate dates are returned as a numeric value representing a bitmask; refer to the Bitmask class for details.

parse('2016?-~02')
#-> { type: 'Date', level: 2, values: [2016, 1], uncertain: 15, approximate: 48 }

parse('20XX-02')
#-> { type: 'Date', level: 2, values: [2000, 1], unspecified: 12 }

Note that unspecified date values will always return the least possible value, e.g., '2000' for '20XX'. Note, also, that EDTF.js will not parse impossible unspecified dates. For instance, none of the following examples can be valid dates:

parse('2016-02-3X') #-> A day in February cannot start with a 3
parse('2016-2X-XX') #-> There are only 12 months
parse('2016-XX-32') #-> No month has 32 days

Intervals, Sets, and Lists will contain their parsed constituent dates in the values array:

parse('2015/2016')
#-> { type: 'Interval', level: 0, values: [{..}, {..}] }

By passing level or types constraints to the parser, you can ensure EDTF.js will accept only dates supported by your application.

parse('2016?', { level: 0 }) #-> parse error
parse('2016?', { level: 1 }) #-> ok

parse('2016?-02', { level: 1 }) #-> parse error
parse('2016?-02', { level: 2 }) #-> ok

parse('2016-21', { types: ['Date'] })           #-> parse error
parse('2016-21', { types: ['Date', 'Season'] }) #-> ok


parse('2016?', { level: 0, types: ['Date'] })   #-> parse error
parse('2016?', { level: 1, types: ['Date'] })   #-> ok

Generator

EDTF.js can generate random EDTF strings for you. Simply add randexp to your NPM dependencies and import edtf/sample to create a new iterator:

import { sample } from 'edtf/sample'
let it = sample()

it.next() #-> { value: '0097-26', done: false }
it.next() #-> { value: '0000-09-30T22:50:54-07', done: false }
...

For a finite iterator, simply pass a count:

[...sample({ count: 3 })]
#-> ['-003%', '-0070-07-31%', '[-0080-10..]']

You can also generate strings at a given compatibility level:

[...sample({ count: 3, level: 0 })]
#-> ['0305/0070-04-30', '-07', '0000/0013']

[...sample({ count: 3, level: 1 })]
#-> ['00XX', 'Y80105', '0000~']

[...sample({ count: 3, level: 2 })]
#-> ['Y1E30', '-8110S2', '{%0401}']

Note that some grammar rules at levels 1 and 2 may, potentially, generate strings at a lower level (but never higher).

Finally, at each level you can also limit the generated strings to a given type (you must specify a level for this to work):

[...sample({ count: 3, level: 2, type: 'Decade' })]
#-> ['003', '030~', '000']

Credits

The EDTF.js parser is based on the awesome nearley parser generator.

The EDTF.js sample generator uses the ingenious randexp.

License

EDTF.js is licensed under the terms of the BSD-2-Clause license.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].