damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

commit 0b27a49e32a89a0dcd594ca16ae8add832159e7c
parent 58b4d57f32c81fc5433de58668246ef6ffcb05d4
Author: Terry Yiu <963907+tyiu@users.noreply.github.com>
Date:   Fri, 30 Dec 2022 23:32:33 -0500

Internationalize time ago since string

Switches to using standard date component formatting abbreviations and
enables it to be localized to non-English locales

Closes: #194
Changelog-Changed: Internationalize relative dates

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 4++++
Mdamus/Util/TimeAgo.swift | 29+++++++++++++++++++----------
AdamusTests/TimeAgoTests.swift | 34++++++++++++++++++++++++++++++++++
3 files changed, 57 insertions(+), 10 deletions(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */; }; 3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAEC294FCCFC00EE4006 /* Constants.swift */; }; 31D2E847295218AF006D67F8 /* Shimmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D2E846295218AF006D67F8 /* Shimmer.swift */; }; + 3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */; }; 4C06670128FC7C5900038D2A /* RelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670028FC7C5900038D2A /* RelayView.swift */; }; 4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 4C06670328FC7EC500038D2A /* Kingfisher */; }; 4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670528FCB08600038D2A /* ImageCarousel.swift */; }; @@ -162,6 +163,7 @@ 3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyTimelineView.swift; sourceTree = "<group>"; }; 3169CAEC294FCCFC00EE4006 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Constants.swift; path = damus/Util/Constants.swift; sourceTree = SOURCE_ROOT; }; 31D2E846295218AF006D67F8 /* Shimmer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shimmer.swift; sourceTree = "<group>"; }; + 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeAgoTests.swift; sourceTree = "<group>"; }; 4C06670028FC7C5900038D2A /* RelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayView.swift; sourceTree = "<group>"; }; 4C06670528FCB08600038D2A /* ImageCarousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCarousel.swift; sourceTree = "<group>"; }; 4C06670828FDE64700038D2A /* damus-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "damus-Bridging-Header.h"; sourceTree = "<group>"; }; @@ -608,6 +610,7 @@ 4C363A9D2828A822006E126D /* ReplyTests.swift */, 4CE6DEF727F7A08200C66700 /* damusTests.swift */, 4C3EA67A28FF7B3900C48A62 /* InvoiceTests.swift */, + 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */, ); path = damusTests; sourceTree = "<group>"; @@ -898,6 +901,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */, 4C3EA67B28FF7B3900C48A62 /* InvoiceTests.swift in Sources */, 4C363A9E2828A822006E126D /* ReplyTests.swift in Sources */, 4C363AA02828A8DD006E126D /* LikeTests.swift in Sources */, diff --git a/damus/Util/TimeAgo.swift b/damus/Util/TimeAgo.swift @@ -11,36 +11,45 @@ public func time_ago_since(_ date: Date) -> String { let calendar = Calendar.current let now = Date() - let unitFlags: NSCalendar.Unit = [.second, .minute, .hour, .day, .weekOfYear, .month, .year] + let unitFlags: NSCalendar.Unit = [.second, .minute, .hour, .day, .weekOfMonth, .month, .year] + let components = (calendar as NSCalendar).components(unitFlags, from: date, to: now, options: []) + let formatter = DateComponentsFormatter() + formatter.unitsStyle = .abbreviated + formatter.maximumUnitCount = 1 + formatter.allowedUnits = unitFlags + + // Manually format date component from only the most significant time unit because + // DateComponentsFormatter rounds up by default. + if let year = components.year, year >= 1 { - return "\(year)yr" + return formatter.string(from: DateComponents(calendar: calendar, year: year))! } if let month = components.month, month >= 1 { - return "\(month)mth" + return formatter.string(from: DateComponents(calendar: calendar, month: month))! } - if let week = components.weekOfYear, week >= 1 { - return "\(week)wk" + if let week = components.weekOfMonth, week >= 1 { + return formatter.string(from: DateComponents(calendar: calendar, weekOfMonth: week))! } if let day = components.day, day >= 1 { - return "\(day)d" + return formatter.string(from: DateComponents(calendar: calendar, day: day))! } if let hour = components.hour, hour >= 1 { - return "\(hour)h" + return formatter.string(from: DateComponents(calendar: calendar, hour: hour))! } if let minute = components.minute, minute >= 1 { - return "\(minute)m" + return formatter.string(from: DateComponents(calendar: calendar, minute: minute))! } if let second = components.second, second >= 3 { - return "\(second)s" + return formatter.string(from: DateComponents(calendar: calendar, second: second))! } - return "now" + return NSLocalizedString("now", comment: "String indicating that a given timestamp just occurred") } diff --git a/damusTests/TimeAgoTests.swift b/damusTests/TimeAgoTests.swift @@ -0,0 +1,34 @@ +// +// TimeAgoTests.swift +// damusTests +// +// Created by Terry Yiu on 12/30/22. +// + +import XCTest +@testable import damus + +final class TimeAgoTests: XCTestCase { + + func testTimeAgoSince() { + XCTAssertEqual(time_ago_since(Date.now), "now") + XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-2)), "now") + XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-3)), "3s") + XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-59)), "59s") + XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-60)), "1m") + XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-3599)), "59m") + XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-3600)), "1h") + XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-86399)), "23h") + XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-86400)), "1d") + XCTAssertEqual(time_ago_since(Calendar.current.date(byAdding: .weekOfMonth, value: -1, to: Date.now)!.addingTimeInterval(1)), "6d") + XCTAssertEqual(time_ago_since(Calendar.current.date(byAdding: .weekOfMonth, value: -1, to: Date.now)!), "1w") + XCTAssertEqual(time_ago_since(Calendar.current.date(byAdding: .weekOfMonth, value: -2, to: Date.now)!), "2w") + XCTAssertEqual(time_ago_since(Calendar.current.date(byAdding: .weekOfMonth, value: -3, to: Date.now)!), "3w") + // Not testing the 4-5 week boundary since how it is formatted depends on which month and year it is currently when this test executes. + XCTAssertEqual(time_ago_since(Calendar.current.date(byAdding: .month, value: -1, to: Date.now)!), "1mo") + XCTAssertEqual(time_ago_since(Calendar.current.date(byAdding: .year, value: -1, to: Date.now)!.addingTimeInterval(1)), "11mo") + XCTAssertEqual(time_ago_since(Calendar.current.date(byAdding: .year, value: -1, to: Date.now)!), "1y") + XCTAssertEqual(time_ago_since(Calendar.current.date(byAdding: .year, value: -1000, to: Date.now)!), "1,000y") + } + +}