Last active
October 7, 2015 22:58
-
-
Save HHuckebein/3238249 to your computer and use it in GitHub Desktop.
NSDate+Toolbox
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// NSDate+Toolbox.h | |
// | |
// Created by Rabe Bernd on 01.04.12. | |
// Copyright (c) 2012 RABE_IT Services. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
typedef enum { | |
NSDateCalendarUnitMinute = (1UL << 0), | |
NSDateCalendarUnitHour = (1UL << 1), | |
NSDateCalendarUnitDay = (1UL << 2), | |
NSDateCalendarUnitMonth = (1UL << 3), | |
NSDateCalendarUnitYear = (1UL << 4), | |
NSDateCalendarUnitMax = 0x1F | |
} NSDateCalendarUnits; | |
@interface NSDate (Toolbox) | |
// convert hour, minute, second into an angle between 0 and 2 Pi (00:00:00 - 23:59:59) | |
- (CGFloat)hourAngle; | |
// convert into decimal time | |
- (double)decimalTime; | |
// compares the receiver on day level | |
// returns wether receiver lies in the future or in the past | |
- (BOOL)crossesMidNight:(NSDate *)date; | |
// returns the difference in minutes | |
- (NSUInteger)minutesIntervalSinceReferenceDate:(NSDate *)date; | |
// the difference between dates for the specified component | |
// used by componentsDifferenceStringForCalendar:toDate:endDate:forCalendarUnits: | |
- (NSInteger)componentDifferenceForCalendar:(NSCalendar *)cal toDate:(NSDate *)endDate forCalendarUnit:(NSCalendarUnit)unit; | |
// return the difference in the form of x years x months x days x hours, format specified by units | |
// a format specifier is ignored if the corresponding value is zero | |
// used by timeDifferenceStringFromNowForCalendarUnits: | |
// numbers are presented in a localized fashion | |
- (NSString *)componentsDifferenceStringForCalendar:(NSCalendar *)cal toDate:(NSDate *)endDate forCalendarUnits:(NSDateCalendarUnits)units; | |
// returns a string for the specified components as difference from now | |
// if the difference for a given format specifier (calendarUnits) is smaller | |
// returns a string int the form "less than" with the largest component specified | |
// if the reference date lies in the past adds "overdue" to the output string | |
// otherwise returns "in ...difference as returned by componentsDifferenceStringForCalendar:toDate:forCalendarUnits:" | |
// | |
// see NSDateCategoryTest for unit tests | |
// | |
- (NSString *)timeDifferenceStringFromNowForCalendarUnits:(NSDateCalendarUnits)calendarUnits; | |
// second precision, time interval since January 1, −4712 (4713 BC) | |
- (NSTimeInterval)julianDay; | |
// FOR SUNSET/SUNRISE CALCULATION ONLY | |
// julianDay at 0:00 hour | |
- (NSTimeInterval)julianDay0; | |
// Julian date without hours, minutes and secondes | |
- (NSInteger)julianDayNumber; | |
// the modified julian day begins at greenwich mean midnight | |
- (NSTimeInterval)mJulianDay; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// NSDate+Toolbox.m | |
// | |
// Created by Rabe Bernd on 01.04.12. | |
// Copyright (c) 2012 RABE_IT Services. All rights reserved. | |
// | |
#import "NSDate+Toolbox.h" | |
@implementation NSDate(Toolbox) | |
- (CGFloat)hourAngle | |
{ | |
NSUInteger unitFlags = NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit; | |
NSCalendar *calendar = [NSCalendar currentCalendar]; | |
NSDateComponents *comps = [calendar components:unitFlags fromDate:self]; | |
CGFloat hourAngle = [comps hour] * 3600 + [comps minute] * 60 + [comps second]; | |
hourAngle /= 86400.; | |
hourAngle *= 2 * M_PI; | |
return hourAngle; | |
} | |
- (double)decimalTime | |
{ | |
NSUInteger unitFlags = NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit; | |
NSCalendar *calendar = [NSCalendar currentCalendar]; | |
NSDateComponents *comps = [calendar components:unitFlags fromDate:self]; | |
double decimalTime = [comps hour] + [comps minute] * 60/3600. + [comps second] / 3600.; | |
return decimalTime; | |
} | |
- (BOOL)crossesMidNight:(NSDate *)date | |
{ | |
NSCalendar *calendar = [NSCalendar currentCalendar]; | |
NSDateComponents* compRefDate = [calendar components:NSDayCalendarUnit fromDate:self]; | |
NSDateComponents* compOtherDate = [calendar components:NSDayCalendarUnit fromDate:date]; | |
NSComparisonResult result; | |
if ([compRefDate day] == [compOtherDate day]) { | |
result = NSOrderedSame; | |
} | |
else { | |
result = [compRefDate day] < [compOtherDate day] ? NSOrderedAscending : NSOrderedDescending; | |
} | |
return result != NSOrderedSame ? YES : NO; | |
} | |
- (NSUInteger)minutesIntervalSinceReferenceDate:(NSDate *)date | |
{ | |
NSCalendar *calendar = [NSCalendar currentCalendar]; | |
NSUInteger interval; | |
NSComparisonResult result = [self compare:date]; | |
if (result == NSOrderedSame) { | |
return 0; | |
} | |
NSDateComponents* compRefDate = [calendar components:NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit fromDate:self]; | |
NSDateComponents* compOtherDate = [calendar components:NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit fromDate:date]; | |
if (result == NSOrderedDescending) { | |
compOtherDate.day++; | |
} | |
NSDate *startDate = [calendar dateFromComponents:compRefDate]; | |
NSDate *endDate = [calendar dateFromComponents:compOtherDate]; | |
interval = fabs([startDate timeIntervalSinceDate:endDate])/60; | |
return interval; | |
} | |
- (NSString *)_highestComponentStringFromCalendarUnit:(NSDateCalendarUnits)units | |
{ | |
NSString *compString = nil; | |
if (units & NSDateCalendarUnitYear) compString = @"year"; | |
else if (units & NSDateCalendarUnitMonth) compString = @"month"; | |
else if (units & NSDateCalendarUnitDay) compString = @"day"; | |
else if (units & NSDateCalendarUnitHour) compString = @"hour"; | |
return compString; | |
} | |
- (NSString *)_lowestComponentStringFromCalendarUnit:(NSDateCalendarUnits)units | |
{ | |
NSString *compString = nil; | |
if (units & NSDateCalendarUnitHour) compString = @"hour"; | |
else if (units & NSDateCalendarUnitDay) compString = @"day"; | |
else if (units & NSDateCalendarUnitMonth) compString = @"month"; | |
else if (units & NSDateCalendarUnitYear) compString = @"year"; | |
return compString; | |
} | |
- (NSInteger)componentDifferenceForCalendar:(NSCalendar *)cal toDate:(NSDate *)endDate forCalendarUnit:(NSCalendarUnit)unit | |
{ | |
NSInteger startUnit = [cal ordinalityOfUnit:unit inUnit:NSEraCalendarUnit forDate:self]; | |
NSInteger endUnit = [cal ordinalityOfUnit:unit inUnit: NSEraCalendarUnit forDate:endDate]; | |
return endUnit - startUnit; | |
} | |
- (NSString *)componentsDifferenceStringForCalendar:(NSCalendar *)cal toDate:(NSDate *)endDate forCalendarUnits:(NSDateCalendarUnits)units | |
{ | |
NSMutableString *compString = [NSMutableString string]; | |
NSDateComponents *comps = [[NSDateComponents alloc] init]; | |
NSDate *refDate = endDate; | |
if (units & NSDateCalendarUnitYear) { | |
NSInteger years = [self componentDifferenceForCalendar:cal toDate:refDate forCalendarUnit:NSYearCalendarUnit]; | |
if (years) { | |
NSNumber *theYears = [NSNumber numberWithInt:abs(years)]; | |
[compString appendFormat:@"%@ %@ ", [NSNumberFormatter localizedStringFromNumber:theYears numberStyle:NSNumberFormatterDecimalStyle], abs(years) > 1 ? @"years" : @"year"]; | |
[comps setYear:-years]; | |
refDate = [cal dateByAddingComponents:comps toDate:endDate options:0]; | |
} | |
} | |
if (units & NSDateCalendarUnitMonth) { | |
NSInteger months = [self componentDifferenceForCalendar:cal toDate:refDate forCalendarUnit:NSMonthCalendarUnit]; | |
if (months) { | |
NSNumber *theMonths = [NSNumber numberWithInt:abs(months)]; | |
[compString appendFormat:@"%@ %@ ", [NSNumberFormatter localizedStringFromNumber:theMonths numberStyle:NSNumberFormatterDecimalStyle], abs(months) > 1 ? @"months" : @"month"]; | |
[comps setMonth:-months]; | |
refDate = [cal dateByAddingComponents:comps toDate:endDate options:0]; | |
} | |
} | |
if (units & NSDateCalendarUnitDay) { | |
NSInteger days = [self componentDifferenceForCalendar:cal toDate:refDate forCalendarUnit:NSDayCalendarUnit]; | |
if (days) { | |
NSNumber *theDays = [NSNumber numberWithInt:abs(days)]; | |
[compString appendFormat:@"%@ %@ ", [NSNumberFormatter localizedStringFromNumber:theDays numberStyle:NSNumberFormatterDecimalStyle], abs(days) > 1 ? @"days" : @"day"]; | |
[comps setDay:-days]; | |
refDate = [cal dateByAddingComponents:comps toDate:endDate options:0]; | |
} | |
} | |
if (units & NSDateCalendarUnitHour) { | |
NSInteger hours = [self componentDifferenceForCalendar:cal toDate:refDate forCalendarUnit:NSHourCalendarUnit]; | |
if (hours) { | |
NSNumber *theHours = [NSNumber numberWithInt:abs(hours)]; | |
[compString appendFormat:@"%@ %@ ", [NSNumberFormatter localizedStringFromNumber:theHours numberStyle:NSNumberFormatterDecimalStyle], abs(hours) > 1 ? @"hours" : @"hour"]; | |
[comps setHour:-hours]; | |
refDate = [cal dateByAddingComponents:comps toDate:endDate options:0]; | |
} | |
} | |
if (units & NSDateCalendarUnitMinute) { | |
NSInteger minutes = [self componentDifferenceForCalendar:cal toDate:refDate forCalendarUnit:NSMinuteCalendarUnit]; | |
if (minutes) { | |
NSNumber *theMinutes = [NSNumber numberWithInt:abs(minutes)]; | |
[compString appendFormat:@"%@ %@", [NSNumberFormatter localizedStringFromNumber:theMinutes numberStyle:NSNumberFormatterDecimalStyle], abs(minutes) > 1 ? @"minutes" : @"minute"]; | |
} | |
} | |
return compString; | |
} | |
- (NSString *)timeDifferenceStringForComponents:(NSDateComponents *)components forCalendarUnits:(NSDateCalendarUnits)units | |
{ | |
NSMutableString *compString = [NSMutableString string]; | |
NSInteger years = abs([components year]); | |
if (years && (units & NSDateCalendarUnitYear)) { | |
[compString appendFormat:@"%@ %@ ", [NSNumberFormatter localizedStringFromNumber:@(years) numberStyle:NSNumberFormatterDecimalStyle], abs(years) > 1 ? @"years" : @"year"]; | |
} | |
NSInteger months = abs([components month]); | |
if (months && (units & NSDateCalendarUnitMonth)) { | |
[compString appendFormat:@"%@ %@ ", [NSNumberFormatter localizedStringFromNumber:@(months) numberStyle:NSNumberFormatterDecimalStyle], abs(months) > 1 ? @"months" : @"month"]; | |
} | |
NSInteger days = abs([components day]); | |
if (days && (units & NSDateCalendarUnitDay)) { | |
[compString appendFormat:@"%@ %@ ", [NSNumberFormatter localizedStringFromNumber:@(days) numberStyle:NSNumberFormatterDecimalStyle], abs(days) > 1 ? @"days" : @"day"]; | |
} | |
NSInteger hours = abs([components hour]); | |
if (hours && (units & NSDateCalendarUnitHour)) { | |
[compString appendFormat:@"%@ %@ ", [NSNumberFormatter localizedStringFromNumber:@(hours) numberStyle:NSNumberFormatterDecimalStyle], abs(hours) > 1 ? @"hours" : @"hour"]; | |
} | |
NSInteger minutes = abs([components minute]); | |
if (minutes && (units & NSDateCalendarUnitMinute)) { | |
[compString appendFormat:@"%@ %@", [NSNumberFormatter localizedStringFromNumber:@(minutes) numberStyle:NSNumberFormatterDecimalStyle], abs(minutes) > 1 ? @"minutes" : @"minute"]; | |
} | |
return compString; | |
} | |
- (NSString *)timeDifferenceStringFromNowForCalendarUnits:(NSDateCalendarUnits)calendarUnits | |
{ | |
NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit; | |
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; | |
NSDateComponents *todayComps = [calendar components:unitFlags fromDate:[NSDate date]]; | |
NSDate *today = [calendar dateFromComponents:todayComps]; | |
NSMutableString *interval = [NSMutableString string]; | |
NSInteger format = 0; | |
BOOL addOverdueString = NO; | |
NSDateComponents *components = [calendar components:unitFlags fromDate:today toDate:self options:0]; | |
NSInteger years = [components year]; | |
NSInteger months = [components month]; | |
NSInteger days = [components day]; | |
NSInteger hours = [components hour]; | |
NSInteger minutes = [components minute]; | |
if (years < 0) addOverdueString = YES; | |
else if (years == 0 && months < 0) addOverdueString = YES; | |
else if (months == 0 && days < 0) addOverdueString = YES; | |
else if (days == 0 && hours < 0) addOverdueString = YES; | |
else if (hours == 0 && minutes < 0) addOverdueString = YES; | |
// make the format from existing items (year, month etc.) | |
format = years ? NSDateCalendarUnitYear : 0; | |
format |= months ? NSDateCalendarUnitMonth : 0; | |
format |= days ? NSDateCalendarUnitDay : 0; | |
format |= hours ? NSDateCalendarUnitHour : 0; | |
format |= minutes ? NSDateCalendarUnitMinute : 0; | |
if (!addOverdueString) { | |
[interval appendString:@"in "]; | |
} | |
if (format &= calendarUnits) { | |
// format matches at least a part calendarUnits | |
// so we use the format (existing items) to construct the interval | |
// [interval appendString:[today componentsDifferenceStringForCalendar:calendar toDate:self forCalendarUnits:format]]; | |
[interval appendString:[today timeDifferenceStringForComponents:components forCalendarUnits:format]]; | |
} | |
else { | |
// if the format has only units smaller whats specified in calendarUnits | |
// we use the lowest item specified | |
if (format < (~calendarUnits & NSDateCalendarUnitMax)) { | |
[interval appendFormat:@"less than a %@", [self _lowestComponentStringFromCalendarUnit:calendarUnits]]; | |
} | |
else { | |
// otherwise we use calendarUnits and let the componentsDifferenceStringForCalendar:toDate:forCalendarUnits: | |
// figure out what is existing | |
[interval appendString:[today timeDifferenceStringForComponents:components forCalendarUnits:calendarUnits]]; | |
} | |
} | |
if (addOverdueString) { | |
[interval appendString:@" overdue"]; | |
} | |
return interval; | |
} | |
//---------------------- Julian Date Functions ----------------------- | |
// http://en.wikipedia.org/wiki/Julian_day | |
// The Julian day number can be calculated using the following formulas | |
// (integer division is used exclusively, that is, the remainder of all divisions are dropped) | |
// The months (M) January to December are 1 to 12. For the year (Y) astronomical year numbering | |
// is used, thus 1 BC is 0, 2 BC is −1, and 4713 BC is −4712. D is the day of the month. | |
// JDN is the Julian Day Number, which pertains to the noon occurring in the corresponding | |
// calendar date. | |
- (NSTimeInterval)julianDay | |
{ | |
NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; | |
cal.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; | |
NSCalendarUnit units = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit; | |
NSDateComponents *dateComps = [cal components:units fromDate:self]; | |
NSInteger month = [dateComps month]; | |
NSInteger year = [dateComps year]; | |
NSInteger day = [dateComps day]; | |
NSInteger hour = [dateComps hour]; | |
NSInteger minute = [dateComps minute]; | |
NSInteger second = [dateComps second]; | |
int a = (14 - month) / 12; | |
int y = year + 4800 - a; | |
int m = month + 12 * a - 3; | |
int jdn = day + (153 * m + 2)/5 + 365 * y + y/4 - y/100 + y/400 - 32045; | |
double jd = jdn + (hour - 12)/24. + minute/1440. + second/86400.; | |
return jd; | |
} | |
- (NSInteger)julianDayNumber | |
{ | |
NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; | |
NSCalendarUnit units = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit; | |
NSDateComponents *dateComps = [cal components:units fromDate:self]; | |
NSInteger month = [dateComps month]; | |
NSInteger year = [dateComps year]; | |
NSInteger day = [dateComps day]; | |
int a = (14 - month) / 12; | |
int y = year + 4800 - a; | |
int m = month + 12 * a - 3; | |
int jdn = day + (153 * m + 2)/5 + 365 * y + y/4 - y/100 + y/400 - 32045; | |
return jdn; | |
} | |
- (NSTimeInterval)julianDay0 | |
{ | |
NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; | |
NSCalendarUnit units = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit; | |
NSDateComponents *dateComps = [cal components:units fromDate:self]; | |
NSInteger month = [dateComps month]; | |
NSInteger year = [dateComps year]; | |
NSInteger day = [dateComps day]; | |
int a = (14 - month) / 12; | |
int y = year + 4800 - a; | |
int m = month + 12 * a - 3; | |
int jdn = day + (153 * m + 2)/5 + 365 * y + y/4 - y/100 + y/400 - 32045; | |
double jd = jdn - 0.5; | |
return jd; | |
} | |
// the modified julian day begins at greenwich mean midnight | |
- (NSTimeInterval)mJulianDay | |
{ | |
return [self julianDay] - 2400000.5; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment