@@ -19,7 +19,7 @@ use crate::ext::DigitCount;
19
19
#[ cfg( feature = "formatting" ) ]
20
20
use crate :: formatting:: Formattable ;
21
21
use crate :: internal_macros:: {
22
- cascade , const_try, const_try_opt, div_floor, ensure_ranged, expect_opt, impl_add_assign,
22
+ const_try, const_try_opt, div_floor, ensure_ranged, expect_opt, impl_add_assign,
23
23
impl_sub_assign,
24
24
} ;
25
25
#[ cfg( feature = "parsing" ) ]
@@ -74,28 +74,41 @@ impl Date {
74
74
unsafe { Self :: __from_ordinal_date_unchecked ( MAX_YEAR , days_in_year ( MAX_YEAR ) ) } ;
75
75
76
76
// region: constructors
77
- /// Construct a `Date` from the year and ordinal values , the validity of which must be
77
+ /// Construct a `Date` from its internal representation , the validity of which must be
78
78
/// guaranteed by the caller.
79
79
///
80
80
/// # Safety
81
81
///
82
- /// `ordinal` must be non-zero and at most the number of days in `year`. `year` should be in the
83
- /// range `MIN_YEAR..=MAX_YEAR`, but this is not a safety invariant.
84
- #[ doc( hidden) ]
85
- pub const unsafe fn __from_ordinal_date_unchecked ( year : i32 , ordinal : u16 ) -> Self {
82
+ /// - `ordinal` must be non-zero and at most the number of days in `year`
83
+ /// - `is_leap_year` must be `true` if and only if `year` is a leap year
84
+ const unsafe fn from_parts ( year : i32 , is_leap_year : bool , ordinal : u16 ) -> Self {
86
85
debug_assert ! ( year >= MIN_YEAR ) ;
87
86
debug_assert ! ( year <= MAX_YEAR ) ;
88
87
debug_assert ! ( ordinal != 0 ) ;
89
88
debug_assert ! ( ordinal <= days_in_year( year) ) ;
90
-
91
- let is_leap = is_leap_year ( year) as i32 ;
89
+ debug_assert ! ( crate :: util:: is_leap_year( year) == is_leap_year) ;
92
90
93
91
Self {
94
- // Safety: The caller must guarantee that `ordinal` is not zero.
95
- value : unsafe { NonZeroI32 :: new_unchecked ( year << 10 | is_leap << 9 | ordinal as i32 ) } ,
92
+ // Safety: `ordinal` is not zero.
93
+ value : unsafe {
94
+ NonZeroI32 :: new_unchecked ( year << 10 | ( is_leap_year as i32 ) << 9 | ordinal as i32 )
95
+ } ,
96
96
}
97
97
}
98
98
99
+ /// Construct a `Date` from the year and ordinal values, the validity of which must be
100
+ /// guaranteed by the caller.
101
+ ///
102
+ /// # Safety
103
+ ///
104
+ /// `ordinal` must be non-zero and at most the number of days in `year`. `year` should be in the
105
+ /// range `MIN_YEAR..=MAX_YEAR`, but this is not a safety invariant.
106
+ #[ doc( hidden) ]
107
+ pub const unsafe fn __from_ordinal_date_unchecked ( year : i32 , ordinal : u16 ) -> Self {
108
+ // Safety: The caller must guarantee that `ordinal` is not zero.
109
+ unsafe { Self :: from_parts ( year, is_leap_year ( year) , ordinal) }
110
+ }
111
+
99
112
/// Attempt to create a `Date` from the year, month, and day.
100
113
///
101
114
/// ```rust
@@ -259,47 +272,50 @@ impl Date {
259
272
pub const fn from_julian_day ( julian_day : i32 ) -> Result < Self , error:: ComponentRange > {
260
273
type JulianDay = RangedI32 < { Date :: MIN . to_julian_day ( ) } , { Date :: MAX . to_julian_day ( ) } > ;
261
274
ensure_ranged ! ( JulianDay : julian_day) ;
262
- Ok ( Self :: from_julian_day_unchecked ( julian_day) )
275
+ // Safety: The Julian day number is in range.
276
+ Ok ( unsafe { Self :: from_julian_day_unchecked ( julian_day) } )
263
277
}
264
278
265
279
/// Create a `Date` from the Julian day.
266
280
///
267
- /// This does not check the validity of the provided Julian day, and as such may result in an
268
- /// internally invalid value.
269
- #[ doc( alias = "from_julian_date_unchecked" ) ]
270
- pub ( crate ) const fn from_julian_day_unchecked ( julian_day : i32 ) -> Self {
281
+ /// # Safety
282
+ ///
283
+ /// The provided Julian day number must be between `Date::MIN.to_julian_day()` and
284
+ /// `Date::MAX.to_julian_day()` inclusive.
285
+ pub ( crate ) const unsafe fn from_julian_day_unchecked ( julian_day : i32 ) -> Self {
271
286
debug_assert ! ( julian_day >= Self :: MIN . to_julian_day( ) ) ;
272
287
debug_assert ! ( julian_day <= Self :: MAX . to_julian_day( ) ) ;
273
288
274
- // To avoid a potential overflow, the value may need to be widened for some arithmetic.
289
+ const S : i32 = 2_500 ;
290
+ const K : i32 = 719_468 + 146_097 * S ;
291
+ const L : i32 = 400 * S ;
275
292
276
- let z = julian_day - 1_721_119 ;
277
- let ( mut year, mut ordinal) = if julian_day < -19_752_948 || julian_day > 23_195_514 {
278
- let g = 100 * z as i64 - 25 ;
279
- let a = ( g / 3_652_425 ) as i32 ;
280
- let b = a - a / 4 ;
281
- let year = div_floor ! ( 100 * b as i64 + g, 36525 ) as i32 ;
282
- let ordinal = ( b + z - div_floor ! ( 36525 * year as i64 , 100 ) as i32 ) as _ ;
283
- ( year, ordinal)
284
- } else {
285
- let g = 100 * z - 25 ;
286
- let a = g / 3_652_425 ;
287
- let b = a - a / 4 ;
288
- let year = div_floor ! ( 100 * b + g, 36525 ) ;
289
- let ordinal = ( b + z - div_floor ! ( 36525 * year, 100 ) ) as _ ;
290
- ( year, ordinal)
291
- } ;
293
+ let julian_day = julian_day - 2_440_588 ;
294
+ let n = ( julian_day + K ) as u32 ;
295
+
296
+ let n_1 = 4 * n + 3 ;
297
+ let c = n_1 / 146_097 ;
298
+ let n_c = n_1 % 146_097 / 4 ;
299
+
300
+ let n_2 = 4 * n_c + 3 ;
301
+ let p_2 = 2_939_745 * n_2 as u64 ;
302
+ let z = ( p_2 >> 32 ) as u32 ;
303
+ let n_y = p_2 as u32 / 2_939_745 / 4 ;
304
+ let y = 100 * c + z;
305
+
306
+ let j = n_y >= 306 ;
307
+ let y_g = y as i32 - L + j as i32 ;
292
308
293
- if is_leap_year ( year ) {
294
- ordinal += 60 ;
295
- cascade ! ( ordinal in 1 .. 367 => year ) ;
309
+ let is_leap_year = is_leap_year ( y_g ) ;
310
+ let ordinal = if j {
311
+ n_y - 305
296
312
} else {
297
- ordinal += 59 ;
298
- cascade ! ( ordinal in 1 ..366 => year) ;
299
- }
313
+ n_y + 60 + is_leap_year as u32
314
+ } ;
300
315
301
- // Safety: `ordinal` is not zero.
302
- unsafe { Self :: __from_ordinal_date_unchecked ( year, ordinal) }
316
+ // Safety: `ordinal` is not zero and `is_leap_year` is correct, so long as the Julian day
317
+ // number is in range.
318
+ unsafe { Self :: from_parts ( y_g, is_leap_year, ordinal as _ ) }
303
319
}
304
320
// endregion constructors
305
321
@@ -708,9 +724,6 @@ impl Date {
708
724
709
725
/// Get the Julian day for the date.
710
726
///
711
- /// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is
712
- /// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms).
713
- ///
714
727
/// ```rust
715
728
/// # use time_macros::date;
716
729
/// assert_eq!(date!(-4713 - 11 - 24).to_julian_day(), 0);
@@ -719,12 +732,15 @@ impl Date {
719
732
/// assert_eq!(date!(2019-12-31).to_julian_day(), 2_458_849);
720
733
/// ```
721
734
pub const fn to_julian_day ( self ) -> i32 {
722
- let year = self . year ( ) - 1 ;
723
- let ordinal = self . ordinal ( ) as i32 ;
735
+ let ( year, ordinal) = self . to_ordinal_date ( ) ;
736
+
737
+ // The algorithm requires a non-negative year. Add the lowest value to make it so. This is
738
+ // adjusted for at the end with the final subtraction.
739
+ let adj_year = year + 999_999 ;
740
+ let century = adj_year / 100 ;
724
741
725
- ordinal + 365 * year + div_floor ! ( year, 4 ) - div_floor ! ( year, 100 )
726
- + div_floor ! ( year, 400 )
727
- + 1_721_425
742
+ let days_before_year = ( 1461 * adj_year as i64 / 4 ) as i32 - century + century / 4 ;
743
+ days_before_year + ordinal as i32 - 363_521_075
728
744
}
729
745
// endregion getters
730
746
0 commit comments