Skip to content

Commit

Permalink
feat: parse well known date strings
Browse files Browse the repository at this point in the history
  • Loading branch information
ValeraS committed Nov 1, 2024
1 parent a511008 commit 615b24e
Show file tree
Hide file tree
Showing 8 changed files with 735 additions and 42 deletions.
84 changes: 59 additions & 25 deletions src/dateTime/dateTime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ import {
weeksInWeekYear,
} from '../utils';
import type {DateObject} from '../utils';
import {getLocaleData, getLocaleWeekValues} from '../utils/locale';

import {formatDate} from './format';
import {getTimestampFromArray, getTimestampFromObject} from './parse';
import {parseDateString} from './regexParse';
import {fromTo} from './relative';

const IS_DATE_TIME = Symbol('isDateTime');
Expand Down Expand Up @@ -281,15 +283,15 @@ class DateTimeImpl implements DateTime {
}

isSame(input?: DateTimeInput, granularity?: DurationUnit): boolean {
const [ts] = getTimestamp(input, 'system');
const [ts] = getTimestamp(input, 'system', this._locale);
if (!this.isValid() || isNaN(ts)) {
return false;
}
return !this.isBefore(ts, granularity) && !this.isAfter(ts, granularity);
}

isBefore(input?: DateTimeInput, granularity?: DurationUnit): boolean {
const [ts] = getTimestamp(input, 'system');
const [ts] = getTimestamp(input, 'system', this._locale);
if (!this.isValid() || isNaN(ts)) {
return false;
}
Expand All @@ -299,7 +301,7 @@ class DateTimeImpl implements DateTime {
}

isAfter(input?: DateTimeInput, granularity?: DurationUnit): boolean {
const [ts] = getTimestamp(input, 'system');
const [ts] = getTimestamp(input, 'system', this._locale);
if (!this.isValid() || isNaN(ts)) {
return false;
}
Expand All @@ -323,7 +325,7 @@ class DateTimeImpl implements DateTime {
const value = DateTimeImpl.isDateTime(amount)
? amount.timeZone(this._timeZone)
: createDateTime({
ts: getTimestamp(amount, 'system')[0],
ts: getTimestamp(amount, 'system', this._locale)[0],
timeZone: this._timeZone,
locale: this._locale,
offset: this._offset,
Expand Down Expand Up @@ -772,11 +774,6 @@ class DateTimeImpl implements DateTime {
}
}

function getLocaleWeekValues(localeData: {yearStart?: number; weekStart?: number}) {
const {weekStart, yearStart} = localeData;
return {startOfWeek: weekStart || 7, minDaysInFirstWeek: yearStart || 1};
}

function absRound(v: number) {
const sign = Math.sign(v);
return Math.round(sign * v) * sign;
Expand Down Expand Up @@ -817,20 +814,27 @@ function createDateTime({
locale: string;
}): DateTime {
const loc = locale || 'en';
const localeData = dayjs.Ls[loc] as Locale;
const localeData = getLocaleData(loc);
const isValid = !isNaN(Number(new Date(ts)));
return new DateTimeImpl({ts, timeZone, offset, locale: loc, localeData, isValid});
return new DateTimeImpl({
ts,
timeZone,
offset,
locale: loc,
localeData,
isValid,
});
}

function getTimestamp(
input: DateTimeInput,
timezone: string,
locale: string,
format?: string,
lang?: string,
utc = false,
fixedOffset?: number,
): [ts: number, offset: number] {
let ts: number;
let offset: number | undefined;
let offset = fixedOffset;
if (
isDateTime(input) ||
typeof input === 'number' ||
Expand All @@ -841,18 +845,35 @@ function getTimestamp(
} else if (input === null || input === undefined) {
ts = Date.now();
} else if (Array.isArray(input)) {
[ts, offset] = getTimestampFromArray(input, timezone);
[ts, offset] = getTimestampFromArray(input, timezone, fixedOffset);
} else if (typeof input === 'object') {
[ts, offset] = getTimestampFromObject(input, timezone);
} else if (utc) {
ts = dayjs.utc(input, format, STRICT).valueOf();
} else {
const locale = dayjs.locale(lang || settings.getLocale(), undefined, true);
[ts, offset] = getTimestampFromObject(input, timezone, fixedOffset);
} else if (format === undefined) {
const [dateObject, timezoneOrOffset] = parseDateString(input);
if (Object.keys(dateObject).length === 0) {
return [NaN, NaN];
}
[ts] = getTimestampFromObject(
dateObject,
typeof timezoneOrOffset === 'string' ? timezoneOrOffset : 'system',
typeof timezoneOrOffset === 'number' ? timezoneOrOffset : fixedOffset,
);
if (
fixedOffset !== undefined &&
timezoneOrOffset !== null &&
timezoneOrOffset !== fixedOffset
) {
ts -= fixedOffset * 60 * 1000;
}
} else if (fixedOffset === undefined) {
const localDate = format
? dayjs(input, format, locale, STRICT)
: dayjs(input, undefined, locale);

ts = localDate.valueOf();
} else {
ts = dayjs.utc(input, format, STRICT).valueOf();
ts -= fixedOffset * 60 * 1000;
}

offset = offset ?? timeZoneOffset(timezone, ts);
Expand Down Expand Up @@ -886,7 +907,7 @@ export function dateTime(opt?: {
const timeZoneOrDefault = normalizeTimeZone(timeZone, settings.getDefaultTimeZone());
const locale = dayjs.locale(lang || settings.getLocale(), undefined, true);

const [ts, offset] = getTimestamp(input, timeZoneOrDefault, format, lang);
const [ts, offset] = getTimestamp(input, timeZoneOrDefault, locale, format);

const date = createDateTime({
ts,
Expand All @@ -898,17 +919,30 @@ export function dateTime(opt?: {
return date;
}

export function dateTimeUtc(opt?: {input?: DateTimeInput; format?: FormatInput; lang?: string}) {
const {input, format, lang} = opt || {};
/**
* Creates a DateTime instance with fixed offset.
* @param [opt]
* @param {DateTimeInput=} [opt.input] - input to parse.
* @param {string=} [opt.format] - strict {@link https://dayjs.gitee.io/docs/en/display/format format} for parsing user's input.
* @param {number=} [opt.offset=0] - specified offset.
* @param {string=} [opt.lang] - specified locale.
*/
export function dateTimeUtc(opt?: {
input?: DateTimeInput;
format?: FormatInput;
lang?: string;
offset?: number;
}): DateTime {
const {input, format, lang, offset = 0} = opt || {};

const locale = dayjs.locale(lang || settings.getLocale(), undefined, true);

const [ts] = getTimestamp(input, UtcTimeZone, format, lang, true);
const [ts] = getTimestamp(input, UtcTimeZone, locale, format, offset);

const date = createDateTime({
ts,
timeZone: UtcTimeZone,
offset: 0,
offset,
locale,
});

Expand Down
18 changes: 18 additions & 0 deletions src/dateTime/dateTimeUtc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,23 @@ describe('DateTimeUtc', () => {
const date = dateTimeUtc({input, format}).toISOString();
expect(date).toEqual(expected);
});

test.each<[string, string]>([
['2023-12-31', '2023-12-31T00:00:00.000+02:30'],
['2023-12-31T01:00', '2023-12-31T01:00:00.000+02:30'],
['2023-12-31T01:00Z', '2023-12-31T01:00:00.000+02:30'],
['2023-12-31T03:00+02:00', '2023-12-31T01:00:00.000+02:30'],
])('input option (%p) with offset', (input, expected) => {
const date = dateTimeUtc({input, offset: 150}).toISOString(true);
expect(date).toEqual(expected);
});

test.each<[string, string, string]>([
['31.12.2023', 'DD.MM.YYYY', '2023-12-31T00:00:00.000+02:30'],
['31.12.2023 01:00', 'DD.MM.YYYY HH:mm', '2023-12-31T01:00:00.000+02:30'],
])('input (%p) format (%p) with offset', (input, format, expected) => {
const date = dateTimeUtc({input, format, offset: 150}).toISOString(true);
expect(date).toEqual(expected);
});
});
});
Loading

0 comments on commit 615b24e

Please sign in to comment.