1 | use crate::{ |
2 | error::{err, ErrorContext}, |
3 | fmt::Parsed, |
4 | util::{ |
5 | escape, parse, |
6 | rangeint::RFrom, |
7 | t::{self, C}, |
8 | }, |
9 | Error, SignedDuration, Span, Unit, |
10 | }; |
11 | |
12 | /// A simple formatter for converting `i64` values to ASCII byte strings. |
13 | /// |
14 | /// This avoids going through the formatting machinery which seems to |
15 | /// substantially slow things down. |
16 | /// |
17 | /// The `itoa` crate does the same thing as this formatter, but is a bit |
18 | /// faster. We roll our own which is a bit slower, but gets us enough of a win |
19 | /// to be satisfied with and with (almost) pure safe code. |
20 | /// |
21 | /// By default, this only includes the sign if it's negative. To always include |
22 | /// the sign, set `force_sign` to `true`. |
23 | #[derive (Clone, Copy, Debug)] |
24 | pub(crate) struct DecimalFormatter { |
25 | force_sign: Option<bool>, |
26 | minimum_digits: u8, |
27 | padding_byte: u8, |
28 | } |
29 | |
30 | impl DecimalFormatter { |
31 | /// Creates a new decimal formatter using the default configuration. |
32 | pub(crate) const fn new() -> DecimalFormatter { |
33 | DecimalFormatter { |
34 | force_sign: None, |
35 | minimum_digits: 0, |
36 | padding_byte: b'0' , |
37 | } |
38 | } |
39 | |
40 | /// Format the given value using this configuration as a decimal ASCII |
41 | /// number. |
42 | #[cfg (test)] |
43 | pub(crate) const fn format(&self, value: i64) -> Decimal { |
44 | Decimal::new(self, value) |
45 | } |
46 | |
47 | /// Forces the sign to be rendered, even if it's positive. |
48 | /// |
49 | /// When `zero_is_positive` is true, then a zero value is formatted with a |
50 | /// positive sign. Otherwise, it is formatted with a negative sign. |
51 | #[cfg (test)] |
52 | pub(crate) const fn force_sign( |
53 | self, |
54 | zero_is_positive: bool, |
55 | ) -> DecimalFormatter { |
56 | DecimalFormatter { force_sign: Some(zero_is_positive), ..self } |
57 | } |
58 | |
59 | /// The minimum number of digits/padding that this number should be |
60 | /// formatted with. If the number would have fewer digits than this, then |
61 | /// it is padded out with the padding byte (which is zero by default) until |
62 | /// the minimum is reached. |
63 | /// |
64 | /// The minimum number of digits is capped at the maximum number of digits |
65 | /// for an i64 value (which is 19). |
66 | pub(crate) const fn padding(self, mut digits: u8) -> DecimalFormatter { |
67 | if digits > Decimal::MAX_I64_DIGITS { |
68 | digits = Decimal::MAX_I64_DIGITS; |
69 | } |
70 | DecimalFormatter { minimum_digits: digits, ..self } |
71 | } |
72 | |
73 | /// The padding byte to use when `padding` is set. |
74 | /// |
75 | /// The default is `0`. |
76 | pub(crate) const fn padding_byte(self, byte: u8) -> DecimalFormatter { |
77 | DecimalFormatter { padding_byte: byte, ..self } |
78 | } |
79 | } |
80 | |
81 | impl Default for DecimalFormatter { |
82 | fn default() -> DecimalFormatter { |
83 | DecimalFormatter::new() |
84 | } |
85 | } |
86 | |
87 | /// A formatted decimal number that can be converted to a sequence of bytes. |
88 | #[derive (Debug)] |
89 | pub(crate) struct Decimal { |
90 | buf: [u8; Self::MAX_I64_LEN as usize], |
91 | start: u8, |
92 | end: u8, |
93 | } |
94 | |
95 | impl Decimal { |
96 | /// Discovered via `i64::MIN.to_string().len()`. |
97 | const MAX_I64_LEN: u8 = 20; |
98 | /// Discovered via `i64::MAX.to_string().len()`. |
99 | const MAX_I64_DIGITS: u8 = 19; |
100 | |
101 | /// Using the given formatter, turn the value given into a decimal |
102 | /// representation using ASCII bytes. |
103 | pub(crate) const fn new( |
104 | formatter: &DecimalFormatter, |
105 | value: i64, |
106 | ) -> Decimal { |
107 | let sign = value.signum(); |
108 | let Some(mut value) = value.checked_abs() else { |
109 | let buf = [ |
110 | b'-' , b'9' , b'2' , b'2' , b'3' , b'3' , b'7' , b'2' , b'0' , b'3' , |
111 | b'6' , b'8' , b'5' , b'4' , b'7' , b'7' , b'5' , b'8' , b'0' , b'8' , |
112 | ]; |
113 | return Decimal { buf, start: 0, end: Self::MAX_I64_LEN }; |
114 | }; |
115 | let mut decimal = Decimal { |
116 | buf: [0; Self::MAX_I64_LEN as usize], |
117 | start: Self::MAX_I64_LEN, |
118 | end: Self::MAX_I64_LEN, |
119 | }; |
120 | loop { |
121 | decimal.start -= 1; |
122 | |
123 | let digit = (value % 10) as u8; |
124 | value /= 10; |
125 | decimal.buf[decimal.start as usize] = b'0' + digit; |
126 | if value == 0 { |
127 | break; |
128 | } |
129 | } |
130 | while decimal.len() < formatter.minimum_digits { |
131 | decimal.start -= 1; |
132 | decimal.buf[decimal.start as usize] = formatter.padding_byte; |
133 | } |
134 | if sign < 0 { |
135 | decimal.start -= 1; |
136 | decimal.buf[decimal.start as usize] = b'-' ; |
137 | } else if let Some(zero_is_positive) = formatter.force_sign { |
138 | let ascii_sign = |
139 | if sign > 0 || zero_is_positive { b'+' } else { b'-' }; |
140 | decimal.start -= 1; |
141 | decimal.buf[decimal.start as usize] = ascii_sign; |
142 | } |
143 | decimal |
144 | } |
145 | |
146 | /// Returns the total number of ASCII bytes (including the sign) that are |
147 | /// used to represent this decimal number. |
148 | #[inline ] |
149 | const fn len(&self) -> u8 { |
150 | self.end - self.start |
151 | } |
152 | |
153 | /// Returns the ASCII representation of this decimal as a byte slice. |
154 | /// |
155 | /// The slice returned is guaranteed to be valid ASCII. |
156 | #[inline ] |
157 | pub(crate) fn as_bytes(&self) -> &[u8] { |
158 | &self.buf[usize::from(self.start)..usize::from(self.end)] |
159 | } |
160 | |
161 | /// Returns the ASCII representation of this decimal as a string slice. |
162 | #[inline ] |
163 | pub(crate) fn as_str(&self) -> &str { |
164 | // SAFETY: This is safe because all bytes written to `self.buf` are |
165 | // guaranteed to be ASCII (including in its initial state), and thus, |
166 | // any subsequence is guaranteed to be valid UTF-8. |
167 | unsafe { core::str::from_utf8_unchecked(self.as_bytes()) } |
168 | } |
169 | } |
170 | |
171 | /// A simple formatter for converting fractional components to ASCII byte |
172 | /// strings. |
173 | /// |
174 | /// We only support precision to 9 decimal places, which corresponds to |
175 | /// nanosecond precision as a fractional second component. |
176 | #[derive (Clone, Copy, Debug)] |
177 | pub(crate) struct FractionalFormatter { |
178 | precision: Option<u8>, |
179 | } |
180 | |
181 | impl FractionalFormatter { |
182 | /// Creates a new fractional formatter using the given precision settings. |
183 | pub(crate) const fn new() -> FractionalFormatter { |
184 | FractionalFormatter { precision: None } |
185 | } |
186 | |
187 | /// Format the given value using this configuration as a decimal ASCII |
188 | /// fractional number. |
189 | pub(crate) const fn format(&self, value: i64) -> Fractional { |
190 | Fractional::new(self, value) |
191 | } |
192 | |
193 | /// Set the precision. |
194 | /// |
195 | /// If the `precision` is greater than `9`, then it is clamped to `9`. |
196 | /// |
197 | /// When the precision is not set, then it is automatically determined |
198 | /// based on the value. |
199 | pub(crate) const fn precision( |
200 | self, |
201 | precision: Option<u8>, |
202 | ) -> FractionalFormatter { |
203 | let precision = match precision { |
204 | None => None, |
205 | Some(p) if p > 9 => Some(9), |
206 | Some(p) => Some(p), |
207 | }; |
208 | FractionalFormatter { precision, ..self } |
209 | } |
210 | |
211 | /// Returns true if and only if at least one digit will be written for the |
212 | /// given value. |
213 | /// |
214 | /// This is useful for callers that need to know whether to write |
215 | /// a decimal separator, e.g., `.`, before the digits. |
216 | pub(crate) fn will_write_digits(self, value: i64) -> bool { |
217 | self.precision.map_or_else(|| value != 0, |p| p > 0) |
218 | } |
219 | |
220 | /// Returns true if and only if this formatter has an explicit non-zero |
221 | /// precision setting. |
222 | /// |
223 | /// This is useful for determining whether something like `0.000` needs to |
224 | /// be written in the case of a `precision=Some(3)` setting and a zero |
225 | /// value. |
226 | pub(crate) fn has_non_zero_fixed_precision(self) -> bool { |
227 | self.precision.map_or(false, |p| p > 0) |
228 | } |
229 | |
230 | /// Returns true if and only if this formatter has fixed zero precision. |
231 | /// That is, no matter what is given as input, a fraction is never written. |
232 | pub(crate) fn has_zero_fixed_precision(self) -> bool { |
233 | self.precision.map_or(false, |p| p == 0) |
234 | } |
235 | } |
236 | |
237 | /// A formatted fractional number that can be converted to a sequence of bytes. |
238 | #[derive (Debug)] |
239 | pub(crate) struct Fractional { |
240 | buf: [u8; Self::MAX_LEN as usize], |
241 | end: u8, |
242 | } |
243 | |
244 | impl Fractional { |
245 | /// Since we don't support precision bigger than this. |
246 | const MAX_LEN: u8 = 9; |
247 | |
248 | /// Using the given formatter, turn the value given into a fractional |
249 | /// decimal representation using ASCII bytes. |
250 | /// |
251 | /// Note that the fractional number returned *may* expand to an empty |
252 | /// slice of bytes. This occurs whenever the precision is set to `0`, or |
253 | /// when the precision is not set and the value is `0`. Any non-zero |
254 | /// explicitly set precision guarantees that the slice returned is not |
255 | /// empty. |
256 | /// |
257 | /// This panics if the value given isn't in the range `0..=999_999_999`. |
258 | pub(crate) const fn new( |
259 | formatter: &FractionalFormatter, |
260 | mut value: i64, |
261 | ) -> Fractional { |
262 | assert!(0 <= value && value <= 999_999_999); |
263 | let mut fractional = Fractional { |
264 | buf: [b'0' ; Self::MAX_LEN as usize], |
265 | end: Self::MAX_LEN, |
266 | }; |
267 | let mut i = 9; |
268 | loop { |
269 | i -= 1; |
270 | |
271 | let digit = (value % 10) as u8; |
272 | value /= 10; |
273 | fractional.buf[i] += digit; |
274 | if value == 0 { |
275 | break; |
276 | } |
277 | } |
278 | if let Some(precision) = formatter.precision { |
279 | fractional.end = precision; |
280 | } else { |
281 | while fractional.end > 0 |
282 | && fractional.buf[fractional.end as usize - 1] == b'0' |
283 | { |
284 | fractional.end -= 1; |
285 | } |
286 | } |
287 | fractional |
288 | } |
289 | |
290 | /// Returns the ASCII representation of this fractional number as a byte |
291 | /// slice. The slice returned may be empty. |
292 | /// |
293 | /// The slice returned is guaranteed to be valid ASCII. |
294 | pub(crate) fn as_bytes(&self) -> &[u8] { |
295 | &self.buf[..usize::from(self.end)] |
296 | } |
297 | |
298 | /// Returns the ASCII representation of this fractional number as a string |
299 | /// slice. The slice returned may be empty. |
300 | pub(crate) fn as_str(&self) -> &str { |
301 | // SAFETY: This is safe because all bytes written to `self.buf` are |
302 | // guaranteed to be ASCII (including in its initial state), and thus, |
303 | // any subsequence is guaranteed to be valid UTF-8. |
304 | unsafe { core::str::from_utf8_unchecked(self.as_bytes()) } |
305 | } |
306 | } |
307 | |
308 | /// Parses an optional fractional number from the start of `input`. |
309 | /// |
310 | /// If `input` does not begin with a `.` (or a `,`), then this returns `None` |
311 | /// and no input is consumed. Otherwise, up to 9 ASCII digits are parsed after |
312 | /// the decimal separator. |
313 | /// |
314 | /// While this is most typically used to parse the fractional component of |
315 | /// second units, it is also used to parse the fractional component of hours or |
316 | /// minutes in ISO 8601 duration parsing, and milliseconds and microseconds in |
317 | /// the "friendly" duration format. The return type in that case is obviously a |
318 | /// misnomer, but the range of possible values is still correct. (That is, the |
319 | /// fractional component of an hour is still limited to 9 decimal places per |
320 | /// the Temporal spec.) |
321 | #[cfg_attr (feature = "perf-inline" , inline(always))] |
322 | pub(crate) fn parse_temporal_fraction<'i>( |
323 | input: &'i [u8], |
324 | ) -> Result<Parsed<'i, Option<t::SubsecNanosecond>>, Error> { |
325 | // TimeFraction ::: |
326 | // TemporalDecimalFraction |
327 | // |
328 | // TemporalDecimalFraction ::: |
329 | // TemporalDecimalSeparator DecimalDigit |
330 | // TemporalDecimalSeparator DecimalDigit DecimalDigit |
331 | // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit |
332 | // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit |
333 | // DecimalDigit |
334 | // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit |
335 | // DecimalDigit DecimalDigit |
336 | // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit |
337 | // DecimalDigit DecimalDigit DecimalDigit |
338 | // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit |
339 | // DecimalDigit DecimalDigit DecimalDigit |
340 | // DecimalDigit |
341 | // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit |
342 | // DecimalDigit DecimalDigit DecimalDigit |
343 | // DecimalDigit DecimalDigit |
344 | // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit |
345 | // DecimalDigit DecimalDigit DecimalDigit |
346 | // DecimalDigit DecimalDigit DecimalDigit |
347 | // |
348 | // TemporalDecimalSeparator ::: one of |
349 | // . , |
350 | // |
351 | // DecimalDigit :: one of |
352 | // 0 1 2 3 4 5 6 7 8 9 |
353 | |
354 | #[inline (never)] |
355 | fn imp<'i>( |
356 | mut input: &'i [u8], |
357 | ) -> Result<Parsed<'i, Option<t::SubsecNanosecond>>, Error> { |
358 | let mkdigits = parse::slicer(input); |
359 | while mkdigits(input).len() <= 8 |
360 | && input.first().map_or(false, u8::is_ascii_digit) |
361 | { |
362 | input = &input[1..]; |
363 | } |
364 | let digits = mkdigits(input); |
365 | if digits.is_empty() { |
366 | return Err(err!( |
367 | "found decimal after seconds component, \ |
368 | but did not find any decimal digits after decimal" , |
369 | )); |
370 | } |
371 | // I believe this error can never happen, since we know we have no more |
372 | // than 9 ASCII digits. Any sequence of 9 ASCII digits can be parsed |
373 | // into an `i64`. |
374 | let nanoseconds = parse::fraction(digits, 9).map_err(|err| { |
375 | err!( |
376 | "failed to parse {digits:?} as fractional component \ |
377 | (up to 9 digits, nanosecond precision): {err}" , |
378 | digits = escape::Bytes(digits), |
379 | ) |
380 | })?; |
381 | // I believe this is also impossible to fail, since the maximal |
382 | // fractional nanosecond is 999_999_999, and which also corresponds |
383 | // to the maximal expressible number with 9 ASCII digits. So every |
384 | // possible expressible value here is in range. |
385 | let nanoseconds = |
386 | t::SubsecNanosecond::try_new("nanoseconds" , nanoseconds).map_err( |
387 | |err| err!("fractional nanoseconds are not valid: {err}" ), |
388 | )?; |
389 | Ok(Parsed { value: Some(nanoseconds), input }) |
390 | } |
391 | |
392 | if input.is_empty() || (input[0] != b'.' && input[0] != b',' ) { |
393 | return Ok(Parsed { value: None, input }); |
394 | } |
395 | imp(&input[1..]) |
396 | } |
397 | |
398 | /// This routine returns a span based on the given with fractional time applied |
399 | /// to it. |
400 | /// |
401 | /// For example, given a span like `P1dT1.5h`, the `unit` would be |
402 | /// `Unit::Hour`, the `value` would be `1` and the `fraction` would be |
403 | /// `500_000_000`. The span given would just be `1d`. The span returned would |
404 | /// be `P1dT1h30m`. |
405 | /// |
406 | /// Note that `fraction` can be a fractional hour, minute, second, millisecond |
407 | /// or microsecond (even though its type suggests its only a fraction of a |
408 | /// second). When milliseconds or microseconds, the given fraction has any |
409 | /// sub-nanosecond precision truncated. |
410 | /// |
411 | /// # Errors |
412 | /// |
413 | /// This can error if the resulting units would be too large for the limits on |
414 | /// a `span`. This also errors if `unit` is not `Hour`, `Minute`, `Second`, |
415 | /// `Millisecond` or `Microsecond`. |
416 | #[inline (never)] |
417 | pub(crate) fn fractional_time_to_span( |
418 | unit: Unit, |
419 | value: t::NoUnits, |
420 | fraction: t::SubsecNanosecond, |
421 | mut span: Span, |
422 | ) -> Result<Span, Error> { |
423 | let allowed = matches!( |
424 | unit, |
425 | Unit::Hour |
426 | | Unit::Minute |
427 | | Unit::Second |
428 | | Unit::Millisecond |
429 | | Unit::Microsecond |
430 | ); |
431 | if !allowed { |
432 | return Err(err!( |
433 | "fractional {unit} units are not allowed" , |
434 | unit = unit.singular(), |
435 | )); |
436 | } |
437 | // We switch everything over to nanoseconds and then divy that up as |
438 | // appropriate. In general, we always create a balanced span, but there |
439 | // are some cases where we can't. For example, if one serializes a span |
440 | // with both the maximum number of seconds and the maximum number of |
441 | // milliseconds, then this just can't be balanced due to the limits on |
442 | // each of the units. When this kind of span is serialized to a string, |
443 | // it results in a second value that is actually bigger than the maximum |
444 | // allowed number of seconds in a span. So here, we have to reverse that |
445 | // operation and spread the seconds over smaller units. This in turn |
446 | // creates an unbalanced span. Annoying. |
447 | // |
448 | // The above is why we have `if unit_value > MAX { <do adjustments> }` in |
449 | // the balancing code below. Basically, if we overshoot our limit, we back |
450 | // out anything over the limit and carry it over to the lesser units. If |
451 | // our value is truly too big, then the final call to set nanoseconds will |
452 | // fail. |
453 | let value = t::NoUnits128::rfrom(value); |
454 | let fraction = t::NoUnits128::rfrom(fraction); |
455 | let mut nanos = match unit { |
456 | Unit::Hour => { |
457 | (value * t::NANOS_PER_HOUR) + (fraction * t::SECONDS_PER_HOUR) |
458 | } |
459 | Unit::Minute => { |
460 | (value * t::NANOS_PER_MINUTE) + (fraction * t::SECONDS_PER_MINUTE) |
461 | } |
462 | Unit::Second => (value * t::NANOS_PER_SECOND) + fraction, |
463 | Unit::Millisecond => { |
464 | (value * t::NANOS_PER_MILLI) + (fraction / t::NANOS_PER_MICRO) |
465 | } |
466 | Unit::Microsecond => { |
467 | (value * t::NANOS_PER_MICRO) + (fraction / t::NANOS_PER_MILLI) |
468 | } |
469 | // We return an error above if we hit this case. |
470 | _ => unreachable!("unsupported unit: {unit:?}" ), |
471 | }; |
472 | |
473 | if unit >= Unit::Hour && nanos > C(0) { |
474 | let mut hours = nanos / t::NANOS_PER_HOUR; |
475 | nanos %= t::NANOS_PER_HOUR; |
476 | if hours > t::SpanHours::MAX_SELF { |
477 | nanos += (hours - t::SpanHours::MAX_SELF) * t::NANOS_PER_HOUR; |
478 | hours = t::NoUnits128::rfrom(t::SpanHours::MAX_SELF); |
479 | } |
480 | // OK because we just checked that our units are in range. |
481 | span = span.try_hours_ranged(hours).unwrap(); |
482 | } |
483 | if unit >= Unit::Minute && nanos > C(0) { |
484 | let mut minutes = nanos / t::NANOS_PER_MINUTE; |
485 | nanos %= t::NANOS_PER_MINUTE; |
486 | if minutes > t::SpanMinutes::MAX_SELF { |
487 | nanos += |
488 | (minutes - t::SpanMinutes::MAX_SELF) * t::NANOS_PER_MINUTE; |
489 | minutes = t::NoUnits128::rfrom(t::SpanMinutes::MAX_SELF); |
490 | } |
491 | // OK because we just checked that our units are in range. |
492 | span = span.try_minutes_ranged(minutes).unwrap(); |
493 | } |
494 | if unit >= Unit::Second && nanos > C(0) { |
495 | let mut seconds = nanos / t::NANOS_PER_SECOND; |
496 | nanos %= t::NANOS_PER_SECOND; |
497 | if seconds > t::SpanSeconds::MAX_SELF { |
498 | nanos += |
499 | (seconds - t::SpanSeconds::MAX_SELF) * t::NANOS_PER_SECOND; |
500 | seconds = t::NoUnits128::rfrom(t::SpanSeconds::MAX_SELF); |
501 | } |
502 | // OK because we just checked that our units are in range. |
503 | span = span.try_seconds_ranged(seconds).unwrap(); |
504 | } |
505 | if unit >= Unit::Millisecond && nanos > C(0) { |
506 | let mut millis = nanos / t::NANOS_PER_MILLI; |
507 | nanos %= t::NANOS_PER_MILLI; |
508 | if millis > t::SpanMilliseconds::MAX_SELF { |
509 | nanos += |
510 | (millis - t::SpanMilliseconds::MAX_SELF) * t::NANOS_PER_MILLI; |
511 | millis = t::NoUnits128::rfrom(t::SpanMilliseconds::MAX_SELF); |
512 | } |
513 | // OK because we just checked that our units are in range. |
514 | span = span.try_milliseconds_ranged(millis).unwrap(); |
515 | } |
516 | if unit >= Unit::Microsecond && nanos > C(0) { |
517 | let mut micros = nanos / t::NANOS_PER_MICRO; |
518 | nanos %= t::NANOS_PER_MICRO; |
519 | if micros > t::SpanMicroseconds::MAX_SELF { |
520 | nanos += |
521 | (micros - t::SpanMicroseconds::MAX_SELF) * t::NANOS_PER_MICRO; |
522 | micros = t::NoUnits128::rfrom(t::SpanMicroseconds::MAX_SELF); |
523 | } |
524 | // OK because we just checked that our units are in range. |
525 | span = span.try_microseconds_ranged(micros).unwrap(); |
526 | } |
527 | if nanos > C(0) { |
528 | span = span.try_nanoseconds_ranged(nanos).with_context(|| { |
529 | err!( |
530 | "failed to set nanosecond value {nanos} on span \ |
531 | determined from {value}. {fraction}" , |
532 | ) |
533 | })?; |
534 | } |
535 | |
536 | Ok(span) |
537 | } |
538 | |
539 | /// Like `fractional_time_to_span`, but just converts the fraction of the given |
540 | /// unit to a signed duration. |
541 | /// |
542 | /// Since a signed duration doesn't keep track of individual units, there is |
543 | /// no loss of fidelity between it and ISO 8601 durations like there is for |
544 | /// `Span`. |
545 | /// |
546 | /// Note that `fraction` can be a fractional hour, minute, second, millisecond |
547 | /// or microsecond (even though its type suggests its only a fraction of a |
548 | /// second). When milliseconds or microseconds, the given fraction has any |
549 | /// sub-nanosecond precision truncated. |
550 | /// |
551 | /// # Errors |
552 | /// |
553 | /// This returns an error if `unit` is not `Hour`, `Minute`, `Second`, |
554 | /// `Millisecond` or `Microsecond`. |
555 | #[inline (never)] |
556 | pub(crate) fn fractional_time_to_duration( |
557 | unit: Unit, |
558 | fraction: t::SubsecNanosecond, |
559 | ) -> Result<SignedDuration, Error> { |
560 | let fraction: ri64<_, _> = t::NoUnits::rfrom(fraction); |
561 | let nanos: ri64<_, _> = match unit { |
562 | Unit::Hour => fraction * t::SECONDS_PER_HOUR, |
563 | Unit::Minute => fraction * t::SECONDS_PER_MINUTE, |
564 | Unit::Second => fraction, |
565 | Unit::Millisecond => fraction / t::NANOS_PER_MICRO, |
566 | Unit::Microsecond => fraction / t::NANOS_PER_MILLI, |
567 | unit: Unit => { |
568 | return Err(err!( |
569 | "fractional {unit} units are not allowed" , |
570 | unit = unit.singular(), |
571 | )) |
572 | } |
573 | }; |
574 | Ok(SignedDuration::from_nanos(nanos.get())) |
575 | } |
576 | |
577 | #[cfg (test)] |
578 | mod tests { |
579 | use alloc::string::ToString; |
580 | |
581 | use super::*; |
582 | |
583 | #[test ] |
584 | fn decimal() { |
585 | let x = DecimalFormatter::new().format(i64::MIN); |
586 | assert_eq!(x.as_str(), "-9223372036854775808" ); |
587 | |
588 | let x = DecimalFormatter::new().format(i64::MIN + 1); |
589 | assert_eq!(x.as_str(), "-9223372036854775807" ); |
590 | |
591 | let x = DecimalFormatter::new().format(i64::MAX); |
592 | assert_eq!(x.as_str(), "9223372036854775807" ); |
593 | |
594 | let x = DecimalFormatter::new().force_sign(true).format(i64::MAX); |
595 | assert_eq!(x.as_str(), "+9223372036854775807" ); |
596 | |
597 | let x = DecimalFormatter::new().format(0); |
598 | assert_eq!(x.as_str(), "0" ); |
599 | |
600 | let x = DecimalFormatter::new().force_sign(true).format(0); |
601 | assert_eq!(x.as_str(), "+0" ); |
602 | |
603 | let x = DecimalFormatter::new().force_sign(false).format(0); |
604 | assert_eq!(x.as_str(), "-0" ); |
605 | |
606 | let x = DecimalFormatter::new().padding(4).format(0); |
607 | assert_eq!(x.as_str(), "0000" ); |
608 | |
609 | let x = DecimalFormatter::new().padding(4).format(789); |
610 | assert_eq!(x.as_str(), "0789" ); |
611 | |
612 | let x = DecimalFormatter::new().padding(4).format(-789); |
613 | assert_eq!(x.as_str(), "-0789" ); |
614 | |
615 | let x = |
616 | DecimalFormatter::new().force_sign(true).padding(4).format(789); |
617 | assert_eq!(x.as_str(), "+0789" ); |
618 | } |
619 | |
620 | #[test ] |
621 | fn fractional_auto() { |
622 | let f = |n| FractionalFormatter::new().format(n).as_str().to_string(); |
623 | |
624 | assert_eq!(f(0), "" ); |
625 | assert_eq!(f(123_000_000), "123" ); |
626 | assert_eq!(f(123_456_000), "123456" ); |
627 | assert_eq!(f(123_456_789), "123456789" ); |
628 | assert_eq!(f(456_789), "000456789" ); |
629 | assert_eq!(f(789), "000000789" ); |
630 | } |
631 | |
632 | #[test ] |
633 | fn fractional_precision() { |
634 | let f = |precision, n| { |
635 | FractionalFormatter::new() |
636 | .precision(Some(precision)) |
637 | .format(n) |
638 | .as_str() |
639 | .to_string() |
640 | }; |
641 | |
642 | assert_eq!(f(0, 0), "" ); |
643 | assert_eq!(f(1, 0), "0" ); |
644 | assert_eq!(f(9, 0), "000000000" ); |
645 | |
646 | assert_eq!(f(3, 123_000_000), "123" ); |
647 | assert_eq!(f(6, 123_000_000), "123000" ); |
648 | assert_eq!(f(9, 123_000_000), "123000000" ); |
649 | |
650 | assert_eq!(f(3, 123_456_000), "123" ); |
651 | assert_eq!(f(6, 123_456_000), "123456" ); |
652 | assert_eq!(f(9, 123_456_000), "123456000" ); |
653 | |
654 | assert_eq!(f(3, 123_456_789), "123" ); |
655 | assert_eq!(f(6, 123_456_789), "123456" ); |
656 | assert_eq!(f(9, 123_456_789), "123456789" ); |
657 | |
658 | // We use truncation, no rounding. |
659 | assert_eq!(f(2, 889_000_000), "88" ); |
660 | assert_eq!(f(2, 999_000_000), "99" ); |
661 | } |
662 | } |
663 | |