Skip to content

Instantly share code, notes, and snippets.

@HHuckebein
Last active October 7, 2015 22:58
Show Gist options
  • Save HHuckebein/3238249 to your computer and use it in GitHub Desktop.
Save HHuckebein/3238249 to your computer and use it in GitHub Desktop.
NSDate+Toolbox
//
// 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
//
// 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