unicode-org / icu4x

Solving i18n for client-side and resource-constrained environments.
https://icu4x.unicode.org
Other
1.33k stars 173 forks source link

Dates don't roundtrip when converting to ISO and back again with non-always-calculating IslamicObservational/IslamicUmmAlQura calendars #5069

Open anba opened 2 months ago

anba commented 2 months ago

The following test case doesn't pass when checking converting to an ISO date and back again returns the same month. When switching to new_always_calculating the test case does succeed.

This is a regression in release 1.5. It still worked correctly in release 1.4.

Test case:

use icu::calendar::types::{Era, MonthCode};
use icu::calendar::Calendar;
use icu::calendar::Ref;

use icu::calendar::islamic::{IslamicObservational, IslamicUmmAlQura};

use std::str::FromStr;

fn test_IslamicObservational() {
  // let cal = IslamicObservational::new_always_calculating();
  let cal = IslamicObservational::new();

  let cal = Ref(&cal);

  let era = Era::from_str("ah").unwrap();
  let month_code = MonthCode::from_str("M01").unwrap();

  let dt = cal.date_from_codes(era, 1390, month_code, 30).unwrap();

  let month = cal.month(&dt);
  assert!(month.code == month_code);

  let iso = cal.date_to_iso(&dt);
  let dt2 = cal.date_from_iso(iso);

  let month = cal.month(&dt2);
  assert!(month.code == month_code);
}

fn test_IslamicUmmAlQura() {
  // let cal = IslamicUmmAlQura::new_always_calculating();
  let cal = IslamicUmmAlQura::new();

  let cal = Ref(&cal);

  let era = Era::from_str("ah").unwrap();
  let month_code = MonthCode::from_str("M01").unwrap();

  let dt = cal.date_from_codes(era, 1391, month_code, 30).unwrap();

  let month = cal.month(&dt);
  assert!(month.code == month_code);

  let iso = cal.date_to_iso(&dt);
  let dt2 = cal.date_from_iso(iso);

  let month = cal.month(&dt2);
  assert!(month.code == month_code);
}

fn main() {
  test_IslamicObservational();
  test_IslamicUmmAlQura();
}
anba commented 2 months ago

day_of_month can return zero for some dates when using the non-always-calculating constructor. That's probably a related issue:

use icu::calendar::Date;
use icu::calendar::Calendar;
use icu::calendar::islamic::IslamicObservational;

fn main() {
  // let cal = IslamicObservational::new_always_calculating();
  let cal = IslamicObservational::new();

  let iso = Date::try_new_iso_date(2000, 5, 5).unwrap();
  let date = cal.date_from_iso(iso);

  println!("month = {}", cal.month(&date).ordinal);
  println!("day = {}", cal.day_of_month(&date).0);
}
Manishearth commented 2 months ago

cc @sffc