2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with SOPE; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include "NSCalendarDate+misc.h"
26 #define NUMBER_OF_SECONDS_IN_DAY (24 * 60 * 60)
28 @implementation NSCalendarDate(misc)
30 + (NSCalendarDate *)mondayOfWeek:(int)_weekNumber inYear:(int)_year
31 timeZone:(NSTimeZone *)_tz
33 NSCalendarDate *janFirst;
35 janFirst = [NSCalendarDate dateWithYear:_year month:1 day:1
36 hour:0 minute:0 second:0
38 return [janFirst mondayOfWeek:_weekNumber];
41 - (NSCalendarDate *)mondayOfWeek:(int)_weekNumber {
42 NSCalendarDate *mondayOfWeek;
45 mondayOfWeek = [self firstMondayAndLastWeekInYear:&lastWeek];
50 return [mondayOfWeek dateByAddingYears:0
52 days:(7 * (_weekNumber - 1))];
55 + (NSArray *)mondaysOfYear:(int)_year timeZone:(NSTimeZone *)_tz {
56 NSCalendarDate *janFirst;
58 janFirst = [NSCalendarDate dateWithYear:_year month:1 day:1
59 hour:0 minute:0 second:0
61 return [janFirst mondaysOfYear];
64 - (NSArray *)mondaysOfYear {
66 NSMutableArray *mondays;
67 NSCalendarDate *mondayOfWeek;
71 mondayOfWeek = [self firstMondayAndLastWeekInYear:&lastWeek];
72 mondays = [[NSMutableArray alloc] initWithCapacity:55];
74 for (i = 0; i < lastWeek; i++) {
75 #if 0 // hh: can someone explain this ?!
78 [mondayOfWeek addYear:0 month:0 day:7 hour:0 minute:0 second:0];
80 mondayOfWeek = [[mondayOfWeek copy] autorelease];
81 [mondays addObject:mondayOfWeek];
84 tmp = [mondayOfWeek dateByAddingYears:0 months:0 days:(i * 7)];
85 [mondays addObject:tmp];
89 array = [mondays copy];
91 return [array autorelease];
94 - (NSCalendarDate *)firstMondayAndLastWeekInYear:(short *)_lastWeek {
98 NSCalendarDate *janFirst;
99 NSCalendarDate *silvester;
100 NSCalendarDate *mondayOfWeek;
102 tz = [self timeZone];
103 currentYear = [self yearOfCommonEra];
105 if ([self weekOfYear] == 53) {
106 NSCalendarDate *nextJanFirst = nil;
108 nextJanFirst = [NSCalendarDate dateWithYear:(currentYear + 1)
110 hour:0 minute:0 second:0
113 if ([nextJanFirst weekOfYear] == 1)
117 janFirst = [NSCalendarDate dateWithYear:currentYear
119 hour:0 minute:0 second:0
121 silvester = [NSCalendarDate dateWithYear:currentYear
123 hour:23 minute:59 second:59
126 lastWeek = [silvester weekOfYear];
128 if (lastWeek == 53) {
129 NSCalendarDate *nextJanFirst = nil;
131 nextJanFirst = [NSCalendarDate dateWithYear:currentYear+1
133 hour:0 minute:0 second:0
136 if ([nextJanFirst weekOfYear] == 1)
140 if ([janFirst weekOfYear] != 1) {
141 mondayOfWeek = [janFirst mondayOfWeek];
142 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
144 [mondayOfWeek dateByAddingYears:0 months:0 days:7
145 hours:0 minutes:0 seconds:0];
148 [mondayOfWeek addYear:0 month:0 day:7 hour:0 minute:0 second:0];
152 mondayOfWeek = [janFirst mondayOfWeek];
155 if (_lastWeek) *_lastWeek = lastWeek;
159 - (NSCalendarDate *)firstDayOfMonth {
161 NSCalendarDate *first;
165 dayOfMonth = [self dayOfMonth];
166 tz = [self timeZone];
168 tv = (1 - dayOfMonth) * NUMBER_OF_SECONDS_IN_DAY;
169 tv = [self timeIntervalSince1970] + tv;
170 first = [NSCalendarDate dateWithTimeIntervalSince1970:tv];
171 [first setTimeZone:tz];
175 - (NSCalendarDate *)lastDayOfMonth {
176 int offset = [self numberOfDaysInMonth] - [self dayOfMonth];
177 return [self dateByAddingYears:0 months:0 days:offset];
180 - (int)numberOfDaysInMonth {
181 static int leapYearMonths[12] = {31,29,31,30,31,30,31,31,30,31,30,31};
182 static int nonLeapYearMonths[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
183 int *numberOfDaysInMonth = [self isInLeapYear] ? leapYearMonths :
185 return numberOfDaysInMonth[[self monthOfYear] - 1];
190 unsigned year = [self yearOfCommonEra];
191 return (((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0);
200 dayOfMonth = [self dayOfMonth]; /* 1-31 */
204 /* could be done smarter (by calculating on dayOfWeek) */
205 weekOfYear = [self weekOfYear];
206 firstWeekOfYear = [[self firstDayOfMonth] weekOfYear];
208 return (weekOfYear - firstWeekOfYear + 1);
212 static int whereToStart[] = { 6, 7, 8, 9, 10, 4, 5 };
213 NSCalendarDate *janFirst;
214 int year, day, weekOfYear;
217 year = [self yearOfCommonEra];
218 day = [self dayOfYear] - 1;
220 tz = [self timeZone];
222 janFirst = [NSCalendarDate dateWithYear:year month:1 day:1
223 hour:0 minute:0 second:0
226 weekOfYear = (day + whereToStart[[janFirst dayOfWeek]]) / 7;
228 if (weekOfYear == 0) {
229 NSCalendarDate *silvesterLastYear;
231 silvesterLastYear = [NSCalendarDate dateWithYear:(year - 1)
233 hour:0 minute:0 second:0
235 return [silvesterLastYear weekOfYear];
239 if (weekOfYear == 53) {
240 NSCalendarDate *nextJanFirst = nil;
241 int janYear = year + 1;
245 nextJanFirst = [NSCalendarDate dateWithYear:janYear
247 hour:0 minute:0 second:0
248 timeZone:[self timeZone]];
250 janDay = [nextJanFirst dayOfYear];
251 week = (janDay + whereToStart[[nextJanFirst dayOfWeek]]) / 7;
260 - (short)numberOfWeeksInYear {
261 NSCalendarDate *silvester;
262 NSCalendarDate *dayOfLastWeek;
266 currentYear = [self yearOfCommonEra];
267 tz = [self timeZone];
269 silvester = [NSCalendarDate dateWithYear:currentYear
271 hour:23 minute:59 second:59
273 dayOfLastWeek = [silvester dateByAddingYears:0 months:0 days:-3 hours:0
274 minutes:0 seconds:0];
276 return [dayOfLastWeek weekOfYear];
279 - (NSCalendarDate *)mondayOfWeek {
281 NSCalendarDate *monday;
285 dayOfWeek = [self dayOfWeek];
286 tz = [self timeZone];
288 if (dayOfWeek == 0) dayOfWeek = 7; // readjust Sunday
290 tv = (1 - dayOfWeek) * NUMBER_OF_SECONDS_IN_DAY;
291 tv = [self timeIntervalSince1970] + tv;
292 monday = [NSCalendarDate dateWithTimeIntervalSince1970:tv];
293 [monday setTimeZone:tz];
297 - (NSCalendarDate *)beginOfDay {
300 tz = [self timeZone];
302 return [NSCalendarDate dateWithYear:[self yearOfCommonEra]
303 month: [self monthOfYear]
304 day: [self dayOfMonth]
305 hour:0 minute:0 second:0
309 - (NSCalendarDate *)endOfDay {
312 tz = [self timeZone];
314 return [NSCalendarDate dateWithYear:[self yearOfCommonEra]
315 month: [self monthOfYear]
316 day: [self dayOfMonth]
317 hour:23 minute:59 second:59
321 - (BOOL)isDateOnSameDay:(NSCalendarDate *)_date {
322 if ([self dayOfYear] != [_date dayOfYear]) return NO;
323 if ([self yearOfCommonEra] != [_date yearOfCommonEra]) return NO;
326 - (BOOL)isDateInSameWeek:(NSCalendarDate *)_date {
327 if ([self weekOfYear] != [_date weekOfYear]) return NO;
328 if ([self yearOfCommonEra] != [_date yearOfCommonEra]) return NO;
336 d = [[NSCalendarDate alloc] init];
337 [d setTimeZone:[self timeZone]];
338 result = [self isDateOnSameDay:d];
344 return [self hourOfDay] >= 12 ? NO : YES;
346 - (BOOL)isAfternoon {
347 return [self hourOfDay] >= 12 ? YES : NO;
350 - (NSCalendarDate *)yesterday {
351 return [self dateByAddingYears:0 months:0 days:-1
352 hours:0 minutes:0 seconds:0];
354 - (NSCalendarDate *)tomorrow {
355 return [self dateByAddingYears:0 months:0 days:1
356 hours:0 minutes:0 seconds:0];
359 - (NSCalendarDate *)lastYear {
360 return [self dateByAddingYears:-1 months:0 days:0
361 hours:0 minutes:0 seconds:0];
363 - (NSCalendarDate *)nextYear {
364 return [self dateByAddingYears:1 months:0 days:0
365 hours:0 minutes:0 seconds:0];
368 - (NSCalendarDate *)hour:(int)_hour minute:(int)_minute second:(int)_second {
371 tz = [self timeZone];
373 return [NSCalendarDate dateWithYear:[self yearOfCommonEra]
374 month: [self monthOfYear]
375 day: [self dayOfMonth]
376 hour:_hour minute:_minute second:_second
379 - (NSCalendarDate *)hour:(int)_hour minute:(int)_minute {
380 return [self hour:_hour minute:_minute second:0];
385 - (NSCalendarDate *)y2kDate {
386 NSCalendarDate *newDate;
389 year = [self yearOfCommonEra];
390 if (year >= 70 && year < 135) {
391 newDate = [[NSCalendarDate
393 initWithYear:(year + 1900)
394 month:[self monthOfYear]
395 day:[self dayOfMonth]
396 hour:[self hourOfDay]
397 minute:[self minuteOfHour]
398 second:[self secondOfMinute]
399 timeZone:[self timeZone]];
401 else if (year >= 0 && year < 70) {
402 newDate = [[NSCalendarDate
404 initWithYear:(year + 2000)
405 month:[self monthOfYear]
406 day:[self dayOfMonth]
407 hour:[self hourOfDay]
408 minute:[self minuteOfHour]
409 second:[self secondOfMinute]
410 timeZone:[self timeZone]];
413 newDate = [self retain];
415 return [newDate autorelease];
418 - (NSCalendarDate *)dateByAddingYears:(int)_years
423 /* this expects that NSCalendarDate accepts invalid days, like
425 int newYear, newMonth, newDay;
427 newYear = [self yearOfCommonEra] + _years;
428 newMonth = [self monthOfYear] + _months;
429 newDay = [self dayOfMonth] + _days;
431 // this doesn't check month overflow !!
432 return [NSCalendarDate dateWithYear:newYear month:newMonth day:newDay
433 hour:[self hourOfDay]
434 minute:[self minuteOfHour]
435 second:[self secondOfMinute]
436 timeZone:[self timeZone]];
439 return [self dateByAddingYears:_years months:_months days:_days
440 hours:0 minutes:0 seconds:0];
444 /* calculate easter ... */
446 - (NSCalendarDate *)easterOfYear {
448 algorithm taken from:
449 http://www.uni-bamberg.de/~ba1lw1/fkal.html#Algorithmus
454 unsigned easterMonth, easterDay;
456 y = [self yearOfCommonEra];
458 if ((y > 1699) && (y < 1800)) {
462 else if ((y > 1799) && (y < 1900)) {
466 else if ((y > 1899) && (y < 2100)) {
470 else if ((y > 2099) && (y < 2200)) {
480 d = (19 * a + m) % 30;
481 e = (2 * b + 4 * c + 6 * d + n) % 7;
484 easterDay = 22 + d + e;
485 if (easterDay > 31) {
490 if ((easterDay == 25) && (d == 28) && (a > 10))
494 return [NSCalendarDate dateWithYear:y
497 hour:0 minute:0 second:0
498 timeZone:[self timeZone]];
501 #if !LIB_FOUNDATION_LIBRARY
502 - (id)valueForUndefinedKey:(NSString *)_key {
503 NSLog(@"WARNING: tried to access undefined KVC key '%@' on date object: %@",
510 #define IGREG (15+31L*(10+12L*1582))
512 - (long)julianNumber {
514 int ja, jy, jm, year, month, day;
516 year = [self yearOfCommonEra];
517 month = [self monthOfYear];
518 day = [self dayOfMonth];
533 jul = (long) (floor(365.25 * jy) + floor(30.6001 * jm) + day + 1720995);
535 if (day + 31L * (month + 12L * year) >= IGREG) {
536 ja = (int)(0.01 * jy);
537 jul += 2 - ja + (int) (0.25 * ja);
542 + (NSCalendarDate *)dateForJulianNumber:(long)_jn {
543 long ja, jalpha, jb, jc, jd, je;
544 unsigned day, month, year;
547 jalpha = (long)(((float) (_jn - 1867216) - 0.25) / 36524.25);
548 ja = _jn + 1 + jalpha - (long)(0.25 * jalpha);
554 jc = (long)(6680.0 + ((float)(jb - 2439870) - 122.1) / 365.25);
555 jd = (long)(365 * jc + (0.25 * jc));
556 je = (long)((jb - jd) / 30.6001);
557 day = jb - jd - (long)(30.6001 * je);
566 return [NSCalendarDate dateWithYear:year month:month day:day
567 hour:12 minute:0 second:0
571 @end /* NSCalendarDate(misc) */
574 @implementation NSString(FuzzyDayOfWeek)
576 - (int)dayOfWeekInEnglishOrGerman {
581 if ((len = [self length]) == 0)
584 if (isdigit([self characterAtIndex:0]))
585 return [self intValue];
587 if (len < 2) /* need at least two chars */
590 s = [self lowercaseString];
591 c1 = [s characterAtIndex:1];
592 switch ([s characterAtIndex:0]) {
593 case 'm': // Monday, Montag, Mittwoch
594 return (c1 == 'i') ? 3 /* Wednesday */ : 1 /* Monday */;
596 case 't': // Tuesday, Thursday
597 return (c1 == 'u') ? 2 /* Tuesday */ : 4 /* Thursday */;
599 case 'f': // Friday, Freitag
600 return 5 /* Friday */;
602 case 's': // Saturday, Sunday, Samstag, Sonntag, Sonnabend
604 return 6; /* Saturday */
606 if (c1 == 'o' && [s hasPrefix:@"sonna"])
607 return 6; /* Sonnabend */
609 return 0 /* Sunday */;
612 return 3 /* Wednesday */;
618 @end /* NSString(FuzzyDayOfWeek) */
623 void __link_NSCalendarDate_misc(void) {
624 __link_NSCalendarDate_misc();