I am new to Swift and I have not worked with NSDate. For my app I need how to make to calculate how many days there are until the event. The date of the event is written with DatePicker on Firebase, and I need to calculate from the current date how many days are left until the day it's written. All I need is to count down the days.
Convert your data from DatePicker to Date object, and you can use the following function which returns an Int, a representation of the number of days to a passed date.
func daysTo(date: Date) -> Int? {
let calendar = Calendar.current
let date1 = calendar.startOfDay(for: Date())
let date2 = calendar.startOfDay(for: date)
let components = calendar.dateComponents([.day], from: date1, to: date2)
return components.day
}
you can use this extension for find difference between date:
extension Date {
public func diffrenceTime() -> (Int, Int) {
var cal = Calendar.init(identifier: .persian)
let d1 = Date()
let components = cal.dateComponents([.hour, .minute], from: self, to: d1)
let diffHour = components.hour!
let diffMinute = components.minute!
return (diffHour, diffMinute)
}
public func fullDistance(from date: Date, resultIn component: Calendar.Component, calendar: Calendar = .current) -> Int? {
calendar.dateComponents([component], from: self, to: date).value(for: component)
}
public func distance(from date: Date, only component: Calendar.Component, calendar: Calendar = .current) -> Int {
let days1 = calendar.component(component, from: self)
let days2 = calendar.component(component, from: date)
return days1 - days2
}
public func hasSame(_ component: Calendar.Component, as date: Date) -> Bool {
self.distance(from: date, only: component) == 0
}
}
example for use:
let dateOne = Date() // or any date
let dateTwo = getDateFromServer // your second date for
// option One
let distanceDay = dateOne.fullDistance(from: dateTwo, resultIn: .day)
var cal = Calendar.current // for your calendar
cal.locale = Locale.init(identifier: "EN")
// option Two
let distanceDay = dateOne.fullDistance(from: dateTwo, resultIn: .day, calendar: cal)
you can set hour, minute or any Component for find difference instead of day in result parameter
I am using this code to add dots for events and the dots appear perfectly fine but when I select the day with the event, the dot permanently disappears and even when selecting another day, the dot still doesnt reappear.
here is my code to make the dots appear at the start
func calendar(_ calendar: FSCalendar, willDisplay cell: FSCalendarCell, for date: Date, at monthPosition: FSCalendarMonthPosition) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let dateString = dateFormatter.string(from: date)
cell.eventIndicator.isHidden = false
for wp in worriesPanic{
let wPDateStr = dateFormatter.string(from: wp.date!)
if wPDateStr == dateString {
cell.eventIndicator.numberOfEvents = 1
break
}
}
}
You can use image method for show event.
func calendar(_ calendar: FSCalendar, imageFor date: Date) -> UIImage? {
let dateString = self.dateFormatter1.string(from: date)
if self.yourDate.contains(dateString) {
return UIImage(named:"img")
}
return nil
}
I'm setting a date to a certain birthdayTextField like so
#objc func birthdayDatePickerValueChanged(sender: UIDatePicker) {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none
birthdayTextField.text = formatter.string(from: sender.date)
}
Now this textfield value is stored in coredata in a string attribute. There can be many such birthday dates stored in coredata. Now when I fetch these dates from the database, I want to show in a tableview only those dates which come in the following month.
How can it be achieved...?
This is a solution using the powerful date math abilities of Calendar together with DateComponents in a Date extension.
It calculates the first day of next month with nextDate(after:matching:matchingPolicy:) looking for day == 1
It compares the given date with the first date of next month to the month granularity with compare(:to:toGranularity:).
extension Date {
func isDateInNextMonth() -> Bool {
let calendar = Calendar.current
let nextMonth = calendar.nextDate(after: Date(), matching: DateComponents(day:1), matchingPolicy: .nextTime)!
return calendar.compare(self, to: nextMonth, toGranularity: .month) == .orderedSame
}
}
Use it simply in your method
sender.date.isDateInNextMonth()
Or – more versatile – according to the other isDateIn... methods as extension of Calendar
extension Calendar {
func isDateInNextMonth(_ date : Date) -> Bool {
let nextMonth = self.nextDate(after: Date(), matching: DateComponents(day:1), matchingPolicy: .nextTime)!
return self.compare(date, to: nextMonth, toGranularity: .month) == .orderedSame
}
}
and use it
Calendar.current.isDateInNextMonth(sender.date)
Edit:
If you want to check if the date is in the next 30 days it's still easier
extension Calendar {
func isDateInNextThirtyDays(_ date : Date) -> Bool {
return self.dateComponents([.month], from: Date(), to:date).month! < 1
}
}
I'm using fs calendar and i'm trying to set event dots from an array named dates. this array has event dates in it that are saved in string form. so i have to convert each index to date and then set an event dot for that date.
here is my attempt to do so:
if dates.isEmpty == false {
func calendar(_calendar: FSCalendar!, hasEventForDate dateFormatter: DateFormatter) -> Bool {
for i in 0...dates.count - 1 {
let dateFormatter = DateFormatter ()
dateFormatter.dateFormat = "yyyy/MM/dd"
dateFormatter.locale = Locale.init(identifier: "fa_IR")
dateFormatter.date(from: dates[i])
dateFormatter.dateFormat = "yyyy/MM/dd"
return true
}
return false
}
}
but nothing happens and there is no event dot when i compile the code. what am i doing wrong?
First recommendation, use a dictionary instead of array of dates, is less demanding to lookup dates in a dictionary
Second if you need use values in an array then you should use for in and use objects directly, not index to get objects in the original array, also declare the date formatter only once
Third you need use func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int because func calendar(_calendar: FSCalendar!, hasEventForDate dateFormatter: DateFormatter) -> Bool is deprecated
Try with this code
func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy/MM/dd"
dateFormatter.locale = Locale.init(identifier: "fa_IR")
for dateStr in dates{
if(dateFormatter.string(from: date) == dateStr)
{
return 1
}
}
return 0
}
In a swift playground, I have been using
NSDate.date()
But, this always appears with the time element appended. For my app I need to ignore the time element. Is this possible in Swift? How can it be done? Even if I could set the time element to be the same time on every date that would work too.
Also, I am trying to compare two dates and at the moment I am using the following code:
var earlierDate:NSDate = firstDate.earlierDate(secondDate)
Is this the only way or can I do this in a way that ignores the time element? For instance I don't want a result if they are the same day, but different times.
Use this Calendar function to compare dates in iOS 8.0+
func compare(_ date1: Date, to date2: Date, toGranularity component: Calendar.Component) -> ComparisonResult
passing .day as the unit
Use this function as follows:
let now = Date()
// "Sep 23, 2015, 10:26 AM"
let olderDate = Date(timeIntervalSinceNow: -10000)
// "Sep 23, 2015, 7:40 AM"
var order = Calendar.current.compare(now, to: olderDate, toGranularity: .hour)
switch order {
case .orderedDescending:
print("DESCENDING")
case .orderedAscending:
print("ASCENDING")
case .orderedSame:
print("SAME")
}
// Compare to hour: DESCENDING
var order = Calendar.current.compare(now, to: olderDate, toGranularity: .day)
switch order {
case .orderedDescending:
print("DESCENDING")
case .orderedAscending:
print("ASCENDING")
case .orderedSame:
print("SAME")
}
// Compare to day: SAME
Xcode 11.2.1, Swift 5 & Above
Checks whether the date has same day component.
Calendar.current.isDate(date1, equalTo: date2, toGranularity: .day)
Adjust toGranularity as your need.
There are several useful methods in NSCalendar in iOS 8.0+:
startOfDayForDate, isDateInToday, isDateInYesterday, isDateInTomorrow
And even to compare days:
func isDate(date1: NSDate!, inSameDayAsDate date2: NSDate!) -> Bool
To ignore the time element you can use this:
var toDay = Calendar.current.startOfDay(for: Date())
But, if you have to support also iOS 7, you can always write an extension
extension NSCalendar {
func myStartOfDayForDate(date: NSDate!) -> NSDate!
{
let systemVersion:NSString = UIDevice.currentDevice().systemVersion
if systemVersion.floatValue >= 8.0 {
return self.startOfDayForDate(date)
} else {
return self.dateFromComponents(self.components(.CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay, fromDate: date))
}
}
}
In Swift 4:
func compareDate(date1:Date, date2:Date) -> Bool {
let order = NSCalendar.current.compare(date1, to: date2, toGranularity: .day)
switch order {
case .orderedSame:
return true
default:
return false
}
}
I wrote the following method to compare two dates by borrowing from Ashley Mills solution. It compares two dates and returns true if the two dates are the same (stripped of time).
func compareDate(date1:NSDate, date2:NSDate) -> Bool {
let order = NSCalendar.currentCalendar().compareDate(date1, toDate: date2,
toUnitGranularity: .Day)
switch order {
case .OrderedSame:
return true
default:
return false
}
}
And it is called like this:
if compareDate(today, date2: anotherDate) {
// The two dates are on the same day.
}
Two Dates comparisions in swift.
// Date comparision to compare current date and end date.
var dateComparisionResult:NSComparisonResult = currentDate.compare(endDate)
if dateComparisionResult == NSComparisonResult.OrderedAscending
{
// Current date is smaller than end date.
}
else if dateComparisionResult == NSComparisonResult.OrderedDescending
{
// Current date is greater than end date.
}
else if dateComparisionResult == NSComparisonResult.OrderedSame
{
// Current date and end date are same.
}
I wrote a Swift 4 extension for comparing two dates:
import Foundation
extension Date {
func isSameDate(_ comparisonDate: Date) -> Bool {
let order = Calendar.current.compare(self, to: comparisonDate, toGranularity: .day)
return order == .orderedSame
}
func isBeforeDate(_ comparisonDate: Date) -> Bool {
let order = Calendar.current.compare(self, to: comparisonDate, toGranularity: .day)
return order == .orderedAscending
}
func isAfterDate(_ comparisonDate: Date) -> Bool {
let order = Calendar.current.compare(self, to: comparisonDate, toGranularity: .day)
return order == .orderedDescending
}
}
Usage:
startDate.isSameDateAs(endDate) // returns a true or false
For iOS7 support
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let date1String = dateFormatter.stringFromDate(date1)
let date2String = dateFormatter.stringFromDate(date2)
if date1String == date2String {
println("Equal date")
}
You can compare two dates using it's description.
let date1 = NSDate()
let date2 = NSDate(timeIntervalSinceNow: 120)
if date1.description == date2.description {
print(true)
} else {
print(false) // false (I have added 2 seconds between them)
}
If you want set the time element of your dates to a different time you can do as follow:
extension NSDate {
struct Calendar {
static let gregorian = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
}
var day: Int { return Calendar.gregorian.component(.Day, fromDate: self) }
var month: Int { return Calendar.gregorian.component(.Month, fromDate: self) }
var year: Int { return Calendar.gregorian.component(.Year, fromDate: self) }
var noon: NSDate {
return Calendar.gregorian.dateWithEra(1, year: year, month: month, day: day, hour: 12, minute: 0, second: 0, nanosecond: 0)!
}
}
let date1 = NSDate()
let date2 = NSDate(timeIntervalSinceNow: 120)
print(date1.noon == date2.noon) // true
or you can also do it using NSDateFormatter:
extension NSDate {
struct Date {
static let formatterYYYYMMDD: NSDateFormatter = {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyyMMdd"
return formatter
}()
}
var yearMonthDay: String {
return Date.formatterYYYYMMDD.stringFromDate(self)
}
func isSameDayAs(date:NSDate) -> Bool {
return yearMonthDay == date.yearMonthDay
}
}
let date1 = NSDate()
let date2 = NSDate(timeIntervalSinceNow: 120)
print(date1.yearMonthDay == date2.yearMonthDay) // true
print(date1.isSameDayAs(date2)) // true
Another option (iOS8+) is to use calendar method isDate(inSameDayAsDate:):
extension NSDate {
struct Calendar {
static let gregorian = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
}
func isInSameDayAs(date date: NSDate) -> Bool {
return Calendar.gregorian.isDate(self, inSameDayAsDate: date)
}
}
let date1 = NSDate()
let date2 = NSDate(timeIntervalSinceNow: 120)
if date1.isInSameDayAs(date: date2 ){
print(true) // true
} else {
print(false)
}
Swift 3
let order = NSCalendar.current.compare(date1, to: date2, toGranularity: .day)
if order == .orderedAscending {
// date 1 is older
}
else if order == .orderedDescending {
// date 1 is newer
}
else if order == .orderedSame {
// same day/hour depending on granularity parameter
}
For Swift3
var order = NSCalendar.current.compare(firstDate, to: secondDate, toGranularity: .hour)
if order == .orderedSame {
//Both the dates are same.
//Your Logic.
}
Swift:
extension NSDate {
/**
Compares current date with the given one down to the seconds.
If date==nil, then always return false
:param: date date to compare or nil
:returns: true if the dates has equal years, months, days, hours, minutes and seconds.
*/
func sameDate(date: NSDate?) -> Bool {
if let d = date {
let calendar = NSCalendar.currentCalendar()
if NSComparisonResult.OrderedSame == calendar.compareDate(self, toDate: d, toUnitGranularity: NSCalendarUnit.SecondCalendarUnit) {
return true
}
}
return false
}
}
When you NSDate.date() in the playground, you see the default description printed. Use NSDateFormatter to print a localized description of the date object, possibly with only the date portion.
To zero out specific portions of a date (for the sake of comparison), use NSDateComponents in conjunction with NSCalendar.
In my experience, most people's problems with using NSDate comes from the incorrect assumption that an NSDate can be used to represent a date in the 'normal' sense (i.e. a 24 period starting at midnight in the local timezone). In normal (everyday / non-programming) usage, 1st January 2014 in London is the same date as 1st January in Beijing or New York even though they cover different periods in real time. To take this to the extreme, the time on Christmas Island is UTC+14 while the time on Midway Island is UTC-11. So 1st January 2014 on these two island are the same date even though one doesn't even start until the other has been completed for an hour.
If that is the kind of date you are recording (and if you are not recording the time component, it probably is), then do not use NSDate (which stores only seconds past 2001-01-01 00:00 UTC, nothing else) but store the year month and day as integers - perhaps by creating your own CivilDate class that wraps these values - and use that instead.
Only dip into NSDate to compare dates and then make sure to explicitly declare the time zone as "UTC" on both NSDates for comparison purposes.
Swift 4
func compareDate(date1:Date, date2:Date) -> Bool {
let order = Calendar.current.compare(date1, to: date2,toGranularity: .day)
switch order {
case .orderedSame:
return true
default:
return false
}
}
If you need to compare just if date is in the same day as other date use this:
Calendar.current.isDate(date1, inSameDayAs: date2)
To answer your question:
Is this possible in Swift?
Yes, it is possible
Ahh, you also want to now HOW
let cal = NSCalendar.currentCalendar()
cal.rangeOfUnit(.DayCalendarUnit, startDate: &d1, interval: nil, forDate: d1) // d1 NSDate?
cal.rangeOfUnit(.DayCalendarUnit, startDate: &d2, interval: nil, forDate: d2) // d2 NSDate?
Now d1 and d2 will contain the dates at beginning of their days.
compare with d1!.compare(d2!)
To display them without time portion, us NSDateFormatter.