I want to compare two dates to identify which date is older. The dates are stored in String format like
let stringA = "2022-11-14"
let stringB = "2022-11-13"
I can asure that all dates will be saved in this String format: yyyy-MM-dd
In my locale testing I was able to run
let compare = stringA > stringB
With various dates I always got the correct result true/false.
Is this the correct way of comparing those dates? Or should I use a DateFormatter to compare all those dates in Date format?
P.S. I know how two convert those Strings to Dates and compare them afterwards. I just want to avoid it if possible because the String comparison is shorter, easy to read and would save redundant code if possible.
you can convert string to date and compare easily just add following String extension in your code
extension String {
func toDate() -> Date? {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd" //you can add any date formatter I just added according to your question formate
return formatter.date(from: self)
}
}
Use it like this in ViewController
class ViewController: UIViewController {
let stringA = "2022-11-13"
let stringB = "2022-11-13"
override func viewDidLoad() {
super.viewDidLoad()
guard let date1 = stringA.toDate(), let date2 = stringB.toDate() else {
print("not able to convert to date")
return
}
let result = date1.compare(date2)
switch result {
case .orderedAscending:
print("Ascending")
case .orderedDescending:
print("Descending")
case .orderedSame:
print("same")
}
}
}
you can check this answer for different date formatters.
Goal/Problem
I want to convert a Date into a String and back to a Date. I am able to do this but I am losing precision on the way. How can I make sure that not a single bit gets lost in the process?
1573827905079978 vs 157382790508
Main code
var now = Date()
var now_as_string = Date.dateAsString(style: .dayMonthYearHourMinuteSecondMillisecondTimezone, date: now)
var back_as_date = Date.stringAsDate(style: .dayMonthYearHourMinuteSecondMillisecondTimezone, string: now_as_string)
print(Date.dateAsTimeIntervalSince1970WithoutDots(date: now))
print(Date.dateAsTimeIntervalSince1970WithoutDots(date: back_as_date))
Output
1573827905079978
157382790508
Date Extension (the place where the real magic happens)
import Foundation
extension Date {
enum Style {
case dayMonthYear
case dayMonthYearHourMinute
case dayMonthYearHourMinuteSecondMillisecondTimezone
}
static func dateAsString(style: Date.Style, date: Date) -> String{
let formatter = DateFormatter()
formatter.dateFormat = fromStyleToString(style: style)
return formatter.string(from: date)
}
private static func fromStyleToString(style: Date.Style) -> String{
switch style {
case .dayMonthYear:
return "dd.MM.yyyy"
case .dayMonthYearHourMinute:
return "dd.MM.yyyy HH:mm"
case .dayMonthYearHourMinuteSecondMillisecondTimezone:
return "dd.MM.yyyy HH:mm:ss:SSS Z"
}
}
static func stringAsDate(style: Date.Style, string: String) -> Date{
let formatter = DateFormatter()
formatter.dateFormat = fromStyleToString(style: style)
return formatter.date(from: string)!
}
static func dateAsTimeIntervalSince1970WithoutDots(date: Date) -> String{
return String(date.timeIntervalSince1970).replacingOccurrences(of: ".", with: "")
}
}
A Date is just a number of seconds, as a Double, since the reference date. (This is aliased to "TimeInterval," but it's just a Double.)
If you want it to be a string without losing any information, that's just the string form of the Double:
let nowString = "\(now.timeIntervalSinceReferenceDate)" // "595531191.461246"
And to convert it back, turn the Double into a Date:
let originalDate = Date(timeIntervalSinceReferenceDate: TimeInterval(nowString)!)
originalDate == now // true
You definitely don't want to remove the decimal point. That's an important part of the number.
I'm hitting a webservice that is returning a string in the following format:
"yyyy-MM-dd'T'HH:mmZ"
It's very close to the standard UTC format, but just without the ss at the end. My issue is that my dateFormatter is always returning nil...and I have tried to make sure that locale and everything else is setup properly.
Here is an example of an actual string:
2019-12-26T00:00Z
Here is the code that creates the DF:
extension DateFormatter {
#objc static func defaultDateFormat(_ format: String) -> DateFormatter {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "US")
formatter.dateFormat = format
return formatter
}
func date(from string: String?) -> Date? {
if let string = string {
return self.date(from: string)
} else {
return nil
}
}
func string(fromOptional date: Date?) -> String? {
if let date = date {
return self.string(from: date)
} else {
return nil
}
}
func string(fromOptional date: Date?, defaultStr: String) -> String {
return self.string(fromOptional: date) ?? defaultStr
}
}
let df = DateFormatter.defaultDateFormat("yyyy-MM-dd'T'HH:mmZ")
let date: Date? = df.date(from: __dateString__) // always nil
A few observations:
You want to use a locale of en_US_POSIX, an invariant locale that always works for ISO 8601 / RFC 3339 date strings. See Technical Q&A 1480.
If you want to use this formatter to convert a date back to a string like 2019-12-26T00:00Z, you will want to:
Use X (or ZZZZZ or XXXXX) in your format string, not Z (see the “zone” section in the table at Date Format Patterns and you’ll see how these various time zone patterns, e.g. Z, ZZZZZ, and X, are interpreted); and
Set the timeZone of the formatter to use Zulu/GMT/UTC.
Thus:
#objc static func defaultDateFormat(_ format: String) -> DateFormatter {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = format
return formatter
}
And
let df = DateFormatter.defaultDateFormat("yyyy-MM-dd'T'HH:mmX")
let date = df.date(from: "2019-12-26T00:00Z")
Or
let string = df.string(from: Date())
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)
}
Please bear with me as I'm new to Swift: I'm writing a little app that calls an API (Rails) which return some data. The problem I'm having is that the date provided by the API comes like this: yyyy-MM-dd'T'HH:mm:sssZ my function expects yyyy-MM-dd'T'HH:mm:ssZ Please note the extra second
Here's my function:
public func dateFromString(date: String, format: String) -> NSDate {
if dateFormatter == nil {
dateFormatter = NSDateFormatter()
}
dateFormatter!.dateFormat = format
return dateFormatter!.dateFromString(date)!
}
On the console, when I try to run the app I get this:
date String "2015-04-13T12:48:23.310Z"
format String "yyyy-MM-dd'T'HH:mm:ssZ"
Any ideas on how I should resolve this?
You can do if you use the SSS as format string, like below.
"yyyy-MM-dd'T'HH:mm:ss.SSSZ"
See also: Date Format Patterns
You can use the following function:
func dateFromate(_ stringTime:String) -> String{
//"2015-04-13T12:48:23.310Z"
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
guard let date2 = formatter.date(from: stringTime) else{
return stringTime
}
let date1 = Date()
return date1.offset(from: date2)
}