fix: fix type error in datetime module
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { lcm, format } from "./utils";
|
||||
import { universalGetDayOfWeek } from "./i18n";
|
||||
|
||||
// YYC MARK:
|
||||
// This file is synchronized with "dt.py".
|
||||
@@ -30,16 +32,366 @@ const PRECOMPILED_LOOP_STOP_RULES = {
|
||||
|
||||
// region: Core Functions
|
||||
|
||||
export function resolveLoopRules4UI(strl: string) {
|
||||
/** Year loop: [type=0, isStrict, yearSpan] */
|
||||
type YearLoopRule = [0, boolean, number];
|
||||
/** Month loop: [type=1, isStrict, mode, monthSpan] */
|
||||
type MonthLoopRule = [1, boolean, 'A' | 'B' | 'C' | 'D', number];
|
||||
/** Week loop: [type=2, 7 weekday booleans, weekSpan] */
|
||||
type WeekLoopRule = [2, boolean, boolean, boolean, boolean, boolean, boolean, boolean, number];
|
||||
/** Day loop: [type=3, daySpan] */
|
||||
type DayLoopRule = [3, number];
|
||||
|
||||
type LoopRule = YearLoopRule | MonthLoopRule | WeekLoopRule | DayLoopRule;
|
||||
|
||||
/** Infinity stop: [type=0] */
|
||||
type InfinityStopRule = [0];
|
||||
/** Datetime stop: [type=1, timestamp] */
|
||||
type DatetimeStopRule = [1, number];
|
||||
/** Times stop: [type=2, times] */
|
||||
type TimesStopRule = [2, number];
|
||||
|
||||
type LoopStopRule = InfinityStopRule | DatetimeStopRule | TimesStopRule;
|
||||
|
||||
/**
|
||||
* Parses a raw loop rule string (e.g. "YS2-F") into structured rule arrays
|
||||
* for UI display purposes.
|
||||
* @param strl - The loop rule string in format "loopRule-stopRule".
|
||||
* @returns A tuple of [LoopRule, LoopStopRule] on success, or undefined
|
||||
* when the string is empty, malformed, or contains no valid rule.
|
||||
*/
|
||||
export function resolveLoopRules4UI(strl: string): [LoopRule, LoopStopRule] | undefined {
|
||||
if (strl == '') return undefined;
|
||||
|
||||
let sp = strl.split('-');
|
||||
if (sp.length != 2) return undefined;
|
||||
let loopRules: LoopRule | undefined = undefined;
|
||||
let loopStopRules: LoopStopRule | undefined = undefined;
|
||||
|
||||
let match: RegExpExecArray | null;
|
||||
if ((match = PRECOMPILED_LOOP_RULES.year.exec(sp[0]!)) !== null) {
|
||||
loopRules = [0, match[1]! == 'S', parseInt(match[2]!)];
|
||||
} else if ((match = PRECOMPILED_LOOP_RULES.month.exec(sp[0]!)) !== null) {
|
||||
loopRules = [1, match[1]! == 'S', match[2]! as 'A' | 'B' | 'C' | 'D', parseInt(match[3]!)];
|
||||
} else if ((match = PRECOMPILED_LOOP_RULES.week.exec(sp[0]!)) !== null) {
|
||||
const w = match[1]!;
|
||||
loopRules = [2, w[0] == 'T', w[1] == 'T', w[2] == 'T', w[3] == 'T', w[4] == 'T', w[5] == 'T', w[6] == 'T', parseInt(match[2]!)] as WeekLoopRule;
|
||||
} else if ((match = PRECOMPILED_LOOP_RULES.day.exec(sp[0]!)) !== null) {
|
||||
loopRules = [3, parseInt(match[1]!)];
|
||||
} else return undefined;
|
||||
|
||||
if ((match = PRECOMPILED_LOOP_STOP_RULES.infinity.exec(sp[1]!)) !== null) {
|
||||
loopStopRules = [0];
|
||||
} else if ((match = PRECOMPILED_LOOP_STOP_RULES.datetime.exec(sp[1]!)) !== null) {
|
||||
loopStopRules = [1, parseInt(match[1]!)];
|
||||
} else if ((match = PRECOMPILED_LOOP_STOP_RULES.times.exec(sp[1]!)) !== null) {
|
||||
loopStopRules = [2, parseInt(match[1]!)];
|
||||
} else return undefined;
|
||||
|
||||
return [loopRules, loopStopRules];
|
||||
}
|
||||
|
||||
export function resolveLoopRules2Event(loopRules: string, loopDateTimeStart: number, loopDateTimeEnd: number, eventDateTimeStart: number, eventDateTimeEnd: number, timezoneOffset: number, clampStartDateTime: number) {
|
||||
/** A pair of [start, end] timestamps (in minutes) representing a single event occurrence. */
|
||||
type TimeRange = [number, number];
|
||||
|
||||
/**
|
||||
* Resolves a loop rule string into concrete event date ranges by expanding
|
||||
* the loop pattern across the detection window.
|
||||
* @remark loopDateTimeStart's value does not correspond with the database;
|
||||
* it is calculated by the program and should point to the closet
|
||||
* potential event start datetime.
|
||||
* @remark loopDateTimeEnd is performed like loopDateTimeStart and
|
||||
* it indicates the possible time point of the tail of legal event.
|
||||
* @remark clampStartDateTime is the real clamp datetime of event start
|
||||
* datetime.
|
||||
* @remark loopDateTimeStart is the start datetime for detection.
|
||||
* @remark In this section, all times should be analysed with
|
||||
* `Date((time + timezoneOffset) * 60000)` and use `.getUTC...()`
|
||||
* functions.
|
||||
* @param loopRules - The loop rule string (e.g. "YS2-F").
|
||||
* @param loopDateTimeStart - Start of the detection window (minutes).
|
||||
* @param loopDateTimeEnd - End of the detection window (minutes).
|
||||
* @param eventDateTimeStart - Original event start datetime (minutes).
|
||||
* @param eventDateTimeEnd - Original event end datetime (minutes).
|
||||
* @param timezoneOffset - Timezone offset in minutes.
|
||||
* @param clampStartDateTime - Earliest allowed start datetime (minutes).
|
||||
* @returns An array of [start, end] timestamp pairs representing every
|
||||
* occurrence within the window, or undefined if the rule is invalid.
|
||||
*/
|
||||
export function resolveLoopRules2Event(
|
||||
fullLoopRules: string,
|
||||
loopDateTimeStart: number,
|
||||
loopDateTimeEnd: number,
|
||||
eventDateTimeStart: number,
|
||||
eventDateTimeEnd: number,
|
||||
timezoneOffset: number,
|
||||
clampStartDateTime: number): TimeRange[] | undefined {
|
||||
if (fullLoopRules == '') return [
|
||||
[Math.max(eventDateTimeStart, clampStartDateTime),
|
||||
Math.max(loopDateTimeEnd, eventDateTimeEnd)]
|
||||
];
|
||||
|
||||
let sp = fullLoopRules.split('-');
|
||||
if (sp.length != 2) return undefined;
|
||||
let loopRules = sp[0]!; // we don't need consider stop flag
|
||||
let result: TimeRange[] = new Array();
|
||||
|
||||
// compute offset and duration
|
||||
let eventDateTime = new Date((eventDateTimeStart + timezoneOffset) * 60000);
|
||||
eventDateTime.setUTCHours(0, 0, 0, 0);
|
||||
let eventOffset = eventDateTimeStart - (Math.floor(eventDateTime.getTime() / 60000) - timezoneOffset);
|
||||
let eventDuration = eventDateTimeEnd - eventDateTimeStart;
|
||||
|
||||
let detectDateTime = new Date(loopDateTimeStart * 60000);
|
||||
detectDateTime.setUTCHours(0, 0, 0, 0);
|
||||
let originalYear = eventDateTime.getUTCFullYear();
|
||||
let originalMonth = eventDateTime.getUTCMonth() + 1;
|
||||
let originalDay = eventDateTime.getUTCDate();
|
||||
|
||||
// compute event
|
||||
let match: RegExpExecArray | null;
|
||||
if ((match = PRECOMPILED_LOOP_RULES.year.exec(loopRules)) !== null) {
|
||||
let isStrict = match[1]! == 'S';
|
||||
let loopSpan = parseInt(match[2]!);
|
||||
|
||||
let yearCount = detectDateTime.getFullYear() - originalYear;
|
||||
let isSpecial = (originalMonth == 2 && originalDay == 29);
|
||||
let realLoopSpan = (isSpecial && isStrict) ? lcm(4, loopSpan) : loopSpan;
|
||||
|
||||
//let fullSpanCount = Math.floor(yearCount / realLoopSpan);
|
||||
let remainYear = yearCount % realLoopSpan;
|
||||
//detectDateTime.setUTCFullYear(fullSpanCount + detectDateTime.getUTCFullYear(), 1, 1);
|
||||
if (remainYear != 0)
|
||||
detectDateTime.setUTCFullYear(realLoopSpan - remainYear + detectDateTime.getUTCFullYear(), 1 - 1, 1);
|
||||
|
||||
let skipFlag = false;
|
||||
while (Math.floor(detectDateTime.getTime() / 60000) + eventOffset - timezoneOffset <= loopDateTimeEnd) {
|
||||
skipFlag = false;
|
||||
if (isSpecial) {
|
||||
// is special day, 29 Feb
|
||||
// try set it in 29 Feb
|
||||
if (isStrict) {
|
||||
if (isLeapYear(detectDateTime.getUTCFullYear())) detectDateTime.setUTCMonth(2 - 1, 29);
|
||||
else skipFlag = true; // order skip
|
||||
} else {
|
||||
if (isLeapYear(detectDateTime.getUTCFullYear())) detectDateTime.setUTCMonth(2 - 1, 29);
|
||||
else detectDateTime.setUTCMonth(2 - 1, 28);
|
||||
}
|
||||
} else detectDateTime.setUTCMonth(originalMonth - 1, originalDay);
|
||||
|
||||
if (!skipFlag) {
|
||||
result.push(
|
||||
[Math.floor(detectDateTime.getTime() / 60000) + eventOffset - timezoneOffset,
|
||||
Math.floor(detectDateTime.getTime() / 60000) + eventOffset + eventDuration - timezoneOffset]
|
||||
);
|
||||
}
|
||||
|
||||
detectDateTime.setUTCFullYear(realLoopSpan + detectDateTime.getUTCFullYear());
|
||||
}
|
||||
|
||||
} else if ((match = PRECOMPILED_LOOP_RULES.month.exec(loopRules)) !== null) {
|
||||
let isStrict = match[1]! == 'S';
|
||||
let loopMethod = match[2]!;
|
||||
let loopSpan = parseInt(match[3]!);
|
||||
|
||||
let monthsCountValue = monthsCount(detectDateTime.getUTCFullYear(), detectDateTime.getUTCMonth() + 1) -
|
||||
monthsCount(originalYear, originalMonth);
|
||||
|
||||
//let fullSpanCount = Math.floor(monthsCountValue / loopSpan);
|
||||
let remainMonth = monthsCountValue % loopSpan;
|
||||
//detectDateTime.setUTCMonth(fullSpanCount * loopSpan + detectDateTime.getUTCMonth(), 1);
|
||||
detectDateTime.setUTCDate(1);
|
||||
if (remainMonth != 0)
|
||||
detectDateTime.setUTCMonth(loopSpan - remainMonth + detectDateTime.getUTCMonth(), 1);
|
||||
|
||||
while (Math.floor(detectDateTime.getTime() / 60000) + eventOffset - timezoneOffset <= loopDateTimeEnd) {
|
||||
let data = getRemanagedDayInMonth(originalYear, originalMonth, originalDay, detectDateTime.getUTCFullYear(), detectDateTime.getUTCMonth() + 1, isStrict);
|
||||
let predictedDay: number | undefined = undefined;
|
||||
switch (loopMethod) {
|
||||
case 'A':
|
||||
if (typeof (data[0]) !== 'undefined') predictedDay = data[0];
|
||||
break;
|
||||
case 'B':
|
||||
if (typeof (data[1]) !== 'undefined') predictedDay = data[1];
|
||||
break;
|
||||
case 'C':
|
||||
if (typeof (data[2]) !== 'undefined') predictedDay = data[2];
|
||||
break;
|
||||
case 'D':
|
||||
if (typeof (data[3]) !== 'undefined') predictedDay = data[3];
|
||||
break;
|
||||
}
|
||||
if (typeof (predictedDay) !== 'undefined') {
|
||||
detectDateTime.setUTCDate(predictedDay);
|
||||
result.push(
|
||||
[Math.floor(detectDateTime.getTime() / 60000) + eventOffset - timezoneOffset,
|
||||
Math.floor(detectDateTime.getTime() / 60000) + eventOffset + eventDuration - timezoneOffset]
|
||||
);
|
||||
}
|
||||
|
||||
detectDateTime.setUTCMonth(loopSpan + detectDateTime.getUTCMonth(), 1);
|
||||
}
|
||||
|
||||
|
||||
} else if ((match = PRECOMPILED_LOOP_RULES.week.exec(loopRules)) !== null) {
|
||||
let loopSpan = parseInt(match[2]!);
|
||||
let weekOption: boolean[] = [];
|
||||
let weekEventCount = 0
|
||||
for (let i = 0; i < 7; i++) {
|
||||
weekOption.push(match[1]![i] == 'T');
|
||||
if (match[1]![i] == 'T') weekEventCount++;
|
||||
}
|
||||
|
||||
let originalWeek = dayOfWeek(originalYear, originalMonth, originalDay);
|
||||
|
||||
// try insert original event
|
||||
if (!weekOption[originalWeek]) {
|
||||
result.push(
|
||||
[eventDateTimeStart, eventDateTimeEnd]
|
||||
);
|
||||
}
|
||||
|
||||
let daysCountValue = daysCount(detectDateTime.getUTCFullYear(), detectDateTime.getUTCMonth() + 1, detectDateTime.getDate()) -
|
||||
daysCount(originalYear, originalMonth, originalDay);
|
||||
//let fullSpanCount = Math.floor(daysCountValue / (7 * loopSpan));
|
||||
let remainFullSpanCount = Math.floor((daysCountValue % (7 * loopSpan)) / 7);
|
||||
let remainDays = (daysCountValue % (7 * loopSpan)) % 7;
|
||||
|
||||
//detectDateTime.setUTCDate((7 * loopSpan * fullSpanCount) + detectDateTime.getUTCDate());
|
||||
if (remainFullSpanCount != 0) {
|
||||
detectDateTime.setUTCDate((loopSpan - remainFullSpanCount) * 7 + detectDateTime.getUTCDate());
|
||||
}
|
||||
let weekCounter = remainDays;
|
||||
|
||||
while (Math.floor(detectDateTime.getTime() / 60000) + eventOffset - timezoneOffset <= loopDateTimeEnd) {
|
||||
if (weekOption[(weekCounter + originalWeek) % 7])
|
||||
result.push(
|
||||
[Math.floor(detectDateTime.getTime() / 60000) + eventOffset - timezoneOffset,
|
||||
Math.floor(detectDateTime.getTime() / 60000) + eventOffset + eventDuration - timezoneOffset]
|
||||
);
|
||||
|
||||
weekCounter = (weekCounter + 1) % 7;
|
||||
detectDateTime.setUTCDate(detectDateTime.getUTCDate() + 1);
|
||||
if (weekCounter == 0)
|
||||
detectDateTime.setUTCDate(detectDateTime.getUTCDate() + (loopSpan - 1) * 7);
|
||||
}
|
||||
|
||||
} else if ((match = PRECOMPILED_LOOP_RULES.day.exec(loopRules)) !== null) {
|
||||
let loopSpan = parseInt(match[1]!);
|
||||
|
||||
let daysCountValue = daysCount(detectDateTime.getUTCFullYear(), detectDateTime.getUTCMonth() + 1, detectDateTime.getUTCDate()) -
|
||||
daysCount(originalYear, originalMonth, originalDay);
|
||||
//let fullSpanCount = Math.floor(daysCountValue / loopSpan);
|
||||
let remainDays = daysCountValue % loopSpan;
|
||||
//detectDateTime.setUTCDate(fullSpanCount * loopSpan + detectDateTime.getUTCDate());
|
||||
if (remainDays != 0)
|
||||
detectDateTime.setUTCDate(loopSpan - remainDays + detectDateTime.getUTCDate());
|
||||
|
||||
while (Math.floor(detectDateTime.getTime() / 60000) + eventOffset - timezoneOffset <= loopDateTimeEnd) {
|
||||
result.push(
|
||||
[Math.floor(detectDateTime.getTime() / 60000) + eventOffset - timezoneOffset,
|
||||
Math.floor(detectDateTime.getTime() / 60000) + eventOffset + eventDuration - timezoneOffset]
|
||||
);
|
||||
detectDateTime.setUTCDate(detectDateTime.getUTCDate() + loopSpan);
|
||||
}
|
||||
} else return undefined;
|
||||
|
||||
// clamp item
|
||||
let realResult: TimeRange[] = new Array();
|
||||
for (let i of result) {
|
||||
let start = i[0];
|
||||
let end = i[1];
|
||||
if (end > clampStartDateTime && start <= loopDateTimeEnd)
|
||||
realResult.push([Math.max(start, clampStartDateTime), Math.min(end, loopDateTimeEnd)]);
|
||||
}
|
||||
return realResult;
|
||||
}
|
||||
|
||||
export function resolveLoopRules4Text(strl: string, startDateTime: number, timezoneOffset: number) {
|
||||
/**
|
||||
* Resolves a loop rule string into a human-readable i18n-localised text
|
||||
* description suitable for display in the UI.
|
||||
* @param strl - The loop rule string in format "loopRule-stopRule".
|
||||
* @param startDateTime - Reference start datetime (minutes) for computing
|
||||
* contextual fields (e.g. day-in-month info).
|
||||
* @param timezoneOffset - Timezone offset in minutes.
|
||||
* @returns A concatenated human-readable string, or an empty string when
|
||||
* the input is empty or cannot be parsed.
|
||||
*/
|
||||
export function resolveLoopRules4Text(strl: string, startDateTime: number, timezoneOffset: number): string {
|
||||
if (strl == '') return "";
|
||||
|
||||
let sp = strl.split('-');
|
||||
if (sp.length != 2) return "";
|
||||
let loopRules: string;
|
||||
let loopStopRules: string;
|
||||
let datetimeInstance = new Date((startDateTime + timezoneOffset) * 60000)
|
||||
|
||||
let match: RegExpExecArray | null;
|
||||
if ((match = PRECOMPILED_LOOP_RULES.year.exec(sp[0]!)) !== null) {
|
||||
if (match[1]! == 'S')
|
||||
loopRules = "严格模式。";
|
||||
else
|
||||
loopRules = "宽松模式。";
|
||||
loopRules += format("每{0}年于{1}循环一次。",
|
||||
parseInt(match[2]!), datetimeInstance.toLocaleDateString(undefined, { timeZone: "UTC" })
|
||||
);
|
||||
} else if ((match = PRECOMPILED_LOOP_RULES.month.exec(sp[0]!)) !== null) {
|
||||
if (match[1]! == 'S')
|
||||
loopRules = "严格模式。";
|
||||
else
|
||||
loopRules = "宽松模式。";
|
||||
|
||||
let dayInMonth = getDayInMonth(
|
||||
datetimeInstance.getUTCFullYear(),
|
||||
datetimeInstance.getUTCMonth() + 1,
|
||||
datetimeInstance.getUTCDate());
|
||||
switch (match[2]!) {
|
||||
case 'A':
|
||||
loopRules = format("每{0}月的第{1}日循环一次。",
|
||||
parseInt(match[3]!), dayInMonth[0]
|
||||
);
|
||||
break;
|
||||
case 'B':
|
||||
loopRules = format("每{0}月的倒数第{1}日循环一次。",
|
||||
parseInt(match[3]!), dayInMonth[1]
|
||||
);
|
||||
break;
|
||||
case 'C':
|
||||
loopRules = format("每{0}月的第{1}个星期{2}循环一次。",
|
||||
parseInt(match[3]!), dayInMonth[2], dayInMonth[3]
|
||||
);
|
||||
break;
|
||||
case 'D':
|
||||
loopRules = format("每{0}月的倒数第{1}个星期{2}循环一次。",
|
||||
parseInt(match[3]!), dayInMonth[4], dayInMonth[5]
|
||||
);
|
||||
break;
|
||||
}
|
||||
} else if ((match = PRECOMPILED_LOOP_RULES.week.exec(sp[0]!)) !== null) {
|
||||
let weekOfDayCache = [];
|
||||
for (let i = 0; i < 7; i++) {
|
||||
if (match[1]![i] == 'T')
|
||||
weekOfDayCache.push(universalGetDayOfWeek(i));
|
||||
}
|
||||
|
||||
loopRules = format("每{0}周的{1}循环一次。",
|
||||
parseInt(match[2]!), weekOfDayCache.join(', ')
|
||||
);
|
||||
} else if ((match = PRECOMPILED_LOOP_RULES.day.exec(sp[0]!)) !== null) {
|
||||
loopRules = format("每{0}天循环一次。", parseInt(match[1]!));
|
||||
} else return "";
|
||||
|
||||
|
||||
if ((match = PRECOMPILED_LOOP_STOP_RULES.infinity.exec(sp[1]!)) !== null) {
|
||||
loopStopRules = "永远循环。";
|
||||
} else if ((match = PRECOMPILED_LOOP_STOP_RULES.datetime.exec(sp[1]!)) !== null) {
|
||||
loopStopRules = format("到{0}停止循环。",
|
||||
new Date(parseInt(match[1]!)).toLocaleDateString()
|
||||
);
|
||||
} else if ((match = PRECOMPILED_LOOP_STOP_RULES.times.exec(sp[1]!)) !== null) {
|
||||
loopStopRules = format("循环{0}次。", parseInt(match[1]!));
|
||||
} else return "";
|
||||
|
||||
return (loopRules + loopStopRules);
|
||||
}
|
||||
|
||||
// endregion
|
||||
@@ -119,13 +471,13 @@ function getDayInMonth(year: number, month: number, day: number): DayInMonthInfo
|
||||
|
||||
type RemanagedDayInMonth = [
|
||||
/** Count forward by days (Day N) */
|
||||
forwardByDayCount: number,
|
||||
forwardByDayCount: number | undefined,
|
||||
/** Count backward by days (Nth day from the end) */
|
||||
backwardByDayCount: number,
|
||||
backwardByDayCount: number | undefined,
|
||||
/** Count forward by weeks (Week N, Youbi X) */
|
||||
forwardByWeekCount: number,
|
||||
forwardByWeekCount: number | undefined,
|
||||
/** Count backward by weeks (Nth week from the end, Youbi X) */
|
||||
backwardByWeekCount: number,
|
||||
backwardByWeekCount: number | undefined,
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -140,7 +492,7 @@ type RemanagedDayInMonth = [
|
||||
* @param isStrict
|
||||
* @returns
|
||||
*/
|
||||
function getRemanagedDayInMonth(oldYear: number, oldMonth: number, oldDay: number, newYear: number, newMonth: number, isStrict: boolean) {
|
||||
function getRemanagedDayInMonth(oldYear: number, oldMonth: number, oldDay: number, newYear: number, newMonth: number, isStrict: boolean): RemanagedDayInMonth {
|
||||
let ddata = getDayInMonth(oldYear, oldMonth, oldDay);
|
||||
let mdata = getMonthWeekStatistics(newYear, newMonth);
|
||||
let days = MONTH_DAY_COUNT[newMonth - 1]! + ((newMonth == 2 && isLeapYear(newYear)) ? 1 : 0);
|
||||
|
||||
@@ -2,3 +2,65 @@ export enum Language {
|
||||
English,
|
||||
SimplifiedChinese,
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param month Zero-based month.
|
||||
* @returns
|
||||
*/
|
||||
export function universalGetMonth(month: number): string {
|
||||
switch (month) {
|
||||
case 0:
|
||||
return "January";
|
||||
case 1:
|
||||
return "February";
|
||||
case 2:
|
||||
return "March";
|
||||
case 3:
|
||||
return "April";
|
||||
case 4:
|
||||
return "May";
|
||||
case 5:
|
||||
return "June";
|
||||
case 6:
|
||||
return "July";
|
||||
case 7:
|
||||
return "August";
|
||||
case 8:
|
||||
return "September";
|
||||
case 9:
|
||||
return "October";
|
||||
case 10:
|
||||
return "November";
|
||||
case 11:
|
||||
return "December";
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dayOfWeek Zero-based day of week
|
||||
* @returns
|
||||
*/
|
||||
export function universalGetDayOfWeek(dayOfWeek: number): string {
|
||||
switch (dayOfWeek) {
|
||||
case 0:
|
||||
return "Monday";
|
||||
case 1:
|
||||
return "Tuesday";
|
||||
case 2:
|
||||
return "Wednesday";
|
||||
case 3:
|
||||
return "Thursday";
|
||||
case 4:
|
||||
return "Friday";
|
||||
case 5:
|
||||
return "Saturday";
|
||||
case 6:
|
||||
return "Sunday";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,24 @@ export function lcm(a: number, b: number): number {
|
||||
return a / gcd(a, b) * b;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串中的 `{n}` 占位符替换为对应的参数值
|
||||
* @param str 目标字符串,若为 `null`/`undefined`/空字符串则返回原值或空串(根据需求)
|
||||
* @param args 用于替换的值列表
|
||||
* @returns 格式化后的字符串
|
||||
*/
|
||||
export function format(str: string, ...args: any[]): string {
|
||||
// 若 str 为假值(null/undefined/''),直接返回空字符串
|
||||
if (!str) return '';
|
||||
|
||||
return str.replace(/\{(\d+)\}/g, (match, index) => {
|
||||
const idx = parseInt(index, 10);
|
||||
const value = args[idx];
|
||||
// 若参数存在且不为 null/undefined,则转为字符串,否则保留占位符
|
||||
return value != null ? String(value) : match;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Monday-first the day of week from given date instead of Sunday-first.
|
||||
* @param date The date for getting weekday.
|
||||
|
||||
Reference in New Issue
Block a user