如何在没有外部库或复杂天文公式的情况下将5个伊斯兰日历日期中的任何一个转换为18个世界日历中的任何一个 [英] How to Convert any of the 5 Islamic (Hijri) Calendars' Dates to any of 18 World Calendars without External Libraries or Complex Astronomical Formulas

查看:26
本文介绍了如何在没有外部库或复杂天文公式的情况下将5个伊斯兰日历日期中的任何一个转换为18个世界日历中的任何一个的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在StackOverflow和CodeReview站点上搜索,尚未尝试本问题/帖子中描述的方法;即在不使用外部日期库或使用复杂的数学/天文公式的情况下,将5个伊斯兰日历的日期转换为Java脚本中18个可用日历中的任何一个。

希望此帖子遵守StackOverflowCan I answer my own question?建议的与社区共享新概念代码的策略指南。

Java脚本Intl.DateTimeFormat()

我们都知道,Java脚本有一个内置的方法(Intl.DateTimeFormat()),可以将公历日期转换为各种日历的日期(18个世界日历),包括输出字符串的格式。

但是,Java脚本不提供用于反向操作的内置方法,即将18个世界日历日期转换回公历日期(包括伊斯兰日历日期)或其他日历。为此,您将需要使用外部日期库来执行转换,如‘Moment.js’和许多其他文件。

此处使用的方法/代码不使用外部库,而是使用按目标近似方法进行的Java日历转换。下面的流程图总结了可用于将其他日历转换为剩余日历的方法的概念。

下面的短Java脚本函数提供了将五(5)个伊斯兰(Hijri)日历的任何日期(从伊斯兰年份280,803 AH到+281,510 AH)转换为以下18个Java日历中的任何一个的功能,并提供了用于格式化结果输出的选项:

"buddhist", "chinese", "coptic", "dangi", "ethioaa", "ethiopic", "gregory", "hebrew", "indian", "islamic", "islamic-umalqura", "islamic-tbla", "islamic-civil", "islamic-rgsa", "iso8601", "japanese", "persian", "roc", "islamicc".

该方法不使用外部库,也不使用复杂的数学或天文公式,并且仅依赖于基于ICU代码[https://icu.unicode.org/].

]的Java脚本内置日历转换算法

此方法可确保输出始终准确,并与Java引擎输出完全兼容。

虽然一些外部库非常好,但随着时间的推移,它们往往会失去更新和支持;'moment.js' library的最近案例就是一个例子。

语法

函数语法为:

hijriToCalendars(year, month, day, [options])

在其最简单的形式中,该函数默认为转换'islamic-umalqura'日历,该日历是最常用和最近使用的伊斯兰日历。

它还将默认使用ISO日期格式转换为Gregorian日历。

示例:将伊斯兰日期21 Rajab 1443(即21/07/1443)转换为公历。

hijriToCalendars(1443,7,21);

output: 2022-02-22T00:00:00.000Z    // default output Gregorian ISO format

TO将伊斯兰日期转换为另一个日历(如‘波斯历’):

 hijriToCalendars(1443,7,21, { toCal: "persian" });

 output: 12/3/1400 AP

若要向输出添加格式,请使用'dateStyle'选项,就像在Java脚本Intl.DateTimeFormat()方法中一样。

示例:使用完整日期样式将伊斯兰日期转换为波斯日期

 hijriToCalendars(1443,7,21, { toCal: "persian", dateStyle: "full" });

 output: Tuesday, Esfand 3, 1400 AP

示例:使用阿拉伯语区域设置将伊斯兰日期转换为希伯来语

 hijriToCalendars(1443,7,21,{ toCal:"hebrew", dateStyle: "full", locale:"ar"})

 output: الثلاثاء، ٢١ آذار الأول ٥٧٨٢ ص

可以对所有其他18个日历执行上述操作。

新增功能是能够将伊斯兰日期格式设置为可用'dateStyles''locales'中的任何一个,而无需转换

为此,指定'toCal'与伊斯兰输入日历相同,默认使用:'islamic-umalqura'

示例:使用波斯语区域设置设置伊斯兰日期的格式

 hijriToCalendars(1443,7,21,{toCal:"islamic-umalqura",dateStyle:"full", locale:"fa"}));

 output:  سه‌شنبه ۲۱ رجب ۱۴۴۳ ه‍.ق.          // mind the RTL requirements

示例:在印地语区域设置伊斯兰日期格式

 hijriToCalendars(1443,7,21,{ toCal : "islamic-umalqura", dateStyle : "full", locale : "hi"}));

 output: AH मंगलवार, 21 रजब 1443

默认输入的伊斯兰日历为'islamic-umalqura',但是,您可以使用'fromCal'选项将其更改为五(5)个伊斯兰日历中的任何一个。

示例:将Islamic-Civil日期21 Rajab 1443(即21/07/1443)转换为格里高利

 hijriToCalendars(1443,7,21, {fromCal : "islamic-civil" });

 output: 2022-02-23T00:00:00.000Z

您可以从上面看到islamic-civil日期与islamic-umalqura日期相差1天。

您可以使用Intl.DateTimeFormat()中的所有可用选项设置输出日期的格式。

无效的伊斯兰日期

如果将无效的伊斯兰日期传递给该函数,将生成错误Invalid islamic-xxxxx date!

无效的伊斯兰日期是指月份中的日期不正确,或者日期或月份不正确。

例如,伊斯兰日期1443/2/30无效,因为回历(月份和月份)的第二个月始终是29天,而不能是30天。

此外,例如,伊斯兰日期1442/12/30将引发错误,因为1442年是非闰年,而12月只有29天。

此方法可靠、准确吗?

如果您认为内部的Java脚本ICU代码是可靠和准确的,则此方法仅使用从Java引擎内部生成的数据。无需担心外部不正确的数学运算或代码中的错误/错误,除非它们位于Java引擎本身内部。

下面是包含用于转换和格式化的示例测试用例的函数,可以运行和测试。

举例说明如何将伊斯兰日期(1443/7/21)恰好是今天的日期转换为所有其他日历格式。

如有任何改进/建议/替代方案,欢迎光临。

数据-lang="js"数据-隐藏="假"数据-控制台="真"数据-巴贝尔="假">
/**********************************************************************************
* @function  : hijriToCalendars(year, month, day, [options])
*
* @purpose   : Converts Islamic (Hijri) Date to other Calendars' Dates.
*              Handles Hijri dates from -280,803 AH to +281,510 AH.
*              Handles all 5 Islamic Calendar Types.
*              Uses the 'JS Calendar Conversion by Target Approximation' Method.
*              No external libraries or complex mathematical/astronomical formulas.
*
* @version   : 1.00
* @author    : Mohsen Alyafei
* @date      : 21 Feb 2022
* @licence   : MIT
* @param     : year   : (numeric) [required] Hijri year  (-280803 to 281510)
* @param     : month  : (numeric) [required] hijri month (1 to 12) note: months is standard 1 based
* @param     : day    : (numeric) [required] hijri day   (1 to 29/30)
* @param     : options: Object with the following optional parameters:
*
*              'fromCal': Specifies the the type of input Islamic Calendar with 5 options:
*                         - 'islamic-umalqura' (default)
*                         - 'islamic-civil'
*                         - 'islamic-tbla'
*                         - 'islamic-rgsa'
*                         - 'islamic'
*
*              'toCal' : Specifies the the type of output Calendar to convert to with 19 Calendars:
*                        - "gregory" : (default)
*                        - "buddhist", "chinese", "coptic", "dangi", "ethioaa", "ethiopic",
*                          "hebrew", "indian", "islamic", "islamic-umalqura", "islamic-tbla",
*                          "islamic-civil", "islamic-rgsa", "iso8601", "japanese", "persian", "roc".
*
*               'dateStyle' Same as used in the Intl.DateTimeFormat() constructor.
*                           If not stated, default output is in Gregorian ISO Format: YYYY:MM:DDTHH:mm:ss.sssZ
*
*               'locale' The BCP 47 language tag for formatting (default is 'en').
*
*               Other options: As used in the Intl.DateTimeFormat() constructor.
*
* @returns   : Return the date in the calendar and format of the specified options.
***********************************************************************************/



//**********************************************************************************
function hijriToCalendars(year, month, day, op={}) {
 op.fromCal ??= "islamic-umalqura";   //
let   gD      = new Date(Date.UTC(2000,0,1));
      gD      = new Date(gD.setUTCDate(gD.getUTCDate() +
                ~~(227022+(year+(month-1)/12+day/354)*354.367)));
const gY      = gD.getUTCFullYear(gD)-2000,
      dFormat = new Intl.DateTimeFormat('en-u-ca-' + op.fromCal, {dateStyle:'short', timeZone:'UTC'});
      gD      = new Date(( gY < 0 ? "-" : "+")+("00000" + Math.abs(gY)).slice(-6)+"-"+("0" + (gD.getUTCMonth(gD)+1)).slice(-2)+"-" + ("0" + gD.getUTCDate(gD)).slice(-2));
let [iM,iD,iY]= [...dFormat.format(gD).split("/")], i=0;
      gD      = new Date(gD.setUTCDate(gD.getUTCDate() +
                ~~(year*354+month*29.53+day-(iY.split(" ")[0]*354+iM*29.53+iD*1)-2)));
while (i < 4) {
   [iM,iD,iY] = [...dFormat.format(gD).split("/")];
   if (iD == day && iM == month && iY.split(" ")[0] == year) return formatOutput(gD);
   gD = new Date(gD.setUTCDate(gD.getUTCDate()+1)); i++;
   }
throw new Error("Invalid "+op.fromCal+" date!");
function formatOutput(gD){
return "toCal"in op ? (op.calendar= op.toCal,
    new Intl.DateTimeFormat(op.locale ??= "en", op).format(gD)) : gD;
}
}
//**********************************************************************************





//==========================================================
// Test Units
//==========================================================
console.log("=".repeat(60));
console.log("Convert the Hijri (Islamic) Date '1443-07-21' to other calendars:");
console.log("input to function ==>: hijriToCalendars(1443,7,21, option)");
console.log("=".repeat(60));

console.log("Default (Gregory) ISO format     : ",hijriToCalendars(1443,7,21)); // convert default islamic-umalqura date to default gregorian date
console.log("Gregory 'full' format            : ",hijriToCalendars(1443,7,21,{toCal:"gregory",dateStyle:"full"}));

console.log("Persian no format                : ",hijriToCalendars(1443,7,21,{toCal:"persian"}));
console.log("Persian 'medium' format          : ",hijriToCalendars(1443,7,21,{toCal:"persian",dateStyle:"medium"}));
console.log("Persian 'full' format            : ",hijriToCalendars(1443,7,21,{toCal:"persian",dateStyle:"full"}));
console.log("Persian 'full' format 'fa' locale: ",hijriToCalendars(1443,7,21,{toCal:"persian",dateStyle:"full",locale:"fa"}));

console.log("Hebrew no format                 : ",hijriToCalendars(1443,7,21,{toCal:"hebrew"}));
console.log("Hebrew 'full' format             : ",hijriToCalendars(1443,7,21,{toCal:"hebrew",dateStyle:"full"}));
console.log("Hebrew 'full' format 'ar' locale : ",hijriToCalendars(1443,7,21,{toCal:"hebrew",dateStyle:"full",locale:"ar"}));

console.log("Indian no format                 : ",hijriToCalendars(1443,7,21,{toCal:"indian"}));
console.log("Indian 'medium' format           : ",hijriToCalendars(1443,7,21,{toCal:"indian",dateStyle:"medium"}));
console.log("Indian 'full' format             : ",hijriToCalendars(1443,7,21,{toCal:"indian",dateStyle:"full"}));
console.log("Indian 'full' format 'hi' locale : ",hijriToCalendars(1443,7,21,{toCal:"indian",dateStyle:"full",locale:"hi"}));
console.log("Indian 'full' format 'in' locale : ",hijriToCalendars(1443,7,21,{toCal:"indian",dateStyle:"full",locale:"in"}));
console.log("Chinese no format                : ",hijriToCalendars(1443,7,21,{toCal:"chinese"}));
console.log("Chinese 'full' format            : ",hijriToCalendars(1443,7,21,{toCal:"chinese",dateStyle:"full"}));
console.log("Chinese 'full' format 'zh' locale: ",hijriToCalendars(1443,7,21,{toCal:"chinese",dateStyle:"full",locale:"zh-CN"}));

console.log("Coptic 'full' format             : ",hijriToCalendars(1443,7,21,{toCal:"coptic",dateStyle:"full"}));
console.log("Coptic 'full' format 'ar' locale : ",hijriToCalendars(1443,7,21,{toCal:"coptic",dateStyle:"full",locale:"ar"}));

console.log("Dangi (Korean) 'full' format     : ",hijriToCalendars(1443,7,21,{toCal:"dangi",dateStyle:"full"}));
console.log("R.O.C. (Minguo) 'full' format    : ",hijriToCalendars(1443,7,21,{toCal:"roc",dateStyle:"full"}));
console.log("Japanese 'full' format           : ",hijriToCalendars(1443,7,21,{toCal:"japanese",dateStyle:"full"}));
console.log("Ethioaa 'full' format            : ",hijriToCalendars(1443,7,21,{toCal:"ethioaa",dateStyle:"full"}));
console.log("Ethiopic 'full' format           : ",hijriToCalendars(1443,7,21,{toCal:"ethiopic",dateStyle:"full"}));
console.log("Buddhist 'full' format           : ",hijriToCalendars(1443,7,21,{toCal:"buddhist",dateStyle:"full"}));

//console.log("");
console.log("=".repeat(60));
console.log("Format the input Hijri Date in different locales without conversion:");
console.log("=".repeat(60));
console.log("Islamic-umalqura 'ar' locale   : ",hijriToCalendars(1443,7,21,{toCal:"islamic-umalqura",dateStyle:"full", locale:"ar"}));
console.log("Islamic-umalqura 'en' locale   : ",hijriToCalendars(1443,7,21,{toCal:"islamic-umalqura",dateStyle:"full", locale:"en"}));
console.log("Islamic-umalqura 'fa' locale   : ",hijriToCalendars(1443,7,21,{toCal:"islamic-umalqura",dateStyle:"full", locale:"fa"}));
console.log("Islamic-umalqura 'hi' locale   : ",hijriToCalendars(1443,7,21,{toCal:"islamic-umalqura",dateStyle:"full", locale:"hi"}));
console.log("Islamic-umalqura 'id' locale   : ",hijriToCalendars(1443,7,21,{toCal:"islamic-umalqura",dateStyle:"full", locale:"id"}));
console.log("Islamic-umalqura 'pa' locale   : ",hijriToCalendars(1443,7,21,{toCal:"islamic-umalqura",dateStyle:"full", locale:"pa"}));
console.log("Islamic-umalqura 'ma' locale   : ",hijriToCalendars(1443,7,21,{toCal:"islamic-umalqura",dateStyle:"full", locale:"ma"}));
console.log("Islamic-cvil 'ar' locale       : ",hijriToCalendars(1443,7,21,{toCal:"islamic-civil",dateStyle:"full", locale:"ar"}));

//console.log("");
console.log("=".repeat(60));
console.log("Convert Max Negative and Max Positive Hijri Dates to Gregorian");
console.log("=".repeat(60));
console.log("Maximum Negative Date : ",hijriToCalendars(-280803,12,22)); // max negative hijri date
console.log("Maximum Positive Date : ",hijriToCalendars(281510,12,29));  // max positive hijri date
//console.log("=".repeat(60));

// Other Test Cases
var r=0; // test tracker
r |= test(1,1,1,{},"0622-07-19T00:00:00.000Z");
r |= test(622,7,18,{},"1225-08-02T00:00:00.000Z");
r |= test(1443,7,21,{},"2022-02-22T00:00:00.000Z");
r |= test(1443,7,14,{},"2022-02-15T00:00:00.000Z");
r |= test(1443,9,1,{},"2022-04-02T00:00:00.000Z");
r |= test(2000,9,1,{},"2562-09-01T00:00:00.000Z");
r |= test(2100,9,1,{},"2659-09-10T00:00:00.000Z");
r |= test(2200,9,1,{},"2756-09-17T00:00:00.000Z");
r |= test(2300,9,1,{},"2853-09-25T00:00:00.000Z");
r |= test(2400,9,1,{},"2950-10-04T00:00:00.000Z");
r |= test(2443,9,1,{},"2992-06-22T00:00:00.000Z");
r |= test(3443,9,1,{},"3962-09-13T00:00:00.000Z");
r |= test(4443,9,1,{},"4932-12-03T00:00:00.000Z");
r |= test(5443,9,1,{},"5903-02-23T00:00:00.000Z");
r |= test(6443,9,1,{},"6873-05-14T00:00:00.000Z");
r |= test(6550,7,14,{},"6977-01-20T00:00:00.000Z");
r |= test(7443,9,1,{},"7843-08-05T00:00:00.000Z");
r |= test(8443,9,1,{},"8813-10-24T00:00:00.000Z");
r |= test(9443,9,1,{},"9784-01-14T00:00:00.000Z");
r |= test(10443,9,1,{},"+010754-04-06T00:00:00.000Z");
r |= test(150443,9,1,{},"+146585-06-23T00:00:00.000Z");
r |= test(1443,4,29,{fromCal:"islamic"},"2021-12-03T00:00:00.000Z");
r |= test(1443,7,21,{fromCal:"islamic-civil"},"2022-02-23T00:00:00.000Z");
r |= test(1443,7,21,{fromCal:"islamic"},"2022-02-22T00:00:00.000Z");
r |= test(102428,4,29,{fromCal:"islamic-civil"},"+099999-11-24T00:00:00.000Z");
r |= test(-640, 7, 20,{fromCal:"islamic-civil"},"0001-03-03T00:00:00.000Z");
r |= test(-2000,1,1,{},"-001319-02-16T00:00:00.000Z");
r |= test(-2020,1,1,{},"-001339-09-22T00:00:00.000Z");
r |= test(-6000,1,1,{fromCal:"islamic-tbla"},"-005200-03-27T00:00:00.000Z");
r |= test(-6000,1,1,{fromCal:"islamic-rgsa"},"-005200-03-24T00:00:00.000Z");
r |= test(-20000,1,1,{},"-018783-02-11T00:00:00.000Z");
r |= test(-60000,12,29,{fromCal:"islamic"},"-057591-02-03T00:00:00.000Z");
r |= test(-100000,12,20,{fromCal:"islamic"},"-096400-02-08T00:00:00.000Z");
r |= test(-116000,1,1,{},"-111925-09-16T00:00:00.000Z");
r |= test(-270000,12,29,{fromCal:"islamic"},"-261338-01-15T00:00:00.000Z");
r |= test(275000,12,29,{fromCal:"islamic"},"+267434-02-27T00:00:00.000Z");
r |= test(-200000,1,1,{},"-193424-12-23T00:00:00.000Z");
r |= test(-206779,3,1,{},"-200000-01-01T00:00:00.000Z");

if (r==0) console.log("✅ All Other Test Cases Passed.");
//============ test function ============
function test(Y,M,D,OP,should) {
let out = hijriToCalendars(Y,M,D,OP);
out=out.toISOString();
if (out !== should) {console.log(`${Y},${M},${D} Output   : ${out}
${Y},${M},${D} Should be: ${should}`);return 1;}
}

按目标近似法进行的Java日历转换-流程图

推荐答案

工程师、物理学家和数学家在一列穿过英国乡村的火车上,当工程师向窗外望去,看到山坡上有一只孤零零的害群之马时,喊道,嘿,看哪,英格兰有害群之马!物理学家很快警告他,我们所能确定的是,英格兰有一只害群之马。数学家于是告诫他们两人,宣布,我们所能得出的结论是,英格兰至少有一只黑羊在一边……

由于我是一名数学家,我在欠款方面表示歉意...

经过研究,我认为长期的答案在于ECMAScript提案"Temporal",目前(截至2022年2月24日)在Active ProposalsStage 3中。全文Stage 3 Draft of the Temporal proposal提供了关于提案动机的一些背景知识以及整个技术规范,Temporal documentation提供了更容易理解的目的和操作用途。后面的文档是一个很好的起点...

从文档中摘录的几位知名人士:

  • Temporal.Instant抽象时间点,而不考虑日历或位置。即,这是所有日历和时区必须引用以在指定区域设置中呈现日期和时间的绝对时间。

  • Temporal.Calendar是日历系统的表示,它提供有关日历的细节和操作日历的方法。存在许多预置日历,例如‘Gregory’、‘Islamic’、‘Hebrew’等。此外,时间建议还使实现您自己的日历成为可能。

尽管如此,CookBook的例子都使用了现代的时间框架,因此我实际上有兴趣尝试实现从公元4年到1752年的儒略历,在这段时间内它被使用。其概念是,参照儒略历的同期日期可以直接作为儒略日期输入,并且可以很容易地操作并与其他日历中的日期进行比较,确信这些日期引用了相同的基础参照系...威廉·莎士比亚出生的伊斯兰日子是哪天?

作为ECMAScript阶段3的提议,时态仍然处于实验阶段,但鼓励在非生产环境中进行测试,并报告任何错误。由于Temporal.Instant是与日历或位置无关的基本时间点,因此日历之间的转换是通过不同日历显示Temporal.Instant的自然结果。

数据-lang="js"数据-隐藏="假"数据-控制台="真"数据-巴贝尔="假">
<script type='module'>

  import * as TemporalModule from 'https://cdn.jsdelivr.net/npm/@js-temporal/polyfill@0.3.0/dist/index.umd.js'
  
  const Temporal = temporal.Temporal;

  let islamicDate = Temporal.ZonedDateTime.from( {
    year: 1440,
    monthCode: 'M06',
    day: 2,
    hour:12,
    minute: 45,
    timeZone: 'Asia/Dubai',
    calendar: 'islamic'
  } );
  
  let hebrewDate = Temporal.ZonedDateTime.from( islamicDate ).withCalendar( 'hebrew' );
  
  let gregoryDate = Temporal.ZonedDateTime.from( islamicDate ).withCalendar( 'gregory' );
  
  console.log( `Date in Islamic: ${ islamicDate.toPlainDate().toLocaleString( 'en-US', { calendar: 'islamic' } ) }` );
  console.log( `DateTime in Islamic in Dubai: ${ islamicDate.toLocaleString( 'en-US', { calendar: 'islamic' } ) }` );
  console.log( `DateTime in Islamic in London: ${ islamicDate.toInstant().toZonedDateTimeISO( 'Europe/London' ).toLocaleString('en-US', { calendar: 'islamic' } ) }` );  
  
  console.log( `Date in Hebrew: ${ hebrewDate.toPlainDate().toLocaleString( 'en-US', { calendar: 'hebrew' } ) }` );
  console.log( `DateTime in Hebrew in Dubai: ${ hebrewDate.toLocaleString( 'en-US', { calendar: 'hebrew' } ) }` );
  console.log( `DateTime in Hebrew in London: ${ hebrewDate.toInstant().toZonedDateTimeISO( 'Europe/London' ).toLocaleString('en-US', { calendar: 'hebrew' } ) }` );
  
  console.log( `Date in Gregorian: ${ gregoryDate.toPlainDate().toLocaleString( 'en-US' ) }` );
  console.log( `DateTime in Gregorian: ${ gregoryDate.toLocaleString( 'en-US' ) }` ); 
  
  console.log( `Islamic Date in English (United States) Locale:
${ islamicDate.toPlainDate().toString() }` );
  console.log( `Islamic DateTime in English (United States) Locale:
${ islamicDate.toString() }` );
  
</script>

(更新了代码段以更好地演示时区和日历之间的转换,以及显示日期的各种方法。)

这篇关于如何在没有外部库或复杂天文公式的情况下将5个伊斯兰日历日期中的任何一个转换为18个世界日历中的任何一个的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆