You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by tu...@apache.org on 2023/06/26 11:16:06 UTC

[arrow-rs] branch master updated: Parse intervals like `.5` the same as `0.5` (#4425)

This is an automated email from the ASF dual-hosted git repository.

tustvold pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/master by this push:
     new 23465ec49 Parse intervals like `.5` the same as `0.5` (#4425)
23465ec49 is described below

commit 23465ec49962eba398394fdc2a2ee2baa11cc1e4
Author: Andrew Lamb <an...@nerdnetworks.org>
AuthorDate: Mon Jun 26 07:16:00 2023 -0400

    Parse intervals like `.5` the same as `0.5` (#4425)
    
    * Allow intervals like .5
    
    * Support -.5 and -0.5 intervals as well
---
 arrow-cast/src/parse.rs | 51 +++++++++++++++++++++++++++++++++++++------------
 1 file changed, 39 insertions(+), 12 deletions(-)

diff --git a/arrow-cast/src/parse.rs b/arrow-cast/src/parse.rs
index accce99b4..67477c57d 100644
--- a/arrow-cast/src/parse.rs
+++ b/arrow-cast/src/parse.rs
@@ -830,15 +830,21 @@ impl FromStr for IntervalAmount {
         match s.split_once('.') {
             Some((integer, frac))
                 if frac.len() <= INTERVAL_PRECISION as usize
-                    && !integer.is_empty()
                     && !frac.is_empty()
                     && !frac.starts_with('-') =>
             {
-                let integer = integer.parse::<i64>().map_err(|_| {
-                    ArrowError::ParseError(format!(
-                        "Failed to parse {s} as interval amount"
-                    ))
-                })?;
+                // integer will be "" for values like ".5"
+                // and "-" for values like "-.5"
+                let explicit_neg = integer.starts_with('-');
+                let integer = if integer.is_empty() || integer == "-" {
+                    Ok(0)
+                } else {
+                    integer.parse::<i64>().map_err(|_| {
+                        ArrowError::ParseError(format!(
+                            "Failed to parse {s} as interval amount"
+                        ))
+                    })
+                }?;
 
                 let frac_unscaled = frac.parse::<i64>().map_err(|_| {
                     ArrowError::ParseError(format!(
@@ -851,7 +857,11 @@ impl FromStr for IntervalAmount {
                     frac_unscaled * 10_i64.pow(INTERVAL_PRECISION - frac.len() as u32);
 
                 // propagate the sign of the integer part to the fractional part
-                let frac = if integer < 0 { -frac } else { frac };
+                let frac = if integer < 0 || explicit_neg {
+                    -frac
+                } else {
+                    frac
+                };
 
                 let result = Self { integer, frac };
 
@@ -929,7 +939,8 @@ impl Interval {
         (self.months, self.days, self.nanos)
     }
 
-    /// Parse string value in traditional Postgres format (e.g. 1 year 2 months 3 days 4 hours 5 minutes 6 seconds)
+    /// Parse string value in traditional Postgres format such as
+    /// `1 year 2 months 3 days 4 hours 5 minutes 6 seconds`
     fn parse(value: &str, config: &IntervalParseConfig) -> Result<Self, ArrowError> {
         let components = parse_interval_components(value, config)?;
 
@@ -1798,6 +1809,26 @@ mod tests {
             Interval::parse("-1.5 months -3.2 days", &config).unwrap(),
         );
 
+        assert_eq!(
+            Interval::new(0i32, 15i32, 0),
+            Interval::parse("0.5 months", &config).unwrap(),
+        );
+
+        assert_eq!(
+            Interval::new(0i32, 15i32, 0),
+            Interval::parse(".5 months", &config).unwrap(),
+        );
+
+        assert_eq!(
+            Interval::new(0i32, -15i32, 0),
+            Interval::parse("-0.5 months", &config).unwrap(),
+        );
+
+        assert_eq!(
+            Interval::new(0i32, -15i32, 0),
+            Interval::parse("-.5 months", &config).unwrap(),
+        );
+
         assert_eq!(
             Interval::new(2i32, 10i32, 9 * NANOS_PER_HOUR),
             Interval::parse("2.1 months 7.25 days 3 hours", &config).unwrap(),
@@ -1944,10 +1975,6 @@ mod tests {
 
         assert_eq!(result, expected);
 
-        // invalid: missing integer
-        let result = IntervalAmount::from_str(".5");
-        assert!(result.is_err());
-
         // invalid: missing fractional
         let result = IntervalAmount::from_str("3.");
         assert!(result.is_err());