How to get the weekday string representation for a date Swift - ios

I'm trying to get the weekday of a date (Wednesday, Tuesday) but I can't seem to find a good way to actually get the string. This is what I'm doing:
internal func fromToday(daysAgo: Date) -> String {
let calendar = NSCalendar.autoupdatingCurrent
let startOfNow = calendar.startOfDay(for: Date())
let startOfTimeStamp = calendar.startOfDay(for: daysAgo)
let numDaysDifference = abs(calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp).day!)
let daysAgoComponents = calendar.dateComponents([.weekday, .hour, .minute], from: daysAgo)
var hour = daysAgoComponents.hour!
var timeOfDay = "AM"
if hour >= 13 {
hour -= 12
timeOfDay = "PM"
}
var res = "\(hour):\(daysAgoComponents.minute!)\(timeOfDay)"
if calendar.isDateInYesterday(daysAgo) {
res = "Yesterday"
} else if numDaysDifference < 8 && numDaysDifference > 1 {
res = stringFromWeekday(day: daysAgoComponents.weekday!) // HERE THIS IS WHAT IM ASKING ABOUT IGNORE OTHER IFS
} else if numDaysDifference >= 8 {
res = stringFromDate(day: daysAgo)
}
return res
}
fileprivate func stringFromWeekday(day: Int) -> String {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US")
if formatter.weekdaySymbols.count < day {
print("ConversationTVC - \(day) is not a valid day of the week")
}
return formatter.weekdaySymbols[day]
}
Specifically, the line:
else if numDaysDifference < 8 && numDaysDifference > 1
res = stringFromWeekday(day: daysAgoComponents.weekday!)`
I think the issue has to do with weekdaySymbols
How do I get the string representation of the day of the week X days ago given a Date?

Issue was I wasn't subtracting 1 from day, weekday returns 1-7 and weekday symbols is 0-6

Related

Swift get the difference between current date and hours:minutes:seconds as a String

in my app I get a JSON response as a string -> "16:00:00"
What I want to do is to get the timeInterval in seconds from the current date until "16:00:00"
this is my code so far. PS sorry for the bad description, thanks for your help
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm:ss"
if let date = dateFormatter.date(from:task.start) {
print(date.timeDifference(date: Date()))
}
func timeDifference(date: Date) -> String {
let hourMinuteSecond: Set<Calendar.Component> = [.hour, .minute, .second]
let difference = Calendar.current.dateComponents(hourMinuteSecond, from: date, to: self);
let seconds = "\(difference.second ?? 0)s"
let minutes = "\(difference.minute ?? 0)m" + " " + seconds
let hours = "\(difference.hour ?? 0)h" + " " + minutes
if let hour = difference.hour, hour > 0 { return hours }
if let minute = difference.minute, minute > 0 { return minutes }
if let second = difference.second, second > 0 { return seconds }
return ""
}
There are many ways to solve this. Here's one:
let df = DateFormatter()
df.dateFormat = "HH:mm:ss"
df.defaultDate = Date() // default to today's date
if let date = df.date(from:task.start) {
print(date.timeDifference(date: Date()))
}
func timeDifference(date: Date) -> String {
let diff = timeIntervalSince(date)
let fmt = DateComponentsFormatter()
fmt.unitsStyle = .abbreviated
let res = fmt.string(from: diff)
return res
}
This code assumes that timeDifference is an extension method of Date.
Also note that this will give a negative time difference if the time in the string is earlier than "now". If you want a positive time difference no matter if the time string is before or after "now", use the abs function:
let res = fmt.string(from: abs(diff))

Swift Problems understanding the result of Calendar.dateComponents

in my app I have a list of contacts including the contacts birthday.
Now I want to see which of my contacts have birthday within in the next 7 days.
I am using a filter on my list to filter and return only thos contacts that match
let timeSpan = 7
let cal = Calendar.current
let now = Date()
var birthDays = contactList.filter { (contact) -> Bool in
if let birthDate = contact.birthDate {
let difference = cal.dateComponents([.day,.year], from: birthDate as Date, to: now! )
print("BD:\(birthDate) : DIFF \(difference.day!)")
return ((difference.day! <= timeSpan) && difference.day! >= 0)
}
return false
}
My hope was so. However the result was weired. So I added that ugly print into my closure in order to see the result of 'difference'
What is odd is that for instance the following:
Today is 2017-08-18 one of my contacts was born on 1987-08-19.
So instead of returning a difference.day of 1 I receive 364. If I swap from: and to: in the dateComponents I receive difference.day of -364.
My expectation was to have a difference.day = 1.
Again in Playground
import UIKit
var now = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let s = dateFormatter.date(from: "1987-08-19")
let cal = Calendar.current
let difference = cal.dateComponents([Calendar.Component.day,Calendar.Component.month,Calendar.Component.year], from: s!, to: now )
print("\(difference.day!)") // result is difference.day = 30
What am I doing wrong?
Thanks
You can create an extension to return the number of days from the next birthday as follow:
extension Date {
var year: Int { return Calendar.current.component(.year, from: self) }
var month: Int { return Calendar.current.component(.month, from: self) }
var day: Int { return Calendar.current.component(.day, from: self) }
var noon: Date { return Calendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self)! }
var daysFromBirthday: Int {
let nextBirthDate = DateComponents(calendar: .current, year: Date().year + (month < Date().month ? 1 : 0), month: month, day: day, hour: 12).date ?? Date.distantFuture
return Calendar.current.dateComponents([.day], from: Date().noon, to: nextBirthDate).day ?? 0
}
}
And you can now filter your objects as follow:
let timeSpan = 0...7
let birthDays = contactList.filter {
timeSpan ~= $0.birthDate?.daysFromBirthday ?? -1
}
The problem was you were checking the difference of the days from the user's birthday but in the app we will need to get the difference between the current date and the birthday during this year. Say in the example that you have said, it's the difference between 2017-08-19(this year's birthday) and 2017-08-18(current date). You could try the code below.
var now = Date()
dateFormatter.dateFormat = "yyyy-MM-dd"
let s = dateFormatter.date(from: "1987-08-19")
let cal = Calendar.current
let currentDateComponentsYear = cal.dateComponents([.year,.month,.day], from: newDate!)
var dateComponents = cal.dateComponents([.year,.month,.day], from: s!)
dateComponents.year = currentDateComponentsYear.year
let currentYearBDay = cal.date(from: dateComponents)
currentDateComponentsYear.month
let difference = cal.dateComponents([Calendar.Component.year,Calendar.Component.month,Calendar.Component.day], from:newDate! , to: currentYearBDay! )
let daysDiffernce:Int?
if dateComponents.month == 1 && currentDateComponentsYear.month == 12 && difference.day! < 0 {
let range = cal.range(of: .day, in: .month, for: newDate!)!
let numDays = range.count
daysDiffernce = difference.day! + numDays
} else {
daysDiffernce = difference.day
}
print("\(daysDiffernce!)") //result 1

Results of sorting an array are unexpected

I have a an array of objects that contains a date value. I have calculated the date differences and returned the number of days left. Now I am trying to sort it so it appends based on the object with least number of days left.
I have been able to use this function:
func sortList() {
item.sort { (first: Item, second: Item) -> Bool in
return first.days() < second.days()
}
}
Which gives me this:
However as you can see the date which is equal to 0 is appended at the bottom.
This is how I am calculating the days difference:
func daysDiff(startDate: Date, endDate: Date) -> Int {
let calendar = Calendar.current
let date1 = calendar.startOfDay(for: startDate)
let date2 = calendar.startOfDay(for: endDate)
let a = calendar.dateComponents([.day], from: date1, to: date2)
return a.value(for: .day)!
}
And this is how I am formatting it:
func days() -> String {
let formatter = DateFormatter()
formatter.dateFormat = "MM dd, yyyy"
let date = formatter.date(from: itemDate!)
let date1 = Date()
let date2 = date
let days = daysDiff(startDate: date1, endDate: date2!)
if days > 1 {
return "\(days) days left"
} else if days == 1 {
return "a day left"
} else if days == 0 {
return "Due today!"
} else if days < 0 {
return "Late"
} else {
return "\(days)"
}
}
I am not really sure why this issue is happening.
Your sort is based on the text from your days() function so you are sorting the data alphabetically based on your text. You should sort based on an actual integer value, not a string.
You should have two methods on your class.
days() which returns an Int instead of a String.
daysLabel which returns a String based on the result of days.
Use days when sorting by number. Use daysLabel when displaying an Item instance somewhere.
func days() -> Int {
let formatter = DateFormatter()
formatter.dateFormat = "MM dd, yyyy"
let date = formatter.date(from: itemDate!)
let date1 = Date()
let date2 = date
let days = daysDiff(startDate: date1, endDate: date2!)
return days
}
func daysLabel() -> String {
let days = days()
if days > 1 {
return "\(days) days left"
} else if days == 1 {
return "a day left"
} else if days == 0 {
return "Due today!"
} else if days < 0 {
return "Late"
} else {
return "\(days)"
}
}

How do I display time/date like Instagram? [duplicate]

I am trying to get the difference between the current date as NSDate() and a date from a PHP time(); call for example: NSDate(timeIntervalSinceReferenceDate: 1417147270). How do I go about getting the difference in time between the two dates. I'd like to have a function that compares the two dates and if(seconds > 60) then it returns minutes, if(minutes > 60) return hours and if(hours > 24) return days and so on.
How should I go about this?
EDIT: The current accepted answer has done exactly what I've wanted to do. I recommend it for easy usage for getting the time between two dates in the form that that PHP time() function uses. If you aren't particularly familiar with PHP, that's the time in seconds from January 1st, 1970. This is beneficial for a backend in PHP. If perhaps you're using a backend like NodeJS you might want to consider some of the other options you'll find below.
Xcode 8.3 • Swift 3.1 or later
You can use Calendar to help you create an extension to do your date calculations as follow:
extension Date {
/// Returns the amount of years from another date
func years(from date: Date) -> Int {
return Calendar.current.dateComponents([.year], from: date, to: self).year ?? 0
}
/// Returns the amount of months from another date
func months(from date: Date) -> Int {
return Calendar.current.dateComponents([.month], from: date, to: self).month ?? 0
}
/// Returns the amount of weeks from another date
func weeks(from date: Date) -> Int {
return Calendar.current.dateComponents([.weekOfMonth], from: date, to: self).weekOfMonth ?? 0
}
/// Returns the amount of days from another date
func days(from date: Date) -> Int {
return Calendar.current.dateComponents([.day], from: date, to: self).day ?? 0
}
/// Returns the amount of hours from another date
func hours(from date: Date) -> Int {
return Calendar.current.dateComponents([.hour], from: date, to: self).hour ?? 0
}
/// Returns the amount of minutes from another date
func minutes(from date: Date) -> Int {
return Calendar.current.dateComponents([.minute], from: date, to: self).minute ?? 0
}
/// Returns the amount of seconds from another date
func seconds(from date: Date) -> Int {
return Calendar.current.dateComponents([.second], from: date, to: self).second ?? 0
}
/// Returns the a custom time interval description from another date
func offset(from date: Date) -> String {
if years(from: date) > 0 { return "\(years(from: date))y" }
if months(from: date) > 0 { return "\(months(from: date))M" }
if weeks(from: date) > 0 { return "\(weeks(from: date))w" }
if days(from: date) > 0 { return "\(days(from: date))d" }
if hours(from: date) > 0 { return "\(hours(from: date))h" }
if minutes(from: date) > 0 { return "\(minutes(from: date))m" }
if seconds(from: date) > 0 { return "\(seconds(from: date))s" }
return ""
}
}
Using Date Components Formatter
let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.allowedUnits = [.second, .minute, .hour, .day, .weekOfMonth, .month, .year]
dateComponentsFormatter.maximumUnitCount = 1
dateComponentsFormatter.unitsStyle = .full
dateComponentsFormatter.string(from: Date(), to: Date(timeIntervalSinceNow: 4000000)) // "1 month"
let date1 = DateComponents(calendar: .current, year: 2014, month: 11, day: 28, hour: 5, minute: 9).date!
let date2 = DateComponents(calendar: .current, year: 2015, month: 8, day: 28, hour: 5, minute: 9).date!
let years = date2.years(from: date1) // 0
let months = date2.months(from: date1) // 9
let weeks = date2.weeks(from: date1) // 39
let days = date2.days(from: date1) // 273
let hours = date2.hours(from: date1) // 6,553
let minutes = date2.minutes(from: date1) // 393,180
let seconds = date2.seconds(from: date1) // 23,590,800
let timeOffset = date2.offset(from: date1) // "9M"
let date3 = DateComponents(calendar: .current, year: 2014, month: 11, day: 28, hour: 5, minute: 9).date!
let date4 = DateComponents(calendar: .current, year: 2015, month: 11, day: 28, hour: 5, minute: 9).date!
let timeOffset2 = date4.offset(from: date3) // "1y"
let date5 = DateComponents(calendar: .current, year: 2017, month: 4, day: 28).date!
let now = Date()
let timeOffset3 = now.offset(from: date5) // "1w"
If someone needs to display all time units e.g "hours minutes seconds" not just "hours". Let's say the time difference between two dates is 1hour 59minutes 20seconds. This function will display "1h 59m 20s".
Here is my Objective-C code:
extension NSDate {
func offsetFrom(date: NSDate) -> String {
let dayHourMinuteSecond: NSCalendarUnit = [.Day, .Hour, .Minute, .Second]
let difference = NSCalendar.currentCalendar().components(dayHourMinuteSecond, fromDate: date, toDate: self, options: [])
let seconds = "\(difference.second)s"
let minutes = "\(difference.minute)m" + " " + seconds
let hours = "\(difference.hour)h" + " " + minutes
let days = "\(difference.day)d" + " " + hours
if difference.day > 0 { return days }
if difference.hour > 0 { return hours }
if difference.minute > 0 { return minutes }
if difference.second > 0 { return seconds }
return ""
}
}
In Swift 3+:
extension Date {
func offsetFrom(date: Date) -> String {
let dayHourMinuteSecond: Set<Calendar.Component> = [.day, .hour, .minute, .second]
let difference = NSCalendar.current.dateComponents(dayHourMinuteSecond, from: date, to: self)
let seconds = "\(difference.second ?? 0)s"
let minutes = "\(difference.minute ?? 0)m" + " " + seconds
let hours = "\(difference.hour ?? 0)h" + " " + minutes
let days = "\(difference.day ?? 0)d" + " " + hours
if let day = difference.day, day > 0 { return days }
if let hour = difference.hour, hour > 0 { return hours }
if let minute = difference.minute, minute > 0 { return minutes }
if let second = difference.second, second > 0 { return seconds }
return ""
}
}
Swift 5.1 • iOS 13
You can use RelativeDateFormatter that has been introduced by Apple in iOS 13.
let exampleDate = Date().addingTimeInterval(-15000)
let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .full
let relativeDate = formatter.localizedString(for: exampleDate, relativeTo: Date())
print(relativeDate) // 4 hours ago
See How to show a relative date and time using RelativeDateTimeFormatter.
You ask:
I'd like to have a function that compares the two dates and if(seconds > 60) then it returns minutes, if(minutes > 60) return hours and if(hours > 24) return days and so on.
I'm assuming that you're trying to build a string representation of the elapsed time between two dates. Rather than writing your own code to do that, Apple already has a class designed to do precisely that. Namely, use DateComponentsFormatter, set allowedUnits to whatever values make sense to your app, set unitsStyle to whatever you want (e.g. .full), and then call string(from:to:).
E.g. in Swift 3:
let previousDate = ...
let now = Date()
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .full
formatter.allowedUnits = [.month, .day, .hour, .minute, .second]
formatter.maximumUnitCount = 2 // often, you don't care about seconds if the elapsed time is in months, so you'll set max unit to whatever is appropriate in your case
let string = formatter.string(from: previousDate, to: now)
This also will localize the string appropriate for the device in question.
Or, in Swift 2.3:
let previousDate = ...
let now = NSDate()
let formatter = NSDateComponentsFormatter()
formatter.unitsStyle = .Full
formatter.allowedUnits = [.Month, .Day, .Hour, .Minute, .Second]
formatter.maximumUnitCount = 2
let string = formatter.stringFromDate(previousDate, toDate: now)
If you're looking for the actual numeric values, just use dateComponents. E.g. in Swift 3:
let components = Calendar.current.dateComponents([.month, .day, .hour, .minute, .second], from: previousDate, to: now)
Or, in Swift 2.3:
let components = NSCalendar.currentCalendar().components([.Month, .Day, .Hour, .Minute, .Second], fromDate: previousDate, toDate: now, options: [])
combined Extension + DateComponentsFormatter from the answer of #leo-dabus
Xcode 8.3 • Swift 3.1
extension DateComponentsFormatter {
func difference(from fromDate: Date, to toDate: Date) -> String? {
self.allowedUnits = [.year,.month,.weekOfMonth,.day]
self.maximumUnitCount = 1
self.unitsStyle = .full
return self.string(from: fromDate, to: toDate)
}
}
let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.difference(from: Date(), to: Date(timeIntervalSinceNow: 4000000)) // "1 month"
--> Use this to find time gap between two dates in Swift(With two Strings).
func timeGapBetweenDates(previousDate : String,currentDate : String)
{
let dateString1 = previousDate
let dateString2 = currentDate
let Dateformatter = DateFormatter()
Dateformatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date1 = Dateformatter.date(from: dateString1)
let date2 = Dateformatter.date(from: dateString2)
let distanceBetweenDates: TimeInterval? = date2?.timeIntervalSince(date1!)
let secondsInAnHour: Double = 3600
let minsInAnHour: Double = 60
let secondsInDays: Double = 86400
let secondsInWeek: Double = 604800
let secondsInMonths : Double = 2592000
let secondsInYears : Double = 31104000
let minBetweenDates = Int((distanceBetweenDates! / minsInAnHour))
let hoursBetweenDates = Int((distanceBetweenDates! / secondsInAnHour))
let daysBetweenDates = Int((distanceBetweenDates! / secondsInDays))
let weekBetweenDates = Int((distanceBetweenDates! / secondsInWeek))
let monthsbetweenDates = Int((distanceBetweenDates! / secondsInMonths))
let yearbetweenDates = Int((distanceBetweenDates! / secondsInYears))
let secbetweenDates = Int(distanceBetweenDates!)
if yearbetweenDates > 0
{
print(yearbetweenDates,"years")//0 years
}
else if monthsbetweenDates > 0
{
print(monthsbetweenDates,"months")//0 months
}
else if weekBetweenDates > 0
{
print(weekBetweenDates,"weeks")//0 weeks
}
else if daysBetweenDates > 0
{
print(daysBetweenDates,"days")//5 days
}
else if hoursBetweenDates > 0
{
print(hoursBetweenDates,"hours")//120 hours
}
else if minBetweenDates > 0
{
print(minBetweenDates,"minutes")//7200 minutes
}
else if secbetweenDates > 0
{
print(secbetweenDates,"seconds")//seconds
}
}
Slightly modified code for Swift 3.0
let calendar = NSCalendar.current as NSCalendar
// Replace the hour (time) of both dates with 00:00
let date1 = calendar.startOfDay(for: startDateTime)
let date2 = calendar.startOfDay(for: endDateTime)
let flags = NSCalendar.Unit.day
let components = calendar.components(flags, from: date1, to: date2, options: [])
return components.day!
I added a "long" version to Leo Dabus's asnwer in case you want to have a string that says something like "2 weeks ago" instead of just "2w"...
extension Date {
func offsetLong(from date: Date) -> String {
if years(from: date) > 0 {
return years(from: date) > 1 ? "\(years(from: date)) years ago" : "\(years(from: date)) year ago"
}
if months(from: date) > 0 {
return months(from: date) > 1 ? "\(months(from: date)) months ago" : "\(months(from: date)) month ago"
}
if weeks(from: date) > 0 {
return weeks(from: date) > 1 ? "\(weeks(from: date)) weeks ago" : "\(weeks(from: date)) week ago"
}
if days(from: date) > 0 {
return days(from: date) > 1 ? "\(days(from: date)) days ago" : "\(days(from: date)) day ago"
}
if hours(from: date) > 0 {
return hours(from: date) > 1 ? "\(hours(from: date)) hours ago" : "\(hours(from: date)) hour ago"
}
if minutes(from: date) > 0 {
return minutes(from: date) > 1 ? "\(minutes(from: date)) minutes ago" : "\(minutes(from: date)) minute ago"
}
if seconds(from: date) > 0 {
return seconds(from: date) > 1 ? "\(seconds(from: date)) seconds ago" : "\(seconds(from: date)) second ago"
}
return ""
}
}
func dateDiff(dateStr:String) -> String {
var f:NSDateFormatter = NSDateFormatter()
f.timeZone = NSTimeZone.localTimeZone()
f.dateFormat = "yyyy-M-dd'T'HH:mm:ss.SSSZZZ"
var now = f.stringFromDate(NSDate())
var startDate = f.dateFromString(dateStr)
var endDate = f.dateFromString(now)
var calendar: NSCalendar = NSCalendar.currentCalendar()
let calendarUnits = NSCalendarUnit.CalendarUnitWeekOfMonth | NSCalendarUnit.CalendarUnitDay | NSCalendarUnit.CalendarUnitHour | NSCalendarUnit.CalendarUnitMinute | NSCalendarUnit.CalendarUnitSecond
let dateComponents = calendar.components(calendarUnits, fromDate: startDate!, toDate: endDate!, options: nil)
let weeks = abs(dateComponents.weekOfMonth)
let days = abs(dateComponents.day)
let hours = abs(dateComponents.hour)
let min = abs(dateComponents.minute)
let sec = abs(dateComponents.second)
var timeAgo = ""
if (sec > 0){
if (sec > 1) {
timeAgo = "\(sec) Seconds Ago"
} else {
timeAgo = "\(sec) Second Ago"
}
}
if (min > 0){
if (min > 1) {
timeAgo = "\(min) Minutes Ago"
} else {
timeAgo = "\(min) Minute Ago"
}
}
if(hours > 0){
if (hours > 1) {
timeAgo = "\(hours) Hours Ago"
} else {
timeAgo = "\(hours) Hour Ago"
}
}
if (days > 0) {
if (days > 1) {
timeAgo = "\(days) Days Ago"
} else {
timeAgo = "\(days) Day Ago"
}
}
if(weeks > 0){
if (weeks > 1) {
timeAgo = "\(weeks) Weeks Ago"
} else {
timeAgo = "\(weeks) Week Ago"
}
}
print("timeAgo is===> \(timeAgo)")
return timeAgo;
}
With Swift 3, according to your needs, you may choose one of the two following ways to solve your problem.
1. Display the difference between two dates to the user
You can use a DateComponentsFormatter to create strings for your app’s interface. DateComponentsFormatter has a maximumUnitCount property with the following declaration:
var maximumUnitCount: Int { get set }
Use this property to limit the number of units displayed in the resulting string. For example, with this property set to 2, instead of “1h 10m, 30s”, the resulting string would be “1h 10m”. Use this property when you are constrained for space or want to round up values to the nearest large unit.
By setting maximumUnitCount's value to 1, you are guaranteed to display the difference in only one DateComponentsFormatter's unit (years, months, days, hours or minutes).
The Playground code below shows how to display the difference between two dates:
import Foundation
let oldDate = Date(timeIntervalSinceReferenceDate: -16200)
let newDate = Date(timeIntervalSinceReferenceDate: 0)
let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.allowedUnits = [NSCalendar.Unit.year, .month, .day, .hour, .minute]
dateComponentsFormatter.maximumUnitCount = 1
dateComponentsFormatter.unitsStyle = DateComponentsFormatter.UnitsStyle.full
let timeDifference = dateComponentsFormatter.string(from: oldDate, to: newDate)
print(String(reflecting: timeDifference)) // prints Optional("5 hours")
Note that DateComponentsFormatter rounds up the result. Therefore, a difference of 4 hours and 30 minutes will be displayed as 5 hours.
If you need to repeat this operation, you can refactor your code:
import Foundation
struct Formatters {
static let dateComponentsFormatter: DateComponentsFormatter = {
let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.allowedUnits = [NSCalendar.Unit.year, .month, .day, .hour, .minute]
dateComponentsFormatter.maximumUnitCount = 1
dateComponentsFormatter.unitsStyle = DateComponentsFormatter.UnitsStyle.full
return dateComponentsFormatter
}()
}
extension Date {
func offset(from: Date) -> String? {
return Formatters.dateComponentsFormatter.string(from: oldDate, to: self)
}
}
let oldDate = Date(timeIntervalSinceReferenceDate: -16200)
let newDate = Date(timeIntervalSinceReferenceDate: 0)
let timeDifference = newDate.offset(from: oldDate)
print(String(reflecting: timeDifference)) // prints Optional("5 hours")
2. Get the difference between two dates without formatting
If you don't need to display with formatting the difference between two dates to the user, you can use Calendar. Calendar has a method dateComponents(_:from:to:) that has the following declaration:
func dateComponents(_ components: Set<Calendar.Component>, from start: Date, to end: Date) -> DateComponents
Returns the difference between two dates.
The Playground code below that uses dateComponents(_:from:to:) shows how to retrieve the difference between two dates by returning the difference in only one type of Calendar.Component (years, months, days, hours or minutes).
import Foundation
let oldDate = Date(timeIntervalSinceReferenceDate: -16200)
let newDate = Date(timeIntervalSinceReferenceDate: 0)
let descendingOrderedComponents = [Calendar.Component.year, .month, .day, .hour, .minute]
let dateComponents = Calendar.current.dateComponents(Set(descendingOrderedComponents), from: oldDate, to: newDate)
let arrayOfTuples = descendingOrderedComponents.map { ($0, dateComponents.value(for: $0)) }
for (component, value) in arrayOfTuples {
if let value = value, value > 0 {
print(component, value) // prints hour 4
break
}
}
If you need to repeat this operation, you can refactor your code:
import Foundation
extension Date {
func offset(from: Date) -> (Calendar.Component, Int)? {
let descendingOrderedComponents = [Calendar.Component.year, .month, .day, .hour, .minute]
let dateComponents = Calendar.current.dateComponents(Set(descendingOrderedComponents), from: from, to: self)
let arrayOfTuples = descendingOrderedComponents.map { ($0, dateComponents.value(for: $0)) }
for (component, value) in arrayOfTuples {
if let value = value, value > 0 {
return (component, value)
}
}
return nil
}
}
let oldDate = Date(timeIntervalSinceReferenceDate: -16200)
let newDate = Date(timeIntervalSinceReferenceDate: 0)
if let (component, value) = newDate.offset(from: oldDate) {
print(component, value) // prints hour 4
}
If your purpose is to get the exact day number between two dates, you can work around this issue like this:
// Assuming that firstDate and secondDate are defined
// ...
var calendar: NSCalendar = NSCalendar.currentCalendar()
// Replace the hour (time) of both dates with 00:00
let date1 = calendar.startOfDayForDate(firstDate)
let date2 = calendar.startOfDayForDate(secondDate)
let flags = NSCalendarUnit.DayCalendarUnit
let components = calendar.components(flags, fromDate: date1, toDate: date2, options: nil)
components.day // This will return the number of day(s) between dates
In Swift 2.2
/// Returns the amount of years from another date
func years(fromdate: NSDate) -> Int {
return NSCalendar.currentCalendar().components([.Year], fromDate: fromdate, toDate: NSDate(), options: []).year ?? 0
}
/// Returns the amount of months from another date
func months(fromdate: NSDate) -> Int {
return NSCalendar.currentCalendar().components([.Month], fromDate: fromdate, toDate: NSDate(), options: []).month ?? 0
}
/// Returns the amount of weeks from another date
func weeks(fromdate: NSDate) -> Int {
return NSCalendar.currentCalendar().components([.WeekOfYear], fromDate: fromdate, toDate: NSDate(), options: []).weekOfYear ?? 0
}
/// Returns the amount of days from another date
func days(fromdate: NSDate) -> Int {
return NSCalendar.currentCalendar().components([.Day], fromDate: fromdate, toDate: NSDate(), options: []).day ?? 0
}
/// Returns the amount of hours from another date
func hours(fromdate: NSDate) -> Int {
return NSCalendar.currentCalendar().components([.Hour], fromDate: fromdate, toDate: NSDate(), options: []).hour ?? 0
}
/// Returns the amount of minutes from another date
func minutes(fromdate: NSDate) -> Int {
return NSCalendar.currentCalendar().components([.Minute], fromDate: fromdate, toDate: NSDate(), options: []).minute ?? 0
}
/// Returns the amount of seconds from another date
func seconds(fromdate: NSDate) -> Int {
return NSCalendar.currentCalendar().components(.Second, fromDate: fromdate, toDate: NSDate(), options: []).second ?? 0
}
A small addition to Leo Dabus' answer to provide the plural versions and be more human readable.
Swift 3
extension Date {
/// Returns the amount of years from another date
func years(from date: Date) -> Int {
return Calendar.current.dateComponents([.year], from: date, to: self).year ?? 0
}
/// Returns the amount of months from another date
func months(from date: Date) -> Int {
return Calendar.current.dateComponents([.month], from: date, to: self).month ?? 0
}
/// Returns the amount of weeks from another date
func weeks(from date: Date) -> Int {
return Calendar.current.dateComponents([.weekOfMonth], from: date, to: self).weekOfMonth ?? 0
}
/// Returns the amount of days from another date
func days(from date: Date) -> Int {
return Calendar.current.dateComponents([.day], from: date, to: self).day ?? 0
}
/// Returns the amount of hours from another date
func hours(from date: Date) -> Int {
return Calendar.current.dateComponents([.hour], from: date, to: self).hour ?? 0
}
/// Returns the amount of minutes from another date
func minutes(from date: Date) -> Int {
return Calendar.current.dateComponents([.minute], from: date, to: self).minute ?? 0
}
/// Returns the amount of seconds from another date
func seconds(from date: Date) -> Int {
return Calendar.current.dateComponents([.second], from: date, to: self).second ?? 0
}
/// Returns the a custom time interval description from another date
func offset(from date: Date) -> String {
if years(from: date) == 1 { return "\(years(from: date)) year" } else if years(from: date) > 1 { return "\(years(from: date)) years" }
if months(from: date) == 1 { return "\(months(from: date)) month" } else if months(from: date) > 1 { return "\(months(from: date)) month" }
if weeks(from: date) == 1 { return "\(weeks(from: date)) week" } else if weeks(from: date) > 1 { return "\(weeks(from: date)) weeks" }
if days(from: date) == 1 { return "\(days(from: date)) day" } else if days(from: date) > 1 { return "\(days(from: date)) days" }
if hours(from: date) == 1 { return "\(hours(from: date)) hour" } else if hours(from: date) > 1 { return "\(hours(from: date)) hours" }
if minutes(from: date) == 1 { return "\(minutes(from: date)) minute" } else if minutes(from: date) > 1 { return "\(minutes(from: date)) minutes" }
return ""
}
}
Swift 5
func dateDiff(dateStr:String) -> String {
let f:DateFormatter = DateFormatter()
f.timeZone = NSTimeZone.local
f.dateFormat = "yyyy-M-dd'T'HH:mm:ss.SSSZZZ"
let now = f.string(from: NSDate() as Date)
let startDate = f.date(from: dateStr)
let endDate = f.date(from: now)
var _: NSCalendar = NSCalendar.current as NSCalendar
let dateComponents = Calendar.current.dateComponents([ .weekOfMonth, .day , .hour , .minute , .second], from: startDate!, to: endDate!)
let weeks = abs(dateComponents.weekOfMonth!)
let days = abs(dateComponents.day!)
let hours = abs(dateComponents.hour!)
let min = abs(dateComponents.minute!)
let sec = abs(dateComponents.second!)
var timeAgo = ""
if (sec > 0){
if (sec > 1) {
timeAgo = "\(sec) Seconds Ago"
} else {
timeAgo = "\(sec) Second Ago"
}
}
if (min > 0){
if (min > 1) {
timeAgo = "\(min) Minutes Ago"
} else {
timeAgo = "\(min) Minute Ago"
}
}
if(hours > 0){
if (hours > 1) {
timeAgo = "\(hours) Hours Ago"
} else {
timeAgo = "\(hours) Hour Ago"
}
}
if (days > 0) {
if (days > 1) {
timeAgo = "\(days) Days Ago"
} else {
timeAgo = "\(days) Day Ago"
}
}
if(weeks > 0){
if (weeks > 1) {
timeAgo = "\(weeks) Weeks Ago"
} else {
timeAgo = "\(weeks) Week Ago"
}
}
print("timeAgo is===> \(timeAgo)")
return timeAgo;
}
This is the shorter version: Basically I try to get the difference between the post timestamp with the Date() now.
// MARK: - UPDATE Time Stamp
static func updateTimeStampPost(postTimeStamp: Date?, _ completion: (_ finalString: String?) -> Void) {
// date in the current state
let date = Date()
let dateComponentFormatter = DateComponentsFormatter()
// change the styling date, wether second minute or hour
dateComponentFormatter.unitsStyle = .abbreviated
dateComponentFormatter.allowedUnits = [.second, .minute, .hour, .day, .weekOfMonth]
dateComponentFormatter.maximumUnitCount = 1
// return the date new format as a string in the completion
completion(dateComponentFormatter.string(from: postTimeStamp!, to: date))
}
Here is my answer for the Swift 3 answers above. This is current as of Nov 2016, Xcode release was 8.2 Beta (8C23). Used some of both Sagar and Emin suggestions above and sometimes had to let Xcode autocomplete to suggest the syntax. It seemed like the syntax really changed to this beta version. buyDate I got from a DatePicker:
let calendar = NSCalendar.current as NSCalendar
let currentDate = Date()
let date1 = calendar.startOfDay(for: buyDate!)
let date2 = calendar.startOfDay(for: currentDate)
let flags = NSCalendar.Unit.day
let components = calendar.components(flags, from: date1, to: date2)
NSLog(" day= \(components.day)")
For XCode Version 8.3.3 & Swift 3.0:
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .short
var beginDate = "2017-08-24 12:00:00"
var endDate = "2017-09-07 12:00:00"
let startDateTime = dateFormatter.date(from: beginDate) //according to date format your date string
print(startDateTime ?? "") //Convert String to Date
let endDateTime = dateFormatter.date(from: endDate) //according to date format your date string
print(endDateTime ?? "") //Convert String to Date
let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.allowedUnits = [NSCalendar.Unit.minute,NSCalendar.Unit.hour,NSCalendar.Unit.day]
let interval = endDateTime!.timeIntervalSince(startDateTime!)
var diff = dateComponentsFormatter.string(from: interval)!
print(diff)
var day_i = 0
var hour_i = 0
var min_i = 0
if (diff.contains("d"))
{
let day = diff.substring(to: (diff.range(of: "d")?.lowerBound)!)
day_i = Int(day)!
print ("day --> \(day_i)")
diff = diff.substring(from:(diff.range(of : " ")?.upperBound )!)
print(diff)
}
let hour = diff.substring(to: (diff.range(of : ":")?.lowerBound )!)
hour_i = Int(hour)!
print ("hour --> \(hour_i)")
let min = diff.substring(from: (diff.range(of : ":")?.upperBound )!)
min_i = Int(min)!
print ("min --> \(min_i)")
Use this code:
let registrationDateString = "2008-10-06 00:00:00"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd hh:mm:ss"
if let registrationDate = dateFormatter.date(from: registrationDateString) {
let currentDate = Date()
let dateDifference = Calendar.current.dateComponents([.day, .month, .year],
from: registrationDate,
to: currentDate)
print("--------------------- Result: \(dateDifference.year ?? 0) years \(dateDifference.month ?? 0) months and \(dateDifference.day ?? 0) days")
} else {
print("--------------------- No result")
}
Output is: Result: 10 years 1 months and 18 days
import Foundation
extension DateComponents {
func dateComponentsToTimeString() -> String {
var hour = "\(self.hour!)"
var minute = "\(self.minute!)"
var second = "\(self.second!)"
if self.hour! < 10 { hour = "0" + hour }
if self.minute! < 10 { minute = "0" + minute }
if self.second! < 10 { second = "0" + second }
let str = "\(hour):\(minute):\(second)"
return str
}
}
extension Date {
func offset(from date: Date)-> DateComponents {
let components = Set<Calendar.Component>([.second, .minute, .hour, .day, .month, .year])
let differenceOfDate = Calendar.current.dateComponents(components, from: date, to: self)
return differenceOfDate
}
}
Use:
var durationString: String {
return self.endTime.offset(from: self.startTime).dateComponentsToTimeString()
}
Some addition in jose920405 answer to make it compatible with Swift 3.0 and above
func getDateTimeDiff(dateStr:String) -> String {
let formatter : DateFormatter = DateFormatter()
formatter.timeZone = NSTimeZone.local
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let now = formatter.string(from: NSDate() as Date)
let startDate = formatter.date(from: dateStr)
let endDate = formatter.date(from: now)
// *** create calendar object ***
var calendar = NSCalendar.current
// *** Get components using current Local & Timezone ***
print(calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: startDate!))
// *** define calendar components to use as well Timezone to UTC ***
let unitFlags = Set<Calendar.Component>([.year, .month, .day, .hour, .minute, .second])
calendar.timeZone = TimeZone(identifier: "UTC")!
let dateComponents = calendar.dateComponents(unitFlags, from: startDate!, to: endDate!)
// *** Get Individual components from date ***
let years = dateComponents.year!
let months = dateComponents.month!
let days = dateComponents.day!
let hours = dateComponents.hour!
let minutes = dateComponents.minute!
let seconds = dateComponents.second!
var timeAgo = ""
if (seconds > 0){
if seconds < 2 {
timeAgo = "Second Ago"
}
else{
timeAgo = "\(seconds) Second Ago"
}
}
if (minutes > 0){
if minutes < 2 {
timeAgo = "Minute Ago"
}
else{
timeAgo = "\(minutes) Minutes Ago"
}
}
if(hours > 0){
if hours < 2 {
timeAgo = "Hour Ago"
}
else{
timeAgo = "\(hours) Hours Ago"
}
}
if (days > 0) {
if days < 2 {
timeAgo = "Day Ago"
}
else{
timeAgo = "\(days) Days Ago"
}
}
if(months > 0){
if months < 2 {
timeAgo = "Month Ago"
}
else{
timeAgo = "\(months) Months Ago"
}
}
if(years > 0){
if years < 2 {
timeAgo = "Year Ago"
}
else{
timeAgo = "\(years) Years Ago"
}
}
DLog("timeAgo is ===> \(timeAgo)")
return timeAgo;
}

Time converter "071953Z" (ZULU TO CURRENT TIME)

I'm using an api that gives me the following time format:
"071953Z"
07 is the day
19 is the hours
53 are the minutes
Z is Zulu time
As you can see, there is no current month and year, i also want that to be added but i dont know how.
Could you help me to convert this to the current time? Thanks!
I think your best bet is to store the partial-datetime in a DateComponents. Then you can add a month and year to it and use Foundation's calendar API to do what you want:
extension DateComponents {
static func from(str: String) -> DateComponents? {
guard str.characters.count == 7 else {
return nil
}
var components = DateComponents()
components.timeZone = TimeZone(secondsFromGMT: 0)
components.calendar = Calendar(identifier: .gregorian)
let ranges = [0,2,4].map {
return str.index(str.startIndex, offsetBy: $0)..<str.index(str.startIndex, offsetBy: $0 + 2)
}
if let day = Int(str[ranges[0]]) {
components.day = day
} else {
return nil
}
if let hour = Int(str[ranges[1]]), hour < 24 {
components.hour = hour
} else {
return nil
}
if let minute = Int(str[ranges[2]]), minute < 60 {
components.minute = minute
} else {
return nil
}
return components
}
}
// This formatter is to convert the date to your local time. Configure to taste
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .medium
if var components = DateComponents.from(str: "071953Z") {
components.year = 2017
components.month = 4
if let date = components.date {
print(formatter.string(from: date))
} else {
print("\(components) does not make a valid date")
}
}

Resources