본문 바로가기

Backend/Node.js

[Node.js] Date 라이브러리 비교

- Date 라이브러리 (Moment.js 의 대체)

 

프로그래밍을 하다보면 날짜와 관련된 로직을 구현, 처리하는 일이 굉장히 많습니다. 이때 Node.js 에서는 폭넓게 사용되던 라이브러리가 Moment.js 입니다. 하지만 이 라이브러리는 이제 Deprecated 되었고, Moment.js 에선 그 대체로 Luxon,Day.js, date-fns, js-Joda 이렇게 4가지를 공식적으로 언급하고 있습니다. (자세한 내용 링크)

 

이 4가지 라이브러리에 대해 간단한 사용성과 쓸만한 함수들을 비교해보는 시간을 가져보려고 합니다.

 

- Day.js

 

1. 특징

 

npm - Day.js

2KB 사이즈의 경량 라이브러리 / Immutable / Moment.js 와 비슷한 api / i18n 지원

 

2. 사용법

 

const dayjs = require('dayjs');

const d = dayjs();

console.info(d.format()); // 2021-02-04T20:58:05+09:00
console.info(d.format('YYYY-MM-DD HH:mm:ss')); // 2021-02-04 20:58:05

 

객체 생성은 간단하고, format 함수를 통해 원하는 형태로 출력할 수 있습니다.

별도로 설정하지 않으면, 사용자의 local timezone 에 맞춰서 출력됩니다.

 

dayjs('2018-04-04T16:00:00.000Z');
dayjs(1318781876406);

const d = new Date(2018, 8, 18);
const day = dayjs(d);

 

위처럼 ISO format 이나 unix timestamp, javascript 의 Date 객체를 통해서도 날짜를 생성할 수 있습니다.

 

const d1 = dayjs(); // 2021-02-04 20:00:00
const d2 = d1.minute(40); // 2021-02-04 20:40:00
const d3 = dayjs(d1); // 2021-02-04 20:00:00
const d4 = d1.clone(); // 2021-02-04 20:00:00

 

객체 복사는 여러 형태로 가능하며, 모든 api 는 immutable 하게 설계되었기 때문에 d1.minute(40) 으로 d1 의 값을 변경해도, 기존 d1 의 값이 변경되지 않고 새로운 날짜 객체를 반환합니다.

 

const utc = require('dayjs/plugin/utc');

dayjs.extend(utc);

console.info(dayjs().utc().format('YYYY-MM-DD HH:mm:ss')); // 2021-02-04 21:00:00

 

개발간에 local timezone 에 의해 날짜값이 변경되게 하지 않고 utc 를 기준으로 잡아야 할 경우가 많습니다.

그러한 경우 위처럼 dayjs 는 utc plugin 을 추가로 붙여 작업을 할 수 있습니다.

utc 외에도 필요한 plugin 을 그때그때 추가할 수 있습니다. 필요하지 않다면 이러한 플러그인을 전부 빌드할 필요가 없기에 dayjs 라이브러리의 사이즈가 가벼운 것 같습니다.

 

- Luxon

 

1. 특징

 

npm - Luxon

Immutable, Chainable, Unambiguous / parsing 과 formatting 의 자유로움 / native timezone, Intl 지원

 

2. 사용법

 

const { DateTime } = require('luxon');

const d1 = DateTime.local(2021, 1, 1, 17, 30);
/*
  ...,
  c: {
    year: 2021,
    month: 1,
    day: 1,
    hour: 17,
    minute: 30,
    second: 0,
    millisecond: 0
  },
  o: 540,
  ...
*/

const d2 = DateTime.now();
/*
  ...,
  c: {
    year: 2021,
    month: 2,
    day: 11,
    hour: 20,
    minute: 12,
    second: 28,
    millisecond: 528
  },
  o: 540,
  ...
*/

 

마찬가지로 인스턴스 생성은 간단하며, 사용자의 local timezone 에 맞춰 생성됩니다.

 

const d2 = DateTime.fromObject({ day: 22, hour: 12, zone: 'America/Los_Angeles' });

const d3 = DateTime.fromISO("2021-02-10T08:30:00");

 

객체나 ISO format 을 통해서도 DateTime 인스턴스를 쉽게 만들 수 있습니다.

from 으로 시작하는 메소드를 통해 다양한 파싱을 지원합니다.

 

const d1 = DateTime.local(2021, 1, 1, 17, 30);
const d2 = d1.plus({ hours: 3, minutes: 2 });
const d3 = d1.set({ hour: 3 });

console.info(d1.toString()); // 2021-01-01T17:30:00.000+09:00
console.info(d2.toString()); // 2021-01-01T20:32:00.000+09:00
console.info(d3.toString()); // 2021-01-01T03:30:00.000+09:00

 

luxon 의 object 는 Immutable 이라는 말처럼 객체의 날짜, 시간을 변경하는 메소드는 새로운 인스턴스를 반환하도록 설계되어있습니다.

 

const d1 = DateTime.now();
const d2 = d1.toUTC();

console.info(d1.toString()); // 2021-02-11T20:30:31.126+09:00
console.info(d2.toString()); // 2021-02-11T11:30:31.126

 

utc 로의 변환도 함수로 간단히 제공하고 있습니다.

이외에도 Intl 지원 및 날짜 객체 간 연산을 도와주는 여러 함수들이 사용하기 쉬운 형태로 되어 있습니다.

 

- date-fns

 

1. 특징

 

npm - date-fns

date-fns 는 다른 라이브러리들과 다르게 javascript 의 기본 Date 객체를 조작하기 위한 간단한 도구들을 제공하는 형태로 되어있습니다.

 

2. 사용법

 

const { format, formatDistance, formatRelative, subDays } = require('date-fns');

const d1 = format(new Date(), "'Today is a' eeee");

console.info(d1); // Today is a Sunday

const d2 = formatDistance(subDays(new Date(), 3), new Date(), { addSuffix: true });

console.info(d2); // 3 days ago

const d3 = formatRelative(subDays(new Date(), 3), new Date());

console.info(d3); // last Thursday at 8:43 PM

 

가장 기본 함수들을 사용한 예시입니다.

특징적으로 가장 두드러지는 건 역시나 new Date() 의 형태로 javascript 의 Date 객체를 만들어줘야 한다는 점입니다.

함수명으로 어떤 역할을 하는지 유추는 가능하지만, 왜 출력의 형태를 저렇게 했는지는 이해가 잘 안가는 부분입니다.

 

이 라이브러리는 i18n 을 지원하고, FP (함수형 프로그래밍) 도 가능한 서브모듈을 별도로 지원하고 있습니다.

하지만 전체적으로 그닥 직관적이지 못하고 사용하기 편해보이지 않아 date-fns 라이브러리에 대한 내용은 여기까지만 쓰도록 하겠습니다.

 

- js-joda

 

1. 특징

 

npm - @js-joda/core

기존 js-joda 라이브러리가 있었지만, 현재는 deprecated 되었고 현재는 @js-joda/core 로 대체되었습니다.

Immutable / domain-driven API / 43KB / robust and stable

 

2. 사용법

 

const { LocalDate, LocalTime, LocalDateTime } = require('@js-joda/core');

const d1 = LocalDate.parse('2020-12-24');
const d2 = d1.atStartOfDay().plusMonths(2);
console.info(d1); // LocalDate { _year: 2020, _month: 12, _day: 24 }
console.info(d1.month()); // Month { _value: 12, _name: 'DECEMBER' }
console.info(d2);
/*
  LocalDateTime {
    _date: LocalDate { _year: 2021, _month: 2, _day: 24 },
    _time: LocalTime { _hour: 0, _minute: 0, _second: 0, _nano: 0 }
  }
*/

const d3 = LocalDateTime.of(2021, 1, 15, 17);
console.info(d3);
/*
  LocalDateTime {
    _date: LocalDate { _year: 2021, _month: 1, _day: 15 },
    _time: LocalTime { _hour: 17, _minute: 0, _second: 0, _nano: 0 }
  }
*/

const d4 = LocalTime.parse('12:34');
console.info(d4); // LocalTime { _hour: 12, _minute: 34, _second: 0, _nano: 0 }
console.info(d4.month()); // TypeError: d4.month is not a function

 

객체 생성은 Luxon 이나 Day.js 처럼 간단합니다.

parse 나 of 등의 함수를 사용해 js-joda 객체를 만들어 낼 수 있습니다.

Immutable 이라는 특징답게 d1 에 날짜 조작을 해도, 그것이 원본 객체에 영향을 주지 않고 새로운 객체를 만들어 냅니다.

 

굳이 LocalDate, LocalTime 그리고 그 두개가 합쳐진 LocalDateTime 객체가 따로 있는데 그 이유는 모르겠습니다..

LocalDate 는 년월일, LocalTime 시분초 속성만 들고 있기에 LocalTime 에서 month() 를 호출하면 에러가 납니다.

 

const { LocalDate, ZonedDateTime, ZoneOffset, ZoneId } = require('@js-joda/core');
require("@js-joda/timezone");

const d1 = ZonedDateTime.now();
console.info(d1.toString()); // 2021-02-10T21:23:13.772+09:00[Asia/Seoul]

const d2 = ZonedDateTime.now(ZoneOffset.UTC);
console.info(d2.toString()); // 2021-02-10T12:23:13.778Z

const d3 = ZonedDateTime.now(ZoneId.of('UTC-05:00'));
console.info(d3.toString()); // 2021-02-10T07:23:13.778-05:00[UTC-05:00]

const d4 = LocalDate.parse("2021-02-10").atStartOfDay().atZone(ZoneId.of("Europe/Berlin"));
console.info(d4); // 2021-02-10T00:00+01:00[Europe/Berlin]

 

위에서 LocalDate, LocalTime 등을 사용해 객체를 생성하면 timezone 에 대한 정보가 없습니다.

timezone 정보와 함께 이를 변경하기 위해선, @js-joda/timezone 모듈을 별도로 설치해 추가해줘야 합니다.

추가를 안해도 ZonedDateTime 객체를 사용할 수 있지만, timezone 에 region 이 인식되지 않는 등의 문제가 있습니다.

 

- And in addition...

 

4가지의 라이브러리에 대해 객체 생성 및 변경, 몇가지 간단한 함수, utc 관련된 기능과 특징만을 간단히 살펴봤습니다.

라이브러리가 가진 기능들의 일부분만 봤을 뿐이지만 사실 Date 객체를 가지고 그렇게 여러가지 작업을 하진 않습니다.

(제가 Intl 관련 작업을 할 일이 없어서 그럴수도 있습니다)

 

그래서 더욱 객체의 생성 및 변경이 쉽고 Immutable 여부가 중요하다고 느끼는 것 같습니다.

당연히 관련 함수들은 직관적이고 사용이 쉽게 설계되어 있어야 하구요.

Immutable 하지 않으면 그만큼 날짜 객체를 건드릴 때 주의해야할 점이 많아지며 시간을 잡아먹히게 됩니다.

 

지금까지의 비교로는 사용성에서 Day.js 와 Luxon 두 라이브러리가 가장 괜찮고, js-joda > date-fns 순서인 것 같습니다.

date-fns 를 제외하고 나머지 세 라이브러리는 기존 javascript 의 native Date 객체를 래핑한 형태가 아니라 설계에 신경을 많이 쓴 느낌이 듭니다. (기존의 moment, date-utils 와 같은 라이브러리도 native Date 객체를 래핑한 형태였습니다)

 

Date 관련 작업시엔 Date 객체를 만드는 것 외에 timezone 을 설정하는 부분에서 cpu 를 많이 사용합니다.

다음 블로그에서 4가지 라이브러리의 퍼포먼스에 대해 비교해보도록 하겠습니다.