|
1 | 1 | use crate::date::MS_WATERSHED; |
2 | | -use crate::{int_parse_bytes, MicrosecondsPrecisionOverflowBehavior, TimeConfigBuilder}; |
| 2 | +use crate::{ |
| 3 | + float_parse_bytes, numbers::decimal_digits, IntFloat, MicrosecondsPrecisionOverflowBehavior, TimeConfigBuilder, |
| 4 | +}; |
3 | 5 | use crate::{time::TimeConfig, Date, ParseError, Time}; |
4 | 6 | use std::cmp::Ordering; |
5 | 7 | use std::fmt; |
@@ -339,50 +341,41 @@ impl DateTime { |
339 | 341 | pub fn parse_bytes_with_config(bytes: &[u8], config: &TimeConfig) -> Result<Self, ParseError> { |
340 | 342 | match Self::parse_bytes_rfc3339_with_config(bytes, config) { |
341 | 343 | Ok(d) => Ok(d), |
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 | | - }); |
| 344 | + Err(e) => match float_parse_bytes(bytes) { |
| 345 | + IntFloat::Int(int) => Self::from_timestamp_with_config(int, 0, config), |
| 346 | + IntFloat::Float(float) => { |
| 347 | + let timestamp_in_milliseconds = float.abs() > MS_WATERSHED as f64; |
| 348 | + |
| 349 | + if config.microseconds_precision_overflow_behavior == MicrosecondsPrecisionOverflowBehavior::Error { |
| 350 | + let decimal_digits_count = decimal_digits(bytes); |
| 351 | + |
| 352 | + // If the number of decimal digits exceeds the maximum allowed for the timestamp precision, |
| 353 | + // return an error. For timestamps in milliseconds, the maximum is 3, for timestamps in seconds, |
| 354 | + // the maximum is 6. These end up being the same in terms of allowing microsecond precision. |
| 355 | + if timestamp_in_milliseconds && decimal_digits_count > 3 { |
| 356 | + return Err(ParseError::MillisecondFractionTooLong); |
| 357 | + } else if !timestamp_in_milliseconds && decimal_digits_count > 6 { |
| 358 | + return Err(ParseError::SecondFractionTooLong); |
372 | 359 | } |
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 | 360 | } |
| 361 | + |
| 362 | + let timestamp_normalized: f64 = if timestamp_in_milliseconds { |
| 363 | + float / 1_000f64 |
| 364 | + } else { |
| 365 | + float |
| 366 | + }; |
| 367 | + |
| 368 | + // if seconds is negative, we round down (left on the number line), so -6.25 -> -7 |
| 369 | + // which allows for a positive number of microseconds to compensate back up to -6.25 |
| 370 | + // which is the equivalent of doing (seconds - 1) and (microseconds + 1_000_000) |
| 371 | + // like we do in Date::timestamp_watershed |
| 372 | + let seconds = timestamp_normalized.floor() as i64; |
| 373 | + let microseconds = ((timestamp_normalized - seconds as f64) * 1_000_000f64).round() as u32; |
| 374 | + |
| 375 | + Self::from_timestamp_with_config(seconds, microseconds, config) |
384 | 376 | } |
385 | | - } |
| 377 | + IntFloat::Err => Err(e), |
| 378 | + }, |
386 | 379 | } |
387 | 380 | } |
388 | 381 |
|
|
0 commit comments