1use time::{Date, Month};
2
3use super::Timestamp;
4
5#[inline(always)]
6#[allow(dead_code)]
7const fn is_leap_year(y: i32) -> bool {
8 y & (if y % 25 == 0 { 15 } else { 3 }) == 0
12}
13
14#[inline(always)]
15#[cfg(target_feature = "avx2")]
16unsafe fn to_calendar_date_avx2(date: Date) -> (i32, Month, u8) {
17 import_intrinsics!(x86::{
18 _mm256_set1_epi16, _mm256_set_epi16, _mm256_setzero_si256,
19 _mm256_cmpeq_epi16, _mm256_movemask_epi8, _mm256_subs_epu16, __m256i
20 });
21
22 let year = date.year();
23
24 #[rustfmt::skip]
25 let mut days = match is_leap_year(year) {
26 true => _mm256_set_epi16(i16::MAX, i16::MAX, i16::MAX, i16::MAX, 335, 305, 274, 244, 213, 182, 152, 121, 91, 60, 31, 0),
27 false => _mm256_set_epi16(i16::MAX, i16::MAX, i16::MAX, i16::MAX, 334, 304, 273, 243, 212, 181, 151, 120, 90, 59, 31, 0),
28 };
29
30 days = _mm256_subs_epu16(_mm256_set1_epi16(date.ordinal() as i16), days);
31
32 let mask = _mm256_movemask_epi8(_mm256_cmpeq_epi16(days, _mm256_setzero_si256()));
33 let month = mask.trailing_zeros() / 2;
34 let day = *core::mem::transmute::<__m256i, [u16; 16]>(days).get_unchecked(month as usize - 1);
35
36 (year, core::mem::transmute::<u8, Month>(month as u8), day as u8)
37}
38
39#[inline(always)]
40#[cfg(target_feature = "sse2")]
41unsafe fn to_calendar_date_sse2(date: Date) -> (i32, Month, u8) {
42 import_intrinsics!(x86::{
43 _mm_cmpeq_epi16, _mm_movemask_epi8, _mm_set1_epi16,
44 _mm_set_epi16, _mm_setzero_si128, _mm_subs_epu16, __m128i
45 });
46
47 let year = date.year();
48
49 #[rustfmt::skip]
50 let (mut hd, mut ld) = match is_leap_year(year) {
51 true => (
52 _mm_set_epi16(i16::MAX, i16::MAX, i16::MAX, i16::MAX, 335, 305, 274, 244),
53 _mm_set_epi16(213, 182, 152, 121, 91, 60, 31, 0)),
54 false => (
55 _mm_set_epi16(i16::MAX, i16::MAX, i16::MAX, i16::MAX, 334, 304, 273, 243),
56 _mm_set_epi16(212, 181, 151, 120, 90, 59, 31, 0))
57 };
58
59 let ordinals = _mm_set1_epi16(date.ordinal() as i16);
60
61 hd = _mm_subs_epu16(ordinals, hd);
62 ld = _mm_subs_epu16(ordinals, ld);
63
64 let z = _mm_setzero_si128();
65
66 let hm = _mm_movemask_epi8(_mm_cmpeq_epi16(hd, z));
67 let lm = _mm_movemask_epi8(_mm_cmpeq_epi16(ld, z));
68
69 let mask = (hm << 16) | lm;
70 let month = mask.trailing_zeros() / 2;
71
72 let day = *core::mem::transmute::<[__m128i; 2], [u16; 16]>([ld, hd]).get_unchecked(month as usize - 1);
73
74 (year, core::mem::transmute::<u8, Month>(month as u8), day as u8)
75}
76
77#[inline(always)]
78#[allow(unreachable_code)]
79pub(crate) fn to_calendar_date(date: Date) -> (i32, Month, u8) {
80 #[cfg(target_feature = "avx2")]
81 return unsafe { to_calendar_date_avx2(date) };
83
84 #[cfg(target_feature = "sse2")]
85 return unsafe { to_calendar_date_sse2(date) };
87
88 date.to_calendar_date()
89}
90
91impl Timestamp {
92 #[inline(always)]
105 #[must_use]
106 pub fn to_calendar_date(&self) -> (i32, Month, u8) {
107 to_calendar_date(self.date())
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[cfg(target_feature = "avx2")]
116 #[test]
117 fn test_to_calendar_date() {
118 for year in &[2004, 2005, 2006] {
119 for ordinal in 0..367 {
120 let Ok(date) = Date::from_ordinal_date(*year, ordinal) else {
121 continue;
122 };
123
124 let (avx2, sse2, none) = unsafe {
126 (
127 to_calendar_date_avx2(date),
128 to_calendar_date_sse2(date),
129 date.to_calendar_date(),
130 )
131 };
132
133 assert_eq!(none, avx2);
134 assert_eq!(none, sse2);
135 }
136 }
137 }
138}