diff --git a/Sources/SwiftASN1/Basic ASN1 Types/GeneralizedTime.swift b/Sources/SwiftASN1/Basic ASN1 Types/GeneralizedTime.swift index 20404bc..b93d0f0 100644 --- a/Sources/SwiftASN1/Basic ASN1 Types/GeneralizedTime.swift +++ b/Sources/SwiftASN1/Basic ASN1 Types/GeneralizedTime.swift @@ -196,3 +196,16 @@ public struct GeneralizedTime: DERImplicitlyTaggable, Hashable, Sendable { } } } + +extension GeneralizedTime: Comparable { + @inlinable + public static func <(lhs: GeneralizedTime, rhs: GeneralizedTime) -> Bool { + if lhs.year < rhs.year { return true } + if lhs.month < rhs.month { return true } + if lhs.day < rhs.day { return true } + if lhs.hours < rhs.hours { return true } + if lhs.minutes < rhs.minutes { return true } + if lhs.seconds < rhs.seconds { return true } + return lhs.fractionalSeconds < rhs.fractionalSeconds + } +} diff --git a/Sources/SwiftASN1/Basic ASN1 Types/UTCTime.swift b/Sources/SwiftASN1/Basic ASN1 Types/UTCTime.swift index 89b0df9..b202476 100644 --- a/Sources/SwiftASN1/Basic ASN1 Types/UTCTime.swift +++ b/Sources/SwiftASN1/Basic ASN1 Types/UTCTime.swift @@ -180,3 +180,15 @@ public struct UTCTime: DERImplicitlyTaggable, Hashable, Sendable { } } } + +extension UTCTime: Comparable { + @inlinable + public static func <(lhs: UTCTime, rhs: UTCTime) -> Bool { + if lhs.year < rhs.year { return true } + if lhs.month < rhs.month { return true } + if lhs.day < rhs.day { return true } + if lhs.hours < rhs.hours { return true } + if lhs.minutes < rhs.minutes { return true } + return lhs.seconds < rhs.seconds + } +} diff --git a/Tests/SwiftASN1Tests/GeneralizedTimeTests.swift b/Tests/SwiftASN1Tests/GeneralizedTimeTests.swift index 61b6aa0..dc0e0a3 100644 --- a/Tests/SwiftASN1Tests/GeneralizedTimeTests.swift +++ b/Tests/SwiftASN1Tests/GeneralizedTimeTests.swift @@ -177,4 +177,55 @@ final class GeneralizedTimeTests: XCTestCase { XCTAssertThrowsError(try GeneralizedTime(derEncoded: invalidBytes)) } + + func testComparisons() throws { + enum ExpectedComparisonResult { + case lessThan + case equal + case greaterThan + } + + let original = try GeneralizedTime(year: 2020, month: 03, day: 03, hours: 03, minutes: 03, seconds: 03, fractionalSeconds: 0.105) + + func modify(_ field: WritableKeyPath, of time: GeneralizedTime, by modifier: Modifiable) -> GeneralizedTime { + var copy = time + copy[keyPath: field] += modifier + return copy + } + + let integerTransformable: [WritableKeyPath] = [ + \.year, \.month, \.day, \.hours, \.minutes, \.seconds + ] + + var transformationsAndResults: [(GeneralizedTime, ExpectedComparisonResult)] = [] + transformationsAndResults.append((original, .equal)) + + for transform in integerTransformable { + transformationsAndResults.append((modify(transform, of: original, by: 1), .greaterThan)) + transformationsAndResults.append((modify(transform, of: original, by: -1), .lessThan)) + } + + transformationsAndResults.append((modify(\.fractionalSeconds, of: original, by: 0.1), .greaterThan)) + transformationsAndResults.append((modify(\.fractionalSeconds, of: original, by: -0.1), .lessThan)) + + for (newValue, expectedResult) in transformationsAndResults { + switch expectedResult { + case .lessThan: + XCTAssertLessThan(newValue, original) + XCTAssertLessThanOrEqual(newValue, original) + XCTAssertGreaterThan(original, newValue) + XCTAssertGreaterThanOrEqual(original, newValue) + case .equal: + XCTAssertGreaterThanOrEqual(newValue, original) + XCTAssertGreaterThanOrEqual(original, newValue) + XCTAssertLessThanOrEqual(newValue, original) + XCTAssertLessThanOrEqual(original, newValue) + case .greaterThan: + XCTAssertGreaterThan(newValue, original) + XCTAssertGreaterThanOrEqual(newValue, original) + XCTAssertLessThan(original, newValue) + XCTAssertLessThanOrEqual(original, newValue) + } + } + } } diff --git a/Tests/SwiftASN1Tests/UTCTimeTests.swift b/Tests/SwiftASN1Tests/UTCTimeTests.swift new file mode 100644 index 0000000..cdfca89 --- /dev/null +++ b/Tests/SwiftASN1Tests/UTCTimeTests.swift @@ -0,0 +1,66 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftASN1 open source project +// +// Copyright (c) 2023 Apple Inc. and the SwiftASN1 project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftASN1 project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// +import XCTest + +@testable import SwiftASN1 + +final class UTCTimeTests: XCTestCase { + func testComparisons() throws { + enum ExpectedComparisonResult { + case lessThan + case equal + case greaterThan + } + + let original = try UTCTime(year: 2020, month: 03, day: 03, hours: 03, minutes: 03, seconds: 03) + + func modify(_ field: WritableKeyPath, of time: UTCTime, by modifier: Int) -> UTCTime { + var copy = time + copy[keyPath: field] += modifier + return copy + } + + let integerTransformable: [WritableKeyPath] = [ + \.year, \.month, \.day, \.hours, \.minutes, \.seconds + ] + + var transformationsAndResults: [(UTCTime, ExpectedComparisonResult)] = [] + transformationsAndResults.append((original, .equal)) + + for transform in integerTransformable { + transformationsAndResults.append((modify(transform, of: original, by: 1), .greaterThan)) + transformationsAndResults.append((modify(transform, of: original, by: -1), .lessThan)) + } + + for (newValue, expectedResult) in transformationsAndResults { + switch expectedResult { + case .lessThan: + XCTAssertLessThan(newValue, original) + XCTAssertLessThanOrEqual(newValue, original) + XCTAssertGreaterThan(original, newValue) + XCTAssertGreaterThanOrEqual(original, newValue) + case .equal: + XCTAssertGreaterThanOrEqual(newValue, original) + XCTAssertGreaterThanOrEqual(original, newValue) + XCTAssertLessThanOrEqual(newValue, original) + XCTAssertLessThanOrEqual(original, newValue) + case .greaterThan: + XCTAssertGreaterThan(newValue, original) + XCTAssertGreaterThanOrEqual(newValue, original) + XCTAssertLessThan(original, newValue) + XCTAssertLessThanOrEqual(original, newValue) + } + } + } +}