How to convert timezone format in iOS? - ios

I got timezone format like this  GMT+5:30.
TimeZone.current.abbreviation(), this will return string value like: //GMT+5:30
 
But I need to convert the above format to Asia/Kolkata
How to solve this issue?

Instead of calling:
TimeZone.current.abbreviation()
call:
TimeZone.current.identifier
In your case you will get Asia/Kolkata instead of GMT+5:30.
But let's assume you only have a string with a timezone abbreviation such as "GMT+5:30". You can't easily convert that to a specific timezone identifier because there can be more than one timezone at a given time offset.
Here's a little function that creates a timezone from the abbreviation string and then finds all matching timezone identifiers that have the same offset.
func matchingTimeZones(abbreviation: String) -> [TimeZone]? {
if let tz = TimeZone(abbreviation: tzstr) {
return TimeZone.knownTimeZoneIdentifiers.compactMap { TimeZone(identifier: $0) }.filter { $0.secondsFromGMT() == tz.secondsFromGMT() }
} else {
return nil
}
}
You can get the matching list for "GMT+5:30" with:
let matches = matchingTimeZones(abbreviation: "GMT+5:30")
If you print that result you will see one of them is "Asia/Calcutta" (in an English locale).

Related

ISO8601DateFormatter returning nil for converting from valid string to date

I have an ISO8601 String that I want to convert to a Date object, yet I keep getting a nil even though it's a valid string. Here's my code:
let isoFormatter = ISO8601DateFormatter()
isoFormatter.formatOptions = [
.withFractionalSeconds,
.withInternetDateTime,
.withFractionalSeconds,
.withColonSeparatorInTime,
.withDashSeparatorInDate
]
let date = isoFormatter.date(from: self)
My string is 2020-04-10T21:13:40.880000, same thing happens with 2020-04-10T21:13:40 (no miliseconds)
.withInternetDateTime is a combination of withFullDate, withFullTime, withDashSeparatorInDate, withColonSeparatorInTime and withColonSeparatorInTimeZone. The option requires a time zone in the string and works with "2020-04-10T21:13:40.880000Z", note the trailing Z.
The options withDashSeparatorInDate, withColonSeparatorInTime are redundant in your code (besides the duplicate withFractionalSeconds).
To omit the time zone you have to replace withInternetDateTime with withFullDate and withTime
let isoFormatter = ISO8601DateFormatter()
isoFormatter.formatOptions = [
.withFractionalSeconds,
.withFullDate,
.withTime, // without time zone
.withColonSeparatorInTime,
.withDashSeparatorInDate
]

iOS TimeZone get three letter abbreviation

I need to get three/four letter abbreviation for the country called 'Africa/Nairobi', I have tried using
print(TimeZone(abbreviation: "SAST")?.identifier) from the NSTimeZone.abbreviationDictionary, results nil.
How to get the three/four letter abbreviations for this country 'Africa/Nairobi'. Thanks in Advance
You can get a full standard localized name of timezone by the following method.
let timezone:TimeZone = TimeZone.init(identifier: "Africa/Nairobi") ?? TimeZone.current
print(timezone.localizedName(for: .generic, locale: .autoupdatingCurrent))
print(timezone.localizedName(for: .standard, locale: .autoupdatingCurrent))
Will give you following output
Optional("East Africa Time")
Optional("East Africa Time")
I think now you get easily get EAT form East Africa Time by spliting and combining string
let fullName = timezone.localizedName(for: .standard, locale: .autoupdatingCurrent) ?? ""
var result = ""
fullName.enumerateSubstrings(in: fullName.startIndex..<fullName.endIndex, options: .byWords) { (substring, _, _, _) in
if let substring = substring { result += substring.prefix(1) }
}
print(result)
Will give you out put
EAT

trying to convert a string to a decimal for iOS 13

I have an app that this was all working correctly before iOS 13. I've checked a few posts but they seem to be saying what I have already done.
I'm passing a string that has currency symbols and formatting, and I want to strip that and use the string value.
func changeCurrencyToDecimal(stringNumber:String) -> Decimal {
let numberFormatter = NumberFormatter()
// Pull apart the components of the user's locale
var locComps = Locale.components(fromIdentifier: Locale.current.identifier)
// Set the specific currency code
locComps[NSLocale.Key.currencyCode.rawValue] = options?.currencyCode // or any other specific currency code
// Get the updated locale identifier
let locId = Locale.identifier(fromComponents: locComps)
// Get the new custom locale
//numberFormatter.locale = Locale(identifier: locId)
if(options?.currencyCode == nil) {
print("There is no currency code so use the default locale")
numberFormatter.locale = Locale.current
}
else{
print("We have a currency code!")
numberFormatter.locale = Locale(identifier: locId)
}
numberFormatter.numberStyle = .currency
numberFormatter.currencySymbol = ""
numberFormatter.decimalSeparator = ","
print("\(stringNumber) is the number being passed")
let number = numberFormatter.number(from: stringNumber)
// check to see if the number is nil and if so, return 0.
print("\(number) is the number converted")
if number == nil{
return 0
}
else{
print("\(number!) is the number")
let amount = number?.decimalValue
return amount!
}
}
An example of a string that I am passing: $300.00 the $ always triggers a return of nil. If I pass 300.00, then the converter works just fine. But I need to be able to check for any currency the user has set for their device.
The currency codes that I am checking are the ones Apple supplies in the var currencyCode: String? { get } options is just where I am storing those codes.
The numberFormatter produces nil every time, and it seems because my decimal is not being stripped of its formatting.
Again this worked before iOS 13, so I am guessing something changed on Apple's side and I just might not have come acrossed it yet.
UPDATE
Here is a user seniaro. The user enters an amount. If the user hits save right away, I take the amount and convert it into a decimal to save in coredata. But if the user dismisses the keyboard, I take that amount and convert it into a string to display the currency symbol. I allow the user to set their currency, so using the locale doesn't work for me, as sometimes they are not using that system. So after the keyboard is dismissed and their amount is displayed with the correct formatting, I have it programmed that if they would happen to change their amount and once again dismiss the keyboard, and the same steps are repeated. The currency symbols are stripped and the string is converted into a decimal.
I hope this better gives you an idea of how this works in my app.
UPDATE
I've added an if/else statement to see if the locale has been set or if it comes back nil, and if so to set it to the Locale.current
Assuming the input string is using the device's current locale you can create a NumberFormatter with Locale.current and set the numberStyle to currency.
I used a small playground to test it:
import Foundation
func changeCurrencyToDecimal(_ stringNumber: String) -> Decimal? {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .currency
let number = numberFormatter.number(from: stringNumber)
return number?.decimalValue
}
let numbersFormatted = ["$300", "$3,000.04", "3.000,04", "Wazaa", "3000000"]
numbersFormatted
.map { changeCurrencyToDecimal($0) }
.forEach { print($0 ?? "Not a value") }
This prints:
300
3000.04
Not a value
Not a value
3000000
If you keep getting nil as a result your device's locale is different as the sample string number you are using in the question ($300).

How to get full display name for locale

I want to show "繁體中文", "简体中文" in a view.
I use Locale.displayname to get displayname and my parameter is "zh-Hant" and "zh-Hans", the value will return "中文(繁體)"and "中文(简体)".
Here is parts of my code:
let loacleName = locale.displayName(forKey: NSLocale.Key.identifier, value: "zh-Hant")
Is anyone know how can I get "繁體中文", "简体中文" from iOS function?
This is a solution using native Locale. For the identifiers zh-Hant and zh-Hans it removes the unwanted characters with Regular Expression (be aware that the characters are not parentheses and space characters) and swaps both pairs.
extension Locale {
var localizedFullDisplayName : String? {
if self.identifier.hasPrefix("zh-Han") {
guard let trimmed = self.localizedString(forIdentifier: self.identifier)?.replacingOccurrences(of: "[()]", with: "", options: .regularExpression) else { return nil }
return String(trimmed.suffix(2) + trimmed.prefix(2))
} else {
return self.localizedString(forIdentifier: locale.identifier)
}
}
}
let locale = Locale(identifier: "zh-Hans")
locale.localizedFullDisplayName

Using Swift how to convert a string to a number

I am getting values back from a web service that gives me back prices in a string format, this is put into a Dictionary, so I get prices back as "1.5000" for example, which is obviously 1.50 in currency. However for the life of me I cannot get anything to work in Swift to format this correctly. In most other languages you can do this in a couple of seconds, so I'm getting a bit frustrated with something that is so simple.
Here's my test code:
var testnumber = "1.5000"
let n = NSNumberFormatter()
n.numberStyle = NSNumberFormatterStyle.DecimalStyle
n.maximumFractionDigits = 2
n.minimumFractionDigits = 2
let returnNumber = n.numberFromString(testnumber)
println("Returned number is \(returnNumber)")
This prints out in debug "number is Optional(1.5)" not 1.50!
I have changed NSNumberFormatterStyle.DecimalStyle to NSNumberFormatterStyle.CurrencyStyle as I thought that may do it for me as the returned number is a currency anyway, but that gives me back in debug "Returned number is nil" - which is even more confusing to me!
I have tried using maximumIntegerDigits and minimumIntegerDigits, setting locales using n.locale = NSLocale.currentLocale(), setting formatWidth, setting paddingPosition and paddingCharacter but nothing helps, I either get nil back to 1.5.
All I ultimately need to do is convert a string to a float or a currency value, and ensure there are 2 decimal places, and I can't believe it's this hard to accomplish!
Any help would be very gratefully received.
You are printing a number not a string
Xcode 11.4 • Swift 5.2 or later
extension Formatter {
static let usCurrency: NumberFormatter = {
let formatter = NumberFormatter()
formatter.locale = .init(identifier: "en_US")
formatter.numberStyle = .currency
return formatter
}()
}
extension String {
var double: Double? { Double(self) }
var usCurrencyFormatted: String {
Formatter.usCurrency.string(for: double) ?? Formatter.usCurrency.string(for: 0) ?? ""
}
}
"1.1222".usCurrencyFormatted // "$1.12"
"2".usCurrencyFormatted // "$2.00"
The problem is about numberFromString returning an optional - so you have to unwrap before printing. Just to be safe, you can use optional binding:
if let returnNumber = n.numberFromString(testnumber) {
println("Returned number is \(returnNumber)")
}
otherwise if it's ok for the app to crash if the optional is nil (in some cases this is a wanted behavior if the optional is expected to always contain a non nil value) just use forced unwrapping:
let returnNumber = n.numberFromString(testnumber)!
println("Returned number is \(returnNumber)")
That fixes the unwanted "Optional(xx)" text. As for formatting a float/double number, there are probably several ways of doing it - the one I would use is c-like string formatting, available via NSString:
let formattedNumber = NSString(format: "%.2f", returnNumber)
println("Returned number is \(formattedNumber)")
Use String Format Specifiers as reference if you want to know more about format specifiers.
You could probably just use the NSNumberFormatter that you just created.
let returnNumber = n.stringFromNumber(n.numberFromString(testnumber))
returnNumber will now be of type String.
The following returns to 2 decimal places for me in playgrounds. May be of some help to you. Uses NSNumberFormatter and then unwraps the optional
let testnumber: String = "1.50000"
let numberFormatter = NSNumberFormatter()
let number = numberFormatter.numberFromString(testnumber)
if let final = number?.floatValue {
println("Returned number is " + String(format: "%.2f", final))
}

Resources