This is NSCalendarDate.m in view mode; [Download] [Up]
/* NSCalendarDate.m Copyright (C) 1995, 1996 Ovidiu Predescu and Mircea Oancea. All rights reserved. Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro> Ovidiu Predescu <ovidiu@bx.logicnet.ro> Florin Mihaila <phil@pathcom.com> This file is part of libFoundation. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. We disclaim all warranties with regard to this software, including all implied warranties of merchantability and fitness, in no event shall we be liable for any special, indirect or consequential damages or any damages whatsoever resulting from loss of use, data or profits, whether in an action of contract, negligence or other tortious action, arising out of or in connection with the use or performance of this software. */ #include <Foundation/NSDate.h> #include <Foundation/NSArray.h> #include <Foundation/NSDictionary.h> #include <Foundation/NSString.h> #include <Foundation/NSException.h> #include <Foundation/NSAutoreleasePool.h> #include <Foundation/NSCoder.h> #include <extensions/PrintfFormatScanner.h> #include "NSConcreteDate.h" #include "NSCalendarDate.h" #include "NSCalendarDateScannerHandler.h" #include "NSCalendarDateScanf.h" #include <math.h> /* * Functions to deal with date conversions (phil & miki) * deal with dates in day:month:year reprezented in int form * as days since year 1st Jan year 1 ac */ static int AdjustDay(int month, int day, int year); static void DecDate(int *month, int *day, int *year); static void IncDate(int *month, int *day, int *year); static int nr_nebisect(int a); static int day_in_year(int month, int day, int year); static void Date2Long(int theMonth, int theDay, int theYear, long *theDate); static void Long2Date(long theDate, int*theMonth, int*theDay, int*theYear); static void SubFromDate(int *month, int *day, int *year, int dif); static void AddToDate(int *month, int *day, int *year, int dif); static void DecDate( int *month, int *day, int *year); static void IncDate( int *month, int *day, int *year); /* * Magic conversion offsets */ #define DATE_OFFSET 730486 /* Number of days from January 1, 1 to January 1, 2001 */ #define DAY_OFFSET 0 /* * NSCalendarDate implementation */ /* timeSinceRef is expressed in seconds relative to GMT from the reference day. The timeSinceRef is adjusted to represent this value when a date is created and a time zone is specified. Changing the time zone explicitly does not modify the timeSinceRef value. Only the methods that work with time components should take into consideration the time zone. */ @implementation NSCalendarDate static NSString* DEFAULT_FORMAT = @"%Y-%m-%d %H:%M:%S %z"; static NSDictionary* defaultLocaleDictionary = nil; + (void)initialize { id fullMonths[] = { @"January", @"February", @"March", @"April", @"May", @"June", @"July", @"August", @"September", @"October", @"November", @"December" }; id shortMonths[] = { @"Jan", @"Feb", @"Mar", @"Apr", @"May", @"Jun", @"Jul", @"Aug", @"Sep", @"Oct", @"Nov", @"Dec" }; id fullDays[] = { @"Sunday", @"Monday", @"Tuesday", @"Wednesday", @"Thusday", @"Friday", @"Saturday" }; id shortDays[] = { @"Sun", @"Mon", @"Tue", @"Wed", @"Thu", @"Fri", @"Sat" }; defaultLocaleDictionary = [[NSDictionary dictionaryWithObjectsAndKeys: @"%a %b %d %H:%M:%S %z %Y", @"NSTimeDateFormatString", [NSArray arrayWithObjects:@"AM", @"PM", nil], @"NSAMPMDesignation", [[[NSArray alloc] initWithObjects:fullMonths count:sizeof(fullMonths) / sizeof(id)] autorelease], @"NSMonthNameArray", [[[NSArray alloc] initWithObjects:shortMonths count:sizeof(shortMonths) / sizeof(id)] autorelease], @"NSShortMonthNameArray", [[[NSArray alloc] initWithObjects:fullDays count:sizeof(fullDays) / sizeof(id)] autorelease], @"NSWeekDayNameArray", [[[NSArray alloc] initWithObjects:shortDays count:sizeof(shortDays) / sizeof(id)] autorelease], @"NSShortWeekDayNameArray", nil] retain]; } /* * Inherited from NSDate cluster */ - (id)copyWithZone:(NSZone*)zone { NSCalendarDate* date = [[self class] allocWithZone:zone]; date->timeSinceRef = timeSinceRef; date->timeZoneDetail = [timeZoneDetail retain]; date->formatString = [formatString retain]; return date; } - (void)dealloc { [timeZoneDetail release]; [formatString release]; [super dealloc]; } - initWithTimeIntervalSinceReferenceDate:(NSTimeInterval)seconds { [super init]; timeSinceRef = seconds; timeZoneDetail = [[[NSTimeZone localTimeZone] timeZoneDetailForDate:self] retain]; timeSinceRef -= [timeZoneDetail timeZoneSecondsFromGMT]; formatString = [DEFAULT_FORMAT retain]; return self; } - init { [super init]; timeSinceRef = [NSDate timeIntervalSinceReferenceDate]; timeZoneDetail = [[[NSTimeZone localTimeZone] timeZoneDetailForDate:self] retain]; timeSinceRef -= [timeZoneDetail timeZoneSecondsFromGMT]; formatString = [DEFAULT_FORMAT retain]; return self; } - (NSTimeInterval)timeIntervalSinceReferenceDate { return timeSinceRef; } - (void)setTimeIntervalSinceReferenceDate:(NSTimeInterval)seconds { timeSinceRef = seconds; } - (NSComparisonResult)compare:(NSDate*)other { if ([other class] == [self class]) { NSTimeInterval diff = timeSinceRef - [other timeIntervalSinceReferenceDate]; return (diff < 0 ? NSOrderedAscending : (diff == 0 ? NSOrderedSame : NSOrderedDescending)); } NSAssert(0, @"Cannot compare NSCalendarDate with %@", [other class]); return NSOrderedSame; } /* * NSCalendarDate methods */ + (id)calendarDate { return [[[self alloc] init] autorelease]; } + (id)dateWithYear:(int)year month:(unsigned)month day:(unsigned)day hour:(unsigned)hour minute:(unsigned)minute second:(unsigned)second timeZone:(NSTimeZone*)aTimeZone { return [[[self alloc] initWithYear:year month:month day:day hour:hour minute:minute second:second timeZone:aTimeZone] autorelease]; } + (id)dateWithString:(NSString*)string { return [[[self alloc] initWithString:string calendarFormat:nil locale:nil] autorelease]; } + (id)dateWithString:(NSString*)string calendarFormat:(NSString*)format { return [[[self alloc] initWithString:string calendarFormat:format locale:nil] autorelease]; } + (id)dateWithString:(NSString*)string calendarFormat:(NSString*)format locale:(NSDictionary*)locale { return [[[self alloc] initWithString:string calendarFormat:format locale:locale] autorelease]; } - initWithYear:(int)year month:(unsigned)month day:(unsigned)day hour:(unsigned)hour minute:(unsigned)minute second:(unsigned)second timeZone:(NSTimeZone*)timeZone { long date; Date2Long(month, day, year, &date); if (!timeZone) timeZone = [NSTimeZone localTimeZone]; timeSinceRef = ((NSTimeInterval)date - DATE_OFFSET) * 86400 + hour * 3600 + minute * 60 + second; timeZoneDetail = [[timeZone timeZoneDetailForDate:self] retain]; timeSinceRef -= [timeZoneDetail timeZoneSecondsFromGMT]; formatString = [DEFAULT_FORMAT retain]; return self; } - initWithString:(NSString*)description { return [self initWithString:description calendarFormat:DEFAULT_FORMAT locale:defaultLocaleDictionary]; } - initWithString:(NSString*)description calendarFormat:(NSString*)format { return [self initWithString:description calendarFormat:format locale:defaultLocaleDictionary]; } - initWithString:(NSString*)description calendarFormat:(NSString*)format locale:(NSDictionary*)locale { id pool = [NSAutoreleasePool new]; id formatScanner = [[NSCalendarDateScanf new] autorelease]; if (!locale) locale = defaultLocaleDictionary; if (!format) format = DEFAULT_FORMAT; [formatScanner setString:description withLocale:locale]; if ([formatScanner parseFormatString:format context:NULL]) [self initWithYear:[formatScanner year] month:[formatScanner month] day:[formatScanner day] hour:[formatScanner hour] minute:[formatScanner minute] second:[formatScanner second] timeZone:[formatScanner timeZone]]; else { [self autorelease]; return nil; } [format retain]; [formatString release]; formatString = format; [pool release]; return self; } - (NSTimeZoneDetail*)timeZoneDetail { return timeZoneDetail; } - (void)setTimeZone:(NSTimeZone*)timeZone { [timeZoneDetail release]; timeZoneDetail = [[timeZone timeZoneDetailForDate:self] retain]; } - (NSString*)calendarFormat { return formatString; } - (void)setCalendarFormat:(NSString*)format { [format retain]; [formatString release]; formatString = format; } - (int)yearOfCommonEra { NSTimeInterval tm = timeSinceRef + [timeZoneDetail timeZoneSecondsFromGMT]; long date = floor (tm / 86400) + DATE_OFFSET; int d, m, y; Long2Date(date, &m, &d, &y); return y; } - (int)monthOfYear { NSTimeInterval tm = timeSinceRef + [timeZoneDetail timeZoneSecondsFromGMT]; long date = floor (tm / 86400) + DATE_OFFSET; int d, m, y; Long2Date(date, &m, &d, &y); return m; } - (int)dayOfMonth { NSTimeInterval tm = timeSinceRef + [timeZoneDetail timeZoneSecondsFromGMT]; long date = floor (tm / 86400) + DATE_OFFSET; int d, m, y; Long2Date(date, &m, &d, &y); return d; } - (int)dayOfWeek { NSTimeInterval tm = timeSinceRef + [timeZoneDetail timeZoneSecondsFromGMT]; long noOfDays = floor (tm / 86400) + DATE_OFFSET; int dayOfWeek = abs (noOfDays) % 7 + DAY_OFFSET; return dayOfWeek; } - (int)dayOfYear { NSTimeInterval tm = timeSinceRef + [timeZoneDetail timeZoneSecondsFromGMT]; long date = floor(tm / 86400) + DATE_OFFSET; int d, m, y; Long2Date(date, &m, &d, &y); return day_in_year(m, d, y); } - (int)hourOfDay { NSTimeInterval tm = timeSinceRef + [timeZoneDetail timeZoneSecondsFromGMT]; NSTimeInterval tr = tm / 3600; NSTimeInterval ti = floor(tr/24); int ts = (tr - ti * 24); return ts < 0 ? 24 + ts : ts; } - (int)minuteOfHour { NSTimeInterval tm = timeSinceRef + [timeZoneDetail timeZoneSecondsFromGMT]; NSTimeInterval tr = tm / 60; NSTimeInterval ti = floor (tr / 60); int ts = (tr - ti * 60); return ts < 0 ? 60 + ts : ts; } - (int)secondOfMinute { NSTimeInterval tm = timeSinceRef + [timeZoneDetail timeZoneSecondsFromGMT]; NSTimeInterval ti = floor (tm / 60); int ts = (tm - ti * 60); return ts < 0 ? 60 + ts : ts; } - (NSCalendarDate*)addYear:(int)year month:(int)month day:(int)day hour:(int)hour minute:(int)minute second:(int)second { return [self dateByAddingYears:year month:month day:day hour:hour minute:minute second:second]; } - (id)dateByAddingYears:(int)year month:(int)month day:(int)day hour:(int)hour minute:(int)minute second:(int)second { NSTimeInterval tm = timeSinceRef + [timeZoneDetail timeZoneSecondsFromGMT]; NSCalendarDate* date; long monthDayYear, hourMinuteSecond; int selfMonth, selfDay, selfYear; NSTimeInterval newInterval; monthDayYear = floor (tm / 86400) + DATE_OFFSET; Long2Date(monthDayYear, &selfMonth, &selfDay, &selfYear); hourMinuteSecond = (long)(tm - 86400 * ((NSTimeInterval)monthDayYear - DATE_OFFSET)); /* Add day */ if (day >= 0) AddToDate (&selfMonth, &selfDay, &selfYear, day); else SubFromDate (&selfMonth, &selfDay, &selfYear, -day); /* Add month and year */ selfYear += month / 12 + year; selfMonth += month % 12; if (selfMonth > 12) { selfYear++; selfMonth -= 12; } /* Adjust the day */ selfDay = AdjustDay(selfMonth, selfDay, selfYear); /* Convert the (month, day, year) to long */ Date2Long (selfMonth, selfDay, selfYear, &monthDayYear); /* Compute the new interval */ newInterval = ((NSTimeInterval)monthDayYear - DATE_OFFSET) * 86400 + hourMinuteSecond + hour * 3600 + minute * 60 + second; date = [[[self class] alloc] autorelease]; date->timeSinceRef = newInterval - [timeZoneDetail timeZoneSecondsFromGMT]; date->timeZoneDetail = [timeZoneDetail retain]; date->formatString = [formatString retain]; return date; } - (NSString*)description { return [NSCalendarDate descriptionForCalendarDate:self withFormat:formatString timeZoneDetail:timeZoneDetail locale:nil]; } - (NSString*)descriptionWithLocale:(NSDictionary*)locale { return [NSCalendarDate descriptionForCalendarDate:self withFormat:formatString timeZoneDetail:timeZoneDetail locale:locale]; } - (NSString*)descriptionWithCalendarFormat:(NSString*)format { return [NSCalendarDate descriptionForCalendarDate:self withFormat:format timeZoneDetail:timeZoneDetail locale:nil]; } - (NSString*)descriptionWithCalendarFormat:(NSString*)format timeZone:(NSTimeZone*)timeZone { return [NSCalendarDate descriptionForCalendarDate:self withFormat:format timeZoneDetail:[timeZone timeZoneDetailForDate:self] locale:nil]; } - (NSString*)descriptionWithCalendarFormat:(NSString*)format locale:(NSDictionary*)locale { return [NSCalendarDate descriptionForCalendarDate:self withFormat:format timeZoneDetail:timeZoneDetail locale:locale]; } /* Encoding */ - (Class)classForCoder { return [NSCalendarDate class]; } - (void)encodeWithCoder:(NSCoder*)aCoder { [aCoder encodeValueOfObjCType:@encode(NSTimeInterval) at:&timeSinceRef]; [aCoder encodeObject:[timeZoneDetail timeZoneName]]; [aCoder encodeObject:formatString]; } - (id)initWithCoder:(NSCoder*)aDecoder { NSTimeZone* timeZone; [aDecoder decodeValueOfObjCType:@encode(NSTimeInterval) at:&timeSinceRef]; timeZone = [NSTimeZone timeZoneWithName:[aDecoder decodeObject]]; timeZoneDetail = [[timeZone timeZoneDetailForDate:self] retain]; formatString = [[aDecoder decodeObject] retain]; return self; } @end /* * NSCalendarDateImplementation */ @implementation NSCalendarDate (NSCalendarDateImplementation) + (NSString*)descriptionForCalendarDate:(NSCalendarDate*)date withFormat:(NSString*)format timeZoneDetail:(NSTimeZoneDetail*)detail locale:(NSDictionary*)locale { id formatScanner = [PrintfFormatScanner new]; id scannerHandler = [NSCalendarDateScannerHandler alloc]; id string; [scannerHandler initForCalendarDate:date timeZoneDetail:detail]; [formatScanner setFormatScannerHandler:scannerHandler]; string = [formatScanner stringWithFormat:format arguments:NULL]; [scannerHandler release]; [formatScanner release]; return string; } + (NSString*)shortDayOfWeek:(int)day { return [[defaultLocaleDictionary objectForKey:@"NSShortWeekDayNameArray"] objectAtIndex:day]; } + (NSString*)fullDayOfWeek:(int)day { return [[defaultLocaleDictionary objectForKey:@"NSWeekDayNameArray"] objectAtIndex:day]; } + (NSString*)shortMonthOfYear:(int)month { return [[defaultLocaleDictionary objectForKey:@"NSShortMonthNameArray"] objectAtIndex:month - 1]; } + (NSString*)fullMonthOfYear:(int)month { return [[defaultLocaleDictionary objectForKey:@"NSMonthNameArray"] objectAtIndex:month - 1]; } + (int)decimalDayOfYear:(int)year month:(int)month day:(int)day { return day_in_year(month, day, year); } @end /* * Functions to deal with date conversions (phil & miki) * deal with dates in day:month:year reprezented in int form * as days since year 1st Jan year 1 ac */ static int nDays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; #define bisect(a) ((a) % 4 == 0 && ((a) % 400 == 0 || (a) % 100 )) #define nr_bisect(a) ((a - 1) / 4 - nr_nebisect(a - 1)) static int AdjustDay(int month, int day, int year) { if(bisect(year) && month == 2 && day == 29) return day; if (day > nDays[month - 1]) day = nDays[month - 1]; return day; } static int nr_nebisect(int a) { int i; int ret = 0; for(i = 100; i <= a; i += 100) ret += (i % 400 != 0); return ret; } static int day_in_year(int month, int day, int year) { int ret = day + (month > 2 && bisect(year)) ? 1 : 0; while (month--) day += nDays[month]; return ret; } static void Date2Long(int theMonth, int theDay, int theYear, long *theDate) { long base, offset = 0; int i; base = ((long)theYear - 1) * 365 + nr_bisect(theYear); for (i = 1; i < theMonth; i++) offset += nDays[i - 1]; offset += theDay; if (theMonth > 2 && bisect(theYear)) offset++; *theDate = base + offset; } static void Long2Date(long theDate, int *theMonth, int *theDay, int *theYear) { int month = 1, day, year, offset, i, days = 0, dif; long aproxDate; year = (theDate - 1) / 365 + 1; offset = (theDate - 1) % 365 + 1; for (i = 1; i <= 12; i++) { month = i; if (days + nDays[i - 1] >= offset) break; days += nDays[i - 1]; } day = offset - days; Assert (day <= nDays[month - 1] && day > 0); Date2Long(month, day, year, &aproxDate); dif = aproxDate - theDate; Assert(dif >= 0); SubFromDate(&month, &day, &year, dif); *theMonth = month; *theDay = day; *theYear = year; } static void SubFromDate(int *month, int *day, int *year, int dif) { while (dif > 0) { if (*day > dif) { *day -= dif; break; } dif -= *day; *day = 1; DecDate(month, day, year); } } static void AddToDate(int *month, int *day, int *year, int dif) { int rest, bi; while (dif > 0) { bi = bisect(*year); if ((*day + dif <= nDays[*month - 1]) || (*month == 2 && bi && *day + dif <= 29)) { (*day) += dif; break; } rest = nDays[*month - 1] - (*day) + (*month == 2 && bi) + 1; dif -= rest; *day = nDays[*month - 1] + (*month == 2 && bi); IncDate(month, day, year); } } static void DecDate( int *month, int *day, int *year) { (*day)--; if (*day) return; (*month)--; if (*month) { if (*month == 2 && bisect(*year)) *day = 29; else *day = nDays[*month-1]; return; } *month = 12; *day = 31; (*year)--; Assert(*year != 0); } static void IncDate( int *month, int *day, int *year) { (*day)++; if (*day <= nDays[*month - 1] || (*month == 2 && bisect(*year) && *day == 29)) return; *day = 1; (*month)++; if (*month <= 12) return; *month = 1; (*year)++; Assert(*year != 0); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.