/** * This file is part of the NocoBase (R) project. * Copyright (c) 2020-2024 NocoBase Co., Ltd. * Authors: NocoBase Team. * * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. * For more information, please refer to: https://www.nocobase.com/agreement. */ import { offsetFromString } from './date'; import { dayjs } from './dayjs'; function parseUTC(value) { if (value instanceof Date || dayjs.isDayjs(value)) { return { unit: 'utc', start: value.toISOString(), }; } if (value.endsWith('Z')) { return { unit: 'utc', start: value, }; } } function parseYear(value) { if (/^\d\d\d\d$/.test(value)) { return { unit: 'year', start: `${value}-01-01 00:00:00`, }; } } function parseQuarter(value) { if (/^\d\d\d\dQ\d$/.test(value)) { const [year, q] = value.split('Q'); return { unit: 'quarter', start: dayjs(year, 'YYYY').quarter(q).format('YYYY-MM-DD HH:mm:ss'), }; } } export function parseWeek(value) { if (/^\d\d\d\d[W]\d\d$/.test(value)) { const arr = value.split('W'); const year = dayjs(arr[0], 'YYYY').format('GGGG'); if (year !== arr[0]) { return { unit: 'isoWeek', start: dayjs(arr[0], 'YYYY') .add(1, 'week') .startOf('isoWeek') .isoWeek(Number(arr[1])) .format('YYYY-MM-DD HH:mm:ss'), }; } return { unit: 'isoWeek', start: dayjs(arr[0], 'YYYY').isoWeek(Number(arr[1])).format('YYYY-MM-DD HH:mm:ss'), }; } if (/^\d\d\d\d[w]\d\d$/.test(value)) { const arr = value.split('w'); const year = dayjs(arr[0], 'YYYY').format('gggg'); if (year !== arr[0]) { return { unit: 'week', start: dayjs(arr[0], 'YYYY').add(1, 'week').startOf('week').week(Number(arr[1])).format('YYYY-MM-DD HH:mm:ss'), }; } return { unit: 'week', start: dayjs(arr[0], 'YYYY').week(Number(arr[1])).format('YYYY-MM-DD HH:mm:ss'), }; } } function parseMonth(value) { if (/^\d\d\d\d\-\d\d$/.test(value)) { return { unit: 'month', start: `${value}-01 00:00:00`, }; } } function parseDay(value) { if (/^\d\d\d\d\-\d\d\-\d\d$/.test(value)) { return { unit: 'day', start: `${value} 00:00:00`, }; } } function parseHour(value) { if (/^\d\d\d\d\-\d\d\-\d\d(\T|\s)\d\d$/.test(value)) { return { unit: 'hour', start: `${value}:00:00`, }; } } function parseMinute(value) { if (/^\d\d\d\d\-\d\d\-\d\d(\T|\s)\d\d\:\d\d$/.test(value)) { return { unit: 'minute', start: `${value}:00`, }; } } function parseSecond(value) { if (/^\d\d\d\d\-\d\d\-\d\d(\T|\s)\d\d\:\d\d\:\d\d$/.test(value)) { return { unit: 'second', start: `${value}`, }; } } function parseMillisecond(value) { if (/^\d\d\d\d\-\d\d\-\d\d(\T|\s)\d\d\:\d\d\:\d\d\.\d\d\d$/.test(value)) { return { unit: 'millisecond', start: `${value}`, }; } } const parsers = [ parseUTC, parseYear, parseQuarter, parseWeek, parseMonth, parseDay, parseHour, parseMinute, parseSecond, parseMillisecond, ]; type ParseDateResult = { unit: any; start: string; timezone?: string; }; function toISOString(m: dayjs.Dayjs) { return m.toISOString(); } function dateRange(r: ParseDateResult) { if (!r.timezone) { r.timezone = '+00:00'; } let m: dayjs.Dayjs; if (r.unit === 'utc') { return dayjs(r?.start).toISOString(); } else { m = dayjs(`${r?.start}${r?.timezone}`); } m = m.utcOffset(offsetFromString(r.timezone)); return [(m = m.startOf(r.unit)), m.add(1, r.unit === 'isoWeek' ? 'weeks' : r.unit).startOf(r.unit)].map(toISOString); } export function parseDate(value: any, options = {} as { timezone?: string }) { if (!value) { return; } if (Array.isArray(value)) { return parseDateBetween(value, options); } let timezone = options.timezone || '+00:00'; const input = value; if (typeof value === 'string') { const match = /(.+)((\+|\-)\d\d\:\d\d)$/.exec(value); if (match) { value = match[1]; timezone = match[2]; } if (/^(\(|\[)/.test(value)) { return parseDateBetween(input, options); } } for (const parse of parsers) { const r = parse(value); if (r) { r['input'] = input; if (!r['timezone']) { r['timezone'] = timezone; } return dateRange(r); } } } function parseDateBetween(value: any, options = {} as { timezone?: string }) { if (Array.isArray(value) && value.length > 1) { const [startValue, endValue, op = '[]', timezone] = value; const r0 = parseDate(startValue, { timezone }); const r1 = parseDate(endValue, { timezone }); let start; let startOp; let end; let endOp; if (typeof r0 === 'string') { start = r0; startOp = op[0]; } else { start = op.startsWith('(') ? r0[1] : r0[0]; startOp = '['; } if (typeof r1 === 'string') { end = r1; endOp = op[1]; } else { end = op.endsWith(')') ? r1[0] : r1[1]; endOp = ')'; } const newOp = startOp + endOp; return newOp === '[)' ? [start, end] : [start, end, newOp]; } if (typeof value !== 'string') { return; } const match = /(.+)((\+|\-)\d\d\:\d\d)$/.exec(value); let timezone = options.timezone || '+00:00'; if (match) { value = match[1]; timezone = match[2]; } const m = /^(\(|\[)(.+)\,(.+)(\)|\])$/.exec(value); if (!m) { return; } return parseDateBetween([m[2], m[3], `${m[1]}${m[4]}`, timezone]); }