Swift NSDate Comparison - swift

I need to schedule a notification. If the date made from the current date date components with the dateFromData time components is early than the current day, I would like to change it to the next day. Here is what I have so far. The date comparison dose not work no matter how I set it. It either always changes it or never changes it.
var dateComps = calendar.components(NSCalendarUnit.YearCalendarUnit | NSCalendarUnit.MonthCalendarUnit | NSCalendarUnit.DayCalendarUnit | NSCalendarUnit.HourCalendarUnit | NSCalendarUnit.MinuteCalendarUnit, fromDate: NSDate())
dateComps.hour = calendar.component(NSCalendarUnit.HourCalendarUnit, fromDate: dateFromData)
dateComps.minute = calendar.component(NSCalendarUnit.MinuteCalendarUnit, fromDate: dateFromData)
if fireDate.compare(NSDate()) == NSComparisonResult.OrderedDescending {
//change day to next day
dateComps.day += 1
println("Change day")
}else{
println("Do not change day")
}
let notifactionOfAmountOfWork = UILocalNotification()
notifactionOfAmountOfWork.category = "normalNotifactionCatagory"
notifactionOfAmountOfWork.fireDate = calendar.dateFromComponents(dateComps)

How your firstDate defined? The following program works, I just tried it. You could change comps1.day and comps2.day to let it hit both conditions.
import UIKit
let calendar1 = NSCalendar.currentCalendar()
let comps1 = NSDateComponents()
comps1.day = 7
let date1 = calendar1.dateByAddingComponents(comps1, toDate: NSDate(), options: NSCalendarOptions.allZeros)
let calendar2 = NSCalendar.currentCalendar()
let comps2 = NSDateComponents()
comps2.day = 0
let date2 = calendar2.dateByAddingComponents(comps2, toDate: NSDate(), options: NSCalendarOptions.allZeros)
if date1?.compare(date2!) == NSComparisonResult.OrderedDescending
{
println("date1 is after date 2")
}else{
println("date1 is before date 2")
}

Related

how to make daily local notification depending on specific time in CVcalendar?

I am using cvcalendar and there is in everyday a different times for fajer, dohor , aser , maghreb , ishaa . for example i have selected the Adan for Fajer, so i want to get the adan in everyday and everyday has a different time. so when i get a notification in DidreceivedLocalNotification i want go to next day in calendar and get the time of the next day, knowing that am getting the times from CoreData .
in viewWillappear
let date = NSDate()
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy"
let calendar = NSCalendar.currentCalendar()
let calendarForDate = NSCalendar.currentCalendar()
let componentsForDate = calendar.components([.Day , .Month , .Year], fromDate: date)
let year = componentsForDate.year
let month = componentsForDate.month
let day = componentsForDate.day
//////////
//// Conditions after selecting the user (vibration, beep, Adan ) these conditions are testing the selected choice to send a notification to the user on his choice
//
if prayerCommingFromAdan.id == 0 && prayerCommingFromAdan.ringToneId != 0{
notificationId.id = 0
let hours = prayer0.time[0...1]
let minutes = prayer0.time[3...4]
let fajerTime = "\(month)-\(day)-\(year) \(hours):\(minutes)"
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM-dd-yyyy hh:mm"
dateFormatter.timeZone = NSTimeZone.localTimeZone()
// convert string into date
let dateValue = dateFormatter.dateFromString(fajerTime) as NSDate!
var dateComparisionResult:NSComparisonResult = NSDate().compare(dateValue)
if dateComparisionResult == NSComparisonResult.OrderedAscending
{
addNotificationAlarm(year, month: month, day: day, hour: prayer0.time[0...1], minutes: prayer0.time[3...4], soundId: prayerCommingFromAdan.ringToneId, notificationBody: "It is al fajr adan")
}
What should i do in DidreceivedLocalNotification in AppDelegate?
You can use this code for scheduling your daily notifications according to time.
func ScheduleMorning() {
let calendar: NSCalendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
var dateFire=NSDate()
var fireComponents = calendar.components([.Hour, .Minute, .Second], fromDate: dateFire)
if (fireComponents.hour >= 7) {
dateFire=dateFire.dateByAddingTimeInterval(86400) // Use tomorrow's date
fireComponents = calendar.components([.Hour, .Minute, .Second], fromDate: dateFire)
}
fireComponents.hour = 7
fireComponents.minute = 0
fireComponents.second = 0
// Here is the fire time you can change as per your requirement
dateFire = calendar.dateFromComponents(fireComponents)!
let localNotification = UILocalNotification()
localNotification.fireDate = dateFire // Pass your Date here
localNotification.alertBody = "Your Message here."
localNotification.userInfo = ["CustomField1": "w00t"]
localNotification.repeatInterval = NSCalendarUnit.Day
UIApplication.sharedApplication().scheduleLocalNotification(localNotification) }
for receiving
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {
// Do something serious in a real app.
print("Received Local Notification:")
print(notification.userInfo)
}

Working out the start and end of a day. Swift

I have a function to work out the start and end of a week which works as expected. I want to implement another function which works out the start and end of a single day. I have the code below however I get the following error:
Type of expression is ambiguous without more context.
public class Date {
let dateFormatter = NSDateFormatter()
let date = NSDate()
let calendar = NSCalendar.currentCalendar()
func calcStartAndEndDateForWeek(durationOccurance: Double) {
print("Calculating start and end for week")
let componentsWeek = calendar.components([.YearForWeekOfYear, .WeekOfYear], fromDate: date)
let startOfWeek = calendar.dateFromComponents(componentsWeek)!
print("start of Week = \(dateFormatter.stringFromDate(startOfWeek))")
let componentsWeekEnds = NSDateComponents()
componentsWeekEnds.weekOfYear = 1
let endOfWeek = calendar.dateByAddingComponents(componentsWeekEnds, toDate: startOfWeek, options: [])!
print("End of the week = \(dateFormatter.stringFromDate(endOfWeek))")
}
func calcStartAndEndDateForDay(durationOccurance: Double) {
print("Calculating start and end for day")
let componentsWeek = calendar.components([.Minutes, .Seconds], fromDate: date)
let startOfDay = calendar.dateFromComponents(componentsWeek)!
print("start day = \(dateFormatter.stringFromDate(startOfDay))")
}
init(){
dateFormatter.dateFormat = "dd-MM-yyyy"
}
}
We can create a more generic function using the methods on NSCalendar:
func rangeOfPeriod(period: NSCalendarUnit, date: NSDate) -> (NSDate, NSDate) {
let calendar = NSCalendar.currentCalendar()
var startDate: NSDate? = nil
// let's ask calendar for the start of the period
calendar.rangeOfUnit(period, startDate: &startDate, interval: nil, forDate: date)
// end of this period is the start of the next period
let endDate = calendar.dateByAddingUnit(period, value: 1, toDate: startDate!, options: [])
// you can subtract 1 second if you want to make "Feb 1 00:00:00" into "Jan 31 23:59:59"
// let endDate2 = calendar.dateByAddingUnit(.Second, value: -1, toDate: endDate!, options: [])
return (startDate!, endDate!)
}
Called as
print("\(rangeOfPeriod(.WeekOfYear, date: NSDate()))")
print("\(rangeOfPeriod(.Day, date: NSDate()))")
Putting it into your code:
public class Date {
let dateFormatter = NSDateFormatter()
let date = NSDate()
let calendar = NSCalendar.currentCalendar()
func rangeOfPeriod(period: NSCalendarUnit) -> (NSDate, NSDate) {
var startDate: NSDate? = nil
calendar.rangeOfUnit(period, startDate: &startDate, interval: nil, forDate: date)
let endDate = calendar.dateByAddingUnit(period, value: 1, toDate: startDate!, options: [])
return (startDate!, endDate!)
}
func calcStartAndEndDateForWeek() {
let (startOfWeek, endOfWeek) = rangeOfPeriod(.WeekOfYear)
print("Start of week = \(dateFormatter.stringFromDate(startOfWeek))")
print("End of the week = \(dateFormatter.stringFromDate(endOfWeek))")
}
func calcStartAndEndDateForDay() {
let (startOfDay, endOfDay) = rangeOfPeriod(.Day)
print("Start of day = \(dateFormatter.stringFromDate(startOfDay))")
print("End of the day = \(dateFormatter.stringFromDate(endOfDay))")
}
init() {
dateFormatter.dateFormat = "dd-MM-yyyy"
}
}
let myDate = Date()
myDate.calcStartAndEndDateForWeek()
myDate.calcStartAndEndDateForDay()
I was implementing something similar and went the following route:
extension Date {
static var startOfToday: Date? {
let date = Date()
guard !date.isStartOfDay else { return date }
return date
.zero(out: .second)?
.zero(out: .minute)?
.zero(out: .hour)?
.addingTimeInterval(-24 * 60 * 60)
}
private func zero(out: Calendar.Component) -> Date? {
return Calendar.current
.date(bySetting: out, value: 0, of: self)
}
private var isStartOfDay: Bool {
let cal = Calendar.current
let hours = cal.component(.hour, from: self)
let minutes = cal.component(.minute, from: self)
let seconds = cal.component(.second, from: self)
return hours == 0 && minutes == 0 && seconds == 0
}
}
Setting a component to zero will increment the next bigger component. So just setting the hour to zero will push the date to the next day at 00:00, unless of course the hour is already at zero. So to make it work for any date we have to zero out the seconds, minutes and hours (in that order). And to make sure we don't end up at the beginning of yesterday we first check if all values aren't already at zero.
I realize this is kinda hacky and probably not the best way to go about this, but it seems to work well enough for my use-case at least.
Getting the end of the day can be built on top of this by just adding another day.

Swift time being returned as am when it is pm

This function gets current time and finds the next time in an array. When the current time is before midday and the next time is after midday, it returns the next time as am when it should be pm.
How can I change this? Would I need to use a 12 hour clock instead of a 24 hour clock?
import UIKit
import Foundation
let date = NSDate()
let calendar = NSCalendar.currentCalendar()
let components = calendar.components([.Hour, .Minute], fromDate: date)
let hour = components.hour
let minutes = components.minute
let currentTime = "\(hour)" + ":" + "\(minutes)" //output 10:47
let timesArray = ["5:45", "6:35", "7:00", "7:30", "7:50", "8:20", "8:40", "9:15", "10:10",
"12:40", "14:15", "14:50", "15:40", "16:10", "17:10", "17:40", "18:40", "19:25", "20:50"]
// create a method to convert your time to minutes
func stringToMinutes(input:String) -> Int {
let components = input.componentsSeparatedByString(":")
let hour = Int((components.first ?? "0")) ?? 0
let minute = Int((components.last ?? "0")) ?? 0
return hour*60 + minute
}
//create an array with the minutes from the original array
let timesMinutesArray:[Int] = timesArray.map { stringToMinutes($0) }
let dayMinute = stringToMinutes(currentTime)
// filter out the times that has already passed
let filteredTimesArray = timesMinutesArray.filter{$0 > dayMinute }
// get the first time in your array
if let firstTime = filteredTimesArray.first {
// find its position and extract it from the original array
let nextDeparture = timesArray[timesMinutesArray.indexOf(firstTime)!] // output "12:40"
let userCalendar = NSCalendar.currentCalendar()
let dateMakerFormatter = NSDateFormatter()
dateMakerFormatter.calendar = userCalendar
dateMakerFormatter.dateFormat = "yyyy/MM/dd"
// How many hours and minutes between current time and next departure?
dateMakerFormatter.dateFormat = "h:mm"
let startTime = dateMakerFormatter.dateFromString(currentTime)!
let endTime = dateMakerFormatter.dateFromString(nextDeparture)! //this comes back as 12:40 am not pm
let hourMinuteComponents: NSCalendarUnit = [.Hour, .Minute]
let timeDifference = userCalendar.components(
hourMinuteComponents,
fromDate: startTime,
toDate: endTime,
options: [])
let difference = (timeDifference.hour*60) + (timeDifference.minute)
}
Try a capital H in your dateFormat:
dateMakerFormatter.dateFormat = "H:mm"

Calculate number of weeks in a given month - Swift

Having looked at a few different suggestions on SO, I've not been able to determine why the function below does not work. It seems to return 6 for some months and 5 for others. When changing weeks for days it works perfectly.
For example, trying
weeksInMonth(8, forYear 2015)
Results in 6.
I believe I have a mis-understanding of what 'firstWeekday' property on calendar does but haven't found an adequate explanation by Apple or online.
Have tried both .WeekOfMonth and .WeekOfYear. Again can't find explanation of exact difference.
Any suggestions would be greatly welcome.
func weeksInMonth(month: Int, forYear year: Int) -> Int?
{
if (month < 1 || month > 12) { return nil }
let dateString = String(format: "%4d/%d/01", year, month)
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy/MM/dd"
if let date = dateFormatter.dateFromString(dateString),
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
{
calendar.firstWeekday = 2 // Monday
let weekRange = calendar.rangeOfUnit(.WeekOfMonth, inUnit: .Month, forDate: date)
let weeksCount = weekRange.length
return weeksCount
}
else
{
return nil
}
}
Update:
Apologies my question was not clear. I'm trying to work out how many weeks there are in a month that include a Monday in them. For August this should be 5.
Your code computes the number of weeks which occur (complete or partially)
in a month. What you apparently want is the number of Mondays
in the given month. With NSDateComponents and in particular with
the weekdayOrdinal property you can compute the first
(weekdayOrdinal=1) and last (weekdayOrdinal=-1) Monday
in a month. Then compute the difference in weeks (and add one).
A possible implementation in Swift 2:
func numberOfMondaysInMonth(month: Int, forYear year: Int) -> Int?
{
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
calendar.firstWeekday = 2 // 2 == Monday
// First monday in month:
let comps = NSDateComponents()
comps.month = month
comps.year = year
comps.weekday = calendar.firstWeekday
comps.weekdayOrdinal = 1
guard let first = calendar.dateFromComponents(comps) else {
return nil
}
// Last monday in month:
comps.weekdayOrdinal = -1
guard let last = calendar.dateFromComponents(comps) else {
return nil
}
// Difference in weeks:
let weeks = calendar.components(.WeekOfMonth, fromDate: first, toDate: last, options: [])
return weeks.weekOfMonth + 1
}
Note: That a negative weekdayOrdinal counts backwards from the end of the month is not apparent form the documentation. It was observed in
Determine NSDate for Memorial Day in a given year and confirmed by Dave DeLong).
Update for Swift 3:
func numberOfMondaysInMonth(_ month: Int, forYear year: Int) -> Int? {
var calendar = Calendar(identifier: .gregorian)
calendar.firstWeekday = 2 // 2 == Monday
// First monday in month:
var comps = DateComponents(year: year, month: month,
weekday: calendar.firstWeekday, weekdayOrdinal: 1)
guard let first = calendar.date(from: comps) else {
return nil
}
// Last monday in month:
comps.weekdayOrdinal = -1
guard let last = calendar.date(from: comps) else {
return nil
}
// Difference in weeks:
let weeks = calendar.dateComponents([.weekOfMonth], from: first, to: last)
return weeks.weekOfMonth! + 1
}
Actually your question is : How many Mondays are in a given month?
My approach is to calculate the first Monday for the month, this can be accomplished by setting the CalendarUnit WeekdayOrdinal to 1. Then get the number of total days and do some math.
Swift 1.2
func mondaysInMonth(month: Int, forYear year: Int) -> Int?
{
if 1...12 ~= month {
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let components = NSDateComponents()
components.weekday = 2 // Monday
components.weekdayOrdinal = 1
components.month = month
components.year = year
if let date = calendar.dateFromComponents(components) {
let firstDay = calendar.component(.CalendarUnitDay, fromDate: date)
let days = calendar.rangeOfUnit(.CalendarUnitDay, inUnit:.CalendarUnitMonth, forDate:date).length
return (days - firstDay) / 7 + 1
}
}
return nil
}
Swift 2
func mondaysInMonth(month: Int, forYear year: Int) -> Int?
{
guard 1...12 ~= month else { return nil }
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let components = NSDateComponents()
components.weekday = 2 // Monday
components.weekdayOrdinal = 1
components.month = month
components.year = year
if let date = calendar.dateFromComponents(components) {
let firstDay = calendar.component(.Day, fromDate: date)
let days = calendar.rangeOfUnit(.Day, inUnit:.Month, forDate:date).length
return (days - firstDay) / 7 + 1
}
return nil
}
Swift 2
The following code calculates the number of weeks in a month. It does not depend on what day the month started or ended.
func weeksInMonth(month: Int, year: Int) -> (Int)? {
let calendar = NSCalendar.currentCalendar()
let comps = NSDateComponents()
comps.month = month+1
comps.year = year
comps.day = 0
guard let last = calendar.dateFromComponents(comps) else {
return nil
}
// Note: Could get other options as well
let tag = calendar.components([.WeekOfMonth,.WeekOfYear,
.YearForWeekOfYear,.Weekday,.Quarter],fromDate: last)
return tag.weekOfMonth
}
// Example Usage:
if let numWeeks = weeksInMonth(8,year: 2015) {
print(numWeeks) // Prints 6
}
if let numWeeks = weeksInMonth(12,year: 2015) {
print(numWeeks) // Prints 5
}
Swift 4+:
//-- Get number of weeks from calendar
func numberOfWeeksInMonth(_ date: Date) -> Int {
var calendar = Calendar(identifier: .gregorian)
calendar.firstWeekday = 1
let weekRange = calendar.range(of: .weekOfMonth,
in: .month,
for: date)
return weekRange!.count
}
//-- Implementation
let weekCount = numberOfWeeksInMonth(Date)
Swift 4+:
Generate Date from your components:
let dateComponents = DateComponents.init(year: 2019, month: 5)
let monthCurrentDayFirst = Calendar.current.date(from: dateComponents)!
let monthNextDayFirst = Calendar.current.date(byAdding: .month, value: 1, to: monthCurrentDayFirst)!
let monthCurrentDayLast = Calendar.current.date(byAdding: .day, value: -1, to: monthNextDayFirst)!
Detect week number from date:
let numberOfWeeks = Calendar.current.component(.weekOfMonth, from: monthCurrentDayLast)

How to make a countdown to date Swift

I was facing the struggle of making a timer app, so I thought that now that I solved it I could help others who face the problem. So basically this app counts down to a specific date from the current time. As stack overflow allows a Q and A format I hope that can help you. See the comments for explanations.
Cleaned up and updated with countdown computed on a timer and leading zero String format.
let futureDate: Date = {
var future = DateComponents(
year: 2020,
month: 1,
day: 1,
hour: 0,
minute: 0,
second: 0
)
return Calendar.current.date(from: future)!
}()
var countdown: DateComponents {
return Calendar.current.dateComponents([.day, .hour, .minute, .second], from: Date(), to: futureDate)
}
#objc func updateTime() {
let countdown = self.countdown //only compute once per call
let days = countdown.day!
let hours = countdown.hour!
let minutes = countdown.minute!
let seconds = countdown.second!
countdownLabel.text = String(format: "%02d:%02d:%02d:%02d", days, hours, minutes, seconds)
}
func runCountdown() {
Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
Here is the solution of how I managed to create a countdown timer to a specific NSDate, for SO allows Q and A Style Answers.
// here we set the current date
let date = NSDate()
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(.CalendarUnitHour | .CalendarUnitMinute | .CalendarUnitMonth | .CalendarUnitYear | .CalendarUnitDay, fromDate: date)
let hour = components.hour
let minutes = components.minute
let month = components.month
let year = components.year
let day = components.day
let currentDate = calendar.dateFromComponents(components)
// here we set the due date. When the timer is supposed to finish
let userCalendar = NSCalendar.currentCalendar()
let competitionDate = NSDateComponents()
competitionDate.year = 2015
competitionDate.month = 6
competitionDate.day = 21
competitionDate.hour = 08
competitionDate.minute = 00
let competitionDay = userCalendar.dateFromComponents(competitionDate)!
// Here we compare the two dates
competitionDay.timeIntervalSinceDate(currentDate!)
let dayCalendarUnit: NSCalendarUnit = (.CalendarUnitDay | .CalendarUnitHour | .CalendarUnitMinute)
//here we change the seconds to hours,minutes and days
let CompetitionDayDifference = userCalendar.components(
dayCalendarUnit, fromDate: currentDate!, toDate: competitionDay,
options: nil)
//finally, here we set the variable to our remaining time
var daysLeft = CompetitionDayDifference.day
var hoursLeft = CompetitionDayDifference.hour
var minutesLeft = CompetitionDayDifference.minute
Hope that helps you guys if you're facing the same struggle as I have
Cleaned up/updated for latest Swift version of the accepted answer.
// here we set the current date
let date = NSDate()
let calendar = Calendar.current
let components = calendar.dateComponents([.hour, .minute, .month, .year, .day], from: date as Date)
let currentDate = calendar.date(from: components)
let userCalendar = Calendar.current
// here we set the due date. When the timer is supposed to finish
let competitionDate = NSDateComponents()
competitionDate.year = 2017
competitionDate.month = 4
competitionDate.day = 16
competitionDate.hour = 00
competitionDate.minute = 00
let competitionDay = userCalendar.date(from: competitionDate as DateComponents)!
//here we change the seconds to hours,minutes and days
let CompetitionDayDifference = calendar.dateComponents([.day, .hour, .minute], from: currentDate!, to: competitionDay)
//finally, here we set the variable to our remaining time
let daysLeft = CompetitionDayDifference.day
let hoursLeft = CompetitionDayDifference.hour
let minutesLeft = CompetitionDayDifference.minute
print("day:", daysLeft ?? "N/A", "hour:", hoursLeft ?? "N/A", "minute:", minutesLeft ?? "N/A")
//Set countdown label text
countDownLabel.text = "\(daysLeft ?? 0) Days, \(hoursLeft ?? 0) Hours, \(minutesLeft ?? 0) Minutes"
This worked for me.
The only thing that troubles me is that it doesn't really countdown as the user has to refresh the page for it to recount. You can see it "counting" when the user is scrolling up and down cells on a UITableView as the cells do refresh the view.
Another thing is that I have on NSTimeZone of the currentDate "GMT+2:00" as it works for my time but only because I haven't figured out how to use the device NSTimeZone yet.
let releaseDate = "2015-05-02'T'22:00:00:000Z"
let futureDateFormatter = NSDateFormatter()
futureDateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
let date: NSDate = futureDateFormatter.dateFromString(releaseDate!)!
let currentDate = NSDate();
let currentFormatter = NSDateFormatter();
currentFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
currentFormatter.timeZone = NSTimeZone(abbreviation: "GMT+2:00")
let diffDateComponents = NSCalendar.currentCalendar().components([NSCalendarUnit.Month, NSCalendarUnit.Day, NSCalendarUnit.Hour, NSCalendarUnit.Minute], fromDate: currentDate, toDate: date, options: NSCalendarOptions.init(rawValue: 0))
let countdown = "\(diffDateComponents.month) m: \(diffDateComponents.day) d: \(diffDateComponents.hour) h: \(diffDateComponents.minute) min"