From 58420de9977c01beb18dee52052b67944beb033c Mon Sep 17 00:00:00 2001 From: Mehdi <9340937+meduzen@users.noreply.github.com> Date: Mon, 8 Apr 2024 23:40:58 +0200 Subject: [PATCH] Split test file and move tests closer to the code Also: - improve some tests - update JSDoc - trigger the Vitest GitHub action reporter when a test fails - add code coverage to Vitest UI - exclude types from code coverage - lint tests with more rules --- .github/workflows/node.js.yml | 2 + CHANGELOG.md | 5 +- README.md | 8 +- eslint.config.js | 8 +- package.json | 4 +- src/config/datetime.test.js | 63 +++++++ src/config/tz.test.js | 55 ++++++ src/datetime-class.js | 7 + src/datetime-class.test.js | 70 ++++++++ src/datetime.test.js | 303 ++++++++++++++++++++++++++++++++++ src/duration.js | 4 +- src/duration.test.js | 99 +++++++++++ src/timezone.test.js | 99 +++++++++++ src/utils/date.js | 3 + src/utils/date.test.js | 71 ++++++++ tests/index.test.js | 301 --------------------------------- types/datetime-class.d.ts | 3 + types/duration.d.ts | 4 +- 18 files changed, 796 insertions(+), 313 deletions(-) create mode 100644 src/config/datetime.test.js create mode 100644 src/config/tz.test.js create mode 100644 src/datetime-class.test.js create mode 100644 src/datetime.test.js create mode 100644 src/duration.test.js create mode 100644 src/timezone.test.js create mode 100644 src/utils/date.test.js delete mode 100644 tests/index.test.js diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 57babba..8392d9e 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -28,4 +28,6 @@ jobs: - run: pnpm install --no-frozen-lockfile - run: pnpm lint - run: pnpm test + env: + GITHUB_ACTIONS: true - run: pnpm test:types diff --git a/CHANGELOG.md b/CHANGELOG.md index 8338259..85e19ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,8 +16,9 @@ Compare with [last published version](https://github.com/meduzen/datetime-attrib ### Under the hood -- Enforce type definitions for optional properties of the internal config functions `setConfig` and `setTzConfig`: it was possible to use them by omitting the `separator` property, it’s not anymore. -- Upgrade ESLint from 8 to [9](https://eslint.org/blog/2024/04/eslint-v9.0.0-released/). +- Enforce type definitions for optional properties of the internal config functions `setConfig` and `setTzConfig`: it was possible to omit their `separator` property, it’s not anymore. +- Upgrade ESLint from 8 to [9](https://eslint.org/blog/2024/04/eslint-v9.0.0-released/) and lint tests using [`eslint-plugin-vitest`](https://github.com/veritem/eslint-plugin-vitest). +- Split test file and move tests closer to the code. ## v1.3.3 (2024-02-23) diff --git a/README.md b/README.md index b6585d5..d447238 100644 --- a/README.md +++ b/README.md @@ -411,11 +411,11 @@ Calculate the difference between 2 dates in days, **discarding the time of day** ```js import { daysBetween } from 'datetime-attribute' -const january1st = new Date(2021, 0, 1, 10, 10, 12) +const january1st2021 = new Date(2021, 0, 1, 10, 10, 12) const january11th = new Date(2021, 0, 11, 10, 10, 12) const january19th = new Date(2021, 0, 19, 10, 10, 12) -daysBetween(january1st, january11th) // 10 +daysBetween(january1st2021, january11th) // 10 daysBetween(january19th, january11th) // -8 ``` @@ -433,12 +433,12 @@ It accepts a `Date` object. ```js import { weekNumber } from 'datetime-attribute' -const january1st = new Date(2021, 0, 1, 10, 10, 12) +const january1st2021 = new Date(2021, 0, 1, 10, 10, 12) const january11th = new Date(2021, 0, 11, 10, 10, 12) const togoIndependanceDay = new Date(1960, 3, 27) weekNumber(togoIndependanceDay) // 17 -weekNumber(january1st) // 53: it’s a Friday! +weekNumber(january1st2021) // 53: it’s a Friday! weekNumber(january11th) // 2 ``` diff --git a/eslint.config.js b/eslint.config.js index 7a2b32a..e2ded50 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -70,9 +70,13 @@ export default tseslint.config( rules: { ...vitest.configs.recommended.rules, // ...vitest.configs.all.rules, // worth testing from time to time… - // 'vitest/consistent-test-it': 0, + + // Additional rules below taken from `vitest.configs.all.rules` + 'vitest/consistent-test-it': 0, + 'vitest/no-hooks': 0, // when testing `vitest.configs.all.rules` 'vitest/no-test-return-statement': 1, - // 'vitest/prefer-expect-assertions': 0, // when testing `vitest.configs.all.rules` + 'vitest/prefer-expect-assertions': 0, // when testing `vitest.configs.all.rules` + 'vitest/prefer-todo': 1, 'vitest/require-to-throw-message': 1, }, languageOptions: { diff --git a/package.json b/package.json index 59b7870..0b68d33 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,8 @@ "types": "tsc", "test": "vitest run", "test:types": "pnpm exec tsd", - "test:ui": "vitest --ui", - "coverage": "vitest run --coverage", + "test:ui": "vitest --ui --coverage.enabled --coverage.exclude=types", + "coverage": "vitest run --coverage --coverage.exclude=types", "watch": "vitest watch", "build": "echo \"Nothing to build, this command is only here to please size-limit GitHub action\" && exit 0", "size": "size-limit", diff --git a/src/config/datetime.test.js b/src/config/datetime.test.js new file mode 100644 index 0000000..1039d04 --- /dev/null +++ b/src/config/datetime.test.js @@ -0,0 +1,63 @@ +import { describe, expect, test } from 'vitest' + +import { datetime, setTimeSeparator } from '../index.js' + +import { setConfig } from './datetime.js' + +const togoIndependanceDay = new Date(1960, 3, 27) +const date = togoIndependanceDay // alias for the sake of brevity + +describe('setTimeSeparator', () => { + + test('is a function', () => { + expect(setTimeSeparator).toBeInstanceOf(Function) + }) + + test('throws with invalid separator', () => { + expect(() => setTimeSeparator(42)).toThrow(Error) + }) + + test('no time separator', () => { + setTimeSeparator() + expect(datetime(date, 'datetime')).toBe('1960-04-27T00:00') + }) + + test('T as time separator', () => { + setTimeSeparator('T') + expect(datetime(date, 'datetime')).toBe('1960-04-27T00:00') + }) + + test('space as time separator', () => { + setTimeSeparator(' ') + expect(datetime(date, 'datetime')).toBe('1960-04-27 00:00') + }) +}) + +describe('setConfig', () => { + + test('is a function', () => { + expect(setConfig).toBeInstanceOf(Function) + }) + + test('throws without parameter', () => { + expect(() => setConfig()).toThrow(TypeError) + }) + + test('throws without proper parameter', () => { + expect(() => setConfig({})).toThrow(Error) + }) + + test('throws with invalid separator', () => { + expect(() => setConfig({ separator: 42 })).toThrow(Error) + }) + + test('T as time separator using datetime', () => { + setConfig({ separator: 'T' }) + expect(datetime(date, 'datetime')).toBe('1960-04-27T00:00') + }) + + test('space as time separator using datetime', () => { + setConfig({ separator: ' ' }) + expect(datetime(date, 'datetime')).toBe('1960-04-27 00:00') + }) +}) diff --git a/src/config/tz.test.js b/src/config/tz.test.js new file mode 100644 index 0000000..05c6f7d --- /dev/null +++ b/src/config/tz.test.js @@ -0,0 +1,55 @@ +import { describe, expect, test } from 'vitest' + +import { datetimeTz, tzOffset, setTzSeparator } from '../index.js' + +import { setTzConfig } from './tz.js' + +const togoIndependanceDay = new Date(1960, 3, 27) +const date = togoIndependanceDay // alias for the sake of brevity + +describe('setTzSeparator', () => { + test('is a function', () => expect(setTzSeparator).toBeInstanceOf(Function)) + test('throws with invalid separator', () => expect(() => setTzSeparator(42)).toThrow(Error)) + + test('no timezone separator', () => { + setTzSeparator() + expect(tzOffset(3)).toBe('+0300') + }) + + test('empty string timezone separator', () => { + setTzSeparator('') + expect(tzOffset(3)).toBe('+0300') + }) + + test(': as timezone separator', () => { + setTzSeparator(':') + expect(tzOffset(3)).toBe('+03:00') + }) + + test('empty string timezone separator with datetimeTz', () => { + setTzSeparator('') + expect(datetimeTz(date, 'datetime second', -6)).toBe('1960-04-27T00:00:00-0600') + }) +}) + +describe('setTzConfig', () => { + test('is a function', () => expect(setTzConfig).toBeInstanceOf(Function)) + test('throws without parameter', () => expect(() => setTzConfig()).toThrow(TypeError)) + test('throws without proper parameter', () => expect(() => setTzConfig({})).toThrow(Error)) + test('throws with invalid separator', () => expect(() => setTzConfig({ separator: 42 })).toThrow(Error)) + + test('empty string timezone separator using tzOffset', () => { + setTzConfig({ separator: '' }) + expect(tzOffset(3)).toBe('+0300') + }) + + test(': as timezone separator using tzOffset', () => { + setTzConfig({ separator: ':' }) + expect(tzOffset(3)).toBe('+03:00') + }) + + test(': as timezone separator with datetimeTz', () => { + setTzConfig({ separator: ':' }) + expect(datetimeTz(date, 'datetime second', -6)).toBe('1960-04-27T00:00:00-06:00') + }) +}) diff --git a/src/datetime-class.js b/src/datetime-class.js index 539fabc..f9bee65 100644 --- a/src/datetime-class.js +++ b/src/datetime-class.js @@ -11,6 +11,10 @@ export class DateTime extends Date { * - the first week of the year includes a Thursday; * - week numbers go from 1 to 53 (https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#week-number-of-the-last-day). * + * @todo: cache the result of weekNumber as long as date isn’t changed: + * compare the current object timestamp wuth a stored/cached one in + * order to decide if the cache is stale. + * * @returns {number} */ getWeek = () => weekNumber(this) @@ -18,6 +22,9 @@ export class DateTime extends Date { /** * Set the week number. * + * If the week number is outside the year range, the `DateTime` object is + * updated accordingly. + * * @param {number} weekNumber * @returns {number} Milliseconds since midnight on January 1, 1970, UTC */ diff --git a/src/datetime-class.test.js b/src/datetime-class.test.js new file mode 100644 index 0000000..c5c8748 --- /dev/null +++ b/src/datetime-class.test.js @@ -0,0 +1,70 @@ +import { beforeEach, describe, expect, test } from 'vitest' + +import { DateTime, datetime } from './index.js' +import { MILLISECONDS_PER_DAY } from './utils/const.js' + +const FOURTEEN_DAYS_IN_MS = MILLISECONDS_PER_DAY * 14 + +describe('DateTime class', () => { + /** @type {Date} */ let summer + /** @type {Date} */ let twoWeeksIntoSummer + /** @type {Date} */ let winter + /** @type {Date} */ let twoWeeksIntoWinter + + beforeEach(() => { + summer = new DateTime(2021, 5, 21) + twoWeeksIntoSummer = new DateTime(summer.getTime() + FOURTEEN_DAYS_IN_MS) // same as `new DateTime(2021, 6, 5)` + + winter = new DateTime(2021, 11, 21) + twoWeeksIntoWinter = new DateTime(winter.getTime() + FOURTEEN_DAYS_IN_MS) // same as `new DateTime(2022, 1, 4)` + }) + + test('extends the Date class', () => { + expect(summer).toBeInstanceOf(DateTime) + expect(summer).toBeInstanceOf(Date) + }) + + describe('.to() == datetime()', () => { + + test('with default precision', () => { + expect(summer.to()).toBe('2021-06-21') + expect(datetime(summer)).toBe('2021-06-21') + }) + + test(`with 'month' precision`, () => { + expect(twoWeeksIntoSummer.to('month')).toBe('2021-07') + expect(datetime(twoWeeksIntoSummer, 'month')).toBe('2021-07') + }) + + test(`with 'week' precision, after the week is changed to a new year`, () => { + winter.setDate(winter.getDate() + 14) + + expect(winter.to('week')).toBe(twoWeeksIntoWinter.to('week')) + expect(winter.to('week')).toBe('2022-W01') + expect(datetime(winter, 'week')).toBe('2022-W01') + }) + }) + + describe('.getWeek()', () => { + + test('returns the week number', () => { + expect(summer.getWeek()).toBe(25) + }) + + test('returns the week number after the date is changed', () => { + summer.setDate(summer.getDate() + 14) + + expect(summer.getWeek()).toBe(twoWeeksIntoSummer.getWeek()) + }) + + test('returns the week number after the date is changed to a new year', () => { + winter.setDate(winter.getDate() + 14) + + expect(winter.getWeek()).toBe(twoWeeksIntoWinter.getWeek()) + }) + }) + + test('.setWeek() returns the timestamp of the changed date', () => { + expect(summer.setWeek(27)).toBe(twoWeeksIntoSummer.getTime()) + }) +}) diff --git a/src/datetime.test.js b/src/datetime.test.js new file mode 100644 index 0000000..325051e --- /dev/null +++ b/src/datetime.test.js @@ -0,0 +1,303 @@ +import { describe, expect, test } from 'vitest' + +import { datetime, datetimeTz, tzOffset, utc } from './index.js' + +const togoIndependanceDay = new Date(1960, 3, 27) +const date = togoIndependanceDay // alias for the sake of brevity + +const firstCerealHarvest = new Date(-23000, 6, 7) +const foundationOfSyracuse = new Date(-733, 6, 7) +const birthOfChineseEmpressWuZetian = new Date(624, 1, 17) // https://en.wikipedia.org/wiki/Wu_Zetian +const january1st2021 = new Date(2021, 0, 1, 10, 10, 12) +const january11th = new Date(2021, 0, 11, 10, 10, 12) +const january19th = new Date(2021, 0, 19, 10, 10, 12) +const december31th2020 = new Date(2020, 11, 31, 10, 10, 12) +const december31th2021 = new Date(2021, 11, 31, 10, 10, 12) +const year10k = new Date(10000, 0, 1) + +const tzOffsetInMinutes = (new Date()).getTimezoneOffset() * -1 + +describe('datetime', () => { + + test('is a function', () => { + expect(datetime).toBeInstanceOf(Function) + }) + + test('throws on wrong date type', () => { + expect(() => datetime(123)).toThrow(TypeError) + }) + + test('“now” as default date parameter', () => { + expect(datetime()).toBe(datetime((new Date()))) + }) + + test('no precision', () => expect(datetime(date)).toBe('1960-04-27')) + test('no precision before the 10th', () => expect(datetime(january1st2021)).toBe('2021-01-01')) + + describe('precision, no UTC', () => { + + test('day', () => { + expect(datetime(date, 'day')).toBe('1960-04-27') + }) + + test('year', () => { + expect(datetime(date, 'year')).toBe('1960') + }) + + test('month', () => { + expect(datetime(date, 'month')).toBe('1960-04') + }) + + test('yearless', () => { + expect(datetime(date, 'yearless')).toBe('04-27') + }) + + test('time', () => { + expect(datetime(date, 'time')).toBe('00:00') + }) + + test('second', () => { + expect(datetime(date, 'second')).toBe('00:00:00') + }) + + test('ms', () => { + expect(datetime(date, 'ms')).toBe('00:00:00.000') + }) + + test('datetime', () => { + expect(datetime(date, 'datetime')).toBe('1960-04-27T00:00') + }) + + test('datetime second', () => { + expect(datetime(date, 'datetime second')).toBe('1960-04-27T00:00:00') + }) + + test('datetime ms', () => { + expect(datetime(date, 'datetime ms')).toBe('1960-04-27T00:00:00.000') + }) + }) + + describe('week', () => { + + test('week on 1960-04-27 is 1960-W17', () => { + expect(datetime(date, 'week')).toBe('1960-W17') + }) + + test('week on 2020-12-31 is 2020-W53', () => { + expect(datetime(december31th2020, 'week')).toBe('2020-W53') + }) + + test.todo('week on 2021-01-01 is 2020-W53', () => { + expect(datetime(january1st2021, 'week')).toBe('2020-W53') + }) + + test('week on 2021-01-11 is 2021-W02', () => { + expect(datetime(january11th, 'week')).toBe('2021-W02') + }) + + test('week on 2021-01-19 is 2021-W03', () => { + expect(datetime(january19th, 'week')).toBe('2021-W03') + }) + + test('week on 2021-12-31 is 2021-W52', () => { + expect(datetime(december31th2021, 'week')).toBe('2021-W52') + }) + }) + + // These ones can’t be tested providing an exact value as the output depends on client timezone. + describe('precision, UTC', () => { + + test('time utc', () => { + expect(datetime(date, 'time utc')).toBe(date.toJSON().substr(11, 5) + 'Z') + }) + + test('second utc', () => { + expect(datetime(date, 'second utc')).toBe(date.toJSON().substr(11, 8) + 'Z') + }) + + test('ms utc', () => { + expect(datetime(date, 'ms utc')).toBe(date.toJSON().substr(11, 12) + 'Z') + }) + + test('datetime utc', () => { + expect(datetime(date, 'datetime utc')).toBe(date.toJSON().substr(0, 16) + 'Z') + }) + + test('datetime second utc', () => { + expect(datetime(date, 'datetime second utc')).toBe(date.toJSON().substr(0, 19) + 'Z') + }) + + test('datetime ms utc', () => { + expect(datetime(date, 'datetime ms utc')).toBe(date.toJSON()) + }) + }) + + describe('year and yearless, but for years outside the ISO-8601 range', () => { + + test('3 digits year before 0', () => { + expect(datetime(foundationOfSyracuse, 'year')).toBe('-0733') + }) + + test('3 digits year', () => { + expect(datetime(birthOfChineseEmpressWuZetian, 'year')).toBe('0624') + }) + + test('5 digits year before 0', () => { + expect(datetime(firstCerealHarvest, 'year')).toBe('-23000') + }) + + test('5 digits year after 9999', () => { + expect(datetime(year10k, 'year')).toBe('10000') + }) + + test('yearless and year before 0', () => { + expect(datetime(foundationOfSyracuse, 'yearless')).toBe('07-07') + }) + + test('yearless and 3 digits year', () => { + expect(datetime(birthOfChineseEmpressWuZetian, 'yearless')).toBe('02-17') + }) + }) + + test('unsupported precision falls back on the default one', () => { + expect(datetime(date, 'n00t')).toBe('1960-04-27') + }) +}) + +describe('datetimeTz', () => { + + test('is a function', () => { + expect(datetimeTz).toBeInstanceOf(Function) + }) + + test('throws on wrong date type', () => { + expect(() => datetimeTz(123)).toThrow(TypeError) + }) + + test('“now” as default date parameter', () => { + expect(datetimeTz()).toBe(datetimeTz((new Date()))) + }) + + test('no precision', () => { + expect(datetimeTz(date)).toBe('1960-04-27T00:00' + tzOffset(0, tzOffsetInMinutes)) + }) + + test('no offset', () => { + expect(datetimeTz(date, 'time')).toBe('00:00' + tzOffset(0, tzOffsetInMinutes)) + }) + + describe('precision, no UTC', () => { + + test('time 0', () => { + expect(datetimeTz(date, 'time', 0)).toBe('00:00Z') + }) + + test('time +4', () => { + expect(datetimeTz(date, 'time', 4)).toBe('00:00+04:00') + }) + + test('second -3', () => { + expect(datetimeTz(date, 'second', -3)).toBe('00:00:00-03:00') + }) + + test('ms +3.5', () => { + expect(datetimeTz(date, 'ms', 3.5)) .toBe('00:00:00.000+03:30') + }) + + test('datetime +12:45', () => { + expect(datetimeTz(date, 'datetime', 12, 45)).toBe('1960-04-27T00:00+12:45') + }) + + test('datetime +17, 30, true', () => { + expect(datetimeTz(date, 'datetime', 17, 30, true)).toBe('1960-04-27T00:00-06:30') + }) + + test('datetime second -6', () => { + expect(datetimeTz(date, 'datetime second', -6)).toBe('1960-04-27T00:00:00-06:00') + }) + + test('datetime ms +1', () => { + expect(datetimeTz(date, 'datetime ms', 1)).toBe('1960-04-27T00:00:00.000+01:00') + }) + }) + + // These ones can’t be tested providing an exact value as the output depends on client timezone. + + describe('precision, UTC', () => { + + test('time utc', () => { + expect(datetimeTz(date, 'time utc')).toBe(date.toJSON().substr(11, 5) + 'Z') + }) + + test('second utc', () => { + expect(datetimeTz(date, 'second utc')).toBe(date.toJSON().substr(11, 8) + 'Z') + }) + + test('ms utc', () => { + expect(datetimeTz(date, 'ms utc')).toBe(date.toJSON().substr(11, 12) + 'Z') + }) + + test('datetime utc', () => { + expect(datetimeTz(date, 'datetime utc')).toBe(date.toJSON().substr(0, 16) + 'Z') + }) + + test('datetime second utc', () => { + expect(datetimeTz(date, 'datetime second utc')).toBe(date.toJSON().substr(0, 19) + 'Z') + }) + + test('datetime ms utc', () => { + expect(datetimeTz(date, 'datetime ms utc')).toBe(date.toJSON()) + }) + }) +}) + +describe('utc', () => { + test('is a function', () => { + expect(utc).toBeInstanceOf(Function) + }) + + test('throws on wrong date type', () => { + expect(() => utc(123)).toThrow(TypeError) + }) + + test('“now” as default date parameter', () => { + expect(utc()).toBe(utc((new Date()))) + }) + + // These ones can’t be tested providing an exact value as the output depends on client timezone. + + describe('precision', () => { + + test(`'datetime' is the default precision`, () => { + expect(utc(date)).toBe(date.toJSON().substr(0, 16) + 'Z') + }) + + test('time', () => { + expect(utc(date, 'time')).toBe(date.toJSON().substr(11, 5) + 'Z') + }) + + test('second', () => { + expect(utc(date, 'second')).toBe(date.toJSON().substr(11, 8) + 'Z') + }) + + test('ms', () => { + expect(utc(date, 'ms')).toBe(date.toJSON().substr(11, 12) + 'Z') + }) + + test('datetime', () => { + expect(utc(date, 'datetime')).toBe(date.toJSON().substr(0, 16) + 'Z') + }) + + test('datetime second', () => { + expect(utc(date, 'datetime second')).toBe(date.toJSON().substr(0, 19) + 'Z') + }) + + test('datetime ms', () => { + expect(utc(date, 'datetime ms')).toBe(date.toJSON()) + }) + }) + + test('unsupported precision falls back on the default one', () => { + expect(utc(date, 'n00t')).toBe('1960-04-27') + }) +}) diff --git a/src/duration.js b/src/duration.js index 6871c58..8bf53b7 100644 --- a/src/duration.js +++ b/src/duration.js @@ -9,7 +9,9 @@ import { round } from './utils/math.js' * See also: https://www.brucelawson.co.uk/2012/best-of-time/ * * @param {DurationObject} [duration={}] - * @param {boolean} [convertExcess=true] + * @param {boolean} [convertExcess=true] Convert overflow values to their + * higher unit (e.g. `{ m: 90 }` + * equals `{ h: 1, m: 30 }`. * @returns {string} */ export function duration(duration = {}, convertExcess = true) { diff --git a/src/duration.test.js b/src/duration.test.js new file mode 100644 index 0000000..ef382ec --- /dev/null +++ b/src/duration.test.js @@ -0,0 +1,99 @@ +import { describe, expect, test } from 'vitest' + +import { duration } from './index.js' + +describe('duration', () => { + const durationObject = { w: 3, d: 5, h: 10, m: 43, s: 2.61 } + const durationWithTooHighValues = { w: 3, d: 19, h: 36, m: 53, s: 175.3 } + const durationInHours = { h: 17 } + const durationInDays = { d: 43 } + + test('is a function', () => { + expect(duration).toBeInstanceOf(Function) + }) + + test('duration(null) throws', () => { + expect(() => duration(null)).toThrow(Error) + }) + + describe('empty or missing object', () => { + + test('duration()', () => { + expect(duration()).toBe('PT0S') + }) + + test('duration({}) is PT0S', () => { + expect(duration({})).toBe('PT0S') + }) + + test('duration({}, true) is PT0S', () => { + expect(duration({}, true)).toBe('PT0S') + }) + + test('duration({}, false) is PT0S', () => { + expect(duration({}, false)).toBe('PT0S') + }) + }) + + describe('complete object', () => { + + test('{ w: 3, d: 5, h: 10, m: 43, s: 2.61 } is P3W5DT10H43M2.61S', () => { + expect(duration(durationObject)).toBe('P3W5DT10H43M2.61S') + }) + + test('with too high values', () => { + expect(duration(durationWithTooHighValues)).toBe('P5W6DT12H55M55.3S') + }) + }) + + describe('partial object', () => { + + test('hours only', () => { + expect(duration(durationInHours)).toBe('PT17H') + }) + + test('days only', () => { + expect(duration(durationInDays)).toBe('P6W1D') + }) + + }) + + describe('conversion of excess values', () => { + + test('is enabled by default', () => { + expect(duration(durationWithTooHighValues)).toBe(duration(durationWithTooHighValues, true)) + }) + + test('disabled: full object without excess', () => { + expect(duration(durationObject, false)).toBe('P3W5DT10H43M2.61S') + }) + + test('disabled: full object with excess', () => { + expect(duration(durationWithTooHighValues, false)).toBe('P3W19DT36H53M175.3S') + }) + + test('disabled: partial object without excess', () => { + expect(duration(durationInHours, false)).toBe('PT17H') + }) + + test('disabled: partial object with excess', () => { + expect(duration(durationInDays, false)).toBe('P43D') + }) + + test('enabled: full object without excess', () => { + expect(duration(durationObject, true)).toBe('P3W5DT10H43M2.61S') + }) + + test('enabled: full object with excess', () => { + expect(duration(durationWithTooHighValues, true)).toBe('P5W6DT12H55M55.3S') + }) + + test('enabled: partial object without excess', () => { + expect(duration(durationInHours, true)).toBe('PT17H') + }) + + test('enabled: partial object with excess', () => { + expect(duration(durationInDays, true)).toBe('P6W1D') + }) + }) +}) diff --git a/src/timezone.test.js b/src/timezone.test.js new file mode 100644 index 0000000..ef5afba --- /dev/null +++ b/src/timezone.test.js @@ -0,0 +1,99 @@ +import { describe, expect, test } from 'vitest' + +import { tzOffset } from './index.js' + +const tzOffsetInMinutes = (new Date()).getTimezoneOffset() * -1 + +describe('tzOffset', () => { + + test('is a function', () => { + expect(tzOffset).toBeInstanceOf(Function) + }) + + test('tzOffset(Z) throws', () => { + expect(() => tzOffset('Z')).toThrow(TypeError) + }) + + // This one can’t be tested providing an exact value as the output depends on client timezone and daylight time saving. + test('tzOffset() is the local timezone offset from UTC, in minutes', () => { + expect(tzOffset()).toBe(tzOffset(0, tzOffsetInMinutes)) + }) + + describe('in boundaries', () => { + + test('tzOffset(-8) is -08:00', () => { + expect(tzOffset(-8)).toBe('-08:00') + }) + + test('tzOffset(-4.5) is -04:30', () => { + expect(tzOffset(-4.5)).toBe('-04:30') + }) + + test('tzOffset(0, -30) is -00:30', () => { + expect(tzOffset(0, -30)).toBe('-00:30') + }) + + test('tzOffset(0) is Z', () => { + expect(tzOffset(0)).toBe('Z') + }) + + test('tzOffset(0, 30) is +00:30', () => { + expect(tzOffset(0, 30)).toBe('+00:30') + }) + + test('tzOffset(1) is +01:00', () => { + expect(tzOffset(1)).toBe('+01:00') + }) + + test('tzOffset(2, -200) is -01:20', () => { + expect(tzOffset(2, -200)).toBe('-01:20') + }) + + test('tzOffset(4, 30) is +04:30', () => { + expect(tzOffset(4, 30)).toBe('+04:30') + }) + + test('tzOffset(12, 45) is +12:45', () => { + expect(tzOffset(12, 45)).toBe('+12:45') + }) + + test('tzOffset(12.75) is +12:45', () => { + expect(tzOffset(12.75)).toBe('+12:45') + }) + }) + + describe('out of boundaries', () => { + + test('tzOffset(-35) is -11:00', () => { + expect(tzOffset(-35)).toBe('-11:00') + }) + + test('tzOffset(-35, 0, true) is +13:00', () => { + expect(tzOffset(-35, 0, true)).toBe('+13:00') + }) + + test('tzOffset(62.75) is +14:45', () => { + expect(tzOffset(62.75)).toBe('+14:45') + }) + + test('tzOffset(-12, 0, true) is -12:00', () => { + expect(tzOffset(-12, 0, true)).toBe('-12:00') + }) + + test('tzOffset(-12, -20, true) is +11:40', () => { + expect(tzOffset(-12, -20, true)).toBe('+11:40') + }) + + test('tzOffset(-13, -20, true) is +10:40', () => { + expect(tzOffset(-13, -20, true)).toBe('+10:40') + }) + + test('tzOffset(14, 45, true) is -09:15', () => { + expect(tzOffset(14, 45, true)).toBe('-09:15') + }) + + test('tzOffset(62.75, 0, true) is -09:15', () => { + expect(tzOffset(62.75, 0, true)).toBe('-09:15') + }) + }) +}) diff --git a/src/utils/date.js b/src/utils/date.js index 53c6871..2ab2757 100644 --- a/src/utils/date.js +++ b/src/utils/date.js @@ -3,6 +3,9 @@ import { MILLISECONDS_PER_DAY } from './const.js' /** * Calculate the difference between 2 dates in days, discarding the time of day. * + * @todo Consider renaming it daysDiff when there’s a breaking change, since it + * can returns a negative number when date > furtherDate. + * * @param {Date} date The oldest of the two dates. * @param {Date} furtherDate Another date, greater than the first one. * @returns {number} diff --git a/src/utils/date.test.js b/src/utils/date.test.js new file mode 100644 index 0000000..a65527a --- /dev/null +++ b/src/utils/date.test.js @@ -0,0 +1,71 @@ +import { describe, expect, test } from 'vitest' + +import { daysBetween, weekNumber } from '../index.js' + +const togoIndependanceDay = new Date(1960, 3, 27) +const january1st2021 = new Date(2021, 0, 1, 10, 10, 12) +const january11th = new Date(2021, 0, 11, 10, 10, 12) +const december31th2020 = new Date(2020, 11, 31, 10, 10, 12) +const december31th2020OneMinuteLater = new Date(2020, 11, 31, 10, 11, 12) +const december31th2021 = new Date(2021, 11, 31, 10, 10, 12) +const january2nd2022 = new Date(2022, 0, 2, 10, 10, 12) +const january3rd2022 = new Date(2022, 0, 3, 10, 10, 12) + +describe('weekNumber', () => { + + test('is a function', () => { + expect(weekNumber).toBeInstanceOf(Function) + }) + + describe('weeks without year overlap', () => { + + test('1960-04-27 is week 17', () => { + expect(weekNumber(togoIndependanceDay)).toBe(17) + }) + + test('2021-01-11 is week 2', () => { + expect(weekNumber(january11th)).toBe(2) + }) + }) + + describe('weeks with potential year overlap', () => { + + test('2020-12-31 is week 53', () => { + expect(weekNumber(december31th2020)).toBe(53) + }) + + test('2021-01-01 is week 53', () => { + expect(weekNumber(january1st2021)).toBe(53) + }) + + test('2021-12-31 is week 52', () => { + expect(weekNumber(december31th2021)).toBe(52) + }) + + test('2022-01-02 is week 52', () => { + expect(weekNumber(january2nd2022)).toBe(52) + }) + + test('2022-01-03 is week 1', () => { + expect(weekNumber(january3rd2022)).toBe(1) + }) + }) + +}) + +describe('daysBetween', () => { + + test('is a function', () => expect(daysBetween).toBeInstanceOf(Function)) + + test('10 days between 1/1 and 11/1', () => { + expect(daysBetween(january1st2021, january11th)).toBe(10) + }) + + test('-10 days between 11/1 and 1/1', () => { + expect(daysBetween(january11th, january1st2021)).toBe(-10) + }) + + test('same day, different time', () => { + expect(daysBetween(december31th2020, december31th2020OneMinuteLater)).toBe(0) + }) +}) diff --git a/tests/index.test.js b/tests/index.test.js deleted file mode 100644 index dd8a0d4..0000000 --- a/tests/index.test.js +++ /dev/null @@ -1,301 +0,0 @@ -import { describe, expect, test } from 'vitest' - -import { - DateTime, - datetime, - utc, - datetimeTz, - duration, - tzOffset, - daysBetween, - weekNumber, - setTimeSeparator, - setTzSeparator, -} from '../src' - -/** - * These config methods are currently not exported in the main bundle (though - * they are used by `setXxSeparator` functions). There’s no reason to move - * them to the main without any new entry in the configuration objects. - */ -import { setConfig } from '../src/config/datetime.js' -import { setTzConfig } from '../src/config/tz.js' - -const togoIndependanceDay = new Date(1960, 3, 27) -const date = togoIndependanceDay // alias for the sake of brevity - -const firstCerealHarvest = new Date(-23000, 6, 7) -const foundationOfSyracuse = new Date(-733, 6, 7) -const birthOfChineseEmpressWuZetian = new Date(624, 1, 17) // https://en.wikipedia.org/wiki/Wu_Zetian -const january1st = new Date(2021, 0, 1, 10, 10, 12) -const january11th = new Date(2021, 0, 11, 10, 10, 12) -const january19th = new Date(2021, 0, 19, 10, 10, 12) -const december31th2020 = new Date(2020, 11, 31, 10, 10, 12) -const december31th2020OneMinuteLater = new Date(2020, 11, 31, 10, 11, 12) -const december31th2021 = new Date(2021, 11, 31, 10, 10, 12) -const year10k = new Date(10000, 0, 1) - -const tzOffsetInMinutes = (new Date()).getTimezoneOffset() * -1 - -describe('datetime', () => { - test('is a function', () => expect(datetime).toBeInstanceOf(Function)) - test('wrong type', () => expect(() => datetime(123)).toThrow(TypeError)) - - test('()', () => expect(datetime()).toBe(datetime((new Date())))) - - test('no precision', () => expect(datetime(date)).toBe('1960-04-27')) - test('no precision before the 10th', () => expect(datetime(january1st)).toBe('2021-01-01')) - - test('day', () => expect(datetime(date, 'day')).toBe('1960-04-27')) - test('year', () => expect(datetime(date, 'year')).toBe('1960')) - - test('3 digits year before 0', () => expect(datetime(foundationOfSyracuse, 'year')).toBe('-0733')) - test('3 digits year', () => expect(datetime(birthOfChineseEmpressWuZetian, 'year')).toBe('0624')) - test('5 digits year before 0', () => expect(datetime(firstCerealHarvest, 'year')).toBe('-23000')) - test('5 digits year after 9999', () => expect(datetime(year10k, 'year')).toBe('10000')) - - test('month', () => expect(datetime(date, 'month')).toBe('1960-04')) - test('yearless', () => expect(datetime(date, 'yearless')).toBe('04-27')) - test('yearless and year before 0', () => expect(datetime(foundationOfSyracuse, 'yearless')).toBe('07-07')) - test('yearless and 3 digits year', () => expect(datetime(birthOfChineseEmpressWuZetian, 'yearless')).toBe('02-17')) - - test('time', () => expect(datetime(date, 'time')).toBe('00:00')) - test('second', () => expect(datetime(date, 'second')).toBe('00:00:00')) - test('ms', () => expect(datetime(date, 'ms')).toBe('00:00:00.000')) - - test('datetime', () => expect(datetime(date, 'datetime')).toBe('1960-04-27T00:00')) - test('datetime second', () => expect(datetime(date, 'datetime second')).toBe('1960-04-27T00:00:00')) - test('datetime ms', () => expect(datetime(date, 'datetime ms')).toBe('1960-04-27T00:00:00.000')) - - test('week on 1960-04-27', () => expect(datetime(date, 'week')).toBe('1960-W17')) - test('week on 2021-01-01', () => expect(datetime(january1st, 'week')).toBe('2021-W53')) - test('week on 2021-01-11', () => expect(datetime(january11th, 'week')).toBe('2021-W02')) - test('week on 2021-01-19', () => expect(datetime(january19th, 'week')).toBe('2021-W03')) - test('week on 2020-12-31', () => expect(datetime(december31th2020, 'week')).toBe('2020-W53')) - test('week on 2021-12-31', () => expect(datetime(december31th2021, 'week')).toBe('2021-W52')) - - // These ones can’t be tested providing an exact value as the output depends on client timezone. - - test('time utc', () => expect(datetime(date, 'time utc')).toBe(date.toJSON().substr(11, 5) + 'Z')) - test('second utc', () => expect(datetime(date, 'second utc')).toBe(date.toJSON().substr(11, 8) + 'Z')) - test('ms utc', () => expect(datetime(date, 'ms utc')).toBe(date.toJSON().substr(11, 12) + 'Z')) - - test('datetime utc', () => expect(datetime(date, 'datetime utc')).toBe(date.toJSON().substr(0, 16) + 'Z')) - test('datetime second utc', () => expect(datetime(date, 'datetime second utc')).toBe(date.toJSON().substr(0, 19) + 'Z')) - test('datetime ms utc', () => expect(datetime(date, 'datetime ms utc')).toBe(date.toJSON())) - - test('unsupported precision', () => expect(datetime(date, 'n00t')).toBe('1960-04-27')) -}) - -describe('utc', () => { - test('is a function', () => expect(utc).toBeInstanceOf(Function)) - test('wrong type', () => expect(() => utc(123)).toThrow(TypeError)) - test('()', () => expect(utc()).toBe(utc((new Date())))) - - // These ones can’t be tested providing an exact value as the output depends on client timezone. - test('no precision', () => expect(utc(date)).toBe(date.toJSON().substr(0, 16) + 'Z')) - test('time', () => expect(utc(date, 'time')).toBe(date.toJSON().substr(11, 5) + 'Z')) - test('second', () => expect(utc(date, 'second')).toBe(date.toJSON().substr(11, 8) + 'Z')) - test('ms', () => expect(utc(date, 'ms')).toBe(date.toJSON().substr(11, 12) + 'Z')) - test('datetime', () => expect(utc(date, 'datetime')).toBe(date.toJSON().substr(0, 16) + 'Z')) - test('datetime second', () => expect(utc(date, 'datetime second')).toBe(date.toJSON().substr(0, 19) + 'Z')) - test('datetime ms', () => expect(utc(date, 'datetime ms')).toBe(date.toJSON())) - - test('unsupported precision', () => expect(utc(date, 'n00t')).toBe('1960-04-27')) -}) - -describe('duration', () => { - const durationObject = { w: 3, d: 5, h: 10, m: 43, s: 2.61 } - const durationWithTooHighValues = { w: 3, d: 19, h: 36, m: 53, s: 175.3 } - const durationInHours = { h: 17 } - const durationInDays = { d: 43 } - - test('is a function', () => expect(duration).toBeInstanceOf(Function)) - test('(null)', () => expect(() => duration(null)).toThrow(Error)) - test('()', () => expect(duration()).toBe('PT0S')) - test('empty object {}', () => expect(duration({})).toBe('PT0S')) - test('empty object {} and preserve excess value', () => expect(duration({}, false)).toBe('PT0S')) - - test('complete object', () => expect(duration(durationObject)).toBe('P3W5DT10H43M2.61S')) - test('complete object with too high values', () => expect(duration(durationWithTooHighValues)).toBe('P5W6DT12H55M55.3S')) - test('hours only', () => expect(duration(durationInHours)).toBe('PT17H')) - test('days only', () => expect(duration(durationInDays)).toBe('P6W1D')) - - test('complete object without converting the excess', () => expect(duration(durationObject, false)).toBe('P3W5DT10H43M2.61S')) - test('complete object with too high values without converting the excess', () => expect(duration(durationWithTooHighValues, false)).toBe('P3W19DT36H53M175.3S')) - test('hours only without converting the excess', () => expect(duration(durationInHours, false)).toBe('PT17H')) - test('days only without converting the excess', () => expect(duration(durationInDays, false)).toBe('P43D')) -}) - -describe('tzOffset', () => { - test('is a function', () => expect(tzOffset).toBeInstanceOf(Function)) - test('0', () => expect(tzOffset(0)).toBe('Z')) - test('-3', () => expect(tzOffset(-3)).toBe('-03:00')) - test('0, -30', () => expect(tzOffset(0, -30)).toBe('-00:30')) - test('0, 30', () => expect(tzOffset(0, 30)).toBe('+00:30')) - test('1', () => expect(tzOffset(1)).toBe('+01:00')) - test('-4.5', () => expect(tzOffset(-4.5)).toBe('-04:30')) - test('4, 30', () => expect(tzOffset(4, 30)).toBe('+04:30')) - test('12, 45', () => expect(tzOffset(12, 45)).toBe('+12:45')) - test('12.75', () => expect(tzOffset(12.75)).toBe('+12:45')) - test('-8', () => expect(tzOffset(-8)).toBe('-08:00')) - test('2, -200', () => expect(tzOffset(2, -200)).toBe('-01:20')) - test('-35', () => expect(tzOffset(-35)).toBe('-11:00')) - test('62.75', () => expect(tzOffset(62.75)).toBe('+14:45')) - test('-12, 0, true', () => expect(tzOffset(-12, 0, true)).toBe('-12:00')) - test('-12, -20, true', () => expect(tzOffset(-12, -20, true)).toBe('+11:40')) - test('-13, -20, true', () => expect(tzOffset(-13, -20, true)).toBe('+10:40')) - test('14, 45, true', () => expect(tzOffset(14, 45, true)).toBe('-09:15')) - test('62.75, 0, true', () => expect(tzOffset(62.75, 0, true)).toBe('-09:15')) - test('non number', () => expect(() => tzOffset('Z')).toThrow(TypeError)) - - // This one can’t be tested providing an exact value as the output depends on client timezone and daylight time saving. - test('()', () => expect(tzOffset()).toBe(tzOffset(0, tzOffsetInMinutes))) -}) - -describe('datetimeTz', () => { - test('is a function', () => expect(datetimeTz).toBeInstanceOf(Function)) - test('wrong type', () => expect(() => datetimeTz(123)).toThrow(TypeError)) - - test('()', () => expect(datetimeTz()).toBe(datetimeTz((new Date())))) - - test('no precision', () => expect(datetimeTz(date)).toBe('1960-04-27T00:00' + tzOffset(0, tzOffsetInMinutes))) - test('no offset', () => expect(datetimeTz(date, 'time')).toBe('00:00' + tzOffset(0, tzOffsetInMinutes))) - - test('time 0', () => expect(datetimeTz(date, 'time', 0)).toBe('00:00Z')) - test('time +4', () => expect(datetimeTz(date, 'time', 4)).toBe('00:00+04:00')) - test('second -3', () => expect(datetimeTz(date, 'second', -3)).toBe('00:00:00-03:00')) - test('ms +3.5', () => expect(datetimeTz(date, 'ms', 3.5)).toBe('00:00:00.000+03:30')) - - test('datetime +12:45', () => expect(datetimeTz(date, 'datetime', 12, 45)).toBe('1960-04-27T00:00+12:45')) - test('datetime +17, 30, true', () => expect(datetimeTz(date, 'datetime', 17, 30, true)).toBe('1960-04-27T00:00-06:30')) - test('datetime second -6', () => expect(datetimeTz(date, 'datetime second', -6)).toBe('1960-04-27T00:00:00-06:00')) - test('datetime ms +1', () => expect(datetimeTz(date, 'datetime ms', 1)).toBe('1960-04-27T00:00:00.000+01:00')) - - // These ones can’t be tested providing an exact value as the output depends on client timezone. - - test('time utc', () => expect(datetimeTz(date, 'time utc')).toBe(date.toJSON().substr(11, 5) + 'Z')) - test('second utc', () => expect(datetimeTz(date, 'second utc')).toBe(date.toJSON().substr(11, 8) + 'Z')) - test('ms utc', () => expect(datetimeTz(date, 'ms utc')).toBe(date.toJSON().substr(11, 12) + 'Z')) - - test('datetime utc', () => expect(datetimeTz(date, 'datetime utc')).toBe(date.toJSON().substr(0, 16) + 'Z')) - test('datetime second utc', () => expect(datetimeTz(date, 'datetime second utc')).toBe(date.toJSON().substr(0, 19) + 'Z')) - test('datetime ms utc', () => expect(datetimeTz(date, 'datetime ms utc')).toBe(date.toJSON())) -}) - -describe('setTzSeparator', () => { - test('is a function', () => expect(setTzSeparator).toBeInstanceOf(Function)) - - test('no timezone separator', () => { - setTzSeparator() - expect(tzOffset(3)).toBe('+0300') - }) - - test('empty string timezone separator', () => { - setTzSeparator('') - expect(tzOffset(3)).toBe('+0300') - }) - - test(': as timezone separator', () => { - setTzSeparator(':') - expect(tzOffset(3)).toBe('+03:00') - }) - - test('invalid separator', () => expect(() => setTzSeparator(42)).toThrow(Error)) - - test('empty string timezone separator with datetimeTz', () => { - setTzSeparator('') - expect(datetimeTz(date, 'datetime second', -6)).toBe('1960-04-27T00:00:00-0600') - }) -}) - -describe('setTzConfig', () => { - test('is a function', () => expect(setTzConfig).toBeInstanceOf(Function)) - - test('empty string timezone separator using setTzConfig', () => { - setTzConfig({ separator: '' }) - expect(tzOffset(3)).toBe('+0300') - }) - - test(': as timezone separator using setTzConfig', () => { - setTzConfig({ separator: ':' }) - expect(tzOffset(3)).toBe('+03:00') - }) - - test('invalid separator using setTzConfig', () => expect(() => setTzConfig({ separator: 42 })).toThrow(Error)) - - test(': as timezone separator with datetimeTz', () => { - setTzConfig({ separator: ':' }) - expect(datetimeTz(date, 'datetime second', -6)).toBe('1960-04-27T00:00:00-06:00') - }) -}) - -describe('weekNumber', () => { - test('is a function', () => expect(weekNumber).toBeInstanceOf(Function)) - test('1960-04-27', () => expect(weekNumber(date)).toBe(17)) - test('2021-01-01', () => expect(weekNumber(january1st)).toBe(53)) - test('2021-01-11', () => expect(weekNumber(january11th)).toBe(2)) - test('2020-12-31', () => expect(weekNumber(december31th2020)).toBe(53)) - test('2021-12-31', () => expect(weekNumber(december31th2021)).toBe(52)) -}) - -describe('daysBetween', () => { - test('is a function', () => expect(daysBetween).toBeInstanceOf(Function)) - test('1/1 and 11/1', () => expect(daysBetween(january1st, january11th)).toBe(10)) - test('11/1 and 1/1', () => expect(daysBetween(january11th, january1st)).toBe(-10)) - test('same day, different time', () => expect(daysBetween(december31th2020, december31th2020OneMinuteLater)).toBe(0)) -}) - -const summer = new DateTime(2021, 5, 21) -const twoWeeksAfterSummer = new DateTime(2021, 6, 5) - -describe('DateTime class', () => { - test('extends Date', () => expect(summer).toBeInstanceOf(Date)) - test('.getWeek', () => expect(summer.getWeek()).toBe(25)) - test(".to('day')", () => expect(summer.to('day')).toBe('2021-06-21')) - test('.setWeek', () => expect(summer.setWeek(27)).toBe(twoWeeksAfterSummer.getTime())) - test(".to('month')", () => expect(twoWeeksAfterSummer.to('month')).toBe('2021-07')) - test('weeks is changed when day changes', () => { - summer.setDate(summer.getDate() + 14) - expect(summer.getWeek()).toBe(29) - }) -}) - -// These tests are at the end as they change the widely-used separator config. - -describe('setTimeSeparator', () => { - test('is a function', () => expect(setTimeSeparator).toBeInstanceOf(Function)) - - test('no time separator', () => { - setTimeSeparator() - expect(datetime(date, 'datetime')).toBe('1960-04-27T00:00') - }) - - test('T as time separator', () => { - setTimeSeparator('T') - expect(datetime(date, 'datetime')).toBe('1960-04-27T00:00') - }) - - test('space as time separator', () => { - setTimeSeparator(' ') - expect(datetime(date, 'datetime')).toBe('1960-04-27 00:00') - }) - - test('invalid separator', () => expect(() => setTimeSeparator(42)).toThrow(Error)) -}) - -describe('setConfig', () => { - test('is a function', () => expect(setConfig).toBeInstanceOf(Function)) - - test('T as time separator using setConfig', () => { - setConfig({ separator: 'T' }) - expect(datetime(date, 'datetime')).toBe('1960-04-27T00:00') - }) - - test('space as time separator using setConfig', () => { - setConfig({ separator: ' ' }) - expect(datetime(date, 'datetime')).toBe('1960-04-27 00:00') - }) - - test('invalid separator using setConfig', () => expect(() => setConfig({ separator: 42 })).toThrow(Error)) -}) diff --git a/types/datetime-class.d.ts b/types/datetime-class.d.ts index 3ed7855..920bbb6 100644 --- a/types/datetime-class.d.ts +++ b/types/datetime-class.d.ts @@ -13,6 +13,9 @@ export class DateTime extends Date { /** * Set the week number. * + * If the week number is outside the year range, the `DateTime` object is + * updated accordingly. + * * @param {number} weekNumber * @returns {number} Milliseconds since midnight on January 1, 1970, UTC */ diff --git a/types/duration.d.ts b/types/duration.d.ts index c8da629..4a39d62 100644 --- a/types/duration.d.ts +++ b/types/duration.d.ts @@ -7,7 +7,9 @@ * See also: https://www.brucelawson.co.uk/2012/best-of-time/ * * @param {DurationObject} [duration={}] - * @param {boolean} [convertExcess=true] + * @param {boolean} [convertExcess=true] Convert overflow values to their + * higher unit (e.g. `{ m: 90 }` + * equals `{ h: 1, m: 30 }`. * @returns {string} */ export function duration(duration?: DurationObject, convertExcess?: boolean): string;