From 0c182f2eb1c28dd3a871e4793fcd3e5e0df80825 Mon Sep 17 00:00:00 2001 From: Joseph Radford Date: Wed, 5 Nov 2025 18:17:25 +1100 Subject: [PATCH] Bugfix/reading time format issue 451 (#456) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix reading time display affected by 12/24-hour system setting (#451) Reading time was incorrectly displayed as time-of-day (e.g., "12:45:00 AM" on 12-hour format devices) instead of as a duration. This occurred because DateFormatter was being used to format what should be a duration value, causing it to apply system time format preferences. Replaced DateFormatter with simple duration calculation that formats as HH:MM:SS regardless of device settings. Added comprehensive unit tests covering basic durations, hour boundaries, fractional minutes, and edge cases. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * Add GitHub Action to run SharedLib tests --------- Co-authored-by: Claude --- .github/workflows/sharedlib-tests.yml | 33 +++++++++++++++++ .../Sources/SharedLib/Extension/Double.swift | 10 +++--- .../Sources/SharedLib/Extension/Int.swift | 10 +++--- .../Extension/DoubleTests.swift | 35 +++++++++++++++++++ .../SharedLibTests/Extension/IntTests.swift | 21 +++++++++++ 5 files changed, 99 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/sharedlib-tests.yml create mode 100644 SharedLib/Tests/SharedLibTests/Extension/DoubleTests.swift diff --git a/.github/workflows/sharedlib-tests.yml b/.github/workflows/sharedlib-tests.yml new file mode 100644 index 0000000..ed5b287 --- /dev/null +++ b/.github/workflows/sharedlib-tests.yml @@ -0,0 +1,33 @@ +name: SharedLib Tests + +on: + push: + branches: [ main ] + paths: + - 'SharedLib/**' + - '.github/workflows/sharedlib-tests.yml' + pull_request: + branches: [ main ] + paths: + - 'SharedLib/**' + - '.github/workflows/sharedlib-tests.yml' + +jobs: + test: + name: Run SharedLib Tests + runs-on: macos-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Select Xcode version + run: sudo xcode-select -s /Applications/Xcode.app/Contents/Developer + + - name: Show Swift version + run: swift --version + + - name: Run SharedLib tests + run: | + cd SharedLib + swift test diff --git a/SharedLib/Sources/SharedLib/Extension/Double.swift b/SharedLib/Sources/SharedLib/Extension/Double.swift index 4b836d7..e40dc5c 100644 --- a/SharedLib/Sources/SharedLib/Extension/Double.swift +++ b/SharedLib/Sources/SharedLib/Extension/Double.swift @@ -2,11 +2,11 @@ import Foundation public extension Double { var readingTime: String { - let date = Date(timeIntervalSince1970: TimeInterval(self * 60)) - let dayTimePeriodFormatter = DateFormatter() - dayTimePeriodFormatter.timeZone = TimeZone(secondsFromGMT: 0) - dayTimePeriodFormatter.dateFormat = "HH:mm:ss" + let totalSeconds = Int(self * 60) + let hours = totalSeconds / 3600 + let minutes = (totalSeconds % 3600) / 60 + let seconds = totalSeconds % 60 - return dayTimePeriodFormatter.string(from: date as Date) + return String(format: "%02d:%02d:%02d", hours, minutes, seconds) } } diff --git a/SharedLib/Sources/SharedLib/Extension/Int.swift b/SharedLib/Sources/SharedLib/Extension/Int.swift index 9988965..ecafa2c 100644 --- a/SharedLib/Sources/SharedLib/Extension/Int.swift +++ b/SharedLib/Sources/SharedLib/Extension/Int.swift @@ -2,12 +2,12 @@ import Foundation public extension Int { var readingTime: String { - let date = Date(timeIntervalSince1970: TimeInterval(self * 60)) - let dayTimePeriodFormatter = DateFormatter() - dayTimePeriodFormatter.timeZone = TimeZone(secondsFromGMT: 0) - dayTimePeriodFormatter.dateFormat = "HH:mm:ss" + let totalSeconds = self * 60 + let hours = totalSeconds / 3600 + let minutes = (totalSeconds % 3600) / 60 + let seconds = totalSeconds % 60 - return dayTimePeriodFormatter.string(from: date as Date) + return String(format: "%02d:%02d:%02d", hours, minutes, seconds) } var bool: Bool { diff --git a/SharedLib/Tests/SharedLibTests/Extension/DoubleTests.swift b/SharedLib/Tests/SharedLibTests/Extension/DoubleTests.swift new file mode 100644 index 0000000..126ef9d --- /dev/null +++ b/SharedLib/Tests/SharedLibTests/Extension/DoubleTests.swift @@ -0,0 +1,35 @@ +import XCTest + +class DoubleTests: XCTestCase { + func testReadingTime() { + // Basic minute tests + XCTAssertEqual("00:01:00", 1.0.readingTime) + XCTAssertEqual("00:02:00", 2.0.readingTime) + XCTAssertEqual("00:05:00", 5.0.readingTime) + XCTAssertEqual("00:10:00", 10.0.readingTime) + XCTAssertEqual("00:30:00", 30.0.readingTime) + XCTAssertEqual("00:45:00", 45.0.readingTime) + + // Hour boundary tests + XCTAssertEqual("01:00:00", 60.0.readingTime) + XCTAssertEqual("01:01:00", 61.0.readingTime) + XCTAssertEqual("01:30:00", 90.0.readingTime) + + // Multiple hours + XCTAssertEqual("02:00:00", 120.0.readingTime) + XCTAssertEqual("02:15:00", 135.0.readingTime) + XCTAssertEqual("03:45:00", 225.0.readingTime) + + // Fractional minutes (should truncate seconds properly) + XCTAssertEqual("00:01:30", 1.5.readingTime) + XCTAssertEqual("00:02:15", 2.25.readingTime) + XCTAssertEqual("00:05:45", 5.75.readingTime) + + // Edge cases + XCTAssertEqual("00:00:00", 0.0.readingTime) + + // Long reading times + XCTAssertEqual("10:00:00", 600.0.readingTime) + XCTAssertEqual("24:00:00", 1440.0.readingTime) + } +} diff --git a/SharedLib/Tests/SharedLibTests/Extension/IntTests.swift b/SharedLib/Tests/SharedLibTests/Extension/IntTests.swift index 33c50e4..2597301 100644 --- a/SharedLib/Tests/SharedLibTests/Extension/IntTests.swift +++ b/SharedLib/Tests/SharedLibTests/Extension/IntTests.swift @@ -2,9 +2,30 @@ import XCTest class IntTests: XCTestCase { func testReadingTime() { + // Basic minute tests XCTAssertEqual("00:01:00", 1.readingTime) XCTAssertEqual("00:02:00", 2.readingTime) + XCTAssertEqual("00:05:00", 5.readingTime) + XCTAssertEqual("00:10:00", 10.readingTime) + XCTAssertEqual("00:30:00", 30.readingTime) + XCTAssertEqual("00:45:00", 45.readingTime) + + // Hour boundary tests + XCTAssertEqual("01:00:00", 60.readingTime) XCTAssertEqual("01:01:00", 61.readingTime) + XCTAssertEqual("01:30:00", 90.readingTime) + + // Multiple hours + XCTAssertEqual("02:00:00", 120.readingTime) + XCTAssertEqual("02:15:00", 135.readingTime) + XCTAssertEqual("03:45:00", 225.readingTime) + + // Edge cases + XCTAssertEqual("00:00:00", 0.readingTime) + + // Long reading times + XCTAssertEqual("10:00:00", 600.readingTime) + XCTAssertEqual("24:00:00", 1440.readingTime) } func testBool() {