duoyun-ui
Version:
A lightweight desktop UI component library, implemented using Gem
255 lines • 8.24 kB
JavaScript
/* eslint-disable no-fallthrough */
/* biome-ignore-all lint/suspicious/noFallthroughSwitchClause: falls through */
import { locale } from './locale';
const intlDigitFormatter = Intl.DateTimeFormat(undefined, {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
});
/**Parse Date/number to object */
export function parseDate(date) {
const parts = Number.isNaN(Number(date)) ? [] : intlDigitFormatter.formatToParts(date);
const dateObj = {};
parts.forEach(({ type, value }) => (dateObj[type] = value));
const { year = '', month = '', day = '', hour = '', minute = '', second = '' } = dateObj;
return { year, month, day, hour, minute, second };
}
const Ds = 1000;
const Dm = Ds * 60;
const Dh = Dm * 60;
const Dd = Dh * 24;
const Dw = Dd * 7;
const DM = Dd * 31;
const DY = Dd * 366;
const durationList = [
['Y', DY],
['M', DM],
['w', Dw],
['d', Dd],
['h', Dh],
['m', Dm],
['s', Ds],
];
const durationMap = Object.fromEntries(durationList);
const unitMap = {
Y: 'year',
M: 'month',
w: 'week',
d: 'day',
h: 'hour',
m: 'minute',
s: 'second',
};
export function parseNarrowTimeRange(str) {
if (!str)
return;
if (str in unitMap) {
return [new Time().startOf(str), new Time().endOf(str)];
}
}
export function parseNarrowRelativeTime(str) {
if (!str)
return;
const r = str.match(/^(?<number>-?\d+)(?<unit>\w)$/);
if (r?.groups && r.groups.unit in unitMap) {
const number = Number(r.groups.number);
const unit = r.groups.unit;
return new Time().add(number, unit);
}
}
function getDuration(unit) {
switch (unit) {
case 'h':
return Dh;
case 'm':
return Dm;
case 's':
return Ds;
default:
return 1;
}
}
/**
* Provide some convenient operation methods for Date/Time
*/
export class Time extends Date {
static #rtfCache = new Map();
static #formatReg = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|H{1,2}|m{1,2}|s{1,2}/g;
get isInvalidTime() {
return Number.isNaN(this.valueOf());
}
formatToParts(opt = { year: 'numeric', month: 'short' }) {
if (this.isInvalidTime)
return [];
return Intl.DateTimeFormat(locale.localeCode, opt).formatToParts(this);
}
format(opt = 'YYYY-MM-DD HH:mm:ss') {
if (typeof opt !== 'string') {
if (this.isInvalidTime)
return '';
return Intl.DateTimeFormat(locale.localeCode, opt).format(this);
}
const { year, month, day, hour, minute, second } = parseDate(this);
const map = {
YYYY: year,
MM: month,
DD: day,
HH: hour,
mm: minute,
ss: second,
};
return opt.replace(Time.#formatReg, (match) => map[match]);
}
subtract(number, unit) {
switch (unit) {
case 'Y':
this.setFullYear(this.getFullYear() - number);
break;
case 'M':
this.setMonth(this.getMonth() - number);
break;
case 'w':
this.setDate(this.getDate() - 7 * number);
break;
case 'd':
this.setDate(this.getDate() - number);
break;
default:
this.setTime(this.getTime() - number * getDuration(unit));
}
return this;
}
add(number, unit) {
this.subtract(-number, unit);
return this;
}
startOf(unit) {
switch (unit) {
case 'w':
this.subtract(this.getDay(), 'd').startOf('d');
break;
case 'Y':
this.setMonth(0);
case 'M':
this.setDate(1);
case 'd':
this.setHours(0);
case 'h':
this.setMinutes(0);
case 'm':
this.setSeconds(0);
case 's':
this.setMilliseconds(0);
case 'ms':
}
return this;
}
endOf(unit) {
this.add(1, unit).startOf(unit).subtract(1, 'ms');
return this;
}
isSame(d, unit) {
let result = true;
const targetDate = new Time(d);
switch (unit) {
case 'w': {
const ws = new Time(this).subtract(this.getDay(), 'd').startOf('d');
return targetDate.getTime() >= ws.getTime() && targetDate.getTime() <= ws.getTime() + 7 * Dd;
}
case 'ms':
return this.getTime() === targetDate.getTime();
case 's':
result &&= this.getSeconds() === targetDate.getSeconds();
case 'm':
result &&= this.getMinutes() === targetDate.getMinutes();
case 'h':
result &&= this.getHours() === targetDate.getHours();
case 'd':
result &&= this.getDate() === targetDate.getDate();
case 'M':
result &&= this.getMonth() === targetDate.getMonth();
case 'Y':
result &&= this.getFullYear() === targetDate.getFullYear();
}
return result;
}
/**@deprecated */
isSome = this.isSame;
diff(d, unit = 'ms') {
const diff = this.valueOf() - d.valueOf();
if (unit === 'ms')
return diff;
return diff / durationMap[unit];
}
/**
* new D().relativeTimeFormat(new D().add(40, 'd'), { unitLimit: 2, lang: 'zh' }) => '40天后'
*/
relativeTimeFormat(date, { min = 1, lang = locale.localeCode, rtf = Time.#rtfCache.get(String(lang)) } = {}) {
if (!rtf) {
rtf = new Intl.RelativeTimeFormat(lang, { style: 'short', numeric: 'auto' });
Time.#rtfCache.set(String(lang), rtf);
}
const target = new Time(date);
const diff = target.getTime() - this.getTime();
for (const [unit, d] of durationList) {
const n = Math.trunc(diff / d);
if (Math.abs(n) >= 1 * min) {
const normalizeDiff = target.startOf(unit).getTime() - new Time(this).startOf(unit).getTime();
return rtf.format(Math.round(normalizeDiff / d), unitMap[unit]);
}
}
return rtf.format(0, 'minute');
}
}
/**Parse timestamp to Duration object */
export function parseDuration(ms) {
if (ms >= DY) {
return { number: Math.ceil(ms / DY).toString(), unit: locale.year };
}
if (ms >= DM) {
return { number: Math.ceil(ms / DM).toString(), unit: locale.month };
}
if (ms >= Dw) {
return { number: Math.ceil(ms / Dw).toString(), unit: locale.week };
}
if (ms >= Dd) {
return { number: Math.ceil(ms / Dd).toString(), unit: locale.day };
}
if (ms >= Dh) {
return { number: Math.ceil(ms / Dh).toString(), unit: locale.hour };
}
if (ms >= Dm) {
return { number: Math.ceil(ms / Dm).toString(), unit: locale.minute };
}
if (ms >= Ds) {
return { number: Math.ceil(ms / Ds).toString(), unit: locale.second };
}
return { number: Math.ceil(ms).toString(), unit: locale.millisecond };
}
export function parseDurationToParts(ms, hasDays) {
const days = hasDays ? Math.floor(ms / Dd) : 0;
const hours = Math.floor((hasDays ? ms % Dd : ms) / Dh);
const minutes = Math.floor((ms % Dh) / Dm);
const seconds = Math.floor((ms % Dm) / Ds);
const milliseconds = ms % Ds;
return { days, hours, minutes, seconds, milliseconds };
}
export function formatDuration(ms, { numeric, style = 'short' } = {}) {
const { days, hours, minutes, seconds, milliseconds } = parseDurationToParts(ms, !numeric);
if (numeric) {
const fill = (n) => n.toString().padStart(2, '0');
return `${fill(hours)}:${fill(minutes)}:${fill(seconds)}`;
}
return new Intl.DurationFormat(locale.localeCode, { style }).format({
days,
hours,
minutes,
seconds,
milliseconds,
});
}
//# sourceMappingURL=time.js.map