add a new row in tableView every single month - swift

i have a date say 2 March 2016 stored as NSUserDefaults and i want to add a new row in TableView every time a new Month is about to come , so what should i do for accomplishing this , IMO comparing the stored Date and Current Date and if
in Curent Date a new Month is about to come in next 7 days then add the
row into table but i don't know where to start, anyone can give me some hint for checking current date's next 7 days for if a new months is about to come
and if my approach is not good enough then please correct me it'll be so appreciated by me and helpful for me
please see example for better understanding :
storedDate = 2 March 2016
currentDate = 26 March 2016
if CurrentDate + 1 Week == newMonth {
//add the Row into TableView
}

You can add an Extension to NSDate and then do all sorts of day/month addition
This method you can use to add 7 days to the current date...
func dateByAddingDays(daysToAdd: Int)-> NSDate {
let dateComponents = NSDateComponents()
dateComponents.day = daysToAdd
let newDate = NSCalendar.currentCalendar().dateByAddingComponents(dateComponents, toDate: self, options: .MatchFirst)
return newDate!
}
This method to add months to current date
func dateByAddingMonths(monthsToAdd: Int)-> NSDate {
let dateComponents = NSDateComponents()
dateComponents.month = monthsToAdd
let newDate = NSCalendar.currentCalendar().dateByAddingComponents(dateComponents, toDate: self, options: .MatchFirst)
return newDate!
}
Then you need to check that date you created and see if it its a different month than the one that is stored..
func compareMonths(newDate:NSDate)-> Bool {
let today = NSDate()
let todayPlusSeven = today.dateByAddingDays(7)
return todayPlusSeven.isNextMonth(storedDate)
}
Using this method to check if the months of 2 dates are the same
func isNextMonth(storedDate: NSDate)-> Bool {
return isSameMonthAsDate(storedDate.dateByAddingMonth(1))
}
func isSameMonthAsDate(compareDate: NSDate)-> Bool {
let comp1 = NSCalendar.currentCalendar().components([NSCalendarUnit.Year, NSCalendarUnit.Month], fromDate: self)
let comp2 = NSCalendar.currentCalendar().components([NSCalendarUnit.Year, NSCalendarUnit.Month], fromDate: compareDate)
return ((comp1.month == comp2.month) && (comp1.year == comp2.year))
}
An oldie but still goodie, is this page of Date helpers from Erica Sadun's github page here They are all in Obj-c but can be converted to swift easily enough. I still reference it when i need help doing date math

Related

UIDatePicker, choosing date shows one day earlier

I have a UIDatePicker, getting the date from the picker shows different date (one day earlier) from what I chose.
For example, choosing May 2, 2019, shows 2019-05-01
This is where I get the date for my variable:
#objc func datePickerValueChanged(sender: UIDatePicker){
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateTextField.text = dateFormatter.string(from: sender.date)
//Tag 9 is the startDateTextField, Tag 8 is the EndDateTextField
//We need this to know which variable should get the date choosen.
if dateTextField.tag == 9 {
startDateChoosen = sender.date
}else if dateTextField.tag == 8 {
endDateChoosen = sender.date
}
}
This is what I see on the screen:
The issue might be with the time zone that is set on your iPad/iPhone that you are using. Make sure the time zone set in the settings is of that of your current location.

Get unavailable date in a datepicker (Swift)

I have a UIDatePicker which is available for days from today to the next week ( from Date() to Date().day + 7 ).
Suppose that today is 30th day of the month, so the available days to choose are 30th day of this month and first to 6th day of next month.
In this case, I need to change the datePicker month to next month as the user change day from 30 to 1.
But the problem is:
"When I change the day to an invalid one, the .change method give me the least valid day."
Example:
Assumptions:
Today = 28 July.
Choosable dates = 28 July to 3 August.
What I need:
User can not choose the days before 28 July and not after 3 August.
When user wants to change day to 1, 2 or 3 August, the datePicker itself change the month to August and make 1, 2 and 3 available to
be chosen by user.
Main Problem
When user change the day to 1, 2 or 3, I can not get the 1, 2 or 3! and it returns me "28"!
Just set maximumDate property of your UIDatePicker to desired date.
func setupDatePicker(){
var currDateComponents = getDateComponents(fromDate: Date())
var maxDateComponents = currDateComponents
if let date = currDateComponents?.day{
maxDateComponents?.day = date + 7
}
self.datePicker?.maximumDate = getDate(fromDateComponents: maxDateComponents)
}
func getDateComponents(fromDate date: Date?)-> DateComponents?{
let calendar = Calendar(identifier: .gregorian)
var dateComponents: DateComponents? = nil
if(date != nil){
dateComponents = calendar.dateComponents([.day, .month, .year], from: date!)
}
return dateComponents
}
func getDate(fromDateComponents dateComponents:DateComponents?)-> Date?{
let calendar = Calendar(identifier: .gregorian)
if let componentsBasedDate = calendar.date(from: dateComponents!) {
return componentsBasedDate
}
return nil
}
Call this function in viewDidLoad to setup calendar for setting maximum and minimum date:
fileprivate func setupCalendar() {
datePicker.minimumDate = Calendar.current.date(byAdding: .day, value: 0, to: ServerTime.sharedInstance.nowTime)
datePicker.maximumDate = Calendar.current.date(byAdding: .day, value: +7, to: ServerTime.sharedInstance.nowTime)
}
for calculate days between shown date and current date I've used this method:
extension Date {
//Calculate days between two day objects
func daysBetween(_ date: Date) -> Int {
let calendar = NSCalendar.current
// Replace the hour (time) of both dates with 00:00
let date1 = calendar.startOfDay(for: date)
let date2 = calendar.startOfDay(for: self)
let components = calendar.dateComponents([.day], from: date1, to: date2)
return components.day ?? 0 // This will return the number of day(s) between dates
}
}}
In fact, it is impossible to get date before they chose. And according to unavailability of days in next month before changing the month, I can not use UIDatePicker to handle it.
So Finally I implemented my custom UIPickerView.
as #holex said.
Thank you all for answering.

Swift: check if date is the same date as any day in array of dates?

I have an array of date objects - posts. And I am looping through a month. For each day, I want to check if some date in the array is on the same day. So far I have this:
var date = month?.startOfMonth()
var end = month?.endOfMonth()
while date! <= end! {
if posts.reduce(false,{Calendar.current.isDate(date, inSameDayAsDate: post.timeStamp)}) == true {
....
}
date = Calendar.current.date(byAdding: .day, value: 1, to: date!)
}
I believe that this starts with false and for each day in posts it checks whether its in the same day and if it is it turns the result into true. However I think it also changes it back to false the next time it encounters a false value...
What I want is something that returns true if any of the dates in posts is the same as some day rather than the last one. How can I do this?
Your current code is mostly OK though I would replace reduce with contains.
if let start = month?.startOfMonth(), let end = month?.endOfMonth() {
var date = start
var found = false
while !found && date <= end {
if posts.contains { Calendar.current.isDate(date, inSameDayAs: $0.timeStamp) } {
found = true
}
date = Calendar.current.date(byAdding: .day, value: 1, to: date)
}
if found {
// We have a match
}
}
I am basically building a stack view - for every day - I create a rectangle which is blue if there is a post that day and clear if not. Thus I probably do need to know the day. However filtering the array for elements which are in the specified month seems interesting. Can you show how to do that? Perhaps I could specify the location of just those days and then fill the rest of the stackArray with clear values using insertItem atIndex
Basically, I might start with two functions, one to filter the dates by month and one to filter by day. The reason I would so this, in your case, is you for each day, you don't want to refilter all the available dates for the month (but that's just me)
func dates(_ dates: [Date], withinMonth month: Int) -> [Date] {
let calendar = Calendar.current
let components: Set<Calendar.Component> = [.month]
let filtered = dates.filter { (date) -> Bool in
calendar.dateComponents(components, from: date).month == month
}
return filtered
}
func dates(_ dates: [Date], forDay day: Int) -> [Date] {
let calendar = Calendar.current
let components: Set<Calendar.Component> = [.day]
let filtered = dates.filter { (date) -> Bool in
calendar.dateComponents(components, from: date).day == day
}
return filtered
}
You could, use a contains approach, matching both the month and day, but again, there is an overhead to consider. In the above example, you could simply check to see if the day is contained in the resulting filtered dates by month, which might be closer to you desired result
nb This is not as efficient as something like first or contains as this will iterate the entire array finding every matching element, but, it has the nice side effect of providing you with more information. For example, you could sort the resulting filters and simply iterate from the start of the month to the end, popping off each match day as it occurs, as an idea
Thinking out loud...
Another approach might be to filter the available date's by the month, as above, but then to map the result to a Set of days (ie Int), this would allow you to either iterate over each day of the month and use contains(day) to perform a simple check to see if the day is contained or not.
Equally, you could map the view's to each day and iterate of the Set, changing the state of each view.
This all depends on more context then is available, but needless to say, there are any number of ways you might approach this problem
[Updated] As rightly mentioned already, you might be more interested in having a Set of days that have at least one post, something like:
let dayComponents: Set<Calendar.Component> = [.day, .month, .year, .era]
let calendar = Calendar.current
let daysWithPosts = Set(posts.map { post in
calendar.dateComponents(dayComponents, from: post.date)
})
Then for each date you can check if it's in that set (context unchanged, mind the force unwraps):
while date! <= end! {
let currentDayComponents = calendar.dateComponents(dayComponents, from: date)
let postsFound = daysWithPosts.contains(currentDayComponents)
// <use postsFound as needed>
date = Calendar.current.date(byAdding: .day, value: 1, to: date!)
}
Original answer, adapted for multiple dates:
This should tell if there are posts on a given date's day:
func areTherePosts(in posts: [Post], fromSameDayAs date: Date) -> Bool {
let calendar = Calendar.current
let dayComponents: Set<Calendar.Component> = [.day, .month, .year, .era]
let specificDateComponents = calendar.dateComponents(dayComponents, from: date)
return posts.contains { post in
calendar.dateComponents(dayComponents, from: post.date) == specificDateComponents
}
}
Usage in your context (again, unchanged):
while date! <= end! {
let postsFound = areTherePosts(in: posts, fromSameDayAs: date!)
// <use postsFound as needed>
date = Calendar.current.date(byAdding: .day, value: 1, to: date!)
}

NSDate: Getting values for Tomorrow or Yesterday

I have a piece of code that basically acts in 2 parts:
Part 1: The user sets a Date with a UIDatePicker. For example, the user selects 1 day ahead from the current date. So the selected new date is 5/19/16 instead of 5/18/16.
Part 1 code
let dateFormatter = NSDateFormatter()
dateFormatter.locale = NSLocale.currentLocale()
var dateString = "May-19-2016"
dateFormatter.dateFormat = "MMMM-dd-yyyy"
var due_date = dateFormatter.dateFromString(dateString)!
Part 2: I have created code that counts how many days are left from the selected date to the current date. In this example, somehow my code is saying its 0 days before tomorrow. Here is the code for the second part:
Second Part:
func computeDates(dueDate:NSDate)-> Int {
let currentDate = NSDate()
// Adding days to currentDate
let daysToAdd = 1
// Adding on Unit to the current instance
let calculateDate = NSCalendar.currentCalendar().dateByAddingUnit(NSCalendarUnit.Day, value: daysToAdd, toDate: currentDate, options: NSCalendarOptions.init(rawValue: 0))
// Figure out many days from may 3rd
let cal = NSCalendar.currentCalendar()
let unit = NSCalendarUnit.Day
let components = cal.components(unit, fromDate: currentDate, toDate: dueDate, options: [])
let countLeft = components.day
return countLeft
}
print("Days left: \(computeDates(due_date)) ")
// Tests
let calc_date = computeDates(due_date)
if calc_date <= -1 {
print("Yesterday")
} else if calc_date == 0 {
print("Today")
} else if calc_date > 1 {
print("Tomorrow")
}
In the part 1 example, I used a static date which I use to test this code. In this part, I set May 19, 2016, one day ahead. In the second part below in the if statement, It somehow says I have 0 days left and from what i am trying to do, it's suppose to say 1 day left before tomorrow the 19th.
Another example, If i change the 19th to the 20th, I want it to say "In 2 days" from now.
Now if I revert the day to lets say, the 15th of may (in the past), Then the if statement would say something like Overdue or the past.
How can I solve this?
It would help if you thought of NSDate as a structure that operates with the number of seconds from 2001. It means if you pick a "date", it contains "date and time". All you need to do to calculate the correct number of days between specific "dates" is to truncate a time component.
But if you only need to check whether the provided date is Yesterday, Today, or Tomorrow, NSCalendar has methods for this purpose:
Objective-C:
- (BOOL)isDateInToday:(NSDate *)date NS_AVAILABLE(10_9, 8_0);
- (BOOL)isDateInYesterday:(NSDate *)date NS_AVAILABLE(10_9, 8_0);
- (BOOL)isDateInTomorrow:(NSDate *)date NS_AVAILABLE(10_9, 8_0);
And Swift:
#available(OSX 10.9, *)
public func isDateInYesterday(date: NSDate) -> Bool
#available(OSX 10.9, *)
public func isDateInTomorrow(date: NSDate) -> Bool
#available(OSX 10.9, *)
public func isDateInWeekend(date: NSDate) -> Bool

Constructing a date for getRequestedUpdateDateWithHandler:

I need to update my watchOS complication at midnight every day.
startOfDay is the beginning of the day (i.e., 12 AM today).
Should I add a day to the start of today like this?
func getNextRequestedUpdateDateWithHandler(handler: (NSDate?) -> Void) {
// Call the handler with the date when you would next like to be given the opportunity to update your complication content
let startOfDay = NSDate().startOfDay
let components = NSDateComponents()
components.day = 1
let startOfNextDay = NSCalendar.currentCalendar().dateByAddingComponents(components, toDate: startOfDay, options: NSCalendarOptions())
handler(startOfNextDay)
}
Or should I not add a day to the code, and just do something like this:
func getNextRequestedUpdateDateWithHandler(handler: (NSDate?) -> Void) {
// Call the handler with the date when you would next like to be given the opportunity to update your complication content
let startOfDay = NSDate().startOfDay
handler(startOfDay)
}
You'd want to advance the date one day, since you want your next requested update to occur at tomorrow's midnight. The first method would do what you want, but you can simplify it as follows:
let calendar = NSCalendar.currentCalendar()
let startOfDay = calendar.startOfDayForDate(NSDate())
let startOfNextDay = calendar.dateByAddingUnit(.Day, value: 1, toDate: startOfDay, options: NSCalendarOptions())!
The second code would return today's 12 AM, which would already be in the past.