Bug 1899177 - Part 2: Vendor icu_calendar and calendrical_calculations. r=spidermonkey-reviewers,dminor

The large files check in "vendor_rust.py" had to be manually disabled to allow
importing "third_party/rust/calendrical_calculations/src/astronomy.rs". (About 20% of
the file size is test code.)

Differential Revision: https://phabricator.services.mozilla.com/D211763
This commit is contained in:
André Bargull 2024-06-20 17:42:12 +00:00
Родитель e268f80c64
Коммит a4952400f8
68 изменённых файлов: 23777 добавлений и 0 удалений

34
Cargo.lock сгенерированный
Просмотреть файл

@ -622,6 +622,16 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
[[package]]
name = "calendrical_calculations"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dfe3bc6a50b4667fafdb6d9cf26731c5418c457e317d8166c972014facf9a5d"
dependencies = [
"core_maths",
"displaydoc",
]
[[package]] [[package]]
name = "camino" name = "camino"
version = "1.1.2" version = "1.1.2"
@ -2771,12 +2781,36 @@ dependencies = [
"want", "want",
] ]
[[package]]
name = "icu_calendar"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eb932a690c92f87955e923106181ee0d5682e688ff37fb5c7b296e1fe806edb"
dependencies = [
"calendrical_calculations",
"displaydoc",
"icu_calendar_data",
"icu_locid",
"icu_locid_transform",
"icu_provider",
"tinystr",
"writeable",
"zerovec",
]
[[package]]
name = "icu_calendar_data"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22aec7d032735d9acb256eeef72adcac43c3b7572f19b51576a63d664b524ca2"
[[package]] [[package]]
name = "icu_capi" name = "icu_capi"
version = "1.5.0" version = "1.5.0"
dependencies = [ dependencies = [
"diplomat", "diplomat",
"diplomat-runtime", "diplomat-runtime",
"icu_calendar",
"icu_locid", "icu_locid",
"icu_properties", "icu_properties",
"icu_provider", "icu_provider",

Просмотреть файл

@ -0,0 +1 @@
{"files":{"Cargo.toml":"690c151bccdceefbca495bfb96d3d482d4ecff698b5500201cad595ccfd9d7ce","LICENSE":"192ea857d1bff2b87c174de36cbae5c173234726c6b8eceab9790a535d7dbc95","README.md":"cdffd3b4ab1a4efad95de0dcdf0b097fce24fedf04d076e8ab3909ac2e2accb5","src/astronomy.rs":"5e95c8a42bb32570932205d008842332f83a0e03d02166de6ec7956385e12b4c","src/chinese_based.rs":"e6a1c88f7cd627f74a25bd38eee653b83aafe7a01652e8251c97510b05f1818f","src/coptic.rs":"4ef26fbc10d7e81057f7a9876951e1a62758a7881c58dab9428dccee2e5fd25f","src/error.rs":"aec0635f228f81806a8af7683f46e6b89a316423cb493f48fa44e0f942d54d2f","src/ethiopian.rs":"0ea906c421d78462390fc43fd3653bd0f07615051495ba1c0c80b4a90cf8367a","src/hebrew.rs":"f434b7a4d359aa478669491d0cf712082d9ec0e65f9136bb1d1daca6ac453e2f","src/helpers.rs":"8861b51f6c15cb88cfea6322feb8b13ec61c4a37ff44aaac8ead5541ac3a3549","src/islamic.rs":"488ffbad6b8a15c86b51efcc523e3f1095a924180de52b4333fe0d2aabb64ad3","src/iso.rs":"a1ec6befd39edeb8ab625db43411bdb4f38d92081afb848dcfe8ab55b18f57cc","src/julian.rs":"e6d3cef1867c813edc9e5bc28870cd646127eaf13ced32889bce8991af2f6235","src/lib.rs":"5437fa651b598c348f3a789abbfa7e576a7945e16a059731cfa4e3200c3ae625","src/persian.rs":"cbd7258f2efa995f5b9185cdb1e3678af44c0f4059a023c7ef5347aade35c94c","src/rata_die.rs":"d8eac3c48f46956b0f9ddda2da9aab82a7af1187e743fa6058d9b5112e70ebe5"},"package":"8dfe3bc6a50b4667fafdb6d9cf26731c5418c457e317d8166c972014facf9a5d"}

63
third_party/rust/calendrical_calculations/Cargo.toml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,63 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.66"
name = "calendrical_calculations"
version = "0.1.0"
authors = ["The ICU4X Project Developers"]
include = [
"data/**/*",
"src/**/*",
"examples/**/*",
"benches/**/*",
"tests/**/*",
"Cargo.toml",
"LICENSE",
"README.md",
]
description = "Calendrical calculations in Rust"
readme = "README.md"
keywords = [
"zerocopy",
"serialization",
"zero-copy",
"serde",
]
categories = [
"rust-patterns",
"memory-management",
"caching",
"no-std",
"data-structures",
]
license = "Apache-2.0"
repository = "https://github.com/unicode-org/icu4x"
[package.metadata.cargo-all-features]
denylist = ["bench"]
[package.metadata.docs.rs]
all-features = true
[package.metadata.workspaces]
independent = true
[dependencies.core_maths]
version = "0.1"
[dependencies.displaydoc]
version = "0.2.3"
default-features = false
[features]
std = []

201
third_party/rust/calendrical_calculations/LICENSE поставляемый Normal file
Просмотреть файл

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2023 The Unicode Consortium
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

21
third_party/rust/calendrical_calculations/README.md поставляемый Normal file
Просмотреть файл

@ -0,0 +1,21 @@
# calendrical_calculations [![crates.io](https://img.shields.io/crates/v/calendrical_calculations)](https://crates.io/crates/calendrical_calculations)
<!-- cargo-rdme start -->
Calendrical calculations
This crate implements algorithms from
Calendrical Calculations by Reingold & Dershowitz, Cambridge University Press, 4th edition (2018)
as needed by [ICU4X](https://github.com/unicode-org/icu4x).
Most of these algorithms can be found as lisp code in the book or
[on GithHub](https://github.com/EdReingold/calendar-code2/blob/main/calendar.l).
The primary purpose of this crate is use by ICU4X, however if non-ICU4X users need this we are happy
to add more structure to this crate as needed.
<!-- cargo-rdme end -->
## More Information
For more information on development, authorship, contributing etc. please visit [`ICU4X home page`](https://github.com/unicode-org/icu4x).

2650
third_party/rust/calendrical_calculations/src/astronomy.rs поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

652
third_party/rust/calendrical_calculations/src/chinese_based.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,652 @@
use crate::astronomy::{self, Astronomical, Location, MEAN_SYNODIC_MONTH, MEAN_TROPICAL_YEAR};
use crate::helpers::i64_to_i32;
use crate::rata_die::{Moment, RataDie};
use core::num::NonZeroU8;
#[allow(unused_imports)]
use core_maths::*;
// Don't iterate more than 14 times (which accounts for checking for 13 months)
const MAX_ITERS_FOR_MONTHS_OF_YEAR: u8 = 14;
/// The trait ChineseBased is used by Chinese-based calendars to perform computations shared by such calendar.
/// To do so, calendars should:
///
/// - Implement `fn location` by providing a location at which observations of the moon are recorded, which
/// may change over time (the zone is important, long, lat, and elevation are not relevant for these calculations)
/// - Define `const EPOCH` as a `RataDie` marking the start date of the era of the Calendar for internal use,
/// which may not accurately reflect how years or eras are marked traditionally or seen by end-users
pub trait ChineseBased {
/// Given a fixed date, return the location used for observations of the new moon in order to
/// calculate the beginning of months. For multiple Chinese-based lunar calendars, this has
/// changed over the years, and can cause differences in calendar date.
fn location(fixed: RataDie) -> Location;
/// The RataDie of the beginning of the epoch used for internal computation; this may not
/// reflect traditional methods of year-tracking or eras, since Chinese-based calendars
/// may not track years ordinally in the same way many western calendars do.
const EPOCH: RataDie;
}
// The equivalent first day in the Chinese calendar (based on inception of the calendar)
const CHINESE_EPOCH: RataDie = RataDie::new(-963099); // Feb. 15, 2637 BCE (-2636)
/// The Chinese calendar relies on knowing the current day at the moment of a new moon;
/// however, this can vary depending on location. As such, new moon calculations are based
/// on the time in Beijing. Before 1929, local time was used, represented as UTC+(1397/180 h).
/// In 1929, China adopted a standard time zone based on 120 degrees of longitude, meaning
/// from 1929 onward, all new moon calculations are based on UTC+8h.
///
/// Offsets are not given in hours, but in partial days (1 hour = 1 / 24 day)
const UTC_OFFSET_PRE_1929: f64 = (1397.0 / 180.0) / 24.0;
const UTC_OFFSET_POST_1929: f64 = 8.0 / 24.0;
const CHINESE_LOCATION_PRE_1929: Location =
Location::new_unchecked(39.0, 116.0, 43.5, UTC_OFFSET_PRE_1929);
const CHINESE_LOCATION_POST_1929: Location =
Location::new_unchecked(39.0, 116.0, 43.5, UTC_OFFSET_POST_1929);
// The first day in the Korean Dangi calendar (based on the founding of Gojoseon)
const KOREAN_EPOCH: RataDie = RataDie::new(-852065); // Lunar new year 2333 BCE (-2332 ISO)
/// The Korean Dangi calendar relies on knowing the current day at the moment of a new moon;
/// however, this can vary depending on location. As such, new moon calculations are based on
/// the time in Seoul. Before 1908, local time was used, represented as UTC+(3809/450 h).
/// This changed multiple times as different standard timezones were adopted in Korea.
/// Currently, UTC+9h is used.
///
/// Offsets are not given in hours, but in partial days (1 hour = 1 / 24 day).
const UTC_OFFSET_ORIGINAL: f64 = (3809.0 / 450.0) / 24.0;
const UTC_OFFSET_1908: f64 = 8.5 / 24.0;
const UTC_OFFSET_1912: f64 = 9.0 / 24.0;
const UTC_OFFSET_1954: f64 = 8.5 / 24.0;
const UTC_OFFSET_1961: f64 = 9.0 / 24.0;
const FIXED_1908: RataDie = RataDie::new(696608); // Apr 1, 1908
const FIXED_1912: RataDie = RataDie::new(697978); // Jan 1, 1912
const FIXED_1954: RataDie = RataDie::new(713398); // Mar 21, 1954
const FIXED_1961: RataDie = RataDie::new(716097); // Aug 10, 1961
const KOREAN_LATITUDE: f64 = 37.0 + (34.0 / 60.0);
const KOREAN_LONGITUDE: f64 = 126.0 + (58.0 / 60.0);
const KOREAN_ELEVATION: f64 = 0.0;
const KOREAN_LOCATION_ORIGINAL: Location = Location::new_unchecked(
KOREAN_LATITUDE,
KOREAN_LONGITUDE,
KOREAN_ELEVATION,
UTC_OFFSET_ORIGINAL,
);
const KOREAN_LOCATION_1908: Location = Location::new_unchecked(
KOREAN_LATITUDE,
KOREAN_LONGITUDE,
KOREAN_ELEVATION,
UTC_OFFSET_1908,
);
const KOREAN_LOCATION_1912: Location = Location::new_unchecked(
KOREAN_LATITUDE,
KOREAN_LONGITUDE,
KOREAN_ELEVATION,
UTC_OFFSET_1912,
);
const KOREAN_LOCATION_1954: Location = Location::new_unchecked(
KOREAN_LATITUDE,
KOREAN_LONGITUDE,
KOREAN_ELEVATION,
UTC_OFFSET_1954,
);
const KOREAN_LOCATION_1961: Location = Location::new_unchecked(
KOREAN_LATITUDE,
KOREAN_LONGITUDE,
KOREAN_ELEVATION,
UTC_OFFSET_1961,
);
/// A type implementing [`ChineseBased`] for the Chinese calendar
#[derive(Debug)]
#[allow(clippy::exhaustive_structs)] // newtype
pub struct Chinese;
/// A type implementing [`ChineseBased`] for the Dangi (Korean) calendar
#[derive(Debug)]
#[allow(clippy::exhaustive_structs)] // newtype
pub struct Dangi;
impl ChineseBased for Chinese {
fn location(fixed: RataDie) -> Location {
let year = crate::iso::iso_year_from_fixed(fixed);
if year < 1929 {
CHINESE_LOCATION_PRE_1929
} else {
CHINESE_LOCATION_POST_1929
}
}
const EPOCH: RataDie = CHINESE_EPOCH;
}
impl ChineseBased for Dangi {
fn location(fixed: RataDie) -> Location {
if fixed < FIXED_1908 {
KOREAN_LOCATION_ORIGINAL
} else if fixed < FIXED_1912 {
KOREAN_LOCATION_1908
} else if fixed < FIXED_1954 {
KOREAN_LOCATION_1912
} else if fixed < FIXED_1961 {
KOREAN_LOCATION_1954
} else {
KOREAN_LOCATION_1961
}
}
const EPOCH: RataDie = KOREAN_EPOCH;
}
/// Marks the bounds of a lunar year
#[derive(Debug, Copy, Clone)]
#[allow(clippy::exhaustive_structs)] // we're comfortable making frequent breaking changes to this crate
pub struct YearBounds {
/// The date marking the start of the current lunar year
pub new_year: RataDie,
/// The date marking the start of the next lunar year
pub next_new_year: RataDie,
}
impl YearBounds {
/// Compute the YearBounds for the lunar year (年) containing `date`,
/// as well as the corresponding solar year (歲). Note that since the two
/// years overlap significantly but not entirely, the solstice bounds for the solar
/// year *may* not include `date`.
#[inline]
pub fn compute<C: ChineseBased>(date: RataDie) -> Self {
let prev_solstice = winter_solstice_on_or_before::<C>(date);
let (new_year, next_solstice) = new_year_on_or_before_fixed_date::<C>(date, prev_solstice);
// Using 400 here since new years can be up to 390 days apart, and we add some padding
let next_new_year = new_year_on_or_before_fixed_date::<C>(new_year + 400, next_solstice).0;
Self {
new_year,
next_new_year,
}
}
/// The number of days in this year
pub fn count_days(self) -> u16 {
let result = self.next_new_year - self.new_year;
debug_assert!(
((u16::MIN as i64)..=(u16::MAX as i64)).contains(&result),
"Days in year should be in range of u16."
);
result as u16
}
/// Whether or not this is a leap year
pub fn is_leap(self) -> bool {
let difference = self.next_new_year - self.new_year;
difference > 365
}
}
/// Get the current major solar term of a fixed date, output as an integer from 1..=12.
///
/// Based on functions from _Calendrical Calculations_ by Reingold & Dershowitz.
/// Lisp reference code: https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L5273-L5281
pub(crate) fn major_solar_term_from_fixed<C: ChineseBased>(date: RataDie) -> u32 {
let moment: Moment = date.as_moment();
let location = C::location(date);
let universal: Moment = Location::universal_from_standard(moment, location);
let solar_longitude =
i64_to_i32(Astronomical::solar_longitude(Astronomical::julian_centuries(universal)) as i64);
debug_assert!(
solar_longitude.is_ok(),
"Solar longitude should be in range of i32"
);
let s = solar_longitude.unwrap_or_else(|e| e.saturate());
let result_signed = (2 + s.div_euclid(30) - 1).rem_euclid(12) + 1;
debug_assert!(result_signed >= 0);
result_signed as u32
}
/// Returns true if the month of a given fixed date does not have a major solar term,
/// false otherwise.
///
/// Based on functions from _Calendrical Calculations_ by Reingold & Dershowitz.
/// Lisp reference code: https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L5345-L5351
pub(crate) fn no_major_solar_term<C: ChineseBased>(date: RataDie) -> bool {
major_solar_term_from_fixed::<C>(date)
== major_solar_term_from_fixed::<C>(new_moon_on_or_after::<C>((date + 1).as_moment()))
}
/// The fixed date in standard time at the observation location of the next new moon on or after a given Moment.
///
/// Based on functions from _Calendrical Calculations_ by Reingold & Dershowitz.
/// Lisp reference code: https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L5329-L5338
pub(crate) fn new_moon_on_or_after<C: ChineseBased>(moment: Moment) -> RataDie {
let new_moon_moment = Astronomical::new_moon_at_or_after(midnight::<C>(moment));
let location = C::location(new_moon_moment.as_rata_die());
Location::standard_from_universal(new_moon_moment, location).as_rata_die()
}
/// The fixed date in standard time at the observation location of the previous new moon before a given Moment.
///
/// Based on functions from _Calendrical Calculations_ by Reingold & Dershowitz.
/// Lisp reference code: https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L5318-L5327
pub(crate) fn new_moon_before<C: ChineseBased>(moment: Moment) -> RataDie {
let new_moon_moment = Astronomical::new_moon_before(midnight::<C>(moment));
let location = C::location(new_moon_moment.as_rata_die());
Location::standard_from_universal(new_moon_moment, location).as_rata_die()
}
/// Universal time of midnight at start of a Moment's day at the observation location
///
/// Based on functions from _Calendrical Calculations_ by Reingold & Dershowitz.
/// Lisp reference code: https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L5353-L5357
pub(crate) fn midnight<C: ChineseBased>(moment: Moment) -> Moment {
Location::universal_from_standard(moment, C::location(moment.as_rata_die()))
}
/// Determines the fixed date of the lunar new year given the start of its corresponding solar year (歲), which is
/// also the winter solstice
///
/// Based on functions from _Calendrical Calculations_ by Reingold & Dershowitz.
/// Lisp reference code: https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L5370-L5394
pub(crate) fn new_year_in_sui<C: ChineseBased>(prior_solstice: RataDie) -> (RataDie, RataDie) {
// s1 is prior_solstice
// Using 370 here since solstices are ~365 days apart
let following_solstice = winter_solstice_on_or_before::<C>(prior_solstice + 370); // s2
let month_after_eleventh = new_moon_on_or_after::<C>((prior_solstice + 1).as_moment()); // m12
let month_after_twelfth = new_moon_on_or_after::<C>((month_after_eleventh + 1).as_moment()); // m13
let next_eleventh_month = new_moon_before::<C>((following_solstice + 1).as_moment()); // next-m11
let lhs_argument =
((next_eleventh_month - month_after_eleventh) as f64 / MEAN_SYNODIC_MONTH).round() as i64;
if lhs_argument == 12
&& (no_major_solar_term::<C>(month_after_eleventh)
|| no_major_solar_term::<C>(month_after_twelfth))
{
(
new_moon_on_or_after::<C>((month_after_twelfth + 1).as_moment()),
following_solstice,
)
} else {
(month_after_twelfth, following_solstice)
}
}
/// Get the moment of the nearest winter solstice on or before a given fixed date
///
/// Based on functions from _Calendrical Calculations_ by Reingold & Dershowitz.
/// Lisp reference code: https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L5359-L5368
pub(crate) fn winter_solstice_on_or_before<C: ChineseBased>(date: RataDie) -> RataDie {
let approx = Astronomical::estimate_prior_solar_longitude(
astronomy::WINTER,
midnight::<C>((date + 1).as_moment()),
);
let mut iters = 0;
let mut day = Moment::new((approx.inner() - 1.0).floor());
while iters < MAX_ITERS_FOR_MONTHS_OF_YEAR
&& astronomy::WINTER
>= Astronomical::solar_longitude(Astronomical::julian_centuries(midnight::<C>(
day + 1.0,
)))
{
iters += 1;
day += 1.0;
}
debug_assert!(
iters < MAX_ITERS_FOR_MONTHS_OF_YEAR,
"Number of iterations was higher than expected"
);
day.as_rata_die()
}
/// Get the fixed date of the nearest Lunar New Year on or before a given fixed date.
/// This function also returns the solstice following a given date for optimization (see #3743).
///
/// To call this function you must precompute the value of the prior solstice, which
/// is the result of winter_solstice_on_or_before
///
/// Based on functions from _Calendrical Calculations_ by Reingold & Dershowitz.
/// Lisp reference code: https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L5396-L5405
pub(crate) fn new_year_on_or_before_fixed_date<C: ChineseBased>(
date: RataDie,
prior_solstice: RataDie,
) -> (RataDie, RataDie) {
let new_year = new_year_in_sui::<C>(prior_solstice);
if date >= new_year.0 {
new_year
} else {
// This happens when we're at the end of the current lunar year
// and the solstice has already happened. Thus the relevant solstice
// for the current lunar year is the previous one, which we calculate by offsetting
// back by a year.
let date_in_last_sui = date - 180; // This date is in the current lunar year, but the last solar year
let prior_solstice = winter_solstice_on_or_before::<C>(date_in_last_sui);
new_year_in_sui::<C>(prior_solstice)
}
}
/// Get a RataDie in the middle of a year; this is not necessarily meant for direct use in
/// calculations; rather, it is useful for getting a RataDie guaranteed to be in a given year
/// as input for other calculations like calculating the leap month in a year.
///
/// Based on functions from _Calendrical Calculations_ by Reingold & Dershowitz
/// Lisp reference code: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L5469-L5475>
pub fn fixed_mid_year_from_year<C: ChineseBased>(elapsed_years: i32) -> RataDie {
let cycle = (elapsed_years - 1).div_euclid(60) + 1;
let year = (elapsed_years - 1).rem_euclid(60) + 1;
C::EPOCH + ((((cycle - 1) * 60 + year - 1) as f64 + 0.5) * MEAN_TROPICAL_YEAR) as i64
}
/// Whether this year is a leap year
pub fn is_leap_year<C: ChineseBased>(year: i32) -> bool {
let mid_year = fixed_mid_year_from_year::<C>(year);
YearBounds::compute::<C>(mid_year).is_leap()
}
/// The last month and day in this year
pub fn last_month_day_in_year<C: ChineseBased>(year: i32) -> (u8, u8) {
let mid_year = fixed_mid_year_from_year::<C>(year);
let year_bounds = YearBounds::compute::<C>(mid_year);
let last_day = year_bounds.next_new_year - 1;
let month = if year_bounds.is_leap() { 13 } else { 12 };
let day = last_day - new_moon_before::<C>(last_day.as_moment()) + 1;
(month, day as u8)
}
/// Calculated the numbers of days in the given year
pub fn days_in_provided_year<C: ChineseBased>(year: i32) -> u16 {
let mid_year = fixed_mid_year_from_year::<C>(year);
let bounds = YearBounds::compute::<C>(mid_year);
bounds.count_days()
}
/// chinese_based_date_from_fixed returns extra things for use in caching
#[derive(Debug)]
#[non_exhaustive]
pub struct ChineseFromFixedResult {
/// The chinese year
pub year: i32,
/// The chinese month
pub month: u8,
/// The chinese day
pub day: u8,
/// The bounds of the current lunar year
pub year_bounds: YearBounds,
/// The index of the leap month, if any
pub leap_month: Option<NonZeroU8>,
}
/// Get a chinese based date from a fixed date, with the related ISO year
///
/// Months are calculated by iterating through the dates of new moons until finding the last month which
/// does not exceed the given fixed date. The day of month is calculated by subtracting the fixed date
/// from the fixed date of the beginning of the month.
///
/// The calculation for `elapsed_years` and `month` in this function are based on code from _Calendrical Calculations_ by Reingold & Dershowitz.
/// Lisp reference code: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L5414-L5459>
pub fn chinese_based_date_from_fixed<C: ChineseBased>(date: RataDie) -> ChineseFromFixedResult {
let year_bounds = YearBounds::compute::<C>(date);
let first_day_of_year = year_bounds.new_year;
let year_float =
(1.5 - 1.0 / 12.0 + ((first_day_of_year - C::EPOCH) as f64) / MEAN_TROPICAL_YEAR).floor();
let year_int = i64_to_i32(year_float as i64);
debug_assert!(year_int.is_ok(), "Year should be in range of i32");
let year = year_int.unwrap_or_else(|e| e.saturate());
let new_moon = new_moon_before::<C>((date + 1).as_moment());
let month_i64 = ((new_moon - first_day_of_year) as f64 / MEAN_SYNODIC_MONTH).round() as i64 + 1;
debug_assert!(
((u8::MIN as i64)..=(u8::MAX as i64)).contains(&month_i64),
"Month should be in range of u8! Value {month_i64} failed for RD {date:?}"
);
let month = month_i64 as u8;
let day_i64 = date - new_moon + 1;
debug_assert!(
((u8::MIN as i64)..=(u8::MAX as i64)).contains(&month_i64),
"Day should be in range of u8! Value {month_i64} failed for RD {date:?}"
);
let day = day_i64 as u8;
let leap_month = if year_bounds.is_leap() {
// This doesn't need to be checked for `None`, since `get_leap_month_from_new_year`
// will always return a number greater than or equal to 1, and less than 14.
NonZeroU8::new(get_leap_month_from_new_year::<C>(first_day_of_year))
} else {
None
};
ChineseFromFixedResult {
year,
month,
day,
year_bounds,
leap_month,
}
}
/// Given that `new_year` is the first day of a leap year, find which month in the year is a leap month.
/// Since the first month in which there are no major solar terms is a leap month, this function
/// cycles through months until it finds the leap month, then returns the number of that month. This
/// function assumes the date passed in is in a leap year and tests to ensure this is the case in debug
/// mode by asserting that no more than thirteen months are analyzed.
///
/// Conceptually similar to code from _Calendrical Calculations_ by Reingold & Dershowitz
/// Lisp reference code: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L5443-L5450>
pub fn get_leap_month_from_new_year<C: ChineseBased>(new_year: RataDie) -> u8 {
let mut cur = new_year;
let mut result = 1;
while result < MAX_ITERS_FOR_MONTHS_OF_YEAR && !no_major_solar_term::<C>(cur) {
cur = new_moon_on_or_after::<C>((cur + 1).as_moment());
result += 1;
}
debug_assert!(result < MAX_ITERS_FOR_MONTHS_OF_YEAR, "The given year was not a leap year and an unexpected number of iterations occurred searching for a leap month.");
result
}
/// Returns the number of days in the given (year, month). In the Chinese calendar, months start at each
/// new moon, so this function finds the number of days between the new moon at the beginning of the given
/// month and the new moon at the beginning of the next month.
pub fn month_days<C: ChineseBased>(year: i32, month: u8) -> u8 {
let mid_year = fixed_mid_year_from_year::<C>(year);
let prev_solstice = winter_solstice_on_or_before::<C>(mid_year);
let new_year = new_year_on_or_before_fixed_date::<C>(mid_year, prev_solstice).0;
days_in_month::<C>(month, new_year, None).0
}
/// Returns the number of days in the given `month` after the given `new_year`.
/// Also returns the RataDie of the new moon beginning the next month.
pub fn days_in_month<C: ChineseBased>(
month: u8,
new_year: RataDie,
prev_new_moon: Option<RataDie>,
) -> (u8, RataDie) {
let approx = new_year + ((month - 1) as i64 * 29);
let prev_new_moon = if let Some(prev_moon) = prev_new_moon {
prev_moon
} else {
new_moon_before::<C>((approx + 15).as_moment())
};
let next_new_moon = new_moon_on_or_after::<C>((approx + 15).as_moment());
let result = (next_new_moon - prev_new_moon) as u8;
debug_assert!(result == 29 || result == 30);
(result, next_new_moon)
}
/// Given the new year and a month/day pair, calculate the number of days until the first day of the given month
pub fn days_until_month<C: ChineseBased>(new_year: RataDie, month: u8) -> u16 {
let month_approx = 28_u16.saturating_mul(u16::from(month) - 1);
let new_moon = new_moon_on_or_after::<C>(new_year.as_moment() + (month_approx as f64));
let result = new_moon - new_year;
debug_assert!(((u16::MIN as i64)..=(u16::MAX as i64)).contains(&result), "Result {result} from new moon: {new_moon:?} and new year: {new_year:?} should be in range of u16!");
result as u16
}
#[cfg(test)]
mod test {
use super::*;
use crate::rata_die::Moment;
#[test]
fn test_chinese_new_moon_directionality() {
for i in (-1000..1000).step_by(31) {
let moment = Moment::new(i as f64);
let before = new_moon_before::<Chinese>(moment);
let after = new_moon_on_or_after::<Chinese>(moment);
assert!(before < after, "Chinese new moon directionality failed for Moment: {moment:?}, with:\n\tBefore: {before:?}\n\tAfter: {after:?}");
}
}
#[test]
fn test_chinese_new_year_on_or_before() {
let fixed = crate::iso::fixed_from_iso(2023, 6, 22);
let prev_solstice = winter_solstice_on_or_before::<Chinese>(fixed);
let result_fixed = new_year_on_or_before_fixed_date::<Chinese>(fixed, prev_solstice).0;
let (y, m, d) = crate::iso::iso_from_fixed(result_fixed).unwrap();
assert_eq!(y, 2023);
assert_eq!(m, 1);
assert_eq!(d, 22);
}
fn seollal_on_or_before(fixed: RataDie) -> RataDie {
let prev_solstice = winter_solstice_on_or_before::<Dangi>(fixed);
new_year_on_or_before_fixed_date::<Dangi>(fixed, prev_solstice).0
}
#[test]
fn test_seollal() {
#[derive(Debug)]
struct TestCase {
iso_year: i32,
iso_month: u8,
iso_day: u8,
expected_year: i32,
expected_month: u8,
expected_day: u8,
}
let cases = [
TestCase {
iso_year: 2024,
iso_month: 6,
iso_day: 6,
expected_year: 2024,
expected_month: 2,
expected_day: 10,
},
TestCase {
iso_year: 2024,
iso_month: 2,
iso_day: 9,
expected_year: 2023,
expected_month: 1,
expected_day: 22,
},
TestCase {
iso_year: 2023,
iso_month: 1,
iso_day: 22,
expected_year: 2023,
expected_month: 1,
expected_day: 22,
},
TestCase {
iso_year: 2023,
iso_month: 1,
iso_day: 21,
expected_year: 2022,
expected_month: 2,
expected_day: 1,
},
TestCase {
iso_year: 2022,
iso_month: 6,
iso_day: 6,
expected_year: 2022,
expected_month: 2,
expected_day: 1,
},
TestCase {
iso_year: 2021,
iso_month: 6,
iso_day: 6,
expected_year: 2021,
expected_month: 2,
expected_day: 12,
},
TestCase {
iso_year: 2020,
iso_month: 6,
iso_day: 6,
expected_year: 2020,
expected_month: 1,
expected_day: 25,
},
TestCase {
iso_year: 2019,
iso_month: 6,
iso_day: 6,
expected_year: 2019,
expected_month: 2,
expected_day: 5,
},
TestCase {
iso_year: 2018,
iso_month: 6,
iso_day: 6,
expected_year: 2018,
expected_month: 2,
expected_day: 16,
},
TestCase {
iso_year: 2025,
iso_month: 6,
iso_day: 6,
expected_year: 2025,
expected_month: 1,
expected_day: 29,
},
TestCase {
iso_year: 2026,
iso_month: 8,
iso_day: 8,
expected_year: 2026,
expected_month: 2,
expected_day: 17,
},
TestCase {
iso_year: 2027,
iso_month: 4,
iso_day: 4,
expected_year: 2027,
expected_month: 2,
expected_day: 7,
},
TestCase {
iso_year: 2028,
iso_month: 9,
iso_day: 21,
expected_year: 2028,
expected_month: 1,
expected_day: 27,
},
];
for case in cases {
let fixed = crate::iso::fixed_from_iso(case.iso_year, case.iso_month, case.iso_day);
let seollal = seollal_on_or_before(fixed);
let (y, m, d) = crate::iso::iso_from_fixed(seollal).unwrap();
assert_eq!(
y, case.expected_year,
"Year check failed for case: {case:?}"
);
assert_eq!(
m, case.expected_month,
"Month check failed for case: {case:?}"
);
assert_eq!(d, case.expected_day, "Day check failed for case: {case:?}");
}
}
}

32
third_party/rust/calendrical_calculations/src/coptic.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,32 @@
// This file is part of ICU4X.
//
// The contents of this file implement algorithms from Calendrical Calculations
// by Reingold & Dershowitz, Cambridge University Press, 4th edition (2018),
// which have been released as Lisp code at <https://github.com/EdReingold/calendar-code2/>
// under the Apache-2.0 license. Accordingly, this file is released under
// the Apache License, Version 2.0 which can be found at the calendrical_calculations
// package root or at http://www.apache.org/licenses/LICENSE-2.0.
use crate::helpers::{i64_to_i32, I32CastError};
use crate::rata_die::RataDie;
pub(crate) const COPTIC_EPOCH: RataDie = crate::julian::fixed_from_julian(284, 8, 29);
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L1978>
pub fn fixed_from_coptic(year: i32, month: u8, day: u8) -> RataDie {
COPTIC_EPOCH - 1
+ 365 * (year as i64 - 1)
+ year.div_euclid(4) as i64
+ 30 * (month as i64 - 1)
+ day as i64
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L1990>
pub fn coptic_from_fixed(date: RataDie) -> Result<(i32, u8, u8), I32CastError> {
let year = (4 * (date - COPTIC_EPOCH) + 1463).div_euclid(1461);
let year = i64_to_i32(year)?;
let month = ((date - fixed_from_coptic(year, 1, 1)).div_euclid(30) + 1) as u8; // <= 12 < u8::MAX
let day = (date + 1 - fixed_from_coptic(year, month, 1)) as u8; // <= days_in_month < u8::MAX
Ok((year, month, day))
}

27
third_party/rust/calendrical_calculations/src/error.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,27 @@
// This file is part of ICU4X.
//
// The contents of this file implement algorithms from Calendrical Calculations
// by Reingold & Dershowitz, Cambridge University Press, 4th edition (2018),
// which have been released as Lisp code at <https://github.com/EdReingold/calendar-code2/>
// under the Apache-2.0 license. Accordingly, this file is released under
// the Apache License, Version 2.0 which can be found at the calendrical_calculations
// package root or at http://www.apache.org/licenses/LICENSE-2.0.
use displaydoc::Display;
/// A list of error outcomes for exceeding location bounds
#[derive(Display, Debug, Copy, Clone, PartialEq)]
#[non_exhaustive]
pub enum LocationOutOfBoundsError {
/// Latitude value was out of bounds
#[displaydoc("Latitude {0} outside bounds of -90 to 90")]
Latitude(f64),
/// Longitude value was out of bounds
#[displaydoc("Longitude {0} outside bounds of -180 to 180")]
Longitude(f64),
/// Offset value was out of bounds
#[displaydoc("Offset {0} outside bounds of {1} to {2}")]
Offset(f64, f64, f64),
}

24
third_party/rust/calendrical_calculations/src/ethiopian.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,24 @@
// This file is part of ICU4X.
//
// The contents of this file implement algorithms from Calendrical Calculations
// by Reingold & Dershowitz, Cambridge University Press, 4th edition (2018),
// which have been released as Lisp code at <https://github.com/EdReingold/calendar-code2/>
// under the Apache-2.0 license. Accordingly, this file is released under
// the Apache License, Version 2.0 which can be found at the calendrical_calculations
// package root or at http://www.apache.org/licenses/LICENSE-2.0.
use crate::helpers::I32CastError;
use crate::rata_die::RataDie;
const ETHIOPIC_TO_COPTIC_OFFSET: i64 =
super::coptic::COPTIC_EPOCH.const_diff(crate::julian::fixed_from_julian(8, 8, 29));
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L2017>
pub fn fixed_from_ethiopian(year: i32, month: u8, day: u8) -> RataDie {
crate::coptic::fixed_from_coptic(year, month, day) - ETHIOPIC_TO_COPTIC_OFFSET
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L2028>
pub fn ethiopian_from_fixed(date: RataDie) -> Result<(i32, u8, u8), I32CastError> {
crate::coptic::coptic_from_fixed(date + ETHIOPIC_TO_COPTIC_OFFSET)
}

707
third_party/rust/calendrical_calculations/src/hebrew.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,707 @@
// This file is part of ICU4X.
//
// The contents of this file implement algorithms from Calendrical Calculations
// by Reingold & Dershowitz, Cambridge University Press, 4th edition (2018),
// which have been released as Lisp code at <https://github.com/EdReingold/calendar-code2/>
// under the Apache-2.0 license. Accordingly, this file is released under
// the Apache License, Version 2.0 which can be found at the calendrical_calculations
// package root or at http://www.apache.org/licenses/LICENSE-2.0.
use crate::helpers::{final_func, i64_to_i32, next_u8};
use crate::rata_die::{Moment, RataDie};
#[allow(unused_imports)]
use core_maths::*;
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2206>
const FIXED_HEBREW_EPOCH: RataDie = crate::julian::fixed_from_julian_book_version(-3761, 10, 7);
/// Biblical Hebrew dates. The months are reckoned a bit strangely, with the new year occurring on
/// Tishri (as in the civil calendar) but the months being numbered in a different order
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq, PartialOrd, Ord)]
#[allow(clippy::exhaustive_structs)]
pub struct BookHebrew {
/// The year
pub year: i32,
/// The month
pub month: u8,
/// The day
pub day: u8,
}
// The BookHebrew Months
/// The biblical month number used for the month of Nisan
pub const NISAN: u8 = 1;
/// The biblical month number used for the month of Iyyar
pub const IYYAR: u8 = 2;
/// The biblical month number used for the month of Sivan
pub const SIVAN: u8 = 3;
/// The biblical month number used for the month of Tammuz
pub const TAMMUZ: u8 = 4;
/// The biblical month number used for the month of Av
pub const AV: u8 = 5;
/// The biblical month number used for the month of Elul
pub const ELUL: u8 = 6;
/// The biblical month number used for the month of Tishri
pub const TISHRI: u8 = 7;
/// The biblical month number used for the month of Marheshvan
pub const MARHESHVAN: u8 = 8;
/// The biblical month number used for the month of Kislev
pub const KISLEV: u8 = 9;
/// The biblical month number used for the month of Tevet
pub const TEVET: u8 = 10;
/// The biblical month number used for the month of Shevat
pub const SHEVAT: u8 = 11;
/// The biblical month number used for the month of Adar (and Adar I)
pub const ADAR: u8 = 12;
/// The biblical month number used for the month of Adar II
pub const ADARII: u8 = 13;
// BIBLICAL HEBREW CALENDAR FUNCTIONS
impl BookHebrew {
/// The civil calendar has the same year and day numbering as the book one, but the months are numbered
/// differently
pub fn to_civil_date(self) -> (i32, u8, u8) {
let biblical_month = self.month;
let biblical_year = self.year;
let mut civil_month;
civil_month = (biblical_month + 6) % 12;
if civil_month == 0 {
civil_month = 12;
}
if Self::is_hebrew_leap_year(biblical_year) && biblical_month < TISHRI {
civil_month += 1;
}
(biblical_year, civil_month, self.day)
}
/// The civil calendar has the same year and day numbering as the book one, but the months are numbered
/// differently
pub fn from_civil_date(civil_year: i32, civil_month: u8, civil_day: u8) -> Self {
let mut biblical_month;
if civil_month <= 6 {
biblical_month = civil_month + 6; // months 1-6 correspond to biblical months 7-12
} else {
biblical_month = civil_month - 6; // months 7-12 correspond to biblical months 1-6
if Self::is_hebrew_leap_year(civil_year) {
biblical_month -= 1
}
if biblical_month == 0 {
// Special case for Adar II in a leap year
biblical_month = 13;
}
}
BookHebrew {
year: civil_year,
month: biblical_month,
day: civil_day,
}
}
// Moment of mean conjunction (New Moon) of h_month in BookHebrew
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2244>
#[allow(dead_code)]
pub(crate) fn molad(book_year: i32, book_month: u8) -> Moment {
let y = if book_month < TISHRI {
book_year + 1
} else {
book_year
}; // Treat Nisan as start of year
let months_elapsed = (book_month as f64 - TISHRI as f64) // Months this year
+ ((235.0 * y as f64 - 234.0) / 19.0).floor(); // Months until New Year.
Moment::new(
FIXED_HEBREW_EPOCH.to_f64_date() - (876.0 / 25920.0)
+ months_elapsed * (29.0 + (1.0 / 2.0) + (793.0 / 25920.0)),
)
}
// ADAR = 12, ADARII = 13
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2217>
#[allow(dead_code)]
fn last_month_of_book_hebrew_year(book_year: i32) -> u8 {
if Self::is_hebrew_leap_year(book_year) {
ADARII
} else {
ADAR
}
}
// Number of days elapsed from the (Sunday) noon prior to the epoch of the BookHebrew Calendar to the molad of Tishri of BookHebrew year (h_year) or one day later
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2261>
fn book_hebrew_calendar_elapsed_days(book_year: i32) -> i32 {
let months_elapsed = ((235.0 * book_year as f64 - 234.0) / 19.0).floor() as i64;
let parts_elapsed = 12084 + 13753 * months_elapsed;
let days = 29 * months_elapsed + (parts_elapsed as f64 / 25920.0).floor() as i64;
if (3 * (days + 1)).rem_euclid(7) < 3 {
days as i32 + 1
} else {
days as i32
}
}
// Delays to start of BookHebrew year to keep ordinary year in range 353-356 and leap year in range 383-386
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2301>
fn book_hebrew_year_length_correction(book_year: i32) -> u8 {
let ny0 = Self::book_hebrew_calendar_elapsed_days(book_year - 1);
let ny1 = Self::book_hebrew_calendar_elapsed_days(book_year);
let ny2 = Self::book_hebrew_calendar_elapsed_days(book_year + 1);
if (ny2 - ny1) == 356 {
2
} else if (ny1 - ny0) == 382 {
1
} else {
0
}
}
// Fixed date of BookHebrew new year
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2294>
pub fn book_hebrew_new_year(book_year: i32) -> RataDie {
RataDie::new(
FIXED_HEBREW_EPOCH.to_i64_date()
+ Self::book_hebrew_calendar_elapsed_days(book_year) as i64
+ Self::book_hebrew_year_length_correction(book_year) as i64,
)
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2315>
pub fn days_in_book_hebrew_year(book_year: i32) -> u16 {
(Self::book_hebrew_new_year(1 + book_year) - Self::book_hebrew_new_year(book_year)) as u16
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L2275-L2278>
pub fn is_hebrew_leap_year(book_year: i32) -> bool {
(7 * book_year + 1).rem_euclid(19) < 7
}
// True if the month Marheshvan is going to be long in given BookHebrew year
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2321>
#[allow(dead_code)]
fn is_long_marheshvan(book_year: i32) -> bool {
let long_marheshavan_year_lengths = [355, 385];
long_marheshavan_year_lengths.contains(&Self::days_in_book_hebrew_year(book_year))
}
// True if the month Kislve is going to be short in given BookHebrew year
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2326>
#[allow(dead_code)]
fn is_short_kislev(book_year: i32) -> bool {
let short_kislev_year_lengths = [353, 383];
short_kislev_year_lengths.contains(&Self::days_in_book_hebrew_year(book_year))
}
// Last day of month (h_month) in BookHebrew year (book_year)
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2230>
pub fn last_day_of_book_hebrew_month(book_year: i32, book_month: u8) -> u8 {
match book_month {
IYYAR | TAMMUZ | ELUL | TEVET | ADARII => 29,
ADAR => {
if !Self::is_hebrew_leap_year(book_year) {
29
} else {
30
}
}
MARHESHVAN => {
if !Self::is_long_marheshvan(book_year) {
29
} else {
30
}
}
KISLEV => {
if Self::is_short_kislev(book_year) {
29
} else {
30
}
}
_ => 30,
}
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2331>
pub fn fixed_from_book_hebrew(date: BookHebrew) -> RataDie {
let book_year = date.year;
let book_month = date.month;
let book_day = date.day;
let mut total_days = Self::book_hebrew_new_year(book_year) + book_day.into() - 1; // (day - 1) Days so far this month.
if book_month < TISHRI {
// Then add days in prior months this year before
for m in
(TISHRI..=Self::last_month_of_book_hebrew_year(book_year)).chain(NISAN..book_month)
{
total_days += Self::last_day_of_book_hebrew_month(book_year, m).into();
}
} else {
// Else add days in prior months this year
for m in TISHRI..book_month {
total_days += Self::last_day_of_book_hebrew_month(book_year, m).into();
}
}
total_days
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2352>
pub fn book_hebrew_from_fixed(date: RataDie) -> BookHebrew {
let approx = i64_to_i32(
1 + ((date - FIXED_HEBREW_EPOCH) as f64).div_euclid(35975351.0 / 98496.0) as i64, // The value 35975351/98496, the average length of a BookHebrew year, can be approximated by 365.25
)
.unwrap_or_else(|e| e.saturate());
// Search forward for the year
let year_condition = |year: i32| Self::book_hebrew_new_year(year) <= date;
let year = final_func(approx - 1, year_condition);
// Starting month for search for month.
let start = if date
< Self::fixed_from_book_hebrew(BookHebrew {
year,
month: NISAN,
day: 1,
}) {
TISHRI
} else {
NISAN
};
let month_condition = |m: u8| {
date <= Self::fixed_from_book_hebrew(BookHebrew {
year,
month: m,
day: Self::last_day_of_book_hebrew_month(year, m),
})
};
// Search forward from either Tishri or Nisan.
let month = next_u8(start, month_condition);
// Calculate the day by subtraction.
let day = (date
- Self::fixed_from_book_hebrew(BookHebrew {
year,
month,
day: 1,
}))
+ 1;
BookHebrew {
year,
month,
day: day as u8,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug)]
struct DateCase {
year: i32,
month: u8,
day: u8,
}
static TEST_FIXED_DATE: [i64; 33] = [
-214193, -61387, 25469, 49217, 171307, 210155, 253427, 369740, 400085, 434355, 452605,
470160, 473837, 507850, 524156, 544676, 567118, 569477, 601716, 613424, 626596, 645554,
664224, 671401, 694799, 704424, 708842, 709409, 709580, 727274, 728714, 744313, 764652,
];
static HEBREW_DATES: [DateCase; 33] = [
DateCase {
year: 3174,
month: 5,
day: 10,
},
DateCase {
year: 3593,
month: 9,
day: 25,
},
DateCase {
year: 3831,
month: 7,
day: 3,
},
DateCase {
year: 3896,
month: 7,
day: 9,
},
DateCase {
year: 4230,
month: 10,
day: 18,
},
DateCase {
year: 4336,
month: 3,
day: 4,
},
DateCase {
year: 4455,
month: 8,
day: 13,
},
DateCase {
year: 4773,
month: 2,
day: 6,
},
DateCase {
year: 4856,
month: 2,
day: 23,
},
DateCase {
year: 4950,
month: 1,
day: 7,
},
DateCase {
year: 5000,
month: 13,
day: 8,
},
DateCase {
year: 5048,
month: 1,
day: 21,
},
DateCase {
year: 5058,
month: 2,
day: 7,
},
DateCase {
year: 5151,
month: 4,
day: 1,
},
DateCase {
year: 5196,
month: 11,
day: 7,
},
DateCase {
year: 5252,
month: 1,
day: 3,
},
DateCase {
year: 5314,
month: 7,
day: 1,
},
DateCase {
year: 5320,
month: 12,
day: 27,
},
DateCase {
year: 5408,
month: 3,
day: 20,
},
DateCase {
year: 5440,
month: 4,
day: 3,
},
DateCase {
year: 5476,
month: 5,
day: 5,
},
DateCase {
year: 5528,
month: 4,
day: 4,
},
DateCase {
year: 5579,
month: 5,
day: 11,
},
DateCase {
year: 5599,
month: 1,
day: 12,
},
DateCase {
year: 5663,
month: 1,
day: 22,
},
DateCase {
year: 5689,
month: 5,
day: 19,
},
DateCase {
year: 5702,
month: 7,
day: 8,
},
DateCase {
year: 5703,
month: 1,
day: 14,
},
DateCase {
year: 5704,
month: 7,
day: 8,
},
DateCase {
year: 5752,
month: 13,
day: 12,
},
DateCase {
year: 5756,
month: 12,
day: 5,
},
DateCase {
year: 5799,
month: 8,
day: 12,
},
DateCase {
year: 5854,
month: 5,
day: 5,
},
];
static EXPECTED_MOLAD_DATES: [f64; 33] = [
-1850718767f64 / 8640f64,
-1591805959f64 / 25920f64,
660097927f64 / 25920f64,
1275506059f64 / 25920f64,
4439806081f64 / 25920f64,
605235101f64 / 2880f64,
3284237627f64 / 12960f64,
9583515841f64 / 25920f64,
2592403883f64 / 6480f64,
2251656649f64 / 5184f64,
11731320839f64 / 25920f64,
12185988041f64 / 25920f64,
6140833583f64 / 12960f64,
6581722991f64 / 12960f64,
6792982499f64 / 12960f64,
4705980311f64 / 8640f64,
14699670013f64 / 25920f64,
738006961f64 / 1296f64,
1949499007f64 / 3240f64,
5299956319f64 / 8640f64,
3248250415f64 / 5184f64,
16732660061f64 / 25920f64,
17216413717f64 / 25920f64,
1087650871f64 / 1620f64,
2251079609f64 / 3240f64,
608605601f64 / 864f64,
306216383f64 / 432f64,
18387526207f64 / 25920f64,
3678423761f64 / 5184f64,
1570884431f64 / 2160f64,
18888119389f64 / 25920f64,
19292268013f64 / 25920f64,
660655045f64 / 864f64,
];
static EXPECTED_LAST_HEBREW_MONTH: [u8; 33] = [
12, 12, 12, 12, 12, 12, 12, 12, 13, 12, 13, 12, 12, 12, 12, 13, 12, 13, 12, 13, 12, 12, 12,
12, 12, 13, 12, 13, 12, 13, 12, 12, 12,
];
static EXPECTED_HEBREW_ELASPED_CALENDAR_DAYS: [i32; 33] = [
1158928, 1311957, 1398894, 1422636, 1544627, 1583342, 1626812, 1742956, 1773254, 1807597,
1825848, 1843388, 1847051, 1881010, 1897460, 1917895, 1940545, 1942729, 1974889, 1986554,
1999723, 2018712, 2037346, 2044640, 2068027, 2077507, 2082262, 2082617, 2083000, 2100511,
2101988, 2117699, 2137779,
];
static EXPECTED_FIXED_HEBREW_NEW_YEAR: [i64; 33] = [
-214497, -61470, 25467, 49209, 171200, 209915, 253385, 369529, 399827, 434172, 452421,
469963, 473624, 507583, 524033, 544468, 567118, 569302, 601462, 613127, 626296, 645285,
663919, 671213, 694600, 704080, 708835, 709190, 709573, 727084, 728561, 744272, 764352,
];
static EXPECTED_DAYS_IN_HEBREW_YEAR: [u16; 33] = [
354, 354, 355, 355, 355, 355, 355, 353, 383, 354, 383, 354, 354, 355, 353, 383, 353, 385,
353, 383, 355, 354, 354, 354, 355, 385, 355, 383, 354, 385, 355, 354, 355,
];
static EXPECTED_MARHESHVAN_VALUES: [bool; 33] = [
false, false, true, true, true, true, true, false, false, false, false, false, false, true,
false, false, false, true, false, false, true, false, false, false, true, true, true,
false, false, true, true, false, true,
];
static EXPECTED_KISLEV_VALUES: [bool; 33] = [
false, false, false, false, false, false, false, true, true, false, true, false, false,
false, true, true, true, false, true, true, false, false, false, false, false, false,
false, true, false, false, false, false, false,
];
static EXPECTED_DAY_IN_MONTH: [u8; 33] = [
30, 30, 30, 30, 29, 30, 30, 29, 29, 30, 29, 30, 29, 29, 30, 30, 30, 30, 30, 29, 30, 29, 30,
30, 30, 30, 30, 30, 30, 29, 29, 29, 30,
];
#[allow(dead_code)]
static CIVIL_EXPECTED_DAY_IN_MONTH: [u8; 33] = [
30, 30, 30, 30, 29, 30, 29, 29, 29, 30, 30, 30, 29, 29, 30, 29, 30, 29, 29, 30, 30, 29, 30,
30, 30, 30, 30, 29, 30, 30, 29, 29, 30,
];
#[test]
fn test_hebrew_epoch() {
// page 119 of the Calendrical Calculations book
let fixed_hebrew_date = -1373427.0;
assert_eq!(FIXED_HEBREW_EPOCH.to_f64_date(), fixed_hebrew_date);
}
#[test]
fn test_hebrew_molad() {
let precision = 1_00000f64;
for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_MOLAD_DATES.iter()) {
let molad =
(BookHebrew::molad(case.year, case.month).inner() * precision).round() / precision;
let final_expected = (expected * precision).round() / precision;
assert_eq!(molad, final_expected, "{case:?}");
}
}
#[test]
fn test_last_book_hebrew_month() {
for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_LAST_HEBREW_MONTH.iter()) {
let last_month = BookHebrew::last_month_of_book_hebrew_year(case.year);
assert_eq!(last_month, *expected);
}
}
#[test]
fn test_book_hebrew_calendar_elapsed_days() {
for (case, expected) in HEBREW_DATES
.iter()
.zip(EXPECTED_HEBREW_ELASPED_CALENDAR_DAYS.iter())
{
let elapsed_days = BookHebrew::book_hebrew_calendar_elapsed_days(case.year);
assert_eq!(elapsed_days, *expected);
}
}
#[test]
fn test_book_hebrew_year_length_correction() {
let year_length_correction: [u8; 33] = [
2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0,
];
for (case, expected) in HEBREW_DATES.iter().zip(year_length_correction.iter()) {
let correction = BookHebrew::book_hebrew_year_length_correction(case.year);
assert_eq!(correction, *expected);
}
}
#[test]
fn test_book_hebrew_new_year() {
for (case, expected) in HEBREW_DATES
.iter()
.zip(EXPECTED_FIXED_HEBREW_NEW_YEAR.iter())
{
let f_date = BookHebrew::book_hebrew_new_year(case.year);
assert_eq!(f_date.to_i64_date(), *expected);
}
}
#[test]
fn test_days_in_book_hebrew_year() {
for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_DAYS_IN_HEBREW_YEAR.iter()) {
let days_in_year = BookHebrew::days_in_book_hebrew_year(case.year);
assert_eq!(days_in_year, *expected);
}
}
#[test]
fn test_long_marheshvan() {
for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_MARHESHVAN_VALUES.iter()) {
let marsheshvan = BookHebrew::is_long_marheshvan(case.year);
assert_eq!(marsheshvan, *expected);
}
}
#[test]
fn test_short_kislev() {
for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_KISLEV_VALUES.iter()) {
let kislev = BookHebrew::is_short_kislev(case.year);
assert_eq!(kislev, *expected);
}
}
#[test]
fn test_last_day_in_book_hebrew_month() {
for (case, expected) in HEBREW_DATES.iter().zip(EXPECTED_DAY_IN_MONTH.iter()) {
let days_in_month = BookHebrew::last_day_of_book_hebrew_month(case.year, case.month);
assert_eq!(days_in_month, *expected);
}
}
#[test]
fn test_fixed_from_book_hebrew() {
for (case, f_date) in HEBREW_DATES.iter().zip(TEST_FIXED_DATE.iter()) {
assert_eq!(
BookHebrew::fixed_from_book_hebrew(BookHebrew {
year: case.year,
month: case.month,
day: case.day
}),
RataDie::new(*f_date),
"{case:?}"
);
}
}
#[test]
fn test_book_hebrew_from_fixed() {
for (case, f_date) in HEBREW_DATES.iter().zip(TEST_FIXED_DATE.iter()) {
assert_eq!(
BookHebrew::book_hebrew_from_fixed(RataDie::new(*f_date)),
BookHebrew {
year: case.year,
month: case.month,
day: case.day
},
"{case:?}"
);
}
}
#[test]
fn test_civil_to_book_conversion() {
for (f_date, case) in TEST_FIXED_DATE.iter().zip(HEBREW_DATES.iter()) {
let book_hebrew = BookHebrew::book_hebrew_from_fixed(RataDie::new(*f_date));
let (y, m, d) = book_hebrew.to_civil_date();
let book_hebrew = BookHebrew::from_civil_date(y, m, d);
assert_eq!(
(case.year, case.month),
(book_hebrew.year, book_hebrew.month)
)
}
}
}

336
third_party/rust/calendrical_calculations/src/helpers.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,336 @@
// This file is part of ICU4X.
//
// The contents of this file implement algorithms from Calendrical Calculations
// by Reingold & Dershowitz, Cambridge University Press, 4th edition (2018),
// which have been released as Lisp code at <https://github.com/EdReingold/calendar-code2/>
// under the Apache-2.0 license. Accordingly, this file is released under
// the Apache License, Version 2.0 which can be found at the calendrical_calculations
// package root or at http://www.apache.org/licenses/LICENSE-2.0.
use crate::astronomy::Location;
use crate::rata_die::{Moment, RataDie};
#[allow(unused_imports)]
use core_maths::*;
pub(crate) trait IntegerRoundings {
fn div_ceil(self, rhs: Self) -> Self;
}
impl IntegerRoundings for i64 {
// copied from std
fn div_ceil(self, rhs: Self) -> Self {
let d = self / rhs;
let r = self % rhs;
if (r > 0 && rhs > 0) || (r < 0 && rhs < 0) {
d + 1
} else {
d
}
}
}
#[test]
fn test_div_ceil() {
assert_eq!(IntegerRoundings::div_ceil(i64::MIN, 1), i64::MIN);
assert_eq!(
IntegerRoundings::div_ceil(i64::MIN, 2),
-4611686018427387904
);
assert_eq!(
IntegerRoundings::div_ceil(i64::MIN, 3),
-3074457345618258602
);
assert_eq!(IntegerRoundings::div_ceil(-10, 1), -10);
assert_eq!(IntegerRoundings::div_ceil(-10, 2), -5);
assert_eq!(IntegerRoundings::div_ceil(-10, 3), -3);
assert_eq!(IntegerRoundings::div_ceil(-9, 1), -9);
assert_eq!(IntegerRoundings::div_ceil(-9, 2), -4);
assert_eq!(IntegerRoundings::div_ceil(-9, 3), -3);
assert_eq!(IntegerRoundings::div_ceil(-8, 1), -8);
assert_eq!(IntegerRoundings::div_ceil(-8, 2), -4);
assert_eq!(IntegerRoundings::div_ceil(-8, 3), -2);
assert_eq!(IntegerRoundings::div_ceil(-2, 1), -2);
assert_eq!(IntegerRoundings::div_ceil(-2, 2), -1);
assert_eq!(IntegerRoundings::div_ceil(-2, 3), 0);
assert_eq!(IntegerRoundings::div_ceil(-1, 1), -1);
assert_eq!(IntegerRoundings::div_ceil(-1, 2), 0);
assert_eq!(IntegerRoundings::div_ceil(-1, 3), 0);
assert_eq!(IntegerRoundings::div_ceil(0, 1), 0);
assert_eq!(IntegerRoundings::div_ceil(0, 2), 0);
assert_eq!(IntegerRoundings::div_ceil(0, 3), 0);
assert_eq!(IntegerRoundings::div_ceil(1, 1), 1);
assert_eq!(IntegerRoundings::div_ceil(1, 2), 1);
assert_eq!(IntegerRoundings::div_ceil(1, 3), 1);
assert_eq!(IntegerRoundings::div_ceil(2, 1), 2);
assert_eq!(IntegerRoundings::div_ceil(2, 2), 1);
assert_eq!(IntegerRoundings::div_ceil(2, 3), 1);
assert_eq!(IntegerRoundings::div_ceil(8, 1), 8);
assert_eq!(IntegerRoundings::div_ceil(8, 2), 4);
assert_eq!(IntegerRoundings::div_ceil(8, 3), 3);
assert_eq!(IntegerRoundings::div_ceil(9, 1), 9);
assert_eq!(IntegerRoundings::div_ceil(9, 2), 5);
assert_eq!(IntegerRoundings::div_ceil(9, 3), 3);
assert_eq!(IntegerRoundings::div_ceil(10, 1), 10);
assert_eq!(IntegerRoundings::div_ceil(10, 2), 5);
assert_eq!(IntegerRoundings::div_ceil(10, 3), 4);
assert_eq!(IntegerRoundings::div_ceil(i64::MAX, 1), 9223372036854775807);
assert_eq!(IntegerRoundings::div_ceil(i64::MAX, 2), 4611686018427387904);
assert_eq!(IntegerRoundings::div_ceil(i64::MAX, 3), 3074457345618258603);
for n in -100..100 {
for d in 1..5 {
let x1 = IntegerRoundings::div_ceil(n, d);
let x2 = (n as f64 / d as f64).ceil();
assert_eq!(x1, x2 as i64);
}
}
}
// Highest power is *last*
pub(crate) fn poly(x: f64, coeffs: &[f64]) -> f64 {
coeffs.iter().rev().fold(0.0, |a, c| a * x + c)
}
// A generic function that finds a value within an interval
// where a certain condition is satisfied.
pub(crate) fn binary_search(
mut l: f64,
mut h: f64,
test: impl Fn(f64) -> bool,
epsilon: f64,
) -> f64 {
debug_assert!(l < h);
loop {
let mid = l + (h - l) / 2.0;
// Determine which direction to go. `test` returns true if we need to go left.
(l, h) = if test(mid) { (l, mid) } else { (mid, h) };
// When the interval width reaches `epsilon`, return the current midpoint.
if (h - l) < epsilon {
return mid;
}
}
}
pub(crate) fn next_moment<F>(mut index: Moment, location: Location, condition: F) -> RataDie
where
F: Fn(Moment, Location) -> bool,
{
loop {
if condition(index, location) {
return index.as_rata_die();
}
index += 1.0;
}
}
pub(crate) fn next<F>(mut index: RataDie, condition: F) -> RataDie
where
F: Fn(RataDie) -> bool,
{
loop {
if condition(index) {
return index;
}
index += 1;
}
}
pub(crate) fn next_u8<F>(mut index: u8, condition: F) -> u8
where
F: Fn(u8) -> bool,
{
loop {
if condition(index) {
return index;
}
index += 1;
}
}
// "Final" is a reserved keyword in rust, which explains the naming convention here.
pub(crate) fn final_func<F>(mut index: i32, condition: F) -> i32
where
F: Fn(i32) -> bool,
{
while condition(index) {
index += 1;
}
index - 1
}
#[test]
fn test_binary_search() {
struct TestCase {
test_fn: fn(f64) -> bool,
range: (f64, f64),
expected: f64,
}
let test_cases = [
TestCase {
test_fn: |x: f64| x >= 4.0,
range: (0.0, 10.0),
expected: 4.0,
},
TestCase {
test_fn: |x: f64| x * x >= 2.0,
range: (0.0, 2.0),
expected: 2.0f64.sqrt(),
},
TestCase {
test_fn: |x: f64| x >= -4.0,
range: (-10.0, 0.0),
expected: -4.0,
},
TestCase {
test_fn: |x: f64| x >= 0.0,
range: (0.0, 10.0),
expected: 0.0,
},
TestCase {
test_fn: |x: f64| x > 10.0,
range: (0.0, 10.0),
expected: 10.0,
},
];
for case in test_cases {
let result = binary_search(case.range.0, case.range.1, case.test_fn, 1e-4);
assert!((result - case.expected).abs() < 0.0001);
}
}
pub(crate) fn invert_angular<F: Fn(f64) -> f64>(f: F, y: f64, r: (f64, f64)) -> f64 {
binary_search(r.0, r.1, |x| (f(x) - y).rem_euclid(360.0) < 180.0, 1e-5)
}
#[test]
fn test_invert_angular() {
struct TestCase {
f: fn(f64) -> f64,
y: f64,
r: (f64, f64),
expected: f64,
}
fn f1(x: f64) -> f64 {
(2.0 * x).rem_euclid(360.0)
}
fn f2(x: f64) -> f64 {
(3.0 * x).rem_euclid(360.0)
}
fn f3(x: f64) -> f64 {
(x).rem_euclid(360.0)
}
// tolerance for comparing floating points.
let tolerance = 1e-5;
let test_cases = [
TestCase {
f: f1,
y: 4.0,
r: (0.0, 10.0),
expected: 4.0,
},
TestCase {
f: f2,
y: 6.0,
r: (0.0, 20.0),
expected: 6.0,
},
TestCase {
f: f3,
y: 400.0,
r: (0.0, 10.0),
expected: 10.0,
},
TestCase {
f: f3,
y: 0.0,
r: (0.0, 10.0),
expected: 0.0,
},
TestCase {
f: f3,
y: 10.0,
r: (0.0, 10.0),
expected: 10.0,
},
];
for case in test_cases {
let x = invert_angular(case.f, case.y, case.r);
assert!((((case.f)(x)).rem_euclid(360.0) - case.expected).abs() < tolerance);
}
}
/// Error returned when casting from an i32
#[derive(Copy, Clone, Debug)]
#[allow(clippy::exhaustive_enums)] // enum is specific to function and has a closed set of possible values
pub enum I32CastError {
/// Less than i32::MIN
BelowMin,
/// Greater than i32::MAX
AboveMax,
}
impl I32CastError {
pub(crate) const fn saturate(self) -> i32 {
match self {
I32CastError::BelowMin => i32::MIN,
I32CastError::AboveMax => i32::MAX,
}
}
}
/// Convert an i64 to i32 and with information on which way it was out of bounds if so
#[inline]
pub const fn i64_to_i32(input: i64) -> Result<i32, I32CastError> {
if input < i32::MIN as i64 {
Err(I32CastError::BelowMin)
} else if input > i32::MAX as i64 {
Err(I32CastError::AboveMax)
} else {
Ok(input as i32)
}
}
/// Convert an i64 to i32 but saturate at th ebounds
#[inline]
pub fn i64_to_saturated_i32(input: i64) -> i32 {
i64_to_i32(input).unwrap_or_else(|i| i.saturate())
}
#[test]
fn test_i64_to_saturated_i32() {
assert_eq!(i64_to_saturated_i32(i64::MIN), i32::MIN);
assert_eq!(i64_to_saturated_i32(-2147483649), -2147483648);
assert_eq!(i64_to_saturated_i32(-2147483648), -2147483648);
assert_eq!(i64_to_saturated_i32(-2147483647), -2147483647);
assert_eq!(i64_to_saturated_i32(-2147483646), -2147483646);
assert_eq!(i64_to_saturated_i32(-100), -100);
assert_eq!(i64_to_saturated_i32(0), 0);
assert_eq!(i64_to_saturated_i32(100), 100);
assert_eq!(i64_to_saturated_i32(2147483646), 2147483646);
assert_eq!(i64_to_saturated_i32(2147483647), 2147483647);
assert_eq!(i64_to_saturated_i32(2147483648), 2147483647);
assert_eq!(i64_to_saturated_i32(2147483649), 2147483647);
assert_eq!(i64_to_saturated_i32(i64::MAX), i32::MAX);
}

268
third_party/rust/calendrical_calculations/src/islamic.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,268 @@
use crate::astronomy::*;
use crate::helpers::{i64_to_saturated_i32, next};
use crate::rata_die::{Moment, RataDie};
#[allow(unused_imports)]
use core_maths::*;
// Different islamic calendars use different epochs (Thursday vs Friday) due to disagreement on the exact date of Mohammed's migration to Mecca.
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2066>
const FIXED_ISLAMIC_EPOCH_FRIDAY: RataDie = crate::julian::fixed_from_julian(622, 7, 16);
const FIXED_ISLAMIC_EPOCH_THURSDAY: RataDie = crate::julian::fixed_from_julian(622, 7, 15);
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L6898>
const CAIRO: Location = Location {
latitude: 30.1,
longitude: 31.3,
elevation: 200.0,
zone: (1_f64 / 12_f64),
};
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L6904>
pub fn fixed_from_islamic_observational(year: i32, month: u8, day: u8) -> RataDie {
let year = i64::from(year);
let month = i64::from(month);
let day = i64::from(day);
let midmonth = FIXED_ISLAMIC_EPOCH_FRIDAY.to_f64_date()
+ (((year - 1) as f64) * 12.0 + month as f64 - 0.5) * MEAN_SYNODIC_MONTH;
let lunar_phase =
Astronomical::calculate_lunar_phase_at_or_before(RataDie::new(midmonth as i64));
Astronomical::phasis_on_or_before(RataDie::new(midmonth as i64), CAIRO, Some(lunar_phase)) + day
- 1
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L6983-L6995>
pub fn observational_islamic_from_fixed(date: RataDie) -> (i32, u8, u8) {
let lunar_phase = Astronomical::calculate_lunar_phase_at_or_before(date);
let crescent = Astronomical::phasis_on_or_before(date, CAIRO, Some(lunar_phase));
let elapsed_months =
((crescent - FIXED_ISLAMIC_EPOCH_FRIDAY) as f64 / MEAN_SYNODIC_MONTH).round() as i32;
let year = elapsed_months.div_euclid(12) + 1;
let month = elapsed_months.rem_euclid(12) + 1;
let day = (date - crescent + 1) as u8;
(year, month as u8, day)
}
// Saudi visibility criterion on eve of fixed date in Mecca.
// The start of the new month only happens if both of these criterias are met: The moon is a waxing crescent at sunset of the previous day
// and the moon sets after the sun on that same evening.
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L6957>
fn saudi_criterion(date: RataDie) -> Option<bool> {
let sunset = Astronomical::sunset((date - 1).as_moment(), MECCA)?;
let tee = Location::universal_from_standard(sunset, MECCA);
let phase = Astronomical::lunar_phase(tee, Astronomical::julian_centuries(tee));
let moonlag = Astronomical::moonlag((date - 1).as_moment(), MECCA)?;
Some(phase > 0.0 && phase < 90.0 && moonlag > 0.0)
}
pub(crate) fn adjusted_saudi_criterion(date: RataDie) -> bool {
if let Some(x) = saudi_criterion(date) {
x
} else {
false
}
}
// Closest fixed date on or before date when Saudi visibility criterion is held.
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L6966>
pub fn saudi_new_month_on_or_before(date: RataDie) -> RataDie {
let last_new_moon = (Astronomical::lunar_phase_at_or_before(0.0, date.as_moment()))
.inner()
.floor(); // Gets the R.D Date of the prior new moon
let age = date.to_f64_date() - last_new_moon;
// Explanation of why the value 3.0 is chosen: https://github.com/unicode-org/icu4x/pull/3673/files#r1267460916
let tau = if age <= 3.0 && !adjusted_saudi_criterion(date) {
// Checks if the criterion is not yet visible on the evening of date
last_new_moon - 30.0 // Goes back a month
} else {
last_new_moon
};
next(RataDie::new(tau as i64), adjusted_saudi_criterion) // Loop that increments the day and checks if the criterion is now visible
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L6996>
pub fn saudi_islamic_from_fixed(date: RataDie) -> (i32, u8, u8) {
let crescent = saudi_new_month_on_or_before(date);
let elapsed_months =
((crescent - FIXED_ISLAMIC_EPOCH_FRIDAY) as f64 / MEAN_SYNODIC_MONTH).round() as i64;
let year = i64_to_saturated_i32(elapsed_months.div_euclid(12) + 1);
let month = (elapsed_months.rem_euclid(12) + 1) as u8;
let day = ((date - crescent) + 1) as u8;
(year, month, day)
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L6981>
pub fn fixed_from_saudi_islamic(year: i32, month: u8, day: u8) -> RataDie {
let midmonth = RataDie::new(
FIXED_ISLAMIC_EPOCH_FRIDAY.to_i64_date()
+ (((year as f64 - 1.0) * 12.0 + month as f64 - 0.5) * MEAN_SYNODIC_MONTH).floor()
as i64,
);
let first_day_of_month = saudi_new_month_on_or_before(midmonth).to_i64_date();
RataDie::new(first_day_of_month + day as i64 - 1)
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2076>
pub fn fixed_from_islamic_civil(year: i32, month: u8, day: u8) -> RataDie {
let year = i64::from(year);
let month = i64::from(month);
let day = i64::from(day);
RataDie::new(
(FIXED_ISLAMIC_EPOCH_FRIDAY.to_i64_date() - 1)
+ (year - 1) * 354
+ (3 + year * 11).div_euclid(30)
+ 29 * (month - 1)
+ month.div_euclid(2)
+ day,
)
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2090>
pub fn islamic_civil_from_fixed(date: RataDie) -> (i32, u8, u8) {
let year =
i64_to_saturated_i32(((date - FIXED_ISLAMIC_EPOCH_FRIDAY) * 30 + 10646).div_euclid(10631));
let prior_days = date.to_f64_date() - fixed_from_islamic_civil(year, 1, 1).to_f64_date();
debug_assert!(prior_days >= 0.0);
debug_assert!(prior_days <= 354.);
let month = (((prior_days * 11.0) + 330.0) / 325.0) as u8; // Prior days is maximum 354 (when year length is 355), making the value always less than 12
debug_assert!(month <= 12);
let day =
(date.to_f64_date() - fixed_from_islamic_civil(year, month, 1).to_f64_date() + 1.0) as u8; // The value will always be number between 1-30 because of the difference between the date and lunar ordinals function.
(year, month, day)
}
/// Lisp code reference:https: //github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2076
pub fn fixed_from_islamic_tabular(year: i32, month: u8, day: u8) -> RataDie {
let year = i64::from(year);
let month = i64::from(month);
let day = i64::from(day);
RataDie::new(
(FIXED_ISLAMIC_EPOCH_THURSDAY.to_i64_date() - 1)
+ (year - 1) * 354
+ (3 + year * 11).div_euclid(30)
+ 29 * (month - 1)
+ month.div_euclid(2)
+ day,
)
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L2090>
pub fn islamic_tabular_from_fixed(date: RataDie) -> (i32, u8, u8) {
let year = i64_to_saturated_i32(
((date - FIXED_ISLAMIC_EPOCH_THURSDAY) * 30 + 10646).div_euclid(10631),
);
let prior_days = date.to_f64_date() - fixed_from_islamic_tabular(year, 1, 1).to_f64_date();
debug_assert!(prior_days >= 0.0);
debug_assert!(prior_days <= 354.);
let month = (((prior_days * 11.0) + 330.0) / 325.0) as u8; // Prior days is maximum 354 (when year length is 355), making the value always less than 12
debug_assert!(month <= 12);
let day =
(date.to_f64_date() - fixed_from_islamic_tabular(year, month, 1).to_f64_date() + 1.0) as u8; // The value will always be number between 1-30 because of the difference between the date and lunar ordinals function.
(year, month, day)
}
/// The number of days in a month for the observational islamic calendar
pub fn observational_islamic_month_days(year: i32, month: u8) -> u8 {
let midmonth = FIXED_ISLAMIC_EPOCH_FRIDAY.to_f64_date()
+ (((year - 1) as f64) * 12.0 + month as f64 - 0.5) * MEAN_SYNODIC_MONTH;
let lunar_phase: f64 =
Astronomical::calculate_lunar_phase_at_or_before(RataDie::new(midmonth as i64));
let f_date =
Astronomical::phasis_on_or_before(RataDie::new(midmonth as i64), CAIRO, Some(lunar_phase));
Astronomical::month_length(f_date, CAIRO)
}
/// The number of days in a month for the Saudi (Umm Al-Qura) calendar
pub fn saudi_islamic_month_days(year: i32, month: u8) -> u8 {
// We cannot use month_days from the book here, that is for the observational calendar
//
// Instead we subtract the two new months calculated using the saudi criterion
let midmonth = Moment::new(
FIXED_ISLAMIC_EPOCH_FRIDAY.to_f64_date()
+ (((year - 1) as f64) * 12.0 + month as f64 - 0.5) * MEAN_SYNODIC_MONTH,
);
let midmonth_next = midmonth + MEAN_SYNODIC_MONTH;
let month_start = saudi_new_month_on_or_before(midmonth.as_rata_die());
let next_month_start = saudi_new_month_on_or_before(midmonth_next.as_rata_die());
let diff = next_month_start - month_start;
debug_assert!(
diff <= 30,
"umm-al-qura months must not be more than 30 days"
);
u8::try_from(diff).unwrap_or(30)
}
#[cfg(test)]
mod tests {
use super::*;
static TEST_FIXED_DATE: [i64; 33] = [
-214193, -61387, 25469, 49217, 171307, 210155, 253427, 369740, 400085, 434355, 452605,
470160, 473837, 507850, 524156, 544676, 567118, 569477, 601716, 613424, 626596, 645554,
664224, 671401, 694799, 704424, 708842, 709409, 709580, 727274, 728714, 744313, 764652,
];
// Removed: 601716 and 727274 fixed dates
static TEST_FIXED_DATE_UMMALQURA: [i64; 31] = [
-214193, -61387, 25469, 49217, 171307, 210155, 253427, 369740, 400085, 434355, 452605,
470160, 473837, 507850, 524156, 544676, 567118, 569477, 613424, 626596, 645554, 664224,
671401, 694799, 704424, 708842, 709409, 709580, 728714, 744313, 764652,
];
// Values from lisp code
static SAUDI_CRITERION_EXPECTED: [bool; 33] = [
false, false, true, false, false, true, false, true, false, false, true, false, false,
true, true, true, true, false, false, true, true, true, false, false, false, false, false,
false, true, false, true, false, true,
];
// Values from lisp code, removed two expected months.
static SAUDI_NEW_MONTH_OR_BEFORE_EXPECTED: [f64; 31] = [
-214203.0, -61412.0, 25467.0, 49210.0, 171290.0, 210152.0, 253414.0, 369735.0, 400063.0,
434348.0, 452598.0, 470139.0, 473830.0, 507850.0, 524150.0, 544674.0, 567118.0, 569450.0,
613421.0, 626592.0, 645551.0, 664214.0, 671391.0, 694779.0, 704405.0, 708835.0, 709396.0,
709573.0, 728709.0, 744301.0, 764647.0,
];
#[test]
fn test_islamic_epoch_friday() {
let epoch = FIXED_ISLAMIC_EPOCH_FRIDAY.to_i64_date();
// Iso year of Islamic Epoch
let epoch_year_from_fixed = crate::iso::iso_year_from_fixed(RataDie::new(epoch));
// 622 is the correct ISO year for the Islamic Epoch
assert_eq!(epoch_year_from_fixed, 622);
}
#[test]
fn test_islamic_epoch_thursday() {
let epoch = FIXED_ISLAMIC_EPOCH_THURSDAY.to_i64_date();
// Iso year of Islamic Epoch
let epoch_year_from_fixed = crate::iso::iso_year_from_fixed(RataDie::new(epoch));
// 622 is the correct ISO year for the Islamic Epoch
assert_eq!(epoch_year_from_fixed, 622);
}
#[test]
fn test_saudi_criterion() {
for (boolean, f_date) in SAUDI_CRITERION_EXPECTED.iter().zip(TEST_FIXED_DATE.iter()) {
let bool_result = saudi_criterion(RataDie::new(*f_date)).unwrap();
assert_eq!(*boolean, bool_result, "{f_date:?}");
}
}
#[test]
fn test_saudi_new_month_or_before() {
for (date, f_date) in SAUDI_NEW_MONTH_OR_BEFORE_EXPECTED
.iter()
.zip(TEST_FIXED_DATE_UMMALQURA.iter())
{
let date_result = saudi_new_month_on_or_before(RataDie::new(*f_date)).to_f64_date();
assert_eq!(*date, date_result, "{f_date:?}");
}
}
}

93
third_party/rust/calendrical_calculations/src/iso.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,93 @@
// This file is part of ICU4X.
//
// The contents of this file implement algorithms from Calendrical Calculations
// by Reingold & Dershowitz, Cambridge University Press, 4th edition (2018),
// which have been released as Lisp code at <https://github.com/EdReingold/calendar-code2/>
// under the Apache-2.0 license. Accordingly, this file is released under
// the Apache License, Version 2.0 which can be found at the calendrical_calculations
// package root or at http://www.apache.org/licenses/LICENSE-2.0.
use crate::helpers::{i64_to_i32, I32CastError};
use crate::rata_die::RataDie;
// The Gregorian epoch is equivalent to first day in fixed day measurement
const EPOCH: RataDie = RataDie::new(1);
/// Whether or not `year` is a leap year
pub fn is_leap_year(year: i32) -> bool {
year % 4 == 0 && (year % 400 == 0 || year % 100 != 0)
}
// Fixed is day count representation of calendars starting from Jan 1st of year 1.
// The fixed calculations algorithms are from the Calendrical Calculations book.
//
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L1167-L1189>
pub fn fixed_from_iso(year: i32, month: u8, day: u8) -> RataDie {
let prev_year = (year as i64) - 1;
// Calculate days per year
let mut fixed: i64 = (EPOCH.to_i64_date() - 1) + 365 * prev_year;
// Calculate leap year offset
let offset = prev_year.div_euclid(4) - prev_year.div_euclid(100) + prev_year.div_euclid(400);
// Adjust for leap year logic
fixed += offset;
// Days of current year
fixed += (367 * (month as i64) - 362).div_euclid(12);
// Leap year adjustment for the current year
fixed += if month <= 2 {
0
} else if is_leap_year(year) {
-1
} else {
-2
};
// Days passed in current month
fixed += day as i64;
RataDie::new(fixed)
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L1191-L1217>
pub(crate) fn iso_year_from_fixed(date: RataDie) -> i64 {
// Shouldn't overflow because it's not possbile to construct extreme values of RataDie
let date = date - EPOCH;
// 400 year cycles have 146097 days
let (n_400, date) = (date.div_euclid(146097), date.rem_euclid(146097));
// 100 year cycles have 36524 days
let (n_100, date) = (date.div_euclid(36524), date.rem_euclid(36524));
// 4 year cycles have 1461 days
let (n_4, date) = (date.div_euclid(1461), date.rem_euclid(1461));
let n_1 = date.div_euclid(365);
let year = 400 * n_400 + 100 * n_100 + 4 * n_4 + n_1;
if n_100 == 4 || n_1 == 4 {
year
} else {
year + 1
}
}
fn iso_new_year(year: i32) -> RataDie {
fixed_from_iso(year, 1, 1)
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L1525-L1540>
pub fn iso_from_fixed(date: RataDie) -> Result<(i32, u8, u8), I32CastError> {
let year = iso_year_from_fixed(date);
let year = i64_to_i32(year)?;
// Calculates the prior days of the adjusted year, then applies a correction based on leap year conditions for the correct ISO date conversion.
let prior_days = date - iso_new_year(year);
let correction = if date < fixed_from_iso(year, 3, 1) {
0
} else if is_leap_year(year) {
1
} else {
2
};
let month = (12 * (prior_days + correction) + 373).div_euclid(367) as u8; // in 1..12 < u8::MAX
let day = (date - fixed_from_iso(year, month, 1) + 1) as u8; // <= days_in_month < u8::MAX
Ok((year, month, day))
}

115
third_party/rust/calendrical_calculations/src/julian.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,115 @@
// This file is part of ICU4X.
//
// The contents of this file implement algorithms from Calendrical Calculations
// by Reingold & Dershowitz, Cambridge University Press, 4th edition (2018),
// which have been released as Lisp code at <https://github.com/EdReingold/calendar-code2/>
// under the Apache-2.0 license. Accordingly, this file is released under
// the Apache License, Version 2.0 which can be found at the calendrical_calculations
// package root or at http://www.apache.org/licenses/LICENSE-2.0.
use crate::helpers::{i64_to_i32, I32CastError};
use crate::rata_die::RataDie;
// Julian epoch is equivalent to fixed_from_iso of December 30th of 0 year
// 1st Jan of 1st year Julian is equivalent to December 30th of 0th year of ISO year
const JULIAN_EPOCH: RataDie = RataDie::new(-1);
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L1684-L1687>
#[inline(always)]
pub const fn is_leap_year(year: i32) -> bool {
year % 4 == 0
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L1689-L1709>
pub const fn fixed_from_julian(year: i32, month: u8, day: u8) -> RataDie {
let mut fixed =
JULIAN_EPOCH.to_i64_date() - 1 + 365 * (year as i64 - 1) + (year as i64 - 1).div_euclid(4);
debug_assert!(month > 0 && month < 13, "Month should be in range 1..=12.");
fixed += match month {
1 => 0,
2 => 31,
3 => 59,
4 => 90,
5 => 120,
6 => 151,
7 => 181,
8 => 212,
9 => 243,
10 => 273,
11 => 304,
12 => 334,
_ => -1,
};
// Only add one if the month is after February (month > 2), since leap days are added to the end of February
if month > 2 && is_leap_year(year) {
fixed += 1;
}
RataDie::new(fixed + (day as i64))
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L1711-L1738>
pub fn julian_from_fixed(date: RataDie) -> Result<(i32, u8, u8), I32CastError> {
let approx = (4 * date.to_i64_date() + 1464).div_euclid(1461);
let year = i64_to_i32(approx)?;
let prior_days = date
- fixed_from_julian(year, 1, 1)
- if is_leap_year(year) && date > fixed_from_julian(year, 2, 28) {
1
} else {
0
};
let adjusted_year = if prior_days >= 365 {
year.saturating_add(1)
} else {
year
};
let adjusted_prior_days = prior_days.rem_euclid(365);
debug_assert!((0..365).contains(&adjusted_prior_days));
let month = if adjusted_prior_days < 31 {
1
} else if adjusted_prior_days < 59 {
2
} else if adjusted_prior_days < 90 {
3
} else if adjusted_prior_days < 120 {
4
} else if adjusted_prior_days < 151 {
5
} else if adjusted_prior_days < 181 {
6
} else if adjusted_prior_days < 212 {
7
} else if adjusted_prior_days < 243 {
8
} else if adjusted_prior_days < 273 {
9
} else if adjusted_prior_days < 304 {
10
} else if adjusted_prior_days < 334 {
11
} else {
12
};
let day = (date - fixed_from_julian(adjusted_year, month, 1) + 1) as u8; // as days_in_month is < u8::MAX
debug_assert!(day <= 31, "Day assertion failed; date: {date:?}, adjusted_year: {adjusted_year}, prior_days: {prior_days}, month: {month}, day: {day}");
Ok((adjusted_year, month, day))
}
/// Get a fixed date from the ymd of a Julian date; years are counted as in _Calendrical Calculations_ by Reingold & Dershowitz,
/// meaning there is no year 0. For instance, near the epoch date, years are counted: -3, -2, -1, 1, 2, 3 instead of -2, -1, 0, 1, 2, 3.
///
/// Primarily useful for use with code constructing epochs specified in the bookg
pub const fn fixed_from_julian_book_version(book_year: i32, month: u8, day: u8) -> RataDie {
debug_assert!(book_year != 0);
// TODO: Should we check the bounds here?
fixed_from_julian(
if book_year < 0 {
book_year + 1
} else {
book_year
},
month,
day,
)
}

60
third_party/rust/calendrical_calculations/src/lib.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,60 @@
// This file is part of ICU4X.
//
// The contents of this file implement algorithms from Calendrical Calculations
// by Reingold & Dershowitz, Cambridge University Press, 4th edition (2018),
// which have been released as Lisp code at <https://github.com/EdReingold/calendar-code2/>
// under the Apache-2.0 license. Accordingly, this file is released under
// the Apache License, Version 2.0 which can be found at the calendrical_calculations
// package root or at http://www.apache.org/licenses/LICENSE-2.0.
//! Calendrical calculations
//!
//! This crate implements algorithms from
//! Calendrical Calculations by Reingold & Dershowitz, Cambridge University Press, 4th edition (2018)
//! as needed by [ICU4X](https://github.com/unicode-org/icu4x).
//!
//! Most of these algorithms can be found as lisp code in the book or
//! [on GithHub](https://github.com/EdReingold/calendar-code2/blob/main/calendar.l).
//!
//! The primary purpose of this crate is use by ICU4X, however if non-ICU4X users need this we are happy
//! to add more structure to this crate as needed.
// https://github.com/unicode-org/icu4x/blob/main/docs/process/boilerplate.md#library-annotations
#![cfg_attr(not(any(test, feature = "std")), no_std)]
#![cfg_attr(
not(test),
deny(
clippy::indexing_slicing,
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::exhaustive_structs,
clippy::exhaustive_enums,
missing_debug_implementations,
)
)]
#![warn(missing_docs)]
mod astronomy;
/// Chinese-like lunar calendars (Chinese, Dangi)
pub mod chinese_based;
/// The Coptic calendar
pub mod coptic;
/// Error handling
mod error;
/// The ethiopian calendar
pub mod ethiopian;
/// The Hebrew calendar
pub mod hebrew;
/// Additional math helpers
pub mod helpers;
/// Various islamic lunar calendars
pub mod islamic;
/// The ISO calendar (also usable as Gregorian)
pub mod iso;
/// The Julian calendar
pub mod julian;
/// The persian calendar
pub mod persian;
/// Representation of Rata Die (R.D., also called J.D. for Julain Date)
/// dates, which are represented as the number of days since ISO date 0001-01-01.
pub mod rata_die;

120
third_party/rust/calendrical_calculations/src/persian.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,120 @@
// This file is part of ICU4X.
//
// The contents of this file implement algorithms from Calendrical Calculations
// by Reingold & Dershowitz, Cambridge University Press, 4th edition (2018),
// which have been released as Lisp code at <https://github.com/EdReingold/calendar-code2/>
// under the Apache-2.0 license. Accordingly, this file is released under
// the Apache License, Version 2.0 which can be found at the calendrical_calculations
// package root or at http://www.apache.org/licenses/LICENSE-2.0.
use crate::helpers::{i64_to_i32, I32CastError, IntegerRoundings};
use crate::rata_die::RataDie;
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L4720>
// Book states that the Persian epoch is the date: 3/19/622 and since the Persian Calendar has no year 0, the best choice was to use the Julian function.
const FIXED_PERSIAN_EPOCH: RataDie = crate::julian::fixed_from_julian(622, 3, 19);
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L4803>
pub fn fixed_from_arithmetic_persian(year: i32, month: u8, day: u8) -> RataDie {
let p_year = i64::from(year);
let month = i64::from(month);
let day = i64::from(day);
let y = if p_year > 0 {
p_year - 474
} else {
p_year - 473
};
let year = y.rem_euclid(2820) + 474;
RataDie::new(
FIXED_PERSIAN_EPOCH.to_i64_date() - 1
+ 1029983 * y.div_euclid(2820)
+ 365 * (year - 1)
+ (31 * year - 5).div_euclid(128)
+ if month <= 7 {
31 * (month - 1)
} else {
30 * (month - 1) + 6
}
+ day,
)
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L4857>
pub fn arithmetic_persian_from_fixed(date: RataDie) -> Result<(i32, u8, u8), I32CastError> {
let year = arithmetic_persian_year_from_fixed(date);
let year = i64_to_i32(year)?;
#[allow(clippy::unwrap_used)] // valid month,day
let day_of_year = 1_i64 + (date - fixed_from_arithmetic_persian(year, 1, 1));
#[allow(unstable_name_collisions)] // div_ceil is unstable and polyfilled
let month = if day_of_year <= 186 {
day_of_year.div_ceil(31) as u8
} else {
(day_of_year - 6).div_ceil(30) as u8
};
let day = (date - fixed_from_arithmetic_persian(year, month, 1) + 1) as u8;
Ok((year, month, day))
}
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L4829>
fn arithmetic_persian_year_from_fixed(date: RataDie) -> i64 {
let d0 = date - fixed_from_arithmetic_persian(475, 1, 1);
let n2820 = d0.div_euclid(1029983);
let d1 = d0.rem_euclid(1029983);
let y2820 = if d1 == 1029982 {
2820
} else {
(128 * d1 + 46878).div_euclid(46751)
};
let year = 474 + n2820 * 2820 + y2820;
if year > 0 {
year
} else {
year - 1
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_persian_epoch() {
let epoch = FIXED_PERSIAN_EPOCH.to_i64_date();
// Iso year of Persian Epoch
let epoch_year_from_fixed = crate::iso::iso_year_from_fixed(RataDie::new(epoch));
// 622 is the correct ISO year for the Persian Epoch
assert_eq!(epoch_year_from_fixed, 622);
}
// Persian New Year occurring in March of Gregorian year (g_year) to fixed date
fn nowruz(g_year: i32) -> RataDie {
let (y, _m, _d) = crate::iso::iso_from_fixed(FIXED_PERSIAN_EPOCH).unwrap();
let persian_year = g_year - y + 1;
let year = if persian_year <= 0 {
persian_year - 1
} else {
persian_year
};
fixed_from_arithmetic_persian(year, 1, 1)
}
#[test]
fn test_nowruz() {
let fixed_date = nowruz(622).to_i64_date();
assert_eq!(fixed_date, FIXED_PERSIAN_EPOCH.to_i64_date());
// These values are used as test data in appendix C of the "Calendrical Calculations" book
let nowruz_test_year_start = 2000;
let nowruz_test_year_end = 2103;
for year in nowruz_test_year_start..=nowruz_test_year_end {
let two_thousand_eight_to_fixed = nowruz(year).to_i64_date();
let iso_date = crate::iso::fixed_from_iso(year, 3, 21);
let (persian_year, _m, _d) = arithmetic_persian_from_fixed(iso_date).unwrap();
assert_eq!(
arithmetic_persian_from_fixed(RataDie::new(two_thousand_eight_to_fixed))
.unwrap()
.0,
persian_year
);
}
}
}

191
third_party/rust/calendrical_calculations/src/rata_die.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,191 @@
// This file is part of ICU4X.
//
// The contents of this file implement algorithms from Calendrical Calculations
// by Reingold & Dershowitz, Cambridge University Press, 4th edition (2018),
// which have been released as Lisp code at <https://github.com/EdReingold/calendar-code2/>
// under the Apache-2.0 license. Accordingly, this file is released under
// the Apache License, Version 2.0 which can be found at the calendrical_calculations
// package root or at http://www.apache.org/licenses/LICENSE-2.0.
use core::ops::{Add, AddAssign, Sub, SubAssign};
#[allow(unused_imports)]
use core_maths::*;
/// The *Rata Die*, or *R.D.*, or `fixed_date`: number of days since January 1, 1 CE.
///
/// See: <https://en.wikipedia.org/wiki/Rata_Die>
///
/// It is a logic error to construct a RataDie
/// except from a date that is in range of one of the official calendars.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct RataDie(i64);
impl RataDie {
/// Create a RataDie
pub const fn new(fixed_date: i64) -> Self {
let result = Self(fixed_date);
#[cfg(debug_assertions)]
result.check();
result
}
/// Check that it is in range
#[cfg(debug_assertions)]
pub const fn check(&self) {
if self.0 > i64::MAX / 256 {
debug_assert!(
false,
"RataDie is not designed to store values near to the overflow boundary"
);
}
if self.0 < i64::MIN / 256 {
debug_assert!(
false,
"RataDie is not designed to store values near to the overflow boundary"
);
}
}
/// A valid RataDie that is intended to be below all dates representable in calendars
///
/// For testing only
#[doc(hidden)]
pub const fn big_negative() -> Self {
Self::new(i64::MIN / 256 / 256)
}
/// Convert this to an i64 value representing the RataDie
pub const fn to_i64_date(self) -> i64 {
self.0
}
/// Convert this to an f64 value representing the RataDie
pub const fn to_f64_date(self) -> f64 {
self.0 as f64
}
/// Calculate the number of days between two RataDie in a const-friendly way
pub const fn const_diff(self, rhs: Self) -> i64 {
self.0 - rhs.0
}
/// Convert this to a [`Moment`]
pub const fn as_moment(&self) -> Moment {
Moment::new(self.0 as f64)
}
}
/// Shift a RataDie N days into the future
impl Add<i64> for RataDie {
type Output = Self;
fn add(self, rhs: i64) -> Self::Output {
let result = Self(self.0 + rhs);
#[cfg(debug_assertions)]
result.check();
result
}
}
impl AddAssign<i64> for RataDie {
fn add_assign(&mut self, rhs: i64) {
self.0 += rhs;
#[cfg(debug_assertions)]
self.check();
}
}
/// Shift a RataDie N days into the past
impl Sub<i64> for RataDie {
type Output = Self;
fn sub(self, rhs: i64) -> Self::Output {
let result = Self(self.0 - rhs);
#[cfg(debug_assertions)]
result.check();
result
}
}
impl SubAssign<i64> for RataDie {
fn sub_assign(&mut self, rhs: i64) {
self.0 -= rhs;
#[cfg(debug_assertions)]
self.check();
}
}
/// Calculate the number of days between two RataDie
impl Sub for RataDie {
type Output = i64;
fn sub(self, rhs: Self) -> Self::Output {
self.0 - rhs.0
}
}
/// A moment is a RataDie with a fractional part giving the time of day.
///
/// NOTE: This should not cause overflow errors for most cases, but consider
/// alternative implementations if necessary.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub struct Moment(f64);
/// Add a number of days to a Moment
impl Add<f64> for Moment {
type Output = Self;
fn add(self, rhs: f64) -> Self::Output {
Self(self.0 + rhs)
}
}
impl AddAssign<f64> for Moment {
fn add_assign(&mut self, rhs: f64) {
self.0 += rhs;
}
}
/// Subtract a number of days from a Moment
impl Sub<f64> for Moment {
type Output = Self;
fn sub(self, rhs: f64) -> Self::Output {
Self(self.0 - rhs)
}
}
impl SubAssign<f64> for Moment {
fn sub_assign(&mut self, rhs: f64) {
self.0 -= rhs;
}
}
/// Calculate the number of days between two moments
impl Sub for Moment {
type Output = f64;
fn sub(self, rhs: Self) -> Self::Output {
self.0 - rhs.0
}
}
impl Moment {
/// Create a new moment
pub const fn new(value: f64) -> Moment {
Moment(value)
}
/// Get the inner field of a Moment
pub const fn inner(&self) -> f64 {
self.0
}
/// Get the RataDie of a Moment
pub fn as_rata_die(&self) -> RataDie {
RataDie::new(self.0.floor() as i64)
}
}
#[test]
fn test_moment_to_rata_die_conversion() {
for i in -1000..=1000 {
let moment = Moment::new(i as f64);
let rata_die = moment.as_rata_die();
assert_eq!(rata_die.to_i64_date(), i);
}
}

1
third_party/rust/icu_calendar/.cargo-checksum.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
{"files":{"Cargo.lock":"84b7de1aceb9f2202f84e1d284f1c6a2e4e4bbfe729c7fdaed2fdc4603f94041","Cargo.toml":"d4f6e0f401a4d53365f2107d19f7bf7becd73a7e66939cd3a08964460602055d","LICENSE":"853f87c96f3d249f200fec6db1114427bc8bdf4afddc93c576956d78152ce978","README.md":"dcac57005c07034745512963cf09963e54d73141a61e35be9b102b4b1354535a","benches/convert.rs":"f9ab13aa93f1fcef7e20700aab803e358494d97f17e0a853dfd11fb944be4860","benches/date.rs":"664d4e0cb21e7f0e902895543cb00ec2af166488847f14de2ae305bca799f93b","benches/datetime.rs":"03b49c3f427f87a9b44959ef60c17d41a87692bb44134f1fd099635ed9adfb7c","benches/fixtures/datetimes.json":"ad8db74905ad8837b5e75ddc0bb51c1f277a8aa20e486d6927438a8fc7f094a6","benches/fixtures/mod.rs":"fdb50c26990cf5e083fbf7b2d9ee6ddfbd8be0279f7f90952e823e0e2c568e0a","benches/fixtures/structs.rs":"c927a527009af7d29bb16fdb893704178ac577ec2688baffa924f24da7f6b040","benches/iso.rs":"c80fa2c7b2554ad3f63bb88e5b31481757dbd916de0be88ae6b8057c3390c33f","examples/iso_date_manipulations.rs":"ed178ed88aa9a900df2841ae1f443ee8a69932ff1119ab3fadba07085c0665b6","examples/iso_datetime_manipulations.rs":"e6f2063ef0e41dca9ab4efa12646ac1f88ddfce80e8a0aa41f77c68c5e759f91","src/any_calendar.rs":"06e4a65bc02cc57514b820f03db60bf019bc39faeb83c92471301a6658ebbae8","src/buddhist.rs":"faf54cf146147fd2789317a4d28fa7d8bdb6a134488732f060af2bf1b833af36","src/calendar.rs":"1b4a38d7d2715f6832abdcf8b4bf45c4762722fb6c784a48cfb335da33357e21","src/calendar_arithmetic.rs":"bb46b046edaf05f099f39c394278b36caf85467760268143cffde938530532bb","src/chinese.rs":"8e356604a79e6925b6d531ccb6a5b7efb1277de17505a8d662004d9cc7f15a74","src/chinese_based.rs":"bb485620170200d14cedba6d949660d6268c20af0ea5c366f3fb13d08d2c0325","src/chinese_data.rs":"95ac42336b97da6eb16412eb7c03c2b0bb22b4f61e12c71a28e1ff5ee6a51366","src/coptic.rs":"eb52f90acb0a5d84202708a40aa32b9d708123594ff47aa6e83e23a8290add54","src/dangi.rs":"391094ffef10d2a13a0e86b0cbb2c065b9b09e85fa86f7947e2a5aac46579cca","src/date.rs":"d5d2f2be614bd1b0cc77f717e3f216b57e8ce4a950a0052f12287f9647c50351","src/datetime.rs":"238defbf65abf5d21325a364677d1d8174e33f19e88a446a76203828b783cddc","src/duration.rs":"3eb6eacce53b83e5ff2177885b952ed9b8b5102772bf6849d0b7922d623136a5","src/error.rs":"23a9c356dd3bc2d9d8eb73845dd7e48370202dda0d60a6f84fdd74020d2252d6","src/ethiopian.rs":"df4cb822556bbe1c3bdf4e13d63e98123e4b5f324447e1c75f5dde4a43819bde","src/gregorian.rs":"b8d2ed36dce818288b576b226dbff86c0800fd170ebc9ab6df69edbd917ac4d4","src/hebrew.rs":"3298dabdb00c72e2793afef1cb390bceb4c75850cffa867ae4f42b30a6b85f29","src/indian.rs":"d4627a4fe56428b5c781bf2cbc79d7a9fc31af8ce64e56f4c99113901b83917b","src/islamic.rs":"7a4fad4f28fbcc69e62535993756e00b83952d806495c1ddc8f726ca8160aef7","src/iso.rs":"e9ea84f1c774123be55822805caf6006caa3f918348487fc7903ab2401a1e67d","src/japanese.rs":"e859c2c46038173c8082aa8c5d57e38f92bab4650f2ccd8f786757c89e07dd9d","src/julian.rs":"ac98eb7304ec439d7e284f1ee6251501b03bded2f70699e60fb916124afbfb72","src/lib.rs":"e06e108dfffcb2a6b4eac117210c71566065ff83058c28ed5c3bb9c9dc3cb5f3","src/persian.rs":"084dcafeed913e0083a0a1a2592a4eac27e09e977eb61bdab61e0557fc83e707","src/provider.rs":"f228c100919ccdb3d06b84151d5dc84331ce0c18d4dc5a5dd75beb0ab3d30d5f","src/roc.rs":"a941226bfc8ff076e343711d0f1697148d310ac1ed7fe1fcdc20bd3f1a486a07","src/types.rs":"68ee163d33a7daf7ab7b299c03c99efcfa7d2374ca0e9cd1aecb5433990d520d","src/week_of.rs":"48f1a3923a53e49b4790e3f371f23663aa0ab394c3ba61cb22833f6fa80cd2c6"},"package":"7eb932a690c92f87955e923106181ee0d5682e688ff37fb5c7b296e1fe806edb"}

845
third_party/rust/icu_calendar/Cargo.lock сгенерированный поставляемый Normal file
Просмотреть файл

@ -0,0 +1,845 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab"
dependencies = [
"memchr",
]
[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bumpalo"
version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]]
name = "calendrical_calculations"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dfe3bc6a50b4667fafdb6d9cf26731c5418c457e317d8166c972014facf9a5d"
dependencies = [
"core_maths",
"displaydoc",
]
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "ciborium"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656"
[[package]]
name = "ciborium-ll"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "clap"
version = "3.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
dependencies = [
"bitflags",
"clap_lex",
"indexmap",
"textwrap",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "core_maths"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b02505ccb8c50b0aa21ace0fc08c3e53adebd4e58caa18a36152803c7709a3"
dependencies = [
"libm",
]
[[package]]
name = "criterion"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb"
dependencies = [
"anes",
"atty",
"cast",
"ciborium",
"clap",
"criterion-plot",
"itertools",
"lazy_static",
"num-traits",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
"itertools",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
[[package]]
name = "databake"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82175d72e69414ceafbe2b49686794d3a8bed846e0d50267355f83ea8fdd953a"
dependencies = [
"databake-derive",
"proc-macro2",
"quote",
]
[[package]]
name = "databake-derive"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "377af281d8f23663862a7c84623bc5dcf7f8c44b13c7496a590bdc157f941a43"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "displaydoc"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "icu_calendar"
version = "1.4.0"
dependencies = [
"calendrical_calculations",
"criterion",
"databake",
"displaydoc",
"icu_calendar_data",
"icu_locid",
"icu_locid_transform",
"icu_provider",
"serde",
"serde_json",
"tinystr",
"writeable",
"zerovec",
]
[[package]]
name = "icu_calendar_data"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22aec7d032735d9acb256eeef72adcac43c3b7572f19b51576a63d664b524ca2"
[[package]]
name = "icu_locid"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c0aa2536adc14c07e2a521e95512b75ed8ef832f0fdf9299d4a0a45d2be2a9d"
dependencies = [
"displaydoc",
"litemap",
"tinystr",
"writeable",
"zerovec",
]
[[package]]
name = "icu_locid_transform"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c17d8f6524fdca4471101dd71f0a132eb6382b5d6d7f2970441cb25f6f435a"
dependencies = [
"displaydoc",
"icu_locid",
"icu_locid_transform_data",
"icu_provider",
"tinystr",
"zerovec",
]
[[package]]
name = "icu_locid_transform_data"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "545c6c3e8bf9580e2dafee8de6f9ec14826aaf359787789c7724f1f85f47d3dc"
[[package]]
name = "icu_provider"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba58e782287eb6950247abbf11719f83f5d4e4a5c1f2cd490d30a334bc47c2f4"
dependencies = [
"displaydoc",
"icu_locid",
"icu_provider_macros",
"serde",
"stable_deref_trait",
"tinystr",
"writeable",
"yoke",
"zerofrom",
"zerovec",
]
[[package]]
name = "icu_provider_macros"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2abdd3a62551e8337af119c5899e600ca0c88ec8f23a46c60ba216c803dcf1a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "js-sys"
version = "0.3.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.148"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
[[package]]
name = "libm"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
[[package]]
name = "litemap"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d642685b028806386b2b6e75685faadd3eb65a85fff7df711ce18446a422da"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "memchr"
version = "2.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "num-traits"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "os_str_bytes"
version = "6.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac"
[[package]]
name = "plotters"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
[[package]]
name = "plotters-svg"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
dependencies = [
"plotters-backend",
]
[[package]]
name = "proc-macro2"
version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "regex"
version = "1.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "syn"
version = "2.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "synstructure"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06"
dependencies = [
"proc-macro2",
"quote",
"syn",
"unicode-xid",
]
[[package]]
name = "textwrap"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]]
name = "tinystr"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83c02bf3c538ab32ba913408224323915f4ef9a6d61c0e85d493f355921c0ece"
dependencies = [
"databake",
"displaydoc",
"serde",
"zerovec",
]
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-xid"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "walkdir"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
[[package]]
name = "web-sys"
version = "0.3.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "writeable"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dad7bb64b8ef9c0aa27b6da38b452b0ee9fd82beaf276a87dd796fb55cbae14e"
[[package]]
name = "yoke"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65e71b2e4f287f467794c671e2b8f8a5f3716b3c829079a1c44740148eff07e4"
dependencies = [
"serde",
"stable_deref_trait",
"yoke-derive",
"zerofrom",
]
[[package]]
name = "yoke-derive"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e6936f0cce458098a201c245a11bef556c6a0181129c7034d10d76d1ec3a2b8"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "zerofrom"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "655b0814c5c0b19ade497851070c640773304939a6c0fd5f5fb43da0696d05b7"
dependencies = [
"zerofrom-derive",
]
[[package]]
name = "zerofrom-derive"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "zerovec"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eff4439ae91fb5c72b8abc12f3f2dbf51bd27e6eadb9f8a5bc8898dddb0e27ea"
dependencies = [
"databake",
"serde",
"yoke",
"zerofrom",
"zerovec-derive",
]
[[package]]
name = "zerovec-derive"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4e5997cbf58990550ef1f0e5124a05e47e1ebd33a84af25739be6031a62c20"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

159
third_party/rust/icu_calendar/Cargo.toml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,159 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.67"
name = "icu_calendar"
version = "1.4.0"
authors = ["The ICU4X Project Developers"]
include = [
"data/**/*",
"src/**/*",
"examples/**/*",
"benches/**/*",
"tests/**/*",
"Cargo.toml",
"LICENSE",
"README.md",
]
description = "API for supporting various types of calendars"
homepage = "https://icu4x.unicode.org"
readme = "README.md"
categories = ["internationalization"]
license-file = "LICENSE"
repository = "https://github.com/unicode-org/icu4x"
[package.metadata.cargo-all-features]
denylist = ["bench"]
[package.metadata.docs.rs]
all-features = true
[[example]]
name = "iso_date_manipulations"
[[example]]
name = "iso_datetime_manipulations"
[[bench]]
name = "date"
harness = false
[[bench]]
name = "datetime"
harness = false
[[bench]]
name = "iso"
harness = false
[[bench]]
name = "convert"
harness = false
[dependencies.calendrical_calculations]
version = "0.1.0"
default-features = false
[dependencies.databake]
version = "0.1.7"
features = ["derive"]
optional = true
default-features = false
[dependencies.displaydoc]
version = "0.2.3"
default-features = false
[dependencies.icu_calendar_data]
version = "~1.4.0"
optional = true
default-features = false
[dependencies.icu_locid]
version = "~1.4.0"
default-features = false
[dependencies.icu_locid_transform]
version = "~1.4.0"
features = ["compiled_data"]
optional = true
default-features = false
[dependencies.icu_provider]
version = "~1.4.0"
features = ["macros"]
default-features = false
[dependencies.serde]
version = "1.0"
features = [
"derive",
"alloc",
]
optional = true
default-features = false
[dependencies.tinystr]
version = "0.7.4"
features = [
"alloc",
"zerovec",
]
default-features = false
[dependencies.writeable]
version = "0.5.4"
default-features = false
[dependencies.zerovec]
version = "0.10.1"
features = ["derive"]
default-features = false
[dev-dependencies.serde]
version = "1.0"
features = [
"derive",
"alloc",
]
[dev-dependencies.serde_json]
version = "1.0"
[features]
bench = []
compiled_data = [
"dep:icu_calendar_data",
"dep:icu_locid_transform",
]
datagen = [
"serde",
"dep:databake",
"zerovec/databake",
"tinystr/databake",
]
default = ["compiled_data"]
serde = [
"dep:serde",
"zerovec/serde",
"tinystr/serde",
"icu_provider/serde",
]
std = [
"icu_provider/std",
"icu_locid/std",
"calendrical_calculations/std",
]
[target."cfg(not(target_arch = \"wasm32\"))".dev-dependencies.criterion]
version = "0.4"

44
third_party/rust/icu_calendar/LICENSE поставляемый Normal file
Просмотреть файл

@ -0,0 +1,44 @@
UNICODE LICENSE V3
COPYRIGHT AND PERMISSION NOTICE
Copyright © 2020-2023 Unicode, Inc.
NOTICE TO USER: Carefully read the following legal agreement. BY
DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR
SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT
DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
Permission is hereby granted, free of charge, to any person obtaining a
copy of data files and any associated documentation (the "Data Files") or
software and any associated documentation (the "Software") to deal in the
Data Files or Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, and/or sell
copies of the Data Files or Software, and to permit persons to whom the
Data Files or Software are furnished to do so, provided that either (a)
this copyright and permission notice appear with all copies of the Data
Files or Software, or (b) this copyright and permission notice appear in
associated Documentation.
THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA
FILES OR SOFTWARE.
Except as contained in this notice, the name of a copyright holder shall
not be used in advertising or otherwise to promote the sale, use or other
dealings in these Data Files or Software without prior written
authorization of the copyright holder.
Portions of ICU4X may have been adapted from ICU4C and/or ICU4J.
ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.

100
third_party/rust/icu_calendar/README.md поставляемый Normal file
Просмотреть файл

@ -0,0 +1,100 @@
# icu_calendar [![crates.io](https://img.shields.io/crates/v/icu_calendar)](https://crates.io/crates/icu_calendar)
<!-- cargo-rdme start -->
Types for dealing with dates, times, and custom calendars.
This module is published as its own crate ([`icu_calendar`](https://docs.rs/icu_calendar/latest/icu_calendar/))
and as part of the [`icu`](https://docs.rs/icu/latest/icu/) crate. See the latter for more details on the ICU4X project.
The [`types`] module has a lot of common types for dealing with dates and times.
[`Calendar`] is a trait that allows one to define custom calendars, and [`Date`]
can represent dates for arbitrary calendars.
The [`iso`] and [`gregorian`] modules contain implementations for the ISO and
Gregorian calendars respectively. Further calendars can be found in modules like
[`japanese`], [`julian`], [`coptic`], [`indian`], [`buddhist`], and [`ethiopian`].
Most interaction with this crate will be done via the [`Date`] and [`DateTime`] types.
Some of the algorithms implemented here are based on
Dershowitz, Nachum, and Edward M. Reingold. _Calendrical calculations_. Cambridge University Press, 2008.
with associated Lisp code found at <https://github.com/EdReingold/calendar-code2>.
## Examples
Examples of date manipulation using `Date` object. `Date` objects are useful
for working with dates, encompassing information about the day, month, year,
as well as the calendar type.
```rust
use icu::calendar::{types::IsoWeekday, Date};
// Creating ISO date: 1992-09-02.
let mut date_iso = Date::try_new_iso_date(1992, 9, 2)
.expect("Failed to initialize ISO Date instance.");
assert_eq!(date_iso.day_of_week(), IsoWeekday::Wednesday);
assert_eq!(date_iso.year().number, 1992);
assert_eq!(date_iso.month().ordinal, 9);
assert_eq!(date_iso.day_of_month().0, 2);
// Answering questions about days in month and year.
assert_eq!(date_iso.days_in_year(), 366);
assert_eq!(date_iso.days_in_month(), 30);
```
Example of converting an ISO date across Indian and Buddhist calendars.
```rust
use icu::calendar::{buddhist::Buddhist, indian::Indian, Date};
// Creating ISO date: 1992-09-02.
let mut date_iso = Date::try_new_iso_date(1992, 9, 2)
.expect("Failed to initialize ISO Date instance.");
assert_eq!(date_iso.year().number, 1992);
assert_eq!(date_iso.month().ordinal, 9);
assert_eq!(date_iso.day_of_month().0, 2);
// Conversion into Indian calendar: 1914-08-02.
let date_indian = date_iso.to_calendar(Indian);
assert_eq!(date_indian.year().number, 1914);
assert_eq!(date_indian.month().ordinal, 6);
assert_eq!(date_indian.day_of_month().0, 11);
// Conversion into Buddhist calendar: 2535-09-02.
let date_buddhist = date_iso.to_calendar(Buddhist);
assert_eq!(date_buddhist.year().number, 2535);
assert_eq!(date_buddhist.month().ordinal, 9);
assert_eq!(date_buddhist.day_of_month().0, 2);
```
Example using `DateTime` object. Similar to `Date` objects, `DateTime` objects
contain an accessible `Date` object containing information about the day, month,
year, and calendar type. Additionally, `DateTime` objects contain an accessible
`Time` object, including granularity of hour, minute, second, and nanosecond.
```rust
use icu::calendar::{types::IsoWeekday, types::Time, DateTime};
// Creating ISO date: 1992-09-02 8:59
let mut datetime_iso = DateTime::try_new_iso_datetime(1992, 9, 2, 8, 59, 0)
.expect("Failed to initialize ISO DateTime instance.");
assert_eq!(datetime_iso.date.day_of_week(), IsoWeekday::Wednesday);
assert_eq!(datetime_iso.date.year().number, 1992);
assert_eq!(datetime_iso.date.month().ordinal, 9);
assert_eq!(datetime_iso.date.day_of_month().0, 2);
assert_eq!(datetime_iso.time.hour.number(), 8);
assert_eq!(datetime_iso.time.minute.number(), 59);
assert_eq!(datetime_iso.time.second.number(), 0);
assert_eq!(datetime_iso.time.nanosecond.number(), 0);
```
[`ICU4X`]: ../icu/index.html
<!-- cargo-rdme end -->
## More Information
For more information on development, authorship, contributing etc. please visit [`ICU4X home page`](https://github.com/unicode-org/icu4x).

103
third_party/rust/icu_calendar/benches/convert.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,103 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
mod fixtures;
use criterion::{
black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion,
};
use icu_calendar::{Calendar, Date, Ref};
fn bench_calendar<C: Clone + Calendar>(
group: &mut BenchmarkGroup<WallTime>,
name: &str,
calendar: C,
) {
let iso = Date::try_new_iso_date(2023, 8, 16).unwrap();
group.bench_function(name, |b| {
b.iter(|| {
let converted = black_box(iso).to_calendar(Ref(&calendar));
let year = black_box(converted.year().number);
let month = black_box(converted.month().ordinal);
let day = black_box(converted.day_of_month().0);
black_box((converted, year, month, day))
})
});
}
fn convert_benches(c: &mut Criterion) {
let mut group = c.benchmark_group("convert");
bench_calendar(&mut group, "calendar/iso", icu::calendar::iso::Iso);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/buddhist",
icu::calendar::buddhist::Buddhist,
);
#[cfg(feature = "bench")]
bench_calendar(&mut group, "calendar/coptic", icu::calendar::coptic::Coptic);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/ethiopic",
icu::calendar::ethiopian::Ethiopian::new(),
);
#[cfg(feature = "bench")]
bench_calendar(&mut group, "calendar/indian", icu::calendar::indian::Indian);
#[cfg(feature = "bench")]
bench_calendar(&mut group, "calendar/julian", icu::calendar::julian::Julian);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/chinese",
icu::calendar::chinese::Chinese::new_always_calculating(),
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/gregorian",
icu::calendar::gregorian::Gregorian,
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/islamic/observational",
icu::calendar::islamic::IslamicObservational::new_always_calculating(),
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/islamic/civil",
icu::calendar::islamic::IslamicCivil::new_always_calculating(),
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/islamic/ummalqura",
icu::calendar::islamic::IslamicUmmAlQura::new_always_calculating(),
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/islamic/tabular",
icu::calendar::islamic::IslamicTabular::new_always_calculating(),
);
group.finish();
}
criterion_group!(benches, convert_benches);
criterion_main!(benches);

271
third_party/rust/icu_calendar/benches/date.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,271 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
mod fixtures;
use crate::fixtures::structs::DateFixture;
use criterion::{
black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion,
};
use icu_calendar::{AsCalendar, Calendar, Date, DateDuration};
fn bench_date<A: AsCalendar>(date: &mut Date<A>) {
// black_box used to avoid compiler optimization.
// Arithmetic
date.add(DateDuration::new(
black_box(1),
black_box(2),
black_box(3),
black_box(4),
));
// Retrieving vals
let _ = black_box(date.year().number);
let _ = black_box(date.month().ordinal);
let _ = black_box(date.day_of_month().0);
// Conversion to ISO.
let _ = black_box(date.to_iso());
}
fn bench_calendar<C: Clone + Calendar>(
group: &mut BenchmarkGroup<WallTime>,
name: &str,
fxs: &DateFixture,
calendar: C,
calendar_date_init: impl Fn(i32, u8, u8) -> Date<C>,
) {
group.bench_function(name, |b| {
b.iter(|| {
for fx in &fxs.0 {
// Instantion from int
let mut instantiated_date_calendar = calendar_date_init(fx.year, fx.month, fx.day);
// Conversion from ISO
let date_iso = Date::try_new_iso_date(fx.year, fx.month, fx.day).unwrap();
let mut converted_date_calendar = Date::new_from_iso(date_iso, calendar.clone());
bench_date(&mut instantiated_date_calendar);
bench_date(&mut converted_date_calendar);
}
})
});
}
fn date_benches(c: &mut Criterion) {
let mut group = c.benchmark_group("date");
let fxs = fixtures::get_dates_fixture().unwrap();
bench_calendar(
&mut group,
"calendar/overview",
&fxs,
icu::calendar::iso::Iso,
|y, m, d| Date::try_new_iso_date(y, m, d).unwrap(),
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/buddhist",
&fxs,
icu::calendar::buddhist::Buddhist,
|y, m, d| Date::try_new_buddhist_date(y, m, d).unwrap(),
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/coptic",
&fxs,
icu::calendar::coptic::Coptic,
|y, m, d| Date::try_new_coptic_date(y, m, d).unwrap(),
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/ethiopic",
&fxs,
icu::calendar::ethiopian::Ethiopian::new(),
|y, m, d| {
Date::try_new_ethiopian_date(
icu::calendar::ethiopian::EthiopianEraStyle::AmeteMihret,
y,
m,
d,
)
.unwrap()
},
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/indian",
&fxs,
icu::calendar::indian::Indian,
|y, m, d| Date::try_new_indian_date(y, m, d).unwrap(),
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/persian",
&fxs,
icu::calendar::persian::Persian,
|y, m, d| Date::try_new_persian_date(y, m, d).unwrap(),
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/roc",
&fxs,
icu::calendar::roc::Roc,
|y, m, d| Date::try_new_roc_date(y, m, d).unwrap(),
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/julian",
&fxs,
icu::calendar::julian::Julian,
|y, m, d| Date::try_new_julian_date(y, m, d).unwrap(),
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/chinese",
&fxs,
icu::calendar::chinese::Chinese::new_always_calculating(),
|y, m, d| {
Date::try_new_chinese_date_with_calendar(
y,
m,
d,
icu::calendar::chinese::Chinese::new_always_calculating(),
)
.unwrap()
},
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/dangi",
&fxs,
icu::calendar::dangi::Dangi::new_always_calculating(),
|y, m, d| {
Date::try_new_dangi_date_with_calendar(
y,
m,
d,
icu::calendar::dangi::Dangi::new_always_calculating(),
)
.unwrap()
},
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/hebrew",
&fxs,
icu::calendar::hebrew::Hebrew::new_always_calculating(),
|y, m, d| {
Date::try_new_hebrew_date_with_calendar(
y,
m,
d,
icu::calendar::hebrew::Hebrew::new_always_calculating(),
)
.unwrap()
},
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/gregorian",
&fxs,
icu::calendar::gregorian::Gregorian,
|y, m, d| Date::try_new_gregorian_date(y, m, d).unwrap(),
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/islamic/civil",
&fxs,
icu::calendar::islamic::IslamicCivil::new_always_calculating(),
|y, m, d| {
Date::try_new_islamic_civil_date_with_calendar(
y,
m,
d,
icu::calendar::islamic::IslamicCivil::new_always_calculating(),
)
.unwrap()
},
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/islamic/tabular",
&fxs,
icu::calendar::islamic::IslamicTabular::new_always_calculating(),
|y, m, d| {
Date::try_new_islamic_tabular_date_with_calendar(
y,
m,
d,
icu::calendar::islamic::IslamicTabular::new_always_calculating(),
)
.unwrap()
},
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/islamic/ummalqura",
&fxs,
icu::calendar::islamic::IslamicUmmAlQura::new_always_calculating(),
|y, m, d| {
Date::try_new_ummalqura_date(
y,
m,
d,
icu::calendar::islamic::IslamicUmmAlQura::new_always_calculating(),
)
.unwrap()
},
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/islamic/observational",
&fxs,
icu::calendar::islamic::IslamicObservational::new_always_calculating(),
|y, m, d| {
Date::try_new_observational_islamic_date(
y,
m,
d,
icu::calendar::islamic::IslamicObservational::new_always_calculating(),
)
.unwrap()
},
);
group.finish();
}
criterion_group!(benches, date_benches);
criterion_main!(benches);

248
third_party/rust/icu_calendar/benches/datetime.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,248 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
mod fixtures;
use crate::fixtures::structs::DateFixture;
use criterion::{
black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion,
};
use icu_calendar::{types::Time, AsCalendar, Calendar, DateDuration, DateTime};
fn bench_datetime<A: AsCalendar>(datetime: &mut DateTime<A>) {
// black_box used to avoid compiler optimization.
// Arithmetic.
datetime.date.add(DateDuration::new(
black_box(1),
black_box(2),
black_box(3),
black_box(4),
));
datetime.time = Time::try_new(black_box(14), black_box(30), black_box(0), black_box(0))
.expect("Failed to initialize Time instance.");
// Retrieving vals
let _ = black_box(datetime.date.year().number);
let _ = black_box(datetime.date.month().ordinal);
let _ = black_box(datetime.date.day_of_month().0);
let _ = black_box(datetime.time.hour);
let _ = black_box(datetime.time.minute);
let _ = black_box(datetime.time.second);
// Conversion to ISO.
let _ = black_box(datetime.to_iso());
}
fn bench_calendar<C: Clone + Calendar>(
group: &mut BenchmarkGroup<WallTime>,
name: &str,
fxs: &DateFixture,
calendar: C,
calendar_datetime_init: impl Fn(i32, u8, u8, u8, u8, u8) -> DateTime<C>,
) {
group.bench_function(name, |b| {
b.iter(|| {
for fx in &fxs.0 {
// Instantion from int
let mut instantiated_datetime_calendar = calendar_datetime_init(
fx.year, fx.month, fx.day, fx.hour, fx.minute, fx.second,
);
// Conversion from ISO
let datetime_iso = DateTime::try_new_iso_datetime(
fx.year, fx.month, fx.day, fx.hour, fx.minute, fx.second,
)
.unwrap();
let mut converted_datetime_calendar =
DateTime::new_from_iso(datetime_iso, calendar.clone());
bench_datetime(&mut instantiated_datetime_calendar);
bench_datetime(&mut converted_datetime_calendar);
}
})
});
}
fn datetime_benches(c: &mut Criterion) {
let mut group = c.benchmark_group("datetime");
let fxs = fixtures::get_dates_fixture().unwrap();
bench_calendar(
&mut group,
"calendar/overview",
&fxs,
icu::calendar::iso::Iso,
|y, m, d, h, min, s| DateTime::try_new_iso_datetime(y, m, d, h, min, s).unwrap(),
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/buddhist",
&fxs,
icu::calendar::buddhist::Buddhist,
|y, m, d, h, min, s| DateTime::try_new_buddhist_datetime(y, m, d, h, min, s).unwrap(),
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/coptic",
&fxs,
icu::calendar::coptic::Coptic,
|y, m, d, h, min, s| DateTime::try_new_coptic_datetime(y, m, d, h, min, s).unwrap(),
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/ethiopic",
&fxs,
icu::calendar::ethiopian::Ethiopian::new(),
|y, m, d, h, min, s| {
DateTime::try_new_ethiopian_datetime(
icu::calendar::ethiopian::EthiopianEraStyle::AmeteMihret,
y,
m,
d,
h,
min,
s,
)
.unwrap()
},
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/chinese",
&fxs,
icu::calendar::chinese::Chinese::new_always_calculating(),
|y, m, d, h, min, s| {
DateTime::try_new_chinese_datetime_with_calendar(
y,
m,
d,
h,
min,
s,
icu::calendar::chinese::Chinese::new_always_calculating(),
)
.unwrap()
},
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/gregorian",
&fxs,
icu::calendar::gregorian::Gregorian,
|y, m, d, h, min, s| DateTime::try_new_gregorian_datetime(y, m, d, h, min, s).unwrap(),
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/indian",
&fxs,
icu::calendar::indian::Indian,
|y, m, d, h, min, s| DateTime::try_new_indian_datetime(y, m, d, h, min, s).unwrap(),
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/julian",
&fxs,
icu::calendar::julian::Julian,
|y, m, d, h, min, s| DateTime::try_new_julian_datetime(y, m, d, h, min, s).unwrap(),
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/islamic/civil",
&fxs,
icu::calendar::islamic::IslamicCivil::new_always_calculating(),
|y, m, d, h, min, s| {
DateTime::try_new_islamic_civil_datetime_with_calendar(
y,
m,
d,
h,
min,
s,
icu::calendar::islamic::IslamicCivil::new_always_calculating(),
)
.unwrap()
},
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/islamic/tabular",
&fxs,
icu::calendar::islamic::IslamicTabular::new_always_calculating(),
|y, m, d, h, min, s| {
DateTime::try_new_islamic_tabular_datetime_with_calendar(
y,
m,
d,
h,
min,
s,
icu::calendar::islamic::IslamicTabular::new_always_calculating(),
)
.unwrap()
},
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/islamic/ummalqura",
&fxs,
icu::calendar::islamic::IslamicUmmAlQura::new_always_calculating(),
|y, m, d, h, min, s| {
DateTime::try_new_ummalqura_datetime(
y,
m,
d,
h,
min,
s,
icu::calendar::islamic::IslamicUmmAlQura::new_always_calculating(),
)
.unwrap()
},
);
#[cfg(feature = "bench")]
bench_calendar(
&mut group,
"calendar/islamic/observational",
&fxs,
icu::calendar::islamic::IslamicObservational::new_always_calculating(),
|y, m, d, h, min, s| {
DateTime::try_new_observational_islamic_datetime(
y,
m,
d,
h,
min,
s,
icu::calendar::islamic::IslamicObservational::new_always_calculating(),
)
.unwrap()
},
);
group.finish();
}
criterion_group!(benches, datetime_benches);
criterion_main!(benches);

82
third_party/rust/icu_calendar/benches/fixtures/datetimes.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,82 @@
[
{
"year": 1990,
"month": 1,
"day": 20,
"hour": 14,
"minute": 12,
"second": 1
},
{
"year": 1996,
"month": 3,
"day": 20,
"hour": 2,
"minute": 55,
"second": 12
},
{
"year": 2000,
"month": 3,
"day": 23,
"hour": 1,
"minute": 34,
"second": 35
},
{
"year": 2010,
"month": 5,
"day": 10,
"hour": 9,
"minute": 31,
"second": 12
},
{
"year": 2020,
"month": 6,
"day": 13,
"hour": 23,
"minute": 1,
"second": 12
},
{
"year": 1881,
"month": 6,
"day": 10,
"hour": 3,
"minute": 27,
"second": 59
},
{
"year": 1720,
"month": 2,
"day": 1,
"hour": 13,
"minute": 24,
"second": 13
},
{
"year": 1630,
"month": 3,
"day": 4,
"hour": 3,
"minute": 1,
"second": 1
},
{
"year": 1540,
"month": 3,
"day": 4,
"hour": 3,
"minute": 2,
"second": 12
},
{
"year": 1024,
"month": 5,
"day": 10,
"hour": 19,
"minute": 31,
"second": 2
}
]

16
third_party/rust/icu_calendar/benches/fixtures/mod.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,16 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
pub mod structs;
use std::fs::File;
use std::io::BufReader;
#[allow(dead_code)]
pub fn get_dates_fixture() -> std::io::Result<structs::DateFixture> {
let file = File::open("./benches/fixtures/datetimes.json")?;
let reader = BufReader::new(file);
Ok(serde_json::from_reader(reader)?)
}

18
third_party/rust/icu_calendar/benches/fixtures/structs.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,18 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DateFixture(pub Vec<Test>);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Test {
pub year: i32,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: u8,
}

20
third_party/rust/icu_calendar/benches/iso.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,20 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use icu_calendar::{DateTime, Iso};
fn overview_bench(c: &mut Criterion) {
c.bench_function("iso/from_minutes_since_local_unix_epoch", |b| {
b.iter(|| {
for i in -250..250 {
let minutes = i * 10000;
DateTime::<Iso>::from_minutes_since_local_unix_epoch(black_box(minutes));
}
});
});
}
criterion_group!(benches, overview_bench,);
criterion_main!(benches);

Просмотреть файл

@ -0,0 +1,62 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
// An example application which uses icu_datetime to format entries
// from a log into human readable dates and times.
#![no_main] // https://github.com/unicode-org/icu4x/issues/395
icu_benchmark_macros::static_setup!();
use icu_calendar::{Calendar, CalendarError, Date, Iso};
const DATES_ISO: &[(i32, u8, u8)] = &[
(1970, 1, 1),
(1982, 3, 11),
(1999, 2, 21),
(2000, 12, 29),
(2001, 9, 8),
(2017, 7, 12),
(2020, 2, 29),
(2021, 3, 21),
(2021, 6, 10),
(2021, 9, 2),
(2022, 10, 8),
(2022, 2, 9),
(2033, 6, 10),
];
fn print<A: Calendar>(_date_input: &Date<A>) {
#[cfg(debug_assertions)]
{
let formatted_date = format!(
"Year: {}, Month: {}, Day: {}",
_date_input.year().number,
_date_input.month().ordinal,
_date_input.day_of_month().0,
);
println!("{formatted_date}");
}
}
fn tuple_to_iso_date(date: (i32, u8, u8)) -> Result<Date<Iso>, CalendarError> {
Date::try_new_iso_date(date.0, date.1, date.2)
}
#[no_mangle]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
icu_benchmark_macros::main_setup!();
let dates = DATES_ISO
.iter()
.copied()
.map(tuple_to_iso_date)
.collect::<Result<Vec<Date<Iso>>, _>>()
.expect("Failed to parse dates.");
dates.iter().map(print).for_each(drop);
0
}

Просмотреть файл

@ -0,0 +1,65 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
// An example application which uses icu_datetime to format entries
// from a log into human readable dates and times.
#![no_main] // https://github.com/unicode-org/icu4x/issues/395
icu_benchmark_macros::static_setup!();
use icu_calendar::{Calendar, CalendarError, DateTime, Iso};
const DATETIMES_ISO: &[(i32, u8, u8, u8, u8, u8)] = &[
(1970, 1, 1, 3, 5, 12),
(1982, 3, 11, 2, 25, 59),
(1999, 2, 21, 13, 12, 23),
(2000, 12, 29, 10, 50, 23),
(2001, 9, 8, 11, 5, 5),
(2017, 7, 12, 3, 1, 1),
(2020, 2, 29, 23, 12, 23),
(2021, 3, 21, 18, 35, 34),
(2021, 6, 10, 13, 12, 23),
(2021, 9, 2, 5, 50, 22),
(2022, 10, 8, 9, 45, 32),
(2022, 2, 9, 10, 32, 45),
(2033, 6, 10, 17, 22, 22),
];
fn print<A: Calendar>(_datetime_input: &DateTime<A>) {
#[cfg(debug_assertions)]
{
let formatted_datetime = format!(
"Year: {}, Month: {}, Day: {}, Hour: {}, Minute: {}, Second: {}",
_datetime_input.date.year().number,
_datetime_input.date.month().ordinal,
_datetime_input.date.day_of_month().0,
u8::from(_datetime_input.time.hour),
u8::from(_datetime_input.time.minute),
u8::from(_datetime_input.time.second),
);
println!("{formatted_datetime}");
}
}
fn tuple_to_iso_datetime(date: (i32, u8, u8, u8, u8, u8)) -> Result<DateTime<Iso>, CalendarError> {
DateTime::try_new_iso_datetime(date.0, date.1, date.2, date.3, date.4, date.5)
}
#[no_mangle]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
icu_benchmark_macros::main_setup!();
let datetimes = DATETIMES_ISO
.iter()
.copied()
.map(tuple_to_iso_datetime)
.collect::<Result<Vec<DateTime<Iso>>, _>>()
.expect("Failed to parse datetimes.");
datetimes.iter().map(print).for_each(drop);
0
}

1711
third_party/rust/icu_calendar/src/any_calendar.rs поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

485
third_party/rust/icu_calendar/src/buddhist.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,485 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
//! This module contains types and implementations for the Buddhist calendar.
//!
//! ```rust
//! use icu::calendar::{buddhist::Buddhist, Date, DateTime};
//!
//! // `Date` type
//! let date_iso = Date::try_new_iso_date(1970, 1, 2)
//! .expect("Failed to initialize ISO Date instance.");
//! let date_buddhist = Date::new_from_iso(date_iso, Buddhist);
//!
//! // `DateTime` type
//! let datetime_iso = DateTime::try_new_iso_datetime(1970, 1, 2, 13, 1, 0)
//! .expect("Failed to initialize ISO DateTime instance.");
//! let datetime_buddhist = DateTime::new_from_iso(datetime_iso, Buddhist);
//!
//! // `Date` checks
//! assert_eq!(date_buddhist.year().number, 2513);
//! assert_eq!(date_buddhist.month().ordinal, 1);
//! assert_eq!(date_buddhist.day_of_month().0, 2);
//!
//! // `DateTime` type
//! assert_eq!(datetime_buddhist.date.year().number, 2513);
//! assert_eq!(datetime_buddhist.date.month().ordinal, 1);
//! assert_eq!(datetime_buddhist.date.day_of_month().0, 2);
//! assert_eq!(datetime_buddhist.time.hour.number(), 13);
//! assert_eq!(datetime_buddhist.time.minute.number(), 1);
//! assert_eq!(datetime_buddhist.time.second.number(), 0);
//! ```
use crate::any_calendar::AnyCalendarKind;
use crate::calendar_arithmetic::ArithmeticDate;
use crate::iso::{Iso, IsoDateInner};
use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime};
use tinystr::tinystr;
/// The number of years the Buddhist Era is ahead of C.E. by
///
/// (1 AD = 544 BE)
const BUDDHIST_ERA_OFFSET: i32 = 543;
#[derive(Copy, Clone, Debug, Default)]
/// The [Thai Solar Buddhist Calendar][cal]
///
/// The [Thai Solar Buddhist Calendar][cal] is a solar calendar used in Thailand, with twelve months.
/// The months and days are identical to that of the Gregorian calendar, however the years are counted
/// differently using the Buddhist Era.
///
/// This type can be used with [`Date`] or [`DateTime`] to represent dates in this calendar.
///
/// [cal]: https://en.wikipedia.org/wiki/Thai_solar_calendar
///
/// # Era codes
///
/// This calendar supports one era, `"be"`, with 1 B.E. being 543 BCE.
///
/// # Month codes
///
/// This calendar supports 12 solar month codes (`"M01" - "M12"`)
#[allow(clippy::exhaustive_structs)] // this type is stable
pub struct Buddhist;
impl Calendar for Buddhist {
type DateInner = IsoDateInner;
fn date_from_codes(
&self,
era: types::Era,
year: i32,
month_code: types::MonthCode,
day: u8,
) -> Result<Self::DateInner, CalendarError> {
if era.0 != tinystr!(16, "be") {
return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
}
let year = year - BUDDHIST_ERA_OFFSET;
ArithmeticDate::new_from_codes(self, year, month_code, day).map(IsoDateInner)
}
fn date_from_iso(&self, iso: Date<Iso>) -> IsoDateInner {
*iso.inner()
}
fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
Date::from_raw(*date, Iso)
}
fn months_in_year(&self, date: &Self::DateInner) -> u8 {
Iso.months_in_year(date)
}
fn days_in_year(&self, date: &Self::DateInner) -> u16 {
Iso.days_in_year(date)
}
fn days_in_month(&self, date: &Self::DateInner) -> u8 {
Iso.days_in_month(date)
}
fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
Iso.offset_date(date, offset.cast_unit())
}
#[allow(clippy::field_reassign_with_default)] // it's more clear this way
fn until(
&self,
date1: &Self::DateInner,
date2: &Self::DateInner,
_calendar2: &Self,
largest_unit: DateDurationUnit,
smallest_unit: DateDurationUnit,
) -> DateDuration<Self> {
Iso.until(date1, date2, &Iso, largest_unit, smallest_unit)
.cast_unit()
}
/// The calendar-specific year represented by `date`
fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
iso_year_as_buddhist(date.0.year)
}
fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
Iso.is_in_leap_year(date)
}
/// The calendar-specific month represented by `date`
fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
Iso.month(date)
}
/// The calendar-specific day-of-month represented by `date`
fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
Iso.day_of_month(date)
}
/// Information of the day of the year
fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
let prev_year = date.0.year - 1;
let next_year = date.0.year + 1;
types::DayOfYearInfo {
day_of_year: Iso::day_of_year(*date),
days_in_year: Iso::days_in_year_direct(date.0.year),
prev_year: iso_year_as_buddhist(prev_year),
days_in_prev_year: Iso::days_in_year_direct(prev_year),
next_year: iso_year_as_buddhist(next_year),
}
}
fn debug_name(&self) -> &'static str {
"Buddhist"
}
fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
Some(AnyCalendarKind::Buddhist)
}
}
impl Date<Buddhist> {
/// Construct a new Buddhist Date.
///
/// Years are specified as BE years.
///
/// ```rust
/// use icu::calendar::Date;
/// use std::convert::TryFrom;
///
/// let date_buddhist = Date::try_new_buddhist_date(1970, 1, 2)
/// .expect("Failed to initialize Buddhist Date instance.");
///
/// assert_eq!(date_buddhist.year().number, 1970);
/// assert_eq!(date_buddhist.month().ordinal, 1);
/// assert_eq!(date_buddhist.day_of_month().0, 2);
/// ```
pub fn try_new_buddhist_date(
year: i32,
month: u8,
day: u8,
) -> Result<Date<Buddhist>, CalendarError> {
Date::try_new_iso_date(year - BUDDHIST_ERA_OFFSET, month, day)
.map(|d| Date::new_from_iso(d, Buddhist))
}
}
impl DateTime<Buddhist> {
/// Construct a new Buddhist datetime from integers.
///
/// Years are specified as BE years.
///
/// ```rust
/// use icu::calendar::DateTime;
///
/// let datetime_buddhist =
/// DateTime::try_new_buddhist_datetime(1970, 1, 2, 13, 1, 0)
/// .expect("Failed to initialize Buddhist DateTime instance.");
///
/// assert_eq!(datetime_buddhist.date.year().number, 1970);
/// assert_eq!(datetime_buddhist.date.month().ordinal, 1);
/// assert_eq!(datetime_buddhist.date.day_of_month().0, 2);
/// assert_eq!(datetime_buddhist.time.hour.number(), 13);
/// assert_eq!(datetime_buddhist.time.minute.number(), 1);
/// assert_eq!(datetime_buddhist.time.second.number(), 0);
/// ```
pub fn try_new_buddhist_datetime(
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
) -> Result<DateTime<Buddhist>, CalendarError> {
Ok(DateTime {
date: Date::try_new_buddhist_date(year, month, day)?,
time: types::Time::try_new(hour, minute, second, 0)?,
})
}
}
fn iso_year_as_buddhist(year: i32) -> types::FormattableYear {
let buddhist_year = year + BUDDHIST_ERA_OFFSET;
types::FormattableYear {
era: types::Era(tinystr!(16, "be")),
number: buddhist_year,
cyclic: None,
related_iso: None,
}
}
#[cfg(test)]
mod test {
use calendrical_calculations::rata_die::RataDie;
use super::*;
#[test]
fn test_buddhist_roundtrip_near_rd_zero() {
for i in -10000..=10000 {
let rd = RataDie::new(i);
let iso1 = Iso::iso_from_fixed(rd);
let buddhist = iso1.to_calendar(Buddhist);
let iso2 = buddhist.to_calendar(Iso);
let result = Iso::fixed_from_iso(iso2.inner);
assert_eq!(rd, result);
}
}
#[test]
fn test_buddhist_roundtrip_near_epoch() {
// Buddhist epoch start RD: -198326
for i in -208326..=-188326 {
let rd = RataDie::new(i);
let iso1 = Iso::iso_from_fixed(rd);
let buddhist = iso1.to_calendar(Buddhist);
let iso2 = buddhist.to_calendar(Iso);
let result = Iso::fixed_from_iso(iso2.inner);
assert_eq!(rd, result);
}
}
#[test]
fn test_buddhist_directionality_near_rd_zero() {
for i in -100..=100 {
for j in -100..=100 {
let iso_i = Iso::iso_from_fixed(RataDie::new(i));
let iso_j = Iso::iso_from_fixed(RataDie::new(j));
let buddhist_i = Date::new_from_iso(iso_i, Buddhist);
let buddhist_j = Date::new_from_iso(iso_j, Buddhist);
assert_eq!(
i.cmp(&j),
iso_i.cmp(&iso_j),
"ISO directionality inconsistent with directionality for i: {i}, j: {j}"
);
assert_eq!(
i.cmp(&j),
buddhist_i.cmp(&buddhist_j),
"Buddhist directionality inconsistent with directionality for i: {i}, j: {j}"
);
}
}
}
#[test]
fn test_buddhist_directionality_near_epoch() {
// Buddhist epoch start RD: -198326
for i in -198426..=-198226 {
for j in -198426..=-198226 {
let iso_i = Iso::iso_from_fixed(RataDie::new(i));
let iso_j = Iso::iso_from_fixed(RataDie::new(j));
let buddhist_i = Date::new_from_iso(iso_i, Buddhist);
let buddhist_j = Date::new_from_iso(iso_j, Buddhist);
assert_eq!(
i.cmp(&j),
iso_i.cmp(&iso_j),
"ISO directionality inconsistent with directionality for i: {i}, j: {j}"
);
assert_eq!(
i.cmp(&j),
buddhist_i.cmp(&buddhist_j),
"Buddhist directionality inconsistent with directionality for i: {i}, j: {j}"
);
}
}
}
#[derive(Debug)]
struct TestCase {
iso_year: i32,
iso_month: u8,
iso_day: u8,
buddhist_year: i32,
buddhist_month: u8,
buddhist_day: u8,
}
fn check_test_case(case: TestCase) {
let iso_year = case.iso_year;
let iso_month = case.iso_month;
let iso_day = case.iso_day;
let buddhist_year = case.buddhist_year;
let buddhist_month = case.buddhist_month;
let buddhist_day = case.buddhist_day;
let iso1 = Date::try_new_iso_date(iso_year, iso_month, iso_day).unwrap();
let buddhist1 = iso1.to_calendar(Buddhist);
assert_eq!(
buddhist1.year().number,
buddhist_year,
"Iso -> Buddhist year check failed for case: {case:?}"
);
assert_eq!(
buddhist1.month().ordinal,
buddhist_month as u32,
"Iso -> Buddhist month check failed for case: {case:?}"
);
assert_eq!(
buddhist1.day_of_month().0,
buddhist_day as u32,
"Iso -> Buddhist day check failed for case: {case:?}"
);
let buddhist2 =
Date::try_new_buddhist_date(buddhist_year, buddhist_month, buddhist_day).unwrap();
let iso2 = buddhist2.to_calendar(Iso);
assert_eq!(
iso2.year().number,
iso_year,
"Buddhist -> Iso year check failed for case: {case:?}"
);
assert_eq!(
iso2.month().ordinal,
iso_month as u32,
"Buddhist -> Iso month check failed for case: {case:?}"
);
assert_eq!(
iso2.day_of_month().0,
iso_day as u32,
"Buddhist -> Iso day check failed for case: {case:?}"
);
}
#[test]
fn test_buddhist_cases_near_rd_zero() {
let cases = [
TestCase {
iso_year: -100,
iso_month: 2,
iso_day: 15,
buddhist_year: 443,
buddhist_month: 2,
buddhist_day: 15,
},
TestCase {
iso_year: -3,
iso_month: 10,
iso_day: 29,
buddhist_year: 540,
buddhist_month: 10,
buddhist_day: 29,
},
TestCase {
iso_year: 0,
iso_month: 12,
iso_day: 31,
buddhist_year: 543,
buddhist_month: 12,
buddhist_day: 31,
},
TestCase {
iso_year: 1,
iso_month: 1,
iso_day: 1,
buddhist_year: 544,
buddhist_month: 1,
buddhist_day: 1,
},
TestCase {
iso_year: 4,
iso_month: 2,
iso_day: 29,
buddhist_year: 547,
buddhist_month: 2,
buddhist_day: 29,
},
];
for case in cases {
check_test_case(case);
}
}
#[test]
fn test_buddhist_cases_near_epoch() {
// 1 BE = 543 BCE = -542 ISO
let cases = [
TestCase {
iso_year: -554,
iso_month: 12,
iso_day: 31,
buddhist_year: -11,
buddhist_month: 12,
buddhist_day: 31,
},
TestCase {
iso_year: -553,
iso_month: 1,
iso_day: 1,
buddhist_year: -10,
buddhist_month: 1,
buddhist_day: 1,
},
TestCase {
iso_year: -544,
iso_month: 8,
iso_day: 31,
buddhist_year: -1,
buddhist_month: 8,
buddhist_day: 31,
},
TestCase {
iso_year: -543,
iso_month: 5,
iso_day: 12,
buddhist_year: 0,
buddhist_month: 5,
buddhist_day: 12,
},
TestCase {
iso_year: -543,
iso_month: 12,
iso_day: 31,
buddhist_year: 0,
buddhist_month: 12,
buddhist_day: 31,
},
TestCase {
iso_year: -542,
iso_month: 1,
iso_day: 1,
buddhist_year: 1,
buddhist_month: 1,
buddhist_day: 1,
},
TestCase {
iso_year: -541,
iso_month: 7,
iso_day: 9,
buddhist_year: 2,
buddhist_month: 7,
buddhist_day: 9,
},
];
for case in cases {
check_test_case(case);
}
}
}

95
third_party/rust/icu_calendar/src/calendar.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,95 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use crate::any_calendar::AnyCalendarKind;
use crate::{types, CalendarError, Date, DateDuration, DateDurationUnit, Iso};
use core::fmt;
/// A calendar implementation
///
/// Only implementors of [`Calendar`] should care about these methods, in general users of
/// these calendars should use the methods on [`Date`] instead.
///
/// Individual [`Calendar`] implementations may have inherent utility methods
/// allowing for direct construction, etc.
///
/// For ICU4X 1.0, implementing this trait or calling methods directly is considered
/// unstable and prone to change, especially for `offset_date()` and `until()`.
pub trait Calendar {
/// The internal type used to represent dates
type DateInner: PartialEq + Eq + Clone + fmt::Debug;
/// Construct a date from era/month codes and fields
fn date_from_codes(
&self,
era: types::Era,
year: i32,
month_code: types::MonthCode,
day: u8,
) -> Result<Self::DateInner, CalendarError>;
/// Construct the date from an ISO date
fn date_from_iso(&self, iso: Date<Iso>) -> Self::DateInner;
/// Obtain an ISO date from this date
fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso>;
// fn validate_date(&self, e: Era, y: Year, m: MonthCode, d: Day) -> bool;
// // similar validators for YearMonth, etc
// fn is_leap<A: AsCalendar<Calendar = Self>>(&self, date: &Date<A>) -> bool;
/// Count the number of months in a given year, specified by providing a date
/// from that year
fn months_in_year(&self, date: &Self::DateInner) -> u8;
/// Count the number of days in a given year, specified by providing a date
/// from that year
fn days_in_year(&self, date: &Self::DateInner) -> u16;
/// Count the number of days in a given month, specified by providing a date
/// from that year/month
fn days_in_month(&self, date: &Self::DateInner) -> u8;
/// Calculate the day of the week and return it
fn day_of_week(&self, date: &Self::DateInner) -> types::IsoWeekday {
self.date_to_iso(date).day_of_week()
}
// fn week_of_year(&self, date: &Self::DateInner) -> u8;
#[doc(hidden)] // unstable
/// Add `offset` to `date`
fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>);
#[doc(hidden)] // unstable
/// Calculate `date2 - date` as a duration
///
/// `calendar2` is the calendar object associated with `date2`. In case the specific calendar objects
/// differ on data, the data for the first calendar is used, and `date2` may be converted if necessary.
fn until(
&self,
date1: &Self::DateInner,
date2: &Self::DateInner,
calendar2: &Self,
largest_unit: DateDurationUnit,
smallest_unit: DateDurationUnit,
) -> DateDuration<Self>;
/// Obtain a name for the calendar for debug printing
fn debug_name(&self) -> &'static str;
// fn since(&self, from: &Date<Self>, to: &Date<Self>) -> Duration<Self>, Error;
/// The calendar-specific year represented by `date`
fn year(&self, date: &Self::DateInner) -> types::FormattableYear;
/// Calculate if a date is in a leap year
fn is_in_leap_year(&self, date: &Self::DateInner) -> bool;
/// The calendar-specific month represented by `date`
fn month(&self, date: &Self::DateInner) -> types::FormattableMonth;
/// The calendar-specific day-of-month represented by `date`
fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth;
/// Information of the day of the year
fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo;
/// The [`AnyCalendarKind`] corresponding to this calendar,
/// if one exists. Implementors outside of `icu_calendar` should return `None`
fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
None
}
}

336
third_party/rust/icu_calendar/src/calendar_arithmetic.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,336 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use crate::{types, Calendar, CalendarError, DateDuration, DateDurationUnit};
use core::convert::TryInto;
use core::marker::PhantomData;
use tinystr::tinystr;
// Note: The Ord/PartialOrd impls can be derived because the fields are in the correct order.
#[derive(Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
#[allow(clippy::exhaustive_structs)] // this type is stable
pub struct ArithmeticDate<C> {
pub year: i32,
/// 1-based month of year
pub month: u8,
/// 1-based day of month
pub day: u8,
marker: PhantomData<C>,
}
impl<C> Copy for ArithmeticDate<C> {}
impl<C> Clone for ArithmeticDate<C> {
fn clone(&self) -> Self {
*self
}
}
/// Maximum number of iterations when iterating through the days of a month; can be increased if necessary
#[allow(dead_code)] // TODO: Remove dead code tag after use
pub(crate) const MAX_ITERS_FOR_DAYS_OF_MONTH: u8 = 33;
pub trait CalendarArithmetic: Calendar {
fn month_days(year: i32, month: u8) -> u8;
fn months_for_every_year(year: i32) -> u8;
fn is_leap_year(year: i32) -> bool;
fn last_month_day_in_year(year: i32) -> (u8, u8);
/// Calculate the days in a given year
/// Can be overridden with simpler implementations for solar calendars
/// (typically, 366 in leap, 365 otherwise) Leave this as the default
/// for lunar calendars
///
/// The name has `provided` in it to avoid clashes with Calendar
fn days_in_provided_year(year: i32) -> u16 {
let months_in_year = Self::months_for_every_year(year);
let mut days: u16 = 0;
for month in 1..=months_in_year {
days += Self::month_days(year, month) as u16;
}
days
}
}
impl<C: CalendarArithmetic> ArithmeticDate<C> {
/// Create a new `ArithmeticDate` without checking that `month` and `day` are in bounds.
#[inline]
pub const fn new_unchecked(year: i32, month: u8, day: u8) -> Self {
ArithmeticDate {
year,
month,
day,
marker: PhantomData,
}
}
#[inline]
pub fn min_date() -> Self {
ArithmeticDate {
year: i32::MIN,
month: 1,
day: 1,
marker: PhantomData,
}
}
#[inline]
pub fn max_date() -> Self {
let year = i32::MAX;
let (month, day) = C::last_month_day_in_year(year);
ArithmeticDate {
year: i32::MAX,
month,
day,
marker: PhantomData,
}
}
#[inline]
fn offset_days(&mut self, mut day_offset: i32) {
while day_offset != 0 {
let month_days = C::month_days(self.year, self.month);
if self.day as i32 + day_offset > month_days as i32 {
self.offset_months(1);
day_offset -= month_days as i32;
} else if self.day as i32 + day_offset < 1 {
self.offset_months(-1);
day_offset += C::month_days(self.year, self.month) as i32;
} else {
self.day = (self.day as i32 + day_offset) as u8;
day_offset = 0;
}
}
}
#[inline]
fn offset_months(&mut self, mut month_offset: i32) {
while month_offset != 0 {
let year_months = C::months_for_every_year(self.year);
if self.month as i32 + month_offset > year_months as i32 {
self.year += 1;
month_offset -= year_months as i32;
} else if self.month as i32 + month_offset < 1 {
self.year -= 1;
month_offset += C::months_for_every_year(self.year) as i32;
} else {
self.month = (self.month as i32 + month_offset) as u8;
month_offset = 0
}
}
}
#[inline]
pub fn offset_date(&mut self, offset: DateDuration<C>) {
// For offset_date to work with lunar calendars, need to handle an edge case where the original month is not valid in the future year.
self.year += offset.years;
self.offset_months(offset.months);
let day_offset = offset.days + offset.weeks * 7 + self.day as i32 - 1;
self.day = 1;
self.offset_days(day_offset);
}
#[inline]
pub fn until(
&self,
date2: ArithmeticDate<C>,
_largest_unit: DateDurationUnit,
_smaller_unit: DateDurationUnit,
) -> DateDuration<C> {
DateDuration::new(
self.year - date2.year,
self.month as i32 - date2.month as i32,
0,
self.day as i32 - date2.day as i32,
)
}
#[inline]
pub fn days_in_year(&self) -> u16 {
C::days_in_provided_year(self.year)
}
#[inline]
pub fn months_in_year(&self) -> u8 {
C::months_for_every_year(self.year)
}
#[inline]
pub fn days_in_month(&self) -> u8 {
C::month_days(self.year, self.month)
}
#[inline]
pub fn day_of_year(&self) -> u16 {
let mut day_of_year = 0;
for month in 1..self.month {
day_of_year += C::month_days(self.year, month) as u16;
}
day_of_year + (self.day as u16)
}
#[inline]
pub fn date_from_year_day(year: i32, year_day: u32) -> ArithmeticDate<C> {
let mut month = 1;
let mut day = year_day as i32;
while month <= C::months_for_every_year(year) {
let month_days = C::month_days(year, month) as i32;
if day <= month_days {
break;
} else {
day -= month_days;
month += 1;
}
}
debug_assert!(day <= C::month_days(year, month) as i32);
#[allow(clippy::unwrap_used)]
// The day is expected to be within the range of month_days of C
ArithmeticDate {
year,
month,
day: day.try_into().unwrap_or(0),
marker: PhantomData,
}
}
#[inline]
pub fn day_of_month(&self) -> types::DayOfMonth {
types::DayOfMonth(self.day.into())
}
/// The [`types::FormattableMonth`] for the current month (with month code) for a solar calendar
/// Lunar calendars should not use this method and instead manually implement a month code
/// resolver.
/// Originally "solar_month" but renamed because it can be used for some lunar calendars
///
/// Returns "und" if run with months that are out of bounds for the current
/// calendar.
#[inline]
pub fn month(&self) -> types::FormattableMonth {
let code = match self.month {
a if a > C::months_for_every_year(self.year) => tinystr!(4, "und"),
1 => tinystr!(4, "M01"),
2 => tinystr!(4, "M02"),
3 => tinystr!(4, "M03"),
4 => tinystr!(4, "M04"),
5 => tinystr!(4, "M05"),
6 => tinystr!(4, "M06"),
7 => tinystr!(4, "M07"),
8 => tinystr!(4, "M08"),
9 => tinystr!(4, "M09"),
10 => tinystr!(4, "M10"),
11 => tinystr!(4, "M11"),
12 => tinystr!(4, "M12"),
13 => tinystr!(4, "M13"),
_ => tinystr!(4, "und"),
};
types::FormattableMonth {
ordinal: self.month as u32,
code: types::MonthCode(code),
}
}
/// Construct a new arithmetic date from a year, month code, and day, bounds checking
/// the month and day
/// Originally (new_from_solar_codes) but renamed because it works for some lunar calendars
pub fn new_from_codes<C2: Calendar>(
// Separate type since the debug_name() impl may differ when DateInner types
// are nested (e.g. in GregorianDateInner)
cal: &C2,
year: i32,
month_code: types::MonthCode,
day: u8,
) -> Result<Self, CalendarError> {
let month = if let Some((ordinal, false)) = month_code.parsed() {
ordinal
} else {
return Err(CalendarError::UnknownMonthCode(
month_code.0,
cal.debug_name(),
));
};
if month > C::months_for_every_year(year) {
return Err(CalendarError::UnknownMonthCode(
month_code.0,
cal.debug_name(),
));
}
let max_day = C::month_days(year, month);
if day > max_day {
return Err(CalendarError::Overflow {
field: "day",
max: max_day as usize,
});
}
Ok(Self::new_unchecked(year, month, day))
}
/// Construct a new arithmetic date from a year, month ordinal, and day, bounds checking
/// the month and day
/// Originally (new_from_solar_ordinals) but renamed because it works for some lunar calendars
pub fn new_from_ordinals(year: i32, month: u8, day: u8) -> Result<Self, CalendarError> {
let max_month = C::months_for_every_year(year);
if month > max_month {
return Err(CalendarError::Overflow {
field: "month",
max: max_month as usize,
});
}
let max_day = C::month_days(year, month);
if day > max_day {
return Err(CalendarError::Overflow {
field: "day",
max: max_day as usize,
});
}
Ok(Self::new_unchecked(year, month, day))
}
/// This fn currently just calls [`new_from_ordinals`], but exists separately for
/// lunar calendars in case different logic needs to be implemented later.
pub fn new_from_lunar_ordinals(year: i32, month: u8, day: u8) -> Result<Self, CalendarError> {
Self::new_from_ordinals(year, month, day)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Iso;
#[test]
fn test_ord() {
let dates_in_order = [
ArithmeticDate::<Iso>::new_unchecked(-10, 1, 1),
ArithmeticDate::<Iso>::new_unchecked(-10, 1, 2),
ArithmeticDate::<Iso>::new_unchecked(-10, 2, 1),
ArithmeticDate::<Iso>::new_unchecked(-1, 1, 1),
ArithmeticDate::<Iso>::new_unchecked(-1, 1, 2),
ArithmeticDate::<Iso>::new_unchecked(-1, 2, 1),
ArithmeticDate::<Iso>::new_unchecked(0, 1, 1),
ArithmeticDate::<Iso>::new_unchecked(0, 1, 2),
ArithmeticDate::<Iso>::new_unchecked(0, 2, 1),
ArithmeticDate::<Iso>::new_unchecked(1, 1, 1),
ArithmeticDate::<Iso>::new_unchecked(1, 1, 2),
ArithmeticDate::<Iso>::new_unchecked(1, 2, 1),
ArithmeticDate::<Iso>::new_unchecked(10, 1, 1),
ArithmeticDate::<Iso>::new_unchecked(10, 1, 2),
ArithmeticDate::<Iso>::new_unchecked(10, 2, 1),
];
for (i, i_date) in dates_in_order.iter().enumerate() {
for (j, j_date) in dates_in_order.iter().enumerate() {
let result1 = i_date.cmp(j_date);
let result2 = j_date.cmp(i_date);
assert_eq!(result1.reverse(), result2);
assert_eq!(i.cmp(&j), i_date.cmp(j_date));
}
}
}
}

1041
third_party/rust/icu_calendar/src/chinese.rs поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

563
third_party/rust/icu_calendar/src/chinese_based.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,563 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
//! This module contains types and traits for use in the Chinese traditional lunar calendar,
//! as well as in related and derived calendars such as the Korean and Vietnamese lunar calendars.
//!
//! ```rust
//! use icu::calendar::{chinese::Chinese, Date, Iso};
//!
//! let iso_date = Date::try_new_iso_date(2023, 6, 23).unwrap();
//! let chinese_date =
//! Date::new_from_iso(iso_date, Chinese::new_always_calculating());
//!
//! assert_eq!(chinese_date.year().number, 4660);
//! assert_eq!(chinese_date.year().related_iso, Some(2023));
//! assert_eq!(chinese_date.year().cyclic.unwrap().get(), 40);
//! assert_eq!(chinese_date.month().ordinal, 6);
//! assert_eq!(chinese_date.day_of_month().0, 6);
//! ```
use crate::{
calendar_arithmetic::{ArithmeticDate, CalendarArithmetic},
types::MonthCode,
CalendarError, Iso,
};
use calendrical_calculations::chinese_based::{self, ChineseBased, YearBounds};
use calendrical_calculations::rata_die::RataDie;
use core::num::NonZeroU8;
/// The trait ChineseBased is used by Chinese-based calendars to perform computations shared by such calendar.
///
/// For an example of how to use this trait, see `impl ChineseBasedWithDataLoading for Chinese` in [`Chinese`].
pub(crate) trait ChineseBasedWithDataLoading: CalendarArithmetic {
type CB: ChineseBased;
/// Get the compiled const data for a ChineseBased calendar; can return `None` if the given year
/// does not correspond to any compiled data.
fn get_compiled_data_for_year(extended_year: i32) -> Option<ChineseBasedCompiledData>;
}
/// Chinese-based calendars define DateInner as a calendar-specific struct wrapping ChineseBasedDateInner.
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)]
pub(crate) struct ChineseBasedDateInner<C>(
pub(crate) ArithmeticDate<C>,
pub(crate) ChineseBasedYearInfo,
);
// we want these impls without the `C: Copy/Clone` bounds
impl<C> Copy for ChineseBasedDateInner<C> {}
impl<C> Clone for ChineseBasedDateInner<C> {
fn clone(&self) -> Self {
*self
}
}
/// A `ChineseBasedDateInner` has additional information about the year corresponding to the Inner;
/// if there is available data for that year, the ChineseBasedYearInfo will be in the form of `Data`,
/// with a `ChineseBasedCompiledData` struct which contains more information; otherwise, a `Cache`
/// with a `ChineseBasedCache`, which contains less information, but is faster to compute.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub(crate) enum ChineseBasedYearInfo {
Cache(ChineseBasedCache),
Data(ChineseBasedCompiledData),
}
impl ChineseBasedYearInfo {
pub(crate) fn get_new_year(&self) -> RataDie {
match self {
Self::Cache(cache) => cache.new_year,
Self::Data(data) => data.new_year,
}
}
pub(crate) fn get_next_new_year(&self) -> RataDie {
match self {
Self::Cache(cache) => cache.next_new_year,
Self::Data(data) => data.next_new_year(),
}
}
pub(crate) fn get_leap_month(&self) -> Option<NonZeroU8> {
match self {
Self::Cache(cache) => cache.leap_month,
Self::Data(data) => data.leap_month,
}
}
pub(crate) fn get_year_info<C: ChineseBasedWithDataLoading>(year: i32) -> ChineseBasedYearInfo {
if let Some(data) = C::get_compiled_data_for_year(year) {
Self::Data(data)
} else {
Self::Cache(ChineseBasedDateInner::<C>::compute_cache(year))
}
}
}
/// A caching struct used to store information for ChineseBasedDates
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub(crate) struct ChineseBasedCache {
pub(crate) new_year: RataDie,
pub(crate) next_new_year: RataDie,
pub(crate) leap_month: Option<NonZeroU8>,
}
/// The struct containing compiled ChineseData
///
/// Bit structure:
///
/// ```text
/// Bit: 7 6 5 4 3 2 1 0
/// Byte 0: [new year offset] | [ month lengths ..
/// Byte 1: ....... month lengths .......
/// Byte 2: ... ] | [ leap month index ]
/// ```
///
/// Where the New Year Offset is the offset from ISO Jan 21 of that year for Chinese New Year,
/// the month lengths are stored as 1 = 30, 0 = 29 for each month including the leap month.
#[derive(Debug, Copy, Clone)]
pub(crate) struct PackedChineseBasedCompiledData(pub(crate) u8, pub(crate) u8, pub(crate) u8);
impl PackedChineseBasedCompiledData {
pub(crate) fn unpack(self, related_iso: i32) -> ChineseBasedCompiledData {
fn month_length(is_long: bool) -> u16 {
if is_long {
30
} else {
29
}
}
let new_year_offset = ((self.0 & 0b11111000) >> 3) as u16;
let new_year =
Iso::fixed_from_iso(Iso::iso_from_year_day(related_iso, 21 + new_year_offset).inner);
let mut last_day_of_month: [u16; 13] = [0; 13];
let mut months_total = 0;
months_total += month_length(self.0 & 0b100 != 0);
last_day_of_month[0] = months_total;
months_total += month_length(self.0 & 0b010 != 0);
last_day_of_month[1] = months_total;
months_total += month_length(self.0 & 0b001 != 0);
last_day_of_month[2] = months_total;
months_total += month_length(self.1 & 0b10000000 != 0);
last_day_of_month[3] = months_total;
months_total += month_length(self.1 & 0b01000000 != 0);
last_day_of_month[4] = months_total;
months_total += month_length(self.1 & 0b00100000 != 0);
last_day_of_month[5] = months_total;
months_total += month_length(self.1 & 0b00010000 != 0);
last_day_of_month[6] = months_total;
months_total += month_length(self.1 & 0b00001000 != 0);
last_day_of_month[7] = months_total;
months_total += month_length(self.1 & 0b00000100 != 0);
last_day_of_month[8] = months_total;
months_total += month_length(self.1 & 0b00000010 != 0);
last_day_of_month[9] = months_total;
months_total += month_length(self.1 & 0b00000001 != 0);
last_day_of_month[10] = months_total;
months_total += month_length(self.2 & 0b10000000 != 0);
last_day_of_month[11] = months_total;
let leap_month_bits = self.2 & 0b00111111;
// Leap month is if the sentinel bit is set
if leap_month_bits != 0 {
months_total += month_length(self.2 & 0b01000000 != 0);
}
// In non-leap months, `last_day_of_month` will have identical entries at 12 and 11
last_day_of_month[12] = months_total;
// Will automatically set to None when the leap month bits are zero
let leap_month = NonZeroU8::new(leap_month_bits);
ChineseBasedCompiledData {
new_year,
last_day_of_month,
leap_month,
}
}
}
/// A data struct used to load and use information for a set of ChineseBasedDates
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub(crate) struct ChineseBasedCompiledData {
pub(crate) new_year: RataDie,
/// last_day_of_month[12] = last_day_of_month[11] in non-leap years
/// These days are 1-indexed: so the last day of month for a 30-day 一月 is 30
/// The array itself is zero-indexed, be careful passing it self.0.month!
last_day_of_month: [u16; 13],
///
pub(crate) leap_month: Option<NonZeroU8>,
}
impl ChineseBasedCompiledData {
fn next_new_year(self) -> RataDie {
self.new_year + i64::from(self.last_day_of_month[12])
}
/// The last day of year in the previous month.
/// `month` is 1-indexed, and the returned value is also
/// a 1-indexed day of year
///
/// Will be zero for the first month as the last day of the previous month
/// is not in this year
fn last_day_of_previous_month(self, month: u8) -> u16 {
debug_assert!((1..=13).contains(&month), "Month out of bounds!");
// Get the last day of the previous month.
// Since `month` is 1-indexed, this needs to subtract *two* to get to the right index of the array
if month < 2 {
0
} else {
self.last_day_of_month
.get(usize::from(month - 2))
.copied()
.unwrap_or(0)
}
}
/// The last day of year in the current month.
/// `month` is 1-indexed, and the returned value is also
/// a 1-indexed day of year
///
/// Will be zero for the first month as the last day of the previous month
/// is not in this year
fn last_day_of_month(self, month: u8) -> u16 {
debug_assert!((1..=13).contains(&month), "Month out of bounds!");
// Get the last day of the previous month.
// Since `month` is 1-indexed, this needs to subtract one
self.last_day_of_month
.get(usize::from(month - 1))
.copied()
.unwrap_or(0)
}
fn days_in_month(self, month: u8) -> u8 {
let ret =
u8::try_from(self.last_day_of_month(month) - self.last_day_of_previous_month(month));
debug_assert!(ret.is_ok(), "Month too big!");
ret.unwrap_or(30)
}
}
impl<C: ChineseBasedWithDataLoading> ChineseBasedDateInner<C> {
/// Given a 1-indexed chinese extended year, fetch its data from the cache.
///
/// If the actual year data that was fetched is for a different year, update the getter year
fn get_compiled_data_for_year_helper(
date: RataDie,
getter_year: &mut i32,
) -> Option<ChineseBasedCompiledData> {
let data_option = C::get_compiled_data_for_year(*getter_year);
// todo we should be able to do this without unpacking
if let Some(data) = data_option {
if date < data.new_year {
*getter_year -= 1;
C::get_compiled_data_for_year(*getter_year)
} else if date >= data.next_new_year() {
*getter_year += 1;
C::get_compiled_data_for_year(*getter_year)
} else {
data_option
}
} else {
None
}
}
/// Get a ChineseBasedDateInner from a fixed date and the cache/extended year associated with it
fn chinese_based_date_from_cached(
date: RataDie,
data: ChineseBasedCompiledData,
extended_year: i32,
) -> ChineseBasedDateInner<C> {
debug_assert!(
date < data.next_new_year(),
"Stored date {date:?} out of bounds!"
);
// 1-indexed day of year
let day_of_year = u16::try_from(date - data.new_year + 1);
debug_assert!(day_of_year.is_ok(), "Somehow got a very large year in data");
let day_of_year = day_of_year.unwrap_or(1);
let mut month = 1;
// todo perhaps use a binary search
for iter_month in 1..=13 {
month = iter_month;
if data.last_day_of_month(iter_month) >= day_of_year {
break;
}
}
debug_assert!((1..=13).contains(&month), "Month out of bounds!");
debug_assert!(
month < 13 || data.leap_month.is_some(),
"Cannot have 13 months in a non-leap year!"
);
let day_before_month_start = data.last_day_of_previous_month(month);
let day_of_month = day_of_year - day_before_month_start;
let day_of_month = u8::try_from(day_of_month);
debug_assert!(day_of_month.is_ok(), "Month too big!");
let day_of_month = day_of_month.unwrap_or(1);
// This can use `new_unchecked` because this function is only ever called from functions which
// generate the year, month, and day; therefore, there should never be a situation where
// creating this ArithmeticDate would fail, since the same algorithms used to generate the ymd
// are also used to check for valid ymd.
ChineseBasedDateInner(
ArithmeticDate::new_unchecked(extended_year, month, day_of_month),
ChineseBasedYearInfo::Data(data),
)
}
/// Get a ChineseBasedDateInner from a fixed date, with the related ISO year
pub(crate) fn chinese_based_date_from_fixed(
date: RataDie,
iso_year: i32,
) -> ChineseBasedDateInner<C> {
// Get the 1-indexed Chinese extended year, used for fetching data from the cache
let epoch_as_iso = Iso::iso_from_fixed(C::CB::EPOCH);
let mut getter_year = iso_year - epoch_as_iso.year().number + 1;
let data_option = Self::get_compiled_data_for_year_helper(date, &mut getter_year);
if let Some(data) = data_option {
// cache fetch successful, getter year is just the regular extended year
Self::chinese_based_date_from_cached(date, data, getter_year)
} else {
let date = chinese_based::chinese_based_date_from_fixed::<C::CB>(date);
let cache = ChineseBasedCache {
new_year: date.year_bounds.new_year,
next_new_year: date.year_bounds.next_new_year,
leap_month: date.leap_month,
};
// This can use `new_unchecked` because this function is only ever called from functions which
// generate the year, month, and day; therefore, there should never be a situation where
// creating this ArithmeticDate would fail, since the same algorithms used to generate the ymd
// are also used to check for valid ymd.
ChineseBasedDateInner(
ArithmeticDate::new_unchecked(date.year, date.month, date.day),
ChineseBasedYearInfo::Cache(cache),
)
}
}
/// Get a RataDie from a ChineseBasedDateInner
///
/// This finds the RataDie of the new year of the year given, then finds the RataDie of the new moon
/// (beginning of the month) of the month given, then adds the necessary number of days.
pub(crate) fn fixed_from_chinese_based_date_inner(date: ChineseBasedDateInner<C>) -> RataDie {
let first_day_of_year = date.1.get_new_year();
let day_of_year = date.day_of_year(); // 1 indexed
first_day_of_year + i64::from(day_of_year) - 1
}
/// Create a new arithmetic date from a year, month ordinal, and day with bounds checking; returns the
/// result of creating this arithmetic date, as well as a ChineseBasedYearInfo - either the one passed in
/// optionally as an argument, or a new ChineseBasedYearInfo for the given year, month, and day args.
pub(crate) fn new_from_ordinals(
year: i32,
month: u8,
day: u8,
year_info: &ChineseBasedYearInfo,
) -> Result<ArithmeticDate<C>, CalendarError> {
let max_month = Self::months_in_year_with_info(year_info);
if !(1..=max_month).contains(&month) {
return Err(CalendarError::Overflow {
field: "month",
max: max_month as usize,
});
}
let max_day = if let ChineseBasedYearInfo::Data(data) = year_info {
data.days_in_month(month)
} else {
chinese_based::days_in_month::<C::CB>(month, year_info.get_new_year(), None).0
};
if day > max_day {
return Err(CalendarError::Overflow {
field: "day",
max: max_day as usize,
});
}
// Unchecked can be used because month and day are already checked in this fn
Ok(ArithmeticDate::<C>::new_unchecked(year, month, day))
}
/// Call `months_in_year_with_info` on a `ChineseBasedDateInner`
pub(crate) fn months_in_year_inner(&self) -> u8 {
Self::months_in_year_with_info(&self.1)
}
/// Return the number of months in a given year, which is 13 in a leap year, and 12 in a common year.
/// Also takes a `ChineseBasedCache` argument.
fn months_in_year_with_info(year_info: &ChineseBasedYearInfo) -> u8 {
if year_info.get_leap_month().is_some() {
13
} else {
12
}
}
/// Calls `days_in_month` on an instance of ChineseBasedDateInner
pub(crate) fn days_in_month_inner(&self) -> u8 {
if let ChineseBasedYearInfo::Data(data) = self.1 {
data.days_in_month(self.0.month)
} else {
chinese_based::days_in_month::<C::CB>(self.0.month, self.1.get_new_year(), None).0
}
}
pub(crate) fn fixed_mid_year_from_year(year: i32) -> RataDie {
chinese_based::fixed_mid_year_from_year::<C::CB>(year)
}
/// Calls days_in_year on an instance of ChineseBasedDateInner
pub(crate) fn days_in_year_inner(&self) -> u16 {
let next_new_year = self.1.get_next_new_year();
let new_year = self.1.get_new_year();
YearBounds {
new_year,
next_new_year,
}
.count_days()
}
/// Calculate the number of days in the year so far for a ChineseBasedDate;
/// similar to `CalendarArithmetic::day_of_year`
pub(crate) fn day_of_year(&self) -> u16 {
let days_until_month = if let ChineseBasedYearInfo::Data(data) = self.1 {
data.last_day_of_previous_month(self.0.month)
} else {
let new_year = self.1.get_new_year();
chinese_based::days_until_month::<C::CB>(new_year, self.0.month)
};
days_until_month + u16::from(self.0.day)
}
/// Compute a `ChineseBasedCache` from a ChineseBased year
pub(crate) fn compute_cache(year: i32) -> ChineseBasedCache {
let mid_year = Self::fixed_mid_year_from_year(year);
let year_bounds = YearBounds::compute::<C::CB>(mid_year);
let YearBounds {
new_year,
next_new_year,
..
} = year_bounds;
let is_leap_year = year_bounds.is_leap();
let leap_month = if is_leap_year {
// This doesn't need to be checked for None because `get_leap_month_from_new_year`
// will always return a value between 1..=13
NonZeroU8::new(chinese_based::get_leap_month_from_new_year::<C::CB>(
new_year,
))
} else {
None
};
ChineseBasedCache {
new_year,
next_new_year,
leap_month,
}
}
}
impl<C: ChineseBasedWithDataLoading> CalendarArithmetic for C {
fn month_days(year: i32, month: u8) -> u8 {
chinese_based::month_days::<C::CB>(year, month)
}
/// Returns the number of months in a given year, which is 13 in a leap year, and 12 in a common year.
fn months_for_every_year(year: i32) -> u8 {
if Self::is_leap_year(year) {
13
} else {
12
}
}
/// Returns true if the given year is a leap year, and false if not.
fn is_leap_year(year: i32) -> bool {
if let Some(data) = C::get_compiled_data_for_year(year) {
data.leap_month.is_some()
} else {
chinese_based::is_leap_year::<C::CB>(year)
}
}
/// Returns the (month, day) of the last day in a Chinese year (the day before Chinese New Year).
/// The last month in a year will always be 12 in a common year or 13 in a leap year. The day is
/// determined by finding the day immediately before the next new year and calculating the number
/// of days since the last new moon (beginning of the last month in the year).
fn last_month_day_in_year(year: i32) -> (u8, u8) {
if let Some(data) = C::get_compiled_data_for_year(year) {
if data.leap_month.is_some() {
(13, data.days_in_month(13))
} else {
(12, data.days_in_month(12))
}
} else {
chinese_based::last_month_day_in_year::<C::CB>(year)
}
}
fn days_in_provided_year(year: i32) -> u16 {
if let Some(data) = C::get_compiled_data_for_year(year) {
data.last_day_of_month(13)
} else {
chinese_based::days_in_provided_year::<C::CB>(year)
}
}
}
/// Get the ordinal lunar month from a code for chinese-based calendars.
pub(crate) fn chinese_based_ordinal_lunar_month_from_code(
code: MonthCode,
year_info: ChineseBasedYearInfo,
) -> Option<u8> {
let leap_month = if let Some(leap) = year_info.get_leap_month() {
leap.get()
} else {
// 14 is a sentinel value, greater than all other months, for the purpose of computation only;
// it is impossible to actually have 14 months in a year.
14
};
if code.0.len() < 3 {
return None;
}
let bytes = code.0.all_bytes();
if bytes[0] != b'M' {
return None;
}
if code.0.len() == 4 && bytes[3] != b'L' {
return None;
}
let mut unadjusted = 0;
if bytes[1] == b'0' {
if bytes[2] >= b'1' && bytes[2] <= b'9' {
unadjusted = bytes[2] - b'0';
}
} else if bytes[1] == b'1' && bytes[2] >= b'0' && bytes[2] <= b'2' {
unadjusted = 10 + bytes[2] - b'0';
}
if bytes[3] == b'L' {
if unadjusted + 1 != leap_month {
return None;
} else {
return Some(unadjusted + 1);
}
}
if unadjusted != 0 {
if unadjusted + 1 > leap_month {
return Some(unadjusted + 1);
} else {
return Some(unadjusted);
}
}
None
}

26
third_party/rust/icu_calendar/src/chinese_data.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,26 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
//! Compiled and compressed data for Chinese calendar years; includes `RataDie` types for the beginning of years
use crate::chinese_based::PackedChineseBasedCompiledData;
/// The minimum year present in CHINESE_DATA_ARRAY
pub(crate) const MIN_YEAR: i32 = 4660;
pub(crate) const MIN_YEAR_ISO: i32 = 2023;
/// The array of year data for Chinese years between MIN_YEAR and MAX_YEAR; currently, this array must also have
/// an entry for the year after max year, since the function for unpacking this data also uses the next entry's new year.
///
/// Each data entry consists of a `ChineseData`, which in turn is composed of three bytes of data.
/// The first 5 bits represents the offset of the Chinese New Year in the given year from Jan 21 of that year.
/// The next 13 bits are used to indicate the month lengths of the 13 months; a 1 represents 30 days, and a 0 represents 29 days;
/// if there is no 13th month, the 13th in this sequence will be set to 0, but this does not represent 29 days in this case.
/// The final 6 bits indicate which ordinal month is a leap month, or is set to zero if the year is not a leap year.
///
/// TODO: Generate this data
#[allow(clippy::unusual_byte_groupings)]
pub(crate) const CHINESE_DATA_ARRAY: [PackedChineseBasedCompiledData; 2] = [
PackedChineseBasedCompiledData(0b_00001_010, 0b_01101101, 0b_01_000011),
PackedChineseBasedCompiledData(0b_10100_010, 0b_01011011, 0b_00_000000),
];

325
third_party/rust/icu_calendar/src/coptic.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,325 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
//! This module contains types and implementations for the Coptic calendar.
//!
//! ```rust
//! use icu::calendar::{coptic::Coptic, Date, DateTime};
//!
//! // `Date` type
//! let date_iso = Date::try_new_iso_date(1970, 1, 2)
//! .expect("Failed to initialize ISO Date instance.");
//! let date_coptic = Date::new_from_iso(date_iso, Coptic);
//!
//! // `DateTime` type
//! let datetime_iso = DateTime::try_new_iso_datetime(1970, 1, 2, 13, 1, 0)
//! .expect("Failed to initialize ISO DateTime instance.");
//! let datetime_coptic = DateTime::new_from_iso(datetime_iso, Coptic);
//!
//! // `Date` checks
//! assert_eq!(date_coptic.year().number, 1686);
//! assert_eq!(date_coptic.month().ordinal, 4);
//! assert_eq!(date_coptic.day_of_month().0, 24);
//!
//! // `DateTime` type
//! assert_eq!(datetime_coptic.date.year().number, 1686);
//! assert_eq!(datetime_coptic.date.month().ordinal, 4);
//! assert_eq!(datetime_coptic.date.day_of_month().0, 24);
//! assert_eq!(datetime_coptic.time.hour.number(), 13);
//! assert_eq!(datetime_coptic.time.minute.number(), 1);
//! assert_eq!(datetime_coptic.time.second.number(), 0);
//! ```
use crate::any_calendar::AnyCalendarKind;
use crate::calendar_arithmetic::{ArithmeticDate, CalendarArithmetic};
use crate::iso::Iso;
use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime};
use calendrical_calculations::helpers::I32CastError;
use calendrical_calculations::rata_die::RataDie;
use tinystr::tinystr;
/// The [Coptic Calendar]
///
/// The [Coptic calendar] is a solar calendar used by the Coptic Orthodox Church, with twelve normal months
/// and a thirteenth small epagomenal month.
///
/// This type can be used with [`Date`] or [`DateTime`] to represent dates in this calendar.
///
/// [Coptic calendar]: https://en.wikipedia.org/wiki/Coptic_calendar
///
/// # Era codes
///
/// This calendar supports two era codes: `"bd"`, and `"ad"`, corresponding to the Before Diocletian and After Diocletian/Anno Martyrum
/// eras. 1 A.M. is equivalent to 284 C.E.
///
/// # Month codes
///
/// This calendar supports 13 solar month codes (`"M01" - "M13"`), with `"M13"` being used for the short epagomenal month
/// at the end of the year.
#[derive(Copy, Clone, Debug, Hash, Default, Eq, PartialEq, PartialOrd, Ord)]
#[allow(clippy::exhaustive_structs)] // this type is stable
pub struct Coptic;
/// The inner date type used for representing [`Date`]s of [`Coptic`]. See [`Date`] and [`Coptic`] for more details.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct CopticDateInner(pub(crate) ArithmeticDate<Coptic>);
impl CalendarArithmetic for Coptic {
fn month_days(year: i32, month: u8) -> u8 {
if (1..=12).contains(&month) {
30
} else if month == 13 {
if Self::is_leap_year(year) {
6
} else {
5
}
} else {
0
}
}
fn months_for_every_year(_: i32) -> u8 {
13
}
fn is_leap_year(year: i32) -> bool {
year % 4 == 3
}
fn last_month_day_in_year(year: i32) -> (u8, u8) {
if Self::is_leap_year(year) {
(13, 6)
} else {
(13, 5)
}
}
fn days_in_provided_year(year: i32) -> u16 {
if Self::is_leap_year(year) {
366
} else {
365
}
}
}
impl Calendar for Coptic {
type DateInner = CopticDateInner;
fn date_from_codes(
&self,
era: types::Era,
year: i32,
month_code: types::MonthCode,
day: u8,
) -> Result<Self::DateInner, CalendarError> {
let year = if era.0 == tinystr!(16, "ad") {
if year <= 0 {
return Err(CalendarError::OutOfRange);
}
year
} else if era.0 == tinystr!(16, "bd") {
if year <= 0 {
return Err(CalendarError::OutOfRange);
}
1 - year
} else {
return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
};
ArithmeticDate::new_from_codes(self, year, month_code, day).map(CopticDateInner)
}
fn date_from_iso(&self, iso: Date<Iso>) -> CopticDateInner {
let fixed_iso = Iso::fixed_from_iso(*iso.inner());
Self::coptic_from_fixed(fixed_iso)
}
fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
let fixed_coptic = Coptic::fixed_from_coptic(date.0);
Iso::iso_from_fixed(fixed_coptic)
}
fn months_in_year(&self, date: &Self::DateInner) -> u8 {
date.0.months_in_year()
}
fn days_in_year(&self, date: &Self::DateInner) -> u16 {
date.0.days_in_year()
}
fn days_in_month(&self, date: &Self::DateInner) -> u8 {
date.0.days_in_month()
}
fn day_of_week(&self, date: &Self::DateInner) -> types::IsoWeekday {
Iso.day_of_week(Coptic.date_to_iso(date).inner())
}
fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
date.0.offset_date(offset);
}
#[allow(clippy::field_reassign_with_default)]
fn until(
&self,
date1: &Self::DateInner,
date2: &Self::DateInner,
_calendar2: &Self,
_largest_unit: DateDurationUnit,
_smallest_unit: DateDurationUnit,
) -> DateDuration<Self> {
date1.0.until(date2.0, _largest_unit, _smallest_unit)
}
fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
year_as_coptic(date.0.year)
}
fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
Self::is_leap_year(date.0.year)
}
fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
date.0.month()
}
fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
date.0.day_of_month()
}
fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
let prev_year = date.0.year - 1;
let next_year = date.0.year + 1;
types::DayOfYearInfo {
day_of_year: date.0.day_of_year(),
days_in_year: date.0.days_in_year(),
prev_year: year_as_coptic(prev_year),
days_in_prev_year: Coptic::days_in_year_direct(prev_year),
next_year: year_as_coptic(next_year),
}
}
fn debug_name(&self) -> &'static str {
"Coptic"
}
fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
Some(AnyCalendarKind::Coptic)
}
}
impl Coptic {
fn fixed_from_coptic(date: ArithmeticDate<Coptic>) -> RataDie {
calendrical_calculations::coptic::fixed_from_coptic(date.year, date.month, date.day)
}
pub(crate) fn coptic_from_fixed(date: RataDie) -> CopticDateInner {
let (year, month, day) = match calendrical_calculations::coptic::coptic_from_fixed(date) {
Err(I32CastError::BelowMin) => return CopticDateInner(ArithmeticDate::min_date()),
Err(I32CastError::AboveMax) => return CopticDateInner(ArithmeticDate::max_date()),
Ok(ymd) => ymd,
};
CopticDateInner(ArithmeticDate::new_unchecked(year, month, day))
}
fn days_in_year_direct(year: i32) -> u16 {
if Coptic::is_leap_year(year) {
366
} else {
365
}
}
}
impl Date<Coptic> {
/// Construct new Coptic Date.
///
/// Negative years are in the B.D. era, starting with 0 = 1 B.D.
///
/// ```rust
/// use icu::calendar::Date;
///
/// let date_coptic = Date::try_new_coptic_date(1686, 5, 6)
/// .expect("Failed to initialize Coptic Date instance.");
///
/// assert_eq!(date_coptic.year().number, 1686);
/// assert_eq!(date_coptic.month().ordinal, 5);
/// assert_eq!(date_coptic.day_of_month().0, 6);
/// ```
pub fn try_new_coptic_date(
year: i32,
month: u8,
day: u8,
) -> Result<Date<Coptic>, CalendarError> {
ArithmeticDate::new_from_ordinals(year, month, day)
.map(CopticDateInner)
.map(|inner| Date::from_raw(inner, Coptic))
}
}
impl DateTime<Coptic> {
/// Construct a new Coptic datetime from integers.
///
/// Negative years are in the B.D. era, starting with 0 = 1 B.D.
///
/// ```rust
/// use icu::calendar::DateTime;
///
/// let datetime_coptic =
/// DateTime::try_new_coptic_datetime(1686, 5, 6, 13, 1, 0)
/// .expect("Failed to initialize Coptic DateTime instance.");
///
/// assert_eq!(datetime_coptic.date.year().number, 1686);
/// assert_eq!(datetime_coptic.date.month().ordinal, 5);
/// assert_eq!(datetime_coptic.date.day_of_month().0, 6);
/// assert_eq!(datetime_coptic.time.hour.number(), 13);
/// assert_eq!(datetime_coptic.time.minute.number(), 1);
/// assert_eq!(datetime_coptic.time.second.number(), 0);
/// ```
pub fn try_new_coptic_datetime(
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
) -> Result<DateTime<Coptic>, CalendarError> {
Ok(DateTime {
date: Date::try_new_coptic_date(year, month, day)?,
time: types::Time::try_new(hour, minute, second, 0)?,
})
}
}
fn year_as_coptic(year: i32) -> types::FormattableYear {
if year > 0 {
types::FormattableYear {
era: types::Era(tinystr!(16, "ad")),
number: year,
cyclic: None,
related_iso: None,
}
} else {
types::FormattableYear {
era: types::Era(tinystr!(16, "bd")),
number: 1 - year,
cyclic: None,
related_iso: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_coptic_regression() {
// https://github.com/unicode-org/icu4x/issues/2254
let iso_date = Date::try_new_iso_date(-100, 3, 3).unwrap();
let coptic = iso_date.to_calendar(Coptic);
let recovered_iso = coptic.to_iso();
assert_eq!(iso_date, recovered_iso);
}
}

1028
third_party/rust/icu_calendar/src/dangi.rs поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

446
third_party/rust/icu_calendar/src/date.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,446 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use crate::any_calendar::{AnyCalendar, IntoAnyCalendar};
use crate::week::{WeekCalculator, WeekOf};
use crate::{types, Calendar, CalendarError, DateDuration, DateDurationUnit, Iso};
use alloc::rc::Rc;
use alloc::sync::Arc;
use core::fmt;
use core::ops::Deref;
/// Types that contain a calendar
///
/// This allows one to use [`Date`] with wrappers around calendars,
/// e.g. reference counted calendars.
pub trait AsCalendar {
/// The calendar being wrapped
type Calendar: Calendar;
/// Obtain the inner calendar
fn as_calendar(&self) -> &Self::Calendar;
}
impl<C: Calendar> AsCalendar for C {
type Calendar = C;
#[inline]
fn as_calendar(&self) -> &Self {
self
}
}
impl<C: Calendar> AsCalendar for Rc<C> {
type Calendar = C;
#[inline]
fn as_calendar(&self) -> &C {
self
}
}
impl<C: Calendar> AsCalendar for Arc<C> {
type Calendar = C;
#[inline]
fn as_calendar(&self) -> &C {
self
}
}
/// This exists as a wrapper around `&'a T` so that
/// `Date<&'a C>` is possible for calendar `C`.
///
/// Unfortunately,
/// [`AsCalendar`] cannot be implemented on `&'a T` directly because
/// `&'a T` is `#[fundamental]` and the impl would clash with the one above with
/// `AsCalendar` for `C: Calendar`.
///
/// Use `Date<Ref<'a, C>>` where you would use `Date<&'a C>`
#[allow(clippy::exhaustive_structs)] // newtype
#[derive(PartialEq, Eq, Debug)]
pub struct Ref<'a, C>(pub &'a C);
impl<C> Copy for Ref<'_, C> {}
impl<C> Clone for Ref<'_, C> {
fn clone(&self) -> Self {
*self
}
}
impl<C: Calendar> AsCalendar for Ref<'_, C> {
type Calendar = C;
#[inline]
fn as_calendar(&self) -> &C {
self.0
}
}
impl<'a, C> Deref for Ref<'a, C> {
type Target = C;
fn deref(&self) -> &C {
self.0
}
}
/// A date for a given calendar.
///
/// This can work with wrappers around [`Calendar`] types,
/// e.g. `Rc<C>`, via the [`AsCalendar`] trait.
///
/// This can be constructed constructed
/// from its fields via [`Self::try_new_from_codes()`], or can be constructed with one of the
/// `new_<calendar>_datetime()` per-calendar methods (and then freely converted between calendars).
///
/// ```rust
/// use icu::calendar::Date;
///
/// // Example: creation of ISO date from integers.
/// let date_iso = Date::try_new_iso_date(1970, 1, 2)
/// .expect("Failed to initialize ISO Date instance.");
///
/// assert_eq!(date_iso.year().number, 1970);
/// assert_eq!(date_iso.month().ordinal, 1);
/// assert_eq!(date_iso.day_of_month().0, 2);
/// ```
pub struct Date<A: AsCalendar> {
pub(crate) inner: <A::Calendar as Calendar>::DateInner,
pub(crate) calendar: A,
}
impl<A: AsCalendar> Date<A> {
/// Construct a date from from era/month codes and fields, and some calendar representation
#[inline]
pub fn try_new_from_codes(
era: types::Era,
year: i32,
month_code: types::MonthCode,
day: u8,
calendar: A,
) -> Result<Self, CalendarError> {
let inner = calendar
.as_calendar()
.date_from_codes(era, year, month_code, day)?;
Ok(Date { inner, calendar })
}
/// Construct a date from an ISO date and some calendar representation
#[inline]
pub fn new_from_iso(iso: Date<Iso>, calendar: A) -> Self {
let inner = calendar.as_calendar().date_from_iso(iso);
Date { inner, calendar }
}
/// Convert the Date to an ISO Date
#[inline]
pub fn to_iso(&self) -> Date<Iso> {
self.calendar.as_calendar().date_to_iso(self.inner())
}
/// Convert the Date to a date in a different calendar
#[inline]
pub fn to_calendar<A2: AsCalendar>(&self, calendar: A2) -> Date<A2> {
Date::new_from_iso(self.to_iso(), calendar)
}
/// The number of months in the year of this date
#[inline]
pub fn months_in_year(&self) -> u8 {
self.calendar.as_calendar().months_in_year(self.inner())
}
/// The number of days in the year of this date
#[inline]
pub fn days_in_year(&self) -> u16 {
self.calendar.as_calendar().days_in_year(self.inner())
}
/// The number of days in the month of this date
#[inline]
pub fn days_in_month(&self) -> u8 {
self.calendar.as_calendar().days_in_month(self.inner())
}
/// The day of the week for this date
///
/// Monday is 1, Sunday is 7, according to ISO
#[inline]
pub fn day_of_week(&self) -> types::IsoWeekday {
self.calendar.as_calendar().day_of_week(self.inner())
}
/// Add a `duration` to this date, mutating it
///
/// Currently unstable for ICU4X 1.0
#[doc(hidden)]
#[inline]
pub fn add(&mut self, duration: DateDuration<A::Calendar>) {
self.calendar
.as_calendar()
.offset_date(&mut self.inner, duration)
}
/// Add a `duration` to this date, returning the new one
///
/// Currently unstable for ICU4X 1.0
#[doc(hidden)]
#[inline]
pub fn added(mut self, duration: DateDuration<A::Calendar>) -> Self {
self.add(duration);
self
}
/// Calculating the duration between `other - self`
///
/// Currently unstable for ICU4X 1.0
#[doc(hidden)]
#[inline]
pub fn until<B: AsCalendar<Calendar = A::Calendar>>(
&self,
other: &Date<B>,
largest_unit: DateDurationUnit,
smallest_unit: DateDurationUnit,
) -> DateDuration<A::Calendar> {
self.calendar.as_calendar().until(
self.inner(),
other.inner(),
other.calendar.as_calendar(),
largest_unit,
smallest_unit,
)
}
/// The calendar-specific year represented by `self`
#[inline]
pub fn year(&self) -> types::FormattableYear {
self.calendar.as_calendar().year(&self.inner)
}
/// Returns whether `self` is in a calendar-specific leap year
#[inline]
pub fn is_in_leap_year(&self) -> bool {
self.calendar.as_calendar().is_in_leap_year(&self.inner)
}
/// The calendar-specific month represented by `self`
#[inline]
pub fn month(&self) -> types::FormattableMonth {
self.calendar.as_calendar().month(&self.inner)
}
/// The calendar-specific day-of-month represented by `self`
#[inline]
pub fn day_of_month(&self) -> types::DayOfMonth {
self.calendar.as_calendar().day_of_month(&self.inner)
}
/// The calendar-specific day-of-month represented by `self`
#[inline]
pub fn day_of_year_info(&self) -> types::DayOfYearInfo {
self.calendar.as_calendar().day_of_year_info(&self.inner)
}
/// The week of the month containing this date.
///
/// # Examples
///
/// ```
/// use icu::calendar::types::IsoWeekday;
/// use icu::calendar::types::WeekOfMonth;
/// use icu::calendar::Date;
///
/// let date = Date::try_new_iso_date(2022, 8, 10).unwrap(); // second Wednesday
///
/// // The following info is usually locale-specific
/// let first_weekday = IsoWeekday::Sunday;
///
/// assert_eq!(date.week_of_month(first_weekday), WeekOfMonth(2));
/// ```
pub fn week_of_month(&self, first_weekday: types::IsoWeekday) -> types::WeekOfMonth {
let config = WeekCalculator {
first_weekday,
min_week_days: 0, // ignored
};
config.week_of_month(self.day_of_month(), self.day_of_week())
}
/// The week of the year containing this date.
///
/// # Examples
///
/// ```
/// use icu::calendar::types::IsoWeekday;
/// use icu::calendar::week::RelativeUnit;
/// use icu::calendar::week::WeekCalculator;
/// use icu::calendar::week::WeekOf;
/// use icu::calendar::Date;
///
/// let date = Date::try_new_iso_date(2022, 8, 26).unwrap();
///
/// // The following info is usually locale-specific
/// let week_calculator = WeekCalculator::default();
///
/// assert_eq!(
/// date.week_of_year(&week_calculator),
/// Ok(WeekOf {
/// week: 35,
/// unit: RelativeUnit::Current
/// })
/// );
/// ```
pub fn week_of_year(&self, config: &WeekCalculator) -> Result<WeekOf, CalendarError> {
config.week_of_year(self.day_of_year_info(), self.day_of_week())
}
/// Construct a date from raw values for a given calendar. This does not check any
/// invariants for the date and calendar, and should only be called by calendar implementations.
///
/// Calling this outside of calendar implementations is sound, but calendar implementations are not
/// expected to do anything sensible with such invalid dates.
///
/// AnyCalendar *will* panic if AnyCalendar [`Date`] objects with mismatching
/// date and calendar types are constructed
#[inline]
pub fn from_raw(inner: <A::Calendar as Calendar>::DateInner, calendar: A) -> Self {
Self { inner, calendar }
}
/// Get the inner date implementation. Should not be called outside of calendar implementations
#[inline]
pub fn inner(&self) -> &<A::Calendar as Calendar>::DateInner {
&self.inner
}
/// Get a reference to the contained calendar
#[inline]
pub fn calendar(&self) -> &A::Calendar {
self.calendar.as_calendar()
}
/// Get a reference to the contained calendar wrapper
///
/// (Useful in case the user wishes to e.g. clone an Rc)
#[inline]
pub fn calendar_wrapper(&self) -> &A {
&self.calendar
}
}
impl<C: IntoAnyCalendar, A: AsCalendar<Calendar = C>> Date<A> {
/// Type-erase the date, converting it to a date for [`AnyCalendar`]
pub fn to_any(&self) -> Date<AnyCalendar> {
let cal = self.calendar();
Date::from_raw(cal.date_to_any(self.inner()), cal.to_any_cloned())
}
}
impl<C: Calendar> Date<C> {
/// Wrap the calendar type in `Rc<T>`
///
/// Useful when paired with [`Self::to_any()`] to obtain a `Date<Rc<AnyCalendar>>`
pub fn wrap_calendar_in_rc(self) -> Date<Rc<C>> {
Date::from_raw(self.inner, Rc::new(self.calendar))
}
/// Wrap the calendar type in `Arc<T>`
///
/// Useful when paired with [`Self::to_any()`] to obtain a `Date<Rc<AnyCalendar>>`
pub fn wrap_calendar_in_arc(self) -> Date<Arc<C>> {
Date::from_raw(self.inner, Arc::new(self.calendar))
}
}
impl<C, A, B> PartialEq<Date<B>> for Date<A>
where
C: Calendar,
A: AsCalendar<Calendar = C>,
B: AsCalendar<Calendar = C>,
{
fn eq(&self, other: &Date<B>) -> bool {
self.inner.eq(&other.inner)
}
}
impl<A: AsCalendar> Eq for Date<A> {}
impl<C, A, B> PartialOrd<Date<B>> for Date<A>
where
C: Calendar,
C::DateInner: PartialOrd,
A: AsCalendar<Calendar = C>,
B: AsCalendar<Calendar = C>,
{
fn partial_cmp(&self, other: &Date<B>) -> Option<core::cmp::Ordering> {
self.inner.partial_cmp(&other.inner)
}
}
impl<C, A> Ord for Date<A>
where
C: Calendar,
C::DateInner: Ord,
A: AsCalendar<Calendar = C>,
{
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.inner.cmp(&other.inner)
}
}
impl<A: AsCalendar> fmt::Debug for Date<A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"Date({:?}, for calendar {})",
self.inner,
self.calendar.as_calendar().debug_name()
)
}
}
impl<A: AsCalendar + Clone> Clone for Date<A> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
calendar: self.calendar.clone(),
}
}
}
impl<A> Copy for Date<A>
where
A: AsCalendar + Copy,
<<A as AsCalendar>::Calendar as Calendar>::DateInner: Copy,
{
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ord() {
let dates_in_order = [
Date::try_new_iso_date(-10, 1, 1).unwrap(),
Date::try_new_iso_date(-10, 1, 2).unwrap(),
Date::try_new_iso_date(-10, 2, 1).unwrap(),
Date::try_new_iso_date(-1, 1, 1).unwrap(),
Date::try_new_iso_date(-1, 1, 2).unwrap(),
Date::try_new_iso_date(-1, 2, 1).unwrap(),
Date::try_new_iso_date(0, 1, 1).unwrap(),
Date::try_new_iso_date(0, 1, 2).unwrap(),
Date::try_new_iso_date(0, 2, 1).unwrap(),
Date::try_new_iso_date(1, 1, 1).unwrap(),
Date::try_new_iso_date(1, 1, 2).unwrap(),
Date::try_new_iso_date(1, 2, 1).unwrap(),
Date::try_new_iso_date(10, 1, 1).unwrap(),
Date::try_new_iso_date(10, 1, 2).unwrap(),
Date::try_new_iso_date(10, 2, 1).unwrap(),
];
for (i, i_date) in dates_in_order.iter().enumerate() {
for (j, j_date) in dates_in_order.iter().enumerate() {
let result1 = i_date.cmp(j_date);
let result2 = j_date.cmp(i_date);
assert_eq!(result1.reverse(), result2);
assert_eq!(i.cmp(&j), i_date.cmp(j_date));
}
}
}
}

206
third_party/rust/icu_calendar/src/datetime.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,206 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use crate::any_calendar::{AnyCalendar, IntoAnyCalendar};
use crate::types::{self, Time};
use crate::{AsCalendar, Calendar, CalendarError, Date, Iso};
use alloc::rc::Rc;
use alloc::sync::Arc;
/// A date+time for a given calendar.
///
/// This can work with wrappers around [`Calendar`](crate::Calendar) types,
/// e.g. `Rc<C>`, via the [`AsCalendar`] trait, much like
/// [`Date`].
///
/// This can be constructed manually from a [`Date`] and [`Time`], or can be constructed
/// from its fields via [`Self::try_new_from_codes()`], or can be constructed with one of the
/// `new_<calendar>_datetime()` per-calendar methods (and then freely converted between calendars).
///
/// ```rust
/// use icu::calendar::DateTime;
///
/// // Example: Construction of ISO datetime from integers.
/// let datetime_iso = DateTime::try_new_iso_datetime(1970, 1, 2, 13, 1, 0)
/// .expect("Failed to initialize ISO DateTime instance.");
///
/// assert_eq!(datetime_iso.date.year().number, 1970);
/// assert_eq!(datetime_iso.date.month().ordinal, 1);
/// assert_eq!(datetime_iso.date.day_of_month().0, 2);
/// assert_eq!(datetime_iso.time.hour.number(), 13);
/// assert_eq!(datetime_iso.time.minute.number(), 1);
/// assert_eq!(datetime_iso.time.second.number(), 0);
/// ```
#[derive(Debug)]
#[allow(clippy::exhaustive_structs)] // this type is stable
pub struct DateTime<A: AsCalendar> {
/// The date
pub date: Date<A>,
/// The time
pub time: Time,
}
impl<A: AsCalendar> DateTime<A> {
/// Construct a [`DateTime`] for a given [`Date`] and [`Time`]
pub fn new(date: Date<A>, time: Time) -> Self {
DateTime { date, time }
}
/// Construct a datetime from from era/month codes and fields,
/// and some calendar representation
#[inline]
pub fn try_new_from_codes(
era: types::Era,
year: i32,
month_code: types::MonthCode,
day: u8,
time: Time,
calendar: A,
) -> Result<Self, CalendarError> {
let date = Date::try_new_from_codes(era, year, month_code, day, calendar)?;
Ok(DateTime { date, time })
}
/// Construct a DateTime from an ISO datetime and some calendar representation
#[inline]
pub fn new_from_iso(iso: DateTime<Iso>, calendar: A) -> Self {
let date = Date::new_from_iso(iso.date, calendar);
DateTime {
date,
time: iso.time,
}
}
/// Convert the DateTime to an ISO DateTime
#[inline]
pub fn to_iso(&self) -> DateTime<Iso> {
DateTime {
date: self.date.to_iso(),
time: self.time,
}
}
/// Convert the DateTime to a DateTime in a different calendar
#[inline]
pub fn to_calendar<A2: AsCalendar>(&self, calendar: A2) -> DateTime<A2> {
DateTime {
date: self.date.to_calendar(calendar),
time: self.time,
}
}
}
impl<C: IntoAnyCalendar, A: AsCalendar<Calendar = C>> DateTime<A> {
/// Type-erase the date, converting it to a date for [`AnyCalendar`]
pub fn to_any(&self) -> DateTime<AnyCalendar> {
DateTime {
date: self.date.to_any(),
time: self.time,
}
}
}
impl<C: Calendar> DateTime<C> {
/// Wrap the calendar type in `Rc<T>`
///
/// Useful when paired with [`Self::to_any()`] to obtain a `DateTime<Rc<AnyCalendar>>`
pub fn wrap_calendar_in_rc(self) -> DateTime<Rc<C>> {
DateTime {
date: self.date.wrap_calendar_in_rc(),
time: self.time,
}
}
/// Wrap the calendar type in `Arc<T>`
///
/// Useful when paired with [`Self::to_any()`] to obtain a `DateTime<Rc<AnyCalendar>>`
pub fn wrap_calendar_in_arc(self) -> DateTime<Arc<C>> {
DateTime {
date: self.date.wrap_calendar_in_arc(),
time: self.time,
}
}
}
impl<C, A, B> PartialEq<DateTime<B>> for DateTime<A>
where
C: Calendar,
A: AsCalendar<Calendar = C>,
B: AsCalendar<Calendar = C>,
{
fn eq(&self, other: &DateTime<B>) -> bool {
self.date == other.date && self.time == other.time
}
}
// We can do this since DateInner is required to be Eq by the Calendar trait
impl<A: AsCalendar> Eq for DateTime<A> {}
impl<C, A, B> PartialOrd<DateTime<B>> for DateTime<A>
where
C: Calendar,
C::DateInner: PartialOrd,
A: AsCalendar<Calendar = C>,
B: AsCalendar<Calendar = C>,
{
fn partial_cmp(&self, other: &DateTime<B>) -> Option<core::cmp::Ordering> {
match self.date.partial_cmp(&other.date) {
Some(core::cmp::Ordering::Equal) => self.time.partial_cmp(&other.time),
other => other,
}
}
}
impl<C, A> Ord for DateTime<A>
where
C: Calendar,
C::DateInner: Ord,
A: AsCalendar<Calendar = C>,
{
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
(&self.date, &self.time).cmp(&(&other.date, &other.time))
}
}
impl<A: AsCalendar + Clone> Clone for DateTime<A> {
fn clone(&self) -> Self {
Self {
date: self.date.clone(),
time: self.time,
}
}
}
impl<A> Copy for DateTime<A>
where
A: AsCalendar + Copy,
<<A as AsCalendar>::Calendar as Calendar>::DateInner: Copy,
{
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ord() {
let dates_in_order = [
DateTime::try_new_iso_datetime(0, 1, 1, 0, 0, 0).unwrap(),
DateTime::try_new_iso_datetime(0, 1, 1, 0, 0, 1).unwrap(),
DateTime::try_new_iso_datetime(0, 1, 1, 0, 1, 0).unwrap(),
DateTime::try_new_iso_datetime(0, 1, 1, 1, 0, 0).unwrap(),
DateTime::try_new_iso_datetime(0, 1, 2, 0, 0, 0).unwrap(),
DateTime::try_new_iso_datetime(0, 2, 1, 0, 0, 0).unwrap(),
DateTime::try_new_iso_datetime(1, 1, 1, 0, 0, 0).unwrap(),
];
for (i, i_date) in dates_in_order.iter().enumerate() {
for (j, j_date) in dates_in_order.iter().enumerate() {
let result1 = i_date.cmp(j_date);
let result2 = j_date.cmp(i_date);
assert_eq!(result1.reverse(), result2);
assert_eq!(i.cmp(&j), i_date.cmp(j_date));
}
}
}
}

146
third_party/rust/icu_calendar/src/duration.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,146 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use crate::Calendar;
use core::fmt;
use core::marker::PhantomData;
/// A duration between two dates
///
/// Can be used to perform date arithmetic
///
/// # Example
///
/// ```rust
/// use icu_calendar::{
/// types::IsoWeekday, Date, DateDuration, DateDurationUnit,
/// };
///
/// // Creating ISO date: 1992-09-02.
/// let mut date_iso = Date::try_new_iso_date(1992, 9, 2)
/// .expect("Failed to initialize ISO Date instance.");
///
/// assert_eq!(date_iso.day_of_week(), IsoWeekday::Wednesday);
/// assert_eq!(date_iso.year().number, 1992);
/// assert_eq!(date_iso.month().ordinal, 9);
/// assert_eq!(date_iso.day_of_month().0, 2);
///
/// // Answering questions about days in month and year.
/// assert_eq!(date_iso.days_in_year(), 366);
/// assert_eq!(date_iso.days_in_month(), 30);
///
/// // Advancing date in-place by 1 year, 2 months, 3 weeks, 4 days.
/// date_iso.add(DateDuration::new(1, 2, 3, 4));
/// assert_eq!(date_iso.year().number, 1993);
/// assert_eq!(date_iso.month().ordinal, 11);
/// assert_eq!(date_iso.day_of_month().0, 27);
///
/// // Reverse date advancement.
/// date_iso.add(DateDuration::new(-1, -2, -3, -4));
/// assert_eq!(date_iso.year().number, 1992);
/// assert_eq!(date_iso.month().ordinal, 9);
/// assert_eq!(date_iso.day_of_month().0, 2);
///
/// // Creating ISO date: 2022-01-30.
/// let newer_date_iso = Date::try_new_iso_date(2022, 1, 30)
/// .expect("Failed to initialize ISO Date instance.");
///
/// // Comparing dates: 2022-01-30 and 1992-09-02.
/// let duration = newer_date_iso.until(
/// &date_iso,
/// DateDurationUnit::Years,
/// DateDurationUnit::Days,
/// );
/// assert_eq!(duration.years, 30);
/// assert_eq!(duration.months, -8);
/// assert_eq!(duration.days, 28);
///
/// // Create new date with date advancement. Reassign to new variable.
/// let mutated_date_iso = date_iso.added(DateDuration::new(1, 2, 3, 4));
/// assert_eq!(mutated_date_iso.year().number, 1993);
/// assert_eq!(mutated_date_iso.month().ordinal, 11);
/// assert_eq!(mutated_date_iso.day_of_month().0, 27);
/// ```
#[derive(Copy, Clone, Eq, PartialEq)]
#[allow(clippy::exhaustive_structs)] // this type should be stable (and is intended to be constructed manually)
pub struct DateDuration<C: Calendar + ?Sized> {
/// The number of years
pub years: i32,
/// The number of months
pub months: i32,
/// The number of weeks
pub weeks: i32,
/// The number of days
pub days: i32,
/// A marker for the calendar
pub marker: PhantomData<C>,
}
/// A "duration unit" used to specify the minimum or maximum duration of time to
/// care about
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[allow(clippy::exhaustive_enums)] // this type should be stable
pub enum DateDurationUnit {
/// Duration in years
Years,
/// Duration in months
Months,
/// Duration in weeks
Weeks,
/// Duration in days
Days,
}
impl<C: Calendar + ?Sized> Default for DateDuration<C> {
fn default() -> Self {
Self {
years: 0,
months: 0,
weeks: 0,
days: 0,
marker: PhantomData,
}
}
}
impl<C: Calendar + ?Sized> DateDuration<C> {
/// Construct a DateDuration
///
/// ```rust
/// # use icu_calendar::*;
/// // two years, three months, and five days
/// let duration: DateDuration<Iso> = DateDuration::new(2, 3, 0, 5);
/// ```
pub fn new(years: i32, months: i32, weeks: i32, days: i32) -> Self {
DateDuration {
years,
months,
weeks,
days,
marker: PhantomData,
}
}
/// Explicitly cast duration to one for a different calendar
pub fn cast_unit<C2: Calendar + ?Sized>(self) -> DateDuration<C2> {
DateDuration {
years: self.years,
months: self.months,
days: self.days,
weeks: self.weeks,
marker: PhantomData,
}
}
}
impl<C: Calendar> fmt::Debug for DateDuration<C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("DateDuration")
.field("years", &self.years)
.field("months", &self.months)
.field("weeks", &self.weeks)
.field("days", &self.days)
.finish()
}
}

98
third_party/rust/icu_calendar/src/error.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,98 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use displaydoc::Display;
use icu_provider::DataError;
use tinystr::{tinystr, TinyStr16, TinyStr4};
use writeable::Writeable;
#[cfg(feature = "std")]
impl std::error::Error for CalendarError {}
/// A list of error outcomes for various operations in this module.
///
/// Re-exported as [`Error`](crate::Error).
#[derive(Display, Debug, Copy, Clone, PartialEq)]
#[non_exhaustive]
pub enum CalendarError {
/// An input could not be parsed.
#[displaydoc("Could not parse as integer")]
Parse,
/// An input overflowed its range.
#[displaydoc("{field} must be between 0-{max}")]
Overflow {
/// The name of the field
field: &'static str,
/// The maximum value
max: usize,
},
#[displaydoc("{field} must be between {min}-0")]
/// An input underflowed its range.
Underflow {
/// The name of the field
field: &'static str,
/// The minimum value
min: isize,
},
/// Out of range
// TODO(Manishearth) turn this into a proper variant
OutOfRange,
/// Unknown era
#[displaydoc("No era named {0} for calendar {1}")]
UnknownEra(TinyStr16, &'static str),
/// Unknown month code for a given calendar
#[displaydoc("No month code named {0} for calendar {1}")]
UnknownMonthCode(TinyStr4, &'static str),
/// Missing required input field for formatting
#[displaydoc("No value for {0}")]
MissingInput(&'static str),
/// No support for a given calendar in AnyCalendar
#[displaydoc("AnyCalendar does not support calendar {0}")]
UnknownAnyCalendarKind(TinyStr16),
/// An operation required a calendar but a calendar was not provided.
#[displaydoc("An operation required a calendar but a calendar was not provided")]
MissingCalendar,
/// An error originating inside of the [data provider](icu_provider).
#[displaydoc("{0}")]
Data(DataError),
}
impl From<core::num::ParseIntError> for CalendarError {
fn from(_: core::num::ParseIntError) -> Self {
CalendarError::Parse
}
}
impl From<DataError> for CalendarError {
fn from(e: DataError) -> Self {
CalendarError::Data(e)
}
}
impl CalendarError {
/// Create an error when an [`AnyCalendarKind`] is expected but not available.
///
/// # Examples
///
/// ```
/// use icu_calendar::AnyCalendarKind;
/// use icu_calendar::CalendarError;
///
/// let cal_str = "maori";
///
/// AnyCalendarKind::get_for_bcp47_string(cal_str)
/// .ok_or_else(|| CalendarError::unknown_any_calendar_kind(cal_str))
/// .expect_err("Māori calendar is not yet supported");
/// ```
///
/// [`AnyCalendarKind`]: crate::AnyCalendarKind
pub fn unknown_any_calendar_kind(description: impl Writeable) -> Self {
let tiny = description
.write_to_string()
.get(0..16)
.and_then(|x| TinyStr16::from_str(x).ok())
.unwrap_or(tinystr!(16, "invalid"));
Self::UnknownAnyCalendarKind(tiny)
}
}

433
third_party/rust/icu_calendar/src/ethiopian.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,433 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
//! This module contains types and implementations for the Ethiopian calendar.
//!
//! ```rust
//! use icu::calendar::{ethiopian::Ethiopian, Date, DateTime};
//!
//! // `Date` type
//! let date_iso = Date::try_new_iso_date(1970, 1, 2)
//! .expect("Failed to initialize ISO Date instance.");
//! let date_ethiopian = Date::new_from_iso(date_iso, Ethiopian::new());
//!
//! // `DateTime` type
//! let datetime_iso = DateTime::try_new_iso_datetime(1970, 1, 2, 13, 1, 0)
//! .expect("Failed to initialize ISO DateTime instance.");
//! let datetime_ethiopian =
//! DateTime::new_from_iso(datetime_iso, Ethiopian::new());
//!
//! // `Date` checks
//! assert_eq!(date_ethiopian.year().number, 1962);
//! assert_eq!(date_ethiopian.month().ordinal, 4);
//! assert_eq!(date_ethiopian.day_of_month().0, 24);
//!
//! // `DateTime` type
//! assert_eq!(datetime_ethiopian.date.year().number, 1962);
//! assert_eq!(datetime_ethiopian.date.month().ordinal, 4);
//! assert_eq!(datetime_ethiopian.date.day_of_month().0, 24);
//! assert_eq!(datetime_ethiopian.time.hour.number(), 13);
//! assert_eq!(datetime_ethiopian.time.minute.number(), 1);
//! assert_eq!(datetime_ethiopian.time.second.number(), 0);
//! ```
use crate::any_calendar::AnyCalendarKind;
use crate::calendar_arithmetic::{ArithmeticDate, CalendarArithmetic};
use crate::iso::Iso;
use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime};
use calendrical_calculations::helpers::I32CastError;
use calendrical_calculations::rata_die::RataDie;
use tinystr::tinystr;
/// The number of years the Amete Alem epoch precedes the Amete Mihret epoch
const AMETE_ALEM_OFFSET: i32 = 5500;
/// Which era style the ethiopian calendar uses
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum EthiopianEraStyle {
/// Use an era scheme of pre- and post- Incarnation eras,
/// anchored at the date of the Incarnation of Jesus in this calendar
AmeteMihret,
/// Use an era scheme of the Anno Mundi era, anchored at the date of Creation
/// in this calendar
AmeteAlem,
}
/// The [Ethiopian Calendar]
///
/// The [Ethiopian calendar] is a solar calendar used by the Coptic Orthodox Church, with twelve normal months
/// and a thirteenth small epagomenal month.
///
/// This type can be used with [`Date`] or [`DateTime`] to represent dates in this calendar.
///
/// It can be constructed in two modes: using the Amete Alem era scheme, or the Amete Mihret era scheme (the default),
/// see [`EthiopianEraStyle`] for more info.
///
/// [Ethiopian calendar]: https://en.wikipedia.org/wiki/Ethiopian_calendar
///
/// # Era codes
///
/// This calendar supports three era codes, based on what mode it is in. In the Amete Mihret scheme it has
/// the `"incar"` and `"pre-incar"` eras, 1 Incarnation is 9 CE. In the Amete Alem scheme, it instead has a single era,
/// `"mundi`, where 1 Anno Mundi is 5493 BCE. Dates before that use negative year numbers.
///
/// # Month codes
///
/// This calendar supports 13 solar month codes (`"M01" - "M13"`), with `"M13"` being used for the short epagomenal month
/// at the end of the year.
// The bool specifies whether dates should be in the Amete Alem era scheme
#[derive(Copy, Clone, Debug, Hash, Default, Eq, PartialEq, PartialOrd, Ord)]
pub struct Ethiopian(pub(crate) bool);
/// The inner date type used for representing [`Date`]s of [`Ethiopian`]. See [`Date`] and [`Ethiopian`] for more details.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct EthiopianDateInner(ArithmeticDate<Ethiopian>);
impl CalendarArithmetic for Ethiopian {
fn month_days(year: i32, month: u8) -> u8 {
if (1..=12).contains(&month) {
30
} else if month == 13 {
if Self::is_leap_year(year) {
6
} else {
5
}
} else {
0
}
}
fn months_for_every_year(_: i32) -> u8 {
13
}
fn is_leap_year(year: i32) -> bool {
year % 4 == 3
}
fn last_month_day_in_year(year: i32) -> (u8, u8) {
if Self::is_leap_year(year) {
(13, 6)
} else {
(13, 5)
}
}
fn days_in_provided_year(year: i32) -> u16 {
if Self::is_leap_year(year) {
366
} else {
365
}
}
}
impl Calendar for Ethiopian {
type DateInner = EthiopianDateInner;
fn date_from_codes(
&self,
era: types::Era,
year: i32,
month_code: types::MonthCode,
day: u8,
) -> Result<Self::DateInner, CalendarError> {
let year = if era.0 == tinystr!(16, "incar") {
if year <= 0 {
return Err(CalendarError::OutOfRange);
}
year
} else if era.0 == tinystr!(16, "pre-incar") {
if year <= 0 {
return Err(CalendarError::OutOfRange);
}
1 - year
} else if era.0 == tinystr!(16, "mundi") {
year - AMETE_ALEM_OFFSET
} else {
return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
};
ArithmeticDate::new_from_codes(self, year, month_code, day).map(EthiopianDateInner)
}
fn date_from_iso(&self, iso: Date<Iso>) -> EthiopianDateInner {
let fixed_iso = Iso::fixed_from_iso(*iso.inner());
Self::ethiopian_from_fixed(fixed_iso)
}
fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
let fixed_ethiopian = Ethiopian::fixed_from_ethiopian(date.0);
Iso::iso_from_fixed(fixed_ethiopian)
}
fn months_in_year(&self, date: &Self::DateInner) -> u8 {
date.0.months_in_year()
}
fn days_in_year(&self, date: &Self::DateInner) -> u16 {
date.0.days_in_year()
}
fn days_in_month(&self, date: &Self::DateInner) -> u8 {
date.0.days_in_month()
}
fn day_of_week(&self, date: &Self::DateInner) -> types::IsoWeekday {
Iso.day_of_week(self.date_to_iso(date).inner())
}
fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
date.0.offset_date(offset);
}
#[allow(clippy::field_reassign_with_default)]
fn until(
&self,
date1: &Self::DateInner,
date2: &Self::DateInner,
_calendar2: &Self,
_largest_unit: DateDurationUnit,
_smallest_unit: DateDurationUnit,
) -> DateDuration<Self> {
date1.0.until(date2.0, _largest_unit, _smallest_unit)
}
fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
Self::year_as_ethiopian(date.0.year, self.0)
}
fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
Self::is_leap_year(date.0.year)
}
fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
date.0.month()
}
fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
date.0.day_of_month()
}
fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
let prev_year = date.0.year - 1;
let next_year = date.0.year + 1;
types::DayOfYearInfo {
day_of_year: date.0.day_of_year(),
days_in_year: date.0.days_in_year(),
prev_year: Self::year_as_ethiopian(prev_year, self.0),
days_in_prev_year: Ethiopian::days_in_year_direct(prev_year),
next_year: Self::year_as_ethiopian(next_year, self.0),
}
}
fn debug_name(&self) -> &'static str {
"Ethiopian"
}
fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
if self.0 {
Some(AnyCalendarKind::EthiopianAmeteAlem)
} else {
Some(AnyCalendarKind::Ethiopian)
}
}
}
impl Ethiopian {
/// Construct a new Ethiopian Calendar for the Amete Mihret era naming scheme
pub const fn new() -> Self {
Self(false)
}
/// Construct a new Ethiopian Calendar with a value specifying whether or not it is Amete Alem
pub const fn new_with_era_style(era_style: EthiopianEraStyle) -> Self {
Self(matches!(era_style, EthiopianEraStyle::AmeteAlem))
}
/// Set whether or not this uses the Amete Alem era scheme
pub fn set_era_style(&mut self, era_style: EthiopianEraStyle) {
self.0 = era_style == EthiopianEraStyle::AmeteAlem
}
/// Returns whether this has the Amete Alem era
pub fn era_style(&self) -> EthiopianEraStyle {
if self.0 {
EthiopianEraStyle::AmeteAlem
} else {
EthiopianEraStyle::AmeteMihret
}
}
fn fixed_from_ethiopian(date: ArithmeticDate<Ethiopian>) -> RataDie {
calendrical_calculations::ethiopian::fixed_from_ethiopian(date.year, date.month, date.day)
}
fn ethiopian_from_fixed(date: RataDie) -> EthiopianDateInner {
let (year, month, day) =
match calendrical_calculations::ethiopian::ethiopian_from_fixed(date) {
Err(I32CastError::BelowMin) => {
return EthiopianDateInner(ArithmeticDate::min_date())
}
Err(I32CastError::AboveMax) => {
return EthiopianDateInner(ArithmeticDate::max_date())
}
Ok(ymd) => ymd,
};
EthiopianDateInner(ArithmeticDate::new_unchecked(year, month, day))
}
fn days_in_year_direct(year: i32) -> u16 {
if Ethiopian::is_leap_year(year) {
366
} else {
365
}
}
fn year_as_ethiopian(year: i32, amete_alem: bool) -> types::FormattableYear {
if amete_alem {
types::FormattableYear {
era: types::Era(tinystr!(16, "mundi")),
number: year + AMETE_ALEM_OFFSET,
cyclic: None,
related_iso: None,
}
} else if year > 0 {
types::FormattableYear {
era: types::Era(tinystr!(16, "incar")),
number: year,
cyclic: None,
related_iso: None,
}
} else {
types::FormattableYear {
era: types::Era(tinystr!(16, "pre-incar")),
number: 1 - year,
cyclic: None,
related_iso: None,
}
}
}
}
impl Date<Ethiopian> {
/// Construct new Ethiopian Date.
///
/// For the Amete Mihret era style, negative years work with
/// year 0 as 1 pre-Incarnation, year -1 as 2 pre-Incarnation,
/// and so on.
///
/// ```rust
/// use icu::calendar::ethiopian::EthiopianEraStyle;
/// use icu::calendar::Date;
///
/// let date_ethiopian = Date::try_new_ethiopian_date(
/// EthiopianEraStyle::AmeteMihret,
/// 2014,
/// 8,
/// 25,
/// )
/// .expect("Failed to initialize Ethopic Date instance.");
///
/// assert_eq!(date_ethiopian.year().number, 2014);
/// assert_eq!(date_ethiopian.month().ordinal, 8);
/// assert_eq!(date_ethiopian.day_of_month().0, 25);
/// ```
pub fn try_new_ethiopian_date(
era_style: EthiopianEraStyle,
mut year: i32,
month: u8,
day: u8,
) -> Result<Date<Ethiopian>, CalendarError> {
if era_style == EthiopianEraStyle::AmeteAlem {
year -= AMETE_ALEM_OFFSET;
}
ArithmeticDate::new_from_ordinals(year, month, day)
.map(EthiopianDateInner)
.map(|inner| Date::from_raw(inner, Ethiopian::new_with_era_style(era_style)))
}
}
impl DateTime<Ethiopian> {
/// Construct a new Ethiopian datetime from integers.
///
/// For the Amete Mihret era style, negative years work with
/// year 0 as 1 pre-Incarnation, year -1 as 2 pre-Incarnation,
/// and so on.
///
/// ```rust
/// use icu::calendar::ethiopian::EthiopianEraStyle;
/// use icu::calendar::DateTime;
///
/// let datetime_ethiopian = DateTime::try_new_ethiopian_datetime(
/// EthiopianEraStyle::AmeteMihret,
/// 2014,
/// 8,
/// 25,
/// 13,
/// 1,
/// 0,
/// )
/// .expect("Failed to initialize Ethiopian DateTime instance.");
///
/// assert_eq!(datetime_ethiopian.date.year().number, 2014);
/// assert_eq!(datetime_ethiopian.date.month().ordinal, 8);
/// assert_eq!(datetime_ethiopian.date.day_of_month().0, 25);
/// assert_eq!(datetime_ethiopian.time.hour.number(), 13);
/// assert_eq!(datetime_ethiopian.time.minute.number(), 1);
/// assert_eq!(datetime_ethiopian.time.second.number(), 0);
/// ```
pub fn try_new_ethiopian_datetime(
era_style: EthiopianEraStyle,
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
) -> Result<DateTime<Ethiopian>, CalendarError> {
Ok(DateTime {
date: Date::try_new_ethiopian_date(era_style, year, month, day)?,
time: types::Time::try_new(hour, minute, second, 0)?,
})
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_leap_year() {
// 11th September 2023 in gregorian is 6/13/2015 in ethiopian
let iso_date = Date::try_new_iso_date(2023, 9, 11).unwrap();
let ethiopian_date = Ethiopian::new().date_from_iso(iso_date);
assert_eq!(ethiopian_date.0.year, 2015);
assert_eq!(ethiopian_date.0.month, 13);
assert_eq!(ethiopian_date.0.day, 6);
}
#[test]
fn test_iso_to_ethiopian_conversion_and_back() {
let iso_date = Date::try_new_iso_date(1970, 1, 2).unwrap();
let date_ethiopian = Date::new_from_iso(iso_date, Ethiopian::new());
assert_eq!(date_ethiopian.inner.0.year, 1962);
assert_eq!(date_ethiopian.inner.0.month, 4);
assert_eq!(date_ethiopian.inner.0.day, 24);
assert_eq!(
date_ethiopian.to_iso(),
Date::try_new_iso_date(1970, 1, 2).unwrap()
);
}
#[test]
fn test_roundtrip_negative() {
// https://github.com/unicode-org/icu4x/issues/2254
let iso_date = Date::try_new_iso_date(-1000, 3, 3).unwrap();
let ethiopian = iso_date.to_calendar(Ethiopian::new());
let recovered_iso = ethiopian.to_iso();
assert_eq!(iso_date, recovered_iso);
}
}

650
third_party/rust/icu_calendar/src/gregorian.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,650 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
//! This module contains types and implementations for the Gregorian calendar.
//!
//! ```rust
//! use icu::calendar::{gregorian::Gregorian, Date, DateTime};
//!
//! // `Date` type
//! let date_iso = Date::try_new_iso_date(1970, 1, 2)
//! .expect("Failed to initialize ISO Date instance.");
//! let date_gregorian = Date::new_from_iso(date_iso, Gregorian);
//!
//! // `DateTime` type
//! let datetime_iso = DateTime::try_new_iso_datetime(1970, 1, 2, 13, 1, 0)
//! .expect("Failed to initialize ISO DateTime instance.");
//! let datetime_gregorian = DateTime::new_from_iso(datetime_iso, Gregorian);
//!
//! // `Date` checks
//! assert_eq!(date_gregorian.year().number, 1970);
//! assert_eq!(date_gregorian.month().ordinal, 1);
//! assert_eq!(date_gregorian.day_of_month().0, 2);
//!
//! // `DateTime` checks
//! assert_eq!(datetime_gregorian.date.year().number, 1970);
//! assert_eq!(datetime_gregorian.date.month().ordinal, 1);
//! assert_eq!(datetime_gregorian.date.day_of_month().0, 2);
//! assert_eq!(datetime_gregorian.time.hour.number(), 13);
//! assert_eq!(datetime_gregorian.time.minute.number(), 1);
//! assert_eq!(datetime_gregorian.time.second.number(), 0);
//! ```
use crate::any_calendar::AnyCalendarKind;
use crate::calendar_arithmetic::ArithmeticDate;
use crate::iso::{Iso, IsoDateInner};
use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime};
use tinystr::tinystr;
/// The Gregorian Calendar
///
/// The [Gregorian calendar] is a solar calendar used by most of the world, with twelve months.
///
/// This type can be used with [`Date`] or [`DateTime`] to represent dates in this calendar.
///
/// [Gregorian calendar]: https://en.wikipedia.org/wiki/Gregorian_calendar
///
/// # Era codes
///
/// This calendar supports two era codes: `"bce"`, and `"ce"`, corresponding to the BCE and CE eras
#[derive(Copy, Clone, Debug, Default)]
#[allow(clippy::exhaustive_structs)] // this type is stable
pub struct Gregorian;
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
/// The inner date type used for representing [`Date`]s of [`Gregorian`]. See [`Date`] and [`Gregorian`] for more details.
pub struct GregorianDateInner(IsoDateInner);
impl Calendar for Gregorian {
type DateInner = GregorianDateInner;
fn date_from_codes(
&self,
era: types::Era,
year: i32,
month_code: types::MonthCode,
day: u8,
) -> Result<Self::DateInner, CalendarError> {
let year = if era.0 == tinystr!(16, "ce") {
if year <= 0 {
return Err(CalendarError::OutOfRange);
}
year
} else if era.0 == tinystr!(16, "bce") {
if year <= 0 {
return Err(CalendarError::OutOfRange);
}
1 - year
} else {
return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
};
ArithmeticDate::new_from_codes(self, year, month_code, day)
.map(IsoDateInner)
.map(GregorianDateInner)
}
fn date_from_iso(&self, iso: Date<Iso>) -> GregorianDateInner {
GregorianDateInner(*iso.inner())
}
fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
Date::from_raw(date.0, Iso)
}
fn months_in_year(&self, date: &Self::DateInner) -> u8 {
Iso.months_in_year(&date.0)
}
fn days_in_year(&self, date: &Self::DateInner) -> u16 {
Iso.days_in_year(&date.0)
}
fn days_in_month(&self, date: &Self::DateInner) -> u8 {
Iso.days_in_month(&date.0)
}
fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
Iso.offset_date(&mut date.0, offset.cast_unit())
}
#[allow(clippy::field_reassign_with_default)] // it's more clear this way
fn until(
&self,
date1: &Self::DateInner,
date2: &Self::DateInner,
_calendar2: &Self,
largest_unit: DateDurationUnit,
smallest_unit: DateDurationUnit,
) -> DateDuration<Self> {
Iso.until(&date1.0, &date2.0, &Iso, largest_unit, smallest_unit)
.cast_unit()
}
/// The calendar-specific year represented by `date`
fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
year_as_gregorian(date.0 .0.year)
}
fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
Iso.is_in_leap_year(&date.0)
}
/// The calendar-specific month represented by `date`
fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
Iso.month(&date.0)
}
/// The calendar-specific day-of-month represented by `date`
fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
Iso.day_of_month(&date.0)
}
/// Information of the day of the year
fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
let prev_year = date.0 .0.year.saturating_sub(1);
let next_year = date.0 .0.year.saturating_add(1);
types::DayOfYearInfo {
day_of_year: Iso::day_of_year(date.0),
days_in_year: Iso::days_in_year_direct(date.0 .0.year),
prev_year: year_as_gregorian(prev_year),
days_in_prev_year: Iso::days_in_year_direct(prev_year),
next_year: year_as_gregorian(next_year),
}
}
fn debug_name(&self) -> &'static str {
"Gregorian"
}
fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
Some(AnyCalendarKind::Gregorian)
}
}
impl Date<Gregorian> {
/// Construct a new Gregorian Date.
///
/// Years are specified as ISO years.
///
/// ```rust
/// use icu::calendar::Date;
///
/// // Conversion from ISO to Gregorian
/// let date_gregorian = Date::try_new_gregorian_date(1970, 1, 2)
/// .expect("Failed to initialize Gregorian Date instance.");
///
/// assert_eq!(date_gregorian.year().number, 1970);
/// assert_eq!(date_gregorian.month().ordinal, 1);
/// assert_eq!(date_gregorian.day_of_month().0, 2);
/// ```
pub fn try_new_gregorian_date(
year: i32,
month: u8,
day: u8,
) -> Result<Date<Gregorian>, CalendarError> {
Date::try_new_iso_date(year, month, day).map(|d| Date::new_from_iso(d, Gregorian))
}
}
impl DateTime<Gregorian> {
/// Construct a new Gregorian datetime from integers.
///
/// Years are specified as ISO years.
///
/// ```rust
/// use icu::calendar::DateTime;
///
/// let datetime_gregorian =
/// DateTime::try_new_gregorian_datetime(1970, 1, 2, 13, 1, 0)
/// .expect("Failed to initialize Gregorian DateTime instance.");
///
/// assert_eq!(datetime_gregorian.date.year().number, 1970);
/// assert_eq!(datetime_gregorian.date.month().ordinal, 1);
/// assert_eq!(datetime_gregorian.date.day_of_month().0, 2);
/// assert_eq!(datetime_gregorian.time.hour.number(), 13);
/// assert_eq!(datetime_gregorian.time.minute.number(), 1);
/// assert_eq!(datetime_gregorian.time.second.number(), 0);
/// ```
pub fn try_new_gregorian_datetime(
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
) -> Result<DateTime<Gregorian>, CalendarError> {
Ok(DateTime {
date: Date::try_new_gregorian_date(year, month, day)?,
time: types::Time::try_new(hour, minute, second, 0)?,
})
}
}
pub(crate) fn year_as_gregorian(year: i32) -> types::FormattableYear {
if year > 0 {
types::FormattableYear {
era: types::Era(tinystr!(16, "ce")),
number: year,
cyclic: None,
related_iso: None,
}
} else {
types::FormattableYear {
era: types::Era(tinystr!(16, "bce")),
number: 1_i32.saturating_sub(year),
cyclic: None,
related_iso: None,
}
}
}
#[cfg(test)]
mod test {
use calendrical_calculations::rata_die::RataDie;
use super::*;
use types::Era;
#[test]
fn day_of_year_info_max() {
#[derive(Debug)]
struct MaxCase {
year: i32,
month: u8,
day: u8,
next_era_year: i32,
era: &'static str,
}
let cases = [
MaxCase {
year: i32::MAX,
month: 7,
day: 11,
next_era_year: i32::MAX,
era: "ce",
},
MaxCase {
year: i32::MAX,
month: 7,
day: 12,
next_era_year: i32::MAX,
era: "ce",
},
MaxCase {
year: i32::MAX,
month: 8,
day: 10,
next_era_year: i32::MAX,
era: "ce",
},
MaxCase {
year: i32::MAX - 1,
month: 7,
day: 11,
next_era_year: i32::MAX,
era: "ce",
},
MaxCase {
year: -2,
month: 1,
day: 1,
next_era_year: 2,
era: "bce",
},
MaxCase {
year: -1,
month: 1,
day: 1,
next_era_year: 1,
era: "bce",
},
MaxCase {
year: 0,
month: 1,
day: 1,
next_era_year: 1,
era: "ce",
},
MaxCase {
year: 1,
month: 1,
day: 1,
next_era_year: 2,
era: "ce",
},
MaxCase {
year: 2000,
month: 6,
day: 15,
next_era_year: 2001,
era: "ce",
},
MaxCase {
year: 2020,
month: 12,
day: 31,
next_era_year: 2021,
era: "ce",
},
];
for case in cases {
let date = Date::try_new_gregorian_date(case.year, case.month, case.day).unwrap();
assert_eq!(
Calendar::day_of_year_info(&Gregorian, &date.inner)
.next_year
.number,
case.next_era_year,
"{case:?}",
);
assert_eq!(
Calendar::day_of_year_info(&Gregorian, &date.inner)
.next_year
.era
.0,
case.era,
"{case:?}",
);
}
}
#[derive(Debug)]
struct TestCase {
fixed_date: RataDie,
iso_year: i32,
iso_month: u8,
iso_day: u8,
expected_year: i32,
expected_era: Era,
expected_month: u32,
expected_day: u32,
}
fn check_test_case(case: TestCase) {
let iso_from_fixed: Date<Iso> = Iso::iso_from_fixed(case.fixed_date);
let greg_date_from_fixed: Date<Gregorian> = Date::new_from_iso(iso_from_fixed, Gregorian);
assert_eq!(greg_date_from_fixed.year().number, case.expected_year,
"Failed year check from fixed: {case:?}\nISO: {iso_from_fixed:?}\nGreg: {greg_date_from_fixed:?}");
assert_eq!(greg_date_from_fixed.year().era, case.expected_era,
"Failed era check from fixed: {case:?}\nISO: {iso_from_fixed:?}\nGreg: {greg_date_from_fixed:?}");
assert_eq!(greg_date_from_fixed.month().ordinal, case.expected_month,
"Failed month check from fixed: {case:?}\nISO: {iso_from_fixed:?}\nGreg: {greg_date_from_fixed:?}");
assert_eq!(greg_date_from_fixed.day_of_month().0, case.expected_day,
"Failed day check from fixed: {case:?}\nISO: {iso_from_fixed:?}\nGreg: {greg_date_from_fixed:?}");
let iso_date_man: Date<Iso> =
Date::try_new_iso_date(case.iso_year, case.iso_month, case.iso_day)
.expect("Failed to initialize ISO date for {case:?}");
let greg_date_man: Date<Gregorian> = Date::new_from_iso(iso_date_man, Gregorian);
assert_eq!(iso_from_fixed, iso_date_man,
"ISO from fixed not equal to ISO generated from manually-input ymd\nCase: {case:?}\nFixed: {iso_from_fixed:?}\nMan: {iso_date_man:?}");
assert_eq!(greg_date_from_fixed, greg_date_man,
"Greg. date from fixed not equal to Greg. generated from manually-input ymd\nCase: {case:?}\nFixed: {greg_date_from_fixed:?}\nMan: {greg_date_man:?}");
}
#[test]
fn test_gregorian_ce() {
// Tests that the Gregorian calendar gives the correct expected
// day, month, and year for positive years (AD/CE/gregory era)
let cases = [
TestCase {
fixed_date: RataDie::new(1),
iso_year: 1,
iso_month: 1,
iso_day: 1,
expected_year: 1,
expected_era: Era(tinystr!(16, "ce")),
expected_month: 1,
expected_day: 1,
},
TestCase {
fixed_date: RataDie::new(181),
iso_year: 1,
iso_month: 6,
iso_day: 30,
expected_year: 1,
expected_era: Era(tinystr!(16, "ce")),
expected_month: 6,
expected_day: 30,
},
TestCase {
fixed_date: RataDie::new(1155),
iso_year: 4,
iso_month: 2,
iso_day: 29,
expected_year: 4,
expected_era: Era(tinystr!(16, "ce")),
expected_month: 2,
expected_day: 29,
},
TestCase {
fixed_date: RataDie::new(1344),
iso_year: 4,
iso_month: 9,
iso_day: 5,
expected_year: 4,
expected_era: Era(tinystr!(16, "ce")),
expected_month: 9,
expected_day: 5,
},
TestCase {
fixed_date: RataDie::new(36219),
iso_year: 100,
iso_month: 3,
iso_day: 1,
expected_year: 100,
expected_era: Era(tinystr!(16, "ce")),
expected_month: 3,
expected_day: 1,
},
];
for case in cases {
check_test_case(case);
}
}
#[test]
fn day_of_year_info_min() {
#[derive(Debug)]
struct MinCase {
year: i32,
month: u8,
day: u8,
prev_era_year: i32,
era: &'static str,
}
let cases = [
MinCase {
year: i32::MIN + 4,
month: 1,
day: 1,
prev_era_year: i32::MAX - 1,
era: "bce",
},
MinCase {
year: i32::MIN + 3,
month: 12,
day: 31,
prev_era_year: i32::MAX,
era: "bce",
},
MinCase {
year: i32::MIN + 2,
month: 2,
day: 2,
prev_era_year: i32::MAX,
era: "bce",
},
MinCase {
year: i32::MIN + 1,
month: 1,
day: 1,
prev_era_year: i32::MAX,
era: "bce",
},
MinCase {
year: i32::MIN,
month: 1,
day: 1,
prev_era_year: i32::MAX,
era: "bce",
},
MinCase {
year: 3,
month: 1,
day: 1,
prev_era_year: 2,
era: "ce",
},
MinCase {
year: 2,
month: 1,
day: 1,
prev_era_year: 1,
era: "ce",
},
MinCase {
year: 1,
month: 1,
day: 1,
prev_era_year: 1,
era: "bce",
},
MinCase {
year: 0,
month: 1,
day: 1,
prev_era_year: 2,
era: "bce",
},
MinCase {
year: -2000,
month: 6,
day: 15,
prev_era_year: 2002,
era: "bce",
},
MinCase {
year: 2020,
month: 12,
day: 31,
prev_era_year: 2019,
era: "ce",
},
];
for case in cases {
let date = Date::try_new_gregorian_date(case.year, case.month, case.day).unwrap();
assert_eq!(
Calendar::day_of_year_info(&Gregorian, &date.inner)
.prev_year
.number,
case.prev_era_year,
"{case:?}",
);
assert_eq!(
Calendar::day_of_year_info(&Gregorian, &date.inner)
.prev_year
.era
.0,
case.era,
"{case:?}",
);
}
}
#[test]
fn test_gregorian_bce() {
// Tests that the Gregorian calendar gives the correct expected
// day, month, and year for negative years (BC/BCE/pre-gregory era)
let cases = [
TestCase {
fixed_date: RataDie::new(0),
iso_year: 0,
iso_month: 12,
iso_day: 31,
expected_year: 1,
expected_era: Era(tinystr!(16, "bce")),
expected_month: 12,
expected_day: 31,
},
TestCase {
fixed_date: RataDie::new(-365), // This is a leap year
iso_year: 0,
iso_month: 1,
iso_day: 1,
expected_year: 1,
expected_era: Era(tinystr!(16, "bce")),
expected_month: 1,
expected_day: 1,
},
TestCase {
fixed_date: RataDie::new(-366),
iso_year: -1,
iso_month: 12,
iso_day: 31,
expected_year: 2,
expected_era: Era(tinystr!(16, "bce")),
expected_month: 12,
expected_day: 31,
},
TestCase {
fixed_date: RataDie::new(-1461),
iso_year: -4,
iso_month: 12,
iso_day: 31,
expected_year: 5,
expected_era: Era(tinystr!(16, "bce")),
expected_month: 12,
expected_day: 31,
},
TestCase {
fixed_date: RataDie::new(-1826),
iso_year: -4,
iso_month: 1,
iso_day: 1,
expected_year: 5,
expected_era: Era(tinystr!(16, "bce")),
expected_month: 1,
expected_day: 1,
},
];
for case in cases {
check_test_case(case);
}
}
#[test]
fn check_gregorian_directionality() {
// Tests that for a large range of fixed dates, if a fixed date
// is less than another, the corresponding YMD should also be less
// than the other, without exception.
for i in -100..100 {
for j in -100..100 {
let iso_i: Date<Iso> = Iso::iso_from_fixed(RataDie::new(i));
let iso_j: Date<Iso> = Iso::iso_from_fixed(RataDie::new(j));
let greg_i: Date<Gregorian> = Date::new_from_iso(iso_i, Gregorian);
let greg_j: Date<Gregorian> = Date::new_from_iso(iso_j, Gregorian);
assert_eq!(
i.cmp(&j),
iso_i.cmp(&iso_j),
"ISO directionality inconsistent with directionality for i: {i}, j: {j}"
);
assert_eq!(
i.cmp(&j),
greg_i.cmp(&greg_j),
"Gregorian directionality inconsistent with directionality for i: {i}, j: {j}"
);
}
}
}
}

619
third_party/rust/icu_calendar/src/hebrew.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,619 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
//! This module contains types and implementations for the Hebrew calendar.
//!
//! ```rust
//! use icu::calendar::{hebrew::Hebrew, Date, DateTime, Ref};
//!
//! let hebrew = Hebrew::new_always_calculating();
//! let hebrew = Ref(&hebrew); // to avoid cloning
//!
//! // `Date` type
//! let hebrew_date =
//! Date::try_new_hebrew_date_with_calendar(3425, 10, 11, hebrew)
//! .expect("Failed to initialize hebrew Date instance.");
//!
//! // `DateTime` type
//! let hebrew_datetime = DateTime::try_new_hebrew_datetime_with_calendar(
//! 3425, 10, 11, 13, 1, 0, hebrew,
//! )
//! .expect("Failed to initialize hebrew DateTime instance.");
//!
//! // `Date` checks
//! assert_eq!(hebrew_date.year().number, 3425);
//! assert_eq!(hebrew_date.month().ordinal, 10);
//! assert_eq!(hebrew_date.day_of_month().0, 11);
//!
//! // `DateTime` checks
//! assert_eq!(hebrew_datetime.date.year().number, 3425);
//! assert_eq!(hebrew_datetime.date.month().ordinal, 10);
//! assert_eq!(hebrew_datetime.date.day_of_month().0, 11);
//! assert_eq!(hebrew_datetime.time.hour.number(), 13);
//! assert_eq!(hebrew_datetime.time.minute.number(), 1);
//! assert_eq!(hebrew_datetime.time.second.number(), 0);
//! ```
use crate::calendar_arithmetic::{ArithmeticDate, CalendarArithmetic};
use crate::types::FormattableMonth;
use crate::AnyCalendarKind;
use crate::AsCalendar;
use crate::Iso;
use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime};
use ::tinystr::tinystr;
use calendrical_calculations::hebrew::BookHebrew;
use calendrical_calculations::rata_die::RataDie;
/// The Civil Hebrew Calendar
///
/// The [Hebrew calendar] is a lunisolar calendar used as the Jewish liturgical calendar
/// as well as an official calendar in Israel.
///
/// This calendar is the _civil_ Hebrew calendar, with the year starting at in the month of Tishrei.
///
/// # Era codes
///
/// This calendar supports a single era code, Anno Mundi, with code `"am"`
///
/// # Month codes
///
/// This calendar is a lunisolar calendar and thus has a leap month. It supports codes `"M01"-"M12"`
/// for regular months, and the leap month Adar I being coded as `"M05L"`.
///
/// [`FormattableMonth`] has slightly divergent behavior: because the regular month Adar is formatted
/// as "Adar II" in a leap year, this calendar will produce the special code `"M06L"` in any [`FormattableMonth`]
/// objects it creates.
///
/// [Hebrew calendar]: https://en.wikipedia.org/wiki/Hebrew_calendar
#[derive(Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
#[non_exhaustive] // we'll be adding precompiled data to this
pub struct Hebrew;
/// The inner date type used for representing [`Date`]s of [`BookHebrew`]. See [`Date`] and [`BookHebrew`] for more details.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
struct BookHebrewDateInner;
/// The inner date type used for representing [`Date`]s of [`Hebrew`]. See [`Date`] and [`Hebrew`] for more details.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct HebrewDateInner(ArithmeticDate<Hebrew>);
impl Hebrew {
/// Construct a new [`Hebrew`] without any precomputed calendrical calculations.
///
/// This is the only mode currently possible, but once precomputing is available (#3933)
/// there will be additional constructors that load from data providers.
pub fn new_always_calculating() -> Self {
Hebrew
}
}
// HEBREW CALENDAR
impl CalendarArithmetic for Hebrew {
fn month_days(civil_year: i32, civil_month: u8) -> u8 {
Self::last_day_of_civil_hebrew_month(civil_year, civil_month)
}
fn months_for_every_year(civil_year: i32) -> u8 {
Self::last_month_of_civil_hebrew_year(civil_year)
}
fn days_in_provided_year(civil_year: i32) -> u16 {
BookHebrew::days_in_book_hebrew_year(civil_year) // number of days don't change between BookHebrew and Civil Hebrew
}
fn is_leap_year(civil_year: i32) -> bool {
// civil and book years are the same
BookHebrew::is_hebrew_leap_year(civil_year)
}
fn last_month_day_in_year(civil_year: i32) -> (u8, u8) {
let civil_month = Self::last_month_of_civil_hebrew_year(civil_year);
let civil_day = Self::last_day_of_civil_hebrew_month(civil_year, civil_month);
(civil_month, civil_day)
}
}
impl Calendar for Hebrew {
type DateInner = HebrewDateInner;
fn date_from_codes(
&self,
era: types::Era,
year: i32,
month_code: types::MonthCode,
day: u8,
) -> Result<Self::DateInner, CalendarError> {
let is_leap_year = Self::is_leap_year(year);
let year = if era.0 == tinystr!(16, "hebrew") || era.0 == tinystr!(16, "am") {
year
} else {
return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
};
let month_code_str = month_code.0.as_str();
let month_ordinal = if is_leap_year {
match month_code_str {
"M01" => 1,
"M02" => 2,
"M03" => 3,
"M04" => 4,
"M05" => 5,
"M05L" => 6,
// M06L is the formatting era code used for Adar II
"M06" | "M06L" => 7,
"M07" => 8,
"M08" => 9,
"M09" => 10,
"M10" => 11,
"M11" => 12,
"M12" => 13,
_ => {
return Err(CalendarError::UnknownMonthCode(
month_code.0,
self.debug_name(),
))
}
}
} else {
match month_code_str {
"M01" => 1,
"M02" => 2,
"M03" => 3,
"M04" => 4,
"M05" => 5,
"M06" => 6,
"M07" => 7,
"M08" => 8,
"M09" => 9,
"M10" => 10,
"M11" => 11,
"M12" => 12,
_ => {
return Err(CalendarError::UnknownMonthCode(
month_code.0,
self.debug_name(),
))
}
}
};
ArithmeticDate::new_from_lunar_ordinals(year, month_ordinal, day).map(HebrewDateInner)
}
fn date_from_iso(&self, iso: Date<Iso>) -> Self::DateInner {
let fixed_iso = Iso::fixed_from_iso(*iso.inner());
Self::civil_hebrew_from_fixed(fixed_iso).inner
}
fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
let fixed_hebrew = Self::fixed_from_civil_hebrew(*date);
Iso::iso_from_fixed(fixed_hebrew)
}
fn months_in_year(&self, date: &Self::DateInner) -> u8 {
date.0.months_in_year()
}
fn days_in_year(&self, date: &Self::DateInner) -> u16 {
date.0.days_in_year()
}
fn days_in_month(&self, date: &Self::DateInner) -> u8 {
date.0.days_in_month()
}
fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
date.0.offset_date(offset)
}
fn until(
&self,
date1: &Self::DateInner,
date2: &Self::DateInner,
_calendar2: &Self,
_largest_unit: DateDurationUnit,
_smallest_unit: DateDurationUnit,
) -> DateDuration<Self> {
date1.0.until(date2.0, _largest_unit, _smallest_unit)
}
fn debug_name(&self) -> &'static str {
"Hebrew"
}
fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
Self::year_as_hebrew(date.0.year)
}
fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
Self::is_leap_year(date.0.year)
}
fn month(&self, date: &Self::DateInner) -> FormattableMonth {
let mut ordinal = date.0.month;
let is_leap_year = Self::is_leap_year(date.0.year);
if is_leap_year {
if ordinal == 6 {
return types::FormattableMonth {
ordinal: ordinal as u32,
code: types::MonthCode(tinystr!(4, "M05L")),
};
} else if ordinal == 7 {
return types::FormattableMonth {
ordinal: ordinal as u32,
code: types::MonthCode(tinystr!(4, "M06L")),
};
}
}
if is_leap_year && ordinal > 6 {
ordinal -= 1;
}
let code = match ordinal {
1 => tinystr!(4, "M01"),
2 => tinystr!(4, "M02"),
3 => tinystr!(4, "M03"),
4 => tinystr!(4, "M04"),
5 => tinystr!(4, "M05"),
6 => tinystr!(4, "M06"),
7 => tinystr!(4, "M07"),
8 => tinystr!(4, "M08"),
9 => tinystr!(4, "M09"),
10 => tinystr!(4, "M10"),
11 => tinystr!(4, "M11"),
12 => tinystr!(4, "M12"),
_ => tinystr!(4, "und"),
};
types::FormattableMonth {
ordinal: date.0.month as u32,
code: types::MonthCode(code),
}
}
fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
date.0.day_of_month()
}
fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
let prev_year = date.0.year.saturating_sub(1);
let next_year = date.0.year.saturating_add(1);
types::DayOfYearInfo {
day_of_year: date.0.day_of_year(),
days_in_year: date.0.days_in_year(),
prev_year: Self::year_as_hebrew(prev_year),
days_in_prev_year: Self::days_in_provided_year(prev_year),
next_year: Self::year_as_hebrew(next_year),
}
}
fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
Some(AnyCalendarKind::Hebrew)
}
}
impl Hebrew {
// Converts a Biblical Hebrew Date to a Civil Hebrew Date
fn biblical_to_civil_date(biblical_date: BookHebrew) -> HebrewDateInner {
let (y, m, d) = biblical_date.to_civil_date();
debug_assert!(ArithmeticDate::<Hebrew>::new_from_lunar_ordinals(y, m, d,).is_ok());
HebrewDateInner(ArithmeticDate::new_unchecked(y, m, d))
}
// Converts a Civil Hebrew Date to a Biblical Hebrew Date
fn civil_to_biblical_date(civil_date: HebrewDateInner) -> BookHebrew {
BookHebrew::from_civil_date(civil_date.0.year, civil_date.0.month, civil_date.0.day)
}
fn last_month_of_civil_hebrew_year(civil_year: i32) -> u8 {
if Self::is_leap_year(civil_year) {
13 // there are 13 months in a leap year
} else {
12
}
}
fn last_day_of_civil_hebrew_month(civil_year: i32, civil_month: u8) -> u8 {
let book_date = Hebrew::civil_to_biblical_date(HebrewDateInner(
ArithmeticDate::new_unchecked(civil_year, civil_month, 1),
));
BookHebrew::last_day_of_book_hebrew_month(book_date.year, book_date.month)
}
// "Fixed" is a day count representation of calendars staring from Jan 1st of year 1 of the Georgian Calendar.
fn fixed_from_civil_hebrew(date: HebrewDateInner) -> RataDie {
let book_date = Hebrew::civil_to_biblical_date(date);
BookHebrew::fixed_from_book_hebrew(book_date)
}
fn civil_hebrew_from_fixed(date: RataDie) -> Date<Hebrew> {
let book_hebrew = BookHebrew::book_hebrew_from_fixed(date);
Date::from_raw(Hebrew::biblical_to_civil_date(book_hebrew), Hebrew)
}
fn year_as_hebrew(civil_year: i32) -> types::FormattableYear {
types::FormattableYear {
era: types::Era(tinystr!(16, "hebrew")),
number: civil_year,
cyclic: None,
related_iso: None,
}
}
}
impl<A: AsCalendar<Calendar = Hebrew>> Date<A> {
/// Construct new Hebrew Date.
///
/// This datetime will not use any precomputed calendrical calculations,
/// one that loads such data from a provider will be added in the future (#3933)
///
///
/// ```rust
/// use icu::calendar::hebrew::Hebrew;
/// use icu::calendar::Date;
///
/// let hebrew = Hebrew::new_always_calculating();
///
/// let date_hebrew =
/// Date::try_new_hebrew_date_with_calendar(3425, 4, 25, hebrew)
/// .expect("Failed to initialize Hebrew Date instance.");
///
/// assert_eq!(date_hebrew.year().number, 3425);
/// assert_eq!(date_hebrew.month().ordinal, 4);
/// assert_eq!(date_hebrew.day_of_month().0, 25);
/// ```
pub fn try_new_hebrew_date_with_calendar(
year: i32,
month: u8,
day: u8,
calendar: A,
) -> Result<Date<A>, CalendarError> {
ArithmeticDate::new_from_lunar_ordinals(year, month, day)
.map(HebrewDateInner)
.map(|inner| Date::from_raw(inner, calendar))
}
}
impl<A: AsCalendar<Calendar = Hebrew>> DateTime<A> {
/// Construct a new Hebrew datetime from integers.
///
/// This datetime will not use any precomputed calendrical calculations,
/// one that loads such data from a provider will be added in the future (#3933)
///
/// ```rust
/// use icu::calendar::hebrew::Hebrew;
/// use icu::calendar::DateTime;
///
/// let hebrew = Hebrew::new_always_calculating();
///
/// let datetime_hebrew = DateTime::try_new_hebrew_datetime_with_calendar(
/// 4201, 10, 11, 13, 1, 0, hebrew,
/// )
/// .expect("Failed to initialize Hebrew DateTime instance");
///
/// assert_eq!(datetime_hebrew.date.year().number, 4201);
/// assert_eq!(datetime_hebrew.date.month().ordinal, 10);
/// assert_eq!(datetime_hebrew.date.day_of_month().0, 11);
/// assert_eq!(datetime_hebrew.time.hour.number(), 13);
/// assert_eq!(datetime_hebrew.time.minute.number(), 1);
/// assert_eq!(datetime_hebrew.time.second.number(), 0);
/// ```
pub fn try_new_hebrew_datetime_with_calendar(
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
calendar: A,
) -> Result<DateTime<A>, CalendarError> {
Ok(DateTime {
date: Date::try_new_hebrew_date_with_calendar(year, month, day, calendar)?,
time: types::Time::try_new(hour, minute, second, 0)?,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use calendrical_calculations::hebrew::*;
#[test]
fn test_conversions() {
let iso_dates: [Date<Iso>; 48] = [
Date::try_new_iso_date(2021, 1, 10).unwrap(),
Date::try_new_iso_date(2021, 1, 25).unwrap(),
Date::try_new_iso_date(2021, 2, 10).unwrap(),
Date::try_new_iso_date(2021, 2, 25).unwrap(),
Date::try_new_iso_date(2021, 3, 10).unwrap(),
Date::try_new_iso_date(2021, 3, 25).unwrap(),
Date::try_new_iso_date(2021, 4, 10).unwrap(),
Date::try_new_iso_date(2021, 4, 25).unwrap(),
Date::try_new_iso_date(2021, 5, 10).unwrap(),
Date::try_new_iso_date(2021, 5, 25).unwrap(),
Date::try_new_iso_date(2021, 6, 10).unwrap(),
Date::try_new_iso_date(2021, 6, 25).unwrap(),
Date::try_new_iso_date(2021, 7, 10).unwrap(),
Date::try_new_iso_date(2021, 7, 25).unwrap(),
Date::try_new_iso_date(2021, 8, 10).unwrap(),
Date::try_new_iso_date(2021, 8, 25).unwrap(),
Date::try_new_iso_date(2021, 9, 10).unwrap(),
Date::try_new_iso_date(2021, 9, 25).unwrap(),
Date::try_new_iso_date(2021, 10, 10).unwrap(),
Date::try_new_iso_date(2021, 10, 25).unwrap(),
Date::try_new_iso_date(2021, 11, 10).unwrap(),
Date::try_new_iso_date(2021, 11, 25).unwrap(),
Date::try_new_iso_date(2021, 12, 10).unwrap(),
Date::try_new_iso_date(2021, 12, 25).unwrap(),
Date::try_new_iso_date(2022, 1, 10).unwrap(),
Date::try_new_iso_date(2022, 1, 25).unwrap(),
Date::try_new_iso_date(2022, 2, 10).unwrap(),
Date::try_new_iso_date(2022, 2, 25).unwrap(),
Date::try_new_iso_date(2022, 3, 10).unwrap(),
Date::try_new_iso_date(2022, 3, 25).unwrap(),
Date::try_new_iso_date(2022, 4, 10).unwrap(),
Date::try_new_iso_date(2022, 4, 25).unwrap(),
Date::try_new_iso_date(2022, 5, 10).unwrap(),
Date::try_new_iso_date(2022, 5, 25).unwrap(),
Date::try_new_iso_date(2022, 6, 10).unwrap(),
Date::try_new_iso_date(2022, 6, 25).unwrap(),
Date::try_new_iso_date(2022, 7, 10).unwrap(),
Date::try_new_iso_date(2022, 7, 25).unwrap(),
Date::try_new_iso_date(2022, 8, 10).unwrap(),
Date::try_new_iso_date(2022, 8, 25).unwrap(),
Date::try_new_iso_date(2022, 9, 10).unwrap(),
Date::try_new_iso_date(2022, 9, 25).unwrap(),
Date::try_new_iso_date(2022, 10, 10).unwrap(),
Date::try_new_iso_date(2022, 10, 25).unwrap(),
Date::try_new_iso_date(2022, 11, 10).unwrap(),
Date::try_new_iso_date(2022, 11, 25).unwrap(),
Date::try_new_iso_date(2022, 12, 10).unwrap(),
Date::try_new_iso_date(2022, 12, 25).unwrap(),
];
let book_hebrew_dates: [(u8, u8, i32); 48] = [
(26, TEVET, 5781),
(12, SHEVAT, 5781),
(28, SHEVAT, 5781),
(13, ADAR, 5781),
(26, ADAR, 5781),
(12, NISAN, 5781),
(28, NISAN, 5781),
(13, IYYAR, 5781),
(28, IYYAR, 5781),
(14, SIVAN, 5781),
(30, SIVAN, 5781),
(15, TAMMUZ, 5781),
(1, AV, 5781),
(16, AV, 5781),
(2, ELUL, 5781),
(17, ELUL, 5781),
(4, TISHRI, 5782),
(19, TISHRI, 5782),
(4, MARHESHVAN, 5782),
(19, MARHESHVAN, 5782),
(6, KISLEV, 5782),
(21, KISLEV, 5782),
(6, TEVET, 5782),
(21, TEVET, 5782),
(8, SHEVAT, 5782),
(23, SHEVAT, 5782),
(9, ADAR, 5782),
(24, ADAR, 5782),
(7, ADARII, 5782),
(22, ADARII, 5782),
(9, NISAN, 5782),
(24, NISAN, 5782),
(9, IYYAR, 5782),
(24, IYYAR, 5782),
(11, SIVAN, 5782),
(26, SIVAN, 5782),
(11, TAMMUZ, 5782),
(26, TAMMUZ, 5782),
(13, AV, 5782),
(28, AV, 5782),
(14, ELUL, 5782),
(29, ELUL, 5782),
(15, TISHRI, 5783),
(30, TISHRI, 5783),
(16, MARHESHVAN, 5783),
(1, KISLEV, 5783),
(16, KISLEV, 5783),
(1, TEVET, 5783),
];
let civil_hebrew_dates: [(u8, u8, i32); 48] = [
(26, 4, 5781),
(12, 5, 5781),
(28, 5, 5781),
(13, 6, 5781),
(26, 6, 5781),
(12, 7, 5781),
(28, 7, 5781),
(13, 8, 5781),
(28, 8, 5781),
(14, 9, 5781),
(30, 9, 5781),
(15, 10, 5781),
(1, 11, 5781),
(16, 11, 5781),
(2, 12, 5781),
(17, 12, 5781),
(4, 1, 5782),
(19, 1, 5782),
(4, 2, 5782),
(19, 2, 5782),
(6, 3, 5782),
(21, 3, 5782),
(6, 4, 5782),
(21, 4, 5782),
(8, 5, 5782),
(23, 5, 5782),
(9, 6, 5782),
(24, 6, 5782),
(7, 7, 5782),
(22, 7, 5782),
(9, 8, 5782),
(24, 8, 5782),
(9, 9, 5782),
(24, 9, 5782),
(11, 10, 5782),
(26, 10, 5782),
(11, 11, 5782),
(26, 11, 5782),
(13, 12, 5782),
(28, 12, 5782),
(14, 13, 5782),
(29, 13, 5782),
(15, 1, 5783),
(30, 1, 5783),
(16, 2, 5783),
(1, 3, 5783),
(16, 3, 5783),
(1, 4, 5783),
];
for (iso_date, (book_date_nums, civil_date_nums)) in iso_dates
.iter()
.zip(book_hebrew_dates.iter().zip(civil_hebrew_dates.iter()))
{
let book_date = BookHebrew {
year: book_date_nums.2,
month: book_date_nums.1,
day: book_date_nums.0,
};
let civil_date: HebrewDateInner = HebrewDateInner(ArithmeticDate::new_unchecked(
civil_date_nums.2,
civil_date_nums.1,
civil_date_nums.0,
));
let book_to_civil = Hebrew::biblical_to_civil_date(book_date);
let civil_to_book = Hebrew::civil_to_biblical_date(civil_date);
assert_eq!(civil_date, book_to_civil);
assert_eq!(book_date, civil_to_book);
let iso_to_fixed = Iso::fixed_from_iso(iso_date.inner);
let fixed_to_hebrew = Hebrew::civil_hebrew_from_fixed(iso_to_fixed);
let hebrew_to_fixed = Hebrew::fixed_from_civil_hebrew(civil_date);
let fixed_to_iso = Iso::iso_from_fixed(hebrew_to_fixed);
assert_eq!(fixed_to_hebrew.inner, civil_date);
assert_eq!(fixed_to_iso.inner, iso_date.inner);
}
}
#[test]
fn test_icu_bug_22441() {
assert_eq!(BookHebrew::days_in_book_hebrew_year(88369), 383);
}
}

577
third_party/rust/icu_calendar/src/indian.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,577 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
//! This module contains types and implementations for the Indian national calendar.
//!
//! ```rust
//! use icu::calendar::{indian::Indian, Date, DateTime};
//!
//! // `Date` type
//! let date_iso = Date::try_new_iso_date(1970, 1, 2)
//! .expect("Failed to initialize ISO Date instance.");
//! let date_indian = Date::new_from_iso(date_iso, Indian);
//!
//! // `DateTime` type
//! let datetime_iso = DateTime::try_new_iso_datetime(1970, 1, 2, 13, 1, 0)
//! .expect("Failed to initialize ISO DateTime instance.");
//! let datetime_indian = DateTime::new_from_iso(datetime_iso, Indian);
//!
//! // `Date` checks
//! assert_eq!(date_indian.year().number, 1891);
//! assert_eq!(date_indian.month().ordinal, 10);
//! assert_eq!(date_indian.day_of_month().0, 12);
//!
//! // `DateTime` type
//! assert_eq!(datetime_indian.date.year().number, 1891);
//! assert_eq!(datetime_indian.date.month().ordinal, 10);
//! assert_eq!(datetime_indian.date.day_of_month().0, 12);
//! assert_eq!(datetime_indian.time.hour.number(), 13);
//! assert_eq!(datetime_indian.time.minute.number(), 1);
//! assert_eq!(datetime_indian.time.second.number(), 0);
//! ```
use crate::any_calendar::AnyCalendarKind;
use crate::calendar_arithmetic::{ArithmeticDate, CalendarArithmetic};
use crate::iso::Iso;
use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime};
use tinystr::tinystr;
/// The Indian National Calendar (aka the Saka calendar)
///
/// The [Indian National calendar] is a solar calendar used by the Indian government, with twelve months.
///
/// This type can be used with [`Date`] or [`DateTime`] to represent dates in this calendar.
///
/// [Indian National calendar]: https://en.wikipedia.org/wiki/Indian_national_calendar
///
/// # Era codes
///
/// This calendar has a single era: `"saka"`, with Saka 0 being 78 CE. Dates before this era use negative years.
///
/// # Month codes
///
/// This calendar supports 12 solar month codes (`"M01" - "M12"`)
#[derive(Copy, Clone, Debug, Hash, Default, Eq, PartialEq, PartialOrd, Ord)]
#[allow(clippy::exhaustive_structs)] // this type is stable
pub struct Indian;
/// The inner date type used for representing [`Date`]s of [`Indian`]. See [`Date`] and [`Indian`] for more details.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct IndianDateInner(ArithmeticDate<Indian>);
impl CalendarArithmetic for Indian {
fn month_days(year: i32, month: u8) -> u8 {
if month == 1 {
if Self::is_leap_year(year) {
31
} else {
30
}
} else if (2..=6).contains(&month) {
31
} else if (7..=12).contains(&month) {
30
} else {
0
}
}
fn months_for_every_year(_: i32) -> u8 {
12
}
fn is_leap_year(year: i32) -> bool {
Iso::is_leap_year(year + 78)
}
fn last_month_day_in_year(_year: i32) -> (u8, u8) {
(12, 30)
}
fn days_in_provided_year(year: i32) -> u16 {
if Self::is_leap_year(year) {
366
} else {
365
}
}
}
/// The Saka calendar starts on the 81st day of the Gregorian year (March 22 or 21)
/// which is an 80 day offset. This number should be subtracted from Gregorian dates
const DAY_OFFSET: u16 = 80;
/// The Saka calendar is 78 years behind Gregorian. This number should be added to Gregorian dates
const YEAR_OFFSET: i32 = 78;
impl Calendar for Indian {
type DateInner = IndianDateInner;
fn date_from_codes(
&self,
era: types::Era,
year: i32,
month_code: types::MonthCode,
day: u8,
) -> Result<Self::DateInner, CalendarError> {
if era.0 != tinystr!(16, "saka") && era.0 != tinystr!(16, "indian") {
return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
}
ArithmeticDate::new_from_codes(self, year, month_code, day).map(IndianDateInner)
}
// Algorithms directly implemented in icu_calendar since they're not from the book
fn date_from_iso(&self, iso: Date<Iso>) -> IndianDateInner {
// Get day number in year (1 indexed)
let day_of_year_iso = Iso::day_of_year(*iso.inner());
// Convert to Saka year
let mut year = iso.inner().0.year - YEAR_OFFSET;
// This is in the previous Indian year
let day_of_year_indian = if day_of_year_iso <= DAY_OFFSET {
year -= 1;
let n_days = Self::days_in_provided_year(year);
// calculate day of year in previous year
n_days + day_of_year_iso - DAY_OFFSET
} else {
day_of_year_iso - DAY_OFFSET
};
IndianDateInner(ArithmeticDate::date_from_year_day(
year,
day_of_year_indian as u32,
))
}
// Algorithms directly implemented in icu_calendar since they're not from the book
fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
let day_of_year_indian = date.0.day_of_year();
let days_in_year = date.0.days_in_year();
let mut year = date.0.year + YEAR_OFFSET;
let day_of_year_iso = if day_of_year_indian + DAY_OFFSET >= days_in_year {
year += 1;
// calculate day of year in next year
day_of_year_indian + DAY_OFFSET - days_in_year
} else {
day_of_year_indian + DAY_OFFSET
};
Iso::iso_from_year_day(year, day_of_year_iso)
}
fn months_in_year(&self, date: &Self::DateInner) -> u8 {
date.0.months_in_year()
}
fn days_in_year(&self, date: &Self::DateInner) -> u16 {
date.0.days_in_year()
}
fn days_in_month(&self, date: &Self::DateInner) -> u8 {
date.0.days_in_month()
}
fn day_of_week(&self, date: &Self::DateInner) -> types::IsoWeekday {
Iso.day_of_week(Indian.date_to_iso(date).inner())
}
fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
date.0.offset_date(offset);
}
#[allow(clippy::field_reassign_with_default)]
fn until(
&self,
date1: &Self::DateInner,
date2: &Self::DateInner,
_calendar2: &Self,
_largest_unit: DateDurationUnit,
_smallest_unit: DateDurationUnit,
) -> DateDuration<Self> {
date1.0.until(date2.0, _largest_unit, _smallest_unit)
}
fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
types::FormattableYear {
era: types::Era(tinystr!(16, "saka")),
number: date.0.year,
cyclic: None,
related_iso: None,
}
}
fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
Self::is_leap_year(date.0.year)
}
fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
date.0.month()
}
fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
date.0.day_of_month()
}
fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
let prev_year = types::FormattableYear {
era: types::Era(tinystr!(16, "saka")),
number: date.0.year - 1,
cyclic: None,
related_iso: None,
};
let next_year = types::FormattableYear {
era: types::Era(tinystr!(16, "saka")),
number: date.0.year + 1,
cyclic: None,
related_iso: None,
};
types::DayOfYearInfo {
day_of_year: date.0.day_of_year(),
days_in_year: date.0.days_in_year(),
prev_year,
days_in_prev_year: Indian::days_in_year_direct(date.0.year - 1),
next_year,
}
}
fn debug_name(&self) -> &'static str {
"Indian"
}
fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
Some(AnyCalendarKind::Indian)
}
}
impl Indian {
/// Construct a new Indian Calendar
pub fn new() -> Self {
Self
}
fn days_in_year_direct(year: i32) -> u16 {
if Indian::is_leap_year(year) {
366
} else {
365
}
}
}
impl Date<Indian> {
/// Construct new Indian Date, with year provided in the Śaka era.
///
/// ```rust
/// use icu::calendar::Date;
///
/// let date_indian = Date::try_new_indian_date(1891, 10, 12)
/// .expect("Failed to initialize Indian Date instance.");
///
/// assert_eq!(date_indian.year().number, 1891);
/// assert_eq!(date_indian.month().ordinal, 10);
/// assert_eq!(date_indian.day_of_month().0, 12);
/// ```
pub fn try_new_indian_date(
year: i32,
month: u8,
day: u8,
) -> Result<Date<Indian>, CalendarError> {
ArithmeticDate::new_from_ordinals(year, month, day)
.map(IndianDateInner)
.map(|inner| Date::from_raw(inner, Indian))
}
}
impl DateTime<Indian> {
/// Construct a new Indian datetime from integers, with year provided in the Śaka era.
///
/// ```rust
/// use icu::calendar::DateTime;
///
/// let datetime_indian =
/// DateTime::try_new_indian_datetime(1891, 10, 12, 13, 1, 0)
/// .expect("Failed to initialize Indian DateTime instance.");
///
/// assert_eq!(datetime_indian.date.year().number, 1891);
/// assert_eq!(datetime_indian.date.month().ordinal, 10);
/// assert_eq!(datetime_indian.date.day_of_month().0, 12);
/// assert_eq!(datetime_indian.time.hour.number(), 13);
/// assert_eq!(datetime_indian.time.minute.number(), 1);
/// assert_eq!(datetime_indian.time.second.number(), 0);
/// ```
pub fn try_new_indian_datetime(
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
) -> Result<DateTime<Indian>, CalendarError> {
Ok(DateTime {
date: Date::try_new_indian_date(year, month, day)?,
time: types::Time::try_new(hour, minute, second, 0)?,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use calendrical_calculations::rata_die::RataDie;
fn assert_roundtrip(y: i32, m: u8, d: u8, iso_y: i32, iso_m: u8, iso_d: u8) {
let indian =
Date::try_new_indian_date(y, m, d).expect("Indian date should construct successfully");
let iso = indian.to_iso();
assert_eq!(
iso.year().number,
iso_y,
"{y}-{m}-{d}: ISO year did not match"
);
assert_eq!(
iso.month().ordinal as u8,
iso_m,
"{y}-{m}-{d}: ISO month did not match"
);
assert_eq!(
iso.day_of_month().0 as u8,
iso_d,
"{y}-{m}-{d}: ISO day did not match"
);
let roundtrip = iso.to_calendar(Indian);
assert_eq!(
roundtrip.year().number,
indian.year().number,
"{y}-{m}-{d}: roundtrip year did not match"
);
assert_eq!(
roundtrip.month().ordinal,
indian.month().ordinal,
"{y}-{m}-{d}: roundtrip month did not match"
);
assert_eq!(
roundtrip.day_of_month(),
indian.day_of_month(),
"{y}-{m}-{d}: roundtrip day did not match"
);
}
#[test]
fn roundtrip_indian() {
// Ultimately the day of the year will always be identical regardless of it
// being a leap year or not
// Test dates that occur after and before Chaitra 1 (March 22/21), in all years of
// a four-year leap cycle, to ensure that all code paths are tested
assert_roundtrip(1944, 6, 7, 2022, 8, 29);
assert_roundtrip(1943, 6, 7, 2021, 8, 29);
assert_roundtrip(1942, 6, 7, 2020, 8, 29);
assert_roundtrip(1941, 6, 7, 2019, 8, 29);
assert_roundtrip(1944, 11, 7, 2023, 1, 27);
assert_roundtrip(1943, 11, 7, 2022, 1, 27);
assert_roundtrip(1942, 11, 7, 2021, 1, 27);
assert_roundtrip(1941, 11, 7, 2020, 1, 27);
}
#[derive(Debug)]
struct TestCase {
iso_year: i32,
iso_month: u8,
iso_day: u8,
expected_year: i32,
expected_month: u32,
expected_day: u32,
}
fn check_case(case: TestCase) {
let iso = Date::try_new_iso_date(case.iso_year, case.iso_month, case.iso_day).unwrap();
let saka = iso.to_calendar(Indian);
assert_eq!(
saka.year().number,
case.expected_year,
"Year check failed for case: {case:?}"
);
assert_eq!(
saka.month().ordinal,
case.expected_month,
"Month check failed for case: {case:?}"
);
assert_eq!(
saka.day_of_month().0,
case.expected_day,
"Day check failed for case: {case:?}"
);
}
#[test]
fn test_cases_near_epoch_start() {
let cases = [
TestCase {
iso_year: 79,
iso_month: 3,
iso_day: 23,
expected_year: 1,
expected_month: 1,
expected_day: 2,
},
TestCase {
iso_year: 79,
iso_month: 3,
iso_day: 22,
expected_year: 1,
expected_month: 1,
expected_day: 1,
},
TestCase {
iso_year: 79,
iso_month: 3,
iso_day: 21,
expected_year: 0,
expected_month: 12,
expected_day: 30,
},
TestCase {
iso_year: 79,
iso_month: 3,
iso_day: 20,
expected_year: 0,
expected_month: 12,
expected_day: 29,
},
TestCase {
iso_year: 78,
iso_month: 3,
iso_day: 21,
expected_year: -1,
expected_month: 12,
expected_day: 30,
},
];
for case in cases {
check_case(case);
}
}
#[test]
fn test_cases_near_rd_zero() {
let cases = [
TestCase {
iso_year: 1,
iso_month: 3,
iso_day: 22,
expected_year: -77,
expected_month: 1,
expected_day: 1,
},
TestCase {
iso_year: 1,
iso_month: 3,
iso_day: 21,
expected_year: -78,
expected_month: 12,
expected_day: 30,
},
TestCase {
iso_year: 1,
iso_month: 1,
iso_day: 1,
expected_year: -78,
expected_month: 10,
expected_day: 11,
},
TestCase {
iso_year: 0,
iso_month: 3,
iso_day: 21,
expected_year: -78,
expected_month: 1,
expected_day: 1,
},
TestCase {
iso_year: 0,
iso_month: 1,
iso_day: 1,
expected_year: -79,
expected_month: 10,
expected_day: 11,
},
TestCase {
iso_year: -1,
iso_month: 3,
iso_day: 21,
expected_year: -80,
expected_month: 12,
expected_day: 30,
},
];
for case in cases {
check_case(case);
}
}
#[test]
fn test_roundtrip_near_rd_zero() {
for i in -1000..=1000 {
let initial = RataDie::new(i);
let result = Iso::fixed_from_iso(
Iso::iso_from_fixed(initial)
.to_calendar(Indian)
.to_calendar(Iso)
.inner,
);
assert_eq!(
initial, result,
"Roundtrip failed for initial: {initial:?}, result: {result:?}"
);
}
}
#[test]
fn test_roundtrip_near_epoch_start() {
// Epoch start: RD 28570
for i in 27570..=29570 {
let initial = RataDie::new(i);
let result = Iso::fixed_from_iso(
Iso::iso_from_fixed(initial)
.to_calendar(Indian)
.to_calendar(Iso)
.inner,
);
assert_eq!(
initial, result,
"Roundtrip failed for initial: {initial:?}, result: {result:?}"
);
}
}
#[test]
fn test_directionality_near_rd_zero() {
for i in -100..=100 {
for j in -100..=100 {
let rd_i = RataDie::new(i);
let rd_j = RataDie::new(j);
let indian_i = Iso::iso_from_fixed(rd_i).to_calendar(Indian);
let indian_j = Iso::iso_from_fixed(rd_j).to_calendar(Indian);
assert_eq!(i.cmp(&j), indian_i.cmp(&indian_j), "Directionality test failed for i: {i}, j: {j}, indian_i: {indian_i:?}, indian_j: {indian_j:?}");
}
}
}
#[test]
fn test_directionality_near_epoch_start() {
// Epoch start: RD 28570
for i in 28470..=28670 {
for j in 28470..=28670 {
let indian_i = Iso::iso_from_fixed(RataDie::new(i)).to_calendar(Indian);
let indian_j = Iso::iso_from_fixed(RataDie::new(j)).to_calendar(Indian);
assert_eq!(i.cmp(&j), indian_i.cmp(&indian_j), "Directionality test failed for i: {i}, j: {j}, indian_i: {indian_i:?}, indian_j: {indian_j:?}");
}
}
}
}

1963
third_party/rust/icu_calendar/src/islamic.rs поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

824
third_party/rust/icu_calendar/src/iso.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,824 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
//! This module contains types and implementations for the ISO calendar.
//!
//! ```rust
//! use icu::calendar::{Date, DateTime};
//!
//! // `Date` type
//! let date_iso = Date::try_new_iso_date(1970, 1, 2)
//! .expect("Failed to initialize ISO Date instance.");
//!
//! // `DateTime` type
//! let datetime_iso = DateTime::try_new_iso_datetime(1970, 1, 2, 13, 1, 0)
//! .expect("Failed to initialize ISO DateTime instance.");
//!
//! // `Date` checks
//! assert_eq!(date_iso.year().number, 1970);
//! assert_eq!(date_iso.month().ordinal, 1);
//! assert_eq!(date_iso.day_of_month().0, 2);
//!
//! // `DateTime` type
//! assert_eq!(datetime_iso.date.year().number, 1970);
//! assert_eq!(datetime_iso.date.month().ordinal, 1);
//! assert_eq!(datetime_iso.date.day_of_month().0, 2);
//! assert_eq!(datetime_iso.time.hour.number(), 13);
//! assert_eq!(datetime_iso.time.minute.number(), 1);
//! assert_eq!(datetime_iso.time.second.number(), 0);
//! ```
use crate::any_calendar::AnyCalendarKind;
use crate::calendar_arithmetic::{ArithmeticDate, CalendarArithmetic};
use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime};
use calendrical_calculations::helpers::{i64_to_saturated_i32, I32CastError};
use calendrical_calculations::rata_die::RataDie;
use tinystr::tinystr;
/// The [ISO Calendar]
///
/// The [ISO Calendar] is a standardized solar calendar with twelve months.
/// It is identical to the Gregorian calendar, except it uses negative years for years before 1 CE,
/// and may have differing formatting data for a given locale.
///
/// This type can be used with [`Date`] or [`DateTime`] to represent dates in this calendar.
///
/// [ISO Calendar]: https://en.wikipedia.org/wiki/ISO_8601#Dates
///
/// # Era codes
///
/// This calendar supports one era, `"default"`
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[allow(clippy::exhaustive_structs)] // this type is stable
pub struct Iso;
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
/// The inner date type used for representing [`Date`]s of [`Iso`]. See [`Date`] and [`Iso`] for more details.
pub struct IsoDateInner(pub(crate) ArithmeticDate<Iso>);
impl CalendarArithmetic for Iso {
fn month_days(year: i32, month: u8) -> u8 {
match month {
4 | 6 | 9 | 11 => 30,
2 if Self::is_leap_year(year) => 29,
2 => 28,
1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
_ => 0,
}
}
fn months_for_every_year(_: i32) -> u8 {
12
}
fn is_leap_year(year: i32) -> bool {
calendrical_calculations::iso::is_leap_year(year)
}
fn last_month_day_in_year(_year: i32) -> (u8, u8) {
(12, 31)
}
fn days_in_provided_year(year: i32) -> u16 {
if Self::is_leap_year(year) {
366
} else {
365
}
}
}
impl Calendar for Iso {
type DateInner = IsoDateInner;
/// Construct a date from era/month codes and fields
fn date_from_codes(
&self,
era: types::Era,
year: i32,
month_code: types::MonthCode,
day: u8,
) -> Result<Self::DateInner, CalendarError> {
if era.0 != tinystr!(16, "default") {
return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
}
ArithmeticDate::new_from_codes(self, year, month_code, day).map(IsoDateInner)
}
fn date_from_iso(&self, iso: Date<Iso>) -> IsoDateInner {
*iso.inner()
}
fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
Date::from_raw(*date, Iso)
}
fn months_in_year(&self, date: &Self::DateInner) -> u8 {
date.0.months_in_year()
}
fn days_in_year(&self, date: &Self::DateInner) -> u16 {
date.0.days_in_year()
}
fn days_in_month(&self, date: &Self::DateInner) -> u8 {
date.0.days_in_month()
}
fn day_of_week(&self, date: &Self::DateInner) -> types::IsoWeekday {
// For the purposes of the calculation here, Monday is 0, Sunday is 6
// ISO has Monday=1, Sunday=7, which we transform in the last step
// The days of the week are the same every 400 years
// so we normalize to the nearest multiple of 400
let years_since_400 = date.0.year % 400;
let leap_years_since_400 = years_since_400 / 4 - years_since_400 / 100;
// The number of days to the current year
// Can never cause an overflow because years_since_400 has a maximum value of 399.
let days_to_current_year = 365 * years_since_400 + leap_years_since_400;
// The weekday offset from January 1 this year and January 1 2000
let year_offset = days_to_current_year % 7;
// Corresponding months from
// https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week#Corresponding_months
let month_offset = if Self::is_leap_year(date.0.year) {
match date.0.month {
10 => 0,
5 => 1,
2 | 8 => 2,
3 | 11 => 3,
6 => 4,
9 | 12 => 5,
1 | 4 | 7 => 6,
_ => unreachable!(),
}
} else {
match date.0.month {
1 | 10 => 0,
5 => 1,
8 => 2,
2 | 3 | 11 => 3,
6 => 4,
9 | 12 => 5,
4 | 7 => 6,
_ => unreachable!(),
}
};
let january_1_2000 = 5; // Saturday
let day_offset = (january_1_2000 + year_offset + month_offset + date.0.day as i32) % 7;
// We calculated in a zero-indexed fashion, but ISO specifies one-indexed
types::IsoWeekday::from((day_offset + 1) as usize)
}
fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
date.0.offset_date(offset);
}
#[allow(clippy::field_reassign_with_default)]
fn until(
&self,
date1: &Self::DateInner,
date2: &Self::DateInner,
_calendar2: &Self,
_largest_unit: DateDurationUnit,
_smallest_unit: DateDurationUnit,
) -> DateDuration<Self> {
date1.0.until(date2.0, _largest_unit, _smallest_unit)
}
/// The calendar-specific year represented by `date`
fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
Self::year_as_iso(date.0.year)
}
fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
Self::is_leap_year(date.0.year)
}
/// The calendar-specific month represented by `date`
fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
date.0.month()
}
/// The calendar-specific day-of-month represented by `date`
fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
date.0.day_of_month()
}
fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
let prev_year = date.0.year.saturating_sub(1);
let next_year = date.0.year.saturating_add(1);
types::DayOfYearInfo {
day_of_year: date.0.day_of_year(),
days_in_year: date.0.days_in_year(),
prev_year: Self::year_as_iso(prev_year),
days_in_prev_year: Iso::days_in_year_direct(prev_year),
next_year: Self::year_as_iso(next_year),
}
}
fn debug_name(&self) -> &'static str {
"ISO"
}
fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
Some(AnyCalendarKind::Iso)
}
}
impl Date<Iso> {
/// Construct a new ISO date from integers.
///
/// ```rust
/// use icu::calendar::Date;
///
/// let date_iso = Date::try_new_iso_date(1970, 1, 2)
/// .expect("Failed to initialize ISO Date instance.");
///
/// assert_eq!(date_iso.year().number, 1970);
/// assert_eq!(date_iso.month().ordinal, 1);
/// assert_eq!(date_iso.day_of_month().0, 2);
/// ```
pub fn try_new_iso_date(year: i32, month: u8, day: u8) -> Result<Date<Iso>, CalendarError> {
ArithmeticDate::new_from_ordinals(year, month, day)
.map(IsoDateInner)
.map(|inner| Date::from_raw(inner, Iso))
}
/// Constructs an ISO date representing the UNIX epoch on January 1, 1970.
pub fn unix_epoch() -> Self {
Date::from_raw(IsoDateInner(ArithmeticDate::new_unchecked(1970, 1, 1)), Iso)
}
}
impl DateTime<Iso> {
/// Construct a new ISO datetime from integers.
///
/// ```rust
/// use icu::calendar::DateTime;
///
/// let datetime_iso = DateTime::try_new_iso_datetime(1970, 1, 2, 13, 1, 0)
/// .expect("Failed to initialize ISO DateTime instance.");
///
/// assert_eq!(datetime_iso.date.year().number, 1970);
/// assert_eq!(datetime_iso.date.month().ordinal, 1);
/// assert_eq!(datetime_iso.date.day_of_month().0, 2);
/// assert_eq!(datetime_iso.time.hour.number(), 13);
/// assert_eq!(datetime_iso.time.minute.number(), 1);
/// assert_eq!(datetime_iso.time.second.number(), 0);
/// ```
pub fn try_new_iso_datetime(
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
) -> Result<DateTime<Iso>, CalendarError> {
Ok(DateTime {
date: Date::try_new_iso_date(year, month, day)?,
time: types::Time::try_new(hour, minute, second, 0)?,
})
}
/// Minute count representation of calendars starting from 00:00:00 on Jan 1st, 1970.
///
/// ```rust
/// use icu::calendar::DateTime;
///
/// let today = DateTime::try_new_iso_datetime(2020, 2, 29, 0, 0, 0).unwrap();
///
/// assert_eq!(today.minutes_since_local_unix_epoch(), 26382240);
/// assert_eq!(
/// DateTime::from_minutes_since_local_unix_epoch(26382240),
/// today
/// );
///
/// let today = DateTime::try_new_iso_datetime(1970, 1, 1, 0, 0, 0).unwrap();
///
/// assert_eq!(today.minutes_since_local_unix_epoch(), 0);
/// assert_eq!(DateTime::from_minutes_since_local_unix_epoch(0), today);
/// ```
pub fn minutes_since_local_unix_epoch(&self) -> i32 {
let minutes_a_hour = 60;
let hours_a_day = 24;
let minutes_a_day = minutes_a_hour * hours_a_day;
let unix_epoch = Iso::fixed_from_iso(Date::unix_epoch().inner);
let result = (Iso::fixed_from_iso(*self.date.inner()) - unix_epoch) * minutes_a_day
+ i64::from(self.time.hour.number()) * minutes_a_hour
+ i64::from(self.time.minute.number());
i64_to_saturated_i32(result)
}
/// Convert minute count since 00:00:00 on Jan 1st, 1970 to ISO Date.
///
/// # Examples
///
/// ```rust
/// use icu::calendar::DateTime;
///
/// // After Unix Epoch
/// let today = DateTime::try_new_iso_datetime(2020, 2, 29, 0, 0, 0).unwrap();
///
/// assert_eq!(today.minutes_since_local_unix_epoch(), 26382240);
/// assert_eq!(
/// DateTime::from_minutes_since_local_unix_epoch(26382240),
/// today
/// );
///
/// // Unix Epoch
/// let today = DateTime::try_new_iso_datetime(1970, 1, 1, 0, 0, 0).unwrap();
///
/// assert_eq!(today.minutes_since_local_unix_epoch(), 0);
/// assert_eq!(DateTime::from_minutes_since_local_unix_epoch(0), today);
///
/// // Before Unix Epoch
/// let today = DateTime::try_new_iso_datetime(1967, 4, 6, 20, 40, 0).unwrap();
///
/// assert_eq!(today.minutes_since_local_unix_epoch(), -1440200);
/// assert_eq!(
/// DateTime::from_minutes_since_local_unix_epoch(-1440200),
/// today
/// );
/// ```
pub fn from_minutes_since_local_unix_epoch(minute: i32) -> DateTime<Iso> {
let (time, extra_days) = types::Time::from_minute_with_remainder_days(minute);
let unix_epoch = Date::unix_epoch();
let unix_epoch_days = Iso::fixed_from_iso(unix_epoch.inner);
let date = Iso::iso_from_fixed(unix_epoch_days + extra_days as i64);
DateTime { date, time }
}
}
impl Iso {
/// Construct a new ISO Calendar
pub fn new() -> Self {
Self
}
/// Count the number of days in a given month/year combo
fn days_in_month(year: i32, month: u8) -> u8 {
match month {
4 | 6 | 9 | 11 => 30,
2 if Self::is_leap_year(year) => 29,
2 => 28,
_ => 31,
}
}
pub(crate) fn days_in_year_direct(year: i32) -> u16 {
if Self::is_leap_year(year) {
366
} else {
365
}
}
// Fixed is day count representation of calendars starting from Jan 1st of year 1.
// The fixed calculations algorithms are from the Calendrical Calculations book.
pub(crate) fn fixed_from_iso(date: IsoDateInner) -> RataDie {
calendrical_calculations::iso::fixed_from_iso(date.0.year, date.0.month, date.0.day)
}
pub(crate) fn iso_from_year_day(year: i32, year_day: u16) -> Date<Iso> {
let mut month = 1;
let mut day = year_day as i32;
while month <= 12 {
let month_days = Self::days_in_month(year, month) as i32;
if day <= month_days {
break;
} else {
debug_assert!(month < 12); // don't try going to month 13
day -= month_days;
month += 1;
}
}
let day = day as u8; // day <= month_days < u8::MAX
#[allow(clippy::unwrap_used)] // month in 1..=12, day <= month_days
Date::try_new_iso_date(year, month, day).unwrap()
}
pub(crate) fn iso_from_fixed(date: RataDie) -> Date<Iso> {
let (year, month, day) = match calendrical_calculations::iso::iso_from_fixed(date) {
Err(I32CastError::BelowMin) => {
return Date::from_raw(IsoDateInner(ArithmeticDate::min_date()), Iso)
}
Err(I32CastError::AboveMax) => {
return Date::from_raw(IsoDateInner(ArithmeticDate::max_date()), Iso)
}
Ok(ymd) => ymd,
};
#[allow(clippy::unwrap_used)] // valid day and month
Date::try_new_iso_date(year, month, day).unwrap()
}
pub(crate) fn day_of_year(date: IsoDateInner) -> u16 {
// Cumulatively how much are dates in each month
// offset from "30 days in each month" (in non leap years)
let month_offset = [0, 1, -1, 0, 0, 1, 1, 2, 3, 3, 4, 4];
#[allow(clippy::indexing_slicing)] // date.0.month in 1..=12
let mut offset = month_offset[date.0.month as usize - 1];
if Self::is_leap_year(date.0.year) && date.0.month > 2 {
// Months after February in a leap year are offset by one less
offset += 1;
}
let prev_month_days = (30 * (date.0.month as i32 - 1) + offset) as u16;
prev_month_days + date.0.day as u16
}
/// Wrap the year in the appropriate era code
fn year_as_iso(year: i32) -> types::FormattableYear {
types::FormattableYear {
era: types::Era(tinystr!(16, "default")),
number: year,
cyclic: None,
related_iso: None,
}
}
}
impl IsoDateInner {
pub(crate) fn jan_1(year: i32) -> Self {
Self(ArithmeticDate::new_unchecked(year, 1, 1))
}
pub(crate) fn dec_31(year: i32) -> Self {
Self(ArithmeticDate::new_unchecked(year, 12, 1))
}
}
impl From<&'_ IsoDateInner> for crate::provider::EraStartDate {
fn from(other: &'_ IsoDateInner) -> Self {
Self {
year: other.0.year,
month: other.0.month,
day: other.0.day,
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::types::IsoWeekday;
#[test]
fn iso_overflow() {
#[derive(Debug)]
struct TestCase {
year: i32,
month: u8,
day: u8,
fixed: RataDie,
saturating: bool,
}
// Calculates the max possible year representable using i32::MAX as the fixed date
let max_year = Iso::iso_from_fixed(RataDie::new(i32::MAX as i64))
.year()
.number;
// Calculates the minimum possible year representable using i32::MIN as the fixed date
// *Cannot be tested yet due to hard coded date not being available yet (see line 436)
let min_year = -5879610;
let cases = [
TestCase {
// Earliest date that can be represented before causing a minimum overflow
year: min_year,
month: 6,
day: 22,
fixed: RataDie::new(i32::MIN as i64),
saturating: false,
},
TestCase {
year: min_year,
month: 6,
day: 23,
fixed: RataDie::new(i32::MIN as i64 + 1),
saturating: false,
},
TestCase {
year: min_year,
month: 6,
day: 21,
fixed: RataDie::new(i32::MIN as i64 - 1),
saturating: false,
},
TestCase {
year: min_year,
month: 12,
day: 31,
fixed: RataDie::new(-2147483456),
saturating: false,
},
TestCase {
year: min_year + 1,
month: 1,
day: 1,
fixed: RataDie::new(-2147483455),
saturating: false,
},
TestCase {
year: max_year,
month: 6,
day: 11,
fixed: RataDie::new(i32::MAX as i64 - 30),
saturating: false,
},
TestCase {
year: max_year,
month: 7,
day: 9,
fixed: RataDie::new(i32::MAX as i64 - 2),
saturating: false,
},
TestCase {
year: max_year,
month: 7,
day: 10,
fixed: RataDie::new(i32::MAX as i64 - 1),
saturating: false,
},
TestCase {
// Latest date that can be represented before causing a maximum overflow
year: max_year,
month: 7,
day: 11,
fixed: RataDie::new(i32::MAX as i64),
saturating: false,
},
TestCase {
year: max_year,
month: 7,
day: 12,
fixed: RataDie::new(i32::MAX as i64 + 1),
saturating: false,
},
TestCase {
year: i32::MIN,
month: 1,
day: 2,
fixed: RataDie::new(-784352296669),
saturating: false,
},
TestCase {
year: i32::MIN,
month: 1,
day: 1,
fixed: RataDie::new(-784352296670),
saturating: false,
},
TestCase {
year: i32::MIN,
month: 1,
day: 1,
fixed: RataDie::new(-784352296671),
saturating: true,
},
TestCase {
year: i32::MAX,
month: 12,
day: 30,
fixed: RataDie::new(784352295938),
saturating: false,
},
TestCase {
year: i32::MAX,
month: 12,
day: 31,
fixed: RataDie::new(784352295939),
saturating: false,
},
TestCase {
year: i32::MAX,
month: 12,
day: 31,
fixed: RataDie::new(784352295940),
saturating: true,
},
];
for case in cases {
let date = Date::try_new_iso_date(case.year, case.month, case.day).unwrap();
if !case.saturating {
assert_eq!(Iso::fixed_from_iso(date.inner), case.fixed, "{case:?}");
}
assert_eq!(Iso::iso_from_fixed(case.fixed), date, "{case:?}");
}
}
// Calculates the minimum possible year representable using a large negative fixed date
#[test]
fn min_year() {
assert_eq!(
Iso::iso_from_fixed(RataDie::big_negative()).year().number,
i32::MIN
);
}
#[test]
fn test_day_of_week() {
// June 23, 2021 is a Wednesday
assert_eq!(
Date::try_new_iso_date(2021, 6, 23).unwrap().day_of_week(),
IsoWeekday::Wednesday,
);
// Feb 2, 1983 was a Wednesday
assert_eq!(
Date::try_new_iso_date(1983, 2, 2).unwrap().day_of_week(),
IsoWeekday::Wednesday,
);
// Jan 21, 2021 was a Tuesday
assert_eq!(
Date::try_new_iso_date(2020, 1, 21).unwrap().day_of_week(),
IsoWeekday::Tuesday,
);
}
#[test]
fn test_day_of_year() {
// June 23, 2021 was day 174
assert_eq!(
Date::try_new_iso_date(2021, 6, 23)
.unwrap()
.day_of_year_info()
.day_of_year,
174,
);
// June 23, 2020 was day 175
assert_eq!(
Date::try_new_iso_date(2020, 6, 23)
.unwrap()
.day_of_year_info()
.day_of_year,
175,
);
// Feb 2, 1983 was a Wednesday
assert_eq!(
Date::try_new_iso_date(1983, 2, 2)
.unwrap()
.day_of_year_info()
.day_of_year,
33,
);
}
fn simple_subtract(a: &Date<Iso>, b: &Date<Iso>) -> DateDuration<Iso> {
let a = a.inner();
let b = b.inner();
DateDuration::new(
a.0.year - b.0.year,
a.0.month as i32 - b.0.month as i32,
0,
a.0.day as i32 - b.0.day as i32,
)
}
#[test]
fn test_offset() {
let today = Date::try_new_iso_date(2021, 6, 23).unwrap();
let today_plus_5000 = Date::try_new_iso_date(2035, 3, 2).unwrap();
let offset = today.added(DateDuration::new(0, 0, 0, 5000));
assert_eq!(offset, today_plus_5000);
let offset = today.added(simple_subtract(&today_plus_5000, &today));
assert_eq!(offset, today_plus_5000);
let today = Date::try_new_iso_date(2021, 6, 23).unwrap();
let today_minus_5000 = Date::try_new_iso_date(2007, 10, 15).unwrap();
let offset = today.added(DateDuration::new(0, 0, 0, -5000));
assert_eq!(offset, today_minus_5000);
let offset = today.added(simple_subtract(&today_minus_5000, &today));
assert_eq!(offset, today_minus_5000);
}
#[test]
fn test_offset_at_month_boundary() {
let today = Date::try_new_iso_date(2020, 2, 28).unwrap();
let today_plus_2 = Date::try_new_iso_date(2020, 3, 1).unwrap();
let offset = today.added(DateDuration::new(0, 0, 0, 2));
assert_eq!(offset, today_plus_2);
let today = Date::try_new_iso_date(2020, 2, 28).unwrap();
let today_plus_3 = Date::try_new_iso_date(2020, 3, 2).unwrap();
let offset = today.added(DateDuration::new(0, 0, 0, 3));
assert_eq!(offset, today_plus_3);
let today = Date::try_new_iso_date(2020, 2, 28).unwrap();
let today_plus_1 = Date::try_new_iso_date(2020, 2, 29).unwrap();
let offset = today.added(DateDuration::new(0, 0, 0, 1));
assert_eq!(offset, today_plus_1);
let today = Date::try_new_iso_date(2019, 2, 28).unwrap();
let today_plus_2 = Date::try_new_iso_date(2019, 3, 2).unwrap();
let offset = today.added(DateDuration::new(0, 0, 0, 2));
assert_eq!(offset, today_plus_2);
let today = Date::try_new_iso_date(2019, 2, 28).unwrap();
let today_plus_1 = Date::try_new_iso_date(2019, 3, 1).unwrap();
let offset = today.added(DateDuration::new(0, 0, 0, 1));
assert_eq!(offset, today_plus_1);
let today = Date::try_new_iso_date(2020, 3, 1).unwrap();
let today_minus_1 = Date::try_new_iso_date(2020, 2, 29).unwrap();
let offset = today.added(DateDuration::new(0, 0, 0, -1));
assert_eq!(offset, today_minus_1);
}
#[test]
fn test_offset_handles_negative_month_offset() {
let today = Date::try_new_iso_date(2020, 3, 1).unwrap();
let today_minus_2_months = Date::try_new_iso_date(2020, 1, 1).unwrap();
let offset = today.added(DateDuration::new(0, -2, 0, 0));
assert_eq!(offset, today_minus_2_months);
let today = Date::try_new_iso_date(2020, 3, 1).unwrap();
let today_minus_4_months = Date::try_new_iso_date(2019, 11, 1).unwrap();
let offset = today.added(DateDuration::new(0, -4, 0, 0));
assert_eq!(offset, today_minus_4_months);
let today = Date::try_new_iso_date(2020, 3, 1).unwrap();
let today_minus_24_months = Date::try_new_iso_date(2018, 3, 1).unwrap();
let offset = today.added(DateDuration::new(0, -24, 0, 0));
assert_eq!(offset, today_minus_24_months);
let today = Date::try_new_iso_date(2020, 3, 1).unwrap();
let today_minus_27_months = Date::try_new_iso_date(2017, 12, 1).unwrap();
let offset = today.added(DateDuration::new(0, -27, 0, 0));
assert_eq!(offset, today_minus_27_months);
}
#[test]
fn test_offset_handles_out_of_bound_month_offset() {
let today = Date::try_new_iso_date(2021, 1, 31).unwrap();
// since 2021/02/31 isn't a valid date, `offset_date` auto-adjusts by adding 3 days to 2021/02/28
let today_plus_1_month = Date::try_new_iso_date(2021, 3, 3).unwrap();
let offset = today.added(DateDuration::new(0, 1, 0, 0));
assert_eq!(offset, today_plus_1_month);
let today = Date::try_new_iso_date(2021, 1, 31).unwrap();
// since 2021/02/31 isn't a valid date, `offset_date` auto-adjusts by adding 3 days to 2021/02/28
let today_plus_1_month_1_day = Date::try_new_iso_date(2021, 3, 4).unwrap();
let offset = today.added(DateDuration::new(0, 1, 0, 1));
assert_eq!(offset, today_plus_1_month_1_day);
}
#[test]
fn test_iso_to_from_fixed() {
// Reminder: ISO year 0 is Gregorian year 1 BCE.
// Year 0 is a leap year due to the 400-year rule.
fn check(fixed: i64, year: i32, month: u8, day: u8) {
let fixed = RataDie::new(fixed);
assert_eq!(
Iso::iso_from_fixed(fixed),
Date::try_new_iso_date(year, month, day).unwrap(),
"fixed: {fixed:?}"
);
}
check(-1828, -5, 12, 30);
check(-1827, -5, 12, 31); // leap year
check(-1826, -4, 1, 1);
check(-1462, -4, 12, 30);
check(-1461, -4, 12, 31);
check(-1460, -3, 1, 1);
check(-1459, -3, 1, 2);
check(-732, -2, 12, 30);
check(-731, -2, 12, 31);
check(-730, -1, 1, 1);
check(-367, -1, 12, 30);
check(-366, -1, 12, 31);
check(-365, 0, 1, 1); // leap year
check(-364, 0, 1, 2);
check(-1, 0, 12, 30);
check(0, 0, 12, 31);
check(1, 1, 1, 1);
check(2, 1, 1, 2);
check(364, 1, 12, 30);
check(365, 1, 12, 31);
check(366, 2, 1, 1);
check(1459, 4, 12, 29);
check(1460, 4, 12, 30);
check(1461, 4, 12, 31); // leap year
check(1462, 5, 1, 1);
}
#[test]
fn test_from_minutes_since_local_unix_epoch() {
fn check(minutes: i32, year: i32, month: u8, day: u8, hour: u8, minute: u8) {
let today = DateTime::try_new_iso_datetime(year, month, day, hour, minute, 0).unwrap();
assert_eq!(today.minutes_since_local_unix_epoch(), minutes);
assert_eq!(
DateTime::from_minutes_since_local_unix_epoch(minutes),
today
);
}
check(-1441, 1969, 12, 30, 23, 59);
check(-1440, 1969, 12, 31, 0, 0);
check(-1439, 1969, 12, 31, 0, 1);
check(-2879, 1969, 12, 30, 0, 1);
}
}

988
third_party/rust/icu_calendar/src/japanese.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,988 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
//! This module contains types and implementations for the Japanese calendar.
//!
//! ```rust
//! use icu::calendar::japanese::Japanese;
//! use icu::calendar::{types::Era, Date, DateTime};
//! use tinystr::tinystr;
//!
//! let japanese_calendar = Japanese::new();
//!
//! // `Date` type
//! let date_iso = Date::try_new_iso_date(1970, 1, 2)
//! .expect("Failed to initialize ISO Date instance.");
//! let date_japanese = Date::new_from_iso(date_iso, japanese_calendar.clone());
//!
//! // `DateTime` type
//! let datetime_iso = DateTime::try_new_iso_datetime(1970, 1, 2, 13, 1, 0)
//! .expect("Failed to initialize ISO DateTime instance.");
//! let datetime_japanese =
//! DateTime::new_from_iso(datetime_iso, japanese_calendar.clone());
//!
//! // `Date` checks
//! assert_eq!(date_japanese.year().number, 45);
//! assert_eq!(date_japanese.month().ordinal, 1);
//! assert_eq!(date_japanese.day_of_month().0, 2);
//! assert_eq!(date_japanese.year().era, Era(tinystr!(16, "showa")));
//!
//! // `DateTime` type
//! assert_eq!(datetime_japanese.date.year().number, 45);
//! assert_eq!(datetime_japanese.date.month().ordinal, 1);
//! assert_eq!(datetime_japanese.date.day_of_month().0, 2);
//! assert_eq!(
//! datetime_japanese.date.year().era,
//! Era(tinystr!(16, "showa"))
//! );
//! assert_eq!(datetime_japanese.time.hour.number(), 13);
//! assert_eq!(datetime_japanese.time.minute.number(), 1);
//! assert_eq!(datetime_japanese.time.second.number(), 0);
//! ```
use crate::any_calendar::AnyCalendarKind;
use crate::iso::{Iso, IsoDateInner};
use crate::provider::{EraStartDate, JapaneseErasV1Marker, JapaneseExtendedErasV1Marker};
use crate::{
types, AsCalendar, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime, Ref,
};
use icu_provider::prelude::*;
use tinystr::{tinystr, TinyStr16};
/// The [Japanese Calendar] (with modern eras only)
///
/// The [Japanese calendar] is a solar calendar used in Japan, with twelve months.
/// The months and days are identical to that of the Gregorian calendar, however the years are counted
/// differently using the Japanese era system.
///
/// This calendar only contains eras after Meiji, for all historical eras, check out [`JapaneseExtended`].
///
/// This type can be used with [`Date`] or [`DateTime`] to represent dates in this calendar.
///
/// [Japanese calendar]: https://en.wikipedia.org/wiki/Japanese_calendar
///
/// # Era codes
///
/// This calendar currently supports seven era codes. It supports the five post-Meiji eras
/// (`"meiji"`, `"taisho"`, `"showa"`, `"heisei"`, `"reiwa"`), as well as using the Gregorian
/// `"bce"` and `"ce"` for dates before the Meiji era.
///
/// Future eras will also be added to this type when they are decided.
///
/// These eras are loaded from data, requiring a data provider capable of providing [`JapaneseErasV1Marker`]
/// data (`calendar/japanese@1`).
///
/// # Month codes
///
/// This calendar supports 12 solar month codes (`"M01" - "M12"`)
#[derive(Clone, Debug, Default)]
pub struct Japanese {
eras: DataPayload<JapaneseErasV1Marker>,
}
/// The [Japanese Calendar] (with historical eras)
///
/// The [Japanese calendar] is a solar calendar used in Japan, with twelve months.
/// The months and days are identical to that of the Gregorian calendar, however the years are counted
/// differently using the Japanese era system.
///
/// This type can be used with [`Date`] or [`DateTime`] to represent dates in this calendar.
///
/// [Japanese calendar]: https://en.wikipedia.org/wiki/Japanese_calendar
///
/// # Era codes
///
/// This calendar supports a large number of era codes. It supports the five post-Meiji eras
/// (`"meiji"`, `"taisho"`, `"showa"`, `"heisei"`, `"reiwa"`). Pre-Meiji eras are represented
/// with their names converted to lowercase ascii and followed by their start year. E.g. the "Ten'ō"
/// era (781 - 782 CE) has the code `"teno-781"`. The Gregorian `"bce"` and `"ce"` eras
/// are used for dates before the first known era era.
///
///
/// These eras are loaded from data, requiring a data provider capable of providing [`JapaneseExtendedErasV1Marker`]
/// data (`calendar/japanext@1`).
#[derive(Clone, Debug, Default)]
pub struct JapaneseExtended(Japanese);
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
/// The inner date type used for representing [`Date`]s of [`Japanese`]. See [`Date`] and [`Japanese`] for more details.
pub struct JapaneseDateInner {
inner: IsoDateInner,
adjusted_year: i32,
era: TinyStr16,
}
impl Japanese {
/// Creates a new [`Japanese`] using only modern eras (post-meiji) from compiled data.
///
/// ✨ *Enabled with the `compiled_data` Cargo feature.*
///
/// [📚 Help choosing a constructor](icu_provider::constructors)
#[cfg(feature = "compiled_data")]
pub const fn new() -> Self {
Self {
eras: DataPayload::from_static_ref(
crate::provider::Baked::SINGLETON_CALENDAR_JAPANESE_V1,
),
}
}
icu_provider::gen_any_buffer_data_constructors!(locale: skip, options: skip, error: CalendarError,
#[cfg(skip)]
functions: [
new,
try_new_with_any_provider,
try_new_with_buffer_provider,
try_new_unstable,
Self,
]);
#[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::new)]
pub fn try_new_unstable<D: DataProvider<JapaneseErasV1Marker> + ?Sized>(
provider: &D,
) -> Result<Self, CalendarError> {
Ok(Self {
eras: provider.load(Default::default())?.take_payload()?,
})
}
fn japanese_date_from_codes(
&self,
era: types::Era,
year: i32,
month_code: types::MonthCode,
day: u8,
debug_name: &'static str,
) -> Result<JapaneseDateInner, CalendarError> {
let month = month_code.parsed();
let month = if let Some((month, false)) = month {
month
} else {
return Err(CalendarError::UnknownMonthCode(month_code.0, debug_name));
};
if month > 12 {
return Err(CalendarError::UnknownMonthCode(month_code.0, debug_name));
}
self.new_japanese_date_inner(era, year, month, day)
}
pub(crate) const DEBUG_NAME: &'static str = "Japanese";
}
impl JapaneseExtended {
/// Creates a new [`Japanese`] from using all eras (including pre-meiji) from compiled data.
///
/// ✨ *Enabled with the `compiled_data` Cargo feature.*
///
/// [📚 Help choosing a constructor](icu_provider::constructors)
#[cfg(feature = "compiled_data")]
pub const fn new() -> Self {
Self(Japanese {
eras: DataPayload::from_static_ref(
crate::provider::Baked::SINGLETON_CALENDAR_JAPANEXT_V1,
),
})
}
icu_provider::gen_any_buffer_data_constructors!(locale: skip, options: skip, error: CalendarError,
#[cfg(skip)]
functions: [
new,
try_new_with_any_provider,
try_new_with_buffer_provider,
try_new_unstable,
Self,
]);
#[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::new)]
pub fn try_new_unstable<D: DataProvider<JapaneseExtendedErasV1Marker> + ?Sized>(
provider: &D,
) -> Result<Self, CalendarError> {
Ok(Self(Japanese {
eras: provider.load(Default::default())?.take_payload()?.cast(),
}))
}
pub(crate) const DEBUG_NAME: &'static str = "Japanese (historical era data)";
}
impl Calendar for Japanese {
type DateInner = JapaneseDateInner;
fn date_from_codes(
&self,
era: types::Era,
year: i32,
month_code: types::MonthCode,
day: u8,
) -> Result<Self::DateInner, CalendarError> {
self.japanese_date_from_codes(era, year, month_code, day, self.debug_name())
}
fn date_from_iso(&self, iso: Date<Iso>) -> JapaneseDateInner {
let (adjusted_year, era) = self.adjusted_year_for(iso.inner());
JapaneseDateInner {
inner: *iso.inner(),
adjusted_year,
era,
}
}
fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
Date::from_raw(date.inner, Iso)
}
fn months_in_year(&self, date: &Self::DateInner) -> u8 {
Iso.months_in_year(&date.inner)
}
fn days_in_year(&self, date: &Self::DateInner) -> u16 {
Iso.days_in_year(&date.inner)
}
fn days_in_month(&self, date: &Self::DateInner) -> u8 {
Iso.days_in_month(&date.inner)
}
fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
Iso.offset_date(&mut date.inner, offset.cast_unit());
let (adjusted_year, era) = self.adjusted_year_for(&date.inner);
date.adjusted_year = adjusted_year;
date.era = era
}
fn until(
&self,
date1: &Self::DateInner,
date2: &Self::DateInner,
_calendar2: &Self,
largest_unit: DateDurationUnit,
smallest_unit: DateDurationUnit,
) -> DateDuration<Self> {
Iso.until(
&date1.inner,
&date2.inner,
&Iso,
largest_unit,
smallest_unit,
)
.cast_unit()
}
/// The calendar-specific year represented by `date`
fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
types::FormattableYear {
era: types::Era(date.era),
number: date.adjusted_year,
cyclic: None,
related_iso: None,
}
}
fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
Iso.is_in_leap_year(&date.inner)
}
/// The calendar-specific month represented by `date`
fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
Iso.month(&date.inner)
}
/// The calendar-specific day-of-month represented by `date`
fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
Iso.day_of_month(&date.inner)
}
/// Information of the day of the year
fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
let prev_dec_31 = IsoDateInner::dec_31(date.inner.0.year - 1);
let next_jan_1 = IsoDateInner::jan_1(date.inner.0.year + 1);
let prev_dec_31 = self.date_from_iso(Date::from_raw(prev_dec_31, Iso));
let next_jan_1 = self.date_from_iso(Date::from_raw(next_jan_1, Iso));
types::DayOfYearInfo {
day_of_year: Iso::days_in_year_direct(date.inner.0.year),
days_in_year: Iso::days_in_year_direct(date.inner.0.year),
prev_year: self.year(&prev_dec_31),
days_in_prev_year: Iso::days_in_year_direct(prev_dec_31.inner.0.year),
next_year: self.year(&next_jan_1),
}
}
fn debug_name(&self) -> &'static str {
Self::DEBUG_NAME
}
fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
Some(AnyCalendarKind::Japanese)
}
}
impl Calendar for JapaneseExtended {
type DateInner = JapaneseDateInner;
fn date_from_codes(
&self,
era: types::Era,
year: i32,
month_code: types::MonthCode,
day: u8,
) -> Result<Self::DateInner, CalendarError> {
self.0
.japanese_date_from_codes(era, year, month_code, day, self.debug_name())
}
fn date_from_iso(&self, iso: Date<Iso>) -> JapaneseDateInner {
Japanese::date_from_iso(&self.0, iso)
}
fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
Japanese::date_to_iso(&self.0, date)
}
fn months_in_year(&self, date: &Self::DateInner) -> u8 {
Japanese::months_in_year(&self.0, date)
}
fn days_in_year(&self, date: &Self::DateInner) -> u16 {
Japanese::days_in_year(&self.0, date)
}
fn days_in_month(&self, date: &Self::DateInner) -> u8 {
Japanese::days_in_month(&self.0, date)
}
fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
Japanese::offset_date(&self.0, date, offset.cast_unit())
}
fn until(
&self,
date1: &Self::DateInner,
date2: &Self::DateInner,
calendar2: &Self,
largest_unit: DateDurationUnit,
smallest_unit: DateDurationUnit,
) -> DateDuration<Self> {
Japanese::until(
&self.0,
date1,
date2,
&calendar2.0,
largest_unit,
smallest_unit,
)
.cast_unit()
}
/// The calendar-specific year represented by `date`
fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
Japanese::year(&self.0, date)
}
fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
Japanese::is_in_leap_year(&self.0, date)
}
/// The calendar-specific month represented by `date`
fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
Japanese::month(&self.0, date)
}
/// The calendar-specific day-of-month represented by `date`
fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
Japanese::day_of_month(&self.0, date)
}
/// Information of the day of the year
fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
Japanese::day_of_year_info(&self.0, date)
}
fn debug_name(&self) -> &'static str {
Self::DEBUG_NAME
}
fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
Some(AnyCalendarKind::JapaneseExtended)
}
}
impl Date<Japanese> {
/// Construct a new Japanese Date.
///
/// Years are specified in the era provided, and must be in range for Japanese
/// eras (e.g. dates past April 30 Heisei 31 must be in Reiwa; "Jun 5 Heisei 31" and "Jan 1 Heisei 32"
/// will not be adjusted to being in Reiwa 1 and 2 respectively)
///
/// However, dates may always be specified in "bce" or "ce" and they will be adjusted as necessary.
///
/// ```rust
/// use icu::calendar::japanese::Japanese;
/// use icu::calendar::{types, Date, Ref};
/// use std::convert::TryFrom;
/// use tinystr::tinystr;
///
/// let japanese_calendar = Japanese::new();
/// // for easy sharing
/// let japanese_calendar = Ref(&japanese_calendar);
///
/// let era = types::Era(tinystr!(16, "heisei"));
///
/// let date = Date::try_new_japanese_date(era, 14, 1, 2, japanese_calendar)
/// .expect("Constructing a date should succeed");
///
/// assert_eq!(date.year().era, era);
/// assert_eq!(date.year().number, 14);
/// assert_eq!(date.month().ordinal, 1);
/// assert_eq!(date.day_of_month().0, 2);
///
/// // This function will error for eras that are out of bounds:
/// // (Heisei was 32 years long, Heisei 33 is in Reiwa)
/// let oob_date =
/// Date::try_new_japanese_date(era, 33, 1, 2, japanese_calendar);
/// assert!(oob_date.is_err());
///
/// // and for unknown eras
/// let fake_era = types::Era(tinystr!(16, "neko")); // 🐱
/// let fake_date =
/// Date::try_new_japanese_date(fake_era, 10, 1, 2, japanese_calendar);
/// assert!(fake_date.is_err());
/// ```
pub fn try_new_japanese_date<A: AsCalendar<Calendar = Japanese>>(
era: types::Era,
year: i32,
month: u8,
day: u8,
japanese_calendar: A,
) -> Result<Date<A>, CalendarError> {
let inner = japanese_calendar
.as_calendar()
.new_japanese_date_inner(era, year, month, day)?;
Ok(Date::from_raw(inner, japanese_calendar))
}
}
impl Date<JapaneseExtended> {
/// Construct a new Japanese Date with all eras.
///
/// Years are specified in the era provided, and must be in range for Japanese
/// eras (e.g. dates past April 30 Heisei 31 must be in Reiwa; "Jun 5 Heisei 31" and "Jan 1 Heisei 32"
/// will not be adjusted to being in Reiwa 1 and 2 respectively)
///
/// However, dates may always be specified in "bce" or "ce" and they will be adjusted as necessary.
///
/// ```rust
/// use icu::calendar::japanese::JapaneseExtended;
/// use icu::calendar::{types, Date, Ref};
/// use std::convert::TryFrom;
/// use tinystr::tinystr;
///
/// let japanext_calendar = JapaneseExtended::new();
/// // for easy sharing
/// let japanext_calendar = Ref(&japanext_calendar);
///
/// let era = types::Era(tinystr!(16, "kansei-1789"));
///
/// let date =
/// Date::try_new_japanese_extended_date(era, 7, 1, 2, japanext_calendar)
/// .expect("Constructing a date should succeed");
///
/// assert_eq!(date.year().era, era);
/// assert_eq!(date.year().number, 7);
/// assert_eq!(date.month().ordinal, 1);
/// assert_eq!(date.day_of_month().0, 2);
/// ```
pub fn try_new_japanese_extended_date<A: AsCalendar<Calendar = JapaneseExtended>>(
era: types::Era,
year: i32,
month: u8,
day: u8,
japanext_calendar: A,
) -> Result<Date<A>, CalendarError> {
let inner = japanext_calendar
.as_calendar()
.0
.new_japanese_date_inner(era, year, month, day)?;
Ok(Date::from_raw(inner, japanext_calendar))
}
/// For testing era fallback in icu_datetime
#[doc(hidden)]
pub fn into_japanese_date(self) -> Date<Japanese> {
Date::from_raw(self.inner, self.calendar.0)
}
}
impl DateTime<Japanese> {
/// Construct a new Japanese datetime from integers.
///
/// Years are specified in the era provided.
///
/// ```rust
/// use icu::calendar::japanese::Japanese;
/// use icu::calendar::{types, DateTime};
/// use std::convert::TryFrom;
/// use tinystr::tinystr;
///
/// let japanese_calendar = Japanese::new();
///
/// let era = types::Era(tinystr!(16, "heisei"));
///
/// let datetime = DateTime::try_new_japanese_datetime(
/// era,
/// 14,
/// 1,
/// 2,
/// 13,
/// 1,
/// 0,
/// japanese_calendar,
/// )
/// .expect("Constructing a date should succeed");
///
/// assert_eq!(datetime.date.year().era, era);
/// assert_eq!(datetime.date.year().number, 14);
/// assert_eq!(datetime.date.month().ordinal, 1);
/// assert_eq!(datetime.date.day_of_month().0, 2);
/// assert_eq!(datetime.time.hour.number(), 13);
/// assert_eq!(datetime.time.minute.number(), 1);
/// assert_eq!(datetime.time.second.number(), 0);
/// ```
#[allow(clippy::too_many_arguments)] // it's more convenient to have this many arguments
// if people wish to construct this by parts they can use
// Date::try_new_japanese_date() + DateTime::new(date, time)
pub fn try_new_japanese_datetime<A: AsCalendar<Calendar = Japanese>>(
era: types::Era,
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
japanese_calendar: A,
) -> Result<DateTime<A>, CalendarError> {
Ok(DateTime {
date: Date::try_new_japanese_date(era, year, month, day, japanese_calendar)?,
time: types::Time::try_new(hour, minute, second, 0)?,
})
}
}
impl DateTime<JapaneseExtended> {
/// Construct a new Japanese datetime from integers with all eras.
///
/// Years are specified in the era provided.
///
/// ```rust
/// use icu::calendar::japanese::JapaneseExtended;
/// use icu::calendar::{types, DateTime};
/// use std::convert::TryFrom;
/// use tinystr::tinystr;
///
/// let japanext_calendar = JapaneseExtended::new();
///
/// let era = types::Era(tinystr!(16, "kansei-1789"));
///
/// let datetime = DateTime::try_new_japanese_extended_datetime(
/// era,
/// 7,
/// 1,
/// 2,
/// 13,
/// 1,
/// 0,
/// japanext_calendar,
/// )
/// .expect("Constructing a date should succeed");
///
/// assert_eq!(datetime.date.year().era, era);
/// assert_eq!(datetime.date.year().number, 7);
/// assert_eq!(datetime.date.month().ordinal, 1);
/// assert_eq!(datetime.date.day_of_month().0, 2);
/// assert_eq!(datetime.time.hour.number(), 13);
/// assert_eq!(datetime.time.minute.number(), 1);
/// assert_eq!(datetime.time.second.number(), 0);
/// ```
#[allow(clippy::too_many_arguments)] // it's more convenient to have this many arguments
// if people wish to construct this by parts they can use
// Date::try_new_japanese_date() + DateTime::new(date, time)
pub fn try_new_japanese_extended_datetime<A: AsCalendar<Calendar = JapaneseExtended>>(
era: types::Era,
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
japanext_calendar: A,
) -> Result<DateTime<A>, CalendarError> {
Ok(DateTime {
date: Date::try_new_japanese_extended_date(era, year, month, day, japanext_calendar)?,
time: types::Time::try_new(hour, minute, second, 0)?,
})
}
}
const MEIJI_START: EraStartDate = EraStartDate {
year: 1868,
month: 9,
day: 8,
};
const TAISHO_START: EraStartDate = EraStartDate {
year: 1912,
month: 7,
day: 30,
};
const SHOWA_START: EraStartDate = EraStartDate {
year: 1926,
month: 12,
day: 25,
};
const HEISEI_START: EraStartDate = EraStartDate {
year: 1989,
month: 1,
day: 8,
};
const REIWA_START: EraStartDate = EraStartDate {
year: 2019,
month: 5,
day: 1,
};
const FALLBACK_ERA: (EraStartDate, TinyStr16) = (REIWA_START, tinystr!(16, "reiwa"));
impl Japanese {
/// Given an ISO date, give year and era for that date in the Japanese calendar
///
/// This will also use Gregorian eras for eras that are before the earliest era
fn adjusted_year_for(&self, date: &IsoDateInner) -> (i32, TinyStr16) {
let date: EraStartDate = date.into();
let (start, era) = self.japanese_era_for(date);
// The year in which an era starts is Year 1, and it may be short
// The only time this function will experience dates that are *before*
// the era start date are for the first era (Currently, taika-645
// for japanext, meiji for japanese),
// In such a case, we instead fall back to Gregorian era codes
if date < start {
if date.year < 0 {
(1 - date.year, tinystr!(16, "bce"))
} else {
(date.year, tinystr!(16, "ce"))
}
} else {
(date.year - start.year + 1, era)
}
}
/// Given an date, obtain the era data (not counting spliced gregorian eras)
fn japanese_era_for(&self, date: EraStartDate) -> (EraStartDate, TinyStr16) {
let era_data = self.eras.get();
// We optimize for the five "modern" post-Meiji eras, which are stored in a smaller
// array and also hardcoded. The hardcoded version is not used if data indicates the
// presence of newer eras.
if date >= MEIJI_START
&& era_data.dates_to_eras.last().map(|x| x.1) == Some(tinystr!(16, "reiwa"))
{
// Fast path in case eras have not changed since this code was written
return if date >= REIWA_START {
(REIWA_START, tinystr!(16, "reiwa"))
} else if date >= HEISEI_START {
(HEISEI_START, tinystr!(16, "heisei"))
} else if date >= SHOWA_START {
(SHOWA_START, tinystr!(16, "showa"))
} else if date >= TAISHO_START {
(TAISHO_START, tinystr!(16, "taisho"))
} else {
(MEIJI_START, tinystr!(16, "meiji"))
};
}
let data = &era_data.dates_to_eras;
match data.binary_search_by(|(d, _)| d.cmp(&date)) {
Ok(index) => data.get(index),
Err(index) if index == 0 => data.get(index),
Err(index) => data.get(index - 1).or_else(|| data.iter().next_back()),
}
.unwrap_or(FALLBACK_ERA)
}
/// Returns the range of dates for a given Japanese era code,
/// not handling "bce" or "ce"
///
/// Returns (era_start, era_end)
fn japanese_era_range_for(
&self,
era: TinyStr16,
) -> Result<(EraStartDate, Option<EraStartDate>), CalendarError> {
// Avoid linear search by trying well known eras
if era == tinystr!(16, "reiwa") {
// Check if we're the last
if let Some(last) = self.eras.get().dates_to_eras.last() {
if last.1 == era {
return Ok((REIWA_START, None));
}
}
} else if era == tinystr!(16, "heisei") {
return Ok((HEISEI_START, Some(REIWA_START)));
} else if era == tinystr!(16, "showa") {
return Ok((SHOWA_START, Some(HEISEI_START)));
} else if era == tinystr!(16, "taisho") {
return Ok((TAISHO_START, Some(SHOWA_START)));
} else if era == tinystr!(16, "meiji") {
return Ok((MEIJI_START, Some(TAISHO_START)));
}
let era_data = self.eras.get();
let data = &era_data.dates_to_eras;
// Try to avoid linear search by binary searching for the year suffix
if let Some(year) = era.split('-').nth(1) {
if let Ok(ref int) = year.parse::<i32>() {
if let Ok(index) = data.binary_search_by(|(d, _)| d.year.cmp(int)) {
#[allow(clippy::expect_used)] // see expect message
let (era_start, code) = data
.get(index)
.expect("Indexing from successful binary search must succeed");
// There is a slight chance we hit the case where there are two eras in the same year
// There are a couple of rare cases of this, but it's not worth writing a range-based binary search
// to catch them since this is an optimization
if code == era {
return Ok((era_start, data.get(index + 1).map(|e| e.0)));
}
}
}
}
// Avoidance didn't work. Let's find the era manually, searching back from the present
if let Some((index, (start, _))) = data.iter().enumerate().rev().find(|d| d.1 .1 == era) {
return Ok((start, data.get(index + 1).map(|e| e.0)));
}
Err(CalendarError::UnknownEra(era, self.debug_name()))
}
fn new_japanese_date_inner(
&self,
era: types::Era,
year: i32,
month: u8,
day: u8,
) -> Result<JapaneseDateInner, CalendarError> {
let cal = Ref(self);
if era.0 == tinystr!(16, "bce") {
if year <= 0 {
return Err(CalendarError::OutOfRange);
}
return Ok(Date::try_new_iso_date(1 - year, month, day)?
.to_calendar(cal)
.inner);
} else if era.0 == tinystr!(16, "ce") {
if year <= 0 {
return Err(CalendarError::OutOfRange);
}
return Ok(Date::try_new_iso_date(year, month, day)?
.to_calendar(cal)
.inner);
}
let (era_start, next_era_start) = self.japanese_era_range_for(era.0)?;
let date_in_iso = EraStartDate {
year: era_start.year + year - 1,
month,
day,
};
if date_in_iso < era_start {
return Err(CalendarError::OutOfRange);
} else if let Some(next_era_start) = next_era_start {
if date_in_iso >= next_era_start {
return Err(CalendarError::OutOfRange);
}
}
let iso = Date::try_new_iso_date(date_in_iso.year, date_in_iso.month, date_in_iso.day)?;
Ok(JapaneseDateInner {
inner: iso.inner,
adjusted_year: year,
era: era.0,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Ref;
fn single_test_roundtrip(calendar: Ref<Japanese>, era: &str, year: i32, month: u8, day: u8) {
let era = types::Era(era.parse().expect("era must parse"));
let date =
Date::try_new_japanese_date(era, year, month, day, calendar).unwrap_or_else(|e| {
panic!("Failed to construct date with {era:?}, {year}, {month}, {day}: {e}")
});
let iso = date.to_iso();
let reconstructed = Date::new_from_iso(iso, calendar);
assert_eq!(
date, reconstructed,
"Failed to roundtrip with {era:?}, {year}, {month}, {day}"
)
}
fn single_test_roundtrip_ext(
calendar: Ref<JapaneseExtended>,
era: &str,
year: i32,
month: u8,
day: u8,
) {
let era = types::Era(era.parse().expect("era must parse"));
let date = Date::try_new_japanese_extended_date(era, year, month, day, calendar)
.unwrap_or_else(|e| {
panic!("Failed to construct date with {era:?}, {year}, {month}, {day}: {e}")
});
let iso = date.to_iso();
let reconstructed = Date::new_from_iso(iso, calendar);
assert_eq!(
date, reconstructed,
"Failed to roundtrip with {era:?}, {year}, {month}, {day}"
)
}
// test that the Gregorian eras roundtrip to Japanese ones
fn single_test_gregorian_roundtrip_ext(
calendar: Ref<JapaneseExtended>,
era: &str,
year: i32,
month: u8,
day: u8,
era2: &str,
year2: i32,
) {
let era = types::Era(era.parse().expect("era must parse"));
let era2 = types::Era(era2.parse().expect("era must parse"));
let expected = Date::try_new_japanese_extended_date(era2, year2, month, day, calendar)
.unwrap_or_else(|e| {
panic!(
"Failed to construct expectation date with {era2:?}, {year2}, {month}, {day}: {e}"
)
});
let date = Date::try_new_japanese_extended_date(era, year, month, day, calendar)
.unwrap_or_else(|e| {
panic!("Failed to construct date with {era:?}, {year}, {month}, {day}: {e}")
});
let iso = date.to_iso();
let reconstructed = Date::new_from_iso(iso, calendar);
assert_eq!(
expected, reconstructed,
"Failed to roundtrip with {era:?}, {year}, {month}, {day} == {era2:?}, {year}"
)
}
fn single_test_error(
calendar: Ref<Japanese>,
era: &str,
year: i32,
month: u8,
day: u8,
error: CalendarError,
) {
let era = types::Era(era.parse().expect("era must parse"));
let date = Date::try_new_japanese_date(era, year, month, day, calendar);
assert_eq!(
date,
Err(error),
"Construction with {era:?}, {year}, {month}, {day} did not return {error:?}"
)
}
fn single_test_error_ext(
calendar: Ref<JapaneseExtended>,
era: &str,
year: i32,
month: u8,
day: u8,
error: CalendarError,
) {
let era = types::Era(era.parse().expect("era must parse"));
let date = Date::try_new_japanese_extended_date(era, year, month, day, calendar);
assert_eq!(
date,
Err(error),
"Construction with {era:?}, {year}, {month}, {day} did not return {error:?}"
)
}
#[test]
fn test_japanese() {
let calendar = Japanese::new();
let calendar_ext = JapaneseExtended::new();
let calendar = Ref(&calendar);
let calendar_ext = Ref(&calendar_ext);
single_test_roundtrip(calendar, "heisei", 12, 3, 1);
single_test_roundtrip(calendar, "taisho", 3, 3, 1);
// Heisei did not start until later in the year
single_test_error(calendar, "heisei", 1, 1, 1, CalendarError::OutOfRange);
single_test_roundtrip_ext(calendar_ext, "heisei", 12, 3, 1);
single_test_roundtrip_ext(calendar_ext, "taisho", 3, 3, 1);
single_test_error_ext(calendar_ext, "heisei", 1, 1, 1, CalendarError::OutOfRange);
single_test_roundtrip_ext(calendar_ext, "hakuho-672", 4, 3, 1);
single_test_error(
calendar,
"hakuho-672",
4,
3,
1,
CalendarError::UnknownEra("hakuho-672".parse().unwrap(), "Japanese"),
);
// handle bce/ce
single_test_roundtrip(calendar, "bce", 100, 3, 1);
single_test_roundtrip(calendar, "bce", 1, 3, 1);
single_test_roundtrip(calendar, "ce", 1, 3, 1);
single_test_roundtrip(calendar, "ce", 100, 3, 1);
single_test_roundtrip_ext(calendar_ext, "ce", 100, 3, 1);
single_test_roundtrip(calendar, "ce", 1000, 3, 1);
single_test_error(calendar, "ce", 0, 3, 1, CalendarError::OutOfRange);
single_test_error(calendar, "bce", -1, 3, 1, CalendarError::OutOfRange);
// handle the cases where bce/ce get adjusted to different eras
// single_test_gregorian_roundtrip(calendar, "ce", 2021, 3, 1, "reiwa", 3);
single_test_gregorian_roundtrip_ext(calendar_ext, "ce", 1000, 3, 1, "choho-999", 2);
single_test_gregorian_roundtrip_ext(calendar_ext, "ce", 749, 5, 10, "tenpyokampo-749", 1);
single_test_gregorian_roundtrip_ext(calendar_ext, "bce", 10, 3, 1, "bce", 10);
// There were multiple eras in this year
// This one is from Apr 14 to July 2
single_test_roundtrip_ext(calendar_ext, "tenpyokampo-749", 1, 4, 20);
single_test_roundtrip_ext(calendar_ext, "tenpyokampo-749", 1, 4, 14);
single_test_roundtrip_ext(calendar_ext, "tenpyokampo-749", 1, 7, 1);
single_test_error_ext(
calendar_ext,
"tenpyokampo-749",
1,
7,
5,
CalendarError::OutOfRange,
);
single_test_error_ext(
calendar_ext,
"tenpyokampo-749",
1,
4,
13,
CalendarError::OutOfRange,
);
}
}

556
third_party/rust/icu_calendar/src/julian.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,556 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
//! This module contains types and implementations for the Julian calendar.
//!
//! ```rust
//! use icu::calendar::{julian::Julian, Date, DateTime};
//!
//! // `Date` type
//! let date_iso = Date::try_new_iso_date(1970, 1, 2)
//! .expect("Failed to initialize ISO Date instance.");
//! let date_julian = Date::new_from_iso(date_iso, Julian);
//!
//! // `DateTime` type
//! let datetime_iso = DateTime::try_new_iso_datetime(1970, 1, 2, 13, 1, 0)
//! .expect("Failed to initialize ISO DateTime instance.");
//! let datetime_julian = DateTime::new_from_iso(datetime_iso, Julian);
//!
//! // `Date` checks
//! assert_eq!(date_julian.year().number, 1969);
//! assert_eq!(date_julian.month().ordinal, 12);
//! assert_eq!(date_julian.day_of_month().0, 20);
//!
//! // `DateTime` type
//! assert_eq!(datetime_julian.date.year().number, 1969);
//! assert_eq!(datetime_julian.date.month().ordinal, 12);
//! assert_eq!(datetime_julian.date.day_of_month().0, 20);
//! assert_eq!(datetime_julian.time.hour.number(), 13);
//! assert_eq!(datetime_julian.time.minute.number(), 1);
//! assert_eq!(datetime_julian.time.second.number(), 0);
//! ```
use crate::any_calendar::AnyCalendarKind;
use crate::calendar_arithmetic::{ArithmeticDate, CalendarArithmetic};
use crate::gregorian::year_as_gregorian;
use crate::iso::Iso;
use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime};
use calendrical_calculations::helpers::I32CastError;
use calendrical_calculations::rata_die::RataDie;
use tinystr::tinystr;
/// The [Julian Calendar]
///
/// The [Julian calendar] is a solar calendar that was used commonly historically, with twelve months.
///
/// This type can be used with [`Date`] or [`DateTime`] to represent dates in this calendar.
///
/// [Julian calendar]: https://en.wikipedia.org/wiki/Julian_calendar
///
/// # Era codes
///
/// This calendar supports two era codes: `"bce"`, and `"ce"`, corresponding to the BCE/BC and CE/AD eras
///
/// # Month codes
///
/// This calendar supports 12 solar month codes (`"M01" - "M12"`)
#[derive(Copy, Clone, Debug, Hash, Default, Eq, PartialEq, PartialOrd, Ord)]
#[allow(clippy::exhaustive_structs)] // this type is stable
pub struct Julian;
/// The inner date type used for representing [`Date`]s of [`Julian`]. See [`Date`] and [`Julian`] for more details.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
// The inner date type used for representing Date<Julian>
pub struct JulianDateInner(pub(crate) ArithmeticDate<Julian>);
impl CalendarArithmetic for Julian {
fn month_days(year: i32, month: u8) -> u8 {
match month {
4 | 6 | 9 | 11 => 30,
2 if Self::is_leap_year(year) => 29,
2 => 28,
1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
_ => 0,
}
}
fn months_for_every_year(_: i32) -> u8 {
12
}
fn is_leap_year(year: i32) -> bool {
calendrical_calculations::julian::is_leap_year(year)
}
fn last_month_day_in_year(_year: i32) -> (u8, u8) {
(12, 31)
}
fn days_in_provided_year(year: i32) -> u16 {
if Self::is_leap_year(year) {
366
} else {
365
}
}
}
impl Calendar for Julian {
type DateInner = JulianDateInner;
fn date_from_codes(
&self,
era: types::Era,
year: i32,
month_code: types::MonthCode,
day: u8,
) -> Result<Self::DateInner, CalendarError> {
let year = if era.0 == tinystr!(16, "ce") {
if year <= 0 {
return Err(CalendarError::OutOfRange);
}
year
} else if era.0 == tinystr!(16, "bce") {
if year <= 0 {
return Err(CalendarError::OutOfRange);
}
1 - year
} else {
return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
};
ArithmeticDate::new_from_codes(self, year, month_code, day).map(JulianDateInner)
}
fn date_from_iso(&self, iso: Date<Iso>) -> JulianDateInner {
let fixed_iso = Iso::fixed_from_iso(*iso.inner());
Self::julian_from_fixed(fixed_iso)
}
fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
let fixed_julian = Julian::fixed_from_julian(date.0);
Iso::iso_from_fixed(fixed_julian)
}
fn months_in_year(&self, date: &Self::DateInner) -> u8 {
date.0.months_in_year()
}
fn days_in_year(&self, date: &Self::DateInner) -> u16 {
date.0.days_in_year()
}
fn days_in_month(&self, date: &Self::DateInner) -> u8 {
date.0.days_in_month()
}
fn day_of_week(&self, date: &Self::DateInner) -> types::IsoWeekday {
Iso.day_of_week(Julian.date_to_iso(date).inner())
}
fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
date.0.offset_date(offset);
}
#[allow(clippy::field_reassign_with_default)]
fn until(
&self,
date1: &Self::DateInner,
date2: &Self::DateInner,
_calendar2: &Self,
_largest_unit: DateDurationUnit,
_smallest_unit: DateDurationUnit,
) -> DateDuration<Self> {
date1.0.until(date2.0, _largest_unit, _smallest_unit)
}
/// The calendar-specific year represented by `date`
/// Julian has the same era scheme as Gregorian
fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
year_as_gregorian(date.0.year)
}
fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
Self::is_leap_year(date.0.year)
}
/// The calendar-specific month represented by `date`
fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
date.0.month()
}
/// The calendar-specific day-of-month represented by `date`
fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
date.0.day_of_month()
}
fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
let prev_year = date.0.year - 1;
let next_year = date.0.year + 1;
types::DayOfYearInfo {
day_of_year: date.0.day_of_year(),
days_in_year: date.0.days_in_year(),
prev_year: crate::gregorian::year_as_gregorian(prev_year),
days_in_prev_year: Julian::days_in_year_direct(prev_year),
next_year: crate::gregorian::year_as_gregorian(next_year),
}
}
fn debug_name(&self) -> &'static str {
"Julian"
}
fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
None
}
}
impl Julian {
/// Construct a new Julian Calendar
pub fn new() -> Self {
Self
}
// "Fixed" is a day count representation of calendars staring from Jan 1st of year 1 of the Georgian Calendar.
pub(crate) const fn fixed_from_julian(date: ArithmeticDate<Julian>) -> RataDie {
calendrical_calculations::julian::fixed_from_julian(date.year, date.month, date.day)
}
/// Convenience function so we can call days_in_year without
/// needing to construct a full ArithmeticDate
fn days_in_year_direct(year: i32) -> u16 {
if Julian::is_leap_year(year) {
366
} else {
365
}
}
fn julian_from_fixed(date: RataDie) -> JulianDateInner {
let (year, month, day) = match calendrical_calculations::julian::julian_from_fixed(date) {
Err(I32CastError::BelowMin) => return JulianDateInner(ArithmeticDate::min_date()),
Err(I32CastError::AboveMax) => return JulianDateInner(ArithmeticDate::max_date()),
Ok(ymd) => ymd,
};
JulianDateInner(ArithmeticDate::new_unchecked(year, month, day))
}
}
impl Date<Julian> {
/// Construct new Julian Date.
///
/// Years are arithmetic, meaning there is a year 0. Zero and negative years are in BC, with year 0 = 1 BC
///
/// ```rust
/// use icu::calendar::Date;
///
/// let date_julian = Date::try_new_julian_date(1969, 12, 20)
/// .expect("Failed to initialize Julian Date instance.");
///
/// assert_eq!(date_julian.year().number, 1969);
/// assert_eq!(date_julian.month().ordinal, 12);
/// assert_eq!(date_julian.day_of_month().0, 20);
/// ```
pub fn try_new_julian_date(
year: i32,
month: u8,
day: u8,
) -> Result<Date<Julian>, CalendarError> {
ArithmeticDate::new_from_ordinals(year, month, day)
.map(JulianDateInner)
.map(|inner| Date::from_raw(inner, Julian))
}
}
impl DateTime<Julian> {
/// Construct a new Julian datetime from integers.
///
/// Years are arithmetic, meaning there is a year 0. Zero and negative years are in BC, with year 0 = 1 BC
///
/// ```rust
/// use icu::calendar::DateTime;
///
/// let datetime_julian =
/// DateTime::try_new_julian_datetime(1969, 12, 20, 13, 1, 0)
/// .expect("Failed to initialize Julian DateTime instance.");
///
/// assert_eq!(datetime_julian.date.year().number, 1969);
/// assert_eq!(datetime_julian.date.month().ordinal, 12);
/// assert_eq!(datetime_julian.date.day_of_month().0, 20);
/// assert_eq!(datetime_julian.time.hour.number(), 13);
/// assert_eq!(datetime_julian.time.minute.number(), 1);
/// assert_eq!(datetime_julian.time.second.number(), 0);
/// ```
pub fn try_new_julian_datetime(
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
) -> Result<DateTime<Julian>, CalendarError> {
Ok(DateTime {
date: Date::try_new_julian_date(year, month, day)?,
time: types::Time::try_new(hour, minute, second, 0)?,
})
}
}
#[cfg(test)]
mod test {
use super::*;
use types::Era;
#[test]
fn test_day_iso_to_julian() {
// March 1st 200 is same on both calendars
let iso_date = Date::try_new_iso_date(200, 3, 1).unwrap();
let julian_date = Julian.date_from_iso(iso_date);
assert_eq!(julian_date.0.year, 200);
assert_eq!(julian_date.0.month, 3);
assert_eq!(julian_date.0.day, 1);
// Feb 28th, 200 (iso) = Feb 29th, 200 (julian)
let iso_date = Date::try_new_iso_date(200, 2, 28).unwrap();
let julian_date = Julian.date_from_iso(iso_date);
assert_eq!(julian_date.0.year, 200);
assert_eq!(julian_date.0.month, 2);
assert_eq!(julian_date.0.day, 29);
// March 1st 400 (iso) = Feb 29th, 400 (julian)
let iso_date = Date::try_new_iso_date(400, 3, 1).unwrap();
let julian_date = Julian.date_from_iso(iso_date);
assert_eq!(julian_date.0.year, 400);
assert_eq!(julian_date.0.month, 2);
assert_eq!(julian_date.0.day, 29);
// Jan 1st, 2022 (iso) = Dec 19, 2021 (julian)
let iso_date = Date::try_new_iso_date(2022, 1, 1).unwrap();
let julian_date = Julian.date_from_iso(iso_date);
assert_eq!(julian_date.0.year, 2021);
assert_eq!(julian_date.0.month, 12);
assert_eq!(julian_date.0.day, 19);
}
#[test]
fn test_day_julian_to_iso() {
// March 1st 200 is same on both calendars
let julian_date = Date::try_new_julian_date(200, 3, 1).unwrap();
let iso_date = Julian.date_to_iso(julian_date.inner());
let iso_expected_date = Date::try_new_iso_date(200, 3, 1).unwrap();
assert_eq!(iso_date, iso_expected_date);
// Feb 28th, 200 (iso) = Feb 29th, 200 (julian)
let julian_date = Date::try_new_julian_date(200, 2, 29).unwrap();
let iso_date = Julian.date_to_iso(julian_date.inner());
let iso_expected_date = Date::try_new_iso_date(200, 2, 28).unwrap();
assert_eq!(iso_date, iso_expected_date);
// March 1st 400 (iso) = Feb 29th, 400 (julian)
let julian_date = Date::try_new_julian_date(400, 2, 29).unwrap();
let iso_date = Julian.date_to_iso(julian_date.inner());
let iso_expected_date = Date::try_new_iso_date(400, 3, 1).unwrap();
assert_eq!(iso_date, iso_expected_date);
// Jan 1st, 2022 (iso) = Dec 19, 2021 (julian)
let julian_date = Date::try_new_julian_date(2021, 12, 19).unwrap();
let iso_date = Julian.date_to_iso(julian_date.inner());
let iso_expected_date = Date::try_new_iso_date(2022, 1, 1).unwrap();
assert_eq!(iso_date, iso_expected_date);
// March 1st, 2022 (iso) = Feb 16, 2022 (julian)
let julian_date = Date::try_new_julian_date(2022, 2, 16).unwrap();
let iso_date = Julian.date_to_iso(julian_date.inner());
let iso_expected_date = Date::try_new_iso_date(2022, 3, 1).unwrap();
assert_eq!(iso_date, iso_expected_date);
}
#[test]
fn test_roundtrip_negative() {
// https://github.com/unicode-org/icu4x/issues/2254
let iso_date = Date::try_new_iso_date(-1000, 3, 3).unwrap();
let julian = iso_date.to_calendar(Julian::new());
let recovered_iso = julian.to_iso();
assert_eq!(iso_date, recovered_iso);
}
#[test]
fn test_julian_near_era_change() {
// Tests that the Julian calendar gives the correct expected
// day, month, and year for positive years (CE)
#[derive(Debug)]
struct TestCase {
fixed_date: i64,
iso_year: i32,
iso_month: u8,
iso_day: u8,
expected_year: i32,
expected_era: Era,
expected_month: u32,
expected_day: u32,
}
let cases = [
TestCase {
fixed_date: 1,
iso_year: 1,
iso_month: 1,
iso_day: 1,
expected_year: 1,
expected_era: Era(tinystr!(16, "ce")),
expected_month: 1,
expected_day: 3,
},
TestCase {
fixed_date: 0,
iso_year: 0,
iso_month: 12,
iso_day: 31,
expected_year: 1,
expected_era: Era(tinystr!(16, "ce")),
expected_month: 1,
expected_day: 2,
},
TestCase {
fixed_date: -1,
iso_year: 0,
iso_month: 12,
iso_day: 30,
expected_year: 1,
expected_era: Era(tinystr!(16, "ce")),
expected_month: 1,
expected_day: 1,
},
TestCase {
fixed_date: -2,
iso_year: 0,
iso_month: 12,
iso_day: 29,
expected_year: 1,
expected_era: Era(tinystr!(16, "bce")),
expected_month: 12,
expected_day: 31,
},
TestCase {
fixed_date: -3,
iso_year: 0,
iso_month: 12,
iso_day: 28,
expected_year: 1,
expected_era: Era(tinystr!(16, "bce")),
expected_month: 12,
expected_day: 30,
},
TestCase {
fixed_date: -367,
iso_year: -1,
iso_month: 12,
iso_day: 30,
expected_year: 1,
expected_era: Era(tinystr!(16, "bce")),
expected_month: 1,
expected_day: 1,
},
TestCase {
fixed_date: -368,
iso_year: -1,
iso_month: 12,
iso_day: 29,
expected_year: 2,
expected_era: Era(tinystr!(16, "bce")),
expected_month: 12,
expected_day: 31,
},
TestCase {
fixed_date: -1462,
iso_year: -4,
iso_month: 12,
iso_day: 30,
expected_year: 4,
expected_era: Era(tinystr!(16, "bce")),
expected_month: 1,
expected_day: 1,
},
TestCase {
fixed_date: -1463,
iso_year: -4,
iso_month: 12,
iso_day: 29,
expected_year: 5,
expected_era: Era(tinystr!(16, "bce")),
expected_month: 12,
expected_day: 31,
},
];
for case in cases {
let iso_from_fixed: Date<Iso> = Iso::iso_from_fixed(RataDie::new(case.fixed_date));
let julian_from_fixed: Date<Julian> = Date::new_from_iso(iso_from_fixed, Julian);
assert_eq!(julian_from_fixed.year().number, case.expected_year,
"Failed year check from fixed: {case:?}\nISO: {iso_from_fixed:?}\nJulian: {julian_from_fixed:?}");
assert_eq!(julian_from_fixed.year().era, case.expected_era,
"Failed era check from fixed: {case:?}\nISO: {iso_from_fixed:?}\nJulian: {julian_from_fixed:?}");
assert_eq!(julian_from_fixed.month().ordinal, case.expected_month,
"Failed month check from fixed: {case:?}\nISO: {iso_from_fixed:?}\nJulian: {julian_from_fixed:?}");
assert_eq!(julian_from_fixed.day_of_month().0, case.expected_day,
"Failed day check from fixed: {case:?}\nISO: {iso_from_fixed:?}\nJulian: {julian_from_fixed:?}");
let iso_date_man: Date<Iso> =
Date::try_new_iso_date(case.iso_year, case.iso_month, case.iso_day)
.expect("Failed to initialize ISO date for {case:?}");
let julian_date_man: Date<Julian> = Date::new_from_iso(iso_date_man, Julian);
assert_eq!(iso_from_fixed, iso_date_man,
"ISO from fixed not equal to ISO generated from manually-input ymd\nCase: {case:?}\nFixed: {iso_from_fixed:?}\nMan: {iso_date_man:?}");
assert_eq!(julian_from_fixed, julian_date_man,
"Julian from fixed not equal to Julian generated from manually-input ymd\nCase: {case:?}\nFixed: {julian_from_fixed:?}\nMan: {julian_date_man:?}");
}
}
#[test]
fn test_julian_fixed_date_conversion() {
// Tests that converting from fixed date to Julian then
// back to fixed date yields the same fixed date
for i in -10000..=10000 {
let fixed = RataDie::new(i);
let julian = Julian::julian_from_fixed(fixed);
let new_fixed = Julian::fixed_from_julian(julian.0);
assert_eq!(fixed, new_fixed);
}
}
#[test]
fn test_julian_directionality() {
// Tests that for a large range of fixed dates, if a fixed date
// is less than another, the corresponding YMD should also be less
// than the other, without exception.
for i in -100..=100 {
for j in -100..=100 {
let julian_i = Julian::julian_from_fixed(RataDie::new(i)).0;
let julian_j = Julian::julian_from_fixed(RataDie::new(j)).0;
assert_eq!(
i.cmp(&j),
julian_i.cmp(&julian_j),
"Julian directionality inconsistent with directionality for i: {i}, j: {j}"
);
}
}
}
#[test]
fn test_hebrew_epoch() {
assert_eq!(
calendrical_calculations::julian::fixed_from_julian_book_version(-3761, 10, 7),
RataDie::new(-1373427)
);
}
#[test]
fn test_julian_leap_years() {
assert!(Julian::is_leap_year(4));
assert!(Julian::is_leap_year(0));
assert!(Julian::is_leap_year(-4));
Date::try_new_julian_date(2020, 2, 29).unwrap();
}
}

165
third_party/rust/icu_calendar/src/lib.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,165 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
//! Types for dealing with dates, times, and custom calendars.
//!
//! This module is published as its own crate ([`icu_calendar`](https://docs.rs/icu_calendar/latest/icu_calendar/))
//! and as part of the [`icu`](https://docs.rs/icu/latest/icu/) crate. See the latter for more details on the ICU4X project.
//! The [`types`] module has a lot of common types for dealing with dates and times.
//!
//! [`Calendar`] is a trait that allows one to define custom calendars, and [`Date`]
//! can represent dates for arbitrary calendars.
//!
//! The [`iso`] and [`gregorian`] modules contain implementations for the ISO and
//! Gregorian calendars respectively. Further calendars can be found in modules like
//! [`japanese`], [`julian`], [`coptic`], [`indian`], [`buddhist`], and [`ethiopian`].
//!
//! Most interaction with this crate will be done via the [`Date`] and [`DateTime`] types.
//!
//! Some of the algorithms implemented here are based on
//! Dershowitz, Nachum, and Edward M. Reingold. _Calendrical calculations_. Cambridge University Press, 2008.
//! with associated Lisp code found at <https://github.com/EdReingold/calendar-code2>.
//!
//! # Examples
//!
//! Examples of date manipulation using `Date` object. `Date` objects are useful
//! for working with dates, encompassing information about the day, month, year,
//! as well as the calendar type.
//!
//! ```rust
//! use icu::calendar::{types::IsoWeekday, Date};
//!
//! // Creating ISO date: 1992-09-02.
//! let mut date_iso = Date::try_new_iso_date(1992, 9, 2)
//! .expect("Failed to initialize ISO Date instance.");
//!
//! assert_eq!(date_iso.day_of_week(), IsoWeekday::Wednesday);
//! assert_eq!(date_iso.year().number, 1992);
//! assert_eq!(date_iso.month().ordinal, 9);
//! assert_eq!(date_iso.day_of_month().0, 2);
//!
//! // Answering questions about days in month and year.
//! assert_eq!(date_iso.days_in_year(), 366);
//! assert_eq!(date_iso.days_in_month(), 30);
//! ```
//!
//! Example of converting an ISO date across Indian and Buddhist calendars.
//!
//! ```rust
//! use icu::calendar::{buddhist::Buddhist, indian::Indian, Date};
//!
//! // Creating ISO date: 1992-09-02.
//! let mut date_iso = Date::try_new_iso_date(1992, 9, 2)
//! .expect("Failed to initialize ISO Date instance.");
//!
//! assert_eq!(date_iso.year().number, 1992);
//! assert_eq!(date_iso.month().ordinal, 9);
//! assert_eq!(date_iso.day_of_month().0, 2);
//!
//! // Conversion into Indian calendar: 1914-08-02.
//! let date_indian = date_iso.to_calendar(Indian);
//! assert_eq!(date_indian.year().number, 1914);
//! assert_eq!(date_indian.month().ordinal, 6);
//! assert_eq!(date_indian.day_of_month().0, 11);
//!
//! // Conversion into Buddhist calendar: 2535-09-02.
//! let date_buddhist = date_iso.to_calendar(Buddhist);
//! assert_eq!(date_buddhist.year().number, 2535);
//! assert_eq!(date_buddhist.month().ordinal, 9);
//! assert_eq!(date_buddhist.day_of_month().0, 2);
//! ```
//!
//! Example using `DateTime` object. Similar to `Date` objects, `DateTime` objects
//! contain an accessible `Date` object containing information about the day, month,
//! year, and calendar type. Additionally, `DateTime` objects contain an accessible
//! `Time` object, including granularity of hour, minute, second, and nanosecond.
//!
//! ```rust
//! use icu::calendar::{types::IsoWeekday, types::Time, DateTime};
//!
//! // Creating ISO date: 1992-09-02 8:59
//! let mut datetime_iso = DateTime::try_new_iso_datetime(1992, 9, 2, 8, 59, 0)
//! .expect("Failed to initialize ISO DateTime instance.");
//!
//! assert_eq!(datetime_iso.date.day_of_week(), IsoWeekday::Wednesday);
//! assert_eq!(datetime_iso.date.year().number, 1992);
//! assert_eq!(datetime_iso.date.month().ordinal, 9);
//! assert_eq!(datetime_iso.date.day_of_month().0, 2);
//! assert_eq!(datetime_iso.time.hour.number(), 8);
//! assert_eq!(datetime_iso.time.minute.number(), 59);
//! assert_eq!(datetime_iso.time.second.number(), 0);
//! assert_eq!(datetime_iso.time.nanosecond.number(), 0);
//! ```
//! [`ICU4X`]: ../icu/index.html
// https://github.com/unicode-org/icu4x/blob/main/docs/process/boilerplate.md#library-annotations
#![cfg_attr(not(any(test, feature = "std")), no_std)]
#![cfg_attr(
not(test),
deny(
clippy::indexing_slicing,
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::exhaustive_structs,
clippy::exhaustive_enums,
missing_debug_implementations,
)
)]
#![warn(missing_docs)]
extern crate alloc;
// Make sure inherent docs go first
mod date;
mod datetime;
pub mod any_calendar;
pub mod buddhist;
mod calendar;
mod calendar_arithmetic;
pub mod chinese;
mod chinese_based;
mod chinese_data;
pub mod coptic;
pub mod dangi;
mod duration;
mod error;
pub mod ethiopian;
pub mod gregorian;
pub mod hebrew;
pub mod indian;
pub mod islamic;
pub mod iso;
pub mod japanese;
pub mod julian;
pub mod persian;
pub mod provider;
pub mod roc;
pub mod types;
mod week_of;
pub mod week {
//! Functions for week-of-month and week-of-year arithmetic.
use crate::week_of;
pub use week_of::RelativeUnit;
pub use week_of::WeekCalculator;
pub use week_of::WeekOf;
}
#[doc(no_inline)]
pub use any_calendar::{AnyCalendar, AnyCalendarKind};
pub use calendar::Calendar;
pub use date::{AsCalendar, Date, Ref};
pub use datetime::DateTime;
#[doc(hidden)]
pub use duration::{DateDuration, DateDurationUnit};
pub use error::CalendarError;
#[doc(no_inline)]
pub use gregorian::Gregorian;
#[doc(no_inline)]
pub use iso::Iso;
#[doc(no_inline)]
pub use CalendarError as Error;

598
third_party/rust/icu_calendar/src/persian.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,598 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
//! This module contains types and implementations for the Persian calendar.
//!
//! ```rust
//! use icu::calendar::{Date, DateTime};
//!
//! // `Date` type
//! let persian_date = Date::try_new_persian_date(1348, 10, 11)
//! .expect("Failed to initialize Persian Date instance.");
//!
//! // `DateTime` type
//! let persian_datetime =
//! DateTime::try_new_persian_datetime(1348, 10, 11, 13, 1, 0)
//! .expect("Failed to initialize Persian DateTime instance.");
//!
//! // `Date` checks
//! assert_eq!(persian_date.year().number, 1348);
//! assert_eq!(persian_date.month().ordinal, 10);
//! assert_eq!(persian_date.day_of_month().0, 11);
//!
//! // `DateTime` checks
//! assert_eq!(persian_datetime.date.year().number, 1348);
//! assert_eq!(persian_datetime.date.month().ordinal, 10);
//! assert_eq!(persian_datetime.date.day_of_month().0, 11);
//! assert_eq!(persian_datetime.time.hour.number(), 13);
//! assert_eq!(persian_datetime.time.minute.number(), 1);
//! assert_eq!(persian_datetime.time.second.number(), 0);
//! ```
use crate::any_calendar::AnyCalendarKind;
use crate::calendar_arithmetic::{ArithmeticDate, CalendarArithmetic};
use crate::iso::Iso;
use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime};
use ::tinystr::tinystr;
use calendrical_calculations::helpers::I32CastError;
use calendrical_calculations::rata_die::RataDie;
/// The Persian Calendar
///
/// The [Persian Calendar] is a solar calendar used officially by the countries of Iran and Afghanistan and many Persian-speaking regions.
/// It has 12 months and other similarities to the Gregorian Calendar
///
/// This type can be used with [`Date`] or [`DateTime`] to represent dates in this calendar.
///
/// [Persian Calendar]: https://en.wikipedia.org/wiki/Solar_Hijri_calendar
///
/// # Era codes
///
/// This calendar supports only one era code, which starts from the year of the Hijra, designated as "ah".
///
/// # Month codes
///
/// This calendar supports 12 solar month codes (`"M01" - "M12"`)
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq, PartialOrd, Ord)]
#[allow(clippy::exhaustive_structs)]
pub struct Persian;
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
/// The inner date type used for representing [`Date`]s of [`Persian`]. See [`Date`] and [`Persian`] for more details.
pub struct PersianDateInner(ArithmeticDate<Persian>);
impl CalendarArithmetic for Persian {
fn month_days(year: i32, month: u8) -> u8 {
match month {
1..=6 => 31,
7..=11 => 30,
12 if Self::is_leap_year(year) => 30,
12 => 29,
_ => 0,
}
}
fn months_for_every_year(_: i32) -> u8 {
12
}
// Lisp code reference: https://github.com/EdReingold/calendar-code2/blob/main/calendar.l#L4789
fn is_leap_year(p_year: i32) -> bool {
let mut p_year = p_year as i64;
if 0 < p_year {
p_year -= 474;
} else {
p_year -= 473;
};
let year = p_year.rem_euclid(2820) + 474;
((year + 38) * 31).rem_euclid(128) < 31
}
fn days_in_provided_year(year: i32) -> u16 {
if Self::is_leap_year(year) {
366
} else {
365
}
}
fn last_month_day_in_year(year: i32) -> (u8, u8) {
if Self::is_leap_year(year) {
(12, 30)
} else {
(12, 29)
}
}
}
impl Calendar for Persian {
type DateInner = PersianDateInner;
fn date_from_codes(
&self,
era: types::Era,
year: i32,
month_code: types::MonthCode,
day: u8,
) -> Result<Self::DateInner, CalendarError> {
let year = if era.0 == tinystr!(16, "ah") || era.0 == tinystr!(16, "persian") {
year
} else {
return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
};
ArithmeticDate::new_from_codes(self, year, month_code, day).map(PersianDateInner)
}
fn date_from_iso(&self, iso: Date<Iso>) -> PersianDateInner {
let fixed_iso = Iso::fixed_from_iso(*iso.inner());
Self::arithmetic_persian_from_fixed(fixed_iso)
}
fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
let fixed_persian = Persian::fixed_from_arithmetic_persian(*date);
Iso::iso_from_fixed(fixed_persian)
}
fn months_in_year(&self, date: &Self::DateInner) -> u8 {
date.0.months_in_year()
}
fn days_in_year(&self, date: &Self::DateInner) -> u16 {
date.0.days_in_year()
}
fn days_in_month(&self, date: &Self::DateInner) -> u8 {
date.0.days_in_month()
}
fn day_of_week(&self, date: &Self::DateInner) -> types::IsoWeekday {
Iso.day_of_week(self.date_to_iso(date).inner())
}
fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
date.0.offset_date(offset)
}
#[allow(clippy::field_reassign_with_default)]
fn until(
&self,
date1: &Self::DateInner,
date2: &Self::DateInner,
_calendar2: &Self,
_largest_unit: DateDurationUnit,
_smallest_unit: DateDurationUnit,
) -> DateDuration<Self> {
date1.0.until(date2.0, _largest_unit, _smallest_unit)
}
fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
Self::year_as_persian(date.0.year)
}
fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
Self::is_leap_year(date.0.year)
}
fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
date.0.month()
}
fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
date.0.day_of_month()
}
fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
let prev_year = date.0.year.saturating_sub(1);
let next_year = date.0.year.saturating_add(1);
types::DayOfYearInfo {
day_of_year: date.0.day_of_year(),
days_in_year: date.0.days_in_year(),
prev_year: Persian::year_as_persian(prev_year),
days_in_prev_year: Persian::days_in_provided_year(prev_year),
next_year: Persian::year_as_persian(next_year),
}
}
fn debug_name(&self) -> &'static str {
"Persian"
}
// Missing any_calendar persian tests, the rest is completed
fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
Some(AnyCalendarKind::Persian)
}
}
impl Persian {
/// Constructs a new Persian Calendar
pub fn new() -> Self {
Self
}
fn fixed_from_arithmetic_persian(p_date: PersianDateInner) -> RataDie {
calendrical_calculations::persian::fixed_from_arithmetic_persian(
p_date.0.year,
p_date.0.month,
p_date.0.day,
)
}
fn arithmetic_persian_from_fixed(date: RataDie) -> PersianDateInner {
let (year, month, day) =
match calendrical_calculations::persian::arithmetic_persian_from_fixed(date) {
Err(I32CastError::BelowMin) => return PersianDateInner(ArithmeticDate::min_date()),
Err(I32CastError::AboveMax) => return PersianDateInner(ArithmeticDate::max_date()),
Ok(ymd) => ymd,
};
PersianDateInner(ArithmeticDate::new_unchecked(year, month, day))
}
fn year_as_persian(year: i32) -> types::FormattableYear {
types::FormattableYear {
era: types::Era(tinystr!(16, "ah")),
number: year,
cyclic: None,
related_iso: None,
}
}
}
impl Date<Persian> {
/// Construct new Persian Date.
///
/// Has no negative years, only era is the AH/AP.
///
/// ```rust
/// use icu::calendar::Date;
///
/// let date_persian = Date::try_new_persian_date(1392, 4, 25)
/// .expect("Failed to initialize Persian Date instance.");
///
/// assert_eq!(date_persian.year().number, 1392);
/// assert_eq!(date_persian.month().ordinal, 4);
/// assert_eq!(date_persian.day_of_month().0, 25);
/// ```
pub fn try_new_persian_date(
year: i32,
month: u8,
day: u8,
) -> Result<Date<Persian>, CalendarError> {
ArithmeticDate::new_from_ordinals(year, month, day)
.map(PersianDateInner)
.map(|inner| Date::from_raw(inner, Persian))
}
}
impl DateTime<Persian> {
/// Construct a new Persian datetime from integers.
///
/// ```rust
/// use icu::calendar::DateTime;
///
/// let datetime_persian =
/// DateTime::try_new_persian_datetime(474, 10, 11, 13, 1, 0)
/// .expect("Failed to initialize Persian DateTime instance.");
///
/// assert_eq!(datetime_persian.date.year().number, 474);
/// assert_eq!(datetime_persian.date.month().ordinal, 10);
/// assert_eq!(datetime_persian.date.day_of_month().0, 11);
/// assert_eq!(datetime_persian.time.hour.number(), 13);
/// assert_eq!(datetime_persian.time.minute.number(), 1);
/// assert_eq!(datetime_persian.time.second.number(), 0);
/// ```
pub fn try_new_persian_datetime(
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
) -> Result<DateTime<Persian>, CalendarError> {
Ok(DateTime {
date: Date::try_new_persian_date(year, month, day)?,
time: types::Time::try_new(hour, minute, second, 0)?,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug)]
struct DateCase {
year: i32,
month: u8,
day: u8,
}
static TEST_FIXED_DATE: [i64; 33] = [
-214193, -61387, 25469, 49217, 171307, 210155, 253427, 369740, 400085, 434355, 452605,
470160, 473837, 507850, 524156, 544676, 567118, 569477, 601716, 613424, 626596, 645554,
664224, 671401, 694799, 704424, 708842, 709409, 709580, 727274, 728714, 744313, 764652,
];
static CASES: [DateCase; 33] = [
DateCase {
year: -1208,
month: 5,
day: 1,
},
DateCase {
year: -790,
month: 9,
day: 14,
},
DateCase {
year: -552,
month: 7,
day: 2,
},
DateCase {
year: -487,
month: 7,
day: 9,
},
DateCase {
year: -153,
month: 10,
day: 18,
},
DateCase {
year: -46,
month: 2,
day: 30,
},
DateCase {
year: 73,
month: 8,
day: 19,
},
DateCase {
year: 392,
month: 2,
day: 5,
},
DateCase {
year: 475,
month: 3,
day: 3,
},
DateCase {
year: 569,
month: 1,
day: 3,
},
DateCase {
year: 618,
month: 12,
day: 20,
},
DateCase {
year: 667,
month: 1,
day: 14,
},
DateCase {
year: 677,
month: 2,
day: 8,
},
DateCase {
year: 770,
month: 3,
day: 22,
},
DateCase {
year: 814,
month: 11,
day: 13,
},
DateCase {
year: 871,
month: 1,
day: 21,
},
DateCase {
year: 932,
month: 6,
day: 28,
},
DateCase {
year: 938,
month: 12,
day: 14,
},
DateCase {
year: 1027,
month: 3,
day: 21,
},
DateCase {
year: 1059,
month: 4,
day: 10,
},
DateCase {
year: 1095,
month: 5,
day: 2,
},
DateCase {
year: 1147,
month: 3,
day: 30,
},
DateCase {
year: 1198,
month: 5,
day: 10,
},
DateCase {
year: 1218,
month: 1,
day: 7,
},
DateCase {
year: 1282,
month: 1,
day: 29,
},
DateCase {
year: 1308,
month: 6,
day: 3,
},
DateCase {
year: 1320,
month: 7,
day: 7,
},
DateCase {
year: 1322,
month: 1,
day: 29,
},
DateCase {
year: 1322,
month: 7,
day: 14,
},
DateCase {
year: 1370,
month: 12,
day: 27,
},
DateCase {
year: 1374,
month: 12,
day: 6,
},
DateCase {
year: 1417,
month: 8,
day: 19,
},
DateCase {
year: 1473,
month: 4,
day: 28,
},
];
fn days_in_provided_year_core(year: i32) -> u16 {
let fixed_year =
calendrical_calculations::persian::fixed_from_arithmetic_persian(year, 1, 1)
.to_i64_date();
let next_fixed_year =
calendrical_calculations::persian::fixed_from_arithmetic_persian(year + 1, 1, 1)
.to_i64_date();
(next_fixed_year - fixed_year) as u16
}
#[test]
fn test_persian_leap_year() {
let mut leap_years: [i32; 33] = [0; 33];
// These values were computed from the "Calendrical Calculations" reference code output
let expected_values = [
false, true, false, false, false, false, false, true, false, true, false, false, true,
false, false, true, false, false, false, false, false, false, false, true, false,
false, false, false, false, true, false, false, false,
];
let mut leap_year_results: Vec<bool> = Vec::new();
let canonical_leap_year_cycle_start = 474;
let canonical_leap_year_cycle_end = 3293;
for year in canonical_leap_year_cycle_start..=canonical_leap_year_cycle_end {
let r = Persian::is_leap_year(year);
if r {
leap_year_results.push(r);
}
}
// 683 is the amount of leap years in the 2820 Persian year cycle
assert_eq!(leap_year_results.len(), 683);
for (index, case) in CASES.iter().enumerate() {
leap_years[index] = case.year;
}
for (year, bool) in leap_years.iter().zip(expected_values.iter()) {
assert_eq!(Persian::is_leap_year(*year), *bool);
}
}
#[test]
fn days_in_provided_year_test() {
for case in CASES.iter() {
assert_eq!(
days_in_provided_year_core(case.year),
Persian::days_in_provided_year(case.year)
);
}
}
#[test]
fn test_fixed_from_persian() {
for (case, f_date) in CASES.iter().zip(TEST_FIXED_DATE.iter()) {
let date = Date::try_new_persian_date(case.year, case.month, case.day).unwrap();
assert_eq!(
Persian::fixed_from_arithmetic_persian(*date.inner()).to_i64_date(),
*f_date,
"{case:?}"
);
}
}
#[test]
fn test_persian_from_fixed() {
for (case, f_date) in CASES.iter().zip(TEST_FIXED_DATE.iter()) {
let date = Date::try_new_persian_date(case.year, case.month, case.day).unwrap();
assert_eq!(
Persian::arithmetic_persian_from_fixed(RataDie::new(*f_date)),
date.inner,
"{case:?}"
);
}
}
#[test]
fn test_day_of_year_info() {
#[derive(Debug)]
struct TestCase {
input: i32,
expected_prev: i32,
expected_next: i32,
}
let test_cases = [
TestCase {
input: 0,
expected_prev: -1,
expected_next: 1,
},
TestCase {
input: i32::MAX,
expected_prev: i32::MAX - 1,
expected_next: i32::MAX, // can't go above i32::MAX
},
TestCase {
input: i32::MIN + 1,
expected_prev: i32::MIN,
expected_next: i32::MIN + 2,
},
TestCase {
input: i32::MIN,
expected_prev: i32::MIN, // can't go below i32::MIN
expected_next: i32::MIN + 1,
},
];
for case in test_cases {
let date = Date::try_new_persian_date(case.input, 1, 1).unwrap();
let info = Persian::day_of_year_info(&Persian, date.inner());
assert_eq!(info.prev_year.number, case.expected_prev, "{:?}", case);
assert_eq!(info.next_year.number, case.expected_next, "{:?}", case);
}
}
}

153
third_party/rust/icu_calendar/src/provider.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,153 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
//! 🚧 \[Unstable\] Data provider struct definitions for this ICU4X component.
//!
//! <div class="stab unstable">
//! 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
//! including in SemVer minor releases. While the serde representation of data structs is guaranteed
//! to be stable, their Rust representation might not be. Use with caution.
//! </div>
//!
//! Read more about data providers: [`icu_provider`]
// Provider structs must be stable
#![allow(clippy::exhaustive_structs, clippy::exhaustive_enums)]
use crate::types::IsoWeekday;
use core::str::FromStr;
use icu_provider::prelude::*;
use tinystr::TinyStr16;
use zerovec::ZeroVec;
#[cfg(feature = "compiled_data")]
#[derive(Debug)]
/// Baked data
///
/// <div class="stab unstable">
/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
/// </div>
pub struct Baked;
#[cfg(feature = "compiled_data")]
const _: () = {
pub mod icu {
pub use crate as calendar;
pub use icu_locid_transform as locid_transform;
}
icu_calendar_data::make_provider!(Baked);
icu_calendar_data::impl_calendar_japanese_v1!(Baked);
icu_calendar_data::impl_calendar_japanext_v1!(Baked);
icu_calendar_data::impl_datetime_week_data_v1!(Baked);
};
#[cfg(feature = "datagen")]
/// The latest minimum set of keys required by this component.
pub const KEYS: &[DataKey] = &[
JapaneseErasV1Marker::KEY,
JapaneseExtendedErasV1Marker::KEY,
WeekDataV1Marker::KEY,
];
/// The date at which an era started
///
/// The order of fields in this struct is important!
///
/// <div class="stab unstable">
/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
/// to be stable, their Rust representation might not be. Use with caution.
/// </div>
#[zerovec::make_ule(EraStartDateULE)]
#[derive(
Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug, yoke::Yokeable, zerofrom::ZeroFrom,
)]
#[cfg_attr(
feature = "datagen",
derive(serde::Serialize, databake::Bake),
databake(path = icu_calendar::provider),
)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
pub struct EraStartDate {
/// The year the era started in
pub year: i32,
/// The month the era started in
pub month: u8,
/// The day the era started in
pub day: u8,
}
/// A data structure containing the necessary era data for constructing a
/// [`Japanese`](crate::japanese::Japanese) calendar object
///
/// <div class="stab unstable">
/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
/// to be stable, their Rust representation might not be. Use with caution.
/// </div>
#[icu_provider::data_struct(
marker(JapaneseErasV1Marker, "calendar/japanese@1", singleton),
marker(JapaneseExtendedErasV1Marker, "calendar/japanext@1", singleton)
)]
#[derive(Debug, PartialEq, Clone, Default)]
#[cfg_attr(
feature = "datagen",
derive(serde::Serialize, databake::Bake),
databake(path = icu_calendar::provider),
)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
pub struct JapaneseErasV1<'data> {
/// A map from era start dates to their era codes
#[cfg_attr(feature = "serde", serde(borrow))]
pub dates_to_eras: ZeroVec<'data, (EraStartDate, TinyStr16)>,
}
impl FromStr for EraStartDate {
type Err = ();
fn from_str(mut s: &str) -> Result<Self, ()> {
let sign = if let Some(suffix) = s.strip_prefix('-') {
s = suffix;
-1
} else {
1
};
let mut split = s.split('-');
let year = split.next().ok_or(())?.parse::<i32>().map_err(|_| ())? * sign;
let month = split.next().ok_or(())?.parse().map_err(|_| ())?;
let day = split.next().ok_or(())?.parse().map_err(|_| ())?;
Ok(EraStartDate { year, month, day })
}
}
/// An ICU4X mapping to a subset of CLDR weekData.
/// See CLDR-JSON's weekData.json for more context.
///
/// <div class="stab unstable">
/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
/// to be stable, their Rust representation might not be. Use with caution.
/// </div>
#[icu_provider::data_struct(marker(
WeekDataV1Marker,
"datetime/week_data@1",
fallback_by = "region"
))]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(
feature = "datagen",
derive(serde::Serialize, databake::Bake),
databake(path = icu_calendar::provider),
)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[allow(clippy::exhaustive_structs)] // used in data provider
pub struct WeekDataV1 {
/// The first day of a week.
pub first_weekday: IsoWeekday,
/// For a given week, the minimum number of that week's days present in a given month or year for the week to be considered part of that month or year.
pub min_week_days: u8,
}

498
third_party/rust/icu_calendar/src/roc.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,498 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
//! This module contains types and implementations for the Republic of China calendar.
//!
//! ```rust
//! use icu::calendar::{roc::Roc, Date, DateTime};
//!
//! // `Date` type
//! let date_iso = Date::try_new_iso_date(1970, 1, 2)
//! .expect("Failed to initialize ISO Date instance.");
//! let date_roc = Date::new_from_iso(date_iso, Roc);
//!
//! // `DateTime` type
//! let datetime_iso = DateTime::try_new_iso_datetime(1970, 1, 2, 13, 1, 0)
//! .expect("Failed to initialize ISO DateTime instance.");
//! let datetime_roc = DateTime::new_from_iso(datetime_iso, Roc);
//!
//! // `Date` checks
//! assert_eq!(date_roc.year().number, 59);
//! assert_eq!(date_roc.month().ordinal, 1);
//! assert_eq!(date_roc.day_of_month().0, 2);
//!
//! // `DateTime` checks
//! assert_eq!(datetime_roc.date.year().number, 59);
//! assert_eq!(datetime_roc.date.month().ordinal, 1);
//! assert_eq!(datetime_roc.date.day_of_month().0, 2);
//! assert_eq!(datetime_roc.time.hour.number(), 13);
//! assert_eq!(datetime_roc.time.minute.number(), 1);
//! assert_eq!(datetime_roc.time.second.number(), 0);
//! ```
use crate::{
calendar_arithmetic::ArithmeticDate, iso::IsoDateInner, types, AnyCalendarKind, Calendar,
CalendarError, Date, DateTime, Iso,
};
use calendrical_calculations::helpers::i64_to_saturated_i32;
use tinystr::tinystr;
/// Year of the beginning of the Taiwanese (ROC/Minguo) calendar.
/// 1912 ISO = ROC 1
const ROC_ERA_OFFSET: i32 = 1911;
/// The Republic of China (ROC) Calendar
///
/// The [Republic of China calendar] is a solar calendar used in Taiwan and Penghu, as well as by overseas diaspora from
/// those locations. Months and days are identical to the [`Gregorian`] calendar, while years are counted
/// with 1912, the year of the establishment of the Republic of China, as year 1 of the ROC/Minguo/民国/民國 era.
///
/// [Republic of China calendar]: https://en.wikipedia.org/wiki/Republic_of_China_calendar
///
/// The Republic of China calendar should not be confused with the Chinese traditional lunar calendar
/// (see [`Chinese`]).
///
/// # Era codes
///
/// This calendar supports two era codes: `"roc"`, corresponding to years in the 民國 (minguo) era (CE year 1912 and
/// after), and `"roc-inverse"`, corresponding to years before the 民國 (minguo) era (CE year 1911 and before).
///
///
/// # Month codes
///
/// This calendar supports 12 solar month codes (`"M01" - "M12"`)
///
/// [`Chinese`]: crate::chinese::Chinese
/// [`Gregorian`]: crate::Gregorian
#[derive(Copy, Clone, Debug, Default)]
#[allow(clippy::exhaustive_structs)] // this type is stable
pub struct Roc;
/// The inner date type used for representing [`Date`]s of [`Roc`]. See [`Date`] and [`Roc`] for more info.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
pub struct RocDateInner(IsoDateInner);
impl Calendar for Roc {
type DateInner = RocDateInner;
fn date_from_codes(
&self,
era: crate::types::Era,
year: i32,
month_code: crate::types::MonthCode,
day: u8,
) -> Result<Self::DateInner, crate::Error> {
let year = if era.0 == tinystr!(16, "roc") {
if year <= 0 {
return Err(CalendarError::OutOfRange);
}
year + ROC_ERA_OFFSET
} else if era.0 == tinystr!(16, "roc-inverse") {
if year <= 0 {
return Err(CalendarError::OutOfRange);
}
1 - year + ROC_ERA_OFFSET
} else {
return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
};
ArithmeticDate::new_from_codes(self, year, month_code, day)
.map(IsoDateInner)
.map(RocDateInner)
}
fn date_from_iso(&self, iso: crate::Date<crate::Iso>) -> Self::DateInner {
RocDateInner(*iso.inner())
}
fn date_to_iso(&self, date: &Self::DateInner) -> crate::Date<crate::Iso> {
Date::from_raw(date.0, Iso)
}
fn months_in_year(&self, date: &Self::DateInner) -> u8 {
Iso.months_in_year(&date.0)
}
fn days_in_year(&self, date: &Self::DateInner) -> u16 {
Iso.days_in_year(&date.0)
}
fn days_in_month(&self, date: &Self::DateInner) -> u8 {
Iso.days_in_month(&date.0)
}
fn offset_date(&self, date: &mut Self::DateInner, offset: crate::DateDuration<Self>) {
Iso.offset_date(&mut date.0, offset.cast_unit())
}
fn until(
&self,
date1: &Self::DateInner,
date2: &Self::DateInner,
_calendar2: &Self,
largest_unit: crate::DateDurationUnit,
smallest_unit: crate::DateDurationUnit,
) -> crate::DateDuration<Self> {
Iso.until(&date1.0, &date2.0, &Iso, largest_unit, smallest_unit)
.cast_unit()
}
fn debug_name(&self) -> &'static str {
"ROC"
}
fn year(&self, date: &Self::DateInner) -> crate::types::FormattableYear {
year_as_roc(date.0 .0.year as i64)
}
fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
Iso.is_in_leap_year(&date.0)
}
fn month(&self, date: &Self::DateInner) -> crate::types::FormattableMonth {
Iso.month(&date.0)
}
fn day_of_month(&self, date: &Self::DateInner) -> crate::types::DayOfMonth {
Iso.day_of_month(&date.0)
}
fn day_of_year_info(&self, date: &Self::DateInner) -> crate::types::DayOfYearInfo {
let prev_year = date.0 .0.year.saturating_sub(1);
let next_year = date.0 .0.year.saturating_add(1);
types::DayOfYearInfo {
day_of_year: Iso::day_of_year(date.0),
days_in_year: Iso::days_in_year_direct(date.0 .0.year),
prev_year: year_as_roc(prev_year as i64),
days_in_prev_year: Iso::days_in_year_direct(prev_year),
next_year: year_as_roc(next_year as i64),
}
}
/// The [`AnyCalendarKind`] corresponding to this calendar
fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
Some(AnyCalendarKind::Roc)
}
}
impl Date<Roc> {
/// Construct a new Republic of China calendar Date.
///
/// Years are specified in the "roc" era. This function accepts an extended year in that era, so dates
/// before Minguo are negative and year 0 is 1 Before Minguo. To specify dates using explicit era
/// codes, use [`Roc::date_from_codes()`].
///
/// ```rust
/// use icu::calendar::Date;
/// use icu::calendar::gregorian::Gregorian;
/// use tinystr::tinystr;
///
/// // Create a new ROC Date
/// let date_roc = Date::try_new_roc_date(1, 2, 3)
/// .expect("Failed to initialize ROC Date instance.");
///
/// assert_eq!(date_roc.year().era.0, tinystr!(16, "roc"));
/// assert_eq!(date_roc.year().number, 1, "ROC year check failed!");
/// assert_eq!(date_roc.month().ordinal, 2, "ROC month check failed!");
/// assert_eq!(date_roc.day_of_month().0, 3, "ROC day of month check failed!");
///
/// // Convert to an equivalent Gregorian date
/// let date_gregorian = date_roc.to_calendar(Gregorian);
///
/// assert_eq!(date_gregorian.year().number, 1912, "Gregorian from ROC year check failed!");
/// assert_eq!(date_gregorian.month().ordinal, 2, "Gregorian from ROC month check failed!");
/// assert_eq!(date_gregorian.day_of_month().0, 3, "Gregorian from ROC day of month check failed!");
pub fn try_new_roc_date(year: i32, month: u8, day: u8) -> Result<Date<Roc>, CalendarError> {
let iso_year = year.saturating_add(ROC_ERA_OFFSET);
Date::try_new_iso_date(iso_year, month, day).map(|d| Date::new_from_iso(d, Roc))
}
}
impl DateTime<Roc> {
/// Construct a new Republic of China calendar datetime from integers.
///
/// Years are specified in the "roc" era, Before Minguo dates are negative (year 0 is 1 Before Minguo)
///
/// ```rust
/// use icu::calendar::gregorian::Gregorian;
/// use icu::calendar::DateTime;
/// use tinystr::tinystr;
///
/// // Create a new ROC DateTime
/// let datetime_roc = DateTime::try_new_roc_datetime(1, 2, 3, 13, 1, 0)
/// .expect("Failed to initialize ROC DateTime instance.");
///
/// assert_eq!(datetime_roc.date.year().era.0, tinystr!(16, "roc"));
/// assert_eq!(datetime_roc.date.year().number, 1, "ROC year check failed!");
/// assert_eq!(
/// datetime_roc.date.month().ordinal,
/// 2,
/// "ROC month check failed!"
/// );
/// assert_eq!(
/// datetime_roc.date.day_of_month().0,
/// 3,
/// "ROC day of month check failed!"
/// );
/// assert_eq!(datetime_roc.time.hour.number(), 13);
/// assert_eq!(datetime_roc.time.minute.number(), 1);
/// assert_eq!(datetime_roc.time.second.number(), 0);
/// ```
pub fn try_new_roc_datetime(
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
) -> Result<DateTime<Roc>, CalendarError> {
Ok(DateTime {
date: Date::try_new_roc_date(year, month, day)?,
time: types::Time::try_new(hour, minute, second, 0)?,
})
}
}
pub(crate) fn year_as_roc(year: i64) -> types::FormattableYear {
let year_i32 = i64_to_saturated_i32(year);
let offset_i64 = ROC_ERA_OFFSET as i64;
if year > offset_i64 {
types::FormattableYear {
era: types::Era(tinystr!(16, "roc")),
number: year_i32.saturating_sub(ROC_ERA_OFFSET),
cyclic: None,
related_iso: Some(year_i32),
}
} else {
types::FormattableYear {
era: types::Era(tinystr!(16, "roc-inverse")),
number: (ROC_ERA_OFFSET + 1).saturating_sub(year_i32),
cyclic: None,
related_iso: Some(year_i32),
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::types::Era;
use calendrical_calculations::rata_die::RataDie;
#[derive(Debug)]
struct TestCase {
fixed_date: RataDie,
iso_year: i32,
iso_month: u8,
iso_day: u8,
expected_year: i32,
expected_era: Era,
expected_month: u32,
expected_day: u32,
}
fn check_test_case(case: TestCase) {
let iso_from_fixed = Iso::iso_from_fixed(case.fixed_date);
let roc_from_fixed = Date::new_from_iso(iso_from_fixed, Roc);
assert_eq!(roc_from_fixed.year().number, case.expected_year,
"Failed year check from fixed: {case:?}\nISO: {iso_from_fixed:?}\nROC: {roc_from_fixed:?}");
assert_eq!(roc_from_fixed.year().era, case.expected_era,
"Failed era check from fixed: {case:?}\nISO: {iso_from_fixed:?}\nROC: {roc_from_fixed:?}");
assert_eq!(roc_from_fixed.month().ordinal, case.expected_month,
"Failed month check from fixed: {case:?}\nISO: {iso_from_fixed:?}\nROC: {roc_from_fixed:?}");
assert_eq!(roc_from_fixed.day_of_month().0, case.expected_day,
"Failed day_of_month check from fixed: {case:?}\nISO: {iso_from_fixed:?}\nROC: {roc_from_fixed:?}");
let iso_from_case = Date::try_new_iso_date(case.iso_year, case.iso_month, case.iso_day)
.expect("Failed to initialize ISO date for {case:?}");
let roc_from_case = Date::new_from_iso(iso_from_case, Roc);
assert_eq!(iso_from_fixed, iso_from_case,
"ISO from fixed not equal to ISO generated from manually-input ymd\nCase: {case:?}\nFixed: {iso_from_fixed:?}\nManual: {iso_from_case:?}");
assert_eq!(roc_from_fixed, roc_from_case,
"ROC date from fixed not equal to ROC generated from manually-input ymd\nCase: {case:?}\nFixed: {roc_from_fixed:?}\nManual: {roc_from_case:?}");
}
#[test]
fn test_roc_current_era() {
// Tests that the ROC calendar gives the correct expected day, month, and year for years >= 1912
// (years in the ROC/minguo era)
//
// Jan 1. 1912 CE = RD 697978
let cases = [
TestCase {
fixed_date: RataDie::new(697978),
iso_year: 1912,
iso_month: 1,
iso_day: 1,
expected_year: 1,
expected_era: Era(tinystr!(16, "roc")),
expected_month: 1,
expected_day: 1,
},
TestCase {
fixed_date: RataDie::new(698037),
iso_year: 1912,
iso_month: 2,
iso_day: 29,
expected_year: 1,
expected_era: Era(tinystr!(16, "roc")),
expected_month: 2,
expected_day: 29,
},
TestCase {
fixed_date: RataDie::new(698524),
iso_year: 1913,
iso_month: 6,
iso_day: 30,
expected_year: 2,
expected_era: Era(tinystr!(16, "roc")),
expected_month: 6,
expected_day: 30,
},
TestCase {
fixed_date: RataDie::new(738714),
iso_year: 2023,
iso_month: 7,
iso_day: 13,
expected_year: 112,
expected_era: Era(tinystr!(16, "roc")),
expected_month: 7,
expected_day: 13,
},
];
for case in cases {
check_test_case(case);
}
}
#[test]
fn test_roc_prior_era() {
// Tests that the ROC calendar gives the correct expected day, month, and year for years <= 1911
// (years in the ROC/minguo era)
//
// Jan 1. 1912 CE = RD 697978
let cases = [
TestCase {
fixed_date: RataDie::new(697977),
iso_year: 1911,
iso_month: 12,
iso_day: 31,
expected_year: 1,
expected_era: Era(tinystr!(16, "roc-inverse")),
expected_month: 12,
expected_day: 31,
},
TestCase {
fixed_date: RataDie::new(697613),
iso_year: 1911,
iso_month: 1,
iso_day: 1,
expected_year: 1,
expected_era: Era(tinystr!(16, "roc-inverse")),
expected_month: 1,
expected_day: 1,
},
TestCase {
fixed_date: RataDie::new(697612),
iso_year: 1910,
iso_month: 12,
iso_day: 31,
expected_year: 2,
expected_era: Era(tinystr!(16, "roc-inverse")),
expected_month: 12,
expected_day: 31,
},
TestCase {
fixed_date: RataDie::new(696576),
iso_year: 1908,
iso_month: 2,
iso_day: 29,
expected_year: 4,
expected_era: Era(tinystr!(16, "roc-inverse")),
expected_month: 2,
expected_day: 29,
},
TestCase {
fixed_date: RataDie::new(1),
iso_year: 1,
iso_month: 1,
iso_day: 1,
expected_year: 1911,
expected_era: Era(tinystr!(16, "roc-inverse")),
expected_month: 1,
expected_day: 1,
},
TestCase {
fixed_date: RataDie::new(0),
iso_year: 0,
iso_month: 12,
iso_day: 31,
expected_year: 1912,
expected_era: Era(tinystr!(16, "roc-inverse")),
expected_month: 12,
expected_day: 31,
},
];
for case in cases {
check_test_case(case);
}
}
#[test]
fn test_roc_directionality_near_epoch() {
// Tests that for a large range of fixed dates near the beginning of the minguo era (CE 1912),
// the comparison between those two fixed dates should be equal to the comparison between their
// corresponding YMD.
let rd_epoch_start = 697978;
for i in (rd_epoch_start - 100)..=(rd_epoch_start + 100) {
for j in (rd_epoch_start - 100)..=(rd_epoch_start + 100) {
let iso_i = Iso::iso_from_fixed(RataDie::new(i));
let iso_j = Iso::iso_from_fixed(RataDie::new(j));
let roc_i = iso_i.to_calendar(Roc);
let roc_j = iso_j.to_calendar(Roc);
assert_eq!(
i.cmp(&j),
iso_i.cmp(&iso_j),
"ISO directionality inconsistent with directionality for i: {i}, j: {j}"
);
assert_eq!(
i.cmp(&j),
roc_i.cmp(&roc_j),
"ROC directionality inconsistent with directionality for i: {i}, j: {j}"
);
}
}
}
#[test]
fn test_roc_directionality_near_rd_zero() {
// Same as `test_directionality_near_epoch`, but with a focus around RD 0
for i in -100..=100 {
for j in -100..100 {
let iso_i = Iso::iso_from_fixed(RataDie::new(i));
let iso_j = Iso::iso_from_fixed(RataDie::new(j));
let roc_i = iso_i.to_calendar(Roc);
let roc_j = iso_j.to_calendar(Roc);
assert_eq!(
i.cmp(&j),
iso_i.cmp(&iso_j),
"ISO directionality inconsistent with directionality for i: {i}, j: {j}"
);
assert_eq!(
i.cmp(&j),
roc_i.cmp(&roc_j),
"ROC directionality inconsistent with directionality for i: {i}, j: {j}"
);
}
}
}
}

781
third_party/rust/icu_calendar/src/types.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,781 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
//! This module contains various types used by `icu_calendar` and `icu_datetime`
use crate::error::CalendarError;
use core::convert::TryFrom;
use core::convert::TryInto;
use core::fmt;
use core::num::NonZeroU8;
use core::str::FromStr;
use tinystr::TinyAsciiStr;
use tinystr::{TinyStr16, TinyStr4};
use zerovec::maps::ZeroMapKV;
use zerovec::ule::AsULE;
/// The era of a particular date
///
/// Different calendars use different era codes, see their documentation
/// for details.
///
/// Era codes are shared with Temporal, [see Temporal proposal][era-proposal].
///
/// [era-proposal]: https://tc39.es/proposal-intl-era-monthcode/
#[derive(Copy, Clone, Debug, PartialEq)]
#[allow(clippy::exhaustive_structs)] // this is a newtype
pub struct Era(pub TinyStr16);
impl From<TinyStr16> for Era {
fn from(x: TinyStr16) -> Self {
Self(x)
}
}
impl FromStr for Era {
type Err = <TinyStr16 as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse().map(Self)
}
}
/// Representation of a formattable year.
///
/// More fields may be added in the future for things like extended year
#[derive(Copy, Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct FormattableYear {
/// The era containing the year.
///
/// This may not always be the canonical era for the calendar and could be an alias,
/// for example all `islamic` calendars return `islamic` as the formattable era code
/// which allows them to share data.
pub era: Era,
/// The year number in the current era (usually 1-based).
pub number: i32,
/// The year in the current cycle for cyclic calendars (1-indexed)
/// can be set to `None` for non-cyclic calendars
///
/// For chinese and dangi it will be
/// a number between 1 and 60, for hypothetical other calendars it may be something else.
pub cyclic: Option<NonZeroU8>,
/// The related ISO year. This is normally the ISO (proleptic Gregorian) year having the greatest
/// overlap with the calendar year. It is used in certain date formatting patterns.
///
/// Can be `None` if the calendar does not typically use `related_iso` (and CLDR does not contain patterns
/// using it)
pub related_iso: Option<i32>,
}
impl FormattableYear {
/// Construct a new Year given an era and number
///
/// Other fields can be set mutably after construction
/// as needed
pub fn new(era: Era, number: i32, cyclic: Option<NonZeroU8>) -> Self {
Self {
era,
number,
cyclic,
related_iso: None,
}
}
}
/// Representation of a month in a year
///
/// Month codes typically look like `M01`, `M02`, etc, but can handle leap months
/// (`M03L`) in lunar calendars. Solar calendars will have codes between `M01` and `M12`
/// potentially with an `M13` for epagomenal months. Check the docs for a particular calendar
/// for details on what its month codes are.
///
/// Month codes are shared with Temporal, [see Temporal proposal][era-proposal].
///
/// [era-proposal]: https://tc39.es/proposal-intl-era-monthcode/
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[allow(clippy::exhaustive_structs)] // this is a newtype
#[cfg_attr(
feature = "datagen",
derive(serde::Serialize, databake::Bake),
databake(path = icu_calendar::types),
)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
pub struct MonthCode(pub TinyStr4);
impl MonthCode {
/// Returns an option which is `Some` containing the non-month version of a leap month
/// if the [`MonthCode`] this method is called upon is a leap month, and `None` otherwise.
/// This method assumes the [`MonthCode`] is valid.
pub fn get_normal_if_leap(self) -> Option<MonthCode> {
let bytes = self.0.all_bytes();
if bytes[3] == b'L' {
Some(MonthCode(TinyAsciiStr::from_bytes(&bytes[0..3]).ok()?))
} else {
None
}
}
/// Get the month number and whether or not it is leap from the month code
pub fn parsed(self) -> Option<(u8, bool)> {
// Match statements on tinystrs are annoying so instead
// we calculate it from the bytes directly
let bytes = self.0.all_bytes();
let is_leap = bytes[3] == b'L';
if bytes[0] != b'M' {
return None;
}
if bytes[1] == b'0' {
if bytes[2] >= b'1' && bytes[2] <= b'9' {
return Some((bytes[2] - b'0', is_leap));
}
} else if bytes[1] == b'1' && bytes[2] >= b'0' && bytes[2] <= b'3' {
return Some((10 + bytes[2] - b'0', is_leap));
}
None
}
}
#[test]
fn test_get_normal_month_code_if_leap() {
let mc1 = MonthCode(tinystr::tinystr!(4, "M01L"));
let result1 = mc1.get_normal_if_leap();
assert_eq!(result1, Some(MonthCode(tinystr::tinystr!(4, "M01"))));
let mc2 = MonthCode(tinystr::tinystr!(4, "M11L"));
let result2 = mc2.get_normal_if_leap();
assert_eq!(result2, Some(MonthCode(tinystr::tinystr!(4, "M11"))));
let mc_invalid = MonthCode(tinystr::tinystr!(4, "M10"));
let result_invalid = mc_invalid.get_normal_if_leap();
assert_eq!(result_invalid, None);
}
impl AsULE for MonthCode {
type ULE = TinyStr4;
fn to_unaligned(self) -> TinyStr4 {
self.0
}
fn from_unaligned(u: TinyStr4) -> Self {
Self(u)
}
}
impl<'a> ZeroMapKV<'a> for MonthCode {
type Container = zerovec::ZeroVec<'a, MonthCode>;
type Slice = zerovec::ZeroSlice<MonthCode>;
type GetType = <MonthCode as AsULE>::ULE;
type OwnedType = MonthCode;
}
impl fmt::Display for MonthCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<TinyStr4> for MonthCode {
fn from(x: TinyStr4) -> Self {
Self(x)
}
}
impl FromStr for MonthCode {
type Err = <TinyStr4 as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse().map(Self)
}
}
/// Representation of a formattable month.
#[derive(Copy, Clone, Debug, PartialEq)]
#[allow(clippy::exhaustive_structs)] // this type is stable
pub struct FormattableMonth {
/// The month number in this given year. For calendars with leap months, all months after
/// the leap month will end up with an incremented number.
///
/// In general, prefer using the month code in generic code.
pub ordinal: u32,
/// The month code, used to distinguish months during leap years.
///
/// This may not necessarily be the canonical month code for a month in cases where a month has different
/// formatting in a leap year, for example Adar/Adar II in the Hebrew calendar in a leap year has
/// the code M06, but for formatting specifically the Hebrew calendar will return M06L since it is formatted
/// differently.
pub code: MonthCode,
}
/// A struct containing various details about the position of the day within a year. It is returned
// by the [`day_of_year_info()`](trait.DateInput.html#tymethod.day_of_year_info) method of the
// [`DateInput`] trait.
#[derive(Copy, Clone, Debug, PartialEq)]
#[allow(clippy::exhaustive_structs)] // this type is stable
pub struct DayOfYearInfo {
/// The current day of the year, 1-based.
pub day_of_year: u16,
/// The number of days in a year.
pub days_in_year: u16,
/// The previous year.
pub prev_year: FormattableYear,
/// The number of days in the previous year.
pub days_in_prev_year: u16,
/// The next year.
pub next_year: FormattableYear,
}
/// A day number in a month. Usually 1-based.
#[allow(clippy::exhaustive_structs)] // this is a newtype
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct DayOfMonth(pub u32);
/// A week number in a month. Usually 1-based.
#[derive(Clone, Copy, Debug, PartialEq)]
#[allow(clippy::exhaustive_structs)] // this is a newtype
pub struct WeekOfMonth(pub u32);
/// A week number in a year. Usually 1-based.
#[derive(Clone, Copy, Debug, PartialEq)]
#[allow(clippy::exhaustive_structs)] // this is a newtype
pub struct WeekOfYear(pub u32);
/// A day of week in month. 1-based.
#[derive(Clone, Copy, Debug, PartialEq)]
#[allow(clippy::exhaustive_structs)] // this is a newtype
pub struct DayOfWeekInMonth(pub u32);
impl From<DayOfMonth> for DayOfWeekInMonth {
fn from(day_of_month: DayOfMonth) -> Self {
DayOfWeekInMonth(1 + ((day_of_month.0 - 1) / 7))
}
}
#[test]
fn test_day_of_week_in_month() {
assert_eq!(DayOfWeekInMonth::from(DayOfMonth(1)).0, 1);
assert_eq!(DayOfWeekInMonth::from(DayOfMonth(7)).0, 1);
assert_eq!(DayOfWeekInMonth::from(DayOfMonth(8)).0, 2);
}
/// This macro defines a struct for 0-based date fields: hours, minutes, seconds
/// and fractional seconds. Each unit is bounded by a range. The traits implemented
/// here will return a Result on whether or not the unit is in range from the given
/// input.
macro_rules! dt_unit {
($name:ident, $storage:ident, $value:expr, $docs:expr) => {
#[doc=$docs]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct $name($storage);
impl $name {
/// Gets the numeric value for this component.
pub const fn number(self) -> $storage {
self.0
}
/// Creates a new value at 0.
pub const fn zero() -> $name {
Self(0)
}
}
impl FromStr for $name {
type Err = CalendarError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
let val: $storage = input.parse()?;
if val > $value {
Err(CalendarError::Overflow {
field: "$name",
max: $value,
})
} else {
Ok(Self(val))
}
}
}
impl TryFrom<$storage> for $name {
type Error = CalendarError;
fn try_from(input: $storage) -> Result<Self, Self::Error> {
if input > $value {
Err(CalendarError::Overflow {
field: "$name",
max: $value,
})
} else {
Ok(Self(input))
}
}
}
impl TryFrom<usize> for $name {
type Error = CalendarError;
fn try_from(input: usize) -> Result<Self, Self::Error> {
if input > $value {
Err(CalendarError::Overflow {
field: "$name",
max: $value,
})
} else {
Ok(Self(input as $storage))
}
}
}
impl From<$name> for $storage {
fn from(input: $name) -> Self {
input.0
}
}
impl From<$name> for usize {
fn from(input: $name) -> Self {
input.0 as Self
}
}
impl $name {
/// Attempts to add two values.
/// Returns `Some` if the sum is within bounds.
/// Returns `None` if the sum is out of bounds.
pub fn try_add(self, other: $storage) -> Option<Self> {
let sum = self.0.saturating_add(other);
if sum > $value {
None
} else {
Some(Self(sum))
}
}
/// Attempts to subtract two values.
/// Returns `Some` if the difference is within bounds.
/// Returns `None` if the difference is out of bounds.
pub fn try_sub(self, other: $storage) -> Option<Self> {
self.0.checked_sub(other).map(Self)
}
}
};
}
dt_unit!(
IsoHour,
u8,
24,
"An ISO-8601 hour component, for use with ISO calendars.
Must be within inclusive bounds `[0, 24]`. The value could be equal to 24 to
denote the end of a day, with the writing 24:00:00. It corresponds to the same
time as the next day at 00:00:00."
);
dt_unit!(
IsoMinute,
u8,
60,
"An ISO-8601 minute component, for use with ISO calendars.
Must be within inclusive bounds `[0, 60]`. The value could be equal to 60 to
denote the end of an hour, with the writing 12:60:00. This example corresponds
to the same time as 13:00:00. This is an extension to ISO 8601."
);
dt_unit!(
IsoSecond,
u8,
61,
"An ISO-8601 second component, for use with ISO calendars.
Must be within inclusive bounds `[0, 61]`. `60` accommodates for leap seconds.
The value could also be equal to 60 or 61, to indicate the end of a leap second,
with the writing `23:59:61.000000000Z` or `23:59:60.000000000Z`. These examples,
if used with this goal, would correspond to the same time as the next day, at
time `00:00:00.000000000Z`. This is an extension to ISO 8601."
);
dt_unit!(
NanoSecond,
u32,
999_999_999,
"A fractional second component, stored as nanoseconds.
Must be within inclusive bounds `[0, 999_999_999]`."
);
#[test]
fn test_iso_hour_arithmetic() {
const HOUR_MAX: u8 = 24;
const HOUR_VALUE: u8 = 5;
let hour = IsoHour(HOUR_VALUE);
// middle of bounds
assert_eq!(
hour.try_add(HOUR_VALUE - 1),
Some(IsoHour(HOUR_VALUE + (HOUR_VALUE - 1)))
);
assert_eq!(
hour.try_sub(HOUR_VALUE - 1),
Some(IsoHour(HOUR_VALUE - (HOUR_VALUE - 1)))
);
// edge of bounds
assert_eq!(hour.try_add(HOUR_MAX - HOUR_VALUE), Some(IsoHour(HOUR_MAX)));
assert_eq!(hour.try_sub(HOUR_VALUE), Some(IsoHour(0)));
// out of bounds
assert_eq!(hour.try_add(1 + HOUR_MAX - HOUR_VALUE), None);
assert_eq!(hour.try_sub(1 + HOUR_VALUE), None);
}
#[test]
fn test_iso_minute_arithmetic() {
const MINUTE_MAX: u8 = 60;
const MINUTE_VALUE: u8 = 5;
let minute = IsoMinute(MINUTE_VALUE);
// middle of bounds
assert_eq!(
minute.try_add(MINUTE_VALUE - 1),
Some(IsoMinute(MINUTE_VALUE + (MINUTE_VALUE - 1)))
);
assert_eq!(
minute.try_sub(MINUTE_VALUE - 1),
Some(IsoMinute(MINUTE_VALUE - (MINUTE_VALUE - 1)))
);
// edge of bounds
assert_eq!(
minute.try_add(MINUTE_MAX - MINUTE_VALUE),
Some(IsoMinute(MINUTE_MAX))
);
assert_eq!(minute.try_sub(MINUTE_VALUE), Some(IsoMinute(0)));
// out of bounds
assert_eq!(minute.try_add(1 + MINUTE_MAX - MINUTE_VALUE), None);
assert_eq!(minute.try_sub(1 + MINUTE_VALUE), None);
}
#[test]
fn test_iso_second_arithmetic() {
const SECOND_MAX: u8 = 61;
const SECOND_VALUE: u8 = 5;
let second = IsoSecond(SECOND_VALUE);
// middle of bounds
assert_eq!(
second.try_add(SECOND_VALUE - 1),
Some(IsoSecond(SECOND_VALUE + (SECOND_VALUE - 1)))
);
assert_eq!(
second.try_sub(SECOND_VALUE - 1),
Some(IsoSecond(SECOND_VALUE - (SECOND_VALUE - 1)))
);
// edge of bounds
assert_eq!(
second.try_add(SECOND_MAX - SECOND_VALUE),
Some(IsoSecond(SECOND_MAX))
);
assert_eq!(second.try_sub(SECOND_VALUE), Some(IsoSecond(0)));
// out of bounds
assert_eq!(second.try_add(1 + SECOND_MAX - SECOND_VALUE), None);
assert_eq!(second.try_sub(1 + SECOND_VALUE), None);
}
#[test]
fn test_iso_nano_second_arithmetic() {
const NANO_SECOND_MAX: u32 = 999_999_999;
const NANO_SECOND_VALUE: u32 = 5;
let nano_second = NanoSecond(NANO_SECOND_VALUE);
// middle of bounds
assert_eq!(
nano_second.try_add(NANO_SECOND_VALUE - 1),
Some(NanoSecond(NANO_SECOND_VALUE + (NANO_SECOND_VALUE - 1)))
);
assert_eq!(
nano_second.try_sub(NANO_SECOND_VALUE - 1),
Some(NanoSecond(NANO_SECOND_VALUE - (NANO_SECOND_VALUE - 1)))
);
// edge of bounds
assert_eq!(
nano_second.try_add(NANO_SECOND_MAX - NANO_SECOND_VALUE),
Some(NanoSecond(NANO_SECOND_MAX))
);
assert_eq!(nano_second.try_sub(NANO_SECOND_VALUE), Some(NanoSecond(0)));
// out of bounds
assert_eq!(
nano_second.try_add(1 + NANO_SECOND_MAX - NANO_SECOND_VALUE),
None
);
assert_eq!(nano_second.try_sub(1 + NANO_SECOND_VALUE), None);
}
/// A representation of a time in hours, minutes, seconds, and nanoseconds
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[allow(clippy::exhaustive_structs)] // this type is stable
pub struct Time {
/// 0-based hour.
pub hour: IsoHour,
/// 0-based minute.
pub minute: IsoMinute,
/// 0-based second.
pub second: IsoSecond,
/// Fractional second
pub nanosecond: NanoSecond,
}
impl Time {
/// Construct a new [`Time`], without validating that all components are in range
pub const fn new(
hour: IsoHour,
minute: IsoMinute,
second: IsoSecond,
nanosecond: NanoSecond,
) -> Self {
Self {
hour,
minute,
second,
nanosecond,
}
}
/// Construct a new [`Time`] representing midnight (00:00.000)
pub const fn midnight() -> Self {
Self {
hour: IsoHour::zero(),
minute: IsoMinute::zero(),
second: IsoSecond::zero(),
nanosecond: NanoSecond::zero(),
}
}
/// Construct a new [`Time`], whilst validating that all components are in range
pub fn try_new(
hour: u8,
minute: u8,
second: u8,
nanosecond: u32,
) -> Result<Self, CalendarError> {
Ok(Self {
hour: hour.try_into()?,
minute: minute.try_into()?,
second: second.try_into()?,
nanosecond: nanosecond.try_into()?,
})
}
/// Takes a number of minutes, which could be positive or negative, and returns the Time
/// and the day number, which could be positive or negative.
pub(crate) fn from_minute_with_remainder_days(minute: i32) -> (Time, i32) {
let (extra_days, minute_in_day) = (minute.div_euclid(1440), minute.rem_euclid(1440));
let (hours, minutes) = (minute_in_day / 60, minute_in_day % 60);
#[allow(clippy::unwrap_used)] // values are moduloed to be in range
(
Self {
hour: (hours as u8).try_into().unwrap(),
minute: (minutes as u8).try_into().unwrap(),
second: IsoSecond::zero(),
nanosecond: NanoSecond::zero(),
},
extra_days,
)
}
}
#[test]
fn test_from_minute_with_remainder_days() {
#[derive(Debug)]
struct TestCase {
minute: i32,
expected_time: Time,
expected_remainder: i32,
}
let zero_time = Time::new(
IsoHour::zero(),
IsoMinute::zero(),
IsoSecond::zero(),
NanoSecond::zero(),
);
let first_minute_in_day = Time::new(
IsoHour::zero(),
IsoMinute::try_from(1u8).unwrap(),
IsoSecond::zero(),
NanoSecond::zero(),
);
let last_minute_in_day = Time::new(
IsoHour::try_from(23u8).unwrap(),
IsoMinute::try_from(59u8).unwrap(),
IsoSecond::zero(),
NanoSecond::zero(),
);
let cases = [
TestCase {
minute: 0,
expected_time: zero_time,
expected_remainder: 0,
},
TestCase {
minute: 30,
expected_time: Time::new(
IsoHour::zero(),
IsoMinute::try_from(30u8).unwrap(),
IsoSecond::zero(),
NanoSecond::zero(),
),
expected_remainder: 0,
},
TestCase {
minute: 60,
expected_time: Time::new(
IsoHour::try_from(1u8).unwrap(),
IsoMinute::zero(),
IsoSecond::zero(),
NanoSecond::zero(),
),
expected_remainder: 0,
},
TestCase {
minute: 90,
expected_time: Time::new(
IsoHour::try_from(1u8).unwrap(),
IsoMinute::try_from(30u8).unwrap(),
IsoSecond::zero(),
NanoSecond::zero(),
),
expected_remainder: 0,
},
TestCase {
minute: 1439,
expected_time: last_minute_in_day,
expected_remainder: 0,
},
TestCase {
minute: 1440,
expected_time: Time::new(
IsoHour::zero(),
IsoMinute::zero(),
IsoSecond::zero(),
NanoSecond::zero(),
),
expected_remainder: 1,
},
TestCase {
minute: 1441,
expected_time: first_minute_in_day,
expected_remainder: 1,
},
TestCase {
minute: i32::MAX,
expected_time: Time::new(
IsoHour::try_from(2u8).unwrap(),
IsoMinute::try_from(7u8).unwrap(),
IsoSecond::zero(),
NanoSecond::zero(),
),
expected_remainder: 1491308,
},
TestCase {
minute: -1,
expected_time: last_minute_in_day,
expected_remainder: -1,
},
TestCase {
minute: -1439,
expected_time: first_minute_in_day,
expected_remainder: -1,
},
TestCase {
minute: -1440,
expected_time: zero_time,
expected_remainder: -1,
},
TestCase {
minute: -1441,
expected_time: last_minute_in_day,
expected_remainder: -2,
},
TestCase {
minute: i32::MIN,
expected_time: Time::new(
IsoHour::try_from(21u8).unwrap(),
IsoMinute::try_from(52u8).unwrap(),
IsoSecond::zero(),
NanoSecond::zero(),
),
expected_remainder: -1491309,
},
];
for cas in cases {
let (actual_time, actual_remainder) = Time::from_minute_with_remainder_days(cas.minute);
assert_eq!(actual_time, cas.expected_time, "{cas:?}");
assert_eq!(actual_remainder, cas.expected_remainder, "{cas:?}");
}
}
/// A weekday in a 7-day week, according to ISO-8601.
///
/// The discriminant values correspond to ISO-8601 weekday numbers (Monday = 1, Sunday = 7).
///
/// # Examples
///
/// ```
/// use icu::calendar::types::IsoWeekday;
///
/// assert_eq!(1, IsoWeekday::Monday as usize);
/// assert_eq!(7, IsoWeekday::Sunday as usize);
/// ```
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(missing_docs)] // The weekday variants should be self-obvious.
#[repr(i8)]
#[cfg_attr(
feature = "datagen",
derive(serde::Serialize, databake::Bake),
databake(path = icu_calendar::types),
)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[allow(clippy::exhaustive_enums)] // This is stable
pub enum IsoWeekday {
Monday = 1,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday,
}
impl From<usize> for IsoWeekday {
/// Convert from an ISO-8601 weekday number to an [`IsoWeekday`] enum. 0 is automatically converted
/// to 7 (Sunday). If the number is out of range, it is interpreted modulo 7.
///
/// # Examples
///
/// ```
/// use icu::calendar::types::IsoWeekday;
///
/// assert_eq!(IsoWeekday::Sunday, IsoWeekday::from(0));
/// assert_eq!(IsoWeekday::Monday, IsoWeekday::from(1));
/// assert_eq!(IsoWeekday::Sunday, IsoWeekday::from(7));
/// assert_eq!(IsoWeekday::Monday, IsoWeekday::from(8));
/// ```
fn from(input: usize) -> Self {
let mut ordinal = (input % 7) as i8;
if ordinal == 0 {
ordinal = 7;
}
unsafe { core::mem::transmute(ordinal) }
}
}

601
third_party/rust/icu_calendar/src/week_of.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,601 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
use crate::{
error::CalendarError,
provider::WeekDataV1,
types::{DayOfMonth, DayOfYearInfo, IsoWeekday, WeekOfMonth},
};
use icu_provider::prelude::*;
/// Minimum number of days in a month unit required for using this module
pub const MIN_UNIT_DAYS: u16 = 14;
/// Calculator for week-of-month and week-of-year based on locale-specific configurations.
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub struct WeekCalculator {
/// The first day of a week.
pub first_weekday: IsoWeekday,
/// For a given week, the minimum number of that week's days present in a given month or year
/// for the week to be considered part of that month or year.
pub min_week_days: u8,
}
impl From<WeekDataV1> for WeekCalculator {
fn from(other: WeekDataV1) -> Self {
Self {
first_weekday: other.first_weekday,
min_week_days: other.min_week_days,
}
}
}
impl From<&WeekDataV1> for WeekCalculator {
fn from(other: &WeekDataV1) -> Self {
Self {
first_weekday: other.first_weekday,
min_week_days: other.min_week_days,
}
}
}
impl WeekCalculator {
icu_provider::gen_any_buffer_data_constructors!(
locale: include,
options: skip,
error: CalendarError,
/// Creates a new [`WeekCalculator`] from compiled locale data.
///
/// ✨ *Enabled with the `compiled_data` Cargo feature.*
///
/// [📚 Help choosing a constructor](icu_provider::constructors)
);
#[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::try_new)]
pub fn try_new_unstable<P>(provider: &P, locale: &DataLocale) -> Result<Self, CalendarError>
where
P: DataProvider<crate::provider::WeekDataV1Marker> + ?Sized,
{
provider
.load(DataRequest {
locale,
metadata: Default::default(),
})
.and_then(DataResponse::take_payload)
.map(|payload| payload.get().into())
.map_err(Into::into)
}
/// Returns the week of month according to a calendar with min_week_days = 1.
///
/// This is different from what the UTS35 spec describes [1] but the latter is
/// missing a month of week-of-month field so following the spec would result
/// in inconsistencies (e.g. in the ISO calendar 2021-01-01 is the last week
/// of December but 'MMMMW' would have it formatted as 'week 5 of January').
///
/// # Examples
///
/// ```
/// use icu_calendar::types::{DayOfMonth, IsoWeekday, WeekOfMonth};
/// use icu_calendar::week::WeekCalculator;
///
/// let week_calculator =
/// WeekCalculator::try_new(&icu_locid::locale!("und-GB").into())
/// .expect("locale should be present");
///
/// // Wednesday the 10th is in week 2:
/// assert_eq!(
/// WeekOfMonth(2),
/// week_calculator.week_of_month(DayOfMonth(10), IsoWeekday::Wednesday)
/// );
/// ```
///
/// [1]: https://www.unicode.org/reports/tr35/tr35-55/tr35-dates.html#Date_Patterns_Week_Of_Year
pub fn week_of_month(&self, day_of_month: DayOfMonth, iso_weekday: IsoWeekday) -> WeekOfMonth {
WeekOfMonth(simple_week_of(self.first_weekday, day_of_month.0 as u16, iso_weekday) as u32)
}
/// Returns the week of year according to the weekday and [`DayOfYearInfo`].
///
/// # Examples
///
/// ```
/// use icu_calendar::types::{DayOfMonth, IsoWeekday};
/// use icu_calendar::week::{RelativeUnit, WeekCalculator, WeekOf};
/// use icu_calendar::Date;
///
/// let week_calculator =
/// WeekCalculator::try_new(&icu_locid::locale!("und-GB").into())
/// .expect("locale should be present");
///
/// let iso_date = Date::try_new_iso_date(2022, 8, 26).unwrap();
///
/// // Friday August 26 is in week 34 of year 2022:
/// assert_eq!(
/// WeekOf {
/// unit: RelativeUnit::Current,
/// week: 34
/// },
/// week_calculator
/// .week_of_year(iso_date.day_of_year_info(), IsoWeekday::Friday)
/// .unwrap()
/// );
/// ```
pub fn week_of_year(
&self,
day_of_year_info: DayOfYearInfo,
iso_weekday: IsoWeekday,
) -> Result<WeekOf, CalendarError> {
week_of(
self,
day_of_year_info.days_in_prev_year,
day_of_year_info.days_in_year,
day_of_year_info.day_of_year,
iso_weekday,
)
}
/// Returns the zero based index of `weekday` vs this calendar's start of week.
fn weekday_index(&self, weekday: IsoWeekday) -> i8 {
(7 + (weekday as i8) - (self.first_weekday as i8)) % 7
}
}
impl Default for WeekCalculator {
fn default() -> Self {
Self {
first_weekday: IsoWeekday::Monday,
min_week_days: 1,
}
}
}
/// Returns the weekday that's `num_days` after `weekday`.
fn add_to_weekday(weekday: IsoWeekday, num_days: i32) -> IsoWeekday {
let new_weekday = (7 + (weekday as i32) + (num_days % 7)) % 7;
IsoWeekday::from(new_weekday as usize)
}
/// Which year or month that a calendar assigns a week to relative to the year/month
/// the week is in.
#[derive(Clone, Copy, Debug, PartialEq)]
#[allow(clippy::enum_variant_names)]
enum RelativeWeek {
/// A week that is assigned to the last week of the previous year/month. e.g. 2021-01-01 is week 54 of 2020 per the ISO calendar.
LastWeekOfPreviousUnit,
/// A week that's assigned to the current year/month. The offset is 1-based. e.g. 2021-01-11 is week 2 of 2021 per the ISO calendar so would be WeekOfCurrentUnit(2).
WeekOfCurrentUnit(u16),
/// A week that is assigned to the first week of the next year/month. e.g. 2019-12-31 is week 1 of 2020 per the ISO calendar.
FirstWeekOfNextUnit,
}
/// Information about a year or month.
struct UnitInfo {
/// The weekday of this year/month's first day.
first_day: IsoWeekday,
/// The number of days in this year/month.
duration_days: u16,
}
impl UnitInfo {
/// Creates a UnitInfo for a given year or month.
fn new(first_day: IsoWeekday, duration_days: u16) -> Result<UnitInfo, CalendarError> {
if duration_days < MIN_UNIT_DAYS {
return Err(CalendarError::Underflow {
field: "Month/Year duration",
min: MIN_UNIT_DAYS as isize,
});
}
Ok(UnitInfo {
first_day,
duration_days,
})
}
/// Returns the start of this unit's first week.
///
/// The returned value can be negative if this unit's first week started during the previous
/// unit.
fn first_week_offset(&self, calendar: &WeekCalculator) -> i8 {
let first_day_index = calendar.weekday_index(self.first_day);
if 7 - first_day_index >= calendar.min_week_days as i8 {
-first_day_index
} else {
7 - first_day_index
}
}
/// Returns the number of weeks in this unit according to `calendar`.
fn num_weeks(&self, calendar: &WeekCalculator) -> u16 {
let first_week_offset = self.first_week_offset(calendar);
let num_days_including_first_week =
(self.duration_days as i32) - (first_week_offset as i32);
debug_assert!(
num_days_including_first_week >= 0,
"Unit is shorter than a week."
);
((num_days_including_first_week + 7 - (calendar.min_week_days as i32)) / 7) as u16
}
/// Returns the week number for the given day in this unit.
fn relative_week(&self, calendar: &WeekCalculator, day: u16) -> RelativeWeek {
let days_since_first_week =
i32::from(day) - i32::from(self.first_week_offset(calendar)) - 1;
if days_since_first_week < 0 {
return RelativeWeek::LastWeekOfPreviousUnit;
}
let week_number = (1 + days_since_first_week / 7) as u16;
if week_number > self.num_weeks(calendar) {
return RelativeWeek::FirstWeekOfNextUnit;
}
RelativeWeek::WeekOfCurrentUnit(week_number)
}
}
/// The year or month that a calendar assigns a week to relative to the year/month that it is in.
#[derive(Debug, PartialEq)]
#[allow(clippy::exhaustive_enums)] // this type is stable
pub enum RelativeUnit {
/// A week that is assigned to previous year/month. e.g. 2021-01-01 is week 54 of 2020 per the ISO calendar.
Previous,
/// A week that's assigned to the current year/month. e.g. 2021-01-11 is week 2 of 2021 per the ISO calendar.
Current,
/// A week that is assigned to the next year/month. e.g. 2019-12-31 is week 1 of 2020 per the ISO calendar.
Next,
}
/// The week number assigned to a given week according to a calendar.
#[derive(Debug, PartialEq)]
#[allow(clippy::exhaustive_structs)] // this type is stable
pub struct WeekOf {
/// Week of month/year. 1 based.
pub week: u16,
/// The month/year that this week is in, relative to the month/year of the input date.
pub unit: RelativeUnit,
}
/// Computes & returns the week of given month/year according to `calendar`.
///
/// # Arguments
/// - calendar: Calendar information used to compute the week number.
/// - num_days_in_previous_unit: The number of days in the preceding month/year.
/// - num_days_in_unit: The number of days in the month/year.
/// - day: 1-based day of month/year.
/// - week_day: The weekday of `day`..
pub fn week_of(
calendar: &WeekCalculator,
num_days_in_previous_unit: u16,
num_days_in_unit: u16,
day: u16,
week_day: IsoWeekday,
) -> Result<WeekOf, CalendarError> {
let current = UnitInfo::new(
// The first day of this month/year is (day - 1) days from `day`.
add_to_weekday(week_day, 1 - i32::from(day)),
num_days_in_unit,
)?;
match current.relative_week(calendar, day) {
RelativeWeek::LastWeekOfPreviousUnit => {
let previous = UnitInfo::new(
add_to_weekday(current.first_day, -i32::from(num_days_in_previous_unit)),
num_days_in_previous_unit,
)?;
Ok(WeekOf {
week: previous.num_weeks(calendar),
unit: RelativeUnit::Previous,
})
}
RelativeWeek::WeekOfCurrentUnit(w) => Ok(WeekOf {
week: w,
unit: RelativeUnit::Current,
}),
RelativeWeek::FirstWeekOfNextUnit => Ok(WeekOf {
week: 1,
unit: RelativeUnit::Next,
}),
}
}
/// Computes & returns the week of given month or year according to a calendar with min_week_days = 1.
///
/// Does not know anything about the unit size (month or year), and will just assume the date falls
/// within whatever unit that is being considered. In other words, this function returns strictly increasing
/// values as `day` increases, unlike [`week_of()`] which is cyclic.
///
/// # Arguments
/// - first_weekday: The first day of a week.
/// - day: 1-based day of the month or year.
/// - week_day: The weekday of `day`.
pub fn simple_week_of(first_weekday: IsoWeekday, day: u16, week_day: IsoWeekday) -> u16 {
let calendar = WeekCalculator {
first_weekday,
min_week_days: 1,
};
#[allow(clippy::unwrap_used)] // week_of should can't fail with MIN_UNIT_DAYS
week_of(
&calendar,
// The duration of the previous unit does not influence the result if min_week_days = 1
// so we only need to use a valid value.
MIN_UNIT_DAYS,
u16::MAX,
day,
week_day,
)
.unwrap()
.week
}
#[cfg(test)]
mod tests {
use super::{week_of, RelativeUnit, RelativeWeek, UnitInfo, WeekCalculator, WeekOf};
use crate::{error::CalendarError, types::IsoWeekday, Date, DateDuration};
static ISO_CALENDAR: WeekCalculator = WeekCalculator {
first_weekday: IsoWeekday::Monday,
min_week_days: 4,
};
static AE_CALENDAR: WeekCalculator = WeekCalculator {
first_weekday: IsoWeekday::Saturday,
min_week_days: 4,
};
static US_CALENDAR: WeekCalculator = WeekCalculator {
first_weekday: IsoWeekday::Sunday,
min_week_days: 1,
};
#[test]
fn test_weekday_index() {
assert_eq!(ISO_CALENDAR.weekday_index(IsoWeekday::Monday), 0);
assert_eq!(ISO_CALENDAR.weekday_index(IsoWeekday::Sunday), 6);
assert_eq!(AE_CALENDAR.weekday_index(IsoWeekday::Saturday), 0);
assert_eq!(AE_CALENDAR.weekday_index(IsoWeekday::Friday), 6);
}
#[test]
fn test_first_week_offset() {
let first_week_offset =
|calendar, day| UnitInfo::new(day, 30).unwrap().first_week_offset(calendar);
assert_eq!(first_week_offset(&ISO_CALENDAR, IsoWeekday::Monday), 0);
assert_eq!(first_week_offset(&ISO_CALENDAR, IsoWeekday::Tuesday), -1);
assert_eq!(first_week_offset(&ISO_CALENDAR, IsoWeekday::Wednesday), -2);
assert_eq!(first_week_offset(&ISO_CALENDAR, IsoWeekday::Thursday), -3);
assert_eq!(first_week_offset(&ISO_CALENDAR, IsoWeekday::Friday), 3);
assert_eq!(first_week_offset(&ISO_CALENDAR, IsoWeekday::Saturday), 2);
assert_eq!(first_week_offset(&ISO_CALENDAR, IsoWeekday::Sunday), 1);
assert_eq!(first_week_offset(&AE_CALENDAR, IsoWeekday::Saturday), 0);
assert_eq!(first_week_offset(&AE_CALENDAR, IsoWeekday::Sunday), -1);
assert_eq!(first_week_offset(&AE_CALENDAR, IsoWeekday::Monday), -2);
assert_eq!(first_week_offset(&AE_CALENDAR, IsoWeekday::Tuesday), -3);
assert_eq!(first_week_offset(&AE_CALENDAR, IsoWeekday::Wednesday), 3);
assert_eq!(first_week_offset(&AE_CALENDAR, IsoWeekday::Thursday), 2);
assert_eq!(first_week_offset(&AE_CALENDAR, IsoWeekday::Friday), 1);
assert_eq!(first_week_offset(&US_CALENDAR, IsoWeekday::Sunday), 0);
assert_eq!(first_week_offset(&US_CALENDAR, IsoWeekday::Monday), -1);
assert_eq!(first_week_offset(&US_CALENDAR, IsoWeekday::Tuesday), -2);
assert_eq!(first_week_offset(&US_CALENDAR, IsoWeekday::Wednesday), -3);
assert_eq!(first_week_offset(&US_CALENDAR, IsoWeekday::Thursday), -4);
assert_eq!(first_week_offset(&US_CALENDAR, IsoWeekday::Friday), -5);
assert_eq!(first_week_offset(&US_CALENDAR, IsoWeekday::Saturday), -6);
}
#[test]
fn test_num_weeks() -> Result<(), CalendarError> {
// 4 days in first & last week.
assert_eq!(
UnitInfo::new(IsoWeekday::Thursday, 4 + 2 * 7 + 4)?.num_weeks(&ISO_CALENDAR),
4
);
// 3 days in first week, 4 in last week.
assert_eq!(
UnitInfo::new(IsoWeekday::Friday, 3 + 2 * 7 + 4)?.num_weeks(&ISO_CALENDAR),
3
);
// 3 days in first & last week.
assert_eq!(
UnitInfo::new(IsoWeekday::Friday, 3 + 2 * 7 + 3)?.num_weeks(&ISO_CALENDAR),
2
);
// 1 day in first & last week.
assert_eq!(
UnitInfo::new(IsoWeekday::Saturday, 1 + 2 * 7 + 1)?.num_weeks(&US_CALENDAR),
4
);
Ok(())
}
/// Uses enumeration & bucketing to assign each day of a month or year `unit` to a week.
///
/// This alternative implementation serves as an exhaustive safety check
/// of relative_week() (in addition to the manual test points used
/// for testing week_of()).
fn classify_days_of_unit(calendar: &WeekCalculator, unit: &UnitInfo) -> Vec<RelativeWeek> {
let mut weeks: Vec<Vec<IsoWeekday>> = Vec::new();
for day_index in 0..unit.duration_days {
let day = super::add_to_weekday(unit.first_day, i32::from(day_index));
if day == calendar.first_weekday || weeks.is_empty() {
weeks.push(Vec::new());
}
weeks.last_mut().unwrap().push(day);
}
let mut day_week_of_units = Vec::new();
let mut weeks_in_unit = 0;
for (index, week) in weeks.iter().enumerate() {
let week_of_unit = if week.len() < usize::from(calendar.min_week_days) {
match index {
0 => RelativeWeek::LastWeekOfPreviousUnit,
x if x == weeks.len() - 1 => RelativeWeek::FirstWeekOfNextUnit,
_ => panic!(),
}
} else {
weeks_in_unit += 1;
RelativeWeek::WeekOfCurrentUnit(weeks_in_unit)
};
day_week_of_units.append(&mut [week_of_unit].repeat(week.len()));
}
day_week_of_units
}
#[test]
fn test_relative_week_of_month() -> Result<(), CalendarError> {
for min_week_days in 1..7 {
for start_of_week in 1..7 {
let calendar = WeekCalculator {
first_weekday: IsoWeekday::from(start_of_week),
min_week_days,
};
for unit_duration in super::MIN_UNIT_DAYS..400 {
for start_of_unit in 1..7 {
let unit = UnitInfo::new(IsoWeekday::from(start_of_unit), unit_duration)?;
let expected = classify_days_of_unit(&calendar, &unit);
for (index, expected_week_of) in expected.iter().enumerate() {
let day = index + 1;
assert_eq!(
unit.relative_week(&calendar, day as u16),
*expected_week_of,
"For the {day}/{unit_duration} starting on IsoWeekday \
{start_of_unit} using start_of_week {start_of_week} \
& min_week_days {min_week_days}"
);
}
}
}
}
}
Ok(())
}
fn week_of_month_from_iso_date(
calendar: &WeekCalculator,
yyyymmdd: u32,
) -> Result<WeekOf, CalendarError> {
let year = (yyyymmdd / 10000) as i32;
let month = ((yyyymmdd / 100) % 100) as u8;
let day = (yyyymmdd % 100) as u8;
let date = Date::try_new_iso_date(year, month, day)?;
let previous_month = date.added(DateDuration::new(0, -1, 0, 0));
week_of(
calendar,
u16::from(previous_month.days_in_month()),
u16::from(date.days_in_month()),
u16::from(day),
date.day_of_week(),
)
}
#[test]
fn test_week_of_month_using_dates() -> Result<(), CalendarError> {
assert_eq!(
week_of_month_from_iso_date(&ISO_CALENDAR, 20210418)?,
WeekOf {
week: 3,
unit: RelativeUnit::Current,
}
);
assert_eq!(
week_of_month_from_iso_date(&ISO_CALENDAR, 20210419)?,
WeekOf {
week: 4,
unit: RelativeUnit::Current,
}
);
// First day of year is a Thursday.
assert_eq!(
week_of_month_from_iso_date(&ISO_CALENDAR, 20180101)?,
WeekOf {
week: 1,
unit: RelativeUnit::Current,
}
);
// First day of year is a Friday.
assert_eq!(
week_of_month_from_iso_date(&ISO_CALENDAR, 20210101)?,
WeekOf {
week: 5,
unit: RelativeUnit::Previous,
}
);
// The month ends on a Wednesday.
assert_eq!(
week_of_month_from_iso_date(&ISO_CALENDAR, 20200930)?,
WeekOf {
week: 1,
unit: RelativeUnit::Next,
}
);
// The month ends on a Thursday.
assert_eq!(
week_of_month_from_iso_date(&ISO_CALENDAR, 20201231)?,
WeekOf {
week: 5,
unit: RelativeUnit::Current,
}
);
// US calendar always assigns the week to the current month. 2020-12-31 is a Thursday.
assert_eq!(
week_of_month_from_iso_date(&US_CALENDAR, 20201231)?,
WeekOf {
week: 5,
unit: RelativeUnit::Current,
}
);
assert_eq!(
week_of_month_from_iso_date(&US_CALENDAR, 20210101)?,
WeekOf {
week: 1,
unit: RelativeUnit::Current,
}
);
Ok(())
}
}
#[test]
fn test_simple_week_of() {
// The 1st is a Monday and the week starts on Mondays.
assert_eq!(
simple_week_of(IsoWeekday::Monday, 2, IsoWeekday::Tuesday),
1
);
assert_eq!(simple_week_of(IsoWeekday::Monday, 7, IsoWeekday::Sunday), 1);
assert_eq!(simple_week_of(IsoWeekday::Monday, 8, IsoWeekday::Monday), 2);
// The 1st is a Wednesday and the week starts on Tuesdays.
assert_eq!(
simple_week_of(IsoWeekday::Tuesday, 1, IsoWeekday::Wednesday),
1
);
assert_eq!(
simple_week_of(IsoWeekday::Tuesday, 6, IsoWeekday::Monday),
1
);
assert_eq!(
simple_week_of(IsoWeekday::Tuesday, 7, IsoWeekday::Tuesday),
2
);
// The 1st is a Monday and the week starts on Sundays.
assert_eq!(
simple_week_of(IsoWeekday::Sunday, 26, IsoWeekday::Friday),
4
);
}

1
third_party/rust/icu_calendar_data/.cargo-checksum.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
{"files":{"Cargo.toml":"c460ba2e580e4b8ee0353018ef9060729006b442dad69e9a7c403e0581789017","LICENSE":"853f87c96f3d249f200fec6db1114427bc8bdf4afddc93c576956d78152ce978","README.md":"2f95fb0e29ecad6911e71b9aa17b5fb03b451fc3a9cf39cf813685b8adb93c97","data/macros.rs":"1b09cc81f8ce452ac2705c6cb1484d22e00599ea0071b077db9449dede1a7579","data/macros/calendar_japanese_v1.rs.data":"9e2955674aa7584c2701b1c6a68342fa51d33ad9a506063eb2f87e93ac3b990f","data/macros/calendar_japanext_v1.rs.data":"356827b4686d2c2838b12a237c365a96dc6b60b0eaa16ef0574c2f6ef3185db8","data/macros/datetime_week_data_v1.rs.data":"ea6bd34a67f8f4536600ef893daf356c5b39b22dafa2c5e52e89983076874651","src/lib.rs":"ec94d44f61b0ce73dae2d6628aa9771dacf93f309bd27000ac6eb3dca0e955de"},"package":"22aec7d032735d9acb256eeef72adcac43c3b7572f19b51576a63d664b524ca2"}

33
third_party/rust/icu_calendar_data/Cargo.toml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,33 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.67"
name = "icu_calendar_data"
version = "1.4.0"
authors = ["The ICU4X Project Developers"]
include = [
"data/**/*",
"src/**/*",
"examples/**/*",
"benches/**/*",
"tests/**/*",
"Cargo.toml",
"LICENSE",
"README.md",
]
description = "Data for the icu_calendar crate"
homepage = "https://icu4x.unicode.org"
readme = "README.md"
categories = ["internationalization"]
license-file = "LICENSE"
repository = "https://github.com/unicode-org/icu4x"

44
third_party/rust/icu_calendar_data/LICENSE поставляемый Normal file
Просмотреть файл

@ -0,0 +1,44 @@
UNICODE LICENSE V3
COPYRIGHT AND PERMISSION NOTICE
Copyright © 2020-2023 Unicode, Inc.
NOTICE TO USER: Carefully read the following legal agreement. BY
DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR
SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT
DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
Permission is hereby granted, free of charge, to any person obtaining a
copy of data files and any associated documentation (the "Data Files") or
software and any associated documentation (the "Software") to deal in the
Data Files or Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, and/or sell
copies of the Data Files or Software, and to permit persons to whom the
Data Files or Software are furnished to do so, provided that either (a)
this copyright and permission notice appear with all copies of the Data
Files or Software, or (b) this copyright and permission notice appear in
associated Documentation.
THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA
FILES OR SOFTWARE.
Except as contained in this notice, the name of a copyright holder shall
not be used in advertising or otherwise to promote the sale, use or other
dealings in these Data Files or Software without prior written
authorization of the copyright holder.
Portions of ICU4X may have been adapted from ICU4C and/or ICU4J.
ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.

11
third_party/rust/icu_calendar_data/README.md поставляемый Normal file
Просмотреть файл

@ -0,0 +1,11 @@
# icu_calendar_data [![crates.io](https://img.shields.io/crates/v/icu_calendar_data)](https://crates.io/crates/icu_calendar_data)
<!-- cargo-rdme start -->
Data for the icu_calendar crate
<!-- cargo-rdme end -->
## More Information
For more information on development, authorship, contributing etc. please visit [`ICU4X home page`](https://github.com/unicode-org/icu4x).

41
third_party/rust/icu_calendar_data/data/macros.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,41 @@
// @generated
/// Marks a type as a data provider. You can then use macros like
/// `impl_core_helloworld_v1` to add implementations.
///
/// ```ignore
/// struct MyProvider;
/// const _: () = {
/// include!("path/to/generated/macros.rs");
/// make_provider!(MyProvider);
/// impl_core_helloworld_v1!(MyProvider);
/// }
/// ```
#[doc(hidden)]
#[macro_export]
macro_rules! __make_provider {
($ name : ty) => {
#[clippy::msrv = "1.67"]
impl $name {
#[doc(hidden)]
#[allow(dead_code)]
pub const MUST_USE_MAKE_PROVIDER_MACRO: () = ();
}
};
}
#[doc(inline)]
pub use __make_provider as make_provider;
#[macro_use]
#[path = "macros/calendar_japanese_v1.rs.data"]
mod calendar_japanese_v1;
#[doc(inline)]
pub use __impl_calendar_japanese_v1 as impl_calendar_japanese_v1;
#[macro_use]
#[path = "macros/calendar_japanext_v1.rs.data"]
mod calendar_japanext_v1;
#[doc(inline)]
pub use __impl_calendar_japanext_v1 as impl_calendar_japanext_v1;
#[macro_use]
#[path = "macros/datetime_week_data_v1.rs.data"]
mod datetime_week_data_v1;
#[doc(inline)]
pub use __impl_datetime_week_data_v1 as impl_datetime_week_data_v1;

Просмотреть файл

@ -0,0 +1,27 @@
// @generated
/// Implement `DataProvider<JapaneseErasV1Marker>` on the given struct using the data
/// hardcoded in this file. This allows the struct to be used with
/// `icu`'s `_unstable` constructors.
#[doc(hidden)]
#[macro_export]
macro_rules! __impl_calendar_japanese_v1 {
($ provider : ty) => {
#[clippy::msrv = "1.67"]
const _: () = <$provider>::MUST_USE_MAKE_PROVIDER_MACRO;
#[clippy::msrv = "1.67"]
impl $provider {
#[doc(hidden)]
pub const SINGLETON_CALENDAR_JAPANESE_V1: &'static <icu::calendar::provider::JapaneseErasV1Marker as icu_provider::DataMarker>::Yokeable = &icu::calendar::provider::JapaneseErasV1 { dates_to_eras: unsafe { zerovec::ZeroVec::from_bytes_unchecked(b"L\x07\0\0\t\x08meiji\0\0\0\0\0\0\0\0\0\0\0x\x07\0\0\x07\x1Etaisho\0\0\0\0\0\0\0\0\0\0\x86\x07\0\0\x0C\x19showa\0\0\0\0\0\0\0\0\0\0\0\xC5\x07\0\0\x01\x08heisei\0\0\0\0\0\0\0\0\0\0\xE3\x07\0\0\x05\x01reiwa\0\0\0\0\0\0\0\0\0\0\0") } };
}
#[clippy::msrv = "1.67"]
impl icu_provider::DataProvider<icu::calendar::provider::JapaneseErasV1Marker> for $provider {
fn load(&self, req: icu_provider::DataRequest) -> Result<icu_provider::DataResponse<icu::calendar::provider::JapaneseErasV1Marker>, icu_provider::DataError> {
if req.locale.is_empty() {
Ok(icu_provider::DataResponse { payload: Some(icu_provider::DataPayload::from_static_ref(Self::SINGLETON_CALENDAR_JAPANESE_V1)), metadata: Default::default() })
} else {
Err(icu_provider::DataErrorKind::ExtraneousLocale.with_req(<icu::calendar::provider::JapaneseErasV1Marker as icu_provider::KeyedDataMarker>::KEY, req))
}
}
}
};
}

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -0,0 +1,40 @@
// @generated
/// Implement `DataProvider<WeekDataV1Marker>` on the given struct using the data
/// hardcoded in this file. This allows the struct to be used with
/// `icu`'s `_unstable` constructors.
#[doc(hidden)]
#[macro_export]
macro_rules! __impl_datetime_week_data_v1 {
($ provider : ty) => {
#[clippy::msrv = "1.67"]
const _: () = <$provider>::MUST_USE_MAKE_PROVIDER_MACRO;
#[clippy::msrv = "1.67"]
impl icu_provider::DataProvider<icu::calendar::provider::WeekDataV1Marker> for $provider {
fn load(&self, req: icu_provider::DataRequest) -> Result<icu_provider::DataResponse<icu::calendar::provider::WeekDataV1Marker>, icu_provider::DataError> {
static UND_MV: <icu::calendar::provider::WeekDataV1Marker as icu_provider::DataMarker>::Yokeable = icu::calendar::provider::WeekDataV1 { first_weekday: icu::calendar::types::IsoWeekday::Friday, min_week_days: 1u8 };
static UND: <icu::calendar::provider::WeekDataV1Marker as icu_provider::DataMarker>::Yokeable = icu::calendar::provider::WeekDataV1 { first_weekday: icu::calendar::types::IsoWeekday::Monday, min_week_days: 1u8 };
static UND_AD: <icu::calendar::provider::WeekDataV1Marker as icu_provider::DataMarker>::Yokeable = icu::calendar::provider::WeekDataV1 { first_weekday: icu::calendar::types::IsoWeekday::Monday, min_week_days: 4u8 };
static UND_AE: <icu::calendar::provider::WeekDataV1Marker as icu_provider::DataMarker>::Yokeable = icu::calendar::provider::WeekDataV1 { first_weekday: icu::calendar::types::IsoWeekday::Saturday, min_week_days: 1u8 };
static UND_AG: <icu::calendar::provider::WeekDataV1Marker as icu_provider::DataMarker>::Yokeable = icu::calendar::provider::WeekDataV1 { first_weekday: icu::calendar::types::IsoWeekday::Sunday, min_week_days: 1u8 };
static UND_PT: <icu::calendar::provider::WeekDataV1Marker as icu_provider::DataMarker>::Yokeable = icu::calendar::provider::WeekDataV1 { first_weekday: icu::calendar::types::IsoWeekday::Sunday, min_week_days: 4u8 };
static VALUES: [&<icu::calendar::provider::WeekDataV1Marker as icu_provider::DataMarker>::Yokeable; 115usize] = [&UND, &UND_AD, &UND_AE, &UND_AE, &UND_AG, &UND_AD, &UND_AG, &UND_AD, &UND_AD, &UND_AG, &UND_AD, &UND_AD, &UND_AE, &UND_AG, &UND_AG, &UND_AG, &UND_AG, &UND_AG, &UND_AG, &UND_AD, &UND_AG, &UND_AD, &UND_AD, &UND_AE, &UND_AD, &UND_AG, &UND_AG, &UND_AE, &UND_AD, &UND_AE, &UND_AD, &UND_AG, &UND_AD, &UND_AD, &UND_AD, &UND_AD, &UND_AD, &UND_AD, &UND_AD, &UND_AD, &UND_AD, &UND_AD, &UND_AG, &UND_AG, &UND_AG, &UND_AG, &UND_AD, &UND_AG, &UND_AD, &UND_AG, &UND_AD, &UND_AG, &UND_AE, &UND_AE, &UND_AD, &UND_AD, &UND_AD, &UND_AG, &UND_AE, &UND_AG, &UND_AG, &UND_AG, &UND_AG, &UND_AE, &UND_AG, &UND_AD, &UND_AD, &UND_AD, &UND_AE, &UND_AD, &UND_AG, &UND_AG, &UND_AG, &UND_AD, &UND_AG, &UND_MV, &UND_AG, &UND_AG, &UND_AG, &UND_AD, &UND_AD, &UND_AG, &UND_AE, &UND_AG, &UND_AG, &UND_AG, &UND_AG, &UND_AD, &UND_AG, &UND_PT, &UND_AG, &UND_AE, &UND_AD, &UND_AD, &UND_AG, &UND_AE, &UND_AD, &UND_AG, &UND_AD, &UND_AD, &UND_AD, &UND_AG, &UND_AE, &UND_AG, &UND_AG, &UND_AG, &UND_AG, &UND_AG, &UND_AD, &UND_AG, &UND_AG, &UND_AG, &UND_AG, &UND_AG, &UND_AG];
static KEYS: [&str; 115usize] = ["und", "und-AD", "und-AE", "und-AF", "und-AG", "und-AN", "und-AS", "und-AT", "und-AX", "und-BD", "und-BE", "und-BG", "und-BH", "und-BR", "und-BS", "und-BT", "und-BW", "und-BZ", "und-CA", "und-CH", "und-CO", "und-CZ", "und-DE", "und-DJ", "und-DK", "und-DM", "und-DO", "und-DZ", "und-EE", "und-EG", "und-ES", "und-ET", "und-FI", "und-FJ", "und-FO", "und-FR", "und-GB", "und-GF", "und-GG", "und-GI", "und-GP", "und-GR", "und-GT", "und-GU", "und-HK", "und-HN", "und-HU", "und-ID", "und-IE", "und-IL", "und-IM", "und-IN", "und-IQ", "und-IR", "und-IS", "und-IT", "und-JE", "und-JM", "und-JO", "und-JP", "und-KE", "und-KH", "und-KR", "und-KW", "und-LA", "und-LI", "und-LT", "und-LU", "und-LY", "und-MC", "und-MH", "und-MM", "und-MO", "und-MQ", "und-MT", "und-MV", "und-MX", "und-MZ", "und-NI", "und-NL", "und-NO", "und-NP", "und-OM", "und-PA", "und-PE", "und-PH", "und-PK", "und-PL", "und-PR", "und-PT", "und-PY", "und-QA", "und-RE", "und-RU", "und-SA", "und-SD", "und-SE", "und-SG", "und-SJ", "und-SK", "und-SM", "und-SV", "und-SY", "und-TH", "und-TT", "und-TW", "und-UM", "und-US", "und-VA", "und-VE", "und-VI", "und-WS", "und-YE", "und-ZA", "und-ZW"];
let mut metadata = icu_provider::DataResponseMetadata::default();
let payload = if let Ok(payload) = KEYS.binary_search_by(|k| req.locale.strict_cmp(k.as_bytes()).reverse()).map(|i| *unsafe { VALUES.get_unchecked(i) }) {
payload
} else {
const FALLBACKER: icu::locid_transform::fallback::LocaleFallbackerWithConfig<'static> = icu::locid_transform::fallback::LocaleFallbacker::new().for_config(<icu::calendar::provider::WeekDataV1Marker as icu_provider::KeyedDataMarker>::KEY.fallback_config());
let mut fallback_iterator = FALLBACKER.fallback_for(req.locale.clone());
loop {
if let Ok(payload) = KEYS.binary_search_by(|k| fallback_iterator.get().strict_cmp(k.as_bytes()).reverse()).map(|i| *unsafe { VALUES.get_unchecked(i) }) {
metadata.locale = Some(fallback_iterator.take());
break payload;
}
fallback_iterator.step();
}
};
Ok(icu_provider::DataResponse { payload: Some(icu_provider::DataPayload::from_static_ref(payload)), metadata })
}
}
};
}

12
third_party/rust/icu_calendar_data/src/lib.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,12 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
//! Data for the icu_calendar crate
#![no_std]
#[cfg(icu4x_custom_data)]
include!(concat!(core::env!("ICU4X_DATA_DIR"), "/macros.rs"));
#[cfg(not(icu4x_custom_data))]
include!("../data/macros.rs");