2025-03-09 16:20:48 +08:00

238 lines
5.9 KiB
TypeScript

/**
* 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 _ from 'lodash';
import { dayjs } from './dayjs';
export interface Str2momentOptions {
gmt?: boolean;
picker?: 'year' | 'month' | 'week' | 'quarter';
utcOffset?: number;
utc?: boolean;
}
export type Str2momentValue = string | string[] | dayjs.Dayjs | dayjs.Dayjs[];
export interface GetDefaultFormatProps {
format?: string;
dateFormat?: string;
timeFormat?: string;
picker?: 'year' | 'month' | 'week' | 'quarter';
showTime?: boolean;
}
export const getDefaultFormat = (props: GetDefaultFormatProps) => {
if (props.format) {
return props.format;
}
if (props.dateFormat) {
if (props['showTime']) {
return `${props.dateFormat} ${props.timeFormat || 'HH:mm:ss'}`;
}
return props.dateFormat;
}
if (props['picker'] === 'month') {
return 'YYYY-MM';
} else if (props['picker'] === 'quarter') {
return 'YYYY-\\QQ';
} else if (props['picker'] === 'year') {
return 'YYYY';
} else if (props['picker'] === 'week') {
return 'YYYY[W]W';
}
return props['showTime'] ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD';
};
export const toGmt = (value: dayjs.Dayjs) => {
if (!value || !dayjs.isDayjs(value)) {
return value;
}
return `${value.format('YYYY-MM-DD')}T${value.format('HH:mm:ss.SSS')}Z`;
};
export const toLocal = (value: dayjs.Dayjs) => {
if (!value) {
return value;
}
if (Array.isArray(value)) {
return value.map((val) => val.startOf('second').toISOString());
}
if (dayjs.isDayjs(value)) {
return value.startOf('second').toISOString();
}
};
const convertQuarterToFirstDay = (quarterStr) => {
if (dayjs(quarterStr).isValid()) {
const year = parseInt(quarterStr.slice(0, 4)); // 提取年份
const quarter = parseInt(quarterStr.slice(-1)); // 提取季度数字
return dayjs().quarter(quarter).year(year);
}
return null;
};
const toMoment = (val: any, options?: Str2momentOptions) => {
if (!val) {
return;
}
const offset = options.utcOffset;
const { gmt, picker, utc = true } = options;
if (dayjs(val).isValid()) {
if (!utc) {
return dayjs(val);
}
if (dayjs.isDayjs(val)) {
return offset ? val.utcOffset(offsetFromString(offset)) : val;
}
if (gmt) {
return dayjs(val).utcOffset(0);
}
return offset ? dayjs(val).utcOffset(offsetFromString(offset)) : dayjs(val);
} else {
return convertQuarterToFirstDay(val);
}
};
export const str2moment = (
value?: string | string[] | dayjs.Dayjs | dayjs.Dayjs[],
options: Str2momentOptions = {},
): any => {
return Array.isArray(value)
? value.map((val) => {
return toMoment(val, options);
})
: value
? toMoment(value, options)
: value;
};
const toStringByPicker = (value, picker) => {
if (picker === 'year') {
return value.format('YYYY') + '-01-01T00:00:00.000Z';
}
if (picker === 'month') {
return value.format('YYYY-MM') + '-01T00:00:00.000Z';
}
if (picker === 'quarter') {
return value.format('YYYY-MM') + '-01T00:00:00.000Z';
}
if (picker === 'week') {
return value.format('YYYY-MM-DD') + 'T00:00:00.000Z';
}
return value.format('YYYY-MM-DD') + 'T00:00:00.000Z';
};
const toGmtByPicker = (value: dayjs.Dayjs | dayjs.Dayjs[], picker?: any) => {
if (!value) {
return value;
}
if (Array.isArray(value)) {
return value.map((val) => toStringByPicker(val, picker));
}
if (dayjs.isDayjs(value)) {
return toStringByPicker(value, picker);
}
};
export interface Moment2strOptions {
showTime?: boolean;
gmt?: boolean;
picker?: 'year' | 'month' | 'week' | 'quarter';
}
export const moment2str = (value?: dayjs.Dayjs, options: Moment2strOptions = {}) => {
const { showTime, gmt, picker } = options;
if (!value) {
return value;
}
if (showTime) {
return gmt ? toGmt(value) : toLocal(value);
}
return toGmtByPicker(value, picker);
};
/**
* from https://github.com/moment/moment/blob/dca02edaeceda3fcd52b20b51c130631a058a022/src/lib/units/offset.js#L55-L70
*/
export function offsetFromString(string: string | number) {
if (!_.isString(string)) {
return string;
}
// timezone chunker
// '+10:00' > ['10', '00']
// '-1530' > ['-15', '30']
const chunkOffset = /([+-]|\d\d)/gi;
const matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi, // +00 -00 +00:00 -00:00 +0000 -0000 or Z
matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123
let matches = (string || '').match(matchShortOffset);
if (matches === null) {
matches = (string || '').match(matchTimestamp);
}
if (matches === null) {
return null;
}
const chunk = matches[matches.length - 1] || [];
const parts = (chunk + '').match(chunkOffset) || ['-', 0, 0];
const minutes = +(Number(parts[1]) * 60) + toInt(parts[2]);
return minutes === 0 ? 0 : parts[0] === '+' ? minutes : -minutes;
}
function toInt(argumentForCoercion) {
// eslint-disable-next-line prefer-const
let coercedNumber = +argumentForCoercion,
value = 0;
if (coercedNumber !== 0 && isFinite(coercedNumber)) {
value = absFloor(coercedNumber);
}
return value;
}
function absFloor(number) {
if (number < 0) {
// -0 -> 0
return Math.ceil(number) || 0;
} else {
return Math.floor(number);
}
}
export const getPickerFormat = (picker) => {
switch (picker) {
case 'week':
return 'YYYY[W]W';
case 'month':
return 'YYYY-MM';
case 'quarter':
return 'YYYY[Q]Q';
case 'year':
return 'YYYY';
default:
return 'YYYY-MM-DD';
}
};
export const getDateTimeFormat = (picker, format, showTime, timeFormat) => {
if (picker === 'date') {
if (showTime) {
return `${format} ${timeFormat || 'HH:mm:ss'}`;
}
return format;
}
return format;
};