UNPKG

nepalipatrojs

Version:

A JavaScript library for Nepali date conversion and calendar utilities

1,058 lines (1,048 loc) 44 kB
/** * Month names in Nepali language (Devanagari script) */ var NepaliMonth; (function (NepaliMonth) { NepaliMonth[NepaliMonth["Baisakh"] = 1] = "Baisakh"; NepaliMonth[NepaliMonth["Jestha"] = 2] = "Jestha"; NepaliMonth[NepaliMonth["Ashadh"] = 3] = "Ashadh"; NepaliMonth[NepaliMonth["Shrawan"] = 4] = "Shrawan"; NepaliMonth[NepaliMonth["Bhadra"] = 5] = "Bhadra"; NepaliMonth[NepaliMonth["Ashwin"] = 6] = "Ashwin"; NepaliMonth[NepaliMonth["Kartik"] = 7] = "Kartik"; NepaliMonth[NepaliMonth["Mangsir"] = 8] = "Mangsir"; NepaliMonth[NepaliMonth["Poush"] = 9] = "Poush"; NepaliMonth[NepaliMonth["Magh"] = 10] = "Magh"; NepaliMonth[NepaliMonth["Falgun"] = 11] = "Falgun"; NepaliMonth[NepaliMonth["Chaitra"] = 12] = "Chaitra"; })(NepaliMonth || (NepaliMonth = {})); /** * Month names in Nepali language (Devanagari script) */ var NepaliMonthNepali; (function (NepaliMonthNepali) { NepaliMonthNepali[NepaliMonthNepali["\u092C\u0948\u0936\u093E\u0916"] = 1] = "\u092C\u0948\u0936\u093E\u0916"; NepaliMonthNepali[NepaliMonthNepali["\u091C\u0947\u0937\u094D\u0920"] = 2] = "\u091C\u0947\u0937\u094D\u0920"; NepaliMonthNepali[NepaliMonthNepali["\u0906\u0937\u093E\u0922"] = 3] = "\u0906\u0937\u093E\u0922"; NepaliMonthNepali[NepaliMonthNepali["\u0936\u094D\u0930\u093E\u0935\u0923"] = 4] = "\u0936\u094D\u0930\u093E\u0935\u0923"; NepaliMonthNepali[NepaliMonthNepali["\u092D\u093E\u0926\u094D\u0930"] = 5] = "\u092D\u093E\u0926\u094D\u0930"; NepaliMonthNepali[NepaliMonthNepali["\u0906\u0936\u094D\u0935\u093F\u0928"] = 6] = "\u0906\u0936\u094D\u0935\u093F\u0928"; NepaliMonthNepali[NepaliMonthNepali["\u0915\u093E\u0930\u094D\u0924\u093F\u0915"] = 7] = "\u0915\u093E\u0930\u094D\u0924\u093F\u0915"; NepaliMonthNepali[NepaliMonthNepali["\u092E\u0902\u0938\u093F\u0930"] = 8] = "\u092E\u0902\u0938\u093F\u0930"; NepaliMonthNepali[NepaliMonthNepali["\u092A\u094C\u0937"] = 9] = "\u092A\u094C\u0937"; NepaliMonthNepali[NepaliMonthNepali["\u092E\u093E\u0918"] = 10] = "\u092E\u093E\u0918"; NepaliMonthNepali[NepaliMonthNepali["\u092B\u093E\u0932\u094D\u0917\u0941\u0928"] = 11] = "\u092B\u093E\u0932\u094D\u0917\u0941\u0928"; NepaliMonthNepali[NepaliMonthNepali["\u091A\u0948\u0924\u094D\u0930"] = 12] = "\u091A\u0948\u0924\u094D\u0930"; })(NepaliMonthNepali || (NepaliMonthNepali = {})); /** * Days of the week in Nepali language (English transliteration) */ var NepaliWeekDay; (function (NepaliWeekDay) { NepaliWeekDay[NepaliWeekDay["Aitabar"] = 1] = "Aitabar"; NepaliWeekDay[NepaliWeekDay["Sombar"] = 2] = "Sombar"; NepaliWeekDay[NepaliWeekDay["Mangalbar"] = 3] = "Mangalbar"; NepaliWeekDay[NepaliWeekDay["Budhabar"] = 4] = "Budhabar"; NepaliWeekDay[NepaliWeekDay["Bihibar"] = 5] = "Bihibar"; NepaliWeekDay[NepaliWeekDay["Sukrabar"] = 6] = "Sukrabar"; NepaliWeekDay[NepaliWeekDay["Sanibar"] = 7] = "Sanibar"; })(NepaliWeekDay || (NepaliWeekDay = {})); /** * Days of the week in Nepali language (Devanagari script) */ var NepaliWeekDayNepali; (function (NepaliWeekDayNepali) { NepaliWeekDayNepali[NepaliWeekDayNepali["\u0906\u0907\u0924\u092C\u093E\u0930"] = 1] = "\u0906\u0907\u0924\u092C\u093E\u0930"; NepaliWeekDayNepali[NepaliWeekDayNepali["\u0938\u094B\u092E\u092C\u093E\u0930"] = 2] = "\u0938\u094B\u092E\u092C\u093E\u0930"; NepaliWeekDayNepali[NepaliWeekDayNepali["\u092E\u0902\u0917\u0932\u092C\u093E\u0930"] = 3] = "\u092E\u0902\u0917\u0932\u092C\u093E\u0930"; NepaliWeekDayNepali[NepaliWeekDayNepali["\u092C\u0941\u0927\u092C\u093E\u0930"] = 4] = "\u092C\u0941\u0927\u092C\u093E\u0930"; NepaliWeekDayNepali[NepaliWeekDayNepali["\u092C\u093F\u0939\u093F\u092C\u093E\u0930"] = 5] = "\u092C\u093F\u0939\u093F\u092C\u093E\u0930"; NepaliWeekDayNepali[NepaliWeekDayNepali["\u0936\u0941\u0915\u094D\u0930\u092C\u093E\u0930"] = 6] = "\u0936\u0941\u0915\u094D\u0930\u092C\u093E\u0930"; NepaliWeekDayNepali[NepaliWeekDayNepali["\u0936\u0928\u093F\u092C\u093E\u0930"] = 7] = "\u0936\u0928\u093F\u092C\u093E\u0930"; })(NepaliWeekDayNepali || (NepaliWeekDayNepali = {})); const MIN_BS_YEAR = 2000; const MAX_BS_YEAR = 2099; const MIN_AD_YEAR = 1943; /** * Reference dates for calendar conversion * 2000-01-01 BS == 1943-04-14 AD */ const REF_BS = { year: 2000, month: 1, day: 1 }; const REF_AD = new Date(1943, 3, 14); /** * Bikram Sambat calendar data with days in each month for years 2000-2099 * Each year entry contains: * 1. An array with days in each month [Baisakh, Jestha, ..., Chaitra] * 2. Total days in the year */ const BS_CALENDAR_DATA = { 2000: [[30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], 365], 2001: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2002: [[31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], 365], 2003: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2004: [[30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], 365], 2005: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2006: [[31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], 365], 2007: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2008: [[31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 29, 31], 365], 2009: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2010: [[31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], 365], 2011: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2012: [[31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30], 365], 2013: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2014: [[31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], 365], 2015: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2016: [[31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30], 365], 2017: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2018: [[31, 32, 31, 32, 31, 30, 30, 29, 30, 29, 30, 30], 365], 2019: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], 366], 2020: [[31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2021: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2022: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30], 365], 2023: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], 366], 2024: [[31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2025: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2026: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2027: [[30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], 365], 2028: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2029: [[31, 31, 32, 31, 32, 30, 30, 29, 30, 29, 30, 30], 365], 2030: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2031: [[30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], 365], 2032: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2033: [[31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], 365], 2034: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2035: [[30, 32, 31, 32, 31, 31, 29, 30, 30, 29, 29, 31], 365], 2036: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2037: [[31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], 365], 2038: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2039: [[31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30], 365], 2040: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2041: [[31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], 365], 2042: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2043: [[31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30], 365], 2044: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2045: [[31, 32, 31, 32, 31, 30, 30, 29, 30, 29, 30, 30], 365], 2046: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2047: [[31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2048: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2049: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30], 365], 2050: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], 366], 2051: [[31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2052: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2053: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30], 365], 2054: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], 366], 2055: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2056: [[31, 31, 32, 31, 32, 30, 30, 29, 30, 29, 30, 30], 365], 2057: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2058: [[30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], 365], 2059: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2060: [[31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], 365], 2061: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2062: [[30, 32, 31, 32, 31, 31, 29, 30, 29, 30, 29, 31], 365], 2063: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2064: [[31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], 365], 2065: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2066: [[31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 29, 31], 365], 2067: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2068: [[31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], 365], 2069: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2070: [[31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30], 365], 2071: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2072: [[31, 32, 31, 32, 31, 30, 30, 29, 30, 29, 30, 30], 365], 2073: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2074: [[31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2075: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2076: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30], 365], 2077: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], 366], 2078: [[31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2079: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2080: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30], 365], 2081: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], 366], 2082: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2083: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2084: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2085: [[30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], 365], 2086: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2087: [[31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], 365], 2088: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2089: [[30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31], 365], 2090: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2091: [[31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], 365], 2092: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2093: [[31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 29, 31], 365], 2094: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2095: [[31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], 365], 2096: [[31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31], 366], 2097: [[31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30], 365], 2098: [[31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30], 365], 2099: [[31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30], 365], }; const NEPALI_DIGITS = ['०', '१', '२', '३', '४', '५', '६', '७', '८', '९']; /** * Validates a Bikram Sambat (BS) date * @param year The BS year to validate * @param month The month to validate (1-12) * @param day Optional day to validate * @throws Error if the date is invalid or out of supported range */ function validateBsDate(year, month, day) { if (year < MIN_BS_YEAR || year > MAX_BS_YEAR) { throw new Error(`Year out of range. Supported BS years: ${MIN_BS_YEAR} to ${MAX_BS_YEAR}`); } if (month < 1 || month > 12) { throw new Error('Month must be between 1 and 12'); } if (day !== undefined) { if (!BS_CALENDAR_DATA[year]) { throw new Error(`Data not available for year ${year}`); } const daysInMonth = BS_CALENDAR_DATA[year][month - 1]; if (day < 1 || day > daysInMonth) { throw new Error(`Day must be between 1 and ${daysInMonth} for month ${month} in year ${year}`); } } } /** * Validates a Gregorian (AD) date * @param year The AD year to validate * @param month The month to validate (1-12) * @param day Optional day to validate * @throws Error if the date is invalid or out of supported range */ function validateAdDate(year, month, day) { if (year < MIN_AD_YEAR) { throw new Error(`Date out of range. Minimum supported AD year is ${MIN_AD_YEAR}`); } if (month < 1 || month > 12) { throw new Error('Month must be between 1 and 12'); } if (day !== undefined) { const daysInMonth = new Date(year, month, 0).getDate(); if (day < 1 || day > daysInMonth) { throw new Error(`Day must be between 1 and ${daysInMonth} for month ${month} in year ${year}`); } } } /** * Gets the number of days in a Nepali month * @param year The Nepali year in BS calendar * @param month The month (1-12) * @returns Number of days in the specified month * @throws Error if data is not available for the given year */ function getDaysInMonth(year, month) { if (month < 1 || month > 12) { throw new Error('Month must be between 1 and 12'); } if (!BS_CALENDAR_DATA[year]) { throw new Error(`Year out of range. Supported BS years: ${MIN_BS_YEAR} to ${MAX_BS_YEAR}`); } return BS_CALENDAR_DATA[year][0][month - 1]; } /** * Parses different date input formats into a standardized object * @param date Date input, either as a NepaliDate object or [year, month, day] array * @returns Standardized date object with year, month, and day properties * @throws Error if the date format is invalid */ function parseDateInput(date) { if (typeof date === 'string') { throw new Error('Invalid date format. Provide a NepaliDate object or [year, month, day] array.'); } if ('year' in date && typeof date.year === 'number') { return { year: date.year, month: date.month, day: date.day }; } else if (Array.isArray(date) && date.length >= 3) { return { year: date[0], month: date[1], day: date[2] }; } else { throw new Error('Invalid date format. Provide a NepaliDate object or [year, month, day] array.'); } } /** * Localize number to Nepali digits based on language * @param num The number to localize * @param language The language code ('en' or 'np') * @returns Localized string with Nepali digits if language is 'np', otherwise returns the original number as a string */ function localizeDigits(num, language) { switch (language) { case "np": return String(num) .split("") .map((digit) => /\d/.test(digit) ? NEPALI_DIGITS[parseInt(digit, 10)] : digit) .join(""); default: return String(num); } } /** * Localizes the month name based on the language * @param month The month number (1-12) * @param language The language code ('en' or 'np') * @returns Localized month name in either English or Nepali */ function localizeMonth(month, language) { if (language === 'np') { return NepaliMonthNepali[month]; } else { return NepaliMonth[month]; } } /** * Converts Nepali digits to English digits * @param text The text containing Nepali digits to convert * @returns String with English digits */ function nepaliDigitsToEnglish(text) { const nepaliToEnglish = { '०': '0', '१': '1', '२': '2', '३': '3', '४': '4', '५': '5', '६': '6', '७': '7', '८': '8', '९': '9' }; return text.replace(/[०-९]/g, (match) => nepaliToEnglish[match] || match); } /** * Gets common date format patterns for parsing various date formats * @returns Array of date format patterns with regex and index mappings */ function getDateFormatPatterns() { return [ // YYYY-MM-DD, YYYY/MM/DD, YYYY.MM.DD { regex: /(\d{4})[-/.]\s*(\d{1,2})[-/.]\s*(\d{1,2})/, yearIndex: 1, monthIndex: 2, dayIndex: 3, description: 'YYYY-MM-DD format' }, // DD-MM-YYYY, DD/MM/YYYY, DD.MM.YYYY { regex: /(\d{1,2})[-/.]\s*(\d{1,2})[-/.]\s*(\d{4})/, yearIndex: 3, monthIndex: 2, dayIndex: 1, description: 'DD-MM-YYYY format' }, // MM-DD-YYYY, MM/DD/YYYY, MM.DD.YYYY (less common but possible) { regex: /(\d{1,2})[-/.]\s*(\d{1,2})[-/.]\s*(\d{4})/, yearIndex: 3, monthIndex: 1, dayIndex: 2, description: 'MM-DD-YYYY format' }, // YYYYMMDD { regex: /(\d{4})(\d{2})(\d{2})/, yearIndex: 1, monthIndex: 2, dayIndex: 3, description: 'YYYYMMDD format' }, // DDMMYYYY { regex: /(\d{2})(\d{2})(\d{4})/, yearIndex: 3, monthIndex: 2, dayIndex: 1, description: 'DDMMYYYY format' } ]; } /** * Calculates the number of days between two AD dates * @param date1 The first date * @param date2 The second date * @returns Number of days between the two dates */ function daysBetweenAD(date1, date2) { const msPerDay = 24 * 60 * 60 * 1000; return Math.round((date2.getTime() - date1.getTime()) / msPerDay); } /** * Converts a Gregorian (AD) date to Bikram Sambat (BS) date * @param date Date to convert, can be a Date object or [year, month, day] array * @returns NepaliDate object representing the equivalent BS date * @throws Error if date is in invalid format or out of supported range */ function convertADToBS(date) { let adYear; let adMonth; let adDay; if (date instanceof Date) { adYear = date.getFullYear(); adMonth = date.getMonth() + 1; adDay = date.getDate(); } else if (Array.isArray(date) && date.length >= 3) { adYear = date[0]; adMonth = date[1]; adDay = date[2]; } else { throw new Error('Invalid date format. Provide a Date object or [year, month, day] array.'); } validateAdDate(adYear, adMonth, adDay); const inputAD = new Date(adYear, adMonth - 1, adDay); const daysDiff = daysBetweenAD(REF_AD, inputAD); let bsYear = REF_BS.year; let bsMonth = REF_BS.month; let bsDay = REF_BS.day; let daysLeft = daysDiff; while (daysLeft > 0) { const daysInMonth = getDaysInMonth(bsYear, bsMonth); if (bsDay < daysInMonth) { bsDay++; } else { bsDay = 1; if (bsMonth < 12) { bsMonth++; } else { bsMonth = 1; bsYear++; if (!BS_CALENDAR_DATA[bsYear]) { throw new Error(`Year out of range. Supported BS years: ${MIN_BS_YEAR} to ${MAX_BS_YEAR}`); } } } daysLeft--; } while (daysLeft < 0) { if (bsDay > 1) { bsDay--; } else { if (bsMonth > 1) { bsMonth--; } else { bsYear--; if (!BS_CALENDAR_DATA[bsYear]) { throw new Error(`Year out of range. Supported BS years: ${MIN_BS_YEAR} to ${MAX_BS_YEAR}`); } bsMonth = 12; } bsDay = getDaysInMonth(bsYear, bsMonth); } daysLeft++; } return { year: bsYear, month: bsMonth, day: bsDay }; } /** * Gets the current date in Bikram Sambat calendar * @returns Current date as NepaliDate object */ function getCurrentBSDate() { return convertADToBS(new Date()); } function daysBetweenBS(date1, date2) { let days = 0; if (date1.year > date2.year || (date1.year === date2.year && date1.month > date2.month) || (date1.year === date2.year && date1.month === date2.month && date1.day > date2.day)) { return -daysBetweenBS(date2, date1); } if (date1.year === date2.year && date1.month === date2.month && date1.day === date2.day) { return 0; } days += getDaysInMonth(date1.year, date1.month) - date1.day; let currentYear = date1.year; let currentMonth = date1.month + 1; while (currentYear < date2.year || (currentYear === date2.year && currentMonth < date2.month)) { if (currentMonth > 12) { currentYear++; currentMonth = 1; } days += getDaysInMonth(currentYear, currentMonth); currentMonth++; } days += date2.day; return days; } /** * Converts a Bikram Sambat (BS) date to Gregorian (AD) date * @param bsDate BS date to convert, can be a NepaliDate object or [year, month, day] array * @returns JavaScript Date object representing the equivalent AD date * @throws Error if date is invalid or out of supported range */ function convertBSToAD(bsDate) { const { year, month, day } = parseDateInput(bsDate); validateBsDate(year, month, day); const daysDiff = daysBetweenBS(REF_BS, { year, month, day }); const adDate = new Date(REF_AD); adDate.setDate(adDate.getDate() + daysDiff); return adDate; } /** * Converts English digits to Nepali digits * @param num The number to convert * @returns String with Nepali digits */ function toNepaliDigits(num) { return num.toString().replace(/[0-9]/g, (match) => NEPALI_DIGITS[parseInt(match)]); } /** * Formats a Nepali date according to the specified format string * @param date The Nepali date to format * @param format The format string * @param options Formatting options * @returns Formatted date string */ function formatBS(date, format = 'YYYY-MM-DD', options = {}) { const { language = 'en' } = options; const useNepaliDigits = language === 'np'; const adDate = convertBSToAD(date); const weekDay = adDate.getDay() + 1; let result = format; // Year formats result = result.replace(/YYYY/g, useNepaliDigits ? toNepaliDigits(date.year) : date.year.toString()); result = result.replace(/YY/g, useNepaliDigits ? toNepaliDigits((date.year % 100).toString().padStart(2, '0')) : (date.year % 100).toString().padStart(2, '0')); // Month formats // Full month name if (language === 'en') { result = result.replace(/MMMM/g, NepaliMonth[date.month]); result = result.replace(/MMM/g, NepaliMonth[date.month].substring(0, 3)); } else { result = result.replace(/MMMM/g, NepaliMonthNepali[date.month]); result = result.replace(/MMM/g, NepaliMonthNepali[date.month]); } // Two-digit month result = result.replace(/MM/g, useNepaliDigits ? toNepaliDigits(date.month.toString().padStart(2, '0')) : date.month.toString().padStart(2, '0')); // Single-digit month result = result.replace(/M/g, useNepaliDigits ? toNepaliDigits(date.month) : date.month.toString()); // Day formats // Two-digit day result = result.replace(/DD/g, useNepaliDigits ? toNepaliDigits(date.day.toString().padStart(2, '0')) : date.day.toString().padStart(2, '0')); // Single-digit day result = result.replace(/D/g, useNepaliDigits ? toNepaliDigits(date.day) : date.day.toString()); // Weekday formats // Full weekday name if (language === 'en') { result = result.replace(/dddd/g, NepaliWeekDay[weekDay]); result = result.replace(/ddd/g, NepaliWeekDay[weekDay].substring(0, 3)); } else { result = result.replace(/dddd/g, NepaliWeekDayNepali[weekDay]); result = result.replace(/ddd/g, NepaliWeekDayNepali[weekDay]); } // Numeric day of week (1-7) result = result.replace(/d/g, useNepaliDigits ? toNepaliDigits(weekDay) : weekDay.toString()); return result; } class NepaliDatePicker { constructor(inputElement, options = {}) { this.calendarVisible = false; this.currentBSYear = getCurrentBSDate().year; this.currentBSMonth = getCurrentBSDate().month - 1; this.currentBSDay = getCurrentBSDate().day; this.floatingCalendar = null; this.handleClickOutside = (event) => { const target = event.target; if (this.floatingCalendar && target && !this.floatingCalendar.contains(target) && !this.inputElement.contains(target)) { this.closeCalendar(); } }; this.inputElement = inputElement; this.format = options.format || "YYYY-MM-DD"; this.theme = options.theme || "light"; this.language = options.language || "en"; NepaliDatePicker.instances.add(this); this.init(); } init() { this.inputElement.addEventListener("click", (e) => { e.stopPropagation(); this.toggleCalendar(); }); document.addEventListener("click", this.handleClickOutside); } toggleCalendar() { if (this.calendarVisible) { this.closeCalendar(); } else { this.openCalendar(); } } parseAndSetDateFromInput() { const inputValue = this.inputElement.value.trim(); if (!inputValue) { // No value in input, keep current date return; } try { const parsedDate = this.parseDateString(inputValue); if (parsedDate) { this.currentBSYear = parsedDate.year; this.currentBSMonth = parsedDate.month - 1; // Convert to 0-based index this.currentBSDay = parsedDate.day; } } catch (error) { // If parsing fails, keep current date console.warn("Unable to parse date from input:", inputValue, error); } } parseDateString(dateString) { if (!dateString) return null; const normalizedString = nepaliDigitsToEnglish(dateString); const monthNameDate = this.parseWithMonthNames(normalizedString); if (monthNameDate) { return monthNameDate; } const formatPatterns = getDateFormatPatterns(); for (const pattern of formatPatterns) { const match = normalizedString.match(pattern.regex); if (match) { try { const year = parseInt(match[pattern.yearIndex], 10); const month = parseInt(match[pattern.monthIndex], 10); const day = parseInt(match[pattern.dayIndex], 10); // Validate the parsed date if (this.isValidBSDate(year, month, day)) { return { year, month, day }; } } catch (e) { continue; } } } return null; } parseWithMonthNames(dateString) { const englishMonths = []; const nepaliMonths = []; for (let i = 1; i <= 12; i++) { englishMonths.push(localizeMonth(i, "en")); nepaliMonths.push(localizeMonth(i, "np")); } // Try English month names first for (let i = 0; i < englishMonths.length; i++) { const monthName = englishMonths[i]; // Escape special regex characters and create flexible patterns const escapedMonth = monthName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const monthRegex = new RegExp(`${escapedMonth}\\s+(\\d{1,2}),?\\s+(\\d{4})|${escapedMonth}\\s+(\\d{1,2})\\s+(\\d{4})|(\\d{1,2})\\s+${escapedMonth}\\s+(\\d{4})`, 'i'); const match = dateString.match(monthRegex); if (match) { const day = parseInt(match[1] || match[3] || match[5], 10); const year = parseInt(match[2] || match[4] || match[6], 10); const month = i + 1; if (this.isValidBSDate(year, month, day)) { return { year, month, day }; } } } // Try Nepali month names for (let i = 0; i < nepaliMonths.length; i++) { const monthName = nepaliMonths[i]; // Escape special regex characters for Nepali month names const escapedMonth = monthName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const monthRegex = new RegExp(`${escapedMonth}\\s+(\\d{1,2}),?\\s+(\\d{4})|${escapedMonth}\\s+(\\d{1,2})\\s+(\\d{4})|(\\d{1,2})\\s+${escapedMonth}\\s+(\\d{4})`); const match = dateString.match(monthRegex); if (match) { const day = parseInt(match[1] || match[3] || match[5], 10); const year = parseInt(match[2] || match[4] || match[6], 10); const month = i + 1; if (this.isValidBSDate(year, month, day)) { return { year, month, day }; } } } return null; } isValidBSDate(year, month, day) { if (!BS_CALENDAR_DATA[year]) { return false; } if (month < 1 || month > 12) { return false; } // Check if day is valid for the given month and year const monthData = BS_CALENDAR_DATA[year][0][month - 1]; const daysInMonth = Array.isArray(monthData) ? monthData[0] : monthData; return day >= 1 && day <= daysInMonth; } openCalendar() { // Close all other calendars before opening this one this.closeAllOtherCalendars(); // Parse existing value from input field if present this.parseAndSetDateFromInput(); this.calendarVisible = true; this.renderCalendar(this.currentBSYear, this.currentBSMonth); } renderCalendar(bsYear, bsMonth) { if (this.floatingCalendar && this.floatingCalendar.parentNode) { this.floatingCalendar.parentNode.removeChild(this.floatingCalendar); } const calendarDiv = document.createElement("div"); calendarDiv.className = `nepali-datepicker nepali-datepicker-${this.theme}`; calendarDiv.innerHTML = this.generateCalendarHtml(bsYear, bsMonth); this.setupCalendarEvents(calendarDiv); document.body.appendChild(calendarDiv); this.floatingCalendar = calendarDiv; this.positionCalendarPopup(); } generateCalendarHtml(bsYear, bsMonth) { var _a, _b; // Get available years from BS_CALENDAR_DATA const bsYears = Object.keys(BS_CALENDAR_DATA) .map(Number) .sort((a, b) => a - b); let yearOptions = ""; for (const y of bsYears) { yearOptions += `<option value="${y}"${y === bsYear ? " selected" : ""}>${localizeDigits(y, this.language)}</option>`; } let monthOptions = ""; for (let i = 0; i < 12; i++) { monthOptions += `<option value="${i}"${i === bsMonth ? " selected" : ""}>${localizeMonth(i + 1, this.language)}</option>`; } // Get days in this BS month const rawDaysInMonth = (_a = BS_CALENDAR_DATA[bsYear]) === null || _a === void 0 ? void 0 : _a[0][bsMonth]; const daysInMonth = Array.isArray(rawDaysInMonth) ? rawDaysInMonth[0] : typeof rawDaysInMonth === "number" ? rawDaysInMonth : 30; // Calculate the weekday of the 1st day of this BS month const firstDayOfWeek = this.getBSFirstDayOfWeek(bsYear, bsMonth); // Get days in previous month let prevMonth = bsMonth - 1; let prevYear = bsYear; if (prevMonth < 0) { prevMonth = 11; prevYear--; } const rawLastDayOfPrevMonth = (_b = BS_CALENDAR_DATA[prevYear]) === null || _b === void 0 ? void 0 : _b[0][prevMonth]; const lastDayOfPrevMonth = Array.isArray(rawLastDayOfPrevMonth) ? rawLastDayOfPrevMonth[0] : typeof rawLastDayOfPrevMonth === "number" ? rawLastDayOfPrevMonth : 30; const weekdays = this.language === "np" ? ["आ", "सो", "मं", "बु", "बि", "शु", "श"] : ["S", "M", "T", "W", "T", "F", "S"]; return ` <div class="nepali-datepicker-header"> <button type="button" class="nepali-nav-btn nepali-prev-btn">&#9664;</button> <div class="nepali-header-content"> <select class="nepali-month-select">${monthOptions}</select> <select class="nepali-year-select">${yearOptions}</select> </div> <button type="button" class="nepali-nav-btn nepali-next-btn">&#9654;</button> </div> <div class="nepali-weekdays"> ${weekdays.map(day => `<div class="nepali-weekday">${day}</div>`).join('')} </div> <div class="nepali-calendar-body"> ${this.generateBSDaysFullGrid(bsYear, bsMonth, daysInMonth, firstDayOfWeek, lastDayOfPrevMonth)} </div> `; } getBSFirstDayOfWeek(bsYear, bsMonth) { try { const adDate = convertBSToAD({ year: bsYear, month: bsMonth + 1, day: 1, }); if (adDate instanceof Date && !isNaN(adDate.getTime())) { return adDate.getDay(); } } catch (e) { console.log("Error converting BS to AD:", e); } return 0; } // Render the days grid for BS calendar generateBSDaysFullGrid(bsYear, bsMonth, daysInMonth, firstDayOfWeek, lastDayOfPrevMonth) { let daysHTML = ""; const totalCells = 42; // Previous month days for (let i = 0; i < firstDayOfWeek; i++) { const prevDay = lastDayOfPrevMonth - firstDayOfWeek + i + 1; daysHTML += `<div class="nepali-day nepali-other-month" data-day="${prevDay}" data-other="prev">${localizeDigits(prevDay, this.language)}</div>`; } // Current month days for (let day = 1; day <= daysInMonth; day++) { let classes = "nepali-day"; if (this.isBSSelectedDate(bsYear, bsMonth, day)) classes += " nepali-selected"; daysHTML += `<div class="${classes}" data-day="${day}">${localizeDigits(day, this.language)}</div>`; } // Next month days const daysFilled = firstDayOfWeek + daysInMonth; for (let i = 1; i <= totalCells - daysFilled; i++) { daysHTML += `<div class="nepali-day nepali-other-month" data-day="${i}" data-other="next">${localizeDigits(i, this.language)}</div>`; } return daysHTML; } isBSSelectedDate(bsYear, bsMonth, bsDay) { return (bsYear === this.currentBSYear && bsMonth === this.currentBSMonth && bsDay === this.currentBSDay); } setupCalendarEvents(calendarDiv) { const prevButton = calendarDiv.querySelector(".nepali-prev-btn"); const nextButton = calendarDiv.querySelector(".nepali-next-btn"); const monthSelect = calendarDiv.querySelector(".nepali-month-select"); const yearSelect = calendarDiv.querySelector(".nepali-year-select"); prevButton === null || prevButton === void 0 ? void 0 : prevButton.addEventListener("click", (e) => { e.stopPropagation(); this.previousBSMonth(); }); nextButton === null || nextButton === void 0 ? void 0 : nextButton.addEventListener("click", (e) => { e.stopPropagation(); this.nextBSMonth(); }); monthSelect === null || monthSelect === void 0 ? void 0 : monthSelect.addEventListener("change", (e) => { e.stopPropagation(); this.changeBSMonth(e); }); yearSelect === null || yearSelect === void 0 ? void 0 : yearSelect.addEventListener("change", (e) => { e.stopPropagation(); this.changeBSYear(e); }); calendarDiv.addEventListener("click", (e) => this.handleBSDateClick(e)); } handleBSDateClick(event) { const target = event.target; const dayEl = target.closest(".nepali-day"); if (!dayEl) return; event.stopPropagation(); this.selectBSDate(dayEl); } selectBSDate(dayElement) { var _a; let day = parseInt((_a = dayElement.dataset.day) !== null && _a !== void 0 ? _a : "0", 10); let month = this.currentBSMonth; let year = this.currentBSYear; if (this.floatingCalendar) { const monthSelect = this.floatingCalendar.querySelector(".nepali-month-select"); const yearSelect = this.floatingCalendar.querySelector(".nepali-year-select"); if (monthSelect && yearSelect) { month = parseInt(monthSelect.value, 10); year = parseInt(yearSelect.value, 10); } } if (dayElement.dataset.other === "prev") { month -= 1; if (month < 0) { month = 11; year -= 1; } } else if (dayElement.dataset.other === "next") { month += 1; if (month > 11) { month = 0; year += 1; } } this.currentBSYear = year; this.currentBSMonth = month; this.currentBSDay = day; const date = { year: this.currentBSYear, month: this.currentBSMonth + 1, day: this.currentBSDay, }; const formatted = formatBS(date, this.format, { language: this.language, }); this.inputElement.value = formatted; this.inputElement.dispatchEvent(new Event("change", { bubbles: true })); this.closeCalendar(); } previousBSMonth() { let month = this.currentBSMonth - 1; let year = this.currentBSYear; if (month < 0) { month = 11; year--; } this.currentBSMonth = month; this.currentBSYear = year; this.renderCalendar(year, month); } nextBSMonth() { let month = this.currentBSMonth + 1; let year = this.currentBSYear; if (month > 11) { month = 0; year++; } this.currentBSMonth = month; this.currentBSYear = year; this.renderCalendar(year, month); } changeBSMonth(event) { const target = event.target; const newMonth = parseInt(target.value, 10); this.currentBSMonth = newMonth; this.renderCalendar(this.currentBSYear, newMonth); } changeBSYear(event) { const target = event.target; const newYear = parseInt(target.value, 10); this.currentBSYear = newYear; this.renderCalendar(newYear, this.currentBSMonth); } positionCalendarPopup() { if (!this.floatingCalendar) return; this.floatingCalendar.style.position = "absolute"; this.floatingCalendar.style.zIndex = "9999"; const inputRect = this.inputElement.getBoundingClientRect(); const calendarRect = this.floatingCalendar.getBoundingClientRect(); let top = inputRect.bottom + window.scrollY + 4; let left = inputRect.left + window.scrollX; if (window.innerHeight - inputRect.bottom < calendarRect.height && inputRect.top > calendarRect.height) { top = inputRect.top + window.scrollY - calendarRect.height - 4; } if (window.innerWidth - inputRect.left < calendarRect.width) { left = inputRect.right + window.scrollX - calendarRect.width; } this.floatingCalendar.style.top = `${top}px`; this.floatingCalendar.style.left = `${left}px`; } closeCalendar() { this.calendarVisible = false; if (this.floatingCalendar && this.floatingCalendar.parentNode) { this.floatingCalendar.parentNode.removeChild(this.floatingCalendar); this.floatingCalendar = null; } } closeAllOtherCalendars() { NepaliDatePicker.instances.forEach(instance => { if (instance !== this && instance.calendarVisible) { instance.closeCalendar(); } }); } destroy() { this.closeCalendar(); document.removeEventListener("click", this.handleClickOutside); NepaliDatePicker.instances.delete(this); } } NepaliDatePicker.instances = new Set(); class NepaliPatro { /** * Converts a Gregorian (AD) date to Bikram Sambat (BS) * @param date AD date to convert, can be a Date object or [year, month, day] array * @returns NepaliDate object representing the equivalent BS date */ static convertADToBS(date) { return convertADToBS(date); } /** * Converts a Bikram Sambat (BS) date to Gregorian (AD) * @param bsDate BS date to convert, can be a NepaliDate object or [year, month, day] array * @returns JavaScript Date object representing the equivalent AD date */ static convertBSToAD(bsDate) { return convertBSToAD(bsDate); } /** * Gets the current date in Bikram Sambat calendar * @returns Current date as NepaliDate object */ static getCurrentBSDate() { return getCurrentBSDate(); } /** * Formats a Nepali date according to the specified format string * @param date The Nepali date to format * @param format The format string * @param options : {} Formatting options * @returns Formatted date string */ static formatBS(date, format = "YYYY-MM-DD", options = {}) { return formatBS(date, format, options); } /** * Gets the number of days in a Nepali month * @param year The Nepali year in BS calendar * @param month The month (1-12) * @returns Number of days in the specified month */ static getDaysInMonth(year, month) { return getDaysInMonth(year, month); } /** * Converts English digits to Nepali digits * @param num The number to convert * @returns String with Nepali digits */ static toNepaliDigits(num) { return toNepaliDigits(num); } /** * Converts Nepali digits to English digits * @param text The text containing Nepali digits to convert * @returns String with English digits */ static nepaliDigitsToEnglish(text) { return nepaliDigitsToEnglish(text); } /** * Gets common date format patterns for parsing various date formats * @returns Array of date format patterns with regex and index mappings */ static getDateFormatPatterns() { return getDateFormatPatterns(); } /** * Gets the current date by using .now() method * @returns Current date as NepaliDate object */ static now() { return this.getCurrentBSDate(); } } NepaliPatro.MIN_YEAR = MIN_BS_YEAR; NepaliPatro.MAX_YEAR = MAX_BS_YEAR; NepaliPatro.MIN_AD_YEAR = MIN_AD_YEAR; if (typeof window !== "undefined") { window.NepaliDatePicker = NepaliDatePicker; } export { NepaliDatePicker, NepaliMonth, NepaliMonthNepali, NepaliPatro, NepaliWeekDay, NepaliWeekDayNepali, convertADToBS, convertBSToAD, NepaliPatro as default, formatBS as formatBSDate, getCurrentBSDate, getDateFormatPatterns, getDaysInMonth, nepaliDigitsToEnglish, toNepaliDigits }; //# sourceMappingURL=nepali-patro-js.esm.js.map