UNPKG

@ui5/webcomponents-localization

Version:
367 lines (357 loc) 12.6 kB
/*! * OpenUI5 * (c) Copyright 2009-2024 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ // Provides class sap.ui.core.date.Islamic import UniversalDate from "./UniversalDate.js"; import CalendarType from "../CalendarType.js"; import Log from "../../../base/Log.js"; import Formatting from "../../../base/i18n/Formatting.js"; import _Calendars from "./_Calendars.js"; /** * The Islamic date class * * @class * The islamic date does conversion of day, month and year values based on tabular data indicating the start of a month. * In case no tabular data is available for the date, a fallback calculated date will be used. * * @private * @alias sap.ui.core.date.Islamic * @extends sap.ui.core.date.UniversalDate */ var Islamic = UniversalDate.extend("sap.ui.core.date.Islamic", /** @lends sap.ui.core.date.Islamic.prototype */{ constructor: function () { var aArgs = arguments; if (aArgs.length > 1) { aArgs = toGregorianArguments(aArgs); } this.oDate = this.createDate(Date, aArgs); this.sCalendarType = CalendarType.Islamic; } }); Islamic.UTC = function () { var aArgs = toGregorianArguments(arguments); return Date.UTC.apply(Date, aArgs); }; Islamic.now = function () { return Date.now(); }; var BASE_YEAR = 1400, // used for 2 digits "year" related method GREGORIAN_EPOCH_DAYS = 1721425.5, // Julian days since noon on January 1, 4713 BC ISLAMIC_EPOCH_DAYS = 1948439.5, // Julian days since noon on January 1, 4713 BC ISLAMIC_MILLIS = -42521587200000, // 7/16/622 ONE_DAY = 86400000; var oCustomizationMap = null; // Currently those are the two supported Islamic Calendar types in the ABAP var aSupportedIslamicCalendarTypes = ["A", "B"]; /** * Calculate islamic date from gregorian. * * @param {object} oGregorian A JS object containing day, month and year in the gregorian calendar * @returns {object} The islamic date object created * @private */ function toIslamic(oGregorian) { var iGregorianYear = oGregorian.year, iGregorianMonth = oGregorian.month, iGregorianDay = oGregorian.day, iIslamicYear, iIslamicMonth, iIslamicDay, iMonths, iDays, iLeapAdj, iJulianDay; iLeapAdj = 0; if (iGregorianMonth + 1 > 2) { iLeapAdj = isGregorianLeapYear(iGregorianYear) ? -1 : -2; } iJulianDay = GREGORIAN_EPOCH_DAYS - 1 + 365 * (iGregorianYear - 1) + Math.floor((iGregorianYear - 1) / 4) + -Math.floor((iGregorianYear - 1) / 100) + Math.floor((iGregorianYear - 1) / 400) + Math.floor((367 * (iGregorianMonth + 1) - 362) / 12 + iLeapAdj + iGregorianDay); iJulianDay = Math.floor(iJulianDay) + 0.5; iDays = iJulianDay - ISLAMIC_EPOCH_DAYS; iMonths = Math.floor(iDays / 29.530588853); // day/CalendarAstronomer.SYNODIC_MONTH if (iMonths < 0) { //negative means Islamic date before the Islamic's calendar start. So we do not apply customization. iIslamicYear = Math.floor(iMonths / 12) + 1; iIslamicMonth = iMonths % 12; if (iIslamicMonth < 0) { iIslamicMonth += 12; } iIslamicDay = iDays - monthStart(iIslamicYear, iIslamicMonth) + 1; } else { /* Guess the month start. * Always also check the next month, since customization can * differ. It can differ for not more than 3 days. so that * checking the next month is enough. */ iMonths++; /* * Check the true month start for the given month. If it is * later, check the previous month, until a suitable is found. */ while (getCustomMonthStartDays(iMonths) > iDays) { iMonths--; } iIslamicYear = Math.floor(iMonths / 12) + 1; iIslamicMonth = iMonths % 12; iIslamicDay = iDays - getCustomMonthStartDays(12 * (iIslamicYear - 1) + iIslamicMonth) + 1; } return { day: iIslamicDay, month: iIslamicMonth, year: iIslamicYear }; } /** * Calculate gregorian date from islamic. * * @param {object} oIslamic A JS object containing day, month and year in the islamic calendar * @returns {object} The gregorian date object created * @private */ function toGregorian(oIslamic) { var iIslamicYear = oIslamic.year, iIslamicMonth = oIslamic.month, iIslamicDate = oIslamic.day, /* Islamic Calendar starts from 0001/0/1 (19 July 622 AD), so for any date before it customization is not needed */ iMonthStart = iIslamicYear < 1 ? monthStart(iIslamicYear, iIslamicMonth) : getCustomMonthStartDays(12 * (iIslamicYear - 1) + iIslamicMonth), iJulianDay = iIslamicDate + iMonthStart + ISLAMIC_EPOCH_DAYS - 1, iJulianDayNoon = Math.floor(iJulianDay - 0.5) + 0.5, iDaysSinceGregorianEpoch = iJulianDayNoon - GREGORIAN_EPOCH_DAYS, iQuadricent = Math.floor(iDaysSinceGregorianEpoch / 146097), iQuadricentNormalized = mod(iDaysSinceGregorianEpoch, 146097), iCent = Math.floor(iQuadricentNormalized / 36524), iCentNormalized = mod(iQuadricentNormalized, 36524), iQuad = Math.floor(iCentNormalized / 1461), iQuadNormalized = mod(iCentNormalized, 1461), iYearIndex = Math.floor(iQuadNormalized / 365), iYear = iQuadricent * 400 + iCent * 100 + iQuad * 4 + iYearIndex, iMonth, iDay, iGregorianYearStartDays, iDayOfYear, tjd, tjd2, iLeapAdj, iLeapAdj2; if (!(iCent == 4 || iYearIndex == 4)) { iYear++; } iGregorianYearStartDays = GREGORIAN_EPOCH_DAYS + 365 * (iYear - 1) + Math.floor((iYear - 1) / 4) - Math.floor((iYear - 1) / 100) + Math.floor((iYear - 1) / 400); iDayOfYear = iJulianDayNoon - iGregorianYearStartDays; tjd = GREGORIAN_EPOCH_DAYS - 1 + 365 * (iYear - 1) + Math.floor((iYear - 1) / 4) - Math.floor((iYear - 1) / 100) + Math.floor((iYear - 1) / 400) + Math.floor(739 / 12 + (isGregorianLeapYear(iYear) ? -1 : -2) + 1); iLeapAdj = 0; if (iJulianDayNoon < tjd) { iLeapAdj = 0; } else { iLeapAdj = isGregorianLeapYear(iYear) ? 1 : 2; } iMonth = Math.floor(((iDayOfYear + iLeapAdj) * 12 + 373) / 367); tjd2 = GREGORIAN_EPOCH_DAYS - 1 + 365 * (iYear - 1) + Math.floor((iYear - 1) / 4) - Math.floor((iYear - 1) / 100) + Math.floor((iYear - 1) / 400); iLeapAdj2 = 0; if (iMonth > 2) { iLeapAdj2 = isGregorianLeapYear(iYear) ? -1 : -2; } tjd2 += Math.floor((367 * iMonth - 362) / 12 + iLeapAdj2 + 1); iDay = iJulianDayNoon - tjd2 + 1; return { day: iDay, month: iMonth - 1, year: iYear }; } function toGregorianArguments(aArgs) { var aGregorianArgs = Array.prototype.slice.call(aArgs), oIslamic, oGregorian; oIslamic = { year: aArgs[0], month: aArgs[1], day: aArgs[2] !== undefined ? aArgs[2] : 1 }; oGregorian = toGregorian(oIslamic); aGregorianArgs[0] = oGregorian.year; aGregorianArgs[1] = oGregorian.month; aGregorianArgs[2] = oGregorian.day; return aGregorianArgs; } function initCustomizationMap() { var sDateFormat, oCustomizationJSON; oCustomizationMap = {}; sDateFormat = Formatting.getABAPDateFormat(); sDateFormat = _isSupportedIslamicCalendarType(sDateFormat) ? sDateFormat : "A"; // set "A" as a fall-back format always oCustomizationJSON = Formatting.getCustomIslamicCalendarData(); oCustomizationJSON = oCustomizationJSON || []; if (!oCustomizationJSON.length) { Log.warning("No calendar customizations."); return; } oCustomizationJSON.forEach(function (oEntry) { if (oEntry.dateFormat === sDateFormat) { var date = parseDate(oEntry.gregDate); // no need to use UI5Date.getInstance as only the UTC timestamp is used var iGregorianDate = new Date(Date.UTC(date.year, date.month - 1, date.day)); var iMillis = iGregorianDate.getTime(); var iIslamicMonthStartDays = (iMillis - ISLAMIC_MILLIS) / ONE_DAY; date = parseDate(oEntry.islamicMonthStart); var iIslamicMonths = (date.year - 1) * 12 + date.month - 1; oCustomizationMap[iIslamicMonths] = iIslamicMonthStartDays; } }); Log.info("Working with date format: [" + sDateFormat + "] and customization: " + JSON.stringify(oCustomizationJSON)); } function parseDate(sDate) { return { year: parseInt(sDate.substr(0, 4)), month: parseInt(sDate.substr(4, 2)), day: parseInt(sDate.substr(6, 2)) }; } function getCustomMonthStartDays(months) { if (!oCustomizationMap) { initCustomizationMap(); } var iIslamicMonthStartDays = oCustomizationMap[months]; if (!iIslamicMonthStartDays) { var year = Math.floor(months / 12) + 1; var month = months % 12; iIslamicMonthStartDays = monthStart(year, month); } return iIslamicMonthStartDays; } function monthStart(year, month) { return Math.ceil(29.5 * month) + (year - 1) * 354 + Math.floor((3 + 11 * year) / 30.0); } function mod(a, b) { return a - b * Math.floor(a / b); } function isGregorianLeapYear(iYear) { return !(iYear % 400) || !(iYear % 4) && !!(iYear % 100); } function _isSupportedIslamicCalendarType(sCalendarType) { return aSupportedIslamicCalendarTypes.indexOf(sCalendarType) !== -1; } /** * Get the islamic date from the this.oDate. * @returns {object} The islamic date object created */ Islamic.prototype._getIslamic = function () { return toIslamic({ day: this.oDate.getDate(), month: this.oDate.getMonth(), year: this.oDate.getFullYear() }); }; /** * Set the islamic date to the current this.oDate object. * @param {object} oIslamic A JS object containing day, month and year in the islamic calendar * @returns {number} <code>this</code> to allow method chaining */ Islamic.prototype._setIslamic = function (oIslamic) { var oGregorian = toGregorian(oIslamic); return this.oDate.setFullYear(oGregorian.year, oGregorian.month, oGregorian.day); }; /** * Get the islamic date from the this.oDate. * @returns {object} The UTC date object created */ Islamic.prototype._getUTCIslamic = function () { return toIslamic({ day: this.oDate.getUTCDate(), month: this.oDate.getUTCMonth(), year: this.oDate.getUTCFullYear() }); }; /** * Set the islamic date to the current this.oDate object. * @param {object} oIslamic A JS object containing day, month and year in the islamic calendar * @returns {number} <code>this</code> to allow method chaining */ Islamic.prototype._setUTCIslamic = function (oIslamic) { var oGregorian = toGregorian(oIslamic); return this.oDate.setUTCFullYear(oGregorian.year, oGregorian.month, oGregorian.day); }; /* * Override setters and getters specific to the islamic date */ Islamic.prototype.getDate = function (iDate) { return this._getIslamic().day; }; Islamic.prototype.getMonth = function () { return this._getIslamic().month; }; Islamic.prototype.getYear = function () { return this._getIslamic().year - BASE_YEAR; }; Islamic.prototype.getFullYear = function () { return this._getIslamic().year; }; Islamic.prototype.setDate = function (iDate) { var oIslamic = this._getIslamic(); oIslamic.day = iDate; return this._setIslamic(oIslamic); }; Islamic.prototype.setMonth = function (iMonth, iDay) { var oIslamic = this._getIslamic(); oIslamic.month = iMonth; if (iDay !== undefined) { oIslamic.day = iDay; } return this._setIslamic(oIslamic); }; Islamic.prototype.setYear = function (iYear) { var oIslamic = this._getIslamic(); oIslamic.year = iYear + BASE_YEAR; return this._setIslamic(oIslamic); }; Islamic.prototype.setFullYear = function (iYear, iMonth, iDay) { var oIslamic = this._getIslamic(); oIslamic.year = iYear; if (iMonth !== undefined) { oIslamic.month = iMonth; } if (iDay !== undefined) { oIslamic.day = iDay; } return this._setIslamic(oIslamic); }; Islamic.prototype.getUTCDate = function (iDate) { return this._getUTCIslamic().day; }; Islamic.prototype.getUTCMonth = function () { return this._getUTCIslamic().month; }; Islamic.prototype.getUTCFullYear = function () { return this._getUTCIslamic().year; }; Islamic.prototype.setUTCDate = function (iDate) { var oIslamic = this._getUTCIslamic(); oIslamic.day = iDate; return this._setUTCIslamic(oIslamic); }; Islamic.prototype.setUTCMonth = function (iMonth, iDay) { var oIslamic = this._getUTCIslamic(); oIslamic.month = iMonth; if (iDay !== undefined) { oIslamic.day = iDay; } return this._setUTCIslamic(oIslamic); }; Islamic.prototype.setUTCFullYear = function (iYear, iMonth, iDay) { var oIslamic = this._getUTCIslamic(); oIslamic.year = iYear; if (iMonth !== undefined) { oIslamic.month = iMonth; } if (iDay !== undefined) { oIslamic.day = iDay; } return this._setUTCIslamic(oIslamic); }; _Calendars.set(CalendarType.Islamic, Islamic); export default Islamic;