Fix: date and time values were not localized when a different locale is specified using the in-app locale switcher

pull/361/head
Hwee-Boon Yar 7 years ago
parent c3afd9bc9e
commit b105faa43d
  1. 33
      Trust/Extensions/Date.swift
  2. 19
      Trust/Foundation/XMLHandler.swift
  3. 6
      Trust/Market/ViewModels/ImportTicketViewControllerViewModel.swift
  4. 4
      Trust/Redeem/ViewModels/BaseTicketTableViewCellViewModel.swift
  5. 2
      Trust/Redeem/ViewModels/RedeemTicketsQuantitySelectionViewModel.swift
  6. 2
      Trust/Redeem/ViewModels/TicketRedemptionViewModel.swift
  7. 2
      Trust/Sell/ViewModels/EnterSellTicketsPriceQuantityViewControllerViewModel.swift
  8. 1
      Trust/Sell/ViewModels/GenerateSellMagicLinkViewControllerViewModel.swift
  9. 1
      Trust/Sell/ViewModels/GenerateTransferMagicLinkViewControllerViewModel.swift
  10. 2
      Trust/Sell/ViewModels/SetSellTicketsExpiryDateViewControllerViewModel.swift
  11. 1
      Trust/Sell/Views/DateEntryField.swift
  12. 6
      Trust/Sell/Views/TimeEntryField.swift
  13. 1
      Trust/Settings/Types/Constants.swift
  14. 4
      Trust/Tokens/Types/Ticket.swift
  15. 1
      Trust/Tokens/Types/TicketHolder.swift
  16. 2
      Trust/Transfer/ViewModels/ChooseTicketTransferModeViewControllerViewModel.swift
  17. 2
      Trust/Transfer/ViewModels/SetTransferTicketsExpiryDateViewControllerViewModel.swift
  18. 2
      Trust/Transfer/ViewModels/TransferTicketsQuantitySelectionViewModel.swift
  19. 2
      Trust/Transfer/ViewModels/TransferTicketsViaWalletAddressViewControllerViewModel.swift
  20. 6
      TrustTests/Tokens/Helpers/TicketAdaptorTest.swift
  21. 2
      TrustTests/Transfer/ViewControllers/TransferTicketsQuantitySelectionViewControllerTests.swift

@ -11,6 +11,7 @@ import Foundation
public extension Date { public extension Date {
private static var formatsMap: [String: DateFormatter] = [:] private static var formatsMap: [String: DateFormatter] = [:]
private static var formatsMapLocale: String?
public init?(string: String, format: String) { public init?(string: String, format: String) {
let date = Date.formatter(with: format).date(from: string) let date = Date.formatter(with: format).date(from: string)
@ -21,16 +22,30 @@ public extension Date {
return nil return nil
} }
public func format(_ format: String) -> String { public func format(_ format: String, overrideWithTimezoneIdentifier timezoneIdentifier: String? = nil) -> String {
return Date.formatter(with: format).string(from: self) return Date.formatter(with: format, overrideWithTimezoneIdentifier: timezoneIdentifier).string(from: self)
} }
public static func formatter(with format: String) -> DateFormatter { public static func formatter(with format: String, overrideWithTimezoneIdentifier timezoneIdentifier: String? = nil) -> DateFormatter {
var foundFormatter = Date.formatsMap[format] let config = Config()
if config.locale != formatsMapLocale {
formatsMapLocale = config.locale
formatsMap = Dictionary()
}
var foundFormatter = formatsMap[format]
if foundFormatter == nil { if foundFormatter == nil {
foundFormatter = DateFormatter() foundFormatter = DateFormatter()
foundFormatter?.dateFormat = format if let locale = config.locale {
Date.formatsMap[format] = foundFormatter! 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! return foundFormatter!
} }
@ -43,7 +58,7 @@ public extension Date {
return Calendar.current.date(byAdding: .day, value: 1, to: Date())! return Calendar.current.date(byAdding: .day, value: 1, to: Date())!
} }
public func formatAsShortDateString() -> String { public func formatAsShortDateString(overrideWithTimezoneIdentifier timezoneIdentifier: String? = nil) -> String {
return format("dd MMM yyyy") return format("dd MMM yyyy", overrideWithTimezoneIdentifier: timezoneIdentifier)
} }
} }

@ -14,18 +14,6 @@ public class XMLHandler {
private let xml = try! XML.parse(AssetDefinitionXML().assetDefinitionString) 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 //TODO remove once parser is properly dynamic
public static func parseTicket(ticket: String) -> String 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 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 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 let number = Int(tokenHex.substring(with: Range(uncheckedBounds: (28, 32))), radix: 16) ?? 0
//TODO derive/extract from XML
let timeZoneIdentifier = Constants.eventTimeZone
return Ticket( return Ticket(
id: MarketQueueHandler.bytesToHexa(tokenId.serialize().array), id: MarketQueueHandler.bytesToHexa(tokenId.serialize().array),
@ -66,11 +56,12 @@ public class XMLHandler {
name: getName(lang: lang), name: getName(lang: lang),
venue: venue, venue: venue,
match: match, match: match,
date: formatDateToMoscow(time), date: Date(timeIntervalSince1970: TimeInterval(time)),
seatId: number, seatId: number,
category: getCategory(category, lang: lang), category: getCategory(category, lang: lang),
countryA: countryAString, countryA: countryAString,
countryB: countryBString countryB: countryBString,
timeZoneIdentifier: timeZoneIdentifier
) )
} }

@ -98,8 +98,7 @@ struct ImportTicketViewControllerViewModel {
if case let .validating = state { if case let .validating = state {
return "" return ""
} else { } else {
//TODO Should format be localized? return ticketHolder.date.format("hh:mm", overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier)
return ticketHolder.date.format("hh:mm")
} }
} }
@ -131,8 +130,7 @@ struct ImportTicketViewControllerViewModel {
if case let .validating = state { if case let .validating = state {
return "" return ""
} else { } else {
//TODO Should format be localized? return ticketHolder.date.format("dd MMM yyyy", overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier)
return ticketHolder.date.format("dd MMM yyyy")
} }
} }

@ -33,7 +33,7 @@ struct BaseTicketTableViewCellViewModel {
var time: String { var time: String {
//TODO Should format be localized? //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 { var teams: String {
@ -49,7 +49,7 @@ struct BaseTicketTableViewCellViewModel {
} }
var date: String { var date: String {
return ticketHolder.date.formatAsShortDateString() return ticketHolder.date.formatAsShortDateString(overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier)
} }
var backgroundColor: UIColor { var backgroundColor: UIColor {

@ -87,6 +87,6 @@ struct RedeemTicketsQuantitySelectionViewModel {
} }
var date: String { var date: String {
return ticketHolder.date.formatAsShortDateString() return ticketHolder.date.formatAsShortDateString(overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier)
} }
} }

@ -62,6 +62,6 @@ struct TicketRedemptionViewModel {
} }
var date: String { var date: String {
return ticketHolder.date.formatAsShortDateString() return ticketHolder.date.formatAsShortDateString(overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier)
} }
} }

@ -83,7 +83,7 @@ struct EnterSellTicketsPriceQuantityViewControllerViewModel {
} }
var date: String { var date: String {
return ticketHolder.date.formatAsShortDateString() return ticketHolder.date.formatAsShortDateString(overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier)
} }
var pricePerTicketLabelText: String { var pricePerTicketLabelText: String {

@ -58,7 +58,6 @@ struct GenerateSellMagicLinkViewControllerViewModel {
} }
var descriptionLabelText: String { var descriptionLabelText: String {
//TODO Should format be localized?
return R.string.localizable.aWalletTicketTokenSellConfirmExpiryDateDescription(linkExpiryDate.format("dd MMM yyyy hh:mm")) return R.string.localizable.aWalletTicketTokenSellConfirmExpiryDateDescription(linkExpiryDate.format("dd MMM yyyy hh:mm"))
} }

@ -57,7 +57,6 @@ struct GenerateTransferMagicLinkViewControllerViewModel {
} }
var descriptionLabelText: String { var descriptionLabelText: String {
//TODO Should format be localized?
return R.string.localizable.aWalletTicketTokenSellConfirmExpiryDateDescription(linkExpiryDate.format("dd MMM yyyy hh:mm")) return R.string.localizable.aWalletTicketTokenSellConfirmExpiryDateDescription(linkExpiryDate.format("dd MMM yyyy hh:mm"))
} }

@ -70,7 +70,7 @@ struct SetSellTicketsExpiryDateViewControllerViewModel {
} }
var date: String { var date: String {
return ticketHolder.date.formatAsShortDateString() return ticketHolder.date.formatAsShortDateString(overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier)
} }
var linkExpiryDateLabelText: String { var linkExpiryDateLabelText: String {

@ -87,7 +87,6 @@ class DateEntryField: UIControl {
} }
private func displayDateString() { private func displayDateString() {
//TODO Should format be localized?
let dateString = value.format("dd MMM yyyy") let dateString = value.format("dd MMM yyyy")
leftButton.setTitle(dateString, for: .normal) leftButton.setTitle(dateString, for: .normal)
} }

@ -87,10 +87,6 @@ class TimeEntryField: UIControl {
} }
private func displayTimeString() { private func displayTimeString() {
//TODO Should format be localized? leftButton.setTitle(value.format("hh:mm"), for: .normal)
let formatter = DateFormatter()
formatter.timeStyle = .short
let timeString = formatter.string(from: value)
leftButton.setTitle(timeString, for: .normal)
} }
} }

@ -33,6 +33,7 @@ public struct Constants {
public static let nullTicket = "0x0000000000000000000000000000000000000000000000000000000000000000" public static let nullTicket = "0x0000000000000000000000000000000000000000000000000000000000000000"
public static let burnAddressString = "0x000000000000000000000000000000000000dEaD" public static let burnAddressString = "0x000000000000000000000000000000000000dEaD"
public static let event = "FIFA WC2018" public static let event = "FIFA WC2018"
public static let eventTimeZone = "Europe/Moscow"
} }
public struct UnitConfiguration { public struct UnitConfiguration {

@ -20,6 +20,7 @@ struct Ticket {
let category: String let category: String
let countryA: String let countryA: String
let countryB: String let countryB: String
var timeZoneIdentifier: String?
static var empty: Ticket { static var empty: Ticket {
return Ticket( return Ticket(
id: Constants.nullTicket, id: Constants.nullTicket,
@ -32,7 +33,8 @@ struct Ticket {
seatId: 0, seatId: 0,
category: "N/A", category: "N/A",
countryA: "N/A", countryA: "N/A",
countryB: "N/A" countryB: "N/A",
timeZoneIdentifier: nil
) )
} }
} }

@ -22,6 +22,7 @@ class TicketHolder {
var category: String { return tickets[0].category } var category: String { return tickets[0].category }
var countryA: String { return tickets[0].countryA } var countryA: String { return tickets[0].countryA }
var countryB: String { return tickets[0].countryB } var countryB: String { return tickets[0].countryB }
var timeZoneIdentifier: String? { return tickets[0].timeZoneIdentifier }
var status: TicketHolderStatus var status: TicketHolderStatus
var isSelected = false var isSelected = false
var areDetailsVisible = false var areDetailsVisible = false

@ -65,6 +65,6 @@ struct ChooseTicketTransferModeViewControllerViewModel {
} }
var date: String { var date: String {
return ticketHolder.date.formatAsShortDateString() return ticketHolder.date.formatAsShortDateString(overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier)
} }
} }

@ -61,7 +61,7 @@ struct SetTransferTicketsExpiryDateViewControllerViewModel {
} }
var date: String { var date: String {
return ticketHolder.date.formatAsShortDateString() return ticketHolder.date.formatAsShortDateString(overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier)
} }
var descriptionLabelText: String { var descriptionLabelText: String {

@ -81,6 +81,6 @@ struct TransferTicketsQuantitySelectionViewModel {
} }
var date: String { var date: String {
return ticketHolder.date.formatAsShortDateString() return ticketHolder.date.formatAsShortDateString(overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier)
} }
} }

@ -61,6 +61,6 @@ struct TransferTicketsViaWalletAddressViewControllerViewModel {
} }
var date: String { var date: String {
return ticketHolder.date.formatAsShortDateString() return ticketHolder.date.formatAsShortDateString(overrideWithTimezoneIdentifier: ticketHolder.timeZoneIdentifier)
} }
} }

@ -9,9 +9,9 @@ class TicketAdaptorTest: XCTestCase {
func testBundlesAreBrokenIntoContinousSeatRanges() { func testBundlesAreBrokenIntoContinousSeatRanges() {
let date = Date() let date = Date()
let tickets = [ 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: "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"), 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"), 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) let bundles = TicketAdaptor(token: TokenObject()).bundle(tickets: tickets)
XCTAssertEqual(bundles.count, 2) XCTAssertEqual(bundles.count, 2)

@ -8,7 +8,7 @@ class TransferTicketsQuantitySelectionViewControllerTests: XCTestCase {
func testTransferTicketQuantitySelectionViewControllerCanBeCreated() { func testTransferTicketQuantitySelectionViewControllerCanBeCreated() {
let token = TokenObject() let token = TokenObject()
let type = PaymentFlow.send(type: .stormBird(token)) 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 ticketHolder = TicketHolder(tickets: [ticket], status: .available)
let controller = TransferTicketsQuantitySelectionViewController(paymentFlow: type) let controller = TransferTicketsQuantitySelectionViewController(paymentFlow: type)
let viewModel = TransferTicketsQuantitySelectionViewModel(token: TokenObject(), ticketHolder: ticketHolder) let viewModel = TransferTicketsQuantitySelectionViewModel(token: TokenObject(), ticketHolder: ticketHolder)

Loading…
Cancel
Save