persidate
Version:
persidate is a lightweight package for converting and managing Shamsi (Jalali) and Gregorian dates in JavaScript/TypeScript.
438 lines (437 loc) • 16.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.jalaliWeekdayNames = exports.jalaliMonthNames = exports.isLeapYearJalali = exports.isBeforeDate = exports.addDaysToDate = exports.getTimeAgo = exports.getJalaliTimeStamp = exports.getDaysFromNow = exports.getTimeFromDate = exports.getToday = exports.formatToLocalizedDate = exports.formatToJalaliDatePadded = exports.formatToGregorianDateTime = exports.formatToGregorianDate = exports.convertToJalaliDate = exports.convertToGregorianDateString = exports.convertToGregorianDate = exports.convertToISODateTime = exports.convertToStandardDateTime = void 0;
const parse_arabic_1 = require("./parse-arabic");
// Extending Date prototype
const DT = Date.prototype;
/**
* Locale setting for Persian (Iran) format.
* @constant {string} locale - Locale code for Persian (Iran).
*/
const locale = "fa-IR";
/**
* Converts date to ISO format in local time.
* @param {Date} date - The input date.
* @returns {string} Local ISO date string.
*/
const convertToStandardDateTime = (date) => {
var _a, _b;
const tzoffset = (date === null || date === void 0 ? void 0 : date.getTimezoneOffset()) * 60000; // Offset in milliseconds
const localISOTime = (_b = (_a = new Date(date.getTime() - tzoffset)) === null || _a === void 0 ? void 0 : _a.toISOString()) === null || _b === void 0 ? void 0 : _b.slice(0, -1);
return localISOTime;
};
exports.convertToStandardDateTime = convertToStandardDateTime;
/**
* Converts date to ISO format with a timezone offset adjustment.
* @param {Date | string | number} date - The input date.
* @returns {string} Adjusted ISO date string.
*/
const convertToISODateTime = (date) => {
const formattedDate = typeof date === "string" || typeof date === "number"
? new Date(date)
: date;
const tzoffset = (formattedDate.getTimezoneOffset() - 60) * 60000;
const localISOTime = new Date(formattedDate.getTime() - tzoffset)
.toISOString()
.slice(0, -1);
return localISOTime;
};
exports.convertToISODateTime = convertToISODateTime;
/**
* Converts Jalali date string to Gregorian Date object.
* @param {string} jalaliDate - Jalali date string in the format YYYY/MM/DD or YYYY-MM-DD.
* @returns {Date} Gregorian Date object.
*/
const convertToGregorianDate = (jalaliDate) => {
const [year, month, day] = jalaliDate.split(/[/-]/).map(Number);
const [gYear, gMonth, gDay] = toGregorian(year, month, day);
return new Date(gYear, gMonth - 1, gDay); // JS months are 0-indexed
};
exports.convertToGregorianDate = convertToGregorianDate;
/**
* Converts Jalali date string to Gregorian date string in the format YYYY/MM/DD.
* @param {string} jalaliDate - Jalali date string in the format YYYY/MM/DD or YYYY-MM-DD.
* @returns {string} Gregorian date string in the format YYYY/MM/DD.
*/
const convertToGregorianDateString = (jalaliDate) => {
const [year, month, day] = jalaliDate.split(/[/-]/).map(Number);
const [gYear, gMonth, gDay] = toGregorian(year, month, day);
return `${gYear}/${gMonth.toString().padStart(2, "0")}/${gDay
.toString()
.padStart(2, "0")}`;
};
exports.convertToGregorianDateString = convertToGregorianDateString;
/**
* Converts a Gregorian date to a Jalali date in a specified format.
*
* @param {Date | string | number} date - The Gregorian date input.
* @param { "day" | "weekday" | "month" | "dayMonth" | "dayMonthYear" | "weekdayDayMonth" | "weekdayDayMonthYear" } [format] -
* Optional format for the output:
* - If omitted, returns "YYYY-MM-DD"
* - "day": Returns day (e.g. "26")
* - "weekday": Returns weekday (e.g. "جمعه")
* - "month": Returns month (e.g. "مهر")
* - "dayMonth": Returns day and month (e.g. "26 مهر")
* - "dayMonthYear": Returns full date (e.g. "26 مهر 1403")
* - "weekdayDayMonth": Returns weekday + day + month (e.g. "جمعه 26 مهر")
* - "weekdayDayMonthYear": Full format (e.g. "جمعه 26 مهر 1403")
*
* @returns {string} Jalali date in desired format or "YYYY-MM-DD" by default.
*
* @example
* convertToJalaliDate("2024-10-18"); // → "1403-07-27"
* convertToJalaliDate("2024-10-18", "dayMonthYear"); // → "27 مهر 1403"
* convertToJalaliDate(new Date(), "weekday"); // → "دوشنبه"
*/
const convertToJalaliDate = (date, format) => {
const parsedDate = typeof date === "number" || typeof date === "string"
? new Date(date)
: date;
const parts = (0, parse_arabic_1.parseArabic)(parsedDate.toLocaleDateString("fa-IR")).split(/[-\/]/);
const [year, month, day] = parts;
const weekday = jalaliWeekdayNamesGregorianFormat[parsedDate.getDay()];
const persianMonth = exports.jalaliMonthNames[month - 1];
// Default fallback: YYYY-MM-DD
if (!format) {
return `${year}-${month}-${day}`;
}
switch (format) {
case "day":
return `${day}`;
case "weekday":
return `${weekday}`;
case "month":
return `${persianMonth}`;
case "year":
return `${year}`;
case "dayMonth":
return `${day} ${persianMonth}`;
case "dayMonthYear":
return `${day} ${persianMonth} ${year}`;
case "weekdayDayMonth":
return `${weekday} ${day} ${persianMonth}`;
case "weekdayDayMonthYear":
return `${weekday} ${day} ${persianMonth} ${year}`;
default:
return `${year}-${month}-${day}`;
}
};
exports.convertToJalaliDate = convertToJalaliDate;
/**
* Converts a given timestamp or Date object to a Gregorian date string in the format YYYY-MM-DD.
* @param {number | Date} date - Unix timestamp or Date object.
* @returns {string} Gregorian date string in the format YYYY-MM-DD.
*/
const formatToGregorianDate = (date) => {
const formattedDate = typeof date === "number" ? new Date(date) : date;
const year = formattedDate.getFullYear();
const month = formattedDate.getMonth() + 1;
const day = formattedDate.getDate();
return `${year}-${month.toString()}-${day.toString()}`;
};
exports.formatToGregorianDate = formatToGregorianDate;
/**
* Converts Jalali date and time to Gregorian date string with time.
* @param {number | Date} date - Unix timestamp or Date object.
* @returns {string} Gregorian date string with time in the format YYYY-MM-DDTHH:mm.
*/
const formatToGregorianDateTime = (date, time) => {
const formattedDate = (0, exports.formatToGregorianDate)(date);
return `${formattedDate}T${time}`;
};
exports.formatToGregorianDateTime = formatToGregorianDateTime;
/**
* Converts a Gregorian date to a formatted Jalali date with padded month and day.
* @param {Date | string | number} date - Gregorian date object.
* @returns {string} Jalali date in the format YYYY-MM-DD with zero-padded month and day.
*/
const formatToJalaliDatePadded = (date) => {
var _a, _b;
const formattedDate = typeof date === "string" || typeof date === "number"
? new Date(date)
: date;
const parts = (0, parse_arabic_1.parseArabic)(formattedDate.toLocaleDateString(locale)).split(/[/-]/);
let month = ((_a = parts[1]) === null || _a === void 0 ? void 0 : _a.length) < 2 ? `0${parts[1]}` : parts[1];
let day = ((_b = parts[2]) === null || _b === void 0 ? void 0 : _b.length) < 2 ? `0${parts[2]}` : parts[2];
return `${parts[0]}-${month}-${day}`;
};
exports.formatToJalaliDatePadded = formatToJalaliDatePadded;
/**
* Formats a date string according to the specified format.
* @param {string | Date} date - The input date string or Date object.
* @param {SupportedDateFormats} format - The desired output format.
* @returns {string | null} Formatted date string or null if invalid.
*/
const formatToLocalizedDate = (date, format) => {
var _a, _b, _c, _d, _e, _f;
if (!date)
return null;
if (date instanceof Date) {
date = date.toISOString();
}
if (format === "jYYYY-jM-jD") {
return (0, exports.convertToJalaliDate)(date);
}
if (format === "jYYYY-jMM-jDD") {
return (0, exports.formatToJalaliDatePadded)(date);
}
if (format === "jMMMM") {
return (0, exports.convertToJalaliDate)(date, "month");
}
if (format === "jD") {
return (0, exports.convertToJalaliDate)(date, "day");
}
if (format === "jDDD") {
return (0, exports.convertToJalaliDate)(date, "dayMonth");
}
if (format === "jDDD-jMM-jYY") {
return (0, exports.convertToJalaliDate)(date, "dayMonthYear");
}
if (typeof date === "string") {
if (date.includes("/"))
date = date.replace(/\//g, "-");
}
const spited = (_a = (0, exports.convertToISODateTime)(date)) === null || _a === void 0 ? void 0 : _a.split("T");
const clockSpited = (_b = spited[1]) === null || _b === void 0 ? void 0 : _b.split(":");
if (format === "YYYY-MM-DD")
return spited[0];
if (format === "YYYY/MM/DD")
return (_c = spited[0]) === null || _c === void 0 ? void 0 : _c.replace(/-/g, "/");
if (format === "YYYY/MM/DD HH:mm")
return (((_d = spited[0]) === null || _d === void 0 ? void 0 : _d.replace(/-/g, "/")) +
" " +
clockSpited[0] +
":" +
clockSpited[1]);
if (format === "HH:mm")
return clockSpited[0] + ":" + clockSpited[1];
if (format === "YYYY/MM/DDTHH:mm:ss")
return (((_e = spited[0]) === null || _e === void 0 ? void 0 : _e.replace(/-/g, "/")) +
"T" +
clockSpited[0] +
":" +
clockSpited[1] +
":" +
((_f = clockSpited[2]) === null || _f === void 0 ? void 0 : _f.split(".")[0]));
return null;
};
exports.formatToLocalizedDate = formatToLocalizedDate;
/**
* Gets today's date in ISO format (YYYY-MM-DD).
* @returns {string} Today's date in "YYYY-MM-DD" format.
*/
const getToday = () => {
const date = new Date().toISOString();
const splitDate = date.split("T")[0];
return splitDate;
};
exports.getToday = getToday;
/**
* Extracts time from a date string or Date object in the format HH:mm.
* @param {string | number | Date} date - Date as string (YYYY-MM-DDTHH:mm:ss) or Date object.
* @returns {string} Time in the format HH:mm.
*/
const getTimeFromDate = (date = new Date()) => {
const dateObj = typeof date === "string" || typeof date === "number"
? new Date(date)
: date;
if (!isNaN(dateObj.getTime())) {
// Check if date is valid
const hours = String(dateObj.getHours()).padStart(2, "0");
const minutes = String(dateObj.getMinutes()).padStart(2, "0");
const seconds = String(dateObj.getSeconds()).padStart(2, "0");
return `${hours}:${minutes}:${seconds}`;
}
return "";
};
exports.getTimeFromDate = getTimeFromDate;
/**
* Calculates the number of days between now and a given date.
* @param {string | Date | number} inputDate - A Date object or a string parsable by `new Date() or timeStamp`.
* @returns {number} Number of days until the date (can be negative if in the past).
*/
const getDaysFromNow = (inputDate) => {
const targetDate = typeof inputDate === "string" || typeof inputDate === "number"
? new Date(inputDate)
: inputDate;
if (isNaN(targetDate.getTime())) {
throw new Error("Invalid date format. Use ISO format or Date object.");
}
const now = new Date();
const msDiff = targetDate.getTime() - now.getTime();
const daysDiff = Math.ceil(msDiff / (1000 * 60 * 60 * 24));
return daysDiff;
};
exports.getDaysFromNow = getDaysFromNow;
/**
* Get Jalali (Persian) date string to a Unix timestamp in milliseconds.
* @param {string} jalaliDate - Jalali date in the format "YYYY-MM-DD"
* @returns {number} Unix timestamp (milliseconds since 1970-01-01)
*/
const getJalaliTimeStamp = (jalaliDate) => {
const gregorianDate = (0, exports.convertToGregorianDate)(jalaliDate); // "2025-03-20"
const timestamp = new Date(gregorianDate).getTime(); //timestamp in milliseconds
return timestamp;
};
exports.getJalaliTimeStamp = getJalaliTimeStamp;
/**
* Returns a string indicating how long ago the given date was,
* in Persian language format.
*
* @param {Date | string | number} date - The input date, either a Date object or a string parsable by `new Date()`.
* @param {string} [suffix='پیش'] - Optional string to append, like 'پیش', 'قبل', or custom. Defaults to 'پیش'.
* @returns {string} A Persian relative time string such as "۵ دقیقه پیش"
*/
const getTimeAgo = (date, suffix = "پیش") => {
const d = typeof date === "string" || typeof date === "number"
? new Date(date)
: date;
const now = new Date();
const diffMs = now.getTime() - d.getTime();
const diffSec = Math.floor(diffMs / 1000);
const diffMin = Math.floor(diffSec / 60);
const diffHr = Math.floor(diffMin / 60);
const diffDay = Math.floor(diffHr / 24);
const diffMonth = Math.floor(diffDay / 30);
const diffYear = Math.floor(diffDay / 365);
if (diffSec < 60)
return `لحظاتی ${suffix}`;
if (diffMin < 60)
return `${diffMin} دقیقه ${suffix}`;
if (diffHr < 24)
return `${diffHr} ساعت ${suffix}`;
if (diffDay < 30)
return `${diffDay} روز ${suffix}`;
if (diffMonth < 12)
return `${diffMonth} ماه ${suffix}`;
return `${diffYear} سال ${suffix}`;
};
exports.getTimeAgo = getTimeAgo;
/**
* Adds days to the Date object.
* @param {number} days - Number of days to add.
* @returns {Date} Updated date with added days.
*/
DT.addDaysToDate = function (days) {
const date = new Date(this.valueOf());
date.setDate(date.getDate() + days);
return date;
};
/**
* Adds a specific number of days to the given date.
* @param {Date} date - Input date.
* @param {number} daysCount - Number of days to add.
* @returns {Date} Updated date.
*/
const addDaysToDate = (date, daysCount) => {
return date.addDaysToDate(daysCount);
};
exports.addDaysToDate = addDaysToDate;
/**
* Compares two date strings and checks if the first date is before the second.
* @param {string} first - First date string.
* @param {string} second - Second date string.
* @returns {boolean} True if first date is before second date.
*/
const isBeforeDate = (first, second) => {
const firstTime = new Date(first).getTime();
const secondTime = new Date(second).getTime();
return firstTime < secondTime;
};
exports.isBeforeDate = isBeforeDate;
/**
* Check if the given Jalali year is a leap year.
* @param {number} jalaliYear - Jalali year.
* @returns {boolean} True if the Jalali year is a leap year.
*/
const isLeapYearJalali = (jalaliYear) => {
const [gy] = toGregorian(jalaliYear, 1, 1);
return (gy % 4 === 0 && gy % 100 !== 0) || gy % 400 === 0;
};
exports.isLeapYearJalali = isLeapYearJalali;
// Define arrays for Jalali months and days
exports.jalaliMonthNames = [
"فروردین",
"اردیبهشت",
"خرداد",
"تیر",
"مرداد",
"شهریور",
"مهر",
"آبان",
"آذر",
"دی",
"بهمن",
"اسفند",
];
exports.jalaliWeekdayNames = [
"شنبه",
"یکشنبه",
"دوشنبه",
"سهشنبه",
"چهارشنبه",
"پنجشنبه",
"جمعه",
];
const jalaliWeekdayNamesGregorianFormat = [
"یکشنبه",
"دوشنبه",
"سهشنبه",
"چهارشنبه",
"پنجشنبه",
"جمعه",
"شنبه",
];
/**
* Converts Jalali date to Gregorian date.
* @param {number} jy - Jalali year.
* @param {number} jm - Jalali month.
* @param {number} jd - Jalali day.
* @returns {[number, number, number]} Gregorian year, month, and day.
*/
const toGregorian = (jy, jm, jd) => {
let gy = jy <= 979 ? 621 : 1600;
jy -= jy <= 979 ? 0 : 979;
let days = 365 * jy +
Math.floor(jy / 33) * 8 +
Math.floor(((jy % 33) + 3) / 4) +
78 +
jd +
(jm < 7 ? (jm - 1) * 31 : (jm - 7) * 30 + 186);
gy += 400 * Math.floor(days / 146097);
days %= 146097;
if (days > 36524) {
gy += 100 * Math.floor(--days / 36524);
days %= 36524;
if (days >= 365)
days++;
}
gy += 4 * Math.floor(days / 1461);
days %= 1461;
gy += Math.floor((days - 1) / 365);
if (days > 365)
days = (days - 1) % 365;
let gd = days + 1;
const sal_a = [
0,
31,
(gy % 4 === 0 && gy % 100 !== 0) || gy % 400 === 0 ? 29 : 28,
31,
30,
31,
30,
31,
31,
30,
31,
30,
31,
];
let gm = 0;
for (gm = 0; gm < 13; gm++) {
if (gd <= sal_a[gm])
break;
gd -= sal_a[gm];
}
return [gy, gm, gd];
};