logoNavigate back to the homepage
ARCHETYPER

Date-fns

KNSI

·

 
July 28th, 2020 · 4 min read

Date-fns

Overview

Ever written a method to get the time of day in GMT? or have wondered if i need to convert the time to UTC? Most applications have some common date-time manipulations which are often repeated and after sometime become very difficult to maintain. To add upon this when internationalizing an application Date and Time manipulations are one of the key challenges you will face. So much so that you will lose track of time ammending it all :wink:.

Tick Tock, Tick Tock! Fear not for we have you covered. date-fns is one such package that provides simple helper functions for date-time manipulation. With internationalization options available you can always leverage it and go international quickly.

Highlights

Getting started with date-fns is pretty easy. It can be installed by running the command npm install date-fns. All helper methods can either be imported through the main package or you can install only the required submodule. One such is fp which has two copies of methods with and without format options.

1/* Babel or ES6 */
2import {methodName} from "date-fns";
3
4/* Node or requireJs */
5
6const methodName = require("date-fns").methodName;

It deserves extra brownie points as it provides typescript support without any additional packages and helps developers in not having to maintain another dev dependency. Its package structure promotes tree-shaking which helps in overall application size and hence we suggest to install the main module as only the requried methods would get bundled in. The developer has to handle one of three types namely:

  • Interval: It is a simple representation of the time interval between two date instances. It is purely used in methods which operate on time spans.

  • Locale: Represents data of the desired country which is used in formatting date.

  • Duration: The main difference between a standard Date object and the Duration object is that the latter is devoid of any Locale information. A Duration can consist partially any of the following:

    1{
    2 years, months, weeks, days, hours, minutes, seconds
    3}

Most helpers provided can be categorized into the following four categories based on the dimension of date-time they help simplify.

Time helpers

These helpers mainly focus on manipulating and validating predicates on aspects of time such as seconds, minutes, hours. It provides useful utilities on time spans through the Interval type. The following table gives a nice overview on some such methods:

PurposeNameInputOutput
Check if two intervals are overlappingareIntervalsOverlappinginterval1: Interval, interval2: IntervalBoolean
Get number of days in an intervaleachDayOfIntervalinterval: IntervalArray<Date>
Check if date lies between an intervalisWithinIntervaldate: Date, interval : IntervalBoolean
Get the Date from an UNIX timestampfromUnixTimeNumberDate
Get the timestamp in millisecondsgetTimeDateNumber
Get the timestamp in secondsgetUnixTimeDateNumber
Get the time at the start of the minutestartOfMinuteDate or NumberDate
Check if a date has the same minute as the current dateisThisMinuteDate or NumberDate
Round off the time to the nearest minuteroundToNearestMinuteDate or NumberDate

some examples to help familiarise you with the above functions are:

1const interval1 = {
2 start: new Date(2014, 0, 10), // 10th Jan 2014
3 end: new Date(2014, 11, 21) // 21st Dec 2014
4};
5
6const interval2 = {
7 start: new Date(2014, 6, 10), // 10th July 2014
8 end: new Date(2015, 0, 10) // 10th Jan 2015
9};
10
11const interval3 = {
12 start: new Date(2015, 6, 10), // 10th July 2015
13 end: new Date(2020, 11, 10) // 10th Dec 2020
14};
15
16const interval4 = {
17 start: new Date(2015, 11, 10), // 10th Dec 2015
18 end: new Date(2015, 11, 10) // 10th Dec 2015
19};
20
21/* areIntervalsOverlapping */
22console.log(areIntervalsOverlapping(interval1, interval2)); // => true
23console.log(areIntervalsOverlapping(interval1, interval3)); // => false
24
25/* eachDayOfInterval */
26console.log(eachDayOfInterval(interval4)) // => [Sun Jan 10 2016 00:00:00 GMT+0530 (India Standard Time)]
27
28/* isWithinInterval */
29console.log(isWithinInterval(Date.now(), interval1)); // => false
30console.log(isWithinInterval(Date.now(), interval3)); // => true
31
32/* fromUnixTime */
33console.log(fromUnixTime(1595157314)); // => Sun Jul 19 2020 16:45:14 GMT+0530 (India Standard Time)
34
35/* getTime */
36console.log(getTime(Date.now())); // => 1595157440507
37
38/* getUnixTime */
39console.log(getUnixTime(Date.now())); // => 1595157492
  • startOfMinute
1console.log(startOfMinute(Date.now())); // => Sun Jul 19 2020 17:02:00 GMT+0530 (India Standard Time)
  • isThisMinute
1console.log(isThisMinute(Date.now())); // => true
2console.log(isThisMinute(new Date(2020, 6, 10, 17, 4))); // => false
  • roundToNearestMinute: rounds off the time to the start of minute that the given date is closest to. We can change this by providing the nearestTo option which can be between 1 - 30. If its equally distant to both previous and next interval, then it rounds up.
1console.log(roundToNearestMinutes(Date.now())); // => Sun Jul 19 2020 17:12:00 GMT+0530 (India Standard Time)
2console.log(roundToNearestMinutes(Date.now(), {nearestTo: 15})); // => // => Sun Jul 19 2020 17:15:00 GMT+0530 (India Standard Time)
3console.log(roundToNearestMinutes(new Date(2020, 6, 10, 17, 13), {nearestTo: 8})); // => Fri Jul 10 2020 17:16:00 GMT+0530 (India Standard Time)
4console.log(roundToNearestMinutes(new Date(2020, 6, 10, 17, 11), {nearestTo: 8})); // => Fri Jul 10 2020 17:08:00 GMT+0530 (India Standard Time)
5/* here 17:12 is equidistant from 17:8 and 17:16, the method rounds it off to 16 */
6console.log(roundToNearestMinutes(new Date(2020, 6, 10, 17, 12), {nearestTo: 8})); // => Sun Jul 19 2020 17:16:00 GMT+0530 (India Standard Time)

lets see how we can apply our time helpers to some real life use cases:

  • calculate if a booking has come within time of registration:
1const checkIfBookingIsValid = timeOfBooking => isWithinInterval(timeOfBooking, {
2 start: startTimeOfRegistration,
3 end: endTimeOfRegistration
4});
  • specify when next the scheduled job is to run:
1const getNextStartTimeOfJob = (lastExecutionTime, {days, hours, minutes, seconds}) => add(lastExecutionTime, {days, hours, minutes, seconds})

Date helpers

While the time helpers are useful in handling the micro-aspects, the Day helpers are used to manipulate the macro-aspects such as Days, Weeks, Quarters, Years, Decades. These helpers simplify our applications where we might need to calculate if the user is above an age threshold, group data and perform a bulk operation. More such highlighted functions are as shown below:

PurposeNameInputOutput
get number of full weeks between two datesdifferenceInWeeksdate1: Date or Number, date2: Date or NumberDate
get number of calendar or partial weeks between two datesdifferenceInCalendarWeeksdate1: Date or Number, date2: Date or NumberDate
get the week index or number in year to which the date belongsgetWeekdate: Date or NumberDate
check if the week belongs to the same as currentisThisWeekdate: Date or NumberDate
get the date corresponding to the start of week as currentstartOfWeekdate: Date or NumberDate
get the date corresponding to the end of week as currentendOfWeekdate: Date or NumberDate
get a list of all weekends in the montheachWeekendOfYeardate: Date or NumberArray<Date>

Most methods explained for weeks above are extended to months, quarters and years. such as differenceInMonths, differenceInCalendarMonths, isSameMonth, isSameYear, setMonth, setYear, eachWeekendOfMonth,etc.

some examples to help familiarise you with the above functions are:

  • differenceInWeeks: The left date must be the latest date, else the method returns a -1.
1console.log(differenceInWeeks(new Date(2015, 11, 22), new Date(2015, 11, 10))); // => 1
  • differenceInCalendarWeeks:
1console.log(differenceInCalendarWeeks(new Date(2015, 11, 25), new Date(2015, 11, 10))); // => 2
  • getWeek: We can also specify what day the week starts on through the weekStartsOn flag. It ranges from 0-6 with 0 as Sunday. The week number starts from 1.
1console.log(getWeek(Date.now())); // => 30
2console.log(getWeek(Date.now(), {weekStartsOn: 1})); // => 29
  • isThisWeek
1console.log(isThisWeek(Date.now())); // => true
  • eachWeekendOfYear
1console.log(eachWeekendOfYear(Date.now())); // => [Sat Jan 04 2020 00:00:00 GMT+0530 (India Standard Time)...]

Common Helpers and use cases

Simple operations

Apart from the above we can add, subtract and get the difference on two Date objects and their naming is extremely intuitive as illustrated below:

PurposeNameInputOutputAcceptables values for X
Get all {X} in intervaleach{X}OfIntervalinterval: IntervalArray<Date>Day,Month,Year,Quarter,Week,Weekend
Add {X} time to dateadd{X}date: Date, amount:NumberDateMilliseconds, Seconds, Minutes, Hours, Days, Weeks, Months, Quarters, years
subtract {X} time to datesub{X}date: Date, amount:NumberDateMilliseconds, Seconds, Minutes, Hours, Days, Weeks, Months, Quarters, years
set {X} time on given dateset{X}date: Date, amount:NumberDateMilliseconds, Seconds, Minutes, Hours, Days, Weeks, Months, Quarters, years
get {X} time of given dateadd{X}date: Date, amount:NumberDateMilliseconds, Seconds, Minutes, Hours, Days, Weeks, Months, Quarters, years
check if the given input is a valid dateisValiddate: Dateboolean-
Get the latest of given datesmaxdates: Array<Date>Date-
Get the earliest of given datesmindates: Array<Date>Date-
convert provided parameter to datetoDatedates: Date or NumberDate-

some examples detailing the above methods are:

1addQuarters(new Date("2020-01-01"), 3); // => Thu Oct 01 2020 05:30:00 GMT+0530 (India Standard Time)
2max([new Date(2020, 2, 22), new Date(2020, 2, 2)]); // => Sun Mar 22 2020 00:00:00 GMT+0530 (India Standard Time)

formatting date time

One of the most common ask is to format date and time in a particular format that suits the application requirement. This also means that it is the most self managed method which may go out hand if the format were to change. the Date-fns format util has a wide variety of formatting options and the signature of the method is so easy that we can change the format with the update of a string.

1format(date, formatOptions)

While the formatOptions list is extensive some examples to cover some of most commonly used ones are:

1const date = new Date("2020-07-19 18:32:00");
2
3format(date, "yy-MM-dd") => 20-07-19
4format(date, "dd/MM/yyyy") => 19/07/2020
5format(date, "do LLL yyyy") => 19th Jul 2020
6format(date, "HH:mm:ss") => 18:32:00
7format(new Date("2020-07-19 00:32:00"), "hh:mm:ss a") => 12:32:00 PM
8format(new Date("2020-07-19 12:00:00), "hh:mm:ss b") => 12:00:00 noon
9format(new Date("2020-07-19 00:32:00"), "kk:mm:ss") => 24:32:00
10format(new Date("2020-07-19 00:32:00"), "KK:mm:ss") => 00:32:00
11format(date, "dd/MM/yyy hh:mm:ss x") => 19/07/2020 12:32:00 +0530
12format(date, "dd/MM/yyy hh:mm:ss O) => 19/07/2020 06:32:00 GMT+5:30
13
14//second timestamp
15format(date, "t") => 1595163720
16//millisecond timestamp
17format(date, "T") => 1595163720000
18
19/* some really interesting built-ins */
20
21//Localized date
22format(date, "P") => 07/19/2020
23format(date, "PPPP") => Sunday, July 19th, 2020
24format(date, "PPPP G") => Sunday, July 19th, 2020 AD
25
26//Localized time
27format(date, "p") => 6:32 PM
28format(date, "pppp") => 6:32:00 PM GMT+05:30
29
30// Localized date and time
31format(date, "PPPPpppp") => Sunday, July 19th, 2020 at 6:32:00 PM GMT+05:30

if you do not require such minute locale options while formatting then you can use lightFormat. Its called the same way of format and does not consider certain options which relate to Era, Quarter, Extended year.

1lightFormat(new Date("2020-07-19 18:32:00"), "dd/MM/yy") => 19/07/20
2lightFormat(new Date("2020-07-19 18:32:00"), "p") => throws RangeError

Date-fns allows us to format intervals and durations through its formatDistance and formatDuration methods. The main difference between them is that the former formats the date in reference to a given date and the latter simply represents the duration in words. formatDistanceToNow is the same as formatDistance with the reference set to now()

1formatDistance(Date.now(), sub(Date.now(), {hours: 4})); // => About 4 hours
2formatDistance(Date.now(), sub(Date.now(), {hours: 4}), {addSuffix: true}); // => in about 4 hours
3formatDistance(Date.now(), sub(Date.now(), {seconds: 4}), { addSuffix: true, includeSeconds: true}); // => in less than 5 seconds
4
5formatDistanceStrict(Date.now(), sub(Date.now(), {seconds: 4}), { addSuffix: true, includeSeconds: true }); // => in 4 seconds
6
7formatDuration({days: 4, hours: 3}); // => 4 days 3 hours
8formatDuration({days: 4, hours: 3}, {delimiter: "|"}); // => 4 days|3 hours
9formatDuration({days: 4, hours: 3, months: 44}, {delimiter: "|", format: [ "months", "hours"]}) // => 44 months|3 hours

ISO format

The ISO date format is a standard way of representing date defined as YYYY-MM-DDTHH:mm:ss.sssZ whereas UTC is a primary time standard. The ISO format always has the time standard as UTC and is signified by the Z at the end. date-fns has methods to achieve all above functionalities in the ISO format. some are illustarted below:

PurposeNameInputOutput
get the date in ISO formatformatISOdate: Datestring
get the time interval in ISO formatformatISODurationduration: Durationstring
convert ISO date to a date objectparseISOISOString: stringDate

Internationalization

Most developers and apps face the issue of internationalization as it is not at the top of the TODO list. Date-fns has an accompanying module called date-fns-tz for handling timezone formatting and conversions. This helper module can be installed by running npm install date-fns-tz and also provides typescipt support.

converting zoned time to UTC and ISO

If your system has the date time stored in a particular timezone and you have to convert it to UTC then the zonedTimeToUTC method is the way to go. It takes a date and the timezone in the IANA format of the input date time.

1zonedTimeToUTC(date, originTimeZone)
1zonedTimeToUtc(new Date("Sun Jul 19 2020 14:41:43"), "America/Los_Angeles"); // => Mon Jul 20 2020 03:11:43 GMT+0530 (India Standard Time)
2zonedTimeToUtc(new Date("Sun Jul 19 2020 14:41:43"), "Asia/Bangkok"); // => Sun Jul 19 2020 13:11:43 GMT+0530 (India Standard Time)

converting UTC to zoned time

Consider the use case where you are to send notifications or mails to your users, It is a hassle for them if you send the time and date details in UTC and they have to convert it to their local time. Hence another common use case is to convert the UTC time stored in database to any timezone. This is achieved by utcToZonedTime. This too takes a date and timezone in IANA format, but the timezone represents the zone to which you want to convert to.

1utcToZonedTime(date, destinationTimeZone)
1console.log(utcToZonedTime(Date.now(), "Asia/Kolkata")); // => Mon Jul 20 2020 02:06:59 GMT+0530 (India Standard Time)
2console.log(utcToZonedTime(Date.now(), "America/Los_Angeles")); // Sun Jul 19 2020 13:36:59 GMT+0530 (India Standard Time)

Evaluation Metrics

CategoryRatingsDeliberations
Ease of useAverageExtremely simple syntax, cross platform support, very good typescript support, requires accompanying modules for timezone manipulations
CommunityGoodFrequent updates and release, great community interaction, Few unattended open issues,
Active usageGoodMore that 6M weekly downloads at the time of writing, around 6K dependent packages on npm
VulnerabilitiesGoodNo vulnerabilities reported on npm audit or retire.js
Docs and TrainingsGoodDetailed docs covering all use cases, many codesanbox links and third-party articles for usage scenarios are available

Check out the package and some reading materials

Video review of the package

Video review of the package with interesting use cases and in-depth exploration of the features coming soon! For more related content, check out Unpackaged Reviews

Disclosures

The content and evaluation scores mentioned in this article/review is subjective and is the personal opinion of authors at Unpackaged Reviews based on everyday usage and research on popular developer forums. They do not represent any company’s views and is not impacted by any sponsorships/collaboration.

Header Photo by Fabrizio Verrecchia on Unsplash

More articles from Unpackaged Reviews

Chalk

Let the console show its true colors!

July 17th, 2020 · 3 min read

Dotenv

Simple way to set the right environment for your application!

July 1st, 2020 · 2 min read
© 2020-2021 Unpackaged Reviews
Link to $https://twitter.com/unpakgd_reviewsLink to $https://unpackaged-reviews.medium.com