Frontend Development 12 min read

Understanding JavaScript Date Handling and Comparing Popular Date Libraries

This article examines why native JavaScript Date falls short for time handling, explains time measurement standards and time zones, and compares four major date libraries—MomentJS, DayJS, date-fns, and native Date—highlighting their advantages, drawbacks, and appropriate usage scenarios.

ByteFE
ByteFE
ByteFE
Understanding JavaScript Date Handling and Comparing Popular Date Libraries

1 Background

According to the State of Frontend 2022 survey, time‑handling libraries occupy two of the top five most popular tools among frontend engineers. The article investigates why native JS Date cannot meet development needs and what differences exist among various date libraries.

2 Time

2.1 Measurement Standards

Time consists of moments and intervals. Because Earth's rotation is unstable, two measurement systems have emerged:

Universal Time (GMT) – derived from astronomical observations and not stable.

Atomic Time (IAT) – based on atomic vibrations and considered constant.

The discrepancy between these scales leads to occasional leap seconds, resulting in Coordinated Universal Time (UTC).

2.2 Time Zones

The world is divided into 24 time zones. The prime meridian at Greenwich defines the zero zone; each zone east or west differs by one hour. UTC of the zero zone serves as the global reference.

3 new Date()

3.1 YYYY‑MM‑DD Time‑Zone Issue

Using new Date('YYYY-MM-DD') creates a Date at 00:00:00 UTC, which is then displayed in the user's local time zone, causing different results across regions.

Using new Date('YYYY-MM-DD HH‑MM‑SS') or other officially recommended formats creates a Date at 00:00:00 in the current time zone, avoiding the issue.

// Time zone: China
var date1 = new Date('2022-10-24')
// Mon Oct 24 2022 08:00:00 GMT+0800 (China Standard Time)

// Time zone: United States
var date2 = new Date('2022-10-24')
// Sun Oct 23 2022 17:00:00 GMT-0700 (Pacific Daylight Time)

// Using new Date('YYYY-MM-DD HH‑MM‑SS') creates a Date in the current zone
var date3 = new Date('2022-10-24 00:00:00')
// Mon Oct 24 2022 00:00:00 GMT+0800 (China Standard Time)

// Using format 'DD, MM YYYY'
var date4 = new Date('10, 24 2022')
// Mon Oct 24 2022 00:00:00 GMT+0800 (China Standard Time)

3.2 Safari Compatibility Issue

Safari only supports YYYY/MM/DD , MM/DD/YYYY , or MMMM DD, YYYY formats. Using new Date('YYYY-MM-DD') will throw an error.

3.3 Parsing or Displaying Specific Formats

Parsing custom formats often requires regular expressions.

// Parse with a regular expression
const datePattern = /^(\d{2})-(\d{2})-(\d{4})$/;
const [, month, day, year] = datePattern.exec('10-24-2022');
new Date(`${month}, ${day} ${year}`);

4 Main Date Libraries

4.1 MomentJS

Solves parsing and formatting problems completely. const date1 = moment('2022-10-24'); console.log(date1.format()); // 2022-10-24T00:00:00+08:00 console.log(date1.toArray()); // [2022, 9, 24, 0, 0, 0, 0] (month is zero‑based) console.log(date1.toJSON()); // 2022-10-23T16:00:00.000Z

Supports many custom formats. const date2 = moment('10/24/2022', 'MM/DD/YYYY'); const date3 = moment('2022-10-24-4-30', 'YYYY-MM-DD-HH-mm');

Large bundle size (~300 KB) and cannot be tree‑shaken.

Mutable objects : operations modify the original instance. const startDate = moment(); const endDate = startDate.add(1, 'year'); console.log(startDate === endDate); // true

MomentJS is no longer maintained. Projects that still use it can adopt eslint-plugin-you-dont-need-momentjs to help migrate.

// package.json
"extends": ["plugin:you-dont-need-momentjs/recommended"]

4.2 DayJS

DayJS is a lightweight (~6.5 KB) alternative with an API compatible with MomentJS.

Immutable by default.

Core size is small; advanced features are provided via plugins.

API compatibility makes migration easy, but mutable operations must be rewritten. import moment from "moment"; const timeEntity = moment(); timeEntity.add(1, "d"); // mutates import dayjs from "dayjs"; let timeEntity2 = dayjs(); timeEntity2 = timeEntity2.add(1, "d"); // returns a new instance

Some plugins (e.g., badMutable , dayOfYear , objectSupport ) need explicit installation and configuration. var badMutable = require('dayjs/plugin/badMutable'); dayjs.extend(badMutable); const today = dayjs(); // today.add(1, 'day') returns a new immutable instance import dayjs from "dayjs"; import dayOfYear from "dayjs/plugin/dayOfYear"; import objectSupport from "dayjs/plugin/objectSupport"; dayjs.extend(dayOfYear); dayjs.extend(objectSupport); export default dayjs;

4.3 date-fns

date-fns follows a functional‑programming style, allowing on‑demand function imports.

Immutable.

Functions must be imported from specific paths, which can increase boilerplate. export addDays from 'date-fns/addDays/index.js'; const newDate = addDays(new Date(), 7);

Supports tree‑shaking; individual functions can be very small (e.g., add ≈ 2 KB) but full parsing can reach ~100 KB.

4.4 Horizontal Comparison

5 Summary

Native Date cannot directly parse custom formats and is prone to time‑zone bugs – not recommended .

Moment.js has a large bundle size and mutable objects, and it is no longer maintained – not recommended .

Day.js overcomes Moment's drawbacks, keeps a similar API, and is suitable when UTC handling is not required – recommended .

date-fns also avoids Moment's issues, supports tree‑shaking, and is ideal for lightweight needs, though extensive usage can increase bundle size – recommended for modest requirements .

timezoneDatedate-fnsdayjsMomentJS
ByteFE
Written by

ByteFE

Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.