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

kou pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-swift.git


The following commit(s) were added to refs/heads/main by this push:
     new 476d1c0  feat: Add support for Timestamp data type (#33)
476d1c0 is described below

commit 476d1c0a44a460c0c50357618e63c89240088e9c
Author: Marco Antonio <[email protected]>
AuthorDate: Mon Jun 16 18:44:32 2025 -0300

    feat: Add support for Timestamp data type (#33)
    
    ### Rationale for this change
    
    Currently, the Swift implementation of Arrow does not support Timestamp,
    although they are available in the base C interface. This PR attempts to
    add its support by following the current implemented design pattern.
    
    ### What changes are included in this PR?
    
    1. `TimestampArray` with some basic formatting utilities
    2. `TimestampArrayBuilder`
    3. `Timestamp` alias
    4. `ArrowTimestampUnit`, which includes extensively all the variants
    (seconds, milliseconds, microseconds and nanoseconds)
    5. `ArrowTypeTimestamp` from base `Arrow`
    6. `ArrowType` support for timestamp
    7. `ArrowWriterHelper` support for timestamp
    8. `fromProto` support for timestamp
    
    It properly handles the presence or absence of `timezone`.
    
    ### Are these changes tested?
    
    Tests are included in both `ArrayTests.swift` and `CDataTests.swift`.
    
    ### Are there any user-facing changes?
    
    Yes - users can now work with Timestamp data types in Swift Arrow
    implementations. This is additive and doesn't break existing
    functionality.
    
    Closes #32.
    
    ---------
    
    Co-authored-by: Marco <[email protected]>
    Co-authored-by: Sutou Kouhei <[email protected]>
---
 Arrow/Sources/Arrow/ArrowArray.swift        | 80 +++++++++++++++++++++++++++++
 Arrow/Sources/Arrow/ArrowArrayBuilder.swift | 15 ++++++
 Arrow/Sources/Arrow/ArrowReaderHelper.swift | 38 +++++++++++++-
 Arrow/Sources/Arrow/ArrowType.swift         | 69 +++++++++++++++++++++++++
 Arrow/Sources/Arrow/ArrowWriterHelper.swift | 28 ++++++++++
 Arrow/Sources/Arrow/ProtoUtil.swift         | 16 ++++++
 Arrow/Tests/ArrowTests/ArrayTests.swift     | 70 +++++++++++++++++++++++++
 Arrow/Tests/ArrowTests/CDataTests.swift     |  6 ++-
 8 files changed, 320 insertions(+), 2 deletions(-)

diff --git a/Arrow/Sources/Arrow/ArrowArray.swift 
b/Arrow/Sources/Arrow/ArrowArray.swift
index 4fc1b8b..d4ee873 100644
--- a/Arrow/Sources/Arrow/ArrowArray.swift
+++ b/Arrow/Sources/Arrow/ArrowArray.swift
@@ -105,6 +105,8 @@ public class ArrowArrayHolderImpl: ArrowArrayHolder {
             return try ArrowArrayHolderImpl(Time32Array(with))
         case .time64:
             return try ArrowArrayHolderImpl(Time64Array(with))
+        case .timestamp:
+            return try ArrowArrayHolderImpl(TimestampArray(with))
         case .string:
             return try ArrowArrayHolderImpl(StringArray(with))
         case .boolean:
@@ -233,6 +235,84 @@ public class Date64Array: ArrowArray<Date> {
 public class Time32Array: FixedArray<Time32> {}
 public class Time64Array: FixedArray<Time64> {}
 
+public class TimestampArray: FixedArray<Timestamp> {
+
+    public struct FormattingOptions: Equatable {
+        public var dateFormat: String = "yyyy-MM-dd HH:mm:ss.SSS"
+        public var locale: Locale = .current
+        public var includeTimezone: Bool = true
+        public var fallbackToRaw: Bool = true
+
+        public init(dateFormat: String = "yyyy-MM-dd HH:mm:ss.SSS",
+                    locale: Locale = .current,
+                    includeTimezone: Bool = true,
+                    fallbackToRaw: Bool = true) {
+            self.dateFormat = dateFormat
+            self.locale = locale
+            self.includeTimezone = includeTimezone
+            self.fallbackToRaw = fallbackToRaw
+        }
+
+        public static func == (lhs: FormattingOptions, rhs: FormattingOptions) 
-> Bool {
+            return lhs.dateFormat == rhs.dateFormat &&
+                lhs.locale.identifier == rhs.locale.identifier &&
+                lhs.includeTimezone == rhs.includeTimezone &&
+                lhs.fallbackToRaw == rhs.fallbackToRaw
+        }
+    }
+
+    private var cachedFormatter: DateFormatter?
+    private var cachedOptions: FormattingOptions?
+
+    public func formattedDate(at index: UInt, options: FormattingOptions = 
FormattingOptions()) -> String? {
+        guard let timestamp = self[index] else { return nil }
+
+        guard let timestampType = self.arrowData.type as? ArrowTypeTimestamp 
else {
+            return options.fallbackToRaw ? "\(timestamp)" : nil
+        }
+
+        let date = dateFromTimestamp(timestamp, unit: timestampType.unit)
+
+        if cachedFormatter == nil || cachedOptions != options {
+            let formatter = DateFormatter()
+            formatter.dateFormat = options.dateFormat
+            formatter.locale = options.locale
+            if options.includeTimezone, let timezone = timestampType.timezone {
+                formatter.timeZone = TimeZone(identifier: timezone)
+            }
+            cachedFormatter = formatter
+            cachedOptions = options
+        }
+
+        return cachedFormatter?.string(from: date)
+    }
+
+    private func dateFromTimestamp(_ timestamp: Int64, unit: 
ArrowTimestampUnit) -> Date {
+        let timeInterval: TimeInterval
+
+        switch unit {
+        case .seconds:
+            timeInterval = TimeInterval(timestamp)
+        case .milliseconds:
+            timeInterval = TimeInterval(timestamp) / 1_000
+        case .microseconds:
+            timeInterval = TimeInterval(timestamp) / 1_000_000
+        case .nanoseconds:
+            timeInterval = TimeInterval(timestamp) / 1_000_000_000
+        }
+
+        return Date(timeIntervalSince1970: timeInterval)
+    }
+
+    public override func asString(_ index: UInt) -> String {
+        if let formatted = formattedDate(at: index) {
+            return formatted
+        }
+
+        return super.asString(index)
+    }
+}
+
 public class BinaryArray: ArrowArray<Data> {
     public struct Options {
         public var printAsHex = false
diff --git a/Arrow/Sources/Arrow/ArrowArrayBuilder.swift 
b/Arrow/Sources/Arrow/ArrowArrayBuilder.swift
index 005cad7..493e43a 100644
--- a/Arrow/Sources/Arrow/ArrowArrayBuilder.swift
+++ b/Arrow/Sources/Arrow/ArrowArrayBuilder.swift
@@ -119,6 +119,12 @@ public class Time64ArrayBuilder: 
ArrowArrayBuilder<FixedBufferBuilder<Time64>, T
     }
 }
 
+public class TimestampArrayBuilder: 
ArrowArrayBuilder<FixedBufferBuilder<Int64>, TimestampArray> {
+    fileprivate convenience init(_ unit: ArrowTimestampUnit, timezone: String? 
= nil) throws {
+        try self.init(ArrowTypeTimestamp(unit, timezone: timezone))
+    }
+}
+
 public class StructArrayBuilder: ArrowArrayBuilder<StructBufferBuilder, 
StructArray> {
     let builders: [any ArrowArrayHolderBuilder]
     let fields: [ArrowField]
@@ -279,6 +285,11 @@ public class ArrowArrayBuilders {
                 throw ArrowError.invalid("Expected arrow type for 
\(arrowType.id) not found")
             }
             return try Time64ArrayBuilder(timeType.unit)
+        case .timestamp:
+            guard let timestampType = arrowType as? ArrowTypeTimestamp else {
+                throw ArrowError.invalid("Expected arrow type for 
\(arrowType.id) not found")
+            }
+            return try TimestampArrayBuilder(timestampType.unit)
         default:
             throw ArrowError.unknownType("Builder not found for arrow type: 
\(arrowType.id)")
         }
@@ -338,4 +349,8 @@ public class ArrowArrayBuilders {
     public static func loadTime64ArrayBuilder(_ unit: ArrowTime64Unit) throws 
-> Time64ArrayBuilder {
         return try Time64ArrayBuilder(unit)
     }
+
+    public static func loadTimestampArrayBuilder(_ unit: ArrowTimestampUnit, 
timezone: String? = nil) throws -> TimestampArrayBuilder {
+        return try TimestampArrayBuilder(unit, timezone: timezone)
+    }
 }
diff --git a/Arrow/Sources/Arrow/ArrowReaderHelper.swift 
b/Arrow/Sources/Arrow/ArrowReaderHelper.swift
index 78ad280..37f4680 100644
--- a/Arrow/Sources/Arrow/ArrowReaderHelper.swift
+++ b/Arrow/Sources/Arrow/ArrowReaderHelper.swift
@@ -90,6 +90,24 @@ private func makeTimeHolder(_ field: ArrowField,
     }
 }
 
+private func makeTimestampHolder(_ field: ArrowField,
+                                 buffers: [ArrowBuffer],
+                                 nullCount: UInt
+) -> Result<ArrowArrayHolder, ArrowError> {
+    do {
+        if let arrowType = field.type as? ArrowTypeTimestamp {
+            let arrowData = try ArrowData(arrowType, buffers: buffers, 
nullCount: nullCount)
+            return .success(ArrowArrayHolderImpl(try 
TimestampArray(arrowData)))
+        } else {
+            return .failure(.invalid("Incorrect field type for timestamp: 
\(field.type)"))
+        }
+    } catch let error as ArrowError {
+        return .failure(error)
+    } catch {
+        return .failure(.unknownError("\(error)"))
+    }
+}
+
 private func makeBoolHolder(_ buffers: [ArrowBuffer],
                             nullCount: UInt) -> Result<ArrowArrayHolder, 
ArrowError> {
     do {
@@ -186,6 +204,8 @@ func makeArrayHolder( // swiftlint:disable:this 
cyclomatic_complexity
         return makeDateHolder(field, buffers: buffers, nullCount: nullCount)
     case .time32, .time64:
         return makeTimeHolder(field, buffers: buffers, nullCount: nullCount)
+    case .timestamp:
+        return makeTimestampHolder(field, buffers: buffers, nullCount: 
nullCount)
     case .strct:
         return makeStructHolder(field, buffers: buffers, nullCount: nullCount, 
children: children!, rbLength: rbLength)
     default:
@@ -203,7 +223,7 @@ func makeBuffer(_ buffer: org_apache_arrow_flatbuf_Buffer, 
fileData: Data,
 
 func isFixedPrimitive(_ type: org_apache_arrow_flatbuf_Type_) -> Bool {
     switch type {
-    case .int, .bool, .floatingpoint, .date, .time:
+    case .int, .bool, .floatingpoint, .date, .time, .timestamp:
         return true
     default:
         return false
@@ -261,6 +281,22 @@ func findArrowType( // swiftlint:disable:this 
cyclomatic_complexity function_bod
         }
 
         return ArrowTypeTime64(timeType.unit == .microsecond ? .microseconds : 
.nanoseconds)
+    case .timestamp:
+        let timestampType = field.type(type: 
org_apache_arrow_flatbuf_Timestamp.self)!
+        let arrowUnit: ArrowTimestampUnit
+        switch timestampType.unit {
+        case .second:
+            arrowUnit = .seconds
+        case .millisecond:
+            arrowUnit = .milliseconds
+        case .microsecond:
+            arrowUnit = .microseconds
+        case .nanosecond:
+            arrowUnit = .nanoseconds
+        }
+
+        let timezone = timestampType.timezone
+        return ArrowTypeTimestamp(arrowUnit, timezone: timezone)
     case .struct_:
         _ = field.type(type: org_apache_arrow_flatbuf_Struct_.self)!
         var fields = [ArrowField]()
diff --git a/Arrow/Sources/Arrow/ArrowType.swift 
b/Arrow/Sources/Arrow/ArrowType.swift
index b44f859..9b6091f 100644
--- a/Arrow/Sources/Arrow/ArrowType.swift
+++ b/Arrow/Sources/Arrow/ArrowType.swift
@@ -21,6 +21,7 @@ public typealias Time32 = Int32
 public typealias Time64 = Int64
 public typealias Date32 = Int32
 public typealias Date64 = Int64
+public typealias Timestamp = Int64
 
 func FlatBuffersVersion_23_1_4() { // swiftlint:disable:this identifier_name
 }
@@ -65,6 +66,7 @@ public enum ArrowTypeId {
     case strct
     case time32
     case time64
+    case timestamp
     case time
     case uint16
     case uint32
@@ -122,6 +124,47 @@ public class ArrowTypeTime64: ArrowType {
     }
 }
 
+public enum ArrowTimestampUnit {
+    case seconds
+    case milliseconds
+    case microseconds
+    case nanoseconds
+}
+
+public class ArrowTypeTimestamp: ArrowType {
+    let unit: ArrowTimestampUnit
+    let timezone: String?
+
+    public init(_ unit: ArrowTimestampUnit, timezone: String? = nil) {
+        self.unit = unit
+        self.timezone = timezone
+
+        super.init(ArrowType.ArrowTimestamp)
+    }
+
+    public convenience init(type: ArrowTypeId) {
+        self.init(.milliseconds, timezone: nil)
+    }
+
+    public override var cDataFormatId: String {
+        get throws {
+            let unitChar: String
+            switch self.unit {
+            case .seconds: unitChar = "s"
+            case .milliseconds: unitChar = "m"
+            case .microseconds: unitChar = "u"
+            case .nanoseconds: unitChar = "n"
+            }
+
+            if let timezone = self.timezone {
+                return "ts\(unitChar):\(timezone)"
+            } else {
+                return "ts\(unitChar)"
+            }
+        }
+    }
+}
+
 public class ArrowNestedType: ArrowType {
     let fields: [ArrowField]
     public init(_ info: ArrowType.Info, fields: [ArrowField]) {
@@ -150,6 +193,7 @@ public class ArrowType {
     public static let ArrowBinary = Info.variableInfo(ArrowTypeId.binary)
     public static let ArrowTime32 = Info.timeInfo(ArrowTypeId.time32)
     public static let ArrowTime64 = Info.timeInfo(ArrowTypeId.time64)
+    public static let ArrowTimestamp = Info.timeInfo(ArrowTypeId.timestamp)
     public static let ArrowStruct = Info.complexInfo(ArrowTypeId.strct)
 
     public init(_ info: ArrowType.Info) {
@@ -270,6 +314,8 @@ public class ArrowType {
             return MemoryLayout<Time32>.stride
         case .time64:
             return MemoryLayout<Time64>.stride
+        case .timestamp:
+            return MemoryLayout<Timestamp>.stride
         case .binary:
             return MemoryLayout<Int8>.stride
         case .string:
@@ -320,6 +366,11 @@ public class ArrowType {
                     return try time64.cDataFormatId
                 }
                 return "ttu"
+            case ArrowTypeId.timestamp:
+                if let timestamp = self as? ArrowTypeTimestamp {
+                    return try timestamp.cDataFormatId
+                }
+                return "tsu"
             case ArrowTypeId.binary:
                 return "z"
             case ArrowTypeId.string:
@@ -366,6 +417,24 @@ public class ArrowType {
             return ArrowTypeTime64(.microseconds)
         } else if  from == "ttn" {
             return ArrowTypeTime64(.nanoseconds)
+        } else if from.starts(with: "ts") {
+            let components = from.split(separator: ":", maxSplits: 1)
+            guard let unitPart = components.first, unitPart.count == 3 else {
+                throw ArrowError.invalid("Invalid timestamp format '\(from)'. 
Expected format 'ts[s|m|u|n][:timezone]'")
+            }
+
+            let unitChar = unitPart.suffix(1)
+            let unit: ArrowTimestampUnit
+            switch unitChar {
+            case "s": unit = .seconds
+            case "m": unit = .milliseconds
+            case "u": unit = .microseconds
+            case "n": unit = .nanoseconds
+            default: throw ArrowError.invalid("Unrecognized timestamp unit 
'\(unitChar)'. Expected 's', 'm', 'u', or 'n'.")
+            }
+
+            let timezone = components.count > 1 ? String(components[1]) : nil
+            return ArrowTypeTimestamp(unit, timezone: timezone)
         } else if  from == "z" {
             return ArrowType(ArrowType.ArrowBinary)
         } else if  from == "u" {
diff --git a/Arrow/Sources/Arrow/ArrowWriterHelper.swift 
b/Arrow/Sources/Arrow/ArrowWriterHelper.swift
index b3fa2b4..7ecb3ab 100644
--- a/Arrow/Sources/Arrow/ArrowWriterHelper.swift
+++ b/Arrow/Sources/Arrow/ArrowWriterHelper.swift
@@ -41,6 +41,8 @@ func toFBTypeEnum(_ arrowType: ArrowType) -> 
Result<org_apache_arrow_flatbuf_Typ
         return .success(org_apache_arrow_flatbuf_Type_.date)
     case .time32, .time64:
         return .success(org_apache_arrow_flatbuf_Type_.time)
+    case .timestamp:
+        return .success(org_apache_arrow_flatbuf_Type_.timestamp)
     case .strct:
         return .success(org_apache_arrow_flatbuf_Type_.struct_)
     default:
@@ -103,6 +105,32 @@ func toFBType( // swiftlint:disable:this 
cyclomatic_complexity function_body_len
         }
 
         return .failure(.invalid("Unable to case to Time64"))
+    case .timestamp:
+        if let timestampType = arrowType as? ArrowTypeTimestamp {
+            let startOffset = 
org_apache_arrow_flatbuf_Timestamp.startTimestamp(&fbb)
+
+            let fbUnit: org_apache_arrow_flatbuf_TimeUnit
+            switch timestampType.unit {
+            case .seconds:
+                fbUnit = .second
+            case .milliseconds:
+                fbUnit = .millisecond
+            case .microseconds:
+                fbUnit = .microsecond
+            case .nanoseconds:
+                fbUnit = .nanosecond
+            }
+            org_apache_arrow_flatbuf_Timestamp.add(unit: fbUnit, &fbb)
+
+            if let timezone = timestampType.timezone {
+                let timezoneOffset = fbb.create(string: timezone)
+                org_apache_arrow_flatbuf_Timestamp.add(timezone: 
timezoneOffset, &fbb)
+            }
+
+            return 
.success(org_apache_arrow_flatbuf_Timestamp.endTimestamp(&fbb, start: 
startOffset))
+        }
+
+        return .failure(.invalid("Unable to cast to Timestamp"))
     case .strct:
         let startOffset = org_apache_arrow_flatbuf_Struct_.startStruct_(&fbb)
         return .success(org_apache_arrow_flatbuf_Struct_.endStruct_(&fbb, 
start: startOffset))
diff --git a/Arrow/Sources/Arrow/ProtoUtil.swift 
b/Arrow/Sources/Arrow/ProtoUtil.swift
index 88cfb0b..e91580e 100644
--- a/Arrow/Sources/Arrow/ProtoUtil.swift
+++ b/Arrow/Sources/Arrow/ProtoUtil.swift
@@ -64,6 +64,22 @@ func fromProto( // swiftlint:disable:this 
cyclomatic_complexity function_body_le
             let arrowUnit: ArrowTime64Unit = timeType.unit == .microsecond ? 
.microseconds : .nanoseconds
             arrowType = ArrowTypeTime64(arrowUnit)
         }
+    case .timestamp:
+        let timestampType = field.type(type: 
org_apache_arrow_flatbuf_Timestamp.self)!
+        let arrowUnit: ArrowTimestampUnit
+        switch timestampType.unit {
+        case .second:
+            arrowUnit = .seconds
+        case .millisecond:
+            arrowUnit = .milliseconds
+        case .microsecond:
+            arrowUnit = .microseconds
+        case .nanosecond:
+            arrowUnit = .nanoseconds
+        }
+
+        let timezone = timestampType.timezone
+        arrowType = ArrowTypeTimestamp(arrowUnit, timezone: timezone?.isEmpty 
== true ? nil : timezone)
     case .struct_:
         var children = [ArrowField]()
         for index in 0..<field.childrenCount {
diff --git a/Arrow/Tests/ArrowTests/ArrayTests.swift 
b/Arrow/Tests/ArrowTests/ArrayTests.swift
index d0d7064..c7142c5 100644
--- a/Arrow/Tests/ArrowTests/ArrayTests.swift
+++ b/Arrow/Tests/ArrowTests/ArrayTests.swift
@@ -212,6 +212,76 @@ final class ArrayTests: XCTestCase { // 
swiftlint:disable:this type_body_length
         XCTAssertEqual(microArray[2], 987654321)
     }
 
+    func testTimestampArray() throws {
+        // Test timestamp with seconds unit
+        let secBuilder = try 
ArrowArrayBuilders.loadTimestampArrayBuilder(.seconds, timezone: nil)
+        secBuilder.append(1609459200) // 2021-01-01 00:00:00
+        secBuilder.append(1609545600) // 2021-01-02 00:00:00
+        secBuilder.append(nil)
+        XCTAssertEqual(secBuilder.nullCount, 1)
+        XCTAssertEqual(secBuilder.length, 3)
+        XCTAssertEqual(secBuilder.capacity, 264)
+        let secArray = try secBuilder.finish()
+        let secType = secArray.arrowData.type as! ArrowTypeTimestamp // 
swiftlint:disable:this force_cast
+        XCTAssertEqual(secType.unit, .seconds)
+        XCTAssertNil(secType.timezone)
+        XCTAssertEqual(secArray.length, 3)
+        XCTAssertEqual(secArray[0], 1609459200)
+        XCTAssertEqual(secArray[1], 1609545600)
+        XCTAssertNil(secArray[2])
+
+        // Test timestamp with milliseconds unit and timezone America/New_York
+        let msBuilder = try 
ArrowArrayBuilders.loadTimestampArrayBuilder(.milliseconds, timezone: 
"America/New_York")
+        msBuilder.append(1609459200000) // 2021-01-01 00:00:00.000
+        msBuilder.append(nil)
+        msBuilder.append(1609545600000) // 2021-01-02 00:00:00.000
+        XCTAssertEqual(msBuilder.nullCount, 1)
+        XCTAssertEqual(msBuilder.length, 3)
+        XCTAssertEqual(msBuilder.capacity, 264)
+        let msArray = try msBuilder.finish()
+        let msType = msArray.arrowData.type as! ArrowTypeTimestamp // 
swiftlint:disable:this force_cast
+        XCTAssertEqual(msType.unit, .milliseconds)
+        XCTAssertEqual(msType.timezone, "America/New_York")
+        XCTAssertEqual(msArray.length, 3)
+        XCTAssertEqual(msArray[0], 1609459200000)
+        XCTAssertNil(msArray[1])
+        XCTAssertEqual(msArray[2], 1609545600000)
+
+        // Test timestamp with microseconds unit and timezone UTC
+        let usBuilder = try 
ArrowArrayBuilders.loadTimestampArrayBuilder(.microseconds, timezone: "UTC")
+        usBuilder.append(1609459200000000) // 2021-01-01 00:00:00.000000
+        usBuilder.append(1609545600000000) // 2021-01-02 00:00:00.000000
+        usBuilder.append(1609632000000000) // 2021-01-03 00:00:00.000000
+        XCTAssertEqual(usBuilder.nullCount, 0)
+        XCTAssertEqual(usBuilder.length, 3)
+        XCTAssertEqual(usBuilder.capacity, 264)
+        let usArray = try usBuilder.finish()
+        let usType = usArray.arrowData.type as! ArrowTypeTimestamp // 
swiftlint:disable:this force_cast
+        XCTAssertEqual(usType.unit, .microseconds)
+        XCTAssertEqual(usType.timezone, "UTC")
+        XCTAssertEqual(usArray.length, 3)
+        XCTAssertEqual(usArray[0], 1609459200000000)
+        XCTAssertEqual(usArray[1], 1609545600000000)
+        XCTAssertEqual(usArray[2], 1609632000000000)
+
+        // Test timestamp with nanoseconds unit
+        let nsBuilder = try 
ArrowArrayBuilders.loadTimestampArrayBuilder(.nanoseconds, timezone: nil)
+        nsBuilder.append(nil)
+        nsBuilder.append(1609459200000000000) // 2021-01-01 00:00:00.000000000
+        nsBuilder.append(1609545600000000000) // 2021-01-02 00:00:00.000000000
+        XCTAssertEqual(nsBuilder.nullCount, 1)
+        XCTAssertEqual(nsBuilder.length, 3)
+        XCTAssertEqual(nsBuilder.capacity, 264)
+        let nsArray = try nsBuilder.finish()
+        let nsType = nsArray.arrowData.type as! ArrowTypeTimestamp // 
swiftlint:disable:this force_cast
+        XCTAssertEqual(nsType.unit, .nanoseconds)
+        XCTAssertNil(nsType.timezone)
+        XCTAssertEqual(nsArray.length, 3)
+        XCTAssertNil(nsArray[0])
+        XCTAssertEqual(nsArray[1], 1609459200000000000)
+        XCTAssertEqual(nsArray[2], 1609545600000000000)
+    }
+
     func testStructArray() throws { // swiftlint:disable:this 
function_body_length
         class StructTest {
             var fieldBool: Bool = false
diff --git a/Arrow/Tests/ArrowTests/CDataTests.swift 
b/Arrow/Tests/ArrowTests/CDataTests.swift
index 2344b23..e48ebd0 100644
--- a/Arrow/Tests/ArrowTests/CDataTests.swift
+++ b/Arrow/Tests/ArrowTests/CDataTests.swift
@@ -43,7 +43,11 @@ final class CDataTests: XCTestCase {
             .addField("colTime64", type: ArrowType(ArrowType.ArrowTime64), 
isNullable: false)
             .addField("colTime64u", type: ArrowTypeTime64(.microseconds), 
isNullable: false)
             .addField("colTime64n", type: ArrowTypeTime64(.nanoseconds), 
isNullable: false)
-            .addField("colTime64", type: ArrowType(ArrowType.ArrowTime64), 
isNullable: false)
+            .addField("colTimestamp", type: 
ArrowType(ArrowType.ArrowTimestamp), isNullable: false)
+            .addField("colTimestampts", type: ArrowTypeTimestamp(.seconds), 
isNullable: false)
+            .addField("colTimestamptm", type: 
ArrowTypeTimestamp(.milliseconds), isNullable: false)
+            .addField("colTimestamptu", type: 
ArrowTypeTimestamp(.microseconds), isNullable: false)
+            .addField("colTimestamptn", type: 
ArrowTypeTimestamp(.nanoseconds), isNullable: false)
             .addField("colFloat", type: ArrowType(ArrowType.ArrowFloat), 
isNullable: false)
             .addField("colDouble", type: ArrowType(ArrowType.ArrowDouble), 
isNullable: false)
             .finish()

Reply via email to