Computing 'Last Friday' w/ NSDate + NSDateComponents - cocoa

I'm having some trouble computing specific days relative to the current date using NSDate and NSDateComponents. For example, let's say I want last Friday. I tried the following:
NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSYearCalendarUnit| NSMonthCalendarUnit | NSDayCalendarUnit | NSWeekCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit | NSWeekdayCalendarUnit | NSWeekdayOrdinalCalendarUnit) fromDate:[NSDate date]];
[components setHour:0];
[components setMinute:0];
[components setSecond:0];
[components setWeekday:6]; //Friday
[components setWeek:[components week] - 1]; //Last Week
return [[NSCalendar currentCalendar] dateFromComponents:components];
But that just returns the current date, as if it can't figure out what I'm asking for.
This feels like it should be simple enough but I'm just not getting it working. Any pointers?

How about something like this:
NSDate* today = [NSDate date];
NSDateComponents* comps = [[NSCalendar currentCalendar] components:NSWeekdayCalendarUnit fromDate:today];
// do the modulo arithmetic to determine the delta days to last Friday
NSInteger weekday = [comps weekday];
NSInteger deltaDays = 0; // left as exercise, I am hungry for lunch...
NSDateComponents* delta = [[NSDateComponents alloc] init];
[delta setDay:deltaDays];
NSDate* lastFriday = [[NSCalendar currentCalendar] dateByAddingComponents:delta toDate:today options:0];
[delta release];

Use this code:
- (NSDate *)lastDayOfMonthWithDate:(NSDate *)date
{
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSYearCalendarUnit | NSMonthCalendarUnit fromDate:date];
components.month += 1;
components.day = 0;
NSDate *lastDayOfMonth = [[NSCalendar currentCalendar] dateFromComponents:components];
return lastDayOfMonth;
}
- (NSDate *)lastFridayOfMonthWithDate:(NSDate *)date
{
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSWeekdayCalendarUnit fromDate:[self lastDayOfMonthWithDate:date]];
if (components.weekday >= 6) {
components.day -= components.weekday - 6;
} else {
components.day += 6 - components.weekday - 7;
}
NSDate *lastFridayOfMonth = [[NSCalendar currentCalendar] dateFromComponents:components];
return lastFridayOfMonth;
}

I wrote this function:
- (NSInteger)lastOccurrenceOfWeekdayWithNumber:(NSInteger)weekdayNumber inMonthForDate:(NSDate *)date {
NSCalendarUnit units = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday | NSCalendarUnitWeekdayOrdinal;
NSRange range = [_calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:date];
NSDateComponents *dateComponents = [_calendar components:units fromDate:date];
dateComponents.day = range.length;
date = [_calendar dateFromComponents:dateComponents];
dateComponents = [_calendar components:units fromDate:date];
while (dateComponents.weekday != weekdayNumber) {
dateComponents.day -= 1;
date = [_calendar dateFromComponents:dateComponents];
dateComponents = [_calendar components:units fromDate:date];
}
return dateComponents.weekdayOrdinal;
}
weekdayNumber is 1 when you are looking for Sunday. 2 for Monday.
If you want to find last Friday in October you can use this:
[self lastOccurrenceOfWeekdayWithNumber:6 inMonthForDate:date];
where date is let's say the 11th of October, 2016 or any other day of October.

Related

NSDateFormatter return blank for minute if == 0

The code:
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"m"];
NSLog(#"%#", [formatter stringFromDate:myDate]);
outputs: 0 if myDate has its minute value to 0.
This is normal, but how do I make it output a blank value instead of 0 if the minute is set to 0?
NSString * newString = [myString stringByReplacingOccurrencesOfString:#"0" withString:#""];

Getting NSDate from JSON generated by WCF REST service

The following code works, but it feels dirty. Is there a more standard way to convert epoch date with offset into an NSDate?
- (NSDate *) dateFromJSONString: (NSString *) JSONString{
//expects JSON from .NET WCF Service in epoch ticks, ex:
//"timeScheduled":"\/Date(1348600140000+0100)\/"
NSString *date = [[JSONString stringByReplacingOccurrencesOfString:#"/Date(" withString:#""] stringByReplacingOccurrencesOfString:#")/" withString:#""];
NSString *offsetString = [date substringFromIndex:(date.length - 5)];
//convert to seconds
NSTimeInterval dateInterval = [date doubleValue] /1000;
//gets offset value in seconds - +0100 -> 100 -> 1 -> 3600
double offsetValue = ([offsetString doubleValue] / 100) * 60 * 60;
if ([[offsetString substringToIndex:1] isEqualToString:#"+"]) {
dateInterval = dateInterval + offsetValue;
}
else{
dateInterval = dateInterval - offsetValue;
}
NSDate *retVal = [[NSDate alloc]initWithTimeIntervalSince1970:dateInterval];
return retVal;
}
Try this
the original gist
#implementation NSDate (DotNetDates)
+(NSDate*) dateFromDotNet:(NSString*)stringDate{
NSDate *returnValue;
if ([stringDate isMemberOfClass:[NSNull class]]) {
returnValue=nil;
}
else {
NSInteger offset = [[NSTimeZone defaultTimeZone] secondsFromGMT];
returnValue= [[NSDate dateWithTimeIntervalSince1970:
[[stringDate substringWithRange:NSMakeRange(6, 10)] intValue]]
dateByAddingTimeInterval:offset];
}
return returnValue;
}
-(NSString*) dateToDotNet{
double timeSince1970=[self timeIntervalSince1970];
NSInteger offset = [[NSTimeZone defaultTimeZone] secondsFromGMT];
offset=offset/3600;
double nowMillis = 1000.0 * (timeSince1970);
NSString *dotNetDate=[NSString stringWithFormat:#"/Date(%.0f%+03d00)/",nowMillis,offset] ;
return dotNetDate;
}
#end
// /Date(-422928000000+0100)/
Docs:
DateTime values appear as JSON strings in the form of "/Date(700000+0500)/", where the first number (700000 in the example provided) is the number of milliseconds in the GMT time zone, regular (non-daylight savings) time since midnight, January 1, 1970. The number may be negative to represent earlier times. The part that consists of "+0500" in the example is optional and indicates that the time is of the Local kind - that is, should be converted to the local time zone on deserialization. If it is absent, the time is deserialized as Utc. The actual number ("0500" in this example) and its sign (+ or -) are ignored.
NSTimeInterval is always specified in seconds; it yields sub-millisecond precision over a range of 10,000 years.
+ (NSDate*) dateFromDotNet:(NSString *)stringDate{
if(stringDate==(id)[NSNull null])
return nil;
NSInteger ix0= [stringDate rangeOfString:#"("].location;
NSInteger ix1= [stringDate rangeOfString:#")"].location;
if(ix0==NSNotFound || ix1==NSNotFound)
#throw [NSException exceptionWithName:#"ExceptionName" reason:#"Invalid JSON data" userInfo:#{#"json":stringDate}];
NSRange range= NSMakeRange(ix0+1, ix1-ix0);
NSString *dateString= [stringDate substringWithRange:range];
// dateString: -422928000000+0100
NSCharacterSet *signs= [NSCharacterSet characterSetWithCharactersInString:#"+-"];
range= [dateString rangeOfCharacterFromSet:signs option:NSBackwardSearch];
// WCF will send 13 digit-long value for the time interval since 1970 (millisecond precision)
// whereas iOS works with 10 digit-long values (second precision), hence the divide by 1000
NSTimeInterval unixTime = [dateString doubleValue] / 1000;
if(range.location!=NSNotFound){
NSString *sign = [dateString substringWithRange:range];
NSString *off = [dateString substringFromIndex:range.location+1];
// gets offset value in seconds -+0100 -> 100 -> 1 -> 3600
double offset = ([off doubleValue] / 100) * 60 * 60;
if ([sign isEqualToString:#"+"])
unixTime+= offset;
else
unixTime-= offset;
}
NSDate *date= [NSDate dateWithTimeIntervalSince1970:unixTime];
return date;
}

How can I check that NSDate is between two times?

I am making a Cocoa application and I want it to be able to conduct a daily update. (this app is for personal use) I want it to be doing its update at a specific time everyday so I set my computer to wake up at a that time. I set a notification observer thing in my app and it will conduct this function if the app gets a computer did awake notification:
- (void) receiveWakeNote: (NSNotification*) note
{
[self conductBeginning];
}
What should I add to make sure that the wake up notice occurred between a specific time, say between 16:00 and 16:15, and then only execute the
[self conductBeginning];
line.
NSDate *now = [NSDate date];
NSUInteger units = NSHourCalendarUnit | NSMinuteCalendarUnit;
NSDateComponents *components = [[NSCalendar currentCalendar] components:units
fromDate:now];
if (components.hour == 16 && components.minute <= 15)
NSLog(#"it's time");
something like this:
NSDate * aDate = ...;
NSDate * bDate = ...;
NSTimeInterval a = [aDate timeIntervalSinceNow];
NSTimeInterval b = [bDate timeIntervalSinceNow];
NSTimeInterval min = a > b ? b : a;
NSTimeInterval max = a < b ? b : a;
NSTimeInterval now = CFAbsoluteTimeGetCurrent();
bool result = now >= min && now <= max;

optimize core-data fetchrequest

Dear Community.
I have some issues, where i have to compare digits in core data with decreased range.
here is a first part of code:
NSFetchRequest *requestDestinationWeBuy = [[[NSFetchRequest alloc] init] autorelease];
[requestDestinationWeBuy setEntity:[NSEntityDescription entityForName:#"DestinationsListWeBuy"
inManagedObjectContext:moc]];
NSError *error = nil;
[requestDestinationWeBuy setPredicate:
[NSPredicate predicateWithFormat:
#"carrier.name == %# and (country == %#) and (specific == %#) and (prefix == %#) and (enabled == YES) and ((rateSheet == %#) OR (rateSheet == %#))",
outCarrierName,
countryStr,
specificStr,
outCarrierPrefixStr,
outCarrierRateSheet,
#"Price table"]];
NSArray *destinationWeBuyList = nil;
//[requestDestinationWeBuy includesSubentities];
[requestDestinationWeBuy setResultType:NSManagedObjectIDResultType];
destinationWeBuyList = [moc executeFetchRequest:requestDestinationWeBuy error:&error];
if i didn't match first predicate, i have to cut last digit of code volume (it's a digits and send fetch request again.
int maxCodeDeep = 8;
if ([codeStr length] < maxCodeDeep) maxCodeDeep = [[NSNumber numberWithUnsignedInt:[codeStr length]] intValue] - 1;
NSRange codeStrRange = NSMakeRange(0,[codeStr length]);
NSString *changedCode = [NSString string];
BOOL huntingWasSuccess = NO;
for (NSUInteger codeDeep = 0; codeDeep < maxCodeDeep;codeDeep++)
{
codeStrRange.length = codeStrRange.length - 1;
changedCode = [codeStr substringWithRange:codeStrRange];
NSFetchRequest *compareCode = [[[NSFetchRequest alloc] init] autorelease];
[compareCode setEntity:[NSEntityDescription entityForName:#"CodesvsDestinationsList"
inManagedObjectContext:moc]];
NSString *codeRelationshipName = #"destinationsListWeBuy";
[compareCode setPredicate:[NSPredicate predicateWithFormat:#"(%K.carrier.name == %#) and ((code == %#) OR (originalCode == %#)) and (%K.prefix == %#) and (enabled == YES) and ((rateSheetID == %#) OR (rateSheetID == %#))",codeRelationshipName, outCarrierName,changedCode,changedCode, codeRelationshipName, outCarrierPrefixStr,outCarrierRateSheetID,#"65535"]];
//[compareCode includesSubentities];
//[compareCode includesPropertyValues];
[compareCode setResultType:NSManagedObjectIDResultType];
NSArray *codeAfterComparing = [moc executeFetchRequest:compareCode error:&error];
if ([codeAfterComparing count] == 0) {
NSLog(#"ROUTING: Compare was unsucceseful with parameters:%#",compareCode);
continue;
}
else {
destinationWeBuy = [[moc objectWithID:[codeAfterComparing lastObject]] valueForKey:codeRelationshipName];
NSLog(#"ROUTING: Compare was succeseful with parameters:%#\n and destination object:%#\n Carrier name is %# ",compareCode,destinationWeBuy,carrierName);
//destinationWeBuy = [destinationWeBuyObj objectID];
huntingWasSuccess = YES;
break;
}
}
Unfortunately, this is get a time and processor resources.
Some of latest WWDC recommendations is propose me to using #count, but i don't understand how i can using it in my case.
p.s. important note - i'm using a object, which i find, in next operations and parent object.
Try using subquery to narrow down your search area.Subquery NSExpression

What kind of category methods do you use to make Cocoa programming easier?

I use a collection of category methods for Cocoa's built in classes to make my life easier. I'll post some examples, but I really want to see what other coders have come up with. What kind of handy category methods are you using?
Example #1:
#implementation NSColor (MyCategories)
+ (NSColor *)colorWithCode:(long)code
{
return [NSColor colorWithCalibratedRed:((code & 0xFF000000) >> 24) / 255.0
green:((code & 0x00FF0000) >> 16) / 255.0
blue:((code & 0x0000FF00) >> 8) / 255.0
alpha:((code & 0x000000FF) ) / 255.0];
}
#end
// usage:
NSColor * someColor = [NSColor colorWithCode:0xABCDEFFF];
Example #2:
#implementation NSView (MyCategories)
- (id)addNewSubViewOfType:(Class)viewType inFrame:(NSRect)frame
{
id newView = [[viewType alloc] initWithFrame:frame];
[self addSubview:newView];
return [newView autorelease];
}
#end
// usage:
NSButton * myButton = [someView addNewSubviewOfType:[NSButton class]
inFrame:someRect];
I've really been loving Andy Matuschak's "KVO+Blocks" category on NSObject. (Yes, it adds some new classes internally as implementation details, but the end result is just a category on NSObject). It lets you provide a block to be executed when a KVO-conforming value changes rather than having to handle every KVO observation in the observeValueForKeyPath:ofObject:change:context: method.
Regular Expressions with RegexKitLite. Download # RegexKitLite-3.1.tar.bz2
Category, which adds md5/sha1 hashing to NSString. NSData one is similar.
#define COMMON_DIGEST_FOR_OPENSSL
#import <CommonCrypto/CommonDigest.h>
#implementation NSString(GNExtensions)
- (NSString*)
hashMD5
{
NSData* data = [self dataUsingEncoding: NSUTF8StringEncoding allowLossyConversion: NO];
unsigned char hashingBuffer[16];
char outputBuffer[32];
CC_MD5([data bytes], [data length], hashingBuffer);
for(int index = 0; index < 16; index++)
{
sprintf(&outputBuffer[2 * index], "%02x", hashingBuffer[index]);
}
return([NSString stringWithCString: outputBuffer length: 32]);
}
- (NSString*)
hashSHA1
{
NSData* data = [self dataUsingEncoding: NSUTF8StringEncoding allowLossyConversion: NO];
unsigned char hashingBuffer[20];
char outputBuffer[40];
CC_SHA1([data bytes], [data length], hashingBuffer);
for(int index = 0; index < 20; index++)
{
sprintf(&outputBuffer[2 * index], "%02x", hashingBuffer[index]);
}
return([NSString stringWithCString: outputBuffer length: 40]);
}
#end
I have a few nifty methods on NSDate. This is self-explanatory:
-(BOOL)isOnTheSameDayAsDate:(NSDate *)date {
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *selfComponents = [cal components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit
fromDate:self];
NSDateComponents *dateComponents = [cal components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit
fromDate:date];
return (([selfComponents day] == [dateComponents day]) &&
([selfComponents month] == [dateComponents month]) &&
([selfComponents year] == [dateComponents year]));
}
Usage:
if ([aDate isOnTheSameDayAsDate:anotherDate]) { ... }
This provides a method to easily get dates like "9am the day before":
-(NSDate *)dateWithDayDelta:(NSInteger)daysBeforeOrAfter atHour:(NSUInteger)hour minute:(NSUInteger)minute second:(NSUInteger)second {
NSDate *date = [self addTimeInterval:(24 * 60 * 60) * daysBeforeOrAfter];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *comps = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit |
NSMinuteCalendarUnit | NSSecondCalendarUnit
fromDate:date];
[comps setHour:hour];
[comps setMinute:minute];
[comps setSecond:second];
return [calendar dateFromComponents:comps];
}
Usage:
// We want 9am yesterday
NSDate *nineAmYesterday = [[NSDate date] dateWithDayDelta:-1
atHour:9
minute:0
second:0];

Resources