From aead21cd6d9502b51d3d0114ffa5db5a51258f68 Mon Sep 17 00:00:00 2001 From: Andrew McCallum Date: Thu, 31 Oct 1996 17:13:12 +0000 Subject: [PATCH] New file. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1884 72102866-910b-0410-8b05-ffd578937521 --- Source/NSCalendarDate.m | 753 ++++++++++++++++++++++++++++++++++++++++ Testing/nsdate.m | 126 +++++++ 2 files changed, 879 insertions(+) create mode 100644 Source/NSCalendarDate.m create mode 100644 Testing/nsdate.m diff --git a/Source/NSCalendarDate.m b/Source/NSCalendarDate.m new file mode 100644 index 000000000..fd8e52d9f --- /dev/null +++ b/Source/NSCalendarDate.m @@ -0,0 +1,753 @@ +/* Implementation for NSCalendarDate for GNUstep + Copyright (C) 1996 Free Software Foundation, Inc. + + Author: Scott Christley + Date: October 1996 + + This file is part of the GNUstep Base Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +#ifndef __WIN32__ +#include +#endif /* !__WIN32__ */ +#include +#include +#ifndef __WIN32__ +#include +#endif /* !__WIN32__ */ + +// Absolute Gregorian date for NSDate reference date Jan 01 2001 +// +// N = 1; // day of month +// N = N + 0; // days in prior months for year +// N = N + // days this year +// + 365 * (year - 1) // days in previous years ignoring leap days +// + (year - 1)/4 // Julian leap days before this year... +// - (year - 1)/100 // ...minus prior century years... +// + (year - 1)/400 // ...plus prior years divisible by 400 + +#define GREGORIAN_REFERENCE 730486 + +@implementation NSCalendarDate + + +// +// Getting an NSCalendar Date +// ++ (NSCalendarDate *)calendarDate +{ + return [[[self alloc] init] autorelease]; +} + ++ (NSCalendarDate *)dateWithString:(NSString *)description + calendarFormat:(NSString *)format +{ + NSCalendarDate *d = [[NSCalendarDate alloc] initWithString: description + calendarFormat: format]; + return [d autorelease]; +} + ++ (NSCalendarDate *)dateWithString:(NSString *)description + calendarFormat:(NSString *)format + locale:(NSDictionary *)dictionary +{ + NSCalendarDate *d = [[NSCalendarDate alloc] initWithString: description + calendarFormat: format + locale: dictionary]; + return [d autorelease]; +} + ++ (NSCalendarDate *)dateWithYear:(int)year + month:(unsigned int)month + day:(unsigned int)day + hour:(unsigned int)hour + minute:(unsigned int)minute + second:(unsigned int)second + timeZone:(NSTimeZone *)aTimeZone +{ + NSCalendarDate *d = [[NSCalendarDate alloc] initWithYear: year + month: month + day: day + hour: hour + minute: minute + second: second + timeZone: aTimeZone]; + return [d autorelease]; +} + +// Initializing an NSCalendar Date +- (id)initWithString:(NSString *)description +{ + // +++ What is the locale? + return [self initWithString: description + calendarFormat: @"%Y-%m-%d %H:%M:%S %Z" + locale: nil]; +} + +- (id)initWithString:(NSString *)description + calendarFormat:(NSString *)format +{ + // ++ What is the locale? + return [self initWithString: description + calendarFormat: format + locale: nil]; +} + +// +// This function could possibly be written better +// but it works ok; currently ignores locale +// information and some specifiers. +// +- (id)initWithString:(NSString *)description + calendarFormat:(NSString *)format + locale:(NSDictionary *)dictionary +{ + const char *d = [description cString]; + const char *f = [format cString]; + char *newf; + int lf = strlen(f); + BOOL mtag = NO, dtag = NO, ycent = NO; + char ms[80] = "", ds[80] = ""; + int yd = 0, md = 0, dd = 0, hd = 0, mnd = 0, sd = 0; + void *pntr[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int order; + int yord = 0, mord = 0, dord = 0, hord = 0, mnord = 0, sord = 0; + int i; + + // If either the string or format is nil then raise exception + if (!description) + [NSException raise: NSInvalidArgumentException + format: @"NSCalendar date description is nil"]; + if (!format) + [NSException raise: NSInvalidArgumentException + format: @"NSCalendar date format is nil"]; + + // The strftime specifiers + // %a abbreviated weekday name according to locale + // %A full weekday name according to locale + // %b abbreviated month name according to locale + // %B full month name according to locale + // %d day of month as decimal number + // %H hour as a decimal number using 24-hour clock + // %I hour as a decimal number using 12-hour clock + // %j day of year as a decimal number + // %m month as decimal number + // %M minute as decimal number + // %p 'am' or 'pm' + // %S second as decimal number + // %U week of the current year as decimal number (Sunday first day) + // %W week of the current year as decimal number (Monday first day) + // %w day of the week as decimal number (Sunday = 0) + // %y year as a decimal number without century + // %Y year as a decimal number with century + // %Z time zone + // %% literal % character + + // Find the order of date elements + // and translate format string into scanf ready string + order = 1; + newf = malloc(lf+1); + for (i = 0;i < lf; ++i) + { + newf[i] = f[i]; + + // Only care about a format specifier + if (f[i] == '%') + { + // check the character that comes after + switch (f[i+1]) + { + // skip literal % + case '%': + ++i; + newf[i] = f[i]; + break; + + // is it the year + case 'Y': + ycent = YES; + case 'y': + yord = order; + ++order; + ++i; + newf[i] = 'd'; + pntr[yord] = (void *)&yd; + break; + + // is it the month + case 'b': + case 'B': + mtag = YES; // Month is character string + case 'm': + mord = order; + ++order; + ++i; + if (mtag) + { + newf[i] = 's'; + pntr[mord] = (void *)ms; + } + else + { + newf[i] = 'd'; + pntr[mord] = (void *)&md; + } + break; + + // is it the day + case 'a': + case 'A': + dtag = YES; // Day is character string + case 'd': + case 'j': + case 'w': + dord = order; + ++order; + ++i; + if (dtag) + { + newf[i] = 's'; + pntr[dord] = (void *)ds; + } + else + { + newf[i] = 'd'; + pntr[dord] = (void *)ⅆ + } + break; + + // is it the hour + case 'H': + case 'I': + hord = order; + ++order; + ++i; + newf[i] = 'd'; + pntr[hord] = (void *)&hd; + break; + + // is it the minute + case 'M': + mnord = order; + ++order; + ++i; + newf[i] = 'd'; + pntr[mnord] = (void *)&mnd; + break; + + // is it the second + case 'S': + sord = order; + ++order; + ++i; + newf[i] = 'd'; + pntr[sord] = (void *)&sd; + break; + + // Anything else is an invalid format + default: + free(newf); + [NSException raise: NSInvalidArgumentException + format: @"Invalid NSCalendar date, specifier %c not recognized in format %s", f[i+1], f]; + } + } + } + newf[lf] = '\0'; + + // Have sscanf parse and retrieve the values for us + if (order != 1) + sscanf(d, newf, pntr[1], pntr[2], pntr[3], pntr[4], pntr[5], pntr[6], + pntr[7], pntr[8], pntr[9]); + else + // nothing in the string? + ; + + // Put century on year if need be + // +++ How do we be year 2000 compliant? + if (!ycent) + yd += 1900; + + // Possibly convert month from string to decimal number + // +++ how do we take locale into account? + if (mtag) + { + } + + // Possibly convert day from string to decimal number + // +++ how do we take locale into account? + if (dtag) + { + } + + // +++ We need to take 'am' and 'pm' into account + + // +++ then there is the time zone + + free(newf); + + return [self initWithYear: yd month: md day: dd hour: hd + minute: mnd second: sd + timeZone: [NSTimeZone defaultTimeZone]]; +} + +- (id)initWithYear:(int)year + month:(unsigned int)month + day:(unsigned int)day + hour:(unsigned int)hour + minute:(unsigned int)minute + second:(unsigned int)second + timeZone:(NSTimeZone *)aTimeZone +{ + int a; + NSTimeInterval s; + + a = [self absoluteGregorianDay: day month: month year: year]; + + a -= GREGORIAN_REFERENCE; + s = (double)a * 86400; + s += hour * 3600; + s += minute * 60; + s += second; + + // +++ adjust for time zone + time_zone = (NSTimeZoneDetail *)aTimeZone; + + return [self initWithTimeIntervalSinceReferenceDate: s]; +} + +// Defalut initializer +- (id)initWithTimeIntervalSinceReferenceDate:(NSTimeInterval)seconds +{ + [super initWithTimeIntervalSinceReferenceDate: seconds]; + if (!calendar_format) + calendar_format = @"%Y-%m-%d %H:%M:%S %Z"; + if (!time_zone) + time_zone = [NSTimeZone defaultTimeZone]; + return self; +} + +// Retreiving Date Elements +- (void)getYear:(int *)year month:(int *)month day:(int *)day + hour:(int *)hour minute:(int *)minute second:(int *)second +{ + int h, m; + double a, b, c, d = [self dayOfCommonEra]; + + // Calculate year, month, and day + [self gregorianDateFromAbsolute: d day: day month: month year: year]; + + // Calculate hour, minute, and seconds + d -= GREGORIAN_REFERENCE; + d *= 86400; + a = abs(d - seconds_since_ref); + b = a / 3600; + *hour = (int)b; + h = *hour; + h = h * 3600; + b = a - h; + b = b / 60; + *minute = (int)b; + m = *minute; + m = m * 60; + c = a - h - m; + *second = (int)c; +} + +- (int)dayOfCommonEra +{ + double a; + int r; + + // Get reference date in terms of days + a = seconds_since_ref / 86400.0; + // Offset by Gregorian reference + a += GREGORIAN_REFERENCE; + r = (int)a; + + return r; +} + +- (int)dayOfMonth +{ + int m, d, y; + + [self gregorianDateFromAbsolute: [self dayOfCommonEra] + day: &d month: &m year: &y]; + + return d; +} + +- (int)dayOfWeek +{ + return 0; +} + +- (int)dayOfYear +{ + int m, d, y, days, i; + + [self gregorianDateFromAbsolute: [self dayOfCommonEra] + day: &d month: &m year: &y]; + days = d; + for (i = m - 1; i > 0; i--) // days in prior months this year + days = days + [self lastDayOfGregorianMonth: i year: y]; + + return days; +} + +- (int)hourOfDay +{ + int h; + double a, d = [self dayOfCommonEra]; + d -= GREGORIAN_REFERENCE; + d *= 86400; + a = abs(d - seconds_since_ref); + a = a / 3600; + h = (int)a; + + // There is a small chance of getting + // it right at the stroke of midnight + if (h == 24) + h = 0; + + return h; +} + +- (int)minuteOfHour +{ + int h, m; + double a, b, d = [self dayOfCommonEra]; + d -= GREGORIAN_REFERENCE; + d *= 86400; + a = abs(d - seconds_since_ref); + b = a / 3600; + h = (int)b; + h = h * 3600; + b = a - h; + b = b / 60; + m = (int)b; + + return m; +} + +- (int)monthOfYear +{ + int m, d, y; + + [self gregorianDateFromAbsolute: [self dayOfCommonEra] + day: &d month: &m year: &y]; + + return m; +} + +- (int)secondOfMinute +{ + int h, m, s; + double a, b, c, d = [self dayOfCommonEra]; + d -= GREGORIAN_REFERENCE; + d *= 86400; + a = abs(d - seconds_since_ref); + b = a / 3600; + h = (int)b; + h = h * 3600; + b = a - h; + b = b / 60; + m = (int)b; + m = m * 60; + c = a - h - m; + s = (int)c; + + return s; +} + +- (int)yearOfCommonEra +{ + int m, d, y; + int a; + + // Get reference date in terms of days + a = seconds_since_ref / 86400; + // Offset by Gregorian reference + a += GREGORIAN_REFERENCE; + [self gregorianDateFromAbsolute: a day: &d month: &m year: &y]; + + return y; +} + +// Providing Adjusted Dates +- (NSCalendarDate *)addYear:(int)year + month:(unsigned int)month + day:(unsigned int)day + hour:(unsigned int)hour + minute:(unsigned int)minute + second:(unsigned int)second +{ + return self; +} + +// Getting String Descriptions of Dates +- (NSString *)description +{ + return [self descriptionWithCalendarFormat: calendar_format + locale: nil]; +} + +- (NSString *)descriptionWithCalendarFormat:(NSString *)format +{ + return [self descriptionWithCalendarFormat: format + locale: nil]; +} + +#define UNIX_REFERENCE_INTERVAL -978307200.0 +- (NSString *)descriptionWithCalendarFormat:(NSString *)format + locale:(NSDictionary *)locale +{ + char buf[1024]; + const char *f = [format cString]; + int lf = strlen(f); + BOOL mtag = NO, dtag = NO, ycent = NO; + char ms[80] = "", ds[80] = ""; + int yd = 0, md = 0, dd = 0, hd = 0, mnd = 0, sd = 0; + int i, j, k; + + // If the format is nil then return an empty string + if (!format) + return @""; + + [self getYear: &yd month: &md day: &dd hour: &hd minute: &mnd second: &sd]; + + // The strftime specifiers + // %a abbreviated weekday name according to locale + // %A full weekday name according to locale + // %b abbreviated month name according to locale + // %B full month name according to locale + // %d day of month as decimal number + // %H hour as a decimal number using 24-hour clock + // %I hour as a decimal number using 12-hour clock + // %j day of year as a decimal number + // %m month as decimal number + // %M minute as decimal number + // %p 'am' or 'pm' + // %S second as decimal number + // %U week of the current year as decimal number (Sunday first day) + // %W week of the current year as decimal number (Monday first day) + // %w day of the week as decimal number (Sunday = 0) + // %y year as a decimal number without century + // %Y year as a decimal number with century + // %Z time zone + // %% literal % character + + // Find the order of date elements + // and translate format string into printf ready string + j = 0; + for (i = 0;i < lf; ++i) + { + // Only care about a format specifier + if (f[i] == '%') + { + // check the character that comes after + switch (f[i+1]) + { + // literal % + case '%': + ++i; + buf[j] = f[i]; + ++j; + break; + + // is it the year + case 'Y': + ycent = YES; + case 'y': + ++i; + if (ycent) + k = sprintf(&(buf[j]), "%04d", yd); + else + k = sprintf(&(buf[j]), "%02d", (yd - 1900)); + j += k; + break; + + // is it the month + case 'b': + case 'B': + mtag = YES; // Month is character string + case 'm': + ++i; + if (mtag) + // +++ Translate to locale character string + k = sprintf(&(buf[j]), ""); + else + k = sprintf(&(buf[j]), "%02d", md); + j += k; + break; + + // is it the day + case 'a': + case 'A': + dtag = YES; // Day is character string + case 'd': + case 'j': + case 'w': + ++i; + if (dtag) + // +++ Translate to locale character string + k = sprintf(&(buf[j]), ""); + else + k = sprintf(&(buf[j]), "%02d", dd); + j += k; + break; + + // is it the hour + case 'H': + case 'I': + ++i; + k = sprintf(&(buf[j]), "%02d", hd); + j += k; + break; + + // is it the minute + case 'M': + ++i; + k = sprintf(&(buf[j]), "%02d", mnd); + j += k; + break; + + // is it the second + case 'S': + ++i; + k = sprintf(&(buf[j]), "%02d", sd); + j += k; + break; + + // Anything else is unknown so just copy + default: + buf[j] = f[i]; + ++i; + ++j; + buf[j] = f[i]; + ++i; + ++j; + break; + } + } + else + { + buf[j] = f[i]; + ++j; + } + } + buf[j] = '\0'; + + return [NSString stringWithCString: buf]; +} + +- (NSString *)descriptionWithLocale:(NSDictionary *)locale +{ + return [self descriptionWithCalendarFormat: calendar_format + locale: locale]; +} + +// Getting and Setting Calendar Formats +- (NSString *)calendarFormat +{ + return calendar_format; +} + +- (void)setCalendarFormat:(NSString *)format +{ + calendar_format = format; +} + +// Getting and Setting Time Zones +- (void)setTimeZone:(NSTimeZone *)aTimeZone +{ + time_zone = (NSTimeZoneDetail *)aTimeZone; +} + +- (NSTimeZoneDetail *)timeZoneDetail +{ + return time_zone; +} + +@end + +// +// Routines for manipulating Gregorian dates +// +// The following code is based upon the source code in +// ``Calendrical Calculations'' by Nachum Dershowitz and Edward M. Reingold, +// Software---Practice & Experience, vol. 20, no. 9 (September, 1990), +// pp. 899--928. +// + +@implementation NSCalendarDate (GregorianDate) + +- (int)lastDayOfGregorianMonth:(int)month year:(int)year +{ + switch (month) { + case 2: + if ((((year % 4) == 0) && ((year % 100) != 0)) + || ((year % 400) == 0)) + return 29; + else + return 28; + case 4: + case 6: + case 9: + case 11: return 30; + default: return 31; + } +} + +- (int)absoluteGregorianDay:(int)day month:(int)month year:(int)year +{ + int m, N; + + N = day; // day of month + for (m = month - 1; m > 0; m--) // days in prior months this year + N = N + [self lastDayOfGregorianMonth: m year: year]; + return + (N // days this year + + 365 * (year - 1) // days in previous years ignoring leap days + + (year - 1)/4 // Julian leap days before this year... + - (year - 1)/100 // ...minus prior century years... + + (year - 1)/400); // ...plus prior years divisible by 400 +} + +- (void)gregorianDateFromAbsolute:(int)d + day:(int *)day + month:(int *)month + year:(int *)year +{ + // Search forward year by year from approximate year + *year = d/366; + while (d >= [self absoluteGregorianDay: 1 month: 1 year: (*year)+1]) + (*year)++; + // Search forward month by month from January + (*month) = 1; + while (d > [self absoluteGregorianDay: + [self lastDayOfGregorianMonth: *month year: *year] + month: *month year: *year]) + (*month)++; + *day = d - [self absoluteGregorianDay: 1 month: *month year: *year] + 1; +} + +@end diff --git a/Testing/nsdate.m b/Testing/nsdate.m new file mode 100644 index 000000000..6f3292107 --- /dev/null +++ b/Testing/nsdate.m @@ -0,0 +1,126 @@ +#include +#include +#include + +#ifdef __MS_WIN32__ +int _MB_init_runtime() +{ + libobjc_init_runtime(); + gnustep_base_init_runtime(); + nsdate_init_runtime(); + return 0; +} +#endif + +int +main() +{ + id a, b, c, e; /* dates */ + id pool; + + behavior_set_debug(0); + + pool = [[NSAutoreleasePool alloc] init]; + + // NSDate tests + printf("NSDate tests\n"); + { + // Create NSDate instances + a = [NSDate date]; + printf("+[date] -- %s\n", [[a description] cString]); + b = [NSDate dateWithTimeIntervalSinceNow: 0]; + printf("+[dateWithTimeIntervalSinceNow: 0] -- %s\n", + [[b description] cString]); + b = [NSDate dateWithTimeIntervalSinceNow: 600]; + printf("+[dateWithTimeIntervalSinceNow: 600] -- %s\n", + [[b description] cString]); + b = [NSDate dateWithTimeIntervalSince1970: 0]; + printf("+[dateWithTimeIntervalSince1970: 0] -- %s\n", + [[b description] cString]); + b = [NSDate dateWithTimeIntervalSince1970: -600]; + printf("+[dateWithTimeIntervalSince1970: -600] -- %s\n", + [[b description] cString]); + b = [NSDate dateWithTimeIntervalSinceReferenceDate: 0]; + printf("+[dateWithTimeIntervalSinceReferenceDate: 0] -- %s\n", + [[b description] cString]); + b = [NSDate dateWithTimeIntervalSinceReferenceDate: 300]; + printf("+[dateWithTimeIntervalSinceReferenceDate: 300] -- %s\n", + [[b description] cString]); + + // Comparisons + + if ([a compare: [NSDate distantFuture]] == NSOrderedAscending) + printf("Current date is before distantFuture\n"); + else + printf("ERROR: Current date is *not* before distantFuture\n"); + + if ([a compare: [NSDate distantPast]] == NSOrderedDescending) + printf("Current date is after distantPast\n"); + else + printf("ERROR: Current date is *not* after distantPast\n"); + + c = [a earlierDate: b]; + if (c == a) + printf("%s is earlier than %s\n", [[a description] cString], + [[b description] cString]); + else + printf("ERROR: %s is not earlier than %s\n", [[a description] cString], + [[b description] cString]); + + c = [a laterDate: b]; + if (c == b) + printf("%s is later than %s\n", [[b description] cString], + [[a description] cString]); + else + printf("ERROR: %s is not later than %s\n", [[b description] cString], + [[a description] cString]); + } + + // NSCalendarDate tests + printf("NSCalendarDate tests\n"); + { + int m, y, d, a; + + // Create an NSCalendarDate with current date and time + c = [NSCalendarDate calendarDate]; + printf("+[calendarDate] -- %s\n", [[c description] cString]); + printf("-[dayOfMonth] %d\n", [c dayOfMonth]); + printf("-[monthOfYear] %d\n", [c monthOfYear]); + printf("-[yearOfCommonEra] %d\n", [c yearOfCommonEra]); + + a = [c absoluteGregorianDay: 9 month: 10 year: 1996]; + printf("%d-%d-%d is Gregorian absolute %d\n", 9, 10, 1996, a); + printf("-[dayOfCommonEra] %d\n", [c dayOfCommonEra]); + printf("-[timeIntervalSinceReferenceDate] %f\n", + [c timeIntervalSinceReferenceDate]); + + a = [c absoluteGregorianDay: 1 month: 1 year: 2001]; + printf("%d-%d-%d is Gregorian absolute %d\n", 1, 1, 2001, a); + [c gregorianDateFromAbsolute: a day: &d month: &m year: &y]; + printf("Gregorian absolute %d is %d-%d-%d\n", a, d, m, y); + + c = [NSCalendarDate dateWithString: @"1996-10-09 0:00:01" + calendarFormat: @"%Y-%m-%d %H:%M:%S"]; + printf("calendar date %s\n", [[c description] cString]); + printf("-[dayOfCommonEra] %d\n", [c dayOfCommonEra]); + printf("-[dayOfMonth] %d\n", [c dayOfMonth]); + printf("-[dayOfWeek] %d\n", [c dayOfWeek]); + printf("-[dayOfYear] %d\n", [c dayOfYear]); + printf("-[hourOfDay] %d\n", [c hourOfDay]); + printf("-[minuteOfHour] %d\n", [c minuteOfHour]); + printf("-[monthOfYear] %d\n", [c monthOfYear]); + printf("-[secondOfMinute] %d\n", [c secondOfMinute]); + printf("-[yearOfCommonEra] %d\n", [c yearOfCommonEra]); + printf("-[timeIntervalSinceReferenceDate] %f\n", + [c timeIntervalSinceReferenceDate]); + e = [NSCalendarDate dateWithString: @"1996-10-09 0:00:0" + calendarFormat: @"%Y-%m-%d %H:%M:%S"]; + printf("calendar date %s\n", [[e description] cString]); + printf("-[timeIntervalSinceReferenceDate] %f\n", + [e timeIntervalSinceReferenceDate]); + } + + [pool release]; + + exit(0); +}