从Hijri日期字符串获取格里高利日期

时间:2015-08-03 16:24:43

标签: java android date hijri

我想从Hijri日期索引中得到一个格里高利日期。

e.g

Fiddler

这应该返回

getDate(1436, "SHawwal", 18)

我怎么能进行转换?

1 个答案:

答案 0 :(得分:5)

我很遗憾你不明智。许多人盲目地推荐像Joda-Time这样的知名图书馆,甚至没有检查这是否能解决你的问题并符合你的要求

哪个要求?您已声明:

  

value tuple(1436,“SHawwal”,18)=>格里高利日期为String(“3rd   2015年8月“)

让我们首先证明 Joda-Time无法进行此转换。 Shawwal(“شوال”)是伊斯兰农历的第十个月。所以我们可以尝试这个Joda-Time代码:

Chronology hijri = 
  IslamicChronology.getInstance(DateTimeZone.UTC, IslamicChronology.LEAP_YEAR_INDIAN);
LocalDate dtHijri = new LocalDate(1436, 10, 18, hijri);

Chronology iso = ISOChronology.getInstanceUTC();
LocalDate dtIso = // Joda does not offer a direct conversion, sorry
  dtHijri.toDateTimeAtStartOfDay(DateTimeZone.UTC)
  .withChronology(iso).toLocalDate(); 
System.out.println("hijri-to-gregorian: " + dtHijri + "=>" + dtIso); 
// hijri-to-gregorian: 1436-10-18=>2015-08-04

Joda-Time(LEAP_YEAR_15_BASED,LEAP_YEAR_16_BASED,LEAP_YEAR_HABASH_AL_HASIB或LEAP_YEAR_INDIAN)提供的所有闰年模式都会产生相同的结果“2015-08-04”,这比您的预期晚一天。我已经明确测试了所有四种闰年模式。

Java-8是另类吗?

HijrahDate hd = HijrahChronology.INSTANCE.date(HijrahEra.AH, 1436, 10, 18);
LocalDate ld = LocalDate.from(hd);
System.out.println("java-8: " + ld); // java-8: 2015-08-03

没关系,但我认为你需要在Android上完成这项工作。那里你会失败,因为Java-8类根本不可用。

顺便说一下:为什么Java-8会在这里产生正确的结果?它使用了沙特阿拉伯的Umalqura日历数据,而Joda-Time使用了四个半学术近似的伊斯兰日历。事实上,伊斯兰日历存在于许多本地变体中(我上面的评论针对这一点,询问您要使用哪种变体)。

ThreetenABP 也会这样做,因为它应该是Android上java.time(JSR-310)的后端?

HijrahDate hd = HijrahChronology.INSTANCE.date(HijrahEra.AH, 1436, 10, 18);
org.threeten.bp.LocalDate ld = org.threeten.bp.LocalDate.from(hd);
System.out.println("threeten-bp: " + ld); // threeten-bp: 2015-08-04

令人惊讶的是,后端失败了。如果您注意ThreetenBP和Java-8关于Hijri日历的文档,那么您将看到一个很大的不同。 ThreetenBP不使用Umalqura日历!它事实上相当于Joda-Time的闰年模式LEAP_YEAR_16_BASED。好吧,作为(笨拙)解决方法,您可以配置ThreetenBP,以便通过定义描述here的配置文件来告知偏差。但我强烈怀疑这只适用于Java平台,因为ThreetenABP(ThreetenBP的Android迁移)并不关心在给定Android应用程序的assets-directory中定义这样的文件。

ICU4J怎么样?

ICU4J(Unicode的国际组件)似乎有效,至少有三个variants:ISLAMIC,ISLAMIC_TBLA和ISLAMIC_UMALQURA。所有3种变体都产生“2015-08-03”。因此,您必须仔细决定使用哪一个,因为这些变体在其他日子会有所不同!关于TBLA变体,这是一个半学术近似,类似于Joda-Time的LEAP_YEAR_16_BASED(但使用星期四时代 - 一直有一天的差异)。称为ISLAMIC的变体是月球周期的简化天文模拟。我怀疑你宁愿想要沙特阿拉伯的Umalqura日历,对吗???

无论如何,ICU4J有一个很大的缺点。它不是为Android平台设计的,非常大(10.7 MByte)。据报道有些人无法在Android上运行该软件。

我的建议:使用Time4A

我对Android平台上当前支持Hijri日历的分析结果是开发了一个名为Time4A的替代库,以帮助人们在该平台上使用Hijri日历。该库(版本v3.6-2015f)支持8个半学术闰年模式,包括tbla-variant(HijriAlgorithm.WEST_ISLAMIC_ASTRO),沙特阿拉伯的Umalqura日历和ICU4J的天文模拟。并且重要的是:Time4A还将理解阿拉伯语月份名称(使用new Locale("ar"))。例如:

HijriCalendar hijri = 
  HijriCalendar.of(HijriCalendar.VARIANT_UMALQURA, 1436, HijriMonth.SHAWWAL, 18);
PlainDate iso = hijri.transform(PlainDate.class);
System.out.println("Time4A: " + iso); // Time4A: 2015-08-03

现在最终的代码解决方案处理了您的要求中的一些奇怪的事情:

int hijriYear = 1436;
String hijriMonth = "SHawwal";
int hijriDayOfMonth = 18;

ChronoFormatter<HijriCalendar> inputFormat =
        ChronoFormatter.setUp(HijriCalendar.class, Locale.ENGLISH)
        .addPattern("yyyyMMMMd", PatternType.NON_ISO_DATE).build();
HijriCalendar hijriDate = 
        inputFormat
        .with(Attributes.PARSE_CASE_INSENSITIVE, true)
        .withCalendarVariant(HijriCalendar.VARIANT_UMALQURA)
        .parse(hijriYear + hijriMonth + hijriDayOfMonth);

PlainDate iso = hijriDate.transform(PlainDate.class);

ChronoFormatter<PlainDate> outputFormat =
    ChronoFormatter.setUp(PlainDate.class, Locale.ENGLISH)
        .addEnglishOrdinal(PlainDate.DAY_OF_MONTH)
        .addPattern(" MMMM uuuu", PatternType.CLDR).build();
String s = outputFormat.format(iso);
System.out.println(s); // 3rd August 2015

但你仍然需要考虑你真正想要的Hijri日历的哪个版本。没有智能软件可以为您决定。