Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

calcdate

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

calcdate

Evalate a date expression calc`now + 3 days`)!

  • 1.0.2
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
21
increased by75%
Maintainers
1
Weekly downloads
 
Created
Source

Date Calculator

TL;DR

import { calc } from "calcdate";
// import { datefnscalc } from "calcdate"; // with date-fns
// import { luxoncalc } from "calcdate"; // with luxon
// import { luxonsimplecalc } from "calcdate"; // with luxon
// import { momentcalc } from "calcdate"; // with moment

calc`now + 3 days + 5 hours`;

Install

yarn add calcdate
npm i calcdate

git repo

Find source code on git

Dependencies

There are no direct dependencies!

We recommend using this lib with your favourite date-time lib, use the bindings provided.

importrequired dependencyimport
datefnsCalculatordate-fnsimport { datefnsCalculator } from "calcdate";
luxonCalculatorluxonimport { luxonCalculator } from "calcdate";
simpleluxonCalculatorluxonimport { simpleluxonCalculator } from "calcdate";
momentCalculatormomentimport { momentCalculator } from "calcdate";

Examples

TBD

Api docs

Calling the calculator is easy:

calculator`<expression>`

where calculator is your imported calculator and expression is what you want calculated. The following section outlines what is permitted in expressions.

Types

Being a date-calculator the primary types used in expressions are date-times and the differences in between them (being durations and intervals).

Date-Time

A datetime is an exact point in time. It can be expressed as a ISO-8601-date-time (see w3 and wikipedia). Simply put, use the format YYYY-MM-DDThh:mm:ss.sss ("extended format"). All parts (but the year) are optional. If a part is not present, its default is used (0 for times and 1 for dates; 2019 = 2019-01-01T00:00:00.000). You can also use the normal form YYYYMMDD hhmmsssss. You can also add a time-zone using YYYY-MM-DDThh:mm:ss.sss+-hh:mm or YYYY-MM-DDThh:mm:ss.sssZ for utc.

Examples

2009-12T12:34 // Tue Dec 01 2009 12:34:00 GMT+1100 (Australian Eastern Daylight Time)
2009 // Thu Jan 01 2009 11:00:00 GMT+1100 (Australian Eastern Daylight Time)
2009-05-19 // Tue May 19 2009 10:00:00 GMT+1000 (Australian Eastern Standard Time)
 2009-05-19  // Tue May 19 2009 00:00:00 GMT+1000 (Australian Eastern Standard Time)
2009-05 // Fri May 01 2009 10:00:00 GMT+1000 (Australian Eastern Standard Time)
2009-001 // Thu Jan 01 2009 00:00:00 GMT+1100 (Australian Eastern Daylight Time)
2009-05-19 // Tue May 19 2009 10:00:00 GMT+1000 (Australian Eastern Standard Time)
2009-05-19T00:00 // Tue May 19 2009 00:00:00 GMT+1000 (Australian Eastern Standard Time)
2009-05-19T14:31 // Tue May 19 2009 14:31:00 GMT+1000 (Australian Eastern Standard Time)
2009-05-19T14:39:22 // Tue May 19 2009 14:39:22 GMT+1000 (Australian Eastern Standard Time)
2009-05-19T14:39Z // Wed May 20 2009 00:39:00 GMT+1000 (Australian Eastern Standard Time)
2009-05-19T14:39:22-06:00 // Wed May 20 2009 06:39:22 GMT+1000 (Australian Eastern Standard Time)
2009-05-19T14:39:22+0600 // Tue May 19 2009 18:39:22 GMT+1000 (Australian Eastern Standard Time)
2007-04-06T00:00 // Fri Apr 06 2007 00:00:00 GMT+1000 (Australian Eastern Standard Time)
200912T1234 // Tue Dec 01 2009 12:34:00 GMT+1100 (Australian Eastern Daylight Time)
2009 // Thu Jan 01 2009 11:00:00 GMT+1100 (Australian Eastern Daylight Time)
20090519 // Tue May 19 2009 10:00:00 GMT+1000 (Australian Eastern Standard Time)
 20090519  // Tue May 19 2009 00:00:00 GMT+1000 (Australian Eastern Standard Time)
200905 // Fri May 01 2009 10:00:00 GMT+1000 (Australian Eastern Standard Time)
2009001 // Thu Jan 01 2009 00:00:00 GMT+1100 (Australian Eastern Daylight Time)
20090519 // Tue May 19 2009 10:00:00 GMT+1000 (Australian Eastern Standard Time)
20090519 0000 // Tue May 19 2009 00:00:00 GMT+1000 (Australian Eastern Standard Time)
20090519 1431 // Tue May 19 2009 14:31:00 GMT+1000 (Australian Eastern Standard Time)
20090519 143922 // Tue May 19 2009 14:39:22 GMT+1000 (Australian Eastern Standard Time)
20090519 1439Z // Wed May 20 2009 00:39:00 GMT+1000 (Australian Eastern Standard Time)
20090519 143922-06:00 // Wed May 20 2009 06:39:22 GMT+1000 (Australian Eastern Standard Time)
20090519 143922+0600 // Tue May 19 2009 18:39:22 GMT+1000 (Australian Eastern Standard Time)
20070406 0000 // Fri Apr 06 2007 00:00:00 GMT+1000 (Australian Eastern Standard Time)
Binding depended

Each parser may provide its own parsing-from-string mechanism using {....}-notation.

Duration

A duration expresses a time-period, aka "how long". A duration may be expressed using an iso-duration or the short-hand notation as follows.

Note Without a start- or end-date (see Interval) the units in a duration are hard to compare. For example, a duration of 1 month can be either anywhere from 28 days (2019-02-01/2019-03-01) to 31 days (2019-12-01/2020-01-01). Therefore some operations might be unsafe to perform, such as 1 month / 1 days (interpreted as "how many days fit into a month"). Depending on the bindings a loss of information and accuracy may occurs. (Also see luxon-notes.)

Short-hand notation

Each short-hand notation has three different forms which can be used synonymously: spelled-out singular, plural and shorthand (which makes it a shorthand-shorthand, I guess?). Whitespace is irrelevant. So 1 day, 1 days, 1 d, 1day, 1days, 1d all mean "1 day".

singularpluralshorthandexamplenotes
millisecondmillisecondsms5ms
secondsecondss1 salias: ss, sec
minuteminutesm1 minalias: min, mm, Tm
hourhoursh1 halias: hh
daydaysd1 d
weekdayweekdayswd1 wd
weekweeksw1 w
fortnightfortnightsfn1 fn= two weeks
monthmonthsM1 Malias: l, L
quarterquartersq1 q
yearyearsy1 yalias: a

In general these notation are caseinsensetive and can be pluralized, with the following exceptions:

  • M, months, not to be confused with m, minutes
  • m, minutes, see above
  • ms, milliseconds, not to be confused Ms, plural of M, months
Iso-Notation

Durations may be written in iso as PnYnMnDTnHnMnS (where n is a any number). From wikipedia:

P is the duration designator (for period) placed at the start of the duration representation.

Y is the year designator that follows the value for the number of years.

M is the month designator that follows the value for the number of months.

W is the week designator that follows the value for the number of weeks.

D is the day designator that follows the value for the number of days.

T is the time designator that precedes the time components of the representation.

H is the hour designator that follows the value for the number of hours.

M is the minute designator that follows the value for the number of minutes.

S is the second designator that follows the value for the number of seconds.

"+" and "-" are allowed for the entire expression and parts individually. Thus +P1Y (1 year), -P1Y (minus 1 year) and -P-1y (minus minus 1 year = 1 year) are valid.

Examples

// Iso extended form, any number of digits allowed
P1Y2M3DT4H05M600S // 1 year, 2 months, 3 days, 4 hours, 5 minutes, 600 seconds
PT3H5M // 3 hours, 5 minus
P1Y // 1 year
P-1Y1M // -1 year, 1 month -> effectively 11 month
P1DT-30S // 1 day, -30 seconds
P1Y // 1 year
P2M // 2 month
P3D // 3 days
PT4H // 4 hours
PT05M // 5 minutes; padding with 0s is allowed
PT600S // 600 seconds; more than 2 digits are allowed
P1y2m3dT4h5m6s // 1 year, 2 months, 3 days, 4 hours, 5 minutes, 6 seconds; case-insensitive
// Iso-short-form, 4 digits for year, 2 for each other component
P00021015T103020 // 2 years, 10 months, 15 days, 10 hours, 30 minutes, 20 seconds
P00021015 // 2 years, 10 months, 15 days
P0002-10-15T10:30:20 // 2 years, 10 months, 15 days, 10 hours, 30 minutes, 20 seconds
P0002-10-15 // 2 years, 10 months, 15 days
+P00021015T103020
+P00021015
+P0002-10-15T10:30:20
+P0002-10-15
-P00021015T103020 // -2 years, -10 months, -15 days, -10 hours, -30 minutes, -20 seconds
-P00021015
-P0002-10-15T10:30:20
-P0002-10-15
P+00021015T103020
P0002-1015
P0002--10-15T10:+30:20 // 2 years, -10 month, 15 days, 10 hours, 30 minutes, 20 seconds
P0002-10--15 // 2 years, 10 months, -15 days
-P0002-10--15 // -2 years, -10 months, --15 days = 15 days

Note The time designator is mandatory when denoting times, so P1H would be invalid while PT1H would be fine.

Binding depended

Each parser may provide its own parsing-from-string mechanism using [....]-notation.

Example {["2012 juillet", "YYYY MMM", "fr"]} + moment-bindings {["12/11/2000", "MM/dd/yyyy"]} + luxon-bindings

Interval

An interval expresses the duration between a start- and an end-date. It can be understood as a box for two dates and the duration in between. Hence an interval can be constructed by providing a start- and an end-date, or a start-date and a duration (end-date = start-date + duration), or an end-date and a duration (start-date = end-date - duration).

Intervals may be written as Iso-intervals. The three options are:

  • Iso-Date-Time/Iso-Date-Time
  • Iso-Date-Time/Iso-Duration
  • Iso-Duration/Iso-Date-Time

Iso-Date-Time and Iso-Duration use the same parsing mechanisms described above and hence allow the notations outlined above.

Examples

2009-12T12:34/20090519 143922+0600
P1Y2M3DT4H05M600S/2009-12T12:34
2009-12T12:34/P1Y2M3DT4H05M600S

For more examples, combine any date-time example with another date-time-example or any duration-example.

Note Note all bindings support intervals!

Binding depended

Each parser may provide its own parsing-from-string mechanism using ~....~-notation.

Unitless

You may enter "bare" numbers, such as 2. They are treated as such. This might be useful for scalar operations such as 2 * 2d (= 4 d).

Examples

1
0
100
-10
-5
0
-09
1 + 1 // 2
10 + 15 - 25 // 0
3 * 5 // 15
3d * -1 // -3d
88 / 11 // 8
1 / 2 // 0.5
2.5 * 2 // 5
2d * 5 // 10d
7 - 8 // -1

Warning

Longer number are going to be interpreted as Iso-dates and thus might fail parsing! If you need to use numbers with more digits than 3, consider using interpolation instead.

2000 // 2000-01-01T00:00:00
`${2000}` // 'number' 2000
Custom / Existing values

Use interpolation to insert custom type:

calculator`${any_thing_here}`

This allows you to use your existing Date/luxon-Date-Times/moments/luxon-Intervals/... directly in the calculator.

The bindings define what custom types are supported!

That means, if you interpolate a datefnsCalculator with a luxon-Date-Time it will likely throw an error, as date-fns can't make sense of the luxon-object.

Examples

import DateTime from 'luxon/src/datetime.js';

luxonCalculator`${DateTime.fromSQL(...)} + ${{ hours: 3, minutes: 13 }}`
import moment from 'moment';

luxonCalculator`${moment()} + ${moment.duration(2, 'seconds');}`

Operations

Add ("plus", "+")

Adds two "things". The binding implementation defines what "add" means and if it has a meaning at all!

Example 1d + 1h

BindingType aType bResultNotes
simpleDateDateError
simpleDateDurationTimestamp (Int)Loses accuracy due to conversion
simpleDurationDateTimestamp (Int)Loses accuracy due to conversion
simpleDurationDurationDuration
simpleUnitlessUnitlessUnitless
simpleUnitless*Error
simpleanyanyanyCast to date/duration; else error
----------------------------------------------------------------------
date-fnsDateDateError
date-fnsDateDurationDate
date-fnsDurationDateDate
date-fnsDurationDurationDuration
date-fnsUnitlessUnitlessUnitless
date-fns**Error
----------------------------------------------------------------------
luxonDateDateError
luxonDateDurationDate
luxonDateIntervalDateDate + Duration(Interval)
luxonDurationDateDate
luxonDurationDurationDuration
luxonDurationIntervalIntervalShifts interval by duration
luxonIntervalDateIntervalInterval extended to Date
luxonIntervalDurationIntervalShifts interval by duration
luxonIntervalIntervalIntervalUnion of intervals
luxonUnitlessUnitlessUnitless
luxon**Error
----------------------------------------------------------------------
simple-luxonDateDateError
simple-luxonDateDurationDate
simple-luxonDurationDateDate
simple-luxonDurationDurationDuration
simple-luxonUnitlessUnitlessUnitless
simple-luxon**Error
----------------------------------------------------------------------
momentDateDateError
momentDateDurationDate
momentDurationDateDate
momentDurationDurationDuration
momentUnitlessUnitlessUnitless
moment**Error
Subtract ("minus", "-")

Subtracts two "things" - binding defines the meaning. Can also be interpreted as a "until", as in "2019 - 2020" yielding an interval between those in some implementations.

Example 1d - 5h

BindingType aType bResultNotes
simpleDateDateNumberDifference of times in milliseconds
simpleDateDurationTimestamp (Int)Loses accuracy due to conversion
simpleDurationDateTimestamp (Int)Same as Date - Duration
simpleDurationDurationDuration
simpleUnitlessUnitlessUnitless
simple***Cast to date/duration; else error
----------------------------------------------------------------------
date-fnsDateDateDurationDifference between the two dates
date-fnsDateDurationDate
date-fnsDurationDateDateSame as Date - Duration
date-fnsDurationDurationDuration
date-fnsUnitlessUnitlessUnitless
date-fns**Error
----------------------------------------------------------------------
luxonDateDateIntervalInterval(start: Date a, end: Date b)
luxonDateDurationDatePuts date into the past by duration
luxonDateIntervalDateDate - Duration(Interval)
luxonDurationDateDateSame as Date - Duration
luxonDurationDurationDuration
luxonDurationIntervalIntervalSame as Interval - Duration
luxonIntervalDateIntervalLimits Interval by Date
luxonIntervalDurationIntervalShifts interval by duration
luxonIntervalIntervalIntervalIntersection or Interval between both
luxonUnitlessUnitlessUnitless
luxon**Error
----------------------------------------------------------------------
simple-luxonDateDateError
simple-luxonDateDurationDate
simple-luxonDurationDateError
simple-luxonDurationDurationDuration
simple-luxonUnitlessUnitlessUnitless
simple-luxon**Error
----------------------------------------------------------------------
momentDateDateError
momentDateDurationDate
momentDurationDateError
momentDurationDurationDuration
momentUnitlessUnitlessUnitless
moment**Error
Multiply ("*")

Example 5d * 3

BindingType aType bResultNotes
simpleDurationUnitlessDuration
simpleUnitlessDurationDuration
simpleUnitlessUnitlessUnitless
simple**Error
----------------------------------------------------------------------
date-fnsDurationUnitlessDuration
date-fnsUnitlessDurationDuration
date-fnsUnitlessUnitlessUnitless
date-fns**Error
----------------------------------------------------------------------
luxonUnitlessIntervalIntervalShifts Interval.start back
luxonIntervalUnitlessIntervalShifts Interval.start forward
luxonDurationUnitlessDuration
luxonUnitlessDurationDuration
luxonUnitlessUnitlessUnitless
luxon**Error
----------------------------------------------------------------------
simple-luxonDurationUnitlessDuration
simple-luxonUnitlessDurationDuration
simple-luxonUnitlessUnitlessUnitless
simple-luxon**Error
----------------------------------------------------------------------
momentDurationUnitlessDuration
momentUnitlessDurationDuration
momentUnitlessUnitlessUnitless
moment**Error
Divide ("/")

Divide things Split "things" into n equal parts.

BindingType aType bResultNotes
simpleDurationUnitlessDuration
simpleUnitlessUnitlessUnitless
simple**Error
----------------------------------------------------------------------
date-fnsDurationUnitlessDuration
date-fnsUnitlessDurationDurationInverts each duration part
date-fnsDurationDurationUnitlessLoses accuracy due to conversion
date-fnsUnitlessUnitlessUnitless
date-fns**Error
----------------------------------------------------------------------
luxonDateDateIntervalInterval(start: Date a, end: Date b)
luxonIntervalUnitlessDurationDuration c fits Unitless times into Interval
luxonIntervalDurationUnitlessDuration b fits Unitless times into Interval
luxonIntervalUnitlessDuration
luxonDurationDurationUnitlessLoses accuracy due to conversion
luxonDurationUnitlessDuration
luxonUnitlessDurationDurationInverts each duration part
luxonInterval1uIntervalSwops the interval (start->end, end->start)
luxonUnitlessUnitlessUnitless
luxon**Error
----------------------------------------------------------------------
simple-luxonDurationUnitlessDuration
simple-luxonUnitlessUnitlessUnitless
simple-luxon**Error
----------------------------------------------------------------------
momentDurationUnitlessDuration
momentUnitlessUnitlessUnitless
moment**Error

Other

Additionally ( + ) is permitted to group any expression and give its evaluation priority. Nesting is allowed. (Or expressed differently: (...) works as you would expect!)

Example ( 1d - 5h ) * 2

On top there is the keywords "now" (alias "today", both case-insensitive), which creates a Date-Time with the value of "now".

Operator precedence

PriorityExpressionExample
1Interval2009-12T12:34/20090519 143922+0600
2now, todaynow, now(), Today
2Custom Constructor{....}, [....], ~....~
2DurationP1D
2Date-Time2019
2Duration shorthand1 day
3Unitless1
4Brackets( .... )
5Multiply.. * ..
5Divide.. / ..
6Plus.. + ..
6Minus.. - ..

This order insures that 2019/P1D is an interval while 2019 / P1D means "divide 2019 by P1D". It also care to interpret 2019-08 as "August of 2019", while 2019 - 08 means "The date 2019 minus unitless 8".

Bring your own lib date-lib

The date-calculator itself is not bound to any library. You can 'teach' it to work with any library by implementing 'bindings'. To create a new calculator type import the calculatorFactory (aka "parser") and call it with your bindings (see below).

import calculatorFactory from "./calcdate";

const yourCalculator = calculatorFactory({
    makeDate: ...,
    makeDuration: ...,
    makeInterval: ...,
    add: ...,
    subtract: ...,
    multiply: ...,
    divide: ...
});

You will need to implement the following methods:

makeDate

You function to create a new date. It can take the following two signatures:

  • makeDate(date: Native_Date, { type: calculatorFactory.NATIVEDATE }) : YourDateType
  • makeDate(date: any, { type: calculatorFactory.DATEXPRESION }) : YourDateType, created by calculator{any_string}

makeDuration

  • makeDuration({ milliseconds, seconds, minutes, hours, days, weekdays, weeks, months, years } : { milliseoncds: Number?, seconds: Number?, minutes: Number?, hours: Number?, days: Number?, weekdays: Number?, weeks: Number?, months: Number?, years: Number? }, { type: calculatorFactory.DURATIONOBJECT }) : YourDurationType
  • makeDuration(duration: any, { type: calculatorFactory.DURATIONEXPRESSION }) : YourDurationType, created by calculator[any_string]

makeInterval

  • makeInterval([from: YourDateType, to: YourDateType], { type: calculatorFactory.INTERVALOBJECT }) : YourIntervalType
  • makeInterval([from: YourDateType, to: YourDurationType], { type: calculatorFactory.INTERVALOBJECT }) : YourIntervalType
  • makeInterval([from: YourDurationType, to: YourDateType], { type: calculatorFactory.INTERVALOBJECT }) : YourIntervalType
  • makeInterval(interval: any, { type: calculatorFactory.DURATIONEXPRESSION }) : INTERVALEXPRESION, created by calculator~any_string~

add

add(a, b) : c, where a, b, c are YourDateType | YourDurationType | YourIntervalType | { unitless: Number } | any

Examples

  • now + 1 day - add(a: YourDateType, b: YourDurationType)
  • now + 1 - add(a: YourDateType, { unitless: 1 } : { unitless: Number })
  • P3Y6M4DT12H30M5S + 1 day - add(a: YourDurationType, b: YourDurationType)
  • 2019-01-01/2019-01-02 + now - add(a: YourIntervalType, b: YourDateType)
  • ${new RegExp("foobar")} + ~hello world~ - add(a: any, b: YourIntervalType)
  • 1d + 1h + 1m - add(add(a: YourDurationType, b: YourDurationType), x: YourDurationType)

Note You should throw (helpful) errors when you can't make sense of the arguments. For example adding up two duration makes sense - adding up two dates might not! The same applies for all the operations!

subtract

subtract(a, b) : c, where a, b, c are YourDateType | YourDurationType | YourIntervalType | { unitless: Number } | any

  • now - 1 day - subtract(a: YourDateType, b: YourDurationType)
  • now - 1 - subtract(a: YourDateType, { unitless: 1 } : { unitless: Number })
  • P3Y6M4DT12H30M5S - 1 day - subtract(a: YourDurationType, b: YourDurationType)
  • 2019-01-01/2019-01-02 - now - subtract(a: YourIntervalType, b: YourDateType)
  • ${new RegExp("foobar")} - ~hello world~ - subtract(a: any, b: YourIntervalType)
  • 1d - 1h - 1m - subtract(add(a: YourDurationType, b: YourDurationType), x: YourDurationType)

Note You should throw (helpful) errors when you can't make sense of the arguments. For example substracting a duration from a date makes sense - substracting a date from a duration might not!

multiply

multiply(a, b) : c, where a, b, c are YourDateType | YourDurationType | YourIntervalType | { unitless: Number } | any

  • now * 1 day - multiply(a: YourDateType, b: YourDurationType)
  • now * 1 - multiply(a: YourDateType, { unitless: 1 } : { unitless: Number })
  • P3Y6M4DT12H30M5S * 1 day - multiply(a: YourDurationType, b: YourDurationType)
  • 2019-01-01/2019-01-02 * now - multiply(a: YourIntervalType, b: YourDateType)
  • ${new RegExp("foobar")} * ~hello world~ - multiply(a: any, b: YourIntervalType)
  • 1d * 1h * 1m - multiply(add(a: YourDurationType, b: YourDurationType), x: YourDurationType)

Note You should throw (helpful) errors when you can't make sense of the arguments. For example multiplying a duration by a unitless makes sense - multiplying a duration by another might not!

divide

divide(a, b) : c, where a, b, c are YourDateType | YourDurationType | YourIntervalType | { unitless: Number } | any

  • now / 1 day - divide(a: YourDateType, b: YourDurationType)
  • now / 1 - divide(a: YourDateType, { unitless: 1 } : { unitless: Number })
  • P3Y6M4DT12H30M5S / 1 day - divide(a: YourDurationType, b: YourDurationType)
  • 2019-01-01/2019-01-02 / now - divide(a: YourIntervalType, b: YourDateType)
  • ${new RegExp("foobar")} / ~hello world~ - divide(a: any, b: YourIntervalType)
  • 1d / 1h / 1m - divide(add(a: YourDurationType, b: YourDurationType), x: YourDurationType)

Note You should throw (helpful) errors when you can't make sense of the arguments. For example dividing a duration by another might make sense (could be "how many durations x fit into duration y") - dividing a unitless by an interval might not!

Keywords

FAQs

Package last updated on 08 Sep 2019

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc