Relative Date And Time With Swift

Relative Date and Time with Swift

You probably have seen the date and time of the messages in the Messages app and other messagings that are relative to the current date and time. This post will explain how to calculate the relative date and time with Swift.

If you want to format dates and times in the form “2 hours ago” or “3 months ago”, Apple provides a dedicated formatter called RelativeDateTimeFormatter. This is localized for many languages, so you’ll automatically get back strings in French, German, Chinese, and more, all depending on the user’s locale.

Here’s an example code for RelativeDateTimeFormatter:

// the date you want to format
let exampleDate = Date().addingTimeInterval(-409000)

// ask for the full relative date
let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .spellOut

// get exampleDate relative to the current date
let relativeDate = formatter.localizedString(for: exampleDate, relativeTo: Date())

The RelativeDateTimeFormatter can be used only with iOS 13 and later and the format string could not be customized. For example, suppose we need the relative date and time string format to be as below:

Time DurationFormatString
within the current minute“Just now”
within the last minute“Last minute”
within today“h:mm a”“2:33 pm”
within yesterday“Yesterday”
within the current week“EE”“Sat”
within that current year“LLL dd”“Jun 18”
earlier than the current year“yyyy”2017
Relative date formatter output

To produce the relative date and time we create a custom formatter by subclassing the Formatter called AERelativeDateTimeFormatter as below:

class AERelativeDateTimeFormatter: Formatter {
	
	override func string(for obj: Any?) -> String? {
		
		guard let date = obj as? Date else { return nil }
		
		//Round down the current date
		let roundUnits: Set<Calendar.Component> = [.year, .month, .day]
		let roundedDateComponents = Calendar.current.dateComponents(roundUnits, from: date)
		
		guard let roundedCurrentDate = Calendar.current.date(from: roundedDateComponents) else {
			
			return "Unable to calculate"
		}
		
		let units: Set<Calendar.Component> = [.minute, .day, .year, .weekOfYear]
		let components = Calendar.current.dateComponents(units, from: roundedCurrentDate, to: Date())
		let dateFormatter = DateFormatter()
		dateFormatter.amSymbol = "AM"
		dateFormatter.pmSymbol = "PM"
		
		let year =      components.year ?? 0
		let weeks =     components.weekOfYear ?? 0
		let days =      components.day ?? 0
		
		if year >= 1 {
			
			dateFormatter.dateFormat = "yyyy"
			return dateFormatter.string(from: date)
		}
		
		if weeks >= 1 {
			
			dateFormatter.dateFormat = "LLL dd"
			return dateFormatter.string(from: date)
		}
		
		if days > 1 {
			
			dateFormatter.dateFormat = "EE"
			return dateFormatter.string(from: date)
		}
		
		if days == 1 {
			
			return "Yesterday"
		}
		
		let units2: Set<Calendar.Component> = [.minute]
		let components2 = Calendar.current.dateComponents(units2, from: date, to: Date())
		let minutes =   components2.minute ?? 0
		
		if minutes > 1 {
			
			dateFormatter.dateFormat = "h:mm a"
			return dateFormatter.string(from: date)
		}
		
		if minutes == 1 {
			
			return "Last minute"
		}
		
		return "Just now"
	}
}

First of all, in the guard statement, we check that the input object is of type Date. Then we round down the input date and compare it to the current date to check if it is in which section and then calculating the output string.

Also note that instead of comparing the desired date, first I round it down to the start of that date and then compare it to the current date. It helps us produce “Yesterday” if it is older than the last midnight. Otherwise, the relative date and time would not be correct for date older than the midnight and later than 24 hours ago. On the other hand, if it is in the current date, I compare the date under question directly with the current date to produce “time of day”, “Last minute” or “Just now”.

let exampleDate01 = Date(timeIntervalSinceNow: -334000)
let aeRelativeFormatter = AERelativeDateTimeFormatter()
print(aeRelativeFormatter.string(for: exampleDate01))

//Optional("Mon")

Hope to implement relative date and time with Swift in your project using this article. Feel free to leave your comments below.


Posted

in

by

Tags:

Comments

2 responses to “Relative Date and Time with Swift”

  1. Yusmle Avatar
    Yusmle

    Awesome article! Thanks for sharing!