I am taking the current time, in UTC, and putting it in nanaoseconds and then I need to take the nanoseconds and go back to a date in local time.
I am able to do get the time to nanoseconds and then back to a date string but the time gets convoluted when I go from a string to date.
//Date to milliseconds
func currentTimeInMiliseconds() -> Int! {
let currentDate = NSDate()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format
dateFormatter.timeZone = NSTimeZone(name: "UTC") as TimeZone!
let date = dateFormatter.date(from: dateFormatter.string(from: currentDate as Date))
let nowDouble = date!.timeIntervalSince1970
return Int(nowDouble*1000)
}
//Milliseconds to date
extension Int {
func dateFromMilliseconds(format:String) -> Date {
let date : NSDate! = NSDate(timeIntervalSince1970:Double(self) / 1000.0)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format
dateFormatter.timeZone = TimeZone.current
let timeStamp = dateFormatter.string(from: date as Date)
let formatter = DateFormatter()
formatter.dateFormat = format
return ( formatter.date( from: timeStamp ) )!
}
}
The timestamp is correct but the date returned isn't.
I don't understand why you're doing anything with strings...
extension Date {
var millisecondsSince1970:Int64 {
Int64((self.timeIntervalSince1970 * 1000.0).rounded())
}
init(milliseconds:Int64) {
self = Date(timeIntervalSince1970: TimeInterval(milliseconds) / 1000)
}
}
Date().millisecondsSince1970 // 1476889390939
Date(milliseconds: 0) // "Dec 31, 1969, 4:00 PM" (PDT variant of 1970 UTC)
As #Travis Solution works but in some cases
var millisecondsSince1970:Int WILL CAUSE CRASH APPLICATION ,
with error
Double value cannot be converted to Int because the result would be greater than Int.max if it occurs Please update your answer with Int64
Here is Updated Answer
extension Date {
var millisecondsSince1970:Int64 {
return Int64((self.timeIntervalSince1970 * 1000.0).rounded())
//RESOLVED CRASH HERE
}
init(milliseconds:Int) {
self = Date(timeIntervalSince1970: TimeInterval(milliseconds / 1000))
}
}
About Int definitions.
On 32-bit platforms, Int is the same size as Int32, and on 64-bit platforms, Int is the same size as Int64.
Generally, I encounter this problem in iPhone 5, which runs in 32-bit env. New devices run 64-bit env now. Their Int will be Int64.
Hope it is helpful to someone who also has same problem
#Travis solution is right, but it loses milliseconds when a Date is generated. I have added a line to include the milliseconds into the date:
If you don't need this precision, use the Travis solution because it will be faster.
extension Date {
func toMillis() -> Int64! {
return Int64(self.timeIntervalSince1970 * 1000)
}
init(millis: Int64) {
self = Date(timeIntervalSince1970: TimeInterval(millis / 1000))
self.addTimeInterval(TimeInterval(Double(millis % 1000) / 1000 ))
}
}
//Date to milliseconds
func currentTimeInMiliseconds() -> Int {
let currentDate = Date()
let since1970 = currentDate.timeIntervalSince1970
return Int(since1970 * 1000)
}
//Milliseconds to date
extension Int {
func dateFromMilliseconds() -> Date {
return Date(timeIntervalSince1970: TimeInterval(self)/1000)
}
}
I removed seemingly useless conversion via string and all those random !.
let dateTimeStamp = NSDate(timeIntervalSince1970:Double(currentTimeInMiliseconds())/1000) //UTC time //YOUR currentTimeInMiliseconds METHOD
let dateFormatter = NSDateFormatter()
dateFormatter.timeZone = NSTimeZone.localTimeZone()
dateFormatter.dateFormat = "yyyy-MM-dd"
dateFormatter.dateStyle = NSDateFormatterStyle.FullStyle
dateFormatter.timeStyle = NSDateFormatterStyle.ShortStyle
let strDateSelect = dateFormatter.stringFromDate(dateTimeStamp)
print("Local Time", strDateSelect) //Local time
let dateFormatter2 = NSDateFormatter()
dateFormatter2.timeZone = NSTimeZone(name: "UTC") as NSTimeZone!
dateFormatter2.dateFormat = "yyyy-MM-dd"
let date3 = dateFormatter.dateFromString(strDateSelect)
print("DATE",date3)
#Prashant Tukadiya answer works. But if you want to save the value in UserDefaults and then compare it to other date you get yout int64 truncated so it can cause problems. I found a solution.
Swift 4:
You can save int64 as string in UserDefaults:
let value: String(Date().millisecondsSince1970)
let stringValue = String(value)
UserDefaults.standard.set(stringValue, forKey: "int64String")
Like that you avoid Int truncation.
And then you can recover the original value:
let int64String = UserDefaults.standard.string(forKey: "int64String")
let originalValue = Int64(int64String!)
This allow you to compare it with other date values:
let currentTime = Date().millisecondsSince1970
let int64String = UserDefaults.standard.string(forKey: "int64String")
let originalValue = Int64(int64String!) ?? 0
if currentTime < originalValue {
return false
} else {
return true
}
Hope this helps someone who has same problem
Heres a simple solution in Swift 5/iOS 13.
extension Date {
func toMilliseconds() -> Int64 {
Int64(self.timeIntervalSince1970 * 1000)
}
init(milliseconds:Int) {
self = Date().advanced(by: TimeInterval(integerLiteral: Int64(milliseconds / 1000)))
}
}
This however assumes you have calculated the difference between UTF time and local time and adjusted and accounted for in the milliseconds. For that look to calendar
var cal = Calendar.current
cal.timeZone = TimeZone(abbreviation: "UTC")!
let difference = cal.compare(dateGiven, to: date, toGranularity: .nanosecond)
Simple one-line code to get time token in UInt64
let time = UInt64(Date().timeIntervalSince1970 * 1000)
print(time) <----- prints time in UInt64
Additional tip:
For timestamp with 10 Digit milliseconds since 1970 for API call then
let timeStamp = Date().timeIntervalSince1970
print(timeStamp) <-- prints current time stamp
Watch out if you are going to compare dates after the conversion!
For instance, I got simulator's asset with date as TimeInterval(366144731.9), converted to milliseconds Int64(1344451931900) and back to TimeInterval(366144731.9000001), using
func convertToMilli(timeIntervalSince1970: TimeInterval) -> Int64 {
return Int64(timeIntervalSince1970 * 1000)
}
func convertMilliToDate(milliseconds: Int64) -> Date {
return Date(timeIntervalSince1970: (TimeInterval(milliseconds) / 1000))
}
I tried to fetch the asset by creationDate and it doesn't find the asset, as you could figure, the numbers are not the same.
I tried multiple solutions to reduce double's decimal precision, like round(interval*1000)/1000, use NSDecimalNumber, etc... with no success.
I ended up fetching by interval -1 < creationDate < interval + 1, instead of creationDate == Interval.
There may be a better solution!?
Unless you absolutely have to convert the date to an integer, consider using a Double instead to represent the time interval. After all, this is the type that timeIntervalSince1970 returns. All of the answers that convert to integers loose sub-millisecond precision, but this solution is much more accurate (although you will still lose some precision due to floating-point imprecision).
public extension Date {
/// The interval, in milliseconds, between the date value and
/// 00:00:00 UTC on 1 January 1970.
/// Equivalent to `self.timeIntervalSince1970 * 1000`.
var millisecondsSince1970: Double {
return self.timeIntervalSince1970 * 1000
}
/**
Creates a date value initialized relative to 00:00:00 UTC
on 1 January 1970 by a given number of **milliseconds**.
equivalent to
```
self.init(timeIntervalSince1970: TimeInterval(milliseconds) / 1000)
```
- Parameter millisecondsSince1970: A time interval in milliseconds.
*/
init(millisecondsSince1970 milliseconds: Double) {
self.init(timeIntervalSince1970: TimeInterval(milliseconds) / 1000)
}
}
Related
How can I get local date and time in Swift?
let last_login = String(NSDate.date())
update: Xcode 8.2.1 • Swift 3.0.2
You can also use the Date method description(with locale: Locale?) to get user's localized time description:
A string representation of the Date, using the given locale, or if the locale
argument is nil, in the international format YYYY-MM-DD HH:MM:SS
±HHMM, where ±HHMM represents the time zone offset in hours and
minutes from UTC (for example, “2001-03-24 10:45:32 +0600”).
description(with locale: Locale?)
Date().description(with: .current) // "Monday, February 9, 2015 at 05:47:51 Brasilia Summer Time"
The method above it is not meant to use when displaying date and time to the user. It is for debugging purposes only.
When displaying local date and time (current timezone) to the user you should respect the users locale and device settings. The only thing you can control is the date and time style (short, medium, long or full). Fore more info on that you can check this post shortDateTime.
If your intent is to create a time stamp UTC for encoding purposes (iso8601) you can check this post iso8601
In case you want to get a Date object and not a string representation you can use the following snippet:
extension Date {
func localDate() -> Date {
let nowUTC = Date()
let timeZoneOffset = Double(TimeZone.current.secondsFromGMT(for: nowUTC))
guard let localDate = Calendar.current.date(byAdding: .second, value: Int(timeZoneOffset), to: nowUTC) else {return Date()}
return localDate
}
}
Use it like this:
let now = Date().localDate()
Leo's answer great. I just wanted to add a way to use it as a computed property.
var currentTime: String {
Date().description(with: .current)
}
Use it like so:
print(currentTime)
Or you can encapsulate it:
extension String {
static var currentTime: String {
Date().description(with: .current)
}
}
And then you can use it anywhere you use a string:
var time: String = .currentTime
use NSDateFormatter, either by setting the format
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "hh:mm"
println(dateFormatter.stringFromDate(NSDate()))
or styles
dateFormatter.dateStyle = .NoStyle
dateFormatter.timeStyle = .MediumStyle
I already found the answer.
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM-dd-yyyy HH:mm"
let dateInFormat = dateFormatter.stringFromDate(NSDate())
let expiryDate: Date = ...
let localizedDateString = DateFormatter.localizedString(from: expiryDate, dateStyle: .medium, timeStyle: .short)
"10 Sep 2017, 14:37"
To get back the most common string formats (when dealing with queries and databases):
Swift 4, 5
2019-01-09T01:07:04Z (RFC3339 in GMT/Zulu time)
let f = ISO8601DateFormatter()
f.formatOptions = [.withInternetDateTime]
let s = f.string(from: Date())
2019-01-08T17:04:16-08:00 (RFC3339 accounting for local time zone)
let f = ISO8601DateFormatter()
f.formatOptions = [.withInternetDateTime]
f.timeZone = TimeZone.current
let s = f.string(from: Date())
2019-01-09 (standard date stamp in GMT/Zulu time)
let f = ISO8601DateFormatter()
f.formatOptions = [.withFullDate, .withDashSeparatorInDate]
let s = f.string(from: Date())
2019-01-08 (standard date stamp accounting for local time zone)
let f = ISO8601DateFormatter()
f.formatOptions = [.withFullDate, .withDashSeparatorInDate]
f.timeZone = TimeZone.current
let s = f.string(from: Date())
All four strings represent the exact same point in time. And remember that sorting these strings in alphabetical order also sorts them into chronological order, which makes this data database agnostic (which I always aim for).
You have to use NSDateFormatter
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MMMM dd, yyyy HH:mm"
dateFormatter.locale = "en" // Change "en" to change the default locale if you want
let stringDate = dateFormatter.stringFromDate(date)
Refactor the answer with swift 5 base on #lajosdeme. My location is in China.
import Foundation
let date = Date() // It is not the local time, less than 8 hours
print(date) // 2022-08-05 08:04:20 +0000
extension Date {
static func localDate() -> Date {
let nowUTC = Date()
let timeZoneOffset = Double(TimeZone.current.secondsFromGMT(for: nowUTC))
guard let localDate = Calendar.current.date(byAdding: .second, value: Int(timeZoneOffset), to: nowUTC) else {
return nowUTC
}
return localDate
}
}
// It is the local time
print(Date.localDate()) // 2022-08-05 16:04:20 +0000
Swift 4
To get current date and time
let currentDate = Date()
print(currentDate) //this will return current date and time
but that will be in date type to convert date into string
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy HH:mm" //give the formate according to your need
let dateStr = dateFormatter.string(from: currentDate) //which will give the string of current date and time in required dateformate
My understanding of Swift Date is that Date is a time point without any calendar or timezone information. I think it is GMT time. If you want to show a date in a specified timezone, you have to use DateFormat API to format the date to a string.
I have an iOS app TapToCount-3W to make notes with date and GPS location information. When I travel, I use it to record/tap a note with date and GPS. The dates are local date when I am in travel countries. However, the problem I found is that when I come back home, the travel dates displayed are in my home country dates instead of those travel country timezones.
I am working on updates with my app now. The solution is to add timezone information when a tap is made. With date and timezone information, the localized dates will be correctly displayed.
The method as recommended in this QA to extend Date is actually to create date from Date() from second offset from GMT time. It is a GMT time and different date from Date().
The following codes are from my updates(I also included #lajosdeme method as comparison):
extension Date {
private func getLocalByID(_ identifier: String?) -> Locale
{
let local: Locale
if let id = identifier, !id.isEmpty {
local = Locale(identifier: id)
} else {
local = Locale.current
}
return local
}
func localizedString(
timezone: String?,
dateStyle: DateFormatter.Style = .short,
timeStyle: DateFormatter.Style = .long
) -> String
{
let dtFormater = DateFormatter()
let tz: String = timezone ?? ""
dtFormater.locale = getLocalByID(tz)
dtFormater.dateStyle = dateStyle
dtFormater.timeStyle = timeStyle
if let timeZone = TimeZone(identifier: tz) {
dtFormater.timeZone = timeZone
}
return dtFormater.string(from: self)
}
func dateForTimezone(_ timezone: String?) -> Date {
let nowUTC = Date()
let tz: TimeZone
if let timezone = timezone,
let v = TimeZone(identifier: timezone)
{
tz = v
} else {
tz = TimeZone.current
}
let timeZoneOffset =
Double(tz.secondsFromGMT(for: nowUTC))
if let dt =
Calendar.current.date(byAdding: .second, value: Int(timeZoneOffset), to: nowUTC)
{
return dt
}
else {
return Date()
}
}
}
// Test above extension in Playground
// [SwiftFiddle][3]
let dt1 = Date()
let tz = "America/Edmonton"
let dt2 = dt1.description(with: .current)
let dt3 = dt1.localizedString(timezone: tz)
let dt4 = dt1.dateForTimezone(tz)
print("Timezone: \(tz)\nDate: \(dt1)\ndescription: \(dt2)\nlocalized string: \(dt3)\ndateForTimezone: \(dt4)")
Here are the test result from SwiftFiddle playground:
Timezone: America/Edmonton
Date: 2022-06-03 15:41:23 +0000
description: Friday, June 3, 2022 at 3:41:23 PM Coordinated Universal Time
localized string: 6/3/22, 9:41:23 AM GMT-6
dateForTimezone: 2022-06-03 09:41:23 +0000
I have to compare the time duration of the recorded audio file against a fixed time duration value of 10 mins (10:00). The duration of the audio file is a string.
Doing string comparison works fine if the format of recorded files duration and the format of the fixed duration is same i.e mm:ss. If the audio file has duration of format hh:mm:ss , it gives false values in certain cases.
Is there any way to handle this?
You can use this String Extension to calculate total minutes from string
extension String {
func getTimeInSeconds()-> Int? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "mm:ss" //Your date format
dateFormatter.timeZone = TimeZone.current //Current time zone
var date = Date()
if let getDate = dateFormatter.date(from: self) {//according to date format your date string
date = getDate //Convert String to Date
} else {
dateFormatter.dateFormat = "hh:mm:ss"
if let getDate = dateFormatter.date(from: self) {//according to date format your date string
date = getDate //Convert String to Date
} else {
return nil
}
}
let calendar = Calendar.current
let components = calendar.dateComponents([.hour, .minute,.second], from: date)
let hour = components.hour ?? 0
let minute = components.minute ?? 0
let seconds = components.second ?? 0
return (hour * 360) + minute*60 + seconds
}
}
You can use this extension like this
let getSeconds = "01:20".getTimeInSeconds()
it will cover both formats mm:ss and hh:mm:ss and return seconds ... you can use these seconds to compare instead of string
I'm trying to make an attendance app and I am really confused about date and time in iOS and Firebase.
I use date as Key, this is the structure of my Firebase database.
--Employees
--Unique_ID
--Details
Name: John
--Attendance
--dateToday
Timein: 8:00 AM
Timeout: 5:00 PM
BreakStart: 12:00 PM
BreakFinish: 1:00 PM
This is my code to get the date timestamp I used as Key
override func viewDidLoad() {
super.viewDidLoad()
let now = NSDate()
let nowTimeStamp = self.getCurrentTimeStampWOMiliseconds(dateToConvert: now)
// I save this dateToday as Key in Firebase
dateToday = nowTimeStamp
}
func getCurrentTimeStampWOMiliseconds(dateToConvert: NSDate) -> String {
let objDateformat: DateFormatter = DateFormatter()
objDateformat.dateFormat = "yyyy-MM-dd"
let strTime: String = objDateformat.string(from: dateToConvert as Date)
let objUTCDate: NSDate = objDateformat.date(from: strTime)! as NSDate
let milliseconds: Int64 = Int64(objUTCDate.timeIntervalSince1970)
let strTimeStamp: String = "\(milliseconds)"
return strTimeStamp
}
But when I convert it back to date I get 2017-09-22 16:00:00 +0000, which is wrong because it is 23rd of September in my location.
What is the right code to use so that I can get the correct date timestamp and time timestamp?
For saving Current time to firebase database I use Unic Epoch Conversation:
let timestamp = NSDate().timeIntervalSince1970
and For Decoding Unix Epoch time to Date().
let myTimeInterval = TimeInterval(timestamp)
let time = NSDate(timeIntervalSince1970: TimeInterval(myTimeInterval))
If you just want the unix timestamp, create an extension:
extension Date {
func currentTimeMillis() -> Int64 {
return Int64(self.timeIntervalSince1970 * 1000)
}
}
Then you can use it just like in other programming languages:
let timestamp = Date().currentTimeMillis()
First I would recommend you to store your timestamp as a NSNumber in your Firebase Database, instead of storing it as a String.
Another thing worth mentioning here, is that if you want to manipulate dates with Swift, you'd better use Date instead of NSDate, except if you're interacting with some Obj-C code in your app.
You can of course use both, but the Documentation states:
Date bridges to the NSDate class. You can use these interchangeably in
code that interacts with Objective-C APIs.
Now to answer your question, I think the problem here is because of the timezone.
For example if you print(Date()), as for now, you would get:
2017-09-23 06:59:34 +0000
This is the Greenwich Mean Time (GMT).
So depending on where you are located (or where your users are located) you need to adjust the timezone before (or after, when you try to access the data for example) storing your Date:
let now = Date()
let formatter = DateFormatter()
formatter.timeZone = TimeZone.current
formatter.dateFormat = "yyyy-MM-dd HH:mm"
let dateString = formatter.string(from: now)
Then you have your properly formatted String, reflecting the current time at your location, and you're free to do whatever you want with it :) (convert it to a Date / NSNumber, or store it directly as a String in the database..)
in Swift 5
extension Date {
static var currentTimeStamp: Int64{
return Int64(Date().timeIntervalSince1970 * 1000)
}
}
call like this:
let timeStamp = Date.currentTimeStamp
print(timeStamp)
Thanks #lenooh
The simple way to create Current TimeStamp. like below,
func generateCurrentTimeStamp () -> String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy_MM_dd_hh_mm_ss"
return (formatter.string(from: Date()) as NSString) as String
}
you can call like this:
let timeStmp = generateCurrentTimeStamp()
print("time stamp: \(timeStmp)")
If you code for iOS 13.0 or later and want a timestamp, then you can use:
let currentDate = NSDate.now
On expanding #MacacoAzul's answer here is my current working example :
import SwiftUI
struct TimestampDemo: View {
var body: some View {
Text(getActualTimeStamp(1))
.padding(10)
Text(getActualTimeStamp(2))
.padding(10)
Text(getActualTimeStamp(3))
.padding(10)
Text(getActualTimeStamp(4))
.padding(10)
}
func getActualTimeStamp(_ tipo:Int) -> String {
let date = Date()
let formatter = DateFormatter()
if tipo == 1{
formatter.dateFormat = "dd/MM/yyyy"
} else if tipo == 2{
formatter.dateFormat = "yyyy-MM-dd HH:mm"
}else if tipo == 3{
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
}
else if tipo == 4 {
formatter.dateFormat = "dd-MM-yyyy"
}
return formatter.string(from: date)
}
}
struct TimestampDemo_Previews: PreviewProvider {
static var previews: some View {
TimestampDemo()
}
}
Swift Language Version : 5
When we convert a UTC timestamp (2017-11-06 20:15:33 -08:00) into a Date object, the time zone is zeroed out to GMT. For calculating time intervals, this isn't an issue, but it can be for rendering times in the UI.
I favor the RFC3339 format (2017-11-06T20:15:33-08:00) for its universality. The date format in Swift is yyyy-MM-dd'T'HH:mm:ssXXXXX but RFC3339 allows us to take advantage of the ISO8601DateFormatter:
func getDateFromUTC(RFC3339: String) -> Date? {
let formatter = ISO8601DateFormatter()
return formatter.date(from: RFC3339)
}
RFC3339 also makes time-zone extraction simple:
func getTimeZoneFromUTC(RFC3339: String) -> TimeZone? {
switch RFC3339.suffix(6) {
case "+05:30":
return TimeZone(identifier: "Asia/Kolkata")
case "+05:45":
return TimeZone(identifier: "Asia/Kathmandu")
default:
return nil
}
}
There are 37 or so other time zones we'd have to account for and it's up to you to determine which ones, because there is no definitive list. Some standards count fewer time zones, some more. Most time zones break on the hour, some on the half hour, some on 0:45, some on 0:15.
We can combine the two methods above into something like this:
func getFormattedDateFromUTC(RFC3339: String) -> String? {
guard let date = getDateFromUTC(RFC3339: RFC3339),
let timeZone = getTimeZoneFromUTC(RFC3339: RFC3339) else {
return nil
}
let formatter = DateFormatter()
formatter.dateFormat = "h:mma EEE, MMM d yyyy"
formatter.amSymbol = "AM"
formatter.pmSymbol = "PM"
formatter.timeZone = timeZone // preserve local time zone
return formatter.string(from: date)
}
And so the string "2018-11-06T17:00:00+05:45", which represents 5:00PM somewhere in Kathmandu, will print 5:00PM Tue, Nov 6 2018, displaying the local time, regardless of where the machine is.
As an aside, I recommend storing dates as strings remotely (including Firestore which has a native date object) because, I think, remote data should agnostic to create as little friction between servers and clients as possible.
you can even create a function to return different time stamps depending on your necessity:
func dataatual(_ tipo:Int) -> String {
let date = Date()
let formatter = DateFormatter()
if tipo == 1{
formatter.dateFormat = "dd/MM/yyyy"
} else if tipo == 2{
formatter.dateFormat = "yyyy-MM-dd HH:mm"
} else {
formatter.dateFormat = "dd-MM-yyyy"
}
return formatter.string(from: date)
}
I am parsing json using Swifty json. Now i have a key as "create_date" which can have a timestamp & as well as a date string like "2017-08-17 20:00:00".Now i am not sure when it will be a string date or it is a timestamp.I am using below code to parse this
if let timeInterval = dic?["created_at"].doubleValue {
print("timeinterval is \(timeInterval)")
date1 = NSDate(timeIntervalSince1970: (timeInterval / 1000))
date = self.getStrDateFromDate(dob: date1!) as String
}
if use dict["create_date"].doubleValue so if it a string date then it r return me 2017 & in case of timestamp it return some timestamp as 15383673563465 .
Now how do i identify if is a date or a timestamp ?
Use optional binding and cast down the value to Double with the double property of SwiftyJSON. If the downcast succeeds create the string from the Date otherwise use the date string directly.
let dateString : String
if let timeInterval = dic?["created_at"].double {
print("timeinterval is \(timeInterval)")
date1 = Date(timeIntervalSince1970: timeInterval / 1000)
dateString = self.getStrDateFromDate(dob: date1!) as String
} else {
dateString = dic?["created_at"].stringValue
}
Side note:
Why do you (bridge) cast a result to string (from getStrDateFromDate) which is supposed to be a string?
I would try a DateFormatter with the format of your possible date string, then try to convert it to a double. Or vise-versa. The order really doesn't matter too much, although it may be slightly less expensive to check for the double first.
import Foundation
func convertDate(dateString:String) -> Date? {
// try `Double`
if let timestamp = Double(dateString) {
return Date(timeIntervalSince1970: timestamp)
}
// try `DateFormatter`
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
if let date = formatter.date(from: dateString) {
return date
}
// give up
return nil
}
let dateString1 = String(Date().timeIntervalSince1970)
let dateString2 = "2017-08-17 20:00:00"
let dateString3 = "something"
print(convertDate(dateString: dateString1) ?? "unknown")
print(convertDate(dateString: dateString2) ?? "unknown")
print(convertDate(dateString: dateString3) ?? "unknown")
// 2017-08-17 15:15:27 +0000
// 2017-08-18 00:00:00 +0000
// unknown
Note that the default description for a Date uses GMT. In my case it's off by 4 hours, which is why the displayed time is 4 hours different from I entered.
I have a date stored on my online server database which is in GMT. I load the date and convert it to the user's timezone using the following code :
if let messagedate = oneitem["timestamp"] as? String {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = dateFormatter.dateFromString(messagedate)
let source_timezone = NSTimeZone(abbreviation: "GMT")
let local_timezone = NSTimeZone.systemTimeZone()
let source_EDT_offset = source_timezone?.secondsFromGMTForDate(date!)
let destination_EDT_offset = local_timezone.secondsFromGMTForDate(date!)
let time_interval : NSTimeInterval = Double(destination_EDT_offset - source_EDT_offset!)
let final_date = NSDate(timeInterval: time_interval, sinceDate: date!)
curr_item.date = final_date
}
Now I need to convert the date back to GMT in order to communicate it to the server, however I'm not sure how to convert it back to GMT.
Simpler version:
extension Date {
func convertToTimeZone(initTimeZone: TimeZone, timeZone: TimeZone) -> Date {
let delta = TimeInterval(timeZone.secondsFromGMT(for: self) - initTimeZone.secondsFromGMT(for: self))
return addingTimeInterval(delta)
}
}
Couldn't you just use your data formatter again with a different time zone and convert it? Such as
dateFormatter.timeZone = NSTimeZone(abbreviation: "GMT")
let gmtDate = dateFormatter.dateFromString(string: "your old date as string here")
At time of writing, most answers contain an edge case bug near DST switchover times (see my note about other answers below). If you just want to convert a date string with no time offset to a Date in a particular time zone, Amloelxer's answer is best, but for the benefit of those with the question of "how to convert a Date between timezones", there are two cases:
Case 1:
Convert a Date to another time zone while preserving the day and time from the initial time zone.
E.g. for GMT to EST: 2020-03-08T10:00:00Z to 2020-03-08T10:00:00-04:00
Case 2:
Convert a Date to the day and time from another time zone while preserving the initial time zone.
E.g. for EST to GMT: 2020-03-08T06:00:00-04:00 to 2020-03-08T10:00:00-04:00 (because the initial Date is 10am in GMT)
These two cases are actually the same (the example start and end Dates are identical), except they are worded differently to swap which time zone is the "initial" and which is the "target". The two solutions below are therefore equivalent if you swap the time zones between them, so you can choose the one that conceptually fits your use case better.
extension Calendar {
// case 1
func dateBySetting(timeZone: TimeZone, of date: Date) -> Date? {
var components = dateComponents(in: self.timeZone, from: date)
components.timeZone = timeZone
return self.date(from: components)
}
// case 2
func dateBySettingTimeFrom(timeZone: TimeZone, of date: Date) -> Date? {
var components = dateComponents(in: timeZone, from: date)
components.timeZone = self.timeZone
return self.date(from: components)
}
}
// example values
let initTz = TimeZone(abbreviation: "GMT")!
let targetTz = TimeZone(abbreviation: "EST")!
let initDate = Calendar.current.date(from: .init(timeZone: initTz, year: 2020, month: 3, day: 8, hour: 4))!
// usage
var calendar = Calendar.current
calendar.timeZone = initTz
let case1TargetDate = calendar.dateBySetting(timeZone: targetTz, of: initDate)!
let case2TargetDate = calendar.dateBySettingTimeFrom(timeZone: targetTz, of: initDate)!
// print results
let formatter = ISO8601DateFormatter()
formatter.timeZone = targetTz // case 1 is concerned with what the `Date` looks like in the target time zone
print(formatter.string(from: case1TargetDate)) // 2020-03-08T04:00:00-04:00
// for case 2, find the initial `Date`'s time in the target time zone
print(formatter.string(from: initDate)) // 2020-03-07T23:00:00-05:00 (the target date should have this same time)
formatter.timeZone = initTz // case 2 is concerned with what the `Date` looks like in the initial time zone
print(formatter.string(from: case2TargetDate)) // 2020-03-07T23:00:00Z
A note about other answers
At time of writing, most other answers assume one of the two above cases, but more importantly, they share a bug - they attempt to calculate the time difference between the time zones, where the sign of the difference determines the case:
Case 1:
initialTz.secondsFromGMT(for: initialDate) - targetTz.secondsFromGMT(for: initialDate)
Case 2:
targetTz.secondsFromGMT(for: initialDate) - initialTz.secondsFromGMT(for: initialDate)
secondsFromGMT takes the Date for which you want to know the offset, so in both cases the target offset should really be targetTz.secondsFromGMT(for: targetDate), which is a catch-22, since we don't know the target date yet. However, in most cases where the Dates are close, as they are here, targetTz.secondsFromGMT(for: initialDate) and targetTz.secondsFromGMT(for: targetDate) are equal - a bug only occurs when they differ, which happens when the time offset changes between the two Dates in the target time zone, e.g. for DST. Here is a bugged example for each case:
extension Date {
// case 1 (bugged)
func converting(from initTz: TimeZone, to targetTz: TimeZone) -> Date {
return self + Double(initTz.secondsFromGMT(for: self) - targetTz.secondsFromGMT(for: self))
}
// case 2 (bugged)
func convertingTime(from initTz: TimeZone, to targetTz: TimeZone) -> Date {
return self + Double(targetTz.secondsFromGMT(for: self) - initTz.secondsFromGMT(for: self))
}
}
let formatter = ISO8601DateFormatter()
// case 1
do {
// example values
let initTz = TimeZone(abbreviation: "GMT")!
let targetTz = TimeZone(abbreviation: "EST")!
let initDate = Calendar.current.date(from: .init(timeZone: initTz, year: 2020, month: 3, day: 8, hour: 4))!
// usage
let targetDate = initDate.converting(from: initTz, to: targetTz)
// print results
formatter.timeZone = targetTz // case 1 is concerned with what the `Date` looks like in the target time zone
print(formatter.string(from: targetDate)) // 2020-03-08T05:00:00-04:00 (should be 4am)
}
// case 2
do {
// example values
let initTz = TimeZone(abbreviation: "EST")!
let targetTz = TimeZone(abbreviation: "GMT")!
let initDate = Calendar.current.date(from: .init(timeZone: initTz, year: 2020, month: 3, day: 8, hour: 1))!
// usage
let targetDate = initDate.convertingTime(from: initTz, to: targetTz)
// print results
formatter.timeZone = targetTz // for case 2, find the initial `Date`'s time in the target time zone
print(formatter.string(from: initDate)) // 2020-03-08T06:00:00Z (the target date should have this same time)
formatter.timeZone = initTz // case 2 is concerned with what the `Date` looks like in the initial time zone
print(formatter.string(from: targetDate)) // 2020-03-08T07:00:00-04:00 (should be 6am)
}
If you adjust the example dates just a few hours forwards or backwards, the bug does not occur. Calendrical calculations are complex, and attempting to roll your own will almost always result in buggy edge cases. Since a time zone is a calendrical unit, to avoid bugs, you should use the existing Calendar interface, as in my initial example.
runs more effeciently
extension Date {
func convertToLocalTime(fromTimeZone timeZoneAbbreviation: String) -> Date? {
if let timeZone = TimeZone(abbreviation: timeZoneAbbreviation) {
let targetOffset = TimeInterval(timeZone.secondsFromGMT(for: self))
let localOffeset = TimeInterval(TimeZone.autoupdatingCurrent.secondsFromGMT(for: self))
return self.addingTimeInterval(targetOffset - localOffeset)
}
return nil
}
}
Based on mukaissi's answer, but the order of deductible in the expression has been corrected.
extension Date {
func convert(from initTimeZone: TimeZone, to targetTimeZone: TimeZone) -> Date {
let delta = TimeInterval(initTimeZone.secondsFromGMT() - targetTimeZone.secondsFromGMT())
return addingTimeInterval(delta)
}
}
Since NSDate is always in GMT/UTC the time zone only becomes relevant when displaying it to, or getting it from, the user. Just always assume it's UTC internally, convert it for the user (by setting it on the NSDateFormatter) as necessary, and you no longer have to worry about the problem.
So this is mukaissi's answer enhanced with valeCocoa's suggestion for daylight saving time:
func convert(from initTimeZone: TimeZone, to targetTimeZone: TimeZone) -> Date {
let delta = TimeInterval(targetTimeZone.secondsFromGMT(for: self) - initTimeZone.secondsFromGMT(for: self))
return addingTimeInterval(delta)
}
The answer from dbplunkett is exactly right that daylight saving time isn't effectively handled by using secondsFromGMT(for: date), however their extension example is for Calendar. The below extension is for date which achieves the same aim:
extension Date {
func convert(from timeZone: TimeZone, to destinationTimeZone: TimeZone) -> Date {
let calendar = Calendar.current
var components = calendar.dateComponents(in: timeZone, from: self)
components.timeZone = destinationTimeZone
return calendar.date(from: components)!
}
}
Details
Xcode 11.4.1 (11E503a), Swift 5.2
Solution 1
Based on mukaissi answer
import Foundation
extension Date {
func to(timeZone outputTimeZone: TimeZone, from inputTimeZone: TimeZone) -> Date {
let delta = TimeInterval(outputTimeZone.secondsFromGMT(for: self) - inputTimeZone.secondsFromGMT(for: self))
return addingTimeInterval(delta)
}
}
Usage of solution 1
let utcTimeZone = TimeZone(abbreviation: "UTC")!
let dateString = "2020-06-03T01:43:44.888Z"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
let date = dateFormatter.date(from: dateString)
print(date)
print(date?.to(timeZone: .autoupdatingCurrent, from: utcTimeZone))
print(date?.to(timeZone: .current, from: utcTimeZone))
print(date?.to(timeZone: TimeZone(abbreviation: "PDT")!, from: utcTimeZone))
Solution 2
Do not forget to paste the Solution 1 code here
extension DateFormatter {
func date(from string: String, timeZoneInString: TimeZone, outputTimeZone: TimeZone = .autoupdatingCurrent) -> Date? {
date(from: string)?.to(timeZone: outputTimeZone, from: timeZoneInString)
}
}
Usage of solution 2
let utcTimeZone = TimeZone(abbreviation: "UTC")!
let pdtTimeZone = TimeZone(abbreviation: "PDT")!
let dateString = "2020-06-03T01:43:44.888Z"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
print(dateFormatter.date(from: dateString))
print(dateFormatter.date(from: dateString, timeZoneInString: utcTimeZone))
print(dateFormatter.date(from: dateString, timeZoneInString: utcTimeZone, outputTimeZone: pdtTimeZone))
I suggest
you set the GMT timezone on your dateFormatter to get back directly a NSDate in UTC (having only NSDates in UTC is a good practice)
when you need to display it you use another NSDateFormatter with the local time zone set on it (it is by default)
when you need to send a date to your server, you use dateFormatter again to generate a string
Find GMT ( Time Difference )
func findGMT() -> (hour: Int, minute: Int, second: Int) {
var formatterOnlyHour: DateFormatter = {
let formatter1 = DateFormatter()
formatter1.dateFormat = "HH"
formatter1.locale = Locale(identifier: Locale.preferredLanguages.first ?? "en")
return formatter1
}()
var hourGMT: Int = (Int(formatterOnlyHour.string(from: Date())) ?? 0) - (Int(Date().preciseGMTTime) ?? 0)
// print("Your GMT: \(GMTvalue)")
var minuteGMT: Int = hourGMT * 60
var secondGMT: Int = hourGMT * 3600
return (hourGMT, minuteGMT, secondGMT)}
Extention 1:
extension Formatter {
// create static date formatters for your date representations
static let preciseLocalTime: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "HH"
return formatter
}()
static let preciseGMTTime: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "HH"
return formatter
}()}
Extention 2:
extension Date {
// you can create a read-only computed property to return just the nanoseconds from your date time
var nanosecond: Int { return Calendar.current.component(.nanosecond, from: self) }
// the same for your local time
var preciseLocalTime: String {
return Formatter.preciseLocalTime.string(for: self) ?? ""
}
// or GMT time
var preciseGMTTime: String {
return Formatter.preciseGMTTime.string(for: self) ?? ""
}}