jiff/util/round/
increment.rs1use crate::{
12 error::{err, Error},
13 util::{
14 rangeint::RFrom,
15 t::{self, Constant, C},
16 },
17 Unit,
18};
19
20pub(crate) fn for_span(
24 unit: Unit,
25 increment: i64,
26) -> Result<t::NoUnits128, Error> {
27 static LIMIT: &[Constant] = &[
29 t::NANOS_PER_MICRO,
30 t::MICROS_PER_MILLI,
31 t::MILLIS_PER_SECOND,
32 t::SECONDS_PER_MINUTE,
33 t::MINUTES_PER_HOUR,
34 t::HOURS_PER_CIVIL_DAY,
35 ];
36 if unit >= Unit::Day {
43 Ok(t::NoUnits128::rfrom(t::NoUnits::new_unchecked(increment)))
47 } else {
48 get_with_limit(unit, increment, "span", LIMIT)
49 }
50}
51
52pub(crate) fn for_datetime(
57 unit: Unit,
58 increment: i64,
59) -> Result<t::NoUnits128, Error> {
60 static LIMIT: &[Constant] = &[
62 t::NANOS_PER_MICRO,
63 t::MICROS_PER_MILLI,
64 t::MILLIS_PER_SECOND,
65 t::SECONDS_PER_MINUTE,
66 t::MINUTES_PER_HOUR,
67 t::HOURS_PER_CIVIL_DAY,
68 Constant(2),
69 ];
70 get_with_limit(unit, increment, "datetime", LIMIT)
71}
72
73pub(crate) fn for_time(
78 unit: Unit,
79 increment: i64,
80) -> Result<t::NoUnits128, Error> {
81 static LIMIT: &[Constant] = &[
83 t::NANOS_PER_MICRO,
84 t::MICROS_PER_MILLI,
85 t::MILLIS_PER_SECOND,
86 t::SECONDS_PER_MINUTE,
87 t::MINUTES_PER_HOUR,
88 t::HOURS_PER_CIVIL_DAY,
89 ];
90 get_with_limit(unit, increment, "time", LIMIT)
91}
92
93pub(crate) fn for_timestamp(
98 unit: Unit,
99 increment: i64,
100) -> Result<t::NoUnits128, Error> {
101 static MAX: &[Constant] = &[
103 t::NANOS_PER_CIVIL_DAY,
104 t::MICROS_PER_CIVIL_DAY,
105 t::MILLIS_PER_CIVIL_DAY,
106 t::SECONDS_PER_CIVIL_DAY,
107 t::MINUTES_PER_CIVIL_DAY,
108 t::HOURS_PER_CIVIL_DAY,
109 ];
110 get_with_max(unit, increment, "timestamp", MAX)
111}
112
113fn get_with_limit(
114 unit: Unit,
115 increment: i64,
116 what: &'static str,
117 limit: &[t::Constant],
118) -> Result<t::NoUnits128, Error> {
119 let increment = t::NoUnits::new_unchecked(increment);
121 if increment <= C(0) {
122 return Err(err!(
123 "rounding increment {increment} for {unit} must be \
124 greater than zero",
125 unit = unit.plural(),
126 ));
127 }
128 let Some(must_divide) = limit.get(unit as usize) else {
129 return Err(err!(
130 "{what} rounding does not support {unit}",
131 unit = unit.plural()
132 ));
133 };
134 let must_divide = t::NoUnits::rfrom(*must_divide);
135 if increment >= must_divide || must_divide % increment != C(0) {
136 Err(err!(
137 "increment {increment} for rounding {what} to {unit} \
138 must be 1) less than {must_divide}, 2) divide into \
139 it evenly and 3) greater than zero",
140 unit = unit.plural(),
141 ))
142 } else {
143 Ok(t::NoUnits128::rfrom(increment))
144 }
145}
146
147fn get_with_max(
148 unit: Unit,
149 increment: i64,
150 what: &'static str,
151 max: &[t::Constant],
152) -> Result<t::NoUnits128, Error> {
153 let increment = t::NoUnits::new_unchecked(increment);
155 if increment <= C(0) {
156 return Err(err!(
157 "rounding increment {increment} for {unit} must be \
158 greater than zero",
159 unit = unit.plural(),
160 ));
161 }
162 let Some(must_divide) = max.get(unit as usize) else {
163 return Err(err!(
164 "{what} rounding does not support {unit}",
165 unit = unit.plural()
166 ));
167 };
168 let must_divide = t::NoUnits::rfrom(*must_divide);
169 if increment > must_divide || must_divide % increment != C(0) {
170 Err(err!(
171 "increment {increment} for rounding {what} to {unit} \
172 must be 1) less than or equal to {must_divide}, \
173 2) divide into it evenly and 3) greater than zero",
174 unit = unit.plural(),
175 ))
176 } else {
177 Ok(t::NoUnits128::rfrom(increment))
178 }
179}