1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
@testable import Grain
import XCTest
final class DateFormattingTests: GrainTestCase {
// MARK: - parse()
func testParseFractionalSeconds() {
let date = DateFormatting.parse("2024-06-15T12:30:45.123Z")
XCTAssertNotNil(date)
}
func testParseWithoutFractionalSeconds() {
let date = DateFormatting.parse("2024-06-15T12:30:45Z")
XCTAssertNotNil(date)
}
func testParseInvalidStringReturnsNil() {
XCTAssertNil(DateFormatting.parse("not-a-date"))
XCTAssertNil(DateFormatting.parse(""))
XCTAssertNil(DateFormatting.parse("2024-13-40"))
}
func testParseRoundTrip() throws {
// Generate an ISO string, parse it back, verify it's close to now
let iso = DateFormatting.nowISO()
let parsed = DateFormatting.parse(iso)
XCTAssertNotNil(parsed)
// Should be within 1 second of now
let interval = try abs(XCTUnwrap(parsed?.timeIntervalSinceNow))
XCTAssertLessThan(interval, 1.0)
}
// MARK: - relativeTime()
func testRelativeTimeNow() {
let iso = DateFormatting.nowISO()
let result = DateFormatting.relativeTime(iso)
XCTAssertEqual(result, "now")
}
func testRelativeTimeInvalidReturnsEmpty() {
XCTAssertEqual(DateFormatting.relativeTime("garbage"), "")
}
/// Offsets sit inside their bucket rather than exactly on its edge.
/// `relativeTime` floors, and the ISO round-trip is only millisecond-precise,
/// so a boundary value can land a hair short and report the bucket below.
func testRelativeTimeMinutesAgo() {
let fiveMinutesAgo = Date().addingTimeInterval(-330) // 5.5 minutes, well within the 5m bucket
let iso = isoString(from: fiveMinutesAgo)
let result = DateFormatting.relativeTime(iso)
XCTAssertEqual(result, "5m")
}
func testRelativeTimeHoursAgo() {
let threeHoursAgo = Date().addingTimeInterval(-12600) // 3.5 hours, well within the 3h bucket
let iso = isoString(from: threeHoursAgo)
let result = DateFormatting.relativeTime(iso)
XCTAssertEqual(result, "3h")
}
func testRelativeTimeDaysAgo() {
let twoDaysAgo = Date().addingTimeInterval(-180_000) // 50 hours, well within the 2d bucket
let iso = isoString(from: twoDaysAgo)
let result = DateFormatting.relativeTime(iso)
XCTAssertEqual(result, "2d")
}
func testRelativeTimeWeeksAgo() {
let twoWeeksAgo = Date().addingTimeInterval(-1_300_000) // ~2.17 weeks, well within the 2w bucket
let iso = isoString(from: twoWeeksAgo)
let result = DateFormatting.relativeTime(iso)
XCTAssertEqual(result, "2w")
}
// MARK: - Helpers
private func isoString(from date: Date) -> String {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
return formatter.string(from: date)
}
}