diff --git a/Trust/Extensions/Date.swift b/Trust/Extensions/Date.swift index 549e23ff3..cccc59723 100644 --- a/Trust/Extensions/Date.swift +++ b/Trust/Extensions/Date.swift @@ -11,6 +11,7 @@ import Foundation public extension Date { private static var formatsMap: [String: DateFormatter] = [:] + private static var formatsMapLocale: String? public init?(string: String, format: String) { let date = Date.formatter(with: format).date(from: string) @@ -21,16 +22,30 @@ public extension Date { return nil } - public func format(_ format: String) -> String { - return Date.formatter(with: format).string(from: self) + public func format(_ format: String, overrideWithTimezoneIdentifier timezoneIdentifier: String? = nil) -> String { + return Date.formatter(with: format, overrideWithTimezoneIdentifier: timezoneIdentifier).string(from: self) } - - public static func formatter(with format: String) -> DateFormatter { - var foundFormatter = Date.formatsMap[format] + + public static func formatter(with format: String, overrideWithTimezoneIdentifier timezoneIdentifier: String? = nil) -> DateFormatter { + let config = Config() + if config.locale != formatsMapLocale { + formatsMapLocale = config.locale + formatsMap = Dictionary() + } + + var foundFormatter = formatsMap[format] if foundFormatter == nil { foundFormatter = DateFormatter() - foundFormatter?.dateFormat = format - Date.formatsMap[format] = foundFormatter! + if let locale = config.locale { + foundFormatter?.locale = Locale(identifier: locale) + } + foundFormatter?.setLocalizedDateFormatFromTemplate(format) + formatsMap[format] = foundFormatter! + } + if let timezoneIdentifier = timezoneIdentifier, let timeZone = TimeZone(identifier: timezoneIdentifier) { + foundFormatter?.timeZone = timeZone + } else { + foundFormatter?.timeZone = .current } return foundFormatter! } @@ -43,7 +58,7 @@ public extension Date { return Calendar.current.date(byAdding: .day, value: 1, to: Date())! } - public func formatAsShortDateString() -> String { - return format("dd MMM yyyy") + public func formatAsShortDateString(overrideWithTimezoneIdentifier timezoneIdentifier: String? = nil) -> String { + return format("dd MMM yyyy", overrideWithTimezoneIdentifier: timezoneIdentifier) } } diff --git a/Trust/Foundation/XMLHandler.swift b/Trust/Foundation/XMLHandler.swift index e319fee96..311dbb0c2 100644 --- a/Trust/Foundation/XMLHandler.swift +++ b/Trust/Foundation/XMLHandler.swift @@ -14,18 +14,6 @@ public class XMLHandler { private let xml = try! XML.parse(AssetDefinitionXML().assetDefinitionString) - private func formatDateToMoscow(_ timestamp: Int) -> Date { - let formatter = DateFormatter() - formatter.timeZone = TimeZone(secondsFromGMT: 10800) - formatter.dateFormat = "dd/MM/yyyy hh:mm:ss a" - let unFormattedDate = Date(timeIntervalSince1970: TimeInterval(timestamp)) - let dateString = formatter.string(from: unFormattedDate) - if let date = Date(string: dateString, format: "dd/MM/yyyy hh:mm:ss a") { - return date - } - return unFormattedDate - } - //TODO remove once parser is properly dynamic public static func parseTicket(ticket: String) -> String { @@ -58,6 +46,8 @@ public class XMLHandler { let match = Int(tokenHex.substring(with: Range(uncheckedBounds: (24, 26))), radix: 16) ?? 0 let category = Int(tokenHex.substring(with: Range(uncheckedBounds: (26, 28))), radix: 16) ?? 0 let number = Int(tokenHex.substring(with: Range(uncheckedBounds: (28, 32))), radix: 16) ?? 0 + //TODO derive/extract from XML + let timeZoneIdentifier = Constants.eventTimeZone return Ticket( id: MarketQueueHandler.bytesToHexa(tokenId.serialize().array), @@ -66,11 +56,12 @@ public class XMLHandler { name: getName(lang: lang), venue: venue, match: match, - date: formatDateToMoscow(time), + date: Date(timeIntervalSince1970: TimeInterval(time)), seatId: number, category: getCategory(category, lang: lang), countryA: countryAString, - countryB: countryBString + countryB: countryBString, + timeZoneIdentifier: timeZoneIdentifier ) } diff --git a/Trust/Market/ViewModels/ImportTicketViewControllerViewModel.swift b/Trust/Market/ViewModels/ImportTicketViewControllerViewModel.swift index 3ef44e4b9..6683f58c5 100644 --- a/Trust/Market/ViewModels/ImportTicketViewControllerViewModel.swift +++ b/Trust/Market/ViewModels/ImportTicketViewControllerViewModel.swift @@ -98,8 +98,7 @@ struct ImportTicketViewControllerViewModel { if case let .validating = state { return "" } else { - //TODO Should format be localized? - return ticketHolder.date.format("hh:mm") + return ticketHolder.date.format("hh:mm", overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier) } } @@ -131,8 +130,7 @@ struct ImportTicketViewControllerViewModel { if case let .validating = state { return "" } else { - //TODO Should format be localized? - return ticketHolder.date.format("dd MMM yyyy") + return ticketHolder.date.format("dd MMM yyyy", overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier) } } diff --git a/Trust/Redeem/ViewModels/BaseTicketTableViewCellViewModel.swift b/Trust/Redeem/ViewModels/BaseTicketTableViewCellViewModel.swift index f7f01cd17..5c004ec9f 100644 --- a/Trust/Redeem/ViewModels/BaseTicketTableViewCellViewModel.swift +++ b/Trust/Redeem/ViewModels/BaseTicketTableViewCellViewModel.swift @@ -33,7 +33,7 @@ struct BaseTicketTableViewCellViewModel { var time: String { //TODO Should format be localized? - return ticketHolder.date.format("h:mm a") + return ticketHolder.date.format("h:mm a", overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier) } var teams: String { @@ -49,7 +49,7 @@ struct BaseTicketTableViewCellViewModel { } var date: String { - return ticketHolder.date.formatAsShortDateString() + return ticketHolder.date.formatAsShortDateString(overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier) } var backgroundColor: UIColor { diff --git a/Trust/Redeem/ViewModels/RedeemTicketsQuantitySelectionViewModel.swift b/Trust/Redeem/ViewModels/RedeemTicketsQuantitySelectionViewModel.swift index 455e425ed..2c4aab9a7 100644 --- a/Trust/Redeem/ViewModels/RedeemTicketsQuantitySelectionViewModel.swift +++ b/Trust/Redeem/ViewModels/RedeemTicketsQuantitySelectionViewModel.swift @@ -87,6 +87,6 @@ struct RedeemTicketsQuantitySelectionViewModel { } var date: String { - return ticketHolder.date.formatAsShortDateString() + return ticketHolder.date.formatAsShortDateString(overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier) } } diff --git a/Trust/Redeem/ViewModels/TicketRedemptionViewModel.swift b/Trust/Redeem/ViewModels/TicketRedemptionViewModel.swift index ddc2576aa..04b10c288 100644 --- a/Trust/Redeem/ViewModels/TicketRedemptionViewModel.swift +++ b/Trust/Redeem/ViewModels/TicketRedemptionViewModel.swift @@ -62,6 +62,6 @@ struct TicketRedemptionViewModel { } var date: String { - return ticketHolder.date.formatAsShortDateString() + return ticketHolder.date.formatAsShortDateString(overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier) } } diff --git a/Trust/Sell/ViewModels/EnterSellTicketsPriceQuantityViewControllerViewModel.swift b/Trust/Sell/ViewModels/EnterSellTicketsPriceQuantityViewControllerViewModel.swift index 79e886cf2..8e704fc54 100644 --- a/Trust/Sell/ViewModels/EnterSellTicketsPriceQuantityViewControllerViewModel.swift +++ b/Trust/Sell/ViewModels/EnterSellTicketsPriceQuantityViewControllerViewModel.swift @@ -83,7 +83,7 @@ struct EnterSellTicketsPriceQuantityViewControllerViewModel { } var date: String { - return ticketHolder.date.formatAsShortDateString() + return ticketHolder.date.formatAsShortDateString(overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier) } var pricePerTicketLabelText: String { diff --git a/Trust/Sell/ViewModels/GenerateSellMagicLinkViewControllerViewModel.swift b/Trust/Sell/ViewModels/GenerateSellMagicLinkViewControllerViewModel.swift index 343c8fda4..bd643a7c4 100644 --- a/Trust/Sell/ViewModels/GenerateSellMagicLinkViewControllerViewModel.swift +++ b/Trust/Sell/ViewModels/GenerateSellMagicLinkViewControllerViewModel.swift @@ -58,7 +58,6 @@ struct GenerateSellMagicLinkViewControllerViewModel { } var descriptionLabelText: String { - //TODO Should format be localized? return R.string.localizable.aWalletTicketTokenSellConfirmExpiryDateDescription(linkExpiryDate.format("dd MMM yyyy hh:mm")) } diff --git a/Trust/Sell/ViewModels/GenerateTransferMagicLinkViewControllerViewModel.swift b/Trust/Sell/ViewModels/GenerateTransferMagicLinkViewControllerViewModel.swift index 3692a8fd7..ed892a080 100644 --- a/Trust/Sell/ViewModels/GenerateTransferMagicLinkViewControllerViewModel.swift +++ b/Trust/Sell/ViewModels/GenerateTransferMagicLinkViewControllerViewModel.swift @@ -57,7 +57,6 @@ struct GenerateTransferMagicLinkViewControllerViewModel { } var descriptionLabelText: String { - //TODO Should format be localized? return R.string.localizable.aWalletTicketTokenSellConfirmExpiryDateDescription(linkExpiryDate.format("dd MMM yyyy hh:mm")) } diff --git a/Trust/Sell/ViewModels/SetSellTicketsExpiryDateViewControllerViewModel.swift b/Trust/Sell/ViewModels/SetSellTicketsExpiryDateViewControllerViewModel.swift index b77a3819e..766452fe4 100644 --- a/Trust/Sell/ViewModels/SetSellTicketsExpiryDateViewControllerViewModel.swift +++ b/Trust/Sell/ViewModels/SetSellTicketsExpiryDateViewControllerViewModel.swift @@ -70,7 +70,7 @@ struct SetSellTicketsExpiryDateViewControllerViewModel { } var date: String { - return ticketHolder.date.formatAsShortDateString() + return ticketHolder.date.formatAsShortDateString(overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier) } var linkExpiryDateLabelText: String { diff --git a/Trust/Sell/Views/DateEntryField.swift b/Trust/Sell/Views/DateEntryField.swift index 6a5ebede1..52d03ace2 100644 --- a/Trust/Sell/Views/DateEntryField.swift +++ b/Trust/Sell/Views/DateEntryField.swift @@ -87,7 +87,6 @@ class DateEntryField: UIControl { } private func displayDateString() { - //TODO Should format be localized? let dateString = value.format("dd MMM yyyy") leftButton.setTitle(dateString, for: .normal) } diff --git a/Trust/Sell/Views/TimeEntryField.swift b/Trust/Sell/Views/TimeEntryField.swift index 654a53c61..25f6f68ab 100644 --- a/Trust/Sell/Views/TimeEntryField.swift +++ b/Trust/Sell/Views/TimeEntryField.swift @@ -87,10 +87,6 @@ class TimeEntryField: UIControl { } private func displayTimeString() { - //TODO Should format be localized? - let formatter = DateFormatter() - formatter.timeStyle = .short - let timeString = formatter.string(from: value) - leftButton.setTitle(timeString, for: .normal) + leftButton.setTitle(value.format("hh:mm"), for: .normal) } } diff --git a/Trust/Settings/Types/Constants.swift b/Trust/Settings/Types/Constants.swift index 708a8f9b2..8b2775697 100644 --- a/Trust/Settings/Types/Constants.swift +++ b/Trust/Settings/Types/Constants.swift @@ -33,6 +33,7 @@ public struct Constants { public static let nullTicket = "0x0000000000000000000000000000000000000000000000000000000000000000" public static let burnAddressString = "0x000000000000000000000000000000000000dEaD" public static let event = "FIFA WC2018" + public static let eventTimeZone = "Europe/Moscow" } public struct UnitConfiguration { diff --git a/Trust/Tokens/Types/Ticket.swift b/Trust/Tokens/Types/Ticket.swift index 4dc4c528f..18b42309b 100644 --- a/Trust/Tokens/Types/Ticket.swift +++ b/Trust/Tokens/Types/Ticket.swift @@ -20,6 +20,7 @@ struct Ticket { let category: String let countryA: String let countryB: String + var timeZoneIdentifier: String? static var empty: Ticket { return Ticket( id: Constants.nullTicket, @@ -32,7 +33,8 @@ struct Ticket { seatId: 0, category: "N/A", countryA: "N/A", - countryB: "N/A" + countryB: "N/A", + timeZoneIdentifier: nil ) } } diff --git a/Trust/Tokens/Types/TicketHolder.swift b/Trust/Tokens/Types/TicketHolder.swift index b1be5561b..19beaf759 100644 --- a/Trust/Tokens/Types/TicketHolder.swift +++ b/Trust/Tokens/Types/TicketHolder.swift @@ -22,6 +22,7 @@ class TicketHolder { var category: String { return tickets[0].category } var countryA: String { return tickets[0].countryA } var countryB: String { return tickets[0].countryB } + var timeZoneIdentifier: String? { return tickets[0].timeZoneIdentifier } var status: TicketHolderStatus var isSelected = false var areDetailsVisible = false diff --git a/Trust/Transfer/ViewModels/ChooseTicketTransferModeViewControllerViewModel.swift b/Trust/Transfer/ViewModels/ChooseTicketTransferModeViewControllerViewModel.swift index bc3c98096..3596ac29d 100644 --- a/Trust/Transfer/ViewModels/ChooseTicketTransferModeViewControllerViewModel.swift +++ b/Trust/Transfer/ViewModels/ChooseTicketTransferModeViewControllerViewModel.swift @@ -65,6 +65,6 @@ struct ChooseTicketTransferModeViewControllerViewModel { } var date: String { - return ticketHolder.date.formatAsShortDateString() + return ticketHolder.date.formatAsShortDateString(overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier) } } diff --git a/Trust/Transfer/ViewModels/SetTransferTicketsExpiryDateViewControllerViewModel.swift b/Trust/Transfer/ViewModels/SetTransferTicketsExpiryDateViewControllerViewModel.swift index c4ce65974..a083b5394 100644 --- a/Trust/Transfer/ViewModels/SetTransferTicketsExpiryDateViewControllerViewModel.swift +++ b/Trust/Transfer/ViewModels/SetTransferTicketsExpiryDateViewControllerViewModel.swift @@ -61,7 +61,7 @@ struct SetTransferTicketsExpiryDateViewControllerViewModel { } var date: String { - return ticketHolder.date.formatAsShortDateString() + return ticketHolder.date.formatAsShortDateString(overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier) } var descriptionLabelText: String { diff --git a/Trust/Transfer/ViewModels/TransferTicketsQuantitySelectionViewModel.swift b/Trust/Transfer/ViewModels/TransferTicketsQuantitySelectionViewModel.swift index ef268a3d9..c1c9002fa 100644 --- a/Trust/Transfer/ViewModels/TransferTicketsQuantitySelectionViewModel.swift +++ b/Trust/Transfer/ViewModels/TransferTicketsQuantitySelectionViewModel.swift @@ -81,6 +81,6 @@ struct TransferTicketsQuantitySelectionViewModel { } var date: String { - return ticketHolder.date.formatAsShortDateString() + return ticketHolder.date.formatAsShortDateString(overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier) } } diff --git a/Trust/Transfer/ViewModels/TransferTicketsViaWalletAddressViewControllerViewModel.swift b/Trust/Transfer/ViewModels/TransferTicketsViaWalletAddressViewControllerViewModel.swift index 5354f0352..e0fbb7151 100644 --- a/Trust/Transfer/ViewModels/TransferTicketsViaWalletAddressViewControllerViewModel.swift +++ b/Trust/Transfer/ViewModels/TransferTicketsViaWalletAddressViewControllerViewModel.swift @@ -61,6 +61,6 @@ struct TransferTicketsViaWalletAddressViewControllerViewModel { } var date: String { - return ticketHolder.date.formatAsShortDateString() + return ticketHolder.date.formatAsShortDateString(overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier) } } diff --git a/TrustTests/Tokens/Helpers/TicketAdaptorTest.swift b/TrustTests/Tokens/Helpers/TicketAdaptorTest.swift index c5a1a0505..bf883c327 100644 --- a/TrustTests/Tokens/Helpers/TicketAdaptorTest.swift +++ b/TrustTests/Tokens/Helpers/TicketAdaptorTest.swift @@ -9,9 +9,9 @@ class TicketAdaptorTest: XCTestCase { func testBundlesAreBrokenIntoContinousSeatRanges() { let date = Date() let tickets = [ - Ticket(id: "1", index: 1, city: "City", name: "Name", venue: "Venue", match: 1, date: date, seatId: 1, category: "1", countryA: "Team A", countryB: "Team B"), - Ticket(id: "2", index: 2, city: "City", name: "Name", venue: "Venue", match: 1, date: date, seatId: 2, category: "1", countryA: "Team A", countryB: "Team B"), - Ticket(id: "3", index: 3, city: "City", name: "Name", venue: "Venue", match: 1, date: date, seatId: 4, category: "1", countryA: "Team A", countryB: "Team B"), + Ticket(id: "1", index: 1, city: "City", name: "Name", venue: "Venue", match: 1, date: date, seatId: 1, category: "1", countryA: "Team A", countryB: "Team B", timeZoneIdentifier: nil), + Ticket(id: "2", index: 2, city: "City", name: "Name", venue: "Venue", match: 1, date: date, seatId: 2, category: "1", countryA: "Team A", countryB: "Team B", timeZoneIdentifier: nil), + Ticket(id: "3", index: 3, city: "City", name: "Name", venue: "Venue", match: 1, date: date, seatId: 4, category: "1", countryA: "Team A", countryB: "Team B", timeZoneIdentifier: nil), ] let bundles = TicketAdaptor(token: TokenObject()).bundle(tickets: tickets) XCTAssertEqual(bundles.count, 2) diff --git a/TrustTests/Transfer/ViewControllers/TransferTicketsQuantitySelectionViewControllerTests.swift b/TrustTests/Transfer/ViewControllers/TransferTicketsQuantitySelectionViewControllerTests.swift index f1955afe5..feab93891 100644 --- a/TrustTests/Transfer/ViewControllers/TransferTicketsQuantitySelectionViewControllerTests.swift +++ b/TrustTests/Transfer/ViewControllers/TransferTicketsQuantitySelectionViewControllerTests.swift @@ -8,7 +8,7 @@ class TransferTicketsQuantitySelectionViewControllerTests: XCTestCase { func testTransferTicketQuantitySelectionViewControllerCanBeCreated() { let token = TokenObject() let type = PaymentFlow.send(type: .stormBird(token)) - let ticket = Ticket(id: "1", index: 1, city: "", name: "", venue: "", match: 1, date: Date(), seatId: 1, category: "MATCH CLUB", countryA: "", countryB: "") + let ticket = Ticket(id: "1", index: 1, city: "", name: "", venue: "", match: 1, date: Date(), seatId: 1, category: "MATCH CLUB", countryA: "", countryB: "", timeZoneIdentifier: nil) let ticketHolder = TicketHolder(tickets: [ticket], status: .available) let controller = TransferTicketsQuantitySelectionViewController(paymentFlow: type) let viewModel = TransferTicketsQuantitySelectionViewModel(token: TokenObject(), ticketHolder: ticketHolder)