How to calculate the difference in days for two Date in Swift as days start at 6 a.m. instead of 0 - swift

I'm trying to get the difference in two Date objects.
I know how to do it basically, which is like this:
let date = Date()
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale.current
print(count)
// print(history[count - 1].value(forKey: "multipleURLs") as! [String])
let latestDate = history[count - 1].value(forKey: "date") as! Date
let calendar = Calendar.current
let components = calendar.dateComponents([.year, .month, .day, .hour], from: date)
let nowDate = calendar.startOfDay(for: date)
let lastDate = calendar.startOfDay(for: latestDate)
let diffDateComponent = (calendar as NSCalendar).components([NSCalendar.Unit.year, NSCalendar.Unit.month, NSCalendar.Unit.day], from: lastDate, to: nowDate, options: NSCalendar.Options.init(rawValue: 0))
print("Status Checked" + String(describing: diffDateComponent.day))
But I'm trying to get the result as a day start from 6 a.m. in the morning instead of 0.
So I did something like this:
let nowDate = calendar.startOfDay(for: date.addingTimeInterval(-3600))
let lastDate = calendar.startOfDay(for: latestDate.addingTimeInterval(-3600))
But it doesn't seem to be working, can anyone help me with this?

This will set each date to 6 AM (in the current locale of the Date):
import Foundation
// set up dates
let calendar = Calendar.current
let date = Date()
if let latestDate = calendar.date(byAdding: .day, value: -5, to: date) {
// set each day to 6 AM
if let nowDate = calendar.date(bySettingHour: 6, minute: 0, second: 0, of: date ),
let lastDate = calendar.date(bySettingHour: 6, minute: 0, second: 0, of: latestDate) {
print( date.description(with: .current),
nowDate.description(with: .current),
lastDate.description(with: .current),
separator: "\n")
// calculate difference in days
if let days = calendar.dateComponents([.day], from: lastDate, to: nowDate).day {
print("Days since: \(days)")
}
}
}
// Thursday, June 8, 2017 at 7:59:42 AM Eastern Daylight Time
// Thursday, June 8, 2017 at 6:00:00 AM Eastern Daylight Time
// Saturday, June 3, 2017 at 6:00:00 AM Eastern Daylight Time
// Days since: 5

Thanks guy, I finally figured it out. In case anyone need it, this is how it's done:
let date = Date()
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale.current
let latestDate = history[count - 1].value(forKey: "date") as! Date
let calendar = Calendar.current
let nowDate = calendar.date(byAdding: .hour, value: -6, to: date)
let lastDate = calendar.date(byAdding: .hour, value: -6, to: latestDate)
let finalNowDate = calendar.startOfDay(for: nowDate!)
let finalLastDate = calendar.startOfDay(for: lastDate!)
let diffDateComponent = calendar.dateComponents([.day], from: finalLastDate, to: finalNowDate)
print("dif\(diffDateComponent.day)")

Related

How to round up date to midnight in Swift? [duplicate]

I'm trying to get the first and last day of the month in swift.
So far I have the following:
let dateFormatter = NSDateFormatter()
let date = NSDate()
dateFormatter.dateFormat = "yyyy-MM-dd"
let calendar = NSCalendar.currentCalendar()
let components = calendar.components([.Year, .Month, .Day, .Hour, .Minute, .Second], fromDate: date)
let month = components.month
let year = components.year
let startOfMonth = ("\(year)-\(month)-01")
But I'm not sure how to get the last date. Is there a built in method I'm missing? Obviously it has to take into account leap years etc.
Swift 3 and 4 drop-in extensions
This actually gets a lot easier with Swift 3+:
You can do it without guard (you could if you wanted to, but because DateComponents is a non-optional type now, it's no longer necessary).
Using iOS 8's startOfDayForDate (now startOfDay), you don't need to manually set the time to 12pm unless you're doing some really crazy calendar calculations across time zones.
It's worth mentioning that some of the other answers claim you can shortcut this by using Calendar.current.date(byAdding: .month, value: 0, to: Date())!, but where this fails, is that it doesn't actually zero out the day, or account for differences in timezones.
Here you go:
extension Date {
func startOfMonth() -> Date {
return Calendar.current.date(from: Calendar.current.dateComponents([.year, .month], from: Calendar.current.startOfDay(for: self)))!
}
func endOfMonth() -> Date {
return Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: self.startOfMonth())!
}
}
print(Date().startOfMonth()) // "2018-02-01 08:00:00 +0000\n"
print(Date().endOfMonth()) // "2018-02-28 08:00:00 +0000\n"
You get the first day of the month simply with
let components = calendar.components([.Year, .Month], fromDate: date)
let startOfMonth = calendar.dateFromComponents(components)!
print(dateFormatter.stringFromDate(startOfMonth)) // 2015-11-01
To get the last day of the month, add one month and subtract one day:
let comps2 = NSDateComponents()
comps2.month = 1
comps2.day = -1
let endOfMonth = calendar.dateByAddingComponents(comps2, toDate: startOfMonth, options: [])!
print(dateFormatter.stringFromDate(endOfMonth)) // 2015-11-30
Alternatively, use the rangeOfUnit method which gives you
the start and the length of the month:
var startOfMonth : NSDate?
var lengthOfMonth : NSTimeInterval = 0
calendar.rangeOfUnit(.Month, startDate: &startOfMonth, interval: &lengthOfMonth, forDate: date)
For a date on the last day of month, add the length of the month minus one second:
let endOfMonth = startOfMonth!.dateByAddingTimeInterval(lengthOfMonth - 1)
Updated for Swift5:
extension Date {
var startOfDay: Date {
return Calendar.current.startOfDay(for: self)
}
var startOfMonth: Date {
let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents([.year, .month], from: self)
return calendar.date(from: components)!
}
var endOfDay: Date {
var components = DateComponents()
components.day = 1
components.second = -1
return Calendar.current.date(byAdding: components, to: startOfDay)!
}
var endOfMonth: Date {
var components = DateComponents()
components.month = 1
components.second = -1
return Calendar(identifier: .gregorian).date(byAdding: components, to: startOfMonth)!
}
func isMonday() -> Bool {
let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents([.weekday], from: self)
return components.weekday == 2
}
}
With Swift 3 & iOS 10 the easiest way I found to do this is Calendar's dateInterval(of:for:):
guard let interval = calendar.dateInterval(of: .month, for: Date()) else { return }
You can then use interval.start and interval.end to get the dates you need.
Swift 3
Many date example for :
Last 6 month,
last 3 month,
yesterday, last 7 day, last 30 day, previous month,
current month start & end, last month start & end date
let startDate = dateFormatter.string(from: Date().getThisMonthStart()!)
let endDate = dateFormatter.string(from: Date().getThisMonthEnd()!)
extension Date {
func getLast6Month() -> Date? {
return Calendar.current.date(byAdding: .month, value: -6, to: self)
}
func getLast3Month() -> Date? {
return Calendar.current.date(byAdding: .month, value: -3, to: self)
}
func getYesterday() -> Date? {
return Calendar.current.date(byAdding: .day, value: -1, to: self)
}
func getLast7Day() -> Date? {
return Calendar.current.date(byAdding: .day, value: -7, to: self)
}
func getLast30Day() -> Date? {
return Calendar.current.date(byAdding: .day, value: -30, to: self)
}
func getPreviousMonth() -> Date? {
return Calendar.current.date(byAdding: .month, value: -1, to: self)
}
// This Month Start
func getThisMonthStart() -> Date? {
let components = Calendar.current.dateComponents([.year, .month], from: self)
return Calendar.current.date(from: components)!
}
func getThisMonthEnd() -> Date? {
let components:NSDateComponents = Calendar.current.dateComponents([.year, .month], from: self) as NSDateComponents
components.month += 1
components.day = 1
components.day -= 1
return Calendar.current.date(from: components as DateComponents)!
}
//Last Month Start
func getLastMonthStart() -> Date? {
let components:NSDateComponents = Calendar.current.dateComponents([.year, .month], from: self) as NSDateComponents
components.month -= 1
return Calendar.current.date(from: components as DateComponents)!
}
//Last Month End
func getLastMonthEnd() -> Date? {
let components:NSDateComponents = Calendar.current.dateComponents([.year, .month], from: self) as NSDateComponents
components.day = 1
components.day -= 1
return Calendar.current.date(from: components as DateComponents)!
}
}
Swift 4
If you only need the ordinal day:
func lastDay(ofMonth m: Int, year y: Int) -> Int {
let cal = Calendar.current
var comps = DateComponents(calendar: cal, year: y, month: m)
comps.setValue(m + 1, for: .month)
comps.setValue(0, for: .day)
let date = cal.date(from: comps)!
return cal.component(.day, from: date)
}
lastDay(ofMonth: 2, year: 2018) // 28
lastDay(ofMonth: 2, year: 2020) // 29
This is the simplest way that I found (Swift 5+):
extension Date {
func getStart(of component: Calendar.Component, calendar: Calendar = Calendar.current) -> Date? {
return calendar.dateInterval(of: component, for: self)?.start
}
func getEnd(of component: Calendar.Component, calendar: Calendar = Calendar.current) -> Date? {
return calendar.dateInterval(of: component, for: self)?.end
}
}
Here is easiest solution:
extension Date {
func startOfMonth() -> Date {
let interval = Calendar.current.dateInterval(of: .month, for: self)
return (interval?.start.toLocalTime())! // Without toLocalTime it give last months last date
}
func endOfMonth() -> Date {
let interval = Calendar.current.dateInterval(of: .month, for: self)
return interval!.end
}
// Convert UTC (or GMT) to local time
func toLocalTime() -> Date {
let timezone = TimeZone.current
let seconds = TimeInterval(timezone.secondsFromGMT(for: self))
return Date(timeInterval: seconds, since: self)
}}
and then call these with your date instance:
print(Date().startOfMonth())
print(Date().endOfMonth())
2017...
First, get the month you need:
let cal = Calendar.current
let d = Calendar.current.date(byAdding: .month, value: 0, to: Date())!
// for "last month" just use -1, for "next month" just use 1, etc
To get the day-of-the-week for the first day of the month:
let c = cal.dateComponents([.year, .month], from: d)
let FDOM = cal.date(from: c)!
let dowFDOM = cal.component(.weekday, from: FDOM)
print("the day-of-week on the 1st is ... \(dowFDOM)")
// so, that's 1=Sunday, 2=Monday, etc.
To get the number of days in the month:
let r = cal.range(of: .day, in: .month, for: d)!
let kDays = r.count
print("the number of days is ... \(kDays)")
With Swift 3, you can choose one of the two following patters in order to retrieve the first and last days of a month.
#1. Using Calendar dateComponents(_:from:), date(from:) and date(byAdding:to:wrappingComponents:) methods
With this pattern, you first get the date of the first day of a month then add a month and remove a day from it in order to get the date of the last day of the month. The Playground code below shows how to set it:
import Foundation
// Set calendar and date
let calendar = Calendar.current
let date = calendar.date(byAdding: DateComponents(day: -10), to: Date())!
// Get first day of month
let firstDayComponents = calendar.dateComponents([.year, .month], from: date)
let firstDay = calendar.date(from: firstDayComponents)!
// Get last day of month
let lastDayComponents = DateComponents(month: 1, day: -1)
let lastDay = calendar.date(byAdding: lastDayComponents, to: firstDay)!
// Set date formatter
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_UK")
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .long
// Print results
print(dateFormatter.string(from: date)) // Prints: 22 March 2017 at 18:07:15 CET
print(dateFormatter.string(from: firstDay)) // Prints: 1 March 2017 at 00:00:00 CET
print(dateFormatter.string(from: lastDay)) // Prints: 31 March 2017 at 00:00:00 CEST
#2. Using Calendar range(of:in:for:), dateComponents(_:from:) and date(from:) and methods
With this pattern, you get a range of absolute day values in a month and then retrieve the dates of the first day and last day of the month from it. The Playground code below shows how to set it:
import Foundation
// Set calendar and date
let calendar = Calendar.current
let date = calendar.date(byAdding: DateComponents(day: -10), to: Date())!
// Get range of days in month
let range = calendar.range(of: .day, in: .month, for: date)! // Range(1..<32)
// Get first day of month
var firstDayComponents = calendar.dateComponents([.year, .month], from: date)
firstDayComponents.day = range.lowerBound
let firstDay = calendar.date(from: firstDayComponents)!
// Get last day of month
var lastDayComponents = calendar.dateComponents([.year, .month], from: date)
lastDayComponents.day = range.upperBound - 1
//lastDayComponents.day = range.count // also works
let lastDay = calendar.date(from: lastDayComponents)!
// Set date formatter
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_UK")
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .long
// Print results
print(dateFormatter.string(from: date)) // prints: 22 March 2017 at 18:07:15 CET
print(dateFormatter.string(from: firstDay)) // prints: 1 March 2017 at 00:00:00 CET
print(dateFormatter.string(from: lastDay)) // prints: 31 March 2017 at 00:00:00 CEST
In swift 3, if you put 0 to day component you can get the last day of the month. There's an example code:
public func isMoreDays(date: Date, asc: Bool)->Bool{
//components
var dayComponents = self.getDateComponents(date: date)
//asc is true if ascendant or false if descendant
dayComponents.day = asc ? 0 : 1
//plus 1 to month 'cos if you set up day to 0 you are going to the previous month
dayComponents.month = asc ? dayComponents.month! + 1 : dayComponents.month
//instantiate calendar and get the date
let calendar : Calendar = NSCalendar.current
let day = calendar.date(from: dayComponents)
//date comparison
if(day?.compare(date) == .orderedSame){
return false
}
return true
}
You can use the following extensions here :
let today = Date()
let startOfMonth = today.beginning(of: .month)
let endOfMonth = today.end(of: .month)

Start and end date of next week swift

I need to get date like "31 of January - 6 of February"
I used next code:
let calendar = Calendar(identifier: .gregorian)
let dateFormatter = DateFormatter()
let weekday = 1
let sundayComponents = DateComponents(calendar: calendar, weekday: weekday)
let nextSunday = calendar.nextDate(after: Date(), matching: sundayComponents, matchingPolicy: .nextTimePreservingSmallerComponents)
I need to take all this period from Monday to Sunday
You can use dateInterval(of:start:interval:for:):
let formatter = DateFormatter()
let calendar: Calendar = .current
let now = Date()
let weekFromNow = calendar.date(byAdding: .weekOfYear, value: 1, to: now)!
var start = weekFromNow
var interval: TimeInterval = 0
guard calendar.dateInterval(of: .weekOfYear, start: &start, interval: &interval, for: weekFromNow) else { return }
let end = calendar.date(byAdding: .day, value: -1, to: start.addingTimeInterval(interval))!
// or let end = start.addingTimeInterval(interval - 1)
// or let end = calendar.date(byAdding: .day, value: 6, to: start)!
print(formatter.string(from: start), "-", formatter.string(from: end))
This will honor the “start of week” on your device.

how to use DateComponentsFormatter for showing date as "Today" or "Yesterday"?

I'm using swift in my project and I want to show specific date like this: 5 days ago or 5 month ago or ...
I am using DateComponentsFormatter and it's doing well, but the problem is I want to show 1 day ago like "Yesterday" or showing 3 second ago like "Today". how can I do this? can I use DateComponentsFormatter for this problem? this is my codes:
func shortDate(_ local: LocaleIdentifier = .fa) -> String {
let now = Date()
let date = self
let formatter = DateComponentsFormatter()
formatter.calendar?.locale = Locale(identifier: local.rawValue)
formatter.unitsStyle = .full
formatter.maximumUnitCount = 1
formatter.allowedUnits = [.year, .month, .day, .hour, .minute, .second]
guard let timeString = formatter.string(from: date, to: now) else {
return ""
}
return String(format: "Ago".localized, timeString)
}
For iOS 13 or later you can use RelativeDateTimeFormatter
let relativeDateTimeFormatter = RelativeDateTimeFormatter()
relativeDateTimeFormatter.dateTimeStyle = .named
relativeDateTimeFormatter.unitsStyle = .full
let date = Date.init(timeIntervalSinceNow: -60*60*24)
relativeDateTimeFormatter.string(for: date) // "yesterday"
edit/update:
If you would like to support iOS 11 you would need to implement your own relative date formatter. You can use Calendar method isDateInToday and isDateInYesterday to combine a relative date formatter with date components formatter. Note that there is no need to check the time interval for setting a single unit in your date components formatter you can set the allowed units of your date components formatter, just make sure you set them respecting the desired priority to be displayed:
// This will avoid creating a formatter every time you call relativeDateFormatted property
extension Formatter {
static let dateComponents: DateComponentsFormatter = {
let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.allowedUnits = [.day, .month, .year] // check the order of the units it does matter when allowing only 1 unit to be displayed
dateComponentsFormatter.maximumUnitCount = 1
dateComponentsFormatter.unitsStyle = .full
return dateComponentsFormatter
}()
static let relativeDate: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.doesRelativeDateFormatting = true
dateFormatter.timeStyle = .none
dateFormatter.dateStyle = .medium
return dateFormatter
}()
}
extension Date {
var relativeDateFormatted: String {
Calendar.current.isDateInToday(self) || Calendar.current.isDateInYesterday(self) ?
Formatter.relativeDate.string(from: self) :
Formatter.dateComponents.string(from: self, to: Date()) ?? ""
}
}
Playground testing:
let date1 = DateComponents(calendar: .current, year: 2020, month: 9, day: 4, hour: 5).date!
let date2 = DateComponents(calendar: .current, year: 2020, month: 9, day: 3, hour: 23, minute: 50).date!
let date3 = DateComponents(calendar: .current, year: 2020, month: 8, day: 25, hour: 10).date!
let date4 = DateComponents(calendar: .current, year: 2020, month: 8, day: 3).date!
let date5 = DateComponents(calendar: .current, year: 2019, month: 8, day: 27).date!
date1.relativeDateFormatted // "Today"
date2.relativeDateFormatted // "Yesterday"
date3.relativeDateFormatted // "10 days"
date4.relativeDateFormatted // "1 month"
date5.relativeDateFormatted // "1 year"
You are looking for the doesRelativeDateFormatting property of a DateFormatter.
I think, I must use both DateComponentsFormatter for more than 1 day and DateFormatter for showing Today(day=0) and Yesterday(day=1) for my problem.
I found my answer here.
but I make codes a little better, this is my codes:
var calendar = Calendar.current
calendar.locale = Locale(identifier: local.rawValue)
let componentFormatter = DateComponentsFormatter()
componentFormatter.calendar = calendar
componentFormatter.unitsStyle = .full
componentFormatter.maximumUnitCount = 1
let interval = Calendar.current.dateComponents([.year, .month, .day], from: self, to: Date())
if let year = interval.year, year > 0 {
componentFormatter.allowedUnits = .year
} else if let month = interval.month, month > 0 {
componentFormatter.allowedUnits = .month
} else if let day = interval.day, day > 1 {
componentFormatter.allowedUnits = .day
} else { // if let day = interval.day, day == 0 || day == 1 for Today or Yesterday
let formatter = DateFormatter()
formatter.doesRelativeDateFormatting = true
formatter.calendar.locale = Locale(identifier: local.rawValue)
formatter.timeStyle = .none
formatter.dateStyle = .medium
return formatter.string(from: self)
}
guard let timeString = componentFormatter.string(from: self, to: Date()) else {
return ""
}
return String(format: "Ago".localized, timeString)

Set Tomorrow Date from custom date swift

i have an app and user can select date from that. How to Set Tomorrow Date from selected date. For example if user select date 24 it print 25 and if user select date 31 it print 1.
Right now i'm using current date to set tomorrow's date
let dates = Date()
var tomorrow: Date {
return Calendar.current.date(byAdding: .day, value: 1, to: Date())!
}
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd"
let dateOutDefault = dateFormatter.string(from: tomorrow as Date)
How to do something like that using custom date?
import Foundation
extension Date {
var tomorrow: Date? {
return Calendar.current.date(byAdding: .day, value: 1, to: self)
}
}
let today = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM dd, yyyy, HH:mm b"
if let tomorrow = today.tomorrow {
let tomorrowString = dateFormatter.string(from: tomorrow)
print("\(tomorrowString)" // "Mar 23, 2017, 00:14 AM\n"
}
let dayComponenet = NSDateComponents()
dayComponenet.day = 1
let theCalendar = NSCalendar.currentCalendar()
let nextDate = theCalendar.dateByAddingComponents(dayComponenet, toDate: NSDate(), options: NSCalendarOptions(rawValue: UInt(0)))
print(nextDate);
let date = nextDate
let unitFlags: NSCalendarUnit = [.Hour, .Day, .Month, .Year]
let components = NSCalendar.currentCalendar().components(unitFlags, fromDate: date!)
print(components.day);

How to change the current day's hours and minutes in Swift?

If I create a Date() to get the current date and time, I want to create a new date from that but with different hour, minute, and zero seconds, what's the easiest way to do it using Swift? I've been finding so many examples with 'getting' but not 'setting'.
Be aware that for locales that uses Daylight Saving Times, on clock change days, some hours may not exist or they may occur twice. Both solutions below return a Date? and use force-unwrapping. You should handle possible nil in your app.
Swift 3+ and iOS 8 / OS X 10.9 or later
let date = Calendar.current.date(bySettingHour: 9, minute: 30, second: 0, of: Date())!
Swift 2
Use NSDateComponents / DateComponents:
let gregorian = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let now = NSDate()
let components = gregorian.components([.Year, .Month, .Day, .Hour, .Minute, .Second], fromDate: now)
// Change the time to 9:30:00 in your locale
components.hour = 9
components.minute = 30
components.second = 0
let date = gregorian.dateFromComponents(components)!
Note that if you call print(date), the printed time is in UTC. It's the same moment in time, just expressed in a different timezone from yours. Use a NSDateFormatter to convert it to your local time.
swift 3 date extension with timezone
extension Date {
public func setTime(hour: Int, min: Int, sec: Int, timeZoneAbbrev: String = "UTC") -> Date? {
let x: Set<Calendar.Component> = [.year, .month, .day, .hour, .minute, .second]
let cal = Calendar.current
var components = cal.dateComponents(x, from: self)
components.timeZone = TimeZone(abbreviation: timeZoneAbbrev)
components.hour = hour
components.minute = min
components.second = sec
return cal.date(from: components)
}
}
//Increase the day & hours in Swift
let dateformat = DateFormatter()
let timeformat = DateFormatter()
dateformat.dateStyle = .medium
timeformat.timeStyle = .medium
//Increase Day
let currentdate = Date()
let currentdateshow = dateformat.string(from: currentdate)
textfield2.text = currentdateshow
let myCurrentdate = dateformat.date(from: dateTimeString)!
let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: myCurrentdate) // Increase 1 Day
let tomorrowday = dateformat.string(from: tomorrow!)
text3.text = tomorrowday
text3.isEnabled = false
//increase Time
let time = Date()
let currenttime = timeformat.string(from: time)
text4.text = currenttime
let mycurrenttime = timeformat.date(from: currenttime)!
let increasetime = Calendar.current.date(byAdding: .hour, value: 2, to: mycurrenttime) //increase 2 hrs.
let increasemytime = timeformat.string(from: increasetime!)
text5.text = increasemytime