import momentGuess from "moment-guess";
import { parse } from "datetime/parse";
import dayjs from "dayjs/dayjs.min.js";
import advancedPlugin from "../resources/library/dayjs/plugins/advanced.js";
import timezonePlugin from "../resources/library/dayjs/plugins/timezone.js";
import utcPlugin from "../resources/library/dayjs/plugins/utc.js";
import isoWeekPlugin from "../resources/library/dayjs/plugins/isoweek.js";
import weekOfYearPlugin from "../resources/library/dayjs/plugins/weekofyear.js";
import weekYearPlugin from "../resources/library/dayjs/plugins/weekyear.js";
import { existsSync } from "../deno_ral/fs.ts";
import { toFileUrl } from "../deno_ral/path.ts";
import { resourcePath } from "./resources.ts";
export const kLastModified = "last-modified";
export const kToday = "today";
export const kNow = "now";
export type DateFormat = "full" | "long" | "medium" | "short" | "iso" | string;
export type TimeFormat = "full" | "long" | "medium" | "short";
export function today(): Date {
const today = new Date();
today.setHours(0);
today.setMinutes(0);
today.setSeconds(0);
today.setMilliseconds(0);
return today;
}
export function resolveAndFormatDate(
input: string | string[],
date?: unknown,
format?: string,
) {
const resolveDate = (date?: unknown) => {
if (date) {
if (typeof date === "string") {
return {
value: date,
format: format || "iso",
};
} else if (typeof date === "object") {
const schemaDate = date as { value: string; format?: string };
return {
value: schemaDate.value,
format: schemaDate.format || format || "iso",
};
}
} else {
return undefined;
}
};
const resolvedDate = resolveDate(date);
if (resolvedDate) {
if (isSpecialDate(resolvedDate.value)) {
resolvedDate.value = parseSpecialDate(
input,
resolvedDate.value,
);
}
const parsed = parsePandocDate(resolvedDate.value);
return formatDate(
parsed,
resolvedDate.format,
);
}
}
export function resolveDate(input: string | string[], val: unknown) {
if (isSpecialDate(val)) {
return parseSpecialDate(input, val);
} else {
return val;
}
}
export function isSpecialDate(val?: unknown) {
return val === kLastModified || val === kToday ||
val === kNow;
}
export function parseSpecialDate(
input: string | string[],
val: unknown,
): string {
if (val === kLastModified) {
if (!Array.isArray(input)) {
input = [input];
}
let lastModifiedTs = 0;
for (const inp of input) {
const stat = Deno.statSync(inp);
if (stat.mtime) {
lastModifiedTs = Math.max(lastModifiedTs, stat.mtime.getTime());
}
}
return formatDate(new Date(lastModifiedTs), "YYYY-MM-DDTHH:mm:ssZ");
} else if (val === kToday) {
return formatDate(today(), "YYYY-MM-DDTHH:mm:ssZ");
} else if (val === kNow) {
return formatDate(new Date(), "YYYY-MM-DDTHH:mm:ssZ");
} else {
return val as string;
}
}
export function initDayJsPlugins() {
dayjs.extend(utcPlugin);
dayjs.extend(timezonePlugin);
dayjs.extend(isoWeekPlugin);
dayjs.extend(weekYearPlugin);
dayjs.extend(weekOfYearPlugin);
dayjs.extend(advancedPlugin);
}
export async function setDateLocale(localeStr: string) {
localeStr = localeStr.toLowerCase();
if (localeStr !== dayjs.locale()) {
const findLocale = () => {
const locales = [localeStr];
if (localeStr.includes("-")) {
locales.push(localeStr.split("-")[0]);
}
for (const locale of locales) {
const path = resourcePath(
`library/dayjs/locale/${locale}.js`,
);
if (existsSync(path)) {
return {
locale,
path,
};
}
}
return undefined;
};
const locale = findLocale();
if (locale) {
const localeUrl = toFileUrl(locale.path).href;
const localeModule = await import(localeUrl);
dayjs.locale(localeModule.default, null, true);
dayjs.locale(locale.locale);
}
}
}
export const formatDate = (
date: Date,
dateStyle: DateFormat,
timeStyle?: TimeFormat,
) => {
if (
dateStyle === "full" || dateStyle === "long" || dateStyle === "medium" ||
dateStyle === "short"
) {
const options: Intl.DateTimeFormatOptions = {
dateStyle,
};
if (timeStyle) {
options.timeStyle = timeStyle;
}
return date.toLocaleString(dayjs.locale(), options);
} else {
if (dateStyle === "iso") dateStyle = "YYYY-MM-DD";
return dayjs(date).format(dateStyle);
}
};
export const formattedDate = (
dateStr: string,
dateFormat: string,
) => {
const date = parsePandocDate(dateStr);
if (date) {
const formatted = formatDate(
date,
dateFormat,
);
return formatted;
} else {
return undefined;
}
};
export const parsePandocDate = (dateRaw: string): Date => {
const formats = [
"MM/dd/yyyy",
"MM-dd-yyyy",
"MM/dd/yy",
"MM-dd-yy",
"yyyy-MM-dd",
"dd MM yyyy",
"MM dd, yyyy",
];
const parseFormat = (dateStr: string) => {
for (const format of formats) {
try {
const date = parse(dateStr, format);
return date;
} catch {
}
}
try {
const formats = momentGuess(dateRaw);
if (formats) {
try {
const format = Array.isArray(formats) ? formats[0] : formats;
const date = dayjs(dateStr, format);
return date.toDate();
} catch {
}
}
} catch {
}
try {
return new Date(dateStr);
} catch {
return undefined;
}
};
return parseFormat(dateRaw);
};