Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 38 additions & 8 deletions packages/ava/__tests__/unit/data/utils/isType/isDateString.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
getIsoDatePatterns,
getIsoTimePatterns,
isDateString,
parseIsoDateString,
} from '../../../../../src/data/utils/isType/isDateString';

describe('func: isDateString', () => {
Expand Down Expand Up @@ -38,21 +39,50 @@ describe('func: isDateString', () => {
});
});

describe('func: parseIsoDateString', () => {
test('parse a date string to date type', () => {
expect(parseIsoDateString('1991')).toStrictEqual(new Date('1991-01-01 00:00:00.000'));
expect(parseIsoDateString('2010 W08 1')).toStrictEqual(new Date('2010-02-15 00:00:00.000'));
expect(parseIsoDateString('2010-W08')).toStrictEqual(new Date('2010-02-15 00:00:00.000'));
expect(parseIsoDateString('2010-08-01')).toStrictEqual(new Date('2010-08-01 00:00:00.000'));
expect(parseIsoDateString('20100801')).toStrictEqual(new Date('2010-08-01 00:00:00.000'));
expect(parseIsoDateString('2010/08/01')).toStrictEqual(new Date('2010-08-01 00:00:00.000'));
expect(parseIsoDateString('12/08/1999')).toStrictEqual(new Date('1999-12-08 00:00:00.000'));
expect(parseIsoDateString('1/1/1999')).toStrictEqual(new Date('1999-01-01 00:00:00.000'));
expect(parseIsoDateString('1999-01')).toStrictEqual(new Date('1999-01 00:00:00.000'));
expect(parseIsoDateString('1999-200')).toStrictEqual(new Date(1999, 0, 200));
expect(parseIsoDateString('1999-367')).toBe(null);
expect(parseIsoDateString('2010-08-01 20:00:00')).toStrictEqual(new Date('2010-08-01 20:00:00.000'));
expect(parseIsoDateString('20:00:00')).toStrictEqual(new Date('01-01 20:00:00'));
expect(parseIsoDateString('20:00:00Z')).toStrictEqual(new Date('01-01 20:00:00Z'));
expect(parseIsoDateString('20:00:00+08:00')).toStrictEqual(new Date('01-01 20:00:00+08:00'));
expect(parseIsoDateString('20:00:00-08:00')).toStrictEqual(new Date('01-01 20:00:00-08:00'));
expect(parseIsoDateString('20:00:00.299-08:00')).toStrictEqual(new Date('01-01 20:00:00.299-08:00'));
expect(parseIsoDateString('20:00:00.299')).toStrictEqual(new Date('01-01 20:00:00.299'));
});

test('strictly recognize number as date', () => {
expect(isDateString('997815')).toBe(false);
expect(isDateString('1135028')).toBe(false);
expect(isDateString('5388715')).toBe(false);
});
});

describe('Not strict', () => {
const isoDatePatternsNotStrict = getIsoDatePatterns(false);
const isoTimePatternsNotStrict = getIsoTimePatterns(false);

expect(isoDatePatternsNotStrict).toStrictEqual([
'(18|19|20)\\d{2}',
'(18|19|20)\\d{2}([-_./\\s])?W([0-4]\\d|5[0-2])(([-_./\\s])?([1-7]))?',
'(0?[1-9]|1[012])([-_./\\s])?(0?[1-9]|[12]\\d|3[01])([-_./\\s])?(18|19|20)\\d{2}',
'(18|19|20)\\d{2}([-_./\\s])?(0?[1-9]|1[012])([-_./\\s])?(0?[1-9]|[12]\\d|3[01])',
'(18|19|20)\\d{2}([-_./\\s])?(0?[1-9]|1[012])',
'(18|19|20)\\d{2}([-_./\\s])?((([0-2]\\d|3[0-5])\\d)|36[0-6])',
'(?<year>(18|19|20)\\d{2})',
'(?<year>(18|19|20)\\d{2})([-_./\\s])?W(?<week>[0-4]\\d|5[0-2])(([-_./\\s])?(?<weekday>[1-7]))?',
'(?<month>0?[1-9]|1[012])([-_./\\s])?(?<day>0?[1-9]|[12]\\d|3[01])([-_./\\s])?(?<year>(18|19|20)\\d{2})',
'(?<year>(18|19|20)\\d{2})([-_./\\s])?(?<month>0?[1-9]|1[012])([-_./\\s])?(?<day>0?[1-9]|[12]\\d|3[01])',
'(?<year>(18|19|20)\\d{2})([-_./\\s])?(?<month>0?[1-9]|1[012])',
'(?<year>(18|19|20)\\d{2})([-_./\\s])?(?<yearDay>(([0-2]\\d|3[0-5])\\d)|36[0-6])',
]);

expect(isoTimePatternsNotStrict).toStrictEqual([
'(0?\\d|1\\d|2[0-4]):?(0?\\d|[012345]\\d):?(0?\\d|[012345]\\d)([.,]\\d{1,4})?(Z|[+-](0?\\d|1\\d|2[0-4])(:(0?\\d|[012345]\\d))?)?',
'(0?\\d|1\\d|2[0-4]):?(0?\\d|[012345]\\d)?(Z|[+-](0?\\d|1\\d|2[0-4])(:(0?\\d|[012345]\\d))?)',
'(?<hour>(0?\\d|[012345]\\d)):?(?<minute>(0?\\d|[012345]\\d)):?(?<second>(0?\\d|[012345]\\d))([.,](?<millisecond>\\d{1,4}))?(?<offset>Z|[+-](0?\\d|1\\d|2[0-4])(:(0?\\d|[012345]\\d))?)?',
'(?<hour>(0?\\d|[012345]\\d)):?(?<minute>(0?\\d|[012345]\\d))?(?<offset>Z|[+-](0?\\d|1\\d|2[0-4])(:(0?\\d|[012345]\\d))?)',
]);
});
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,6 @@ export function advicesForChart(
// if the input data cannot be transformed into DataFrame
// eslint-disable-next-line no-console
console.error('error: ', error);
return null;
return { advices: [], log: [] };
}
}
24 changes: 13 additions & 11 deletions packages/ava/src/data/utils/isType/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ export const SPECIAL_BOOLEANS = [

// For isDateString.ts
export const DELIMITER = '([-_./\\s])';
export const YEAR = '(18|19|20)\\d{2}';
export const MONTH = '(0?[1-9]|1[012])';
export const DAY = '(0?[1-9]|[12]\\d|3[01])';
export const WEEK = '([0-4]\\d|5[0-2])';
export const WEEKDAY = '([1-7])';
export const HOUR = '(0?\\d|1\\d|2[0-4])';
export const MINUTE = '(0?\\d|[012345]\\d)';
export const SECOND = MINUTE;
export const MILLISECOND = '\\d{1,4}';
export const YEARDAY = '((([0-2]\\d|3[0-5])\\d)|36[0-6])';
export const OFFSET = `(Z|[+-]${HOUR}(:${MINUTE})?)`;
export const YEAR = '(?<year>(18|19|20)\\d{2})';
export const MONTH = '(?<month>0?[1-9]|1[012])';
export const DAY = '(?<day>0?[1-9]|[12]\\d|3[01])';
export const WEEK = '(?<week>[0-4]\\d|5[0-2])';
export const WEEKDAY = '(?<weekday>[1-7])';
export const BASE_HOUR = '(0?\\d|1\\d|2[0-4])';
export const BASE_MINUTE = '(0?\\d|[012345]\\d)';
export const HOUR = `(?<hour>${BASE_MINUTE})`;
export const MINUTE = `(?<minute>${BASE_MINUTE})`;
export const SECOND = `(?<second>${BASE_MINUTE})`;
export const MILLISECOND = '(?<millisecond>\\d{1,4})';
export const YEARDAY = '(?<yearDay>(([0-2]\\d|3[0-5])\\d)|36[0-6])';
export const OFFSET = `(?<offset>Z|[+-]${BASE_HOUR}(:${BASE_MINUTE})?)`;
42 changes: 42 additions & 0 deletions packages/ava/src/data/utils/isType/isDateString.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,45 @@ export function isDateString(value: unknown, strictDatePattern?: boolean): value
}
return false;
}

/** parse ISO 8601 date string to standard Date type, if month and date is missing, will use new Date(01-01)
* Reference: https://www.cl.cam.ac.uk/~mgk25/iso-time.html
* 将日期字符串转为标准 Date 类型,如果没有月日,只有时间信息,会默认为 01-01
*/
export function parseIsoDateString(value: string, strictDatePattern: boolean = false): Date | null {
const isoDateAndTimeRegs = getIsoDateAndTimeRegs(strictDatePattern);
for (let i = 0; i < isoDateAndTimeRegs.length; i += 1) {
const reg = isoDateAndTimeRegs[i];
if (reg.test(value.trim())) {
const matches = value.trim().match(reg);
if (matches.groups) {
const { year, month, day, week, weekday, hour, minute, second, millisecond, yearDay, offset } =
matches.groups || {};
const yearNum = parseInt(year, 10);
if (yearDay) {
return new Date(yearNum, 0, parseInt(yearDay, 10));
}

if (week) {
const weekNum = parseInt(week, 10);
const weekDayNum = weekday ? parseInt(weekday, 10) : 1;
const firstDayOfYear = new Date(yearNum, 0, 1);
// 给定年份的第一天是周几
const firstDayOfYearDayOfWeek = firstDayOfYear.getDay() === 0 ? 7 : firstDayOfYear.getDay();
// 计算第一周的第一天相对 firstDayOfYear 的偏移量
const firstWeekStartDayOffset = firstDayOfYearDayOfWeek === 1 ? 1 : 1 - firstDayOfYearDayOfWeek;
// 目标日期偏移量
const targetDateOffset = (weekNum - 1) * 7 + (weekDayNum + firstWeekStartDayOffset);
return new Date(yearNum, 0, targetDateOffset);
}

const formattedDateString = [year, month ?? '01', day ?? '01'].join('-');
const formattedTimeString = `${[hour ?? '00', minute ?? '00', second ?? '00'].join(':')}.${
millisecond ?? '000'
}${offset ?? ''}`;
return new Date(`${formattedDateString} ${formattedTimeString}`);
}
}
}
return null;
}