]> err.no Git - sope/blob - sope-core/NGExtensions/FdExt.subproj/NSCalendarDate+misc.m
fixed bugs in logger, improved performance
[sope] / sope-core / NGExtensions / FdExt.subproj / NSCalendarDate+misc.m
1 /*
2   Copyright (C) 2000-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
6   OGo 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
9   later version.
10
11   OGo 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.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with OGo; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "NSCalendarDate+misc.h"
23 #include "common.h"
24
25 #define NUMBER_OF_SECONDS_IN_DAY (24 * 60 * 60)
26
27 @implementation NSCalendarDate(misc)
28
29 + (NSCalendarDate *)mondayOfWeek:(int)_weekNumber inYear:(int)_year
30   timeZone:(NSTimeZone *)_tz
31 {
32   NSCalendarDate *janFirst;
33   
34   janFirst  = [NSCalendarDate dateWithYear:_year month:1 day:1 
35                             hour:0 minute:0 second:0
36                             timeZone:_tz];
37   return [janFirst mondayOfWeek:_weekNumber];
38 }
39
40 - (NSCalendarDate *)mondayOfWeek:(int)_weekNumber {
41   NSCalendarDate *mondayOfWeek;
42   short          lastWeek;
43   
44   mondayOfWeek = [self firstMondayAndLastWeekInYear:&lastWeek];
45   
46   if (_weekNumber == 1)
47     return mondayOfWeek;
48   
49   return [mondayOfWeek dateByAddingYears:0
50                        months:0
51                        days:(7 * (_weekNumber - 1))];
52 }
53
54 + (NSArray *)mondaysOfYear:(int)_year timeZone:(NSTimeZone *)_tz {
55   NSCalendarDate *janFirst;
56   
57   janFirst  = [NSCalendarDate dateWithYear:_year month:1 day:1 
58                             hour:0 minute:0 second:0
59                             timeZone:_tz];
60   return [janFirst mondaysOfYear];
61 }
62
63 - (NSArray *)mondaysOfYear {
64   NSArray        *array;
65   NSMutableArray *mondays;
66   NSCalendarDate *mondayOfWeek;
67   short          lastWeek;
68   int            i;
69   
70   mondayOfWeek = [self firstMondayAndLastWeekInYear:&lastWeek];
71   mondays = [[NSMutableArray alloc] initWithCapacity:55];
72   
73   for (i = 0; i < lastWeek; i++) {
74 #if 0 // hh: can someone explain this ?!
75     if (i > 0) {
76       mondayOfWeek =
77         [mondayOfWeek addYear:0 month:0 day:7 hour:0 minute:0 second:0];
78     }
79     mondayOfWeek = [[mondayOfWeek copy] autorelease];
80     [mondays addObject:mondayOfWeek];
81 #else
82     NSCalendarDate *tmp;
83     tmp = [mondayOfWeek dateByAddingYears:0 months:0 days:(i * 7)];
84     [mondays addObject:tmp];
85 #endif
86   }
87   
88   array = [mondays copy];
89   [mondays release];
90   return [array autorelease];
91 }
92
93 - (NSCalendarDate *)firstMondayAndLastWeekInYear:(short *)_lastWeek {
94   NSTimeZone     *tz;
95   int            currentYear;
96   short          lastWeek;
97   NSCalendarDate *janFirst;
98   NSCalendarDate *silvester;
99   NSCalendarDate *mondayOfWeek;
100   
101   tz          = [self timeZone];
102   currentYear = [self yearOfCommonEra];
103   
104   if ([self weekOfYear] == 53) {
105     NSCalendarDate *nextJanFirst = nil;
106
107     nextJanFirst = [NSCalendarDate dateWithYear:(currentYear + 1)
108                                  month:1 day:1
109                                  hour:0 minute:0 second:0
110                                  timeZone:tz];
111
112     if ([nextJanFirst weekOfYear] == 1)
113       currentYear++;
114   }
115   
116   janFirst  = [NSCalendarDate dateWithYear:currentYear
117                             month:1 day:1
118                             hour:0 minute:0 second:0
119                             timeZone:tz];
120   silvester = [NSCalendarDate dateWithYear:currentYear
121                             month:12 day:31
122                             hour:23 minute:59 second:59
123                             timeZone:tz];
124
125   lastWeek = [silvester weekOfYear];
126
127   if (lastWeek == 53) {
128     NSCalendarDate *nextJanFirst = nil;
129     
130     nextJanFirst = [NSCalendarDate dateWithYear:currentYear+1
131                                  month:1 day:1
132                                  hour:0 minute:0 second:0
133                                  timeZone:tz];
134     
135     if ([nextJanFirst weekOfYear] == 1)
136       lastWeek = 52;
137   }
138   
139   if ([janFirst weekOfYear] != 1) {
140     mondayOfWeek = [janFirst mondayOfWeek];
141 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
142     mondayOfWeek =
143       [mondayOfWeek dateByAddingYears:0 months:0 days:7 
144                     hours:0 minutes:0 seconds:0];
145 #else
146     mondayOfWeek =
147       [mondayOfWeek addYear:0 month:0 day:7 hour:0 minute:0 second:0];
148 #endif
149   }
150   else {
151     mondayOfWeek = [janFirst mondayOfWeek];
152   }
153
154   if (_lastWeek) *_lastWeek = lastWeek;
155   return mondayOfWeek;
156 }
157
158 - (NSCalendarDate *)firstDayOfMonth {
159   NSTimeInterval tv;
160   NSCalendarDate *first;
161   int            dayOfMonth;
162   NSTimeZone     *tz;
163   
164   dayOfMonth = [self dayOfMonth];
165   tz = [self timeZone];
166   
167   tv = (1 - dayOfMonth) * NUMBER_OF_SECONDS_IN_DAY;
168   tv = [self timeIntervalSince1970] + tv;
169   first = [NSCalendarDate dateWithTimeIntervalSince1970:tv];
170   [first setTimeZone:tz];
171   return first;
172 }
173
174 - (NSCalendarDate *)lastDayOfMonth {
175   int offset = [self numberOfDaysInMonth] - [self dayOfMonth];
176   return [self dateByAddingYears:0 months:0 days:offset];
177 }
178
179 - (int)numberOfDaysInMonth {
180   static int leapYearMonths[12] = {31,29,31,30,31,30,31,31,30,31,30,31};
181   static int nonLeapYearMonths[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
182   int *numberOfDaysInMonth = [self isInLeapYear] ? leapYearMonths :
183                                                    nonLeapYearMonths;
184   return numberOfDaysInMonth[[self monthOfYear] - 1];
185 }
186
187 - (BOOL)isInLeapYear
188 {
189   unsigned year = [self yearOfCommonEra];
190   return (((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0);
191 }
192
193 - (int)weekOfMonth {
194   /* returns 1-6 */
195   int dayOfMonth;
196   int weekOfYear;
197   int firstWeekOfYear;
198   
199   dayOfMonth = [self dayOfMonth]; /* 1-31 */
200   if (dayOfMonth == 1)
201     return 1;
202
203   /* could be done smarter (by calculating on dayOfWeek) */
204   weekOfYear      = [self weekOfYear];
205   firstWeekOfYear = [[self firstDayOfMonth] weekOfYear];
206   
207   return (weekOfYear - firstWeekOfYear + 1);
208 }
209
210 - (int)weekOfYear {
211   static int whereToStart[] = { 6, 7, 8, 9, 10, 4, 5 };
212   NSCalendarDate *janFirst;
213   int            year, day, weekOfYear;
214   NSTimeZone     *tz;
215   
216   year = [self yearOfCommonEra];
217   day  = [self dayOfYear] - 1; 
218   
219   tz = [self timeZone];
220
221   janFirst  = [NSCalendarDate dateWithYear:year month:1 day:1 
222                             hour:0 minute:0 second:0
223                             timeZone:tz];
224   
225   weekOfYear = (day + whereToStart[[janFirst dayOfWeek]]) / 7;
226
227   if (weekOfYear == 0) {
228     NSCalendarDate *silvesterLastYear;
229
230     silvesterLastYear = [NSCalendarDate dateWithYear:(year - 1)
231                                       month:12 day:31 
232                                       hour:0 minute:0 second:0
233                                       timeZone:tz];
234     return [silvesterLastYear weekOfYear];
235   }
236   
237 #if 0  
238   if (weekOfYear == 53) {
239     NSCalendarDate *nextJanFirst = nil;
240     int            janYear       = year + 1;
241     int            janDay;
242     int            week;
243
244     nextJanFirst = [NSCalendarDate dateWithYear:janYear
245                                  month:1 day:1
246                                  hour:0 minute:0 second:0
247                                  timeZone:[self timeZone]];
248
249     janDay = [nextJanFirst dayOfYear];
250     week   = (janDay + whereToStart[[nextJanFirst dayOfWeek]]) / 7;
251
252     if (week == 1)
253       return 52;
254   }
255 #endif  
256   return weekOfYear;
257 }
258
259 - (short)numberOfWeeksInYear {
260   NSCalendarDate *silvester;
261   NSCalendarDate *dayOfLastWeek;
262   NSTimeZone     *tz;
263   short currentYear;
264
265   currentYear = [self yearOfCommonEra];
266   tz          = [self timeZone];
267
268   silvester = [NSCalendarDate dateWithYear:currentYear
269                               month:12 day:31
270                               hour:23 minute:59 second:59
271                               timeZone:tz];
272   dayOfLastWeek = [silvester dateByAddingYears:0 months:0 days:-3 hours:0
273                              minutes:0 seconds:0];
274
275   return [dayOfLastWeek weekOfYear];
276 }
277
278 - (NSCalendarDate *)mondayOfWeek {
279   NSTimeInterval tv;
280   NSCalendarDate *monday;
281   int            dayOfWeek;
282   NSTimeZone     *tz;
283   
284   dayOfWeek = [self dayOfWeek];
285   tz = [self timeZone];
286   
287   if (dayOfWeek == 0) dayOfWeek = 7; // readjust Sunday
288   
289   tv = (1 - dayOfWeek) * NUMBER_OF_SECONDS_IN_DAY;
290   tv = [self timeIntervalSince1970] + tv;
291   monday = [NSCalendarDate dateWithTimeIntervalSince1970:tv];
292   [monday setTimeZone:tz];
293   return monday;
294 }
295
296 - (NSCalendarDate *)beginOfDay {
297   NSTimeZone     *tz;
298
299   tz = [self timeZone];
300   
301   return [NSCalendarDate dateWithYear:[self yearOfCommonEra]
302                          month:       [self monthOfYear]
303                          day:         [self dayOfMonth]
304                          hour:0 minute:0 second:0
305                          timeZone:    tz];
306 }
307
308 - (NSCalendarDate *)endOfDay {
309   NSTimeZone     *tz;
310
311   tz = [self timeZone];
312   
313   return [NSCalendarDate dateWithYear:[self yearOfCommonEra]
314                          month:       [self monthOfYear]
315                          day:         [self dayOfMonth]
316                          hour:23 minute:59 second:59
317                          timeZone:    tz];
318 }
319
320 - (BOOL)isDateOnSameDay:(NSCalendarDate *)_date {
321   if ([self dayOfYear]       != [_date dayOfYear])       return NO;
322   if ([self yearOfCommonEra] != [_date yearOfCommonEra]) return NO;
323   return YES;
324 }
325 - (BOOL)isDateInSameWeek:(NSCalendarDate *)_date {
326   if ([self weekOfYear]      != [_date weekOfYear])      return NO;
327   if ([self yearOfCommonEra] != [_date yearOfCommonEra]) return NO;
328   return YES;
329 }
330
331 - (BOOL)isToday {
332   NSCalendarDate *d;
333   BOOL result;
334   
335   d = [[NSCalendarDate alloc] init];
336   [d setTimeZone:[self timeZone]];
337   result = [self isDateOnSameDay:d];
338   [d release];
339   return result;
340 }
341
342 - (BOOL)isForenoon {
343   return [self hourOfDay] >= 12 ? NO : YES;
344 }
345 - (BOOL)isAfternoon {
346   return [self hourOfDay] >= 12 ? YES : NO;
347 }
348
349 - (NSCalendarDate *)yesterday {
350   return [self dateByAddingYears:0 months:0 days:-1 
351                hours:0 minutes:0 seconds:0];
352 }
353 - (NSCalendarDate *)tomorrow {
354   return [self dateByAddingYears:0 months:0 days:1 
355                hours:0 minutes:0 seconds:0];
356 }
357
358 - (NSCalendarDate *)lastYear {
359   return [self dateByAddingYears:-1 months:0 days:0 
360                hours:0 minutes:0 seconds:0];
361 }
362 - (NSCalendarDate *)nextYear {
363   return [self dateByAddingYears:1 months:0 days:0 
364                hours:0 minutes:0 seconds:0];
365 }
366
367 - (NSCalendarDate *)hour:(int)_hour minute:(int)_minute second:(int)_second {
368   NSTimeZone *tz;
369
370   tz = [self timeZone];
371   
372   return [NSCalendarDate dateWithYear:[self yearOfCommonEra]
373                          month:       [self monthOfYear]
374                          day:         [self dayOfMonth]
375                          hour:_hour minute:_minute second:_second
376                          timeZone:    tz];
377 }
378 - (NSCalendarDate *)hour:(int)_hour minute:(int)_minute {
379   return [self hour:_hour minute:_minute second:0];
380 }
381
382 /* Y2K support */
383
384 - (NSCalendarDate *)y2kDate {
385   NSCalendarDate *newDate;
386   int year;
387   
388   year = [self yearOfCommonEra];
389   if (year >= 70 && year < 135) {
390     newDate = [[NSCalendarDate
391                       alloc]
392                       initWithYear:(year + 1900)
393                       month:[self monthOfYear]
394                       day:[self dayOfMonth]
395                       hour:[self hourOfDay]
396                       minute:[self minuteOfHour]
397                       second:[self secondOfMinute]
398                       timeZone:[self timeZone]];
399   }
400   else if (year >= 0 && year < 70) {
401     newDate = [[NSCalendarDate
402                       alloc]
403                       initWithYear:(year + 2000)
404                       month:[self monthOfYear]
405                       day:[self dayOfMonth]
406                       hour:[self hourOfDay]
407                       minute:[self minuteOfHour]
408                       second:[self secondOfMinute]
409                       timeZone:[self timeZone]];
410   }
411   else
412     newDate = [self retain];
413     
414   return [newDate autorelease];
415 }
416
417 - (NSCalendarDate *)dateByAddingYears:(int)_years
418   months:(int)_months
419   days:(int)_days
420 {
421 #if 0
422   /* this expects that NSCalendarDate accepts invalid days, like
423      2000-02-31 */
424   int newYear, newMonth, newDay;
425   
426   newYear  = [self yearOfCommonEra] + _years;
427   newMonth = [self monthOfYear]     + _months;
428   newDay   = [self dayOfMonth]      + _days;
429
430   // this doesn't check month overflow !!
431   return [NSCalendarDate dateWithYear:newYear month:newMonth day:newDay
432                          hour:[self hourOfDay]
433                          minute:[self minuteOfHour]
434                          second:[self secondOfMinute]
435                          timeZone:[self timeZone]];
436 #else
437   // but this does it 
438   return [self dateByAddingYears:_years months:_months days:_days
439                hours:0 minutes:0 seconds:0];
440 #endif
441 }
442
443 /* calculate easter ... */
444
445 - (NSCalendarDate *)easterOfYear {
446   /*
447     algorithm taken from:
448       http://www.uni-bamberg.de/~ba1lw1/fkal.html#Algorithmus
449   */
450   int      y;
451   unsigned m, n;
452   int      a, b, c, d, e;
453   unsigned easterMonth, easterDay;
454   
455   y = [self yearOfCommonEra];
456
457   if ((y > 1699) && (y < 1800)) {
458     m = 23;
459     n = 3;
460   }
461   else if ((y > 1799) && (y < 1900)) {
462     m = 23;
463     n = 4;
464   }
465   else if ((y > 1899) && (y < 2100)) {
466     m = 24;
467     n = 5;
468   }
469   else if ((y > 2099) && (y < 2200)) {
470     m = 24;
471     n = 6;
472   }
473   else
474     return nil;
475     
476   a = y % 19;
477   b = y % 4;
478   c = y % 7;
479   d = (19 * a + m) % 30;
480   e = (2 * b + 4 * c + 6 * d + n) % 7;
481   
482   easterMonth = 3;
483   easterDay   = 22 + d + e;
484   if (easterDay > 31) {
485     easterDay  -= 31;
486     easterMonth = 4;
487     if (easterDay == 26)
488       easterDay = 19;
489     if ((easterDay == 25) && (d == 28) && (a > 10))
490       easterDay = 18;
491   }
492   
493   return [NSCalendarDate dateWithYear:y
494                          month:easterMonth
495                          day:easterDay
496                          hour:0 minute:0 second:0
497                          timeZone:[self timeZone]];
498 }
499
500 #if !LIB_FOUNDATION_LIBRARY
501 - (id)valueForUndefinedKey:(NSString *)_key {
502   NSLog(@"WARNING: tried to access undefined KVC key '%@' on date object: %@",
503         _key, self);
504   return nil;
505 }
506 #endif
507
508 @end /* NSCalendarDate(misc) */
509
510 /* static linking */
511
512 void __link_NSCalendarDate_misc(void) {
513   __link_NSCalendarDate_misc();
514 }