Swift Date
Date <-> Formatter <-> String
Date
Date:
- single point in time (UTC). It means that it doesn't contain any TimeZone or other metadata
- Is a kind of wrapper of
Double
- seconds from 2001-01-01 00:00:00 +0000
Date(timeIntervalSinceReferenceDate: .zero)
- print output of Date in UTC(+0000) is in ISO 8601(
ISO8601DateFormatter
yyyy-MM-dd'T'HH:mm:ssZ
)
Create Date:
- current time in UTC
Date()
- from String using Formatter and TimeZone
func strToDateWithLocalTimeZone(_ str: String, format: String) -> Date {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
// dateFormatter.timeZone = .current //by default
// dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
return dateFormatter.date(from: str)!
}
Formatter
It is responsible for format date and have two main functions:
- Date -> String:
.string(from: Date)
- String -> Date:
.date(from: String)
Main parts:
String
- If String doesn't contain TimeZone you are able to set TimeZone in Formatter
- When String contains TimeZone - it means that it is a single variant of interpret the String in Date UTC that is why
Formatter.TimeZone
will not have any effect
String <date_with_time_zone> -> Format <any_time_zome> -> single Date
String: 09.04.2023T18:58:32+0300 -> Format <any_time_zome> -> Date: 2023-04-09 15:58:32 +0000
Experiments:
My Local time:
let dateFormatStr = "dd.MM.yyyy'T'HH:mm:ssZ"
//Current TimeZone
let currentTimeZone = TimeZone.current
let seconds = currentTimeZone.secondsFromGMT()
let hours = seconds/3600
let minutes = abs(seconds/60) % 60
print("current TimeZone: \(String(format: "%+.2d:%.2d", hours, minutes)) for:\(currentTimeZone)")
// current TimeZone: +03:00 for:Europe/Kiev (fixed (equal to current))
//Current Date
let currentDate = Date()
print("current Date UTC: \(currentDate)")
// current Date UTC: 2023-04-09 15:58:32 +0000
//Date -> String
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = dateFormatStr
dateFormatter.timeZone = .current //by default. Adds +0300 in my case at the end of string
let currentDateStr = dateFormatter.string(from: currentDate)
print("current DateString Local: \(currentDateStr)")
// current DateString Local: 09.04.2023T18:58:32+0300
//String to Date
let dateFormatterLocal = DateFormatter()
dateFormatterLocal.locale = Locale(identifier: "en_US_POSIX")
dateFormatterLocal.dateFormat = dateFormatStr
dateFormatterLocal.timeZone = .current //by default. Doesn't have effect when String contains TimeZone
let dateLocal = dateFormatterLocal.date(from: currentDateStr)!
print("current Date Local: \(dateLocal)")
// current Date Local: 2023-04-09 15:58:32 +0000
Output in a single place
current TimeZone: +03:00 for:Europe/Kiev (fixed (equal to current))
current Date UTC: 2023-04-09 15:58:32 +0000
current DateString Local: 09.04.2023T18:58:32+0300
current Date Local: 2023-04-09 15:58:32 +0000
Some real use case:
- Client make a transaction
- Send Date to Server as String
- Get Date from Server as String
- Show Date as String in:
- UTC
- Local TimeZone
- Transaction TimeZone
1. Client make a transaction
Date()
Formatter.timeZone = .current
String: 09.04.2023 18:58:32+0300
2. Send Date to Server as String
09.04.2023 18:58:32+0300
3. Get Date from Server as String
09.04.2023 18:58:32+0300
4. Show Date
- UTC TimeZone
String -> Date -> Formatter.timeZone = UTC -> String
UTC: 09.04.2023 15:58:32+0000
- Local TimeZone(Depends where you are)
String -> Date -> Formatter.timeZone = .current -> String
For example Local timezone will be different:
if you now at +0100 TimeZone 09.04.2023 16:58:32+0100
if you now at +0200 TimeZone 09.04.2023 17:58:32+0200
- Transaction TimeZone
remove +0300 from String. It is a workaround, and not a clear solution but as is: 09.04.2023 18:58:32
String -> Formatter.timeZone = UTC -> Date: 09.04.2023 18:58:32+0000
Date -> Formatter.timeZone = UTC -> String: 09.04.2023 18:58:32+0000