I have some methods that return the number of weekdays between two given dates. Since calling these methods become very expensive to call when the two dates lie years apart, I'm wondering how these methods could be refactored in a more efficient way.
The returned result is correct but I feel that the iphone processor is struggling to keep up and consequently freezes up the application when I would call these methods over a period of say 10years.
Any suggestions ?
//daysList contains all weekdays that need to be found between the two dates
-(NSInteger) numberOfWeekdaysFromDaysList:(NSMutableArray*) daysList
startingFromDate:(NSDate*)startDate
toDate:(NSDate*)endDate
{
NSInteger retNumdays = 0;
for (Day *dayObject in [daysList objectEnumerator])
{
if ([dayObject isChecked])
{
retNumdays += [self numberOfWeekday:[dayObject weekdayNr] startingFromDate:startDate toDate:endDate];
}
}
return retNumdays;
}
-(NSInteger) numberOfWeekday:(NSInteger)day
startingFromDate:(NSDate*)startDate
toDate:(NSDate*)endDate
{
NSInteger numWeekdays = 0;
NSDate *nextDate = startDate;
NSComparisonResult result = [endDate compare:nextDate];
//Do while nextDate is in the past
while (result == NSOrderedDescending || result == NSOrderedSame)
{
if ([NSDate weekdayFromDate:nextDate] == day)
{
numWeekdays++;
}
nextDate = [nextDate dateByAddingDays:1];
result = [endDate compare:nextDate];
}
return numWeekdays;
}
You need to create an formula to calculate the number of weekdays rather than loop thru each day and count them.
Something like this (this is a rough approximation), where startJD and endJD are the Julian Dates:
nWeekdays = (endJD - startJD) * 5 / 7;
Of course that's close but not exact since it doesn't take into account what day of the week it starts and ends on. But that's the general idea, you need a formula, not a loop.
You can also find quite a bit on this topic by searching.
Why not look at the core foundation classes that handle dates?
CFAbsoluteTimeGetDayOfWeek
Returns an integer representing the day of the week indicated by the specified absolute time.
SInt32 CFAbsoluteTimeGetDayOfWeek (
CFAbsoluteTime at,
CFTimeZoneRef tz
);
Parameters
at : The absolute time to convert.
tz : The time zone to use for time correction. Pass NULL for GMT.
Return Value :
An integer (1-7) representing the day of the week specified by at. Per ISO-8601, Monday is represented by 1, Tuesday by 2, and so on.
Availability
Available in iOS 2.0 and later.
Declared In
CFDate.h
More can be found at: http://developer.apple.com/library/ios/#documentation/CoreFoundation/Conceptual/CFDatesAndTimes/
Related
I'm running this on flutter, but I guess this could be a more general issue.
I am saving a DateTime in the preferences. I want to be able to then tell if DateTime.now() is on at least a day after the last saved DateTime, i.e.
(pseudocode)
lastDailyCheck = 2020.04.10
now = 2020.04.11
=> now is a day after the lastDailyCheck.
This should already work if it is 00:01 on the new day, even if the lastDailyCheck was on 23:58 the day before, meaning the difference can be as low as minutes between the 2 DateTimes.
Turns out this is really complicated!
Here's what doesn't work:
DateTime.Now().isAfter(lastDailyCheck)
This only checks if now is after the last one, it also return true after a second, and on the same day.
DateTime.Now().isAfter(lastDailyCheck) && lastDailyCheck.day != DateTime.Now().day
I thought this was clever. If the day is different and it is after the last then it does work in recognizing it is a day later - but then I realized it would bug out when both days are say on the 15th of the month - then lastDailyCheck.day would equal DateTime.Now().day.
What do you think would be possible here?
I don't know flutter, but my approach would be to not store the last check, but store the date at which the next check should occur. So when you perform a check you calculate the next midnight and store that. Now you can use isAfter.
In javascript this would look something like this:
const now = new Date();
//this also handles overflow into the next month
const nextCheck = new Date(now.getYear(), now.getMonth(), now.getDate() + 1)
//store nextCheck somewhere
//in js there is no isAfter, you just use >
if(new Date() > nextCheck) {
//do the thing
}
of course you could also calculate nextCheck every time you want to compare it, but I dislike performing the same calculation over and over if I can avoid it.
A thing to mention here is timezones, depending on your date library and if your system and user timezones align, you may need to shift the date.
I cannot write a complete code for now but this is what it would look like:
(pseudocode)
expirationDay = lastDailyCheck.add(oneDayDuration);
isOneDayAfter = DateTime.now().isAfter(expirationDay);
You give an expiration date and compare the DateTime to that. You have to use isAfter for reliability, instead of .day check.
I would compute the difference between midnight of the day of the last timestamp and midnight of the current timestamp. That is, consider only the date portion of a DateTime and ignore the time.
DateTime date(DateTime dateTime) =>
DateTime(dateTime.year, dateTime.month, dateTime.day);
// Intentionally check for a positive difference in hours instead of days
// so we don't need to worry about 23-hour days from DST. Any non-zero
// number of hours here means a difference of at least a "day".
if (date(DateTime.now()).difference(date(lastDailyCheck)).inHours > 0) {
// "One day" after.
}
If you're using UTC timestamps and don't care about when midnight is in whatever the local time is, the comparison could more intuitively use .inDays >= 1.
Figured out another potential solution!
In addition to checking if the day is different (which by itself won't work) you can also check the month and year. Only 1 of those needs to differ for it be true :)
if (now.isAfter(lastDailyCheck)) {
if (now.day != lastDailyCheck.day ||
now.month != lastDailyCheck.month ||
now.year != lastDailyCheck.year) {
return true;
}
}
this is the way I prefer to do some logics based on the comparison between two different times:
var now = DateTime.now();
var myDate = new DateTime.utc(2022, 1, 1);
if(myDate.compareTo(now)>0) //positive value means myDate is greater than DateTime.now()
{
// here is your logic based on the comparison between two times
} else {
//your logic if DateTime.now() pass myDate
}
You can use the difference method to get the difference between 2 dates and check whether those differs in hours with at-least 24 hours. So your if condition becomes:
if (now.isAfter(lastDailyCheck) &&
(lastDailyCheck.day != now.day ||
now.difference(lastDailyCheck).inHours > 24)) {
print('After');
}
Here's an attempt to a succint answer. Simply export this extension and use it. With it you can say if a date is at least one day after the current day.
extension DateExt on DateTime {
bool isAtLeastOneDayAfterToday() {
final now = DateTime.now();
return (isAfter(now) &&
(day != now.day || month != now.month || year != now.year));
}
}
Use it like so:
final isAfter = myDay.isAtLeastOneDayAfterToday(); //will be true or false
I have an iPhone app where a user can set his availability for different times of the day, for each day of the week. Having just three availability periods per day (morning/afternoon/evening) will work for the time being, but I'd want to implement times in the future. A user will try to search for other users based on the availability. What is a good database architecture and search algorithm to implement this? I use stackmob as my backend. If some body can give me some hints or point me to an algorithm that will be able to search for an availability match, I'd really appreciate it. I have a crude way to do it currently - 21 variables (boolean each for 7 days * 3 time periods). I want to find a smarter way.
I would do the following:
Store availability using date/times in the database. One record for each slot they are available. It is arbitrary what time you use to represent each slot. E.g. maybe morning is 9am, afternoon is 1pm and evening is 5pm. But by using date/time you are setting yourself up to change this to something more precise later (e.g. a start-time, end-time).
Query each users availability within the required time frame (e.g. between now and 1 week time). Lets calling the resulting arrays A & B.
Now run the following algorithm...
NSUInteger a = 0;
NSUInteger b = 0;
BOOL foundMatch = NO;
while(a < A.count && b < B.count) {
NSComparisonResult result = [A[a] compare:B[b]];
if(result == NSOrderedSame) {
foundMatch = YES;
break;
} else if(result == NSOrderedDescending) { //a>b
b++;
} else {
a++;
}
}
Is there a quick way in Objective-C of identifying NSDate's in an NSArray that have a time of day after a given time (e.g. 8pm)?
I can't quite see anyway other than manually walking through each NSDate in the array and then using NSDateComponents to break out the hour/minute/second...Not even sure if there is a simple way to get the time from an NSDate in a fashion that represents a fraction of 24hours, as this might help a little. (e.g. 6pm would be 18/24 = 0.75 in this case)
There is no need to break in NSDateComponents.
NSTimeInterval interval = [date1 timeIntervalSinceDate:date2];
if (interval > 0) {
// date2 is earlier
} else {
// date1 is earlier
}
Now you can represent your target time(8 P.M., for example) with date2 and compare all dates of array with that.
Haven't tried this myself, but I guess
- (NSArray *)filteredArrayUsingPredicate:(NSPredicate *)predicate
is what you're looking for.
I would like to check if a given date is the first, second, third, fourth or last monday, tuesday, wednesday,..., sunday in the dates month.
The function should look like this:
if(checkdate(date, "second", "monday"))
{
alert("This is the second monday of the month");
}
else
{
alert("This is NOT the second monday of the month");
}
I would rather write a function that returns an object telling me the day and the week-number-in-month of the given date. Something like:
function infoDate(date) {
return {
day: date.getDay()+1,
week: (date.getDate()-1)%7+1
}
}
Then I can read the returned object and find out if it's the second monday or whatever:
var info = infoDate(date);
if(info.day==1 && info.week==2) {
// second monday
}
Of course, you can still write another localized function that does exactly what you ask for based on an array of numeral and day names.
use getDate() to get day of the month
then getDay() to get the day of the week
using these two you should be able to accomplish your task. Refer to this http://www.w3schools.com/jsref/jsref_obj_date.asp
It seems to me you only need to divide the date by 7 to know how many full weeks have passed, no matter what day it is. Subtracting one before dividing and adding it after sets the first week from 0 to one.
Date.prototype.weekofMonth= function(){
return Math.floor((this.getDate()-1)/7)+1;
}
alert(new Date().weekofMonth())
If I've got a time_t value from gettimeofday() or compatible in a Unix environment (e.g., Linux, BSD), is there a compact algorithm available that would be able to tell me the corresponding week number within the month?
Ideally the return value would work in similar to the way %W behaves in strftime() , except giving the week within the month rather than the week within the year.
I think Java has a W formatting token that does something more or less like what I'm asking.
[Everything below written after answers were posted by David Nehme, Branan, and Sparr.]
I realized that to return this result in a similar way to %W, we want to count the number of Mondays that have occurred in the month so far. If that number is zero, then 0 should be returned.
Thanks to David Nehme and Branan in particular for their solutions which started things on the right track. The bit of code returning [using Branan's variable names] ((ts->mday - 1) / 7) tells the number of complete weeks that have occurred before the current day.
However, if we're counting the number of Mondays that have occurred so far, then we want to count the number of integral weeks, including today, then consider if the fractional week left over also contains any Mondays.
To figure out whether the fractional week left after taking out the whole weeks contains a Monday, we need to consider ts->mday % 7 and compare it to the day of the week, ts->wday. This is easy to see if you write out the combinations, but if we insure the day is not Sunday (wday > 0), then anytime ts->wday <= (ts->mday % 7) we need to increment the count of Mondays by 1. This comes from considering the number of days since the start of the month, and whether, based on the current day of the week within the the first fractional week, the fractional week contains a Monday.
So I would rewrite Branan's return statement as follows:
return (ts->tm_mday / 7) + ((ts->tm_wday > 0) && (ts->tm_wday <= (ts->tm_mday % 7)));
If you define the first week to be days 1-7 of the month, the second week days 8-14, ... then the following code will work.
int week_of_month( const time_t *my_time)
{
struct tm *timeinfo;
timeinfo =localtime(my_time);
return 1 + (timeinfo->tm_mday-1) / 7;
}
Assuming your first week is week 1:
int getWeekOfMonth()
{
time_t my_time;
struct tm *ts;
my_time = time(NULL);
ts = localtime(&my_time);
return ((ts->tm_mday -1) / 7) + 1;
}
For 0-index, drop the +1 in the return statement.
Consider this pseudo-code, since I am writing it in mostly C syntax but pretending I can borrow functionality from other languages (string->int assignment, string->time conversion). Adapt or expand for your language of choice.
int week_num_in_month(time_t timestamp) {
int first_weekday_of_month, day_of_month;
day_of_month = strftime(timestamp,"%d");
first_weekday_of_month = strftime(timefstr(strftime(timestamp,"%d/%m/01")),"%w");
return (day_of_month + first_weekday_of_month - 1 ) / 7 + 1;
}
Obviously I am assuming that you want to handle weeks of the month the way the standard time functions handle weeks of the year, as opposed to just days 1-7, 8-13, etc.