| 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 | |