]> err.no Git - sope/blob - sope-core/NGExtensions/FdExt.subproj/NSCalendarDate+misc.m
fixed gstep-base compile warnings
[sope] / sope-core / NGExtensions / FdExt.subproj / NSCalendarDate+misc.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 #include "NSCalendarDate+misc.h"
23 #include "common.h"
24 #include <math.h>
25
26 #define NUMBER_OF_SECONDS_IN_DAY (24 * 60 * 60)
27
28 @implementation NSCalendarDate(misc)
29
30 + (NSCalendarDate *)mondayOfWeek:(int)_weekNumber inYear:(int)_year
31   timeZone:(NSTimeZone *)_tz
32 {
33   NSCalendarDate *janFirst;
34   
35   janFirst  = [NSCalendarDate dateWithYear:_year month:1 day:1 
36                             hour:0 minute:0 second:0
37                             timeZone:_tz];
38   return [janFirst mondayOfWeek:_weekNumber];
39 }
40
41 - (NSCalendarDate *)mondayOfWeek:(int)_weekNumber {
42   NSCalendarDate *mondayOfWeek;
43   short          lastWeek;
44   
45   mondayOfWeek = [self firstMondayAndLastWeekInYear:&lastWeek];
46   
47   if (_weekNumber == 1)
48     return mondayOfWeek;
49   
50   return [mondayOfWeek dateByAddingYears:0
51                        months:0
52                        days:(7 * (_weekNumber - 1))];
53 }
54
55 + (NSArray *)mondaysOfYear:(int)_year timeZone:(NSTimeZone *)_tz {
56   NSCalendarDate *janFirst;
57   
58   janFirst  = [NSCalendarDate dateWithYear:_year month:1 day:1 
59                             hour:0 minute:0 second:0
60                             timeZone:_tz];
61   return [janFirst mondaysOfYear];
62 }
63
64 - (NSArray *)mondaysOfYear {
65   NSArray        *array;
66   NSMutableArray *mondays;
67   NSCalendarDate *mondayOfWeek;
68   short          lastWeek;
69   int            i;
70   
71   mondayOfWeek = [self firstMondayAndLastWeekInYear:&lastWeek];
72   mondays = [[NSMutableArray alloc] initWithCapacity:55];
73   
74   for (i = 0; i < lastWeek; i++) {
75 #if 0 // hh: can someone explain this ?!
76     if (i > 0) {
77       mondayOfWeek =
78         [mondayOfWeek addYear:0 month:0 day:7 hour:0 minute:0 second:0];
79     }
80     mondayOfWeek = [[mondayOfWeek copy] autorelease];
81     [mondays addObject:mondayOfWeek];
82 #else
83     NSCalendarDate *tmp;
84     tmp = [mondayOfWeek dateByAddingYears:0 months:0 days:(i * 7)];
85     [mondays addObject:tmp];
86 #endif
87   }
88   
89   array = [mondays copy];
90   [mondays release];
91   return [array autorelease];
92 }
93
94 - (NSCalendarDate *)firstMondayAndLastWeekInYear:(short *)_lastWeek {
95   NSTimeZone     *tz;
96   int            currentYear;
97   short          lastWeek;
98   NSCalendarDate *janFirst;
99   NSCalendarDate *silvester;
100   NSCalendarDate *mondayOfWeek;
101   
102   tz          = [self timeZone];
103   currentYear = [self yearOfCommonEra];
104   
105   if ([self weekOfYear] == 53) {
106     NSCalendarDate *nextJanFirst = nil;
107
108     nextJanFirst = [NSCalendarDate dateWithYear:(currentYear + 1)
109                                  month:1 day:1
110                                  hour:0 minute:0 second:0
111                                  timeZone:tz];
112
113     if ([nextJanFirst weekOfYear] == 1)
114       currentYear++;
115   }
116   
117   janFirst  = [NSCalendarDate dateWithYear:currentYear
118                             month:1 day:1
119                             hour:0 minute:0 second:0
120                             timeZone:tz];
121   silvester = [NSCalendarDate dateWithYear:currentYear
122                             month:12 day:31
123                             hour:23 minute:59 second:59
124                             timeZone:tz];
125
126   lastWeek = [silvester weekOfYear];
127
128   if (lastWeek == 53) {
129     NSCalendarDate *nextJanFirst = nil;
130     
131     nextJanFirst = [NSCalendarDate dateWithYear:currentYear+1
132                                  month:1 day:1
133                                  hour:0 minute:0 second:0
134                                  timeZone:tz];
135     
136     if ([nextJanFirst weekOfYear] == 1)
137       lastWeek = 52;
138   }
139   
140   if ([janFirst weekOfYear] != 1) {
141     mondayOfWeek = [janFirst mondayOfWeek];
142 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
143     mondayOfWeek =
144       [mondayOfWeek dateByAddingYears:0 months:0 days:7 
145                     hours:0 minutes:0 seconds:0];
146 #else
147     mondayOfWeek =
148       [mondayOfWeek addYear:0 month:0 day:7 hour:0 minute:0 second:0];
149 #endif
150   }
151   else {
152     mondayOfWeek = [janFirst mondayOfWeek];
153   }
154
155   if (_lastWeek) *_lastWeek = lastWeek;
156   return mondayOfWeek;
157 }
158
159 - (NSCalendarDate *)firstDayOfMonth {
160   NSTimeInterval tv;
161   NSCalendarDate *first;
162   int            dayOfMonth;
163   NSTimeZone     *tz;
164   
165   dayOfMonth = [self dayOfMonth];
166   tz = [self timeZone];
167   
168   tv = (1 - dayOfMonth) * NUMBER_OF_SECONDS_IN_DAY;
169   tv = [self timeIntervalSince1970] + tv;
170   first = [NSCalendarDate dateWithTimeIntervalSince1970:tv];
171   [first setTimeZone:tz];
172   return first;
173 }
174
175 - (NSCalendarDate *)lastDayOfMonth {
176   int offset = [self numberOfDaysInMonth] - [self dayOfMonth];
177   return [self dateByAddingYears:0 months:0 days:offset];
178 }
179
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 :
184                                                    nonLeapYearMonths;
185   return numberOfDaysInMonth[[self monthOfYear] - 1];
186 }
187
188 - (BOOL)isInLeapYear
189 {
190   unsigned year = [self yearOfCommonEra];
191   return (((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0);
192 }
193
194 - (int)weekOfMonth {
195   /* returns 1-6 */
196   int dayOfMonth;
197   int weekOfYear;
198   int firstWeekOfYear;
199   
200   dayOfMonth = [self dayOfMonth]; /* 1-31 */
201   if (dayOfMonth == 1)
202     return 1;
203
204   /* could be done smarter (by calculating on dayOfWeek) */
205   weekOfYear      = [self weekOfYear];
206   firstWeekOfYear = [[self firstDayOfMonth] weekOfYear];
207   
208   return (weekOfYear - firstWeekOfYear + 1);
209 }
210
211 - (int)weekOfYear {
212   static int whereToStart[] = { 6, 7, 8, 9, 10, 4, 5 };
213   NSCalendarDate *janFirst;
214   int            year, day, weekOfYear;
215   NSTimeZone     *tz;
216   
217   year = [self yearOfCommonEra];
218   day  = [self dayOfYear] - 1; 
219   
220   tz = [self timeZone];
221
222   janFirst  = [NSCalendarDate dateWithYear:year month:1 day:1 
223                             hour:0 minute:0 second:0
224                             timeZone:tz];
225   
226   weekOfYear = (day + whereToStart[[janFirst dayOfWeek]]) / 7;
227
228   if (weekOfYear == 0) {
229     NSCalendarDate *silvesterLastYear;
230
231     silvesterLastYear = [NSCalendarDate dateWithYear:(year - 1)
232                                       month:12 day:31 
233                                       hour:0 minute:0 second:0
234                                       timeZone:tz];
235     return [silvesterLastYear weekOfYear];
236   }
237   
238 #if 0  
239   if (weekOfYear == 53) {
240     NSCalendarDate *nextJanFirst = nil;
241     int            janYear       = year + 1;
242     int            janDay;
243     int            week;
244
245     nextJanFirst = [NSCalendarDate dateWithYear:janYear
246                                  month:1 day:1
247                                  hour:0 minute:0 second:0
248                                  timeZone:[self timeZone]];
249
250     janDay = [nextJanFirst dayOfYear];
251     week   = (janDay + whereToStart[[nextJanFirst dayOfWeek]]) / 7;
252
253     if (week == 1)
254       return 52;
255   }
256 #endif  
257   return weekOfYear;
258 }
259
260 - (short)numberOfWeeksInYear {
261   NSCalendarDate *silvester;
262   NSCalendarDate *dayOfLastWeek;
263   NSTimeZone     *tz;
264   short currentYear;
265
266   currentYear = [self yearOfCommonEra];
267   tz          = [self timeZone];
268
269   silvester = [NSCalendarDate dateWithYear:currentYear
270                               month:12 day:31
271                               hour:23 minute:59 second:59
272                               timeZone:tz];
273   dayOfLastWeek = [silvester dateByAddingYears:0 months:0 days:-3 hours:0
274                              minutes:0 seconds:0];
275
276   return [dayOfLastWeek weekOfYear];
277 }
278
279 - (NSCalendarDate *)mondayOfWeek {
280   NSTimeInterval tv;
281   NSCalendarDate *monday;
282   int            dayOfWeek;
283   NSTimeZone     *tz;
284   
285   dayOfWeek = [self dayOfWeek];
286   tz = [self timeZone];
287   
288   if (dayOfWeek == 0) dayOfWeek = 7; // readjust Sunday
289   
290   tv = (1 - dayOfWeek) * NUMBER_OF_SECONDS_IN_DAY;
291   tv = [self timeIntervalSince1970] + tv;
292   monday = [NSCalendarDate dateWithTimeIntervalSince1970:tv];
293   [monday setTimeZone:tz];
294   return monday;
295 }
296
297 - (NSCalendarDate *)beginOfDay {
298   NSTimeZone     *tz;
299
300   tz = [self timeZone];
301   
302   return [NSCalendarDate dateWithYear:[self yearOfCommonEra]
303                          month:       [self monthOfYear]
304                          day:         [self dayOfMonth]
305                          hour:0 minute:0 second:0
306                          timeZone:    tz];
307 }
308
309 - (NSCalendarDate *)endOfDay {
310   NSTimeZone     *tz;
311
312   tz = [self timeZone];
313   
314   return [NSCalendarDate dateWithYear:[self yearOfCommonEra]
315                          month:       [self monthOfYear]
316                          day:         [self dayOfMonth]
317                          hour:23 minute:59 second:59
318                          timeZone:    tz];
319 }
320
321 - (BOOL)isDateOnSameDay:(NSCalendarDate *)_date {
322   if ([self dayOfYear]       != [_date dayOfYear])       return NO;
323   if ([self yearOfCommonEra] != [_date yearOfCommonEra]) return NO;
324   return YES;
325 }
326 - (BOOL)isDateInSameWeek:(NSCalendarDate *)_date {
327   if ([self weekOfYear]      != [_date weekOfYear])      return NO;
328   if ([self yearOfCommonEra] != [_date yearOfCommonEra]) return NO;
329   return YES;
330 }
331
332 - (BOOL)isToday {
333   NSCalendarDate *d;
334   BOOL result;
335   
336   d = [[NSCalendarDate alloc] init];
337   [d setTimeZone:[self timeZone]];
338   result = [self isDateOnSameDay:d];
339   [d release];
340   return result;
341 }
342
343 - (BOOL)isForenoon {
344   return [self hourOfDay] >= 12 ? NO : YES;
345 }
346 - (BOOL)isAfternoon {
347   return [self hourOfDay] >= 12 ? YES : NO;
348 }
349
350 - (NSCalendarDate *)yesterday {
351   return [self dateByAddingYears:0 months:0 days:-1 
352                hours:0 minutes:0 seconds:0];
353 }
354 - (NSCalendarDate *)tomorrow {
355   return [self dateByAddingYears:0 months:0 days:1 
356                hours:0 minutes:0 seconds:0];
357 }
358
359 - (NSCalendarDate *)lastYear {
360   return [self dateByAddingYears:-1 months:0 days:0 
361                hours:0 minutes:0 seconds:0];
362 }
363 - (NSCalendarDate *)nextYear {
364   return [self dateByAddingYears:1 months:0 days:0 
365                hours:0 minutes:0 seconds:0];
366 }
367
368 - (NSCalendarDate *)hour:(int)_hour minute:(int)_minute second:(int)_second {
369   NSTimeZone *tz;
370
371   tz = [self timeZone];
372   
373   return [NSCalendarDate dateWithYear:[self yearOfCommonEra]
374                          month:       [self monthOfYear]
375                          day:         [self dayOfMonth]
376                          hour:_hour minute:_minute second:_second
377                          timeZone:    tz];
378 }
379 - (NSCalendarDate *)hour:(int)_hour minute:(int)_minute {
380   return [self hour:_hour minute:_minute second:0];
381 }
382
383 /* Y2K support */
384
385 - (NSCalendarDate *)y2kDate {
386   NSCalendarDate *newDate;
387   int year;
388   
389   year = [self yearOfCommonEra];
390   if (year >= 70 && year < 135) {
391     newDate = [[NSCalendarDate
392                       alloc]
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]];
400   }
401   else if (year >= 0 && year < 70) {
402     newDate = [[NSCalendarDate
403                       alloc]
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]];
411   }
412   else
413     newDate = [self retain];
414     
415   return [newDate autorelease];
416 }
417
418 - (NSCalendarDate *)dateByAddingYears:(int)_years
419   months:(int)_months
420   days:(int)_days
421 {
422 #if 0
423   /* this expects that NSCalendarDate accepts invalid days, like
424      2000-02-31 */
425   int newYear, newMonth, newDay;
426   
427   newYear  = [self yearOfCommonEra] + _years;
428   newMonth = [self monthOfYear]     + _months;
429   newDay   = [self dayOfMonth]      + _days;
430
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]];
437 #else
438   // but this does it 
439   return [self dateByAddingYears:_years months:_months days:_days
440                hours:0 minutes:0 seconds:0];
441 #endif
442 }
443
444 /* calculate easter ... */
445
446 - (NSCalendarDate *)easterOfYear {
447   /*
448     algorithm taken from:
449       http://www.uni-bamberg.de/~ba1lw1/fkal.html#Algorithmus
450   */
451   int      y;
452   unsigned m, n;
453   int      a, b, c, d, e;
454   unsigned easterMonth, easterDay;
455   
456   y = [self yearOfCommonEra];
457
458   if ((y > 1699) && (y < 1800)) {
459     m = 23;
460     n = 3;
461   }
462   else if ((y > 1799) && (y < 1900)) {
463     m = 23;
464     n = 4;
465   }
466   else if ((y > 1899) && (y < 2100)) {
467     m = 24;
468     n = 5;
469   }
470   else if ((y > 2099) && (y < 2200)) {
471     m = 24;
472     n = 6;
473   }
474   else
475     return nil;
476     
477   a = y % 19;
478   b = y % 4;
479   c = y % 7;
480   d = (19 * a + m) % 30;
481   e = (2 * b + 4 * c + 6 * d + n) % 7;
482   
483   easterMonth = 3;
484   easterDay   = 22 + d + e;
485   if (easterDay > 31) {
486     easterDay  -= 31;
487     easterMonth = 4;
488     if (easterDay == 26)
489       easterDay = 19;
490     if ((easterDay == 25) && (d == 28) && (a > 10))
491       easterDay = 18;
492   }
493   
494   return [NSCalendarDate dateWithYear:y
495                          month:easterMonth
496                          day:easterDay
497                          hour:0 minute:0 second:0
498                          timeZone:[self timeZone]];
499 }
500
501 #if !LIB_FOUNDATION_LIBRARY
502 - (id)valueForUndefinedKey:(NSString *)_key {
503   NSLog(@"WARNING: tried to access undefined KVC key '%@' on date object: %@",
504         _key, self);
505   return nil;
506 }
507 #endif
508
509 /* Oct. 15, 1582 */
510 #define IGREG (15+31L*(10+12L*1582))
511
512 - (long)julianNumber {
513   long jul;
514   int  ja, jy, jm, year, month, day;
515
516   year  = [self yearOfCommonEra];
517   month = [self monthOfYear];
518   day   = [self dayOfMonth];
519   jy    = year;
520
521   if (jy == 0)
522     return 0;
523   if (jy < 0)
524     jy++;
525
526   if (month > 2)
527     jm = month + 1;
528   else {
529     jy--;
530     jm = month + 13;
531   }
532   
533   jul = (long) (floor(365.25 * jy) + floor(30.6001 * jm) + day + 1720995);
534   
535   if (day + 31L * (month + 12L * year) >= IGREG) {
536     ja   = (int)(0.01 * jy);
537     jul += 2 - ja + (int) (0.25 * ja);
538   }
539   return jul;
540 }
541
542 + (NSCalendarDate *)dateForJulianNumber:(long)_jn {
543   long     ja, jalpha, jb, jc, jd, je;
544   unsigned day, month, year;
545
546   if (_jn >= IGREG) {
547     jalpha = (long)(((float) (_jn - 1867216) - 0.25) / 36524.25);
548     ja = _jn + 1 + jalpha - (long)(0.25 * jalpha);
549   } else {
550     ja = _jn;
551   }
552
553   jb    = ja + 1524;
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);
558   month = je - 1;
559   if (month > 12)
560     month -= 12;
561   year = jc - 4715;
562   if (month > 2)
563     year--;
564   if (year <= 0)
565     year--;
566   return [NSCalendarDate dateWithYear:year month:month day:day
567                          hour:12 minute:0 second:0
568                          timeZone:nil];
569 }
570
571 @end /* NSCalendarDate(misc) */
572
573
574 @implementation NSString(FuzzyDayOfWeek)
575
576 - (int)dayOfWeekInEnglishOrGerman {
577   NSString *s;
578   unichar  c1;
579   unsigned len;
580   
581   if ((len = [self length]) == 0)
582     return -1;
583   
584   if (isdigit([self characterAtIndex:0]))
585     return [self intValue];
586
587   if (len < 2) /* need at least two chars */
588     return -1;
589   
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 */;
595
596   case 't': // Tuesday, Thursday
597     return (c1 == 'u') ? 2 /* Tuesday */ : 4 /* Thursday */;
598
599   case 'f': // Friday, Freitag
600     return 5 /* Friday */;
601
602   case 's': // Saturday, Sunday, Samstag, Sonntag, Sonnabend
603     if (c1 == 'a')
604       return 6; /* Saturday */
605     
606     if (c1 == 'o' && [s hasPrefix:@"sonna"])
607       return 6; /* Sonnabend */
608     
609     return 0 /* Sunday */;
610     
611   case 'w': // Wed
612     return 3 /* Wednesday */;
613   }
614   
615   return -1;
616 }
617
618 @end /* NSString(FuzzyDayOfWeek) */
619
620
621 /* static linking */
622
623 void __link_NSCalendarDate_misc(void) {
624   __link_NSCalendarDate_misc();
625 }