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 11a8ed9a4 Parse timestamps with leap seconds (#3861) (#3862)
11a8ed9a4 is described below

commit 11a8ed9a49a9311c0539d584e08c1a7515b1cac3
Author: Raphael Taylor-Davies <[email protected]>
AuthorDate: Wed Mar 15 15:19:17 2023 +0000

    Parse timestamps with leap seconds (#3861) (#3862)
    
    * Parse timestamps with leap seconds (#3861)
    
    * Handle lower case timestamp separator (#3863)
---
 arrow-cast/src/parse.rs | 26 +++++++++++++++++++++-----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/arrow-cast/src/parse.rs b/arrow-cast/src/parse.rs
index 36bc8777c..23c2642e7 100644
--- a/arrow-cast/src/parse.rs
+++ b/arrow-cast/src/parse.rs
@@ -89,13 +89,21 @@ impl TimestampParser {
     ///
     /// Returning the end byte offset
     fn time(&self) -> Option<(NaiveTime, usize)> {
+        // Make a NaiveTime handling leap seconds
+        let time = |hour, min, sec, nano| match sec {
+            60 => {
+                let nano = 1_000_000_000 + nano;
+                NaiveTime::from_hms_nano_opt(hour as _, min as _, 59, nano)
+            }
+            _ => NaiveTime::from_hms_nano_opt(hour as _, min as _, sec as _, 
nano),
+        };
+
         match (self.mask >> 11) & 0b11111111 {
             // 09:26:56
             0b11011011 if self.test(13, b':') && self.test(16, b':') => {
                 let hour = self.digits[11] * 10 + self.digits[12];
                 let minute = self.digits[14] * 10 + self.digits[15];
                 let second = self.digits[17] * 10 + self.digits[18];
-                let time = NaiveTime::from_hms_opt(hour as _, minute as _, 
second as _)?;
 
                 match self.test(19, b'.') {
                     true => {
@@ -112,9 +120,9 @@ impl TimestampParser {
                             8 => parse_nanos::<8>(&self.digits[20..28]),
                             _ => parse_nanos::<9>(&self.digits[20..29]),
                         };
-                        Some((time.with_nanosecond(nanos)?, 20 + digits as 
usize))
+                        Some((time(hour, minute, second, nanos)?, 20 + digits 
as usize))
                     }
-                    false => Some((time, 19)),
+                    false => Some((time(hour, minute, second, 0)?, 19)),
                 }
             }
             // 092656
@@ -122,7 +130,7 @@ impl TimestampParser {
                 let hour = self.digits[11] * 10 + self.digits[12];
                 let minute = self.digits[13] * 10 + self.digits[14];
                 let second = self.digits[15] * 10 + self.digits[16];
-                let time = NaiveTime::from_hms_opt(hour as _, minute as _, 
second as _)?;
+                let time = time(hour, minute, second, 0)?;
                 Some((time, 17))
             }
             _ => None,
@@ -188,7 +196,7 @@ pub fn string_to_datetime<T: TimeZone>(
         return Ok(DateTime::from_local(date.and_time(time), offset));
     }
 
-    if !parser.test(10, b'T') && !parser.test(10, b' ') {
+    if !parser.test(10, b'T') && !parser.test(10, b't') && !parser.test(10, b' 
') {
         return Err(err("invalid timestamp separator"));
     }
 
@@ -1009,6 +1017,14 @@ mod tests {
             "2020-09-08T12:00:12.123456789123+02:00",
             "2020-09-08T12:00:12.12345678912345Z",
             "2020-09-08T12:00:12.1234567891234567+02:00",
+            "2020-09-08T12:00:60Z",
+            "2020-09-08T12:00:60.123Z",
+            "2020-09-08T12:00:60.123456+02:00",
+            "2020-09-08T12:00:60.1234567891234567+02:00",
+            "2020-09-08T12:00:60.999999999+02:00",
+            "2020-09-08t12:00:12.12345678+00:00",
+            "2020-09-08t12:00:12+00:00",
+            "2020-09-08t12:00:12Z",
         ];
 
         for case in cases {

Reply via email to