|
1 |
| -use crate::numbers::{float_parse_bytes, IntFloat}; |
2 |
| -use crate::TimeConfigBuilder; |
| 1 | +use crate::date::MS_WATERSHED; |
| 2 | +use crate::{int_parse_bytes, MicrosecondsPrecisionOverflowBehavior, TimeConfigBuilder}; |
3 | 3 | use crate::{time::TimeConfig, Date, ParseError, Time};
|
4 | 4 | use std::cmp::Ordering;
|
5 | 5 | use std::fmt;
|
@@ -339,14 +339,50 @@ impl DateTime {
|
339 | 339 | pub fn parse_bytes_with_config(bytes: &[u8], config: &TimeConfig) -> Result<Self, ParseError> {
|
340 | 340 | match Self::parse_bytes_rfc3339_with_config(bytes, config) {
|
341 | 341 | Ok(d) => Ok(d),
|
342 |
| - Err(e) => match float_parse_bytes(bytes) { |
343 |
| - IntFloat::Int(int) => Self::from_timestamp_with_config(int, 0, config), |
344 |
| - IntFloat::Float(float) => { |
345 |
| - let micro = (float.fract() * 1_000_000_f64).round() as u32; |
346 |
| - Self::from_timestamp_with_config(float.floor() as i64, micro, config) |
| 342 | + Err(e) => { |
| 343 | + let mut split = bytes.splitn(2, |&b| b == b'.'); |
| 344 | + let Some(timestamp) = |
| 345 | + int_parse_bytes(split.next().expect("splitn always returns at least one element")) |
| 346 | + else { |
| 347 | + return Err(e); |
| 348 | + }; |
| 349 | + let float_fraction = split.next(); |
| 350 | + debug_assert!(split.next().is_none()); // at most two elements |
| 351 | + match float_fraction { |
| 352 | + // If fraction exists but is empty (i.e. trailing `.`), allow for backwards compatibility; |
| 353 | + // TODO might want to reconsider this later? |
| 354 | + Some(b"") | None => Self::from_timestamp_with_config(timestamp, 0, config), |
| 355 | + Some(fract) => { |
| 356 | + // fraction is either: |
| 357 | + // - up to 3 digits of millisecond fractions, i.e. microseconds |
| 358 | + // - or up to 6 digits of second fractions, i.e. milliseconds |
| 359 | + let max_digits = if timestamp > MS_WATERSHED { 3 } else { 6 }; |
| 360 | + let Some(fract_integers) = int_parse_bytes(fract) else { |
| 361 | + return Err(e); |
| 362 | + }; |
| 363 | + if config.microseconds_precision_overflow_behavior |
| 364 | + == MicrosecondsPrecisionOverflowBehavior::Error |
| 365 | + && fract.len() > max_digits |
| 366 | + { |
| 367 | + return Err(if timestamp > MS_WATERSHED { |
| 368 | + ParseError::MillisecondFractionTooLong |
| 369 | + } else { |
| 370 | + ParseError::SecondFractionTooLong |
| 371 | + }); |
| 372 | + } |
| 373 | + // TODO: Technically this is rounding, but this is what the existing |
| 374 | + // behaviour already did. Probably this is always better than "truncating" |
| 375 | + // so we might want to change MicrosecondsPrecisionOverflowBehavior and |
| 376 | + // make other uses also round / deprecate truncating. |
| 377 | + let multiple = 10f64.powf(max_digits as f64 - fract.len() as f64); |
| 378 | + Self::from_timestamp_with_config( |
| 379 | + timestamp, |
| 380 | + (fract_integers as f64 * multiple).round() as u32, |
| 381 | + config, |
| 382 | + ) |
| 383 | + } |
347 | 384 | }
|
348 |
| - IntFloat::Err => Err(e), |
349 |
| - }, |
| 385 | + } |
350 | 386 | }
|
351 | 387 | }
|
352 | 388 |
|
|
0 commit comments