diff --git a/DateToolsSwift/DateTools/Date+Bundle.swift b/DateToolsSwift/DateTools/Date+Bundle.swift index 9bec5565..597f3b5f 100644 --- a/DateToolsSwift/DateTools/Date+Bundle.swift +++ b/DateToolsSwift/DateTools/Date+Bundle.swift @@ -9,9 +9,13 @@ import Foundation public extension Bundle { - class func dateToolsBundle() -> Bundle { + #if PACKAGE_MANAGER + let assetPath = Bundle.module.resourcePath! + #else let assetPath = Bundle(for: Constants.self).resourcePath! + #endif + return Bundle(path: NSString(string: assetPath).appendingPathComponent("DateTools.bundle"))! } } diff --git a/DateToolsSwift/DateTools/Date+TimeAgo.swift b/DateToolsSwift/DateTools/Date+TimeAgo.swift index cdde9371..d358787a 100644 --- a/DateToolsSwift/DateTools/Date+TimeAgo.swift +++ b/DateToolsSwift/DateTools/Date+TimeAgo.swift @@ -197,7 +197,6 @@ public extension Date { } } - private func logicalLocalizedStringFromFormat(format: String, value: Int) -> String{ #if os(Linux) let localeFormat = String.init(format: format, getLocaleFormatUnderscoresWithValue(Double(value)) as! CVarArg) // this may not work, unclear!! @@ -232,22 +231,6 @@ public extension Date { } - // MARK: - Localization - - private func DateToolsLocalizedStrings(_ string: String) -> String { - //let classBundle = Bundle(for:TimeChunk.self as! AnyClass.Type).resourcePath!.appending("DateTools.bundle") - - //let bundelPath = Bundle(path:classBundle)! - #if os(Linux) - // NSLocalizedString() is not available yet, see: https://github.com/apple/swift-corelibs-foundation/blob/16f83ddcd311b768e30a93637af161676b0a5f2f/Foundation/NSData.swift - // However, a seemingly-equivalent method from NSBundle is: https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/NSBundle.swift - return Bundle.main.localizedString(forKey: string, value: "", table: "DateTools") - #else - return NSLocalizedString(string, tableName: "DateTools", bundle: Bundle.dateToolsBundle(), value: "", comment: "") - #endif - } - - // MARK: - Date Earlier/Later /** @@ -273,3 +256,19 @@ public extension Date { } } + + +// MARK: - Localization + +func DateToolsLocalizedStrings(_ string: String) -> String { + //let classBundle = Bundle(for:TimeChunk.self as! AnyClass.Type).resourcePath!.appending("DateTools.bundle") + + //let bundelPath = Bundle(path:classBundle)! + #if os(Linux) + // NSLocalizedString() is not available yet, see: https://github.com/apple/swift-corelibs-foundation/blob/16f83ddcd311b768e30a93637af161676b0a5f2f/Foundation/NSData.swift + // However, a seemingly-equivalent method from NSBundle is: https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/NSBundle.swift + return Bundle.main.localizedString(forKey: string, value: "", table: "DateTools") + #else + return NSLocalizedString(string, tableName: "DateTools", bundle: Bundle.dateToolsBundle(), value: "", comment: "") + #endif +} diff --git a/DateToolsSwift/Tests/PackageTests/DateManipulationsExtensionTests.swift b/DateToolsSwift/Tests/PackageTests/DateManipulationsExtensionTests.swift new file mode 100644 index 00000000..03c0efbe --- /dev/null +++ b/DateToolsSwift/Tests/PackageTests/DateManipulationsExtensionTests.swift @@ -0,0 +1,162 @@ +// +// DateManipulationsExtensionTests.swift +// DateToolsTests +// +// Created by Grayson Webster on 9/30/16. +// Copyright © 2016 Matthew York. All rights reserved. +// + +import XCTest +@testable import DateToolsSwift + +class DateManipulationsTests: XCTestCase { + + let formatter = DateFormatter() + var controlDate = Date() + + override func setUp() { + super.setUp() + formatter.dateFormat = "yyyy MM dd HH:mm:ss.SSS" + controlDate = formatter.date(from: "2015 11 24 14:50:12.001")! + } + + override func tearDown() { + super.tearDown() + } + + // MARK: - Start Of + + func testStartOfSecond() { + let testDate = controlDate.start(of: .second) + XCTAssertTrue(testDate.second == 12) + } + + func testStartOfMinute() { + let testDate = controlDate.start(of: .minute) + XCTAssertTrue(testDate.minute == 50) + XCTAssertTrue(testDate.second == 0) + } + + func testStartOfHour() { + let testDate = controlDate.start(of: .hour) + XCTAssertTrue(testDate.hour == 14) + XCTAssertTrue(testDate.minute == 0) + XCTAssertTrue(testDate.second == 0) + } + + func testStartOfDay() { + let testDate = controlDate.start(of: .day) + XCTAssertTrue(testDate.day == 24) + XCTAssertTrue(testDate.hour == 0) + XCTAssertTrue(testDate.minute == 0) + XCTAssertTrue(testDate.second == 0) + } + + func testStartOfMonth() { + let testDate = controlDate.start(of: .month) + XCTAssertTrue(testDate.month == 11) + XCTAssertTrue(testDate.day == 1) + XCTAssertTrue(testDate.hour == 0) + XCTAssertTrue(testDate.minute == 0) + XCTAssertTrue(testDate.second == 0) + } + + func testStartOfYear() { + let testDate = controlDate.start(of: .year) + XCTAssertTrue(testDate.year == 2015) + XCTAssertTrue(testDate.month == 1) + XCTAssertTrue(testDate.day == 1) + XCTAssertTrue(testDate.hour == 0) + XCTAssertTrue(testDate.minute == 0) + XCTAssertTrue(testDate.second == 0) + } + + + // MARK: - End Of + + func testEndOfSecond() { + let testDate = controlDate.end(of: .second) + XCTAssertTrue(testDate.second == 12) + } + + func testEndOfMinute() { + let testDate = controlDate.end(of: .minute) + XCTAssertTrue(testDate.minute == 50) + XCTAssertTrue(testDate.second == 59) + } + + func testEndOfHour() { + let testDate = controlDate.end(of: .hour) + XCTAssertTrue(testDate.hour == 14) + XCTAssertTrue(testDate.minute == 59) + XCTAssertTrue(testDate.second == 59) + } + + func testEndOfDay() { + let testDate = controlDate.end(of: .day) + XCTAssertTrue(testDate.day == 24) + XCTAssertTrue(testDate.hour == 23) + XCTAssertTrue(testDate.minute == 59) + XCTAssertTrue(testDate.second == 59) + } + + func testEndOfMonth() { + let testDate = controlDate.end(of: .month) + XCTAssertTrue(testDate.month == 11) + XCTAssertTrue(testDate.day == 30) + XCTAssertTrue(testDate.hour == 23) + XCTAssertTrue(testDate.minute == 59) + XCTAssertTrue(testDate.second == 59) + } + + func testEndOfYear() { + let testDate = controlDate.end(of: .year) + XCTAssertTrue(testDate.year == 2015) + XCTAssertTrue(testDate.month == 12) + XCTAssertTrue(testDate.day == 31) + XCTAssertTrue(testDate.hour == 23) + XCTAssertTrue(testDate.minute == 59) + XCTAssertTrue(testDate.second == 59) + } + + + // MARK: - Addition + + func testAddition() { + XCTAssertTrue(controlDate.add(5.days).day == 29) + + let testDate = formatter.date(from: "2016 10 19 18:40:24.001")! + let testChunk = TimeChunk(seconds: 12, minutes: -10, hours: 4, days: 2, weeks: -1, months: -1, years: 1) + let testAddedDate = controlDate + testChunk; + XCTAssertTrue(testAddedDate == testDate) + } + + func testAdditionOperatorChunk() { + XCTAssertTrue((controlDate + 5.days).day == 29) + } + + func testAdditionOperatorInt() { + XCTAssertTrue((controlDate + 5 * Constants.SecondsInDay).day == 29) + } + + + // MARK: - Subtraction + + func testSubtraction() { + XCTAssertTrue(controlDate.subtract(5.days).day == 19) + + let testDate = formatter.date(from: "2016 10 19 18:40:24.001")! + let testChunk = TimeChunk(seconds: -12, minutes: 10, hours: -4, days: -2, weeks: 1, months: 1, years: -1) + let testAddedDate = controlDate - testChunk; + XCTAssertTrue(testAddedDate == testDate) + } + + func testSubtractionOperatorChunk() { + XCTAssertTrue((controlDate - 5.days).day == 19) + } + + func testSubtractionOperatorInt() { + XCTAssertTrue((controlDate - 5 * Constants.SecondsInDay).day == 19) + } + +} diff --git a/DateToolsSwift/Tests/PackageTests/TimeAgoTests.swift b/DateToolsSwift/Tests/PackageTests/TimeAgoTests.swift new file mode 100644 index 00000000..2d67fc7a --- /dev/null +++ b/DateToolsSwift/Tests/PackageTests/TimeAgoTests.swift @@ -0,0 +1,147 @@ +// +// TimeAgoTests.swift +// DateToolsTests +// +// Created by Grayson Webster on 8/19/16. +// Copyright © 2016 Matthew York. All rights reserved. +// + +import XCTest +@testable import DateToolsSwift + + +class TimeAgoTests : XCTestCase { + + var formatter: DateFormatter? + var date0: Date! + var date1: Date! + + override func setUp() { + super.setUp() + + // Put setup code here. This method is called before the invocation of each test method in the class. + self.formatter = DateFormatter() + self.formatter?.dateFormat = "yyyy MM dd HH:mm:ss.SSS" + self.date0 = self.formatter?.date(from: "2014 11 05 18:15:12.000") + self.date1 = self.formatter?.date(from: "2014 11 07 18:15:12.000") + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testBasicLongTimeAgo() { + let now: String = self.date0.timeAgo(since: self.date0) + XCTAssert(!now.isEmpty, "'Now' is nil or empty.") + let ago: String = self.date1.timeAgo(since: self.date0) + XCTAssert(!ago.isEmpty, "Ago is nil or empty.") + } + + func testLongTimeAgo2Days() { + self.date0 = self.formatter?.date(from: "2014 11 05 18:15:12.000") + self.date1 = self.formatter?.date(from: "2014 11 07 18:15:12.000") + let ago: String = self.date0.timeAgo(since: self.date1) + XCTAssertEqual(ago, DateToolsLocalizedStrings("2 days ago")) + } + + func testLongTimeAgo1DayAndHalf() { + self.date0 = self.formatter?.date(from: "2014 11 06 9:15:12.000") + self.date1 = self.formatter?.date(from: "2014 11 07 18:15:12.000") + let ago: String = self.date0.timeAgo(since: self.date1) + XCTAssertEqual(ago, DateToolsLocalizedStrings("Yesterday")) + } + + func testLongTimeAgoExactlyYesterday() { + self.date0 = self.formatter?.date(from: "2014 11 06 18:15:12.000") + self.date1 = self.formatter?.date(from: "2014 11 07 18:15:12.000") + let ago: String = self.date0.timeAgo(since: self.date1) + XCTAssertEqual(ago, DateToolsLocalizedStrings("Yesterday")) + } + + func testLongTimeAgoLessThan24hoursSameDay() { + self.date0 = self.formatter?.date(from: "2014 11 07 10:15:12.000") + self.date1 = self.formatter?.date(from: "2014 11 07 18:15:12.000") + let ago: String = self.date0.timeAgo(since: self.date1) + XCTAssertEqual(ago, DateToolsLocalizedStrings("8 hours ago")) + } + + func testLongTimeAgoBetween24And48Hours() { + self.date0 = self.formatter?.date(from: "2014 11 07 10:15:12.000") + self.date1 = self.formatter?.date(from: "2014 11 08 18:15:12.000") + let ago: String = self.date0.timeAgo(since: self.date1) + XCTAssertEqual(ago, DateToolsLocalizedStrings("Yesterday")) + } + + func testBasicShortTimeAgo() { + let now: String = self.date0.shortTimeAgo(since: self.date0) + XCTAssert(!now.isEmpty, "'Now' is nil or empty.") + let ago: String = self.date1.shortTimeAgo(since: self.date0) + XCTAssert(!ago.isEmpty, "Ago is nil or empty.") + } + + func testShortTimeAgo2Days() { + self.date0 = self.formatter?.date(from: "2014 11 05 18:15:12.000") + self.date1 = self.formatter?.date(from: "2014 11 07 18:15:12.000") + let ago: String = self.date0.shortTimeAgo(since: self.date1) + XCTAssertEqual(ago, DateToolsLocalizedStrings("2d")) + } + + func testShortTimeAgo1DayAndHalf() { + self.date0 = self.formatter?.date(from: "2014 11 06 9:15:12.000") + self.date1 = self.formatter?.date(from: "2014 11 07 18:15:12.000") + let ago: String = self.date0.shortTimeAgo(since: self.date1) + XCTAssertEqual(ago, DateToolsLocalizedStrings("1d")) + } + + func testShortTimeAgoExactlyYesterday() { + self.date0 = self.formatter?.date(from: "2014 11 06 18:15:12.000") + self.date1 = self.formatter?.date(from: "2014 11 07 18:15:12.000") + let ago: String = self.date0.shortTimeAgo(since: self.date1) + XCTAssertEqual(ago, DateToolsLocalizedStrings("1d")) + } + + func testShortTimeAgoLessThan24hoursSameDay() { + self.date0 = self.formatter?.date(from: "2014 11 07 10:15:12.000") + self.date1 = self.formatter?.date(from: "2014 11 07 18:15:12.000") + let ago: String = self.date0.shortTimeAgo(since: self.date1) + XCTAssertEqual(ago, DateToolsLocalizedStrings("8h")) + } + + func testShortTimeAgoBetween24And48Hours() { + self.date0 = self.formatter?.date(from: "2014 11 07 10:15:12.000") + self.date1 = self.formatter?.date(from: "2014 11 08 18:15:12.000") + let ago: String = self.date0.shortTimeAgo(since: self.date1) + XCTAssertEqual(ago, DateToolsLocalizedStrings("1d")) + } + + class func japaneseBundle() -> Bundle { + #if PACKAGE_MANAGER + let assetPath = Bundle.module.resourcePath! + #else + let assetPath = Bundle(for: Constants.self).resourcePath! + #endif + + return Bundle(path: NSString(string: assetPath).appendingPathComponent("DateTools.bundle/ja.lproj"))! + } + + class func localizedString(key: String, localeIdentifier: String) -> String { + guard let path = Bundle.dateToolsBundle().path(forResource: localeIdentifier, ofType: "lproj"), + let bundle = Bundle(path: path) else { + XCTFail("Missing localization for \(localeIdentifier)"); return "" + } + + return bundle.localizedString(forKey: key, value: nil, table: "DateTools") + + } + + func testLongTimeAgoLocalizationsAccessible() { + let en_local: String = "Yesterday" + let ja_local: String = "昨日" + let ja_result: String = TimeAgoTests.localizedString(key: en_local, localeIdentifier: "ja" ) + + XCTAssertFalse(ja_result.isEmpty) + XCTAssertEqual(ja_local, ja_result, "Could not access localizations.") + } +} + diff --git a/Package.swift b/Package.swift index 32485a35..a30133d3 100644 --- a/Package.swift +++ b/Package.swift @@ -1,9 +1,32 @@ +// swift-tools-version:5.3 +// The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "DateToolsSwift", + defaultLocalization: "en", + platforms: [ + .iOS(.v12), + .tvOS(.v12), + .macOS(.v10_13), + ], + products: [ + .library( + name: "DateToolsSwift", targets: ["DateToolsSwift"]), + ], + dependencies: [ + ], targets: [ - Target(name: "DateToolsSwift") + .target(name: "DateToolsSwift", + path: "DateToolsSwift/DateTools", + resources: [.copy("DateTools.bundle")], + swiftSettings: [.define("PACKAGE_MANAGER"),] + ), + .testTarget( + name: "DateToolsSwiftTests", + dependencies: ["DateToolsSwift"], + path: "DateToolsSwift/Tests/PackageTests", + exclude: ["DateToolsTestsTests", "DateToolsTests"]), ] ) -package.exclude = ["DateTools", "Examples", "Tests", "DateToolsSwift/Examples"] +