Skip to content

Commit 1555411

Browse files
committed
store durations as milliseconds
1 parent 439ca13 commit 1555411

File tree

1 file changed

+45
-47
lines changed

1 file changed

+45
-47
lines changed

components/calendar/tests/pingqi.rs

Lines changed: 45 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -14,27 +14,40 @@ use icu_calendar::{
1414

1515
#[derive(Debug, Copy, Clone)]
1616
struct Duration {
17-
days: u32,
18-
milliseconds: u32,
17+
milliseconds: i64,
1918
}
2019

2120
impl Duration {
22-
fn to_i64(&self) -> i64 {
23-
self.days as i64 * MILLISECONDS_IN_DAY as i64 + self.milliseconds as i64
21+
const fn new(days: u32, millis_in_day: u32) -> Self {
22+
Self {
23+
milliseconds: days as i64 * MILLISECONDS_IN_DAY + millis_in_day as i64,
24+
}
25+
}
26+
}
27+
28+
impl core::ops::Mul<i64> for Duration {
29+
type Output = Self;
30+
31+
fn mul(self, rhs: i64) -> Self::Output {
32+
Self {
33+
milliseconds: self.milliseconds * rhs,
34+
}
2435
}
2536
}
2637

2738
macro_rules! duration_from_day_fraction {
2839
($n:tt $(/ $d:tt)+) => {{
2940
let days = ($n $( / $d)+) as u32;
30-
// This works by using exact rounding in the first term, and intermediate rounding in the second term
3141
let milliseconds = ((MILLISECONDS_IN_DAY as i128 * $n as i128 $( / $d as i128)+) - ($n as i128 $( / $d as i128)+ * MILLISECONDS_IN_DAY as i128)) as u32;
32-
Duration { days, milliseconds }
42+
Duration::new(days, milliseconds)
3343
}};
3444
($n:tt $(/ $d:tt)+, exact) => {{
35-
let d = duration_from_day_fraction!($n $(/ $d)+);
36-
assert!((d.milliseconds as i128 $(* $d as i128)+) % MILLISECONDS_IN_DAY as i128 == 0, "inexact");
37-
d
45+
let days = ($n $( / $d)+) as u32;
46+
// This works by using exact rounding in the first term, and intermediate rounding in the second term
47+
let milliseconds = ((MILLISECONDS_IN_DAY as i128 * $n as i128 $( / $d as i128)+) - ($n as i128 $( / $d as i128)+ * MILLISECONDS_IN_DAY as i128)) as u32;
48+
// milliseconds needs to be a multiple of (MILLISECONDS_IN_DAY / $denominators) to be exact
49+
assert!((milliseconds as i128 $(* $d as i128)+) % MILLISECONDS_IN_DAY as i128 == 0, "inexact");
50+
Duration::new(days, milliseconds)
3851
}};
3952
}
4053

@@ -62,55 +75,43 @@ struct LocalMoment {
6275
local_milliseconds: u32,
6376
}
6477

65-
impl LocalMoment {
66-
/// Adds a specific [`Duration`] to this moment `n` times.
67-
fn add_duration_times_n(&self, duration: Duration, n: i64) -> LocalMoment {
68-
let temp = self.local_milliseconds as i64 + (duration.milliseconds as i64 * n);
69-
let (extra_days, local_milliseconds) = (
70-
temp.div_euclid(MILLISECONDS_IN_DAY),
71-
temp.rem_euclid(MILLISECONDS_IN_DAY),
72-
);
73-
let rata_die = self.rata_die + extra_days + (duration.days as i64 * n) as i64;
74-
let local_milliseconds = u32::try_from(local_milliseconds).unwrap();
78+
impl core::ops::Add<Duration> for LocalMoment {
79+
type Output = Self;
80+
81+
fn add(self, duration: Duration) -> Self::Output {
82+
let temp = self.local_milliseconds as i64 + duration.milliseconds;
7583
Self {
76-
rata_die,
77-
local_milliseconds,
84+
rata_die: self.rata_die + temp.div_euclid(MILLISECONDS_IN_DAY),
85+
local_milliseconds: temp.rem_euclid(MILLISECONDS_IN_DAY) as u32,
7886
}
7987
}
80-
81-
/// Converts this moment to an i64 local timestamp in milliseconds (with Rata Die epoch)
82-
fn to_i64(&self) -> i64 {
83-
(self.rata_die.to_i64_date() * MILLISECONDS_IN_DAY) + self.local_milliseconds as i64
84-
}
8588
}
8689

8790
#[test]
8891
fn test_local_moment_add() {
92+
#![allow(clippy::erasing_op)]
8993
let local_moment = LocalMoment {
9094
rata_die: RataDie::new(1000),
9195
local_milliseconds: 0,
9296
};
93-
let duration = Duration {
94-
days: 77,
95-
milliseconds: 25000000,
96-
};
97-
assert_eq!(local_moment.add_duration_times_n(duration, 0), local_moment);
97+
let duration = Duration::new(77, 25000000);
98+
assert_eq!(local_moment + duration * 0, local_moment);
9899
assert_eq!(
99-
local_moment.add_duration_times_n(duration, 1),
100+
local_moment + duration * 1,
100101
LocalMoment {
101102
rata_die: RataDie::new(1077),
102103
local_milliseconds: 25000000
103104
}
104105
);
105106
assert_eq!(
106-
local_moment.add_duration_times_n(duration, -1),
107+
local_moment + duration * -1,
107108
LocalMoment {
108109
rata_die: RataDie::new(922),
109110
local_milliseconds: 61400000
110111
}
111112
);
112113
assert_eq!(
113-
local_moment.add_duration_times_n(duration, -500),
114+
local_moment + duration * -500,
114115
LocalMoment {
115116
rata_die: RataDie::new(-37645),
116117
local_milliseconds: 28000000,
@@ -148,13 +149,12 @@ fn periodic_duration_on_or_before(
148149
duration: Duration,
149150
) -> LocalMoment {
150151
// For now, do math as i64 milliseconds, which covers 600 million years.
151-
let upper_bound = LocalMoment {
152-
rata_die: rata_die + 1,
153-
local_milliseconds: 0,
154-
}
155-
.to_i64();
156-
let num_periods = (upper_bound - base_moment.to_i64() - 1).div_euclid(duration.to_i64());
157-
base_moment.add_duration_times_n(duration, num_periods)
152+
base_moment
153+
+ duration
154+
* ((rata_die - base_moment.rata_die + 1) * MILLISECONDS_IN_DAY
155+
- base_moment.local_milliseconds as i64
156+
- 1)
157+
.div_euclid(duration.milliseconds)
158158
}
159159

160160
impl icu_calendar::cal::scaffold::UnstableSealed for FastPingqi {}
@@ -177,12 +177,11 @@ impl chinese::Rules for FastPingqi {
177177

178178
// Skip the months before the year
179179
while solar_term < 0 {
180-
let next_new_moon = new_moon.add_duration_times_n(MEAN_SYNODIC_MONTH_LENGTH, 1);
180+
let next_new_moon = new_moon + MEAN_SYNODIC_MONTH_LENGTH;
181181

182182
if major_solar_term.rata_die < next_new_moon.rata_die {
183183
solar_term += 1;
184-
major_solar_term =
185-
major_solar_term.add_duration_times_n(MEAN_GREGORIAN_SOLAR_TERM_LENGTH, 1);
184+
major_solar_term = major_solar_term + MEAN_GREGORIAN_SOLAR_TERM_LENGTH;
186185
}
187186

188187
new_moon = next_new_moon;
@@ -195,12 +194,11 @@ impl chinese::Rules for FastPingqi {
195194

196195
// Iterate over the 12 solar terms, producing potentially 13 months
197196
while solar_term < 12 {
198-
let next_new_moon = new_moon.add_duration_times_n(MEAN_SYNODIC_MONTH_LENGTH, 1);
197+
let next_new_moon = new_moon + MEAN_SYNODIC_MONTH_LENGTH;
199198

200199
if major_solar_term.rata_die < next_new_moon.rata_die {
201200
solar_term += 1;
202-
major_solar_term =
203-
major_solar_term.add_duration_times_n(MEAN_GREGORIAN_SOLAR_TERM_LENGTH, 1);
201+
major_solar_term = major_solar_term + MEAN_GREGORIAN_SOLAR_TERM_LENGTH;
204202
} else {
205203
leap_month = Some(month as u8 + 1);
206204
}

0 commit comments

Comments
 (0)