Skip to content

Commit

Permalink
feat: add Duration (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
ValeraS authored Apr 23, 2024
1 parent 5f3d4e9 commit 2da2434
Show file tree
Hide file tree
Showing 23 changed files with 1,800 additions and 128 deletions.
25 changes: 16 additions & 9 deletions src/dateTime/dateTime.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {STRICT, UtcTimeZone} from '../constants';
import dayjs from '../dayjs';
import {duration} from '../duration';
import {settings} from '../settings';
import {fixOffset, guessUserTimeZone, normalizeTimeZone, timeZoneOffset} from '../timeZone';
import type {
AllUnit,
DateTime,
DateTimeInput,
DurationInput,
DurationUnit,
FormatInput,
SetObject,
Expand All @@ -14,7 +16,6 @@ import type {
} from '../typings';
import {
daysInMonth,
getDuration,
normalizeComponent,
normalizeDateComponents,
objToTS,
Expand Down Expand Up @@ -117,11 +118,11 @@ class DateTimeImpl implements DateTime {
return createDateTime({ts, timeZone: zone, offset, locale: this._locale});
}

add(amount: DateTimeInput, unit?: DurationUnit): DateTime {
add(amount: DurationInput, unit?: DurationUnit): DateTime {
return this.addSubtract(amount, unit, 1);
}

subtract(amount: DateTimeInput, unit?: DurationUnit): DateTime {
subtract(amount: DurationInput, unit?: DurationUnit): DateTime {
return this.addSubtract(amount, unit, -1);
}

Expand Down Expand Up @@ -303,6 +304,7 @@ class DateTimeImpl implements DateTime {
const dateComponents = tsToObject(this._timestamp, this._offset);
const newComponents = normalizeDateComponents(
typeof unit === 'object' ? unit : {[unit]: amount},
normalizeComponent,
);

const settingWeekStuff =
Expand Down Expand Up @@ -470,7 +472,7 @@ class DateTimeImpl implements DateTime {
toString(): string {
return this._date.toString();
}
private addSubtract(amount: DateTimeInput, unit: DurationUnit | undefined, sign: 1 | -1) {
private addSubtract(amount: DurationInput, unit: DurationUnit | undefined, sign: 1 | -1) {
if (!this.isValid()) {
return this;
}
Expand All @@ -479,11 +481,16 @@ class DateTimeImpl implements DateTime {
let ts = this.valueOf();
let offset = this._offset;

const duration = getDuration(amount, unit);
const dur = duration(amount, unit);
const dateComponents = tsToObject(ts, offset);

const monthsInput = absRound(duration.months);
const daysInput = absRound(duration.days);
const monthsInput = absRound(dur.months() + dur.quarters() * 3 + dur.years() * 12);
const daysInput = absRound(dur.days() + dur.weeks() * 7);
const milliseconds =
dur.milliseconds() +
dur.seconds() * 1000 +
dur.minutes() * 60 * 1000 +
dur.hours() * 60 * 60 * 1000;

if (monthsInput || daysInput) {
const month = dateComponents.month + sign * monthsInput;
Expand All @@ -498,8 +505,8 @@ class DateTimeImpl implements DateTime {
}
}

if (duration.milliseconds) {
ts += sign * duration.milliseconds;
if (milliseconds) {
ts += sign * milliseconds;
if (timeZone !== UtcTimeZone) {
offset = timeZoneOffset(timeZone, ts);
}
Expand Down
5 changes: 2 additions & 3 deletions src/datemath/datemath.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright 2015 Grafana Labs
// Copyright 2021 YANDEX LLC

import each from 'lodash/each';
import sinon from 'sinon';
import type {SinonFakeTimers} from 'sinon';

Expand Down Expand Up @@ -76,7 +75,7 @@ describe('DateMath', () => {
anchored = dateTime({input: anchor});
});

each(spans, (span) => {
spans.forEach((span) => {
const nowEx = 'now-5' + span;
const thenEx = anchor + '||-5' + span;

Expand Down Expand Up @@ -106,7 +105,7 @@ describe('DateMath', () => {
now = dateTime();
});

each(spans, (span) => {
spans.forEach((span) => {
it('should round now to the beginning of the ' + span, () => {
expect(dateMath.parse('now/' + span)?.format(format)).toEqual(
now.startOf(span).format(format),
Expand Down
4 changes: 1 addition & 3 deletions src/datemath/datemath.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Copyright 2015 Grafana Labs
// Copyright 2021 YANDEX LLC

import includes from 'lodash/includes';

import {dateTime} from '../dateTime';
import type {DateTime, DurationUnit, TimeZone} from '../typings';

Expand Down Expand Up @@ -106,7 +104,7 @@ export function parseDateMath(

const unit = strippedMathString.charAt(i++) as DurationUnit;

if (includes(units, unit)) {
if (units.includes(unit)) {
if (type === 0) {
if (roundUp) {
resultTime = resultTime.endOf(unit);
Expand Down
99 changes: 99 additions & 0 deletions src/duration/__tests__/createDuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright 2019 JS Foundation and other contributors
// Copyright 2024 YANDEX LLC

import {duration} from '../';

test('Duration sets all the values', () => {
const dur = duration({
years: 1,
months: 2,
days: 3,
hours: 4,
minutes: 5,
seconds: 6,
milliseconds: 7,
});
expect(dur.years()).toBe(1);
expect(dur.months()).toBe(2);
expect(dur.days()).toBe(3);
expect(dur.hours()).toBe(4);
expect(dur.minutes()).toBe(5);
expect(dur.seconds()).toBe(6);
expect(dur.milliseconds()).toBe(7);
});

test('Duration sets all the fractional values', () => {
const dur = duration({
years: 1,
months: 2,
days: 3,
hours: 4.5,
minutes: 5,
});
expect(dur.years()).toBe(1);
expect(dur.months()).toBe(2);
expect(dur.days()).toBe(3);
expect(dur.hours()).toBe(4.5);
expect(dur.minutes()).toBe(5);
expect(dur.seconds()).toBe(0);
expect(dur.milliseconds()).toBe(0);
});

test('Duration sets all the values from the object having string type values', () => {
const dur = duration({
years: '1',
months: '2',
days: '3',
hours: '4',
minutes: '5',
seconds: '6',
milliseconds: '7',
});
expect(dur.years()).toBe(1);
expect(dur.months()).toBe(2);
expect(dur.days()).toBe(3);
expect(dur.hours()).toBe(4);
expect(dur.minutes()).toBe(5);
expect(dur.seconds()).toBe(6);
expect(dur.milliseconds()).toBe(7);
});

test('Duration({}) constructs zero duration', () => {
const dur = duration({});
expect(dur.years()).toBe(0);
expect(dur.months()).toBe(0);
expect(dur.days()).toBe(0);
expect(dur.hours()).toBe(0);
expect(dur.minutes()).toBe(0);
expect(dur.seconds()).toBe(0);
expect(dur.milliseconds()).toBe(0);
});

test('Duration throws if the initial object has invalid keys', () => {
// @ts-expect-error
expect(() => duration({foo: 0})).toThrow();
// @ts-expect-error
expect(() => duration({years: 1, foo: 0})).toThrow();
});

test('Duration throws if the initial object has invalid values', () => {
// @ts-expect-error
expect(() => duration({years: {}})).toThrow();
expect(() => duration({months: 'some'})).toThrow();
expect(() => duration({days: NaN})).toThrow();
// @ts-expect-error
expect(() => duration({hours: true})).toThrow();
// @ts-expect-error
expect(() => duration({minutes: false})).toThrow();
expect(() => duration({seconds: ''})).toThrow();
});

it('Duration returns passed Duration', () => {
const durFromObject = duration({hours: 1});
const dur = duration(durFromObject);
expect(dur).toStrictEqual(durFromObject);
});

it('Duration throws on invalid input', () => {
expect(() => duration('foo')).toThrow();
});
Loading

0 comments on commit 2da2434

Please sign in to comment.