~ubuntu-branches/ubuntu/karmic/gnustep-base/karmic

« back to all changes in this revision

Viewing changes to Source/NSCalendarDate.m

  • Committer: Bazaar Package Importer
  • Author(s): Eric Heintzmann
  • Date: 2005-04-17 00:14:38 UTC
  • mfrom: (1.2.1 upstream) (2.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050417001438-enf0y07c9tku85z1
Tags: 1.10.3-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/** Implementation for NSCalendarDate for GNUstep
2
 
   Copyright (C) 1996, 1998 Free Software Foundation, Inc.
 
2
   Copyright (C) 1996, 1998, 1999, 2000, 2002 Free Software Foundation, Inc.
3
3
 
4
4
   Author:  Scott Christley <scottc@net-community.com>
5
5
   Date: October 1996
6
6
 
 
7
   Author: Richard Frith-Macdonald <rfm@gnu.org>
 
8
   Date: September 2002
 
9
 
7
10
   This file is part of the GNUstep Base Library.
8
11
 
9
12
   This library is free software; you can redistribute it and/or
21
24
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
22
25
 
23
26
   <title>NSCalendarDate class reference</title>
24
 
   $Date: 2002/03/13 09:58:43 $ $Revision: 1.60 $
 
27
   $Date: 2005/02/22 11:22:43 $ $Revision: 1.102 $
25
28
   */
26
29
 
27
 
#include <config.h>
 
30
#include "config.h"
28
31
#include <math.h>
29
 
#include <Foundation/NSObjCRuntime.h>
30
 
#include <Foundation/NSData.h>
31
 
#include <Foundation/NSDate.h>
32
 
#include <Foundation/NSCalendarDate.h>
33
 
#include <Foundation/NSTimeZone.h>
34
 
#include <Foundation/NSArray.h>
35
 
#include <Foundation/NSString.h>
36
 
#include <Foundation/NSCoder.h>
37
 
#include <Foundation/NSException.h>
38
 
#include <Foundation/NSUserDefaults.h>
39
 
#include <base/behavior.h>
 
32
#include "Foundation/NSObjCRuntime.h"
 
33
#include "Foundation/NSData.h"
 
34
#include "Foundation/NSDate.h"
 
35
#include "Foundation/NSCalendarDate.h"
 
36
#include "Foundation/NSTimeZone.h"
 
37
#include "Foundation/NSArray.h"
 
38
#include "Foundation/NSString.h"
 
39
#include "Foundation/NSCoder.h"
 
40
#include "Foundation/NSException.h"
 
41
#include "Foundation/NSUserDefaults.h"
 
42
#include "Foundation/NSAutoreleasePool.h"
 
43
#include "Foundation/NSDebug.h"
 
44
#include "GNUstepBase/GSObjCRuntime.h"
40
45
#include <stdio.h>
41
46
#include <stdlib.h>
42
47
#include <ctype.h>
54
59
 
55
60
#define GREGORIAN_REFERENCE 730486
56
61
 
 
62
@class  GSTimeZone;
 
63
@class  GSAbsTimeZone;
 
64
 
 
65
static NSString *cformat = @"%Y-%m-%d %H:%M:%S %z";
 
66
 
 
67
static NSTimeZone       *localTZ = nil;
 
68
 
 
69
static Class    absClass;
 
70
static Class    dstClass;
 
71
 
 
72
static SEL              offSEL;
 
73
static int (*offIMP)(id, SEL, id);
 
74
static int (*absOffIMP)(id, SEL, id);
 
75
static int (*dstOffIMP)(id, SEL, id);
 
76
 
 
77
static SEL              abrSEL;
 
78
static NSString* (*abrIMP)(id, SEL, id);
 
79
static NSString* (*absAbrIMP)(id, SEL, id);
 
80
static NSString* (*dstAbrIMP)(id, SEL, id);
 
81
 
 
82
 
 
83
/*
 
84
 * Return the offset from GMT for a date in a timezone ...
 
85
 * Optimize for the local timezone, and less so for the other
 
86
 * base library time zone classes.
 
87
 */
57
88
static inline int
 
89
offset(NSTimeZone *tz, NSDate *d)
 
90
{
 
91
  if (tz == nil)
 
92
    {
 
93
      return 0;
 
94
    }
 
95
  if (tz == localTZ && offIMP != 0)
 
96
    {
 
97
      return (*offIMP)(tz, offSEL, d);
 
98
    }
 
99
  else
 
100
    {
 
101
      Class     c = GSObjCClass(tz);
 
102
 
 
103
      if (c == dstClass && dstOffIMP != 0)
 
104
        {
 
105
          return (*dstOffIMP)(tz, offSEL, d);
 
106
        }
 
107
      if (c == absClass && absOffIMP != 0)
 
108
        {
 
109
          return (*absOffIMP)(tz, offSEL, d);
 
110
        }
 
111
      return [tz secondsFromGMTForDate: d];
 
112
    }
 
113
}
 
114
 
 
115
/*
 
116
 * Return the offset from GMT for a date in a timezone ...
 
117
 * Optimize for the local timezone, and less so for the other
 
118
 * base library time zone classes.
 
119
 */
 
120
static inline NSString*
 
121
abbrev(NSTimeZone *tz, NSDate *d)
 
122
{
 
123
  if (tz == nil)
 
124
    {
 
125
      return @"GMT";
 
126
    }
 
127
  if (tz == localTZ && abrIMP != 0)
 
128
    {
 
129
      return (*abrIMP)(tz, abrSEL, d);
 
130
    }
 
131
  else
 
132
    {
 
133
      Class     c = GSObjCClass(tz);
 
134
 
 
135
      if (c == dstClass && dstAbrIMP != 0)
 
136
        {
 
137
          return (*dstAbrIMP)(tz, abrSEL, d);
 
138
        }
 
139
      if (c == absClass && absAbrIMP != 0)
 
140
        {
 
141
          return (*absAbrIMP)(tz, abrSEL, d);
 
142
        }
 
143
      return [tz abbreviationForDate: d];
 
144
    }
 
145
}
 
146
 
 
147
static inline unsigned int
58
148
lastDayOfGregorianMonth(int month, int year)
59
149
{
60
150
  switch (month)
89
179
     + (year - 1)/400);   // ...plus prior years divisible by 400
90
180
}
91
181
 
92
 
/*
93
 
 * External - so NSDate can use it.
 
182
/* Should be static, but temporarily changed to non-static until
 
183
   WIndows fixes are done.  */
 
184
int
 
185
dayOfCommonEra(NSTimeInterval when)
 
186
{
 
187
  double a;
 
188
  int r;
 
189
 
 
190
  // Get reference date in terms of days
 
191
  a = when / 86400.0;
 
192
  // Offset by Gregorian reference
 
193
  a += GREGORIAN_REFERENCE;
 
194
  r = (int)a;
 
195
  return r;
 
196
}
 
197
 
 
198
static void
 
199
gregorianDateFromAbsolute(int abs, int *day, int *month, int *year)
 
200
{
 
201
  // Search forward year by year from approximate year
 
202
  *year = abs/366;
 
203
  while (abs >= absoluteGregorianDay(1, 1, (*year)+1))
 
204
    {
 
205
      (*year)++;
 
206
    }
 
207
  // Search forward month by month from January
 
208
  (*month) = 1;
 
209
  while (abs > absoluteGregorianDay(lastDayOfGregorianMonth(*month, *year),
 
210
    *month, *year))
 
211
    {
 
212
      (*month)++;
 
213
    }
 
214
  *day = abs - absoluteGregorianDay(1, *month, *year) + 1;
 
215
}
 
216
 
 
217
/**
 
218
 * Convert a broken out time specification into a time interval
 
219
 * since the reference date.<br />
 
220
 * External - so NSDate and others can use it.
94
221
 */
95
222
NSTimeInterval
96
 
GSTime(int day, int month, int year, int h, int m, int s, int mil)
 
223
GSTime(int day, int month, int year, int hour, int minute, int second, int mil)
97
224
{
98
225
  NSTimeInterval        a;
99
226
 
102
229
  // Calculate date as GMT
103
230
  a -= GREGORIAN_REFERENCE;
104
231
  a = (NSTimeInterval)a * 86400;
105
 
  a += h * 3600;
106
 
  a += m * 60;
107
 
  a += s;
 
232
  a += hour * 3600;
 
233
  a += minute * 60;
 
234
  a += second;
108
235
  a += mil/1000.0;
109
236
  return a;
110
237
}
111
238
 
112
 
@interface NSCalendarDate (Private)
113
 
 
114
 
- (void)getYear: (int *)year month: (int *)month day: (int *)day
115
 
           hour: (int *)hour minute: (int *)minute second: (int *)second;
116
 
 
117
 
@end
118
 
 
 
239
/**
 
240
 * Convert a time interval since the reference date into broken out
 
241
 * elements.<br />
 
242
 * External - so NSDate and others can use it.
 
243
 */
 
244
void
 
245
GSBreakTime(NSTimeInterval when, int *year, int *month, int *day,
 
246
  int *hour, int *minute, int *second, int *mil)
 
247
{
 
248
  int h, m, dayOfEra;
 
249
  double a, b, c, d;
 
250
 
 
251
  // Get reference date in terms of days
 
252
  a = when / 86400.0;
 
253
  // Offset by Gregorian reference
 
254
  a += GREGORIAN_REFERENCE;
 
255
  // result is the day of common era.
 
256
  dayOfEra = (int)a;
 
257
 
 
258
  // Calculate year, month, and day
 
259
  gregorianDateFromAbsolute(dayOfEra, day, month, year);
 
260
 
 
261
  // Calculate hour, minute, and seconds
 
262
  d = dayOfEra - GREGORIAN_REFERENCE;
 
263
  d *= 86400;
 
264
  a = abs(d - when);
 
265
  b = a / 3600;
 
266
  *hour = (int)b;
 
267
  h = *hour;
 
268
  h = h * 3600;
 
269
  b = a - h;
 
270
  b = b / 60;
 
271
  *minute = (int)b;
 
272
  m = *minute;
 
273
  m = m * 60;
 
274
  c = a - h - m;
 
275
  *second = (int)c;
 
276
  *mil = (a - h - m - c) * 1000;
 
277
}
 
278
 
 
279
@class  NSGDate;
 
280
 
 
281
/**
 
282
 * An [NSDate] subclass which understands about timezones and provides
 
283
 * methods for dealing with date and time information by calendar and
 
284
 * with hours minutes and seconds.
 
285
 */
119
286
@implementation NSCalendarDate
120
287
 
121
288
+ (void) initialize
123
290
  if (self == [NSCalendarDate class])
124
291
    {
125
292
      [self setVersion: 1];
126
 
      behavior_class_add_class(self, [NSGDate class]);
 
293
      localTZ = RETAIN([NSTimeZone localTimeZone]);
 
294
 
 
295
      dstClass = [GSTimeZone class];
 
296
      absClass = [GSAbsTimeZone class];
 
297
 
 
298
      offSEL = @selector(secondsFromGMTForDate:);
 
299
      offIMP = (int (*)(id,SEL,id))
 
300
        [localTZ methodForSelector: offSEL];
 
301
      dstOffIMP = (int (*)(id,SEL,id))
 
302
        [dstClass instanceMethodForSelector: offSEL];
 
303
      absOffIMP = (int (*)(id,SEL,id))
 
304
        [absClass instanceMethodForSelector: offSEL];
 
305
 
 
306
      abrSEL = @selector(abbreviationForDate:);
 
307
      abrIMP = (NSString* (*)(id,SEL,id))
 
308
        [localTZ methodForSelector: abrSEL];
 
309
      dstAbrIMP = (NSString* (*)(id,SEL,id))
 
310
        [dstClass instanceMethodForSelector: abrSEL];
 
311
      absAbrIMP = (NSString* (*)(id,SEL,id))
 
312
        [absClass instanceMethodForSelector: abrSEL];
 
313
 
 
314
      GSObjCAddClassBehavior(self, [NSGDate class]);
127
315
    }
128
316
}
129
317
 
130
 
//
131
 
// Getting an NSCalendar Date
132
 
//
 
318
/**
 
319
 * Return an NSCalendarDate for the current date and time using the
 
320
 * default timezone.
 
321
 */
133
322
+ (id) calendarDate
134
323
{
135
324
  id    d = [[self alloc] init];
137
326
  return AUTORELEASE(d);
138
327
}
139
328
 
 
329
/**
 
330
 * Return an NSCalendarDate generated from the supplied description
 
331
 * using the format specified for parsing that string.<br />
 
332
 * Calls -initWithString:calendarFormat: to create the date.
 
333
 */
140
334
+ (id) dateWithString: (NSString *)description
141
335
       calendarFormat: (NSString *)format
142
336
{
145
339
  return AUTORELEASE(d);
146
340
}
147
341
 
 
342
/**
 
343
 * Return an NSCalendarDate generated from the supplied description
 
344
 * using the format specified for parsing that string and interpreting
 
345
 * it according to the dictionary specified.<br />
 
346
 * Calls -initWithString:calendarFormat:locale: to create the date.
 
347
 */
148
348
+ (id) dateWithString: (NSString *)description
149
349
       calendarFormat: (NSString *)format
150
350
               locale: (NSDictionary *)dictionary
155
355
  return AUTORELEASE(d);
156
356
}
157
357
 
 
358
/**
 
359
 * Creates and returns an NSCalendarDate from the specified values
 
360
 * by calling -initWithYear:month:day:hour:minute:second:timeZone:
 
361
 */
158
362
+ (id) dateWithYear: (int)year
159
363
              month: (unsigned int)month
160
364
                day: (unsigned int)day
164
368
           timeZone: (NSTimeZone *)aTimeZone
165
369
{
166
370
  NSCalendarDate *d = [[self alloc] initWithYear: year
167
 
                                    month: month
168
 
                                    day: day
169
 
                                    hour: hour
170
 
                                    minute: minute
171
 
                                    second: second
172
 
                                    timeZone: aTimeZone];
 
371
                                           month: month
 
372
                                             day: day
 
373
                                            hour: hour
 
374
                                          minute: minute
 
375
                                          second: second
 
376
                                        timeZone: aTimeZone];
173
377
  return AUTORELEASE(d);
174
378
}
175
379
 
 
380
/**
 
381
 * Creates and returns a new NSCalendarDate object by taking the
 
382
 * value of the receiver and adding the interval in seconds specified.
 
383
 */
176
384
- (id) addTimeInterval: (NSTimeInterval)seconds
177
385
{
178
386
  id newObj = [[self class] dateWithTimeIntervalSinceReferenceDate:
217
425
  [super dealloc];
218
426
}
219
427
 
220
 
/*
221
 
 * Initializing an NSCalendar Date
 
428
/**
 
429
 * Initializes an NSCalendarDate using the specified description and the
 
430
 * default calendar format and locale.<br />
 
431
 * Calls -initWithString:calendarFormat:locale:
222
432
 */
223
433
- (id) initWithString: (NSString *)description
224
434
{
225
435
  // +++ What is the locale?
226
436
  return [self initWithString: description
227
 
               calendarFormat: @"%Y-%m-%d %H:%M:%S %z"
 
437
               calendarFormat: cformat
228
438
                       locale: nil];
229
439
}
230
440
 
 
441
/**
 
442
 * Initializes an NSCalendarDate using the specified description and format
 
443
 * string interpreted in the default locale.<br />
 
444
 * Calls -initWithString:calendarFormat:locale:
 
445
 */
231
446
- (id) initWithString: (NSString *)description
232
447
       calendarFormat: (NSString *)format
233
448
{
279
494
#define hads    32
280
495
#define hadw    64
281
496
 
282
 
- (id) initWithString: (NSString *)description 
283
 
       calendarFormat: (NSString *)fmt
284
 
               locale: (NSDictionary *)locale
 
497
/**
 
498
 * Initializes an NSCalendarDate using the specified description and format
 
499
 * string interpreted in the given locale.<br />
 
500
 * If description does not match fmt exactly, this method returns nil.<br />
 
501
 * Format specifiers are -
 
502
 * <list>
 
503
 *   <item>
 
504
 *     %%   literal % character
 
505
 *   </item>
 
506
 *   <item>
 
507
 *     %a   abbreviated weekday name according to locale
 
508
 *   </item>
 
509
 *   <item>
 
510
 *     %A   full weekday name according to locale
 
511
 *   </item>
 
512
 *   <item>
 
513
 *     %b   abbreviated month name according to locale
 
514
 *   </item>
 
515
 *   <item>
 
516
 *     %B   full month name according to locale
 
517
 *   </item>
 
518
 *   <item>
 
519
 *     %c   same as '%X %x'
 
520
 *   </item>
 
521
 *   <item>
 
522
 *     %d   day of month as decimal number
 
523
 *   </item>
 
524
 *   <item>
 
525
 *     %e   same as %d without leading zero (you get a leading space instead)
 
526
 *   </item>
 
527
 *   <item>
 
528
 *     %F   milliseconds as a decimal number
 
529
 *   </item>
 
530
 *   <item>
 
531
 *     %H   hour as a decimal number using 24-hour clock
 
532
 *   </item>
 
533
 *   <item>
 
534
 *     %I   hour as a decimal number using 12-hour clock
 
535
 *   </item>
 
536
 *   <item>
 
537
 *     %j   day of year as a decimal number
 
538
 *   </item>
 
539
 *   <item>
 
540
 *     %m   month as decimal number
 
541
 *   </item>
 
542
 *   <item>
 
543
 *     %M   minute as decimal number
 
544
 *   </item>
 
545
 *   <item>
 
546
 *     %p   'am' or 'pm'
 
547
 *   </item>
 
548
 *   <item>
 
549
 *     %S   second as decimal number
 
550
 *   </item>
 
551
 *   <item>
 
552
 *     %U   week of the current year as decimal number (Sunday first day)
 
553
 *   </item>
 
554
 *   <item>
 
555
 *     %W   week of the current year as decimal number (Monday first day)
 
556
 *   </item>
 
557
 *   <item>
 
558
 *     %w   day of the week as decimal number (Sunday = 0)
 
559
 *   </item>
 
560
 *   <item>
 
561
 *     %x   date with date representation for locale
 
562
 *   </item>
 
563
 *   <item>
 
564
 *     %X   time with time representation for locale
 
565
 *   </item>
 
566
 *   <item>
 
567
 *     %y   year as a decimal number without century
 
568
 *   </item>
 
569
 *   <item>
 
570
 *     %Y   year as a decimal number with century
 
571
 *   </item>
 
572
 *   <item>
 
573
 *     %z   time zone offset in hours and minutes from GMT (HHMM)
 
574
 *   </item>
 
575
 *   <item>
 
576
 *     %Z   time zone abbreviation
 
577
 *   </item>
 
578
 * </list>
 
579
 * If no year is specified in the format, the current year is assumed.<br />
 
580
 * If no month is specified in the format, January is assumed.<br />
 
581
 * If no day is specified in the format, 1 is assumed.<br />
 
582
 * If no hour is specified in the format, 0 is assumed.<br />
 
583
 * If no minute is specified in the format, 0 is assumed.<br />
 
584
 * If no second is specified in the format, 0 is assumed.<br />
 
585
 * If no millisecond is specified in the format, 0 is assumed.<br />
 
586
 * If no timezone is specified in the format, the local timezone is assumed.
 
587
 */
 
588
- (id) initWithString: (NSString*)description
 
589
       calendarFormat: (NSString*)fmt
 
590
               locale: (NSDictionary*)locale
285
591
{
286
 
  // If description does not match this format exactly, this method returns nil 
287
 
  if ([description length] == 0)
 
592
  int           milliseconds = 0;
 
593
  int           year = 1;
 
594
  int           month = 1;
 
595
  int           day = 1;
 
596
  int           hour = 0;
 
597
  int           min = 0;
 
598
  int           sec = 0;
 
599
  NSTimeZone    *tz = nil;
 
600
  BOOL          ampm = NO;
 
601
  BOOL          twelveHrClock = NO;
 
602
  int           julianWeeks = -1, weekStartsMonday = 0, dayOfWeek = -1;
 
603
  const char    *source = [description cString];
 
604
  unsigned      sourceLen = strlen(source);
 
605
  unichar       *format;
 
606
  unsigned      formatLen;
 
607
  unsigned      formatIdx = 0;
 
608
  unsigned      sourceIdx = 0;
 
609
  char          tmpStr[20];
 
610
  unsigned int  tmpIdx;
 
611
  unsigned      had = 0;
 
612
  unsigned int  pos;
 
613
  BOOL          hadPercent = NO;
 
614
  NSMutableData *fd;
 
615
  BOOL          changedFormat = NO;
 
616
  BOOL          error = NO;
 
617
 
 
618
  if (locale == nil)
288
619
    {
289
 
      // Autorelease self because it isn't done by the calling function
290
 
      // [[NSCalendarDate alloc] initWithString:calendarFormat:locale:];
291
 
      AUTORELEASE(self);
292
 
      return nil;
 
620
      locale = GSUserDefaultsDictionaryRepresentation();
293
621
    }
294
 
  else
 
622
  if (fmt == nil)
295
623
    {
296
 
      int               year = 0, month = 1, day = 1;
297
 
      int               hour = 0, min = 0, sec = 0;
298
 
      NSTimeZone        *tz = [NSTimeZone localTimeZone];
299
 
      BOOL              ampm = NO;
300
 
      BOOL              twelveHrClock = NO; 
301
 
      int               julianWeeks = -1, weekStartsMonday = 0, dayOfWeek = -1;
302
 
      const char        *source = [description cString];
303
 
      unsigned          sourceLen = strlen(source);
304
 
      unichar           *format;
305
 
      unsigned          formatLen;
306
 
      unsigned          formatIdx = 0;
307
 
      unsigned          sourceIdx = 0;
308
 
      char              tmpStr[20];
309
 
      int               tmpIdx;
310
 
      unsigned          had = 0;
311
 
      int               pos;
312
 
      BOOL              hadPercent = NO;
313
 
      NSString          *dForm;
314
 
      NSString          *tForm;
315
 
      NSString          *TForm;
316
 
      NSMutableData     *fd;
317
 
      BOOL              changedFormat = NO;
318
 
      
319
 
      if (locale == nil)
320
 
        {
321
 
          locale = GSUserDefaultsDictionaryRepresentation();
322
 
        }
 
624
      fmt = [locale objectForKey: NSTimeDateFormatString];
323
625
      if (fmt == nil)
324
626
        {
325
 
          fmt = [locale objectForKey: NSTimeDateFormatString];
326
 
          if (fmt == nil)
327
 
            fmt = @"";
 
627
          fmt = @"";
328
628
        }
329
 
 
330
 
      TForm = [locale objectForKey: NSTimeDateFormatString];
331
 
      if (TForm == nil)
332
 
        TForm = @"%X %x";
333
 
      dForm = [locale objectForKey: NSShortDateFormatString];
334
 
      if (dForm == nil)
335
 
        dForm = @"%y-%m-%d";
336
 
      tForm = [locale objectForKey: NSTimeFormatString];
337
 
      if (tForm == nil)
338
 
        tForm = @"%H-%M-%S";
339
 
 
340
 
      /*
341
 
       * Get format into a buffer, leaving room for expansion in case it has
342
 
       * escapes that need to be converted.
343
 
       */
344
 
      formatLen = [fmt length];
345
 
      fd = [[NSMutableData alloc]
346
 
        initWithLength: (formatLen + 32) * sizeof(unichar)];
347
 
      format = (unichar*)[fd mutableBytes];
348
 
      [fmt getCharacters: format];
349
 
 
350
 
      /*
351
 
       * Expand any sequences to their basic components.
352
 
       */
353
 
      for (pos = 0; pos < formatLen; pos++)
 
629
    }
 
630
  if (description == nil)
 
631
    {
 
632
      description = @"";
 
633
    }
 
634
 
 
635
  /*
 
636
   * Get format into a buffer, leaving room for expansion in case it has
 
637
   * escapes that need to be converted.
 
638
   */
 
639
  formatLen = [fmt length];
 
640
  fd = [[NSMutableData alloc]
 
641
    initWithLength: (formatLen + 32) * sizeof(unichar)];
 
642
  format = (unichar*)[fd mutableBytes];
 
643
  [fmt getCharacters: format];
 
644
 
 
645
  /*
 
646
   * Expand any sequences to their basic components.
 
647
   */
 
648
  for (pos = 0; pos < formatLen; pos++)
 
649
    {
 
650
      unichar   c = format[pos];
 
651
 
 
652
      if (c == '%')
354
653
        {
355
 
          unichar       c = format[pos];
356
 
 
357
 
          if (c == '%')
358
 
            {
359
 
              if (hadPercent == YES)
360
 
                {
361
 
                  hadPercent = NO;
362
 
                }
363
 
              else
364
 
                {
365
 
                  hadPercent = YES;
366
 
                }
367
 
            }
368
 
          else
369
 
            {
370
 
              if (hadPercent == YES)
371
 
                {
372
 
                  NSString      *sub = nil;
373
 
 
374
 
                  if (c == 'c')
375
 
                    {
376
 
                      sub = TForm;
377
 
                    }
378
 
                  else if (c == 'R')
379
 
                    {
380
 
                      sub = @"%H:%M";
381
 
                    }
382
 
                  else if (c == 'r')
383
 
                    {
384
 
                      sub = @"%I:%M:%S %p";
385
 
                    }
386
 
                  else if (c == 'X')
387
 
                    {
388
 
                      sub = tForm;
389
 
                    }
390
 
                  else if (c == 'x')
391
 
                    {
392
 
                      sub = dForm;
393
 
                    }
394
 
 
395
 
                  if (sub != nil)
396
 
                    {
397
 
                      unsigned  sLen = [sub length];
398
 
                      unsigned  i;
399
 
 
400
 
                      if (sLen > 2)
401
 
                        {
402
 
                          [fd setLength:
403
 
                            (formatLen + sLen - 2) * sizeof(unichar)];
404
 
                          format = (unichar*)[fd mutableBytes];
405
 
                          for (i = formatLen-1; i > pos; i--)
406
 
                            {
407
 
                              format[i+sLen-2] = format[i];
408
 
                            }
409
 
                        }
410
 
                      else
411
 
                        {
412
 
                          for (i = pos+1; i < formatLen; i++)
413
 
                            {
414
 
                              format[i+sLen-2] = format[i];
415
 
                            }
416
 
                          [fd setLength:
417
 
                            (formatLen + sLen - 2) * sizeof(unichar)];
418
 
                          format = (unichar*)[fd mutableBytes];
419
 
                        }
420
 
                      [sub getCharacters: &format[pos-1]];
421
 
                      formatLen += sLen - 2;
422
 
                      changedFormat = YES;
423
 
                      pos -= 2; // Re-parse the newly substituted data.
424
 
                    }
425
 
                }
 
654
          if (hadPercent == YES)
 
655
            {
426
656
              hadPercent = NO;
427
657
            }
428
 
        }
429
 
 
430
 
      /*
431
 
       * Set up calendar format.
432
 
       */
433
 
      if (changedFormat == YES)
434
 
        {
435
 
          fmt = [NSString stringWithCharacters: format length: formatLen];
436
 
        }
437
 
      ASSIGN(_calendar_format, fmt);
438
 
 
439
 
      //
440
 
      // WARNING:
441
 
      //   %F, does NOT work.
442
 
      //    and the underlying call has granularity to the second.
443
 
      //   -Most locale stuff is dubious at best.
444
 
      //   -Long day and month names depend on a non-alpha character after the
445
 
      //    last digit to work.
446
 
      //
447
 
      // The strftime specifiers as used by OpenStep + %U.
448
 
      //
449
 
      // %%   literal % character
450
 
      // %a   abbreviated weekday name according to locale
451
 
      // %A   full weekday name according to locale
452
 
      // %b   abbreviated month name according to locale
453
 
      // %B   full month name according to locale
454
 
      // %c   same as '%X %x'
455
 
      // %d   day of month as decimal number
456
 
      // %e   same as %d without leading zero (you get a leading space instead)
457
 
      // %F   milliseconds as a decimal number
458
 
      // %H   hour as a decimal number using 24-hour clock
459
 
      // %I   hour as a decimal number using 12-hour clock
460
 
      // %j   day of year as a decimal number
461
 
      // %m   month as decimal number
462
 
      // %M   minute as decimal number
463
 
      // %p   'am' or 'pm'
464
 
      // %S   second as decimal number
465
 
      // %U   week of the current year as decimal number (Sunday first day)
466
 
      // %W   week of the current year as decimal number (Monday first day)
467
 
      // %w   day of the week as decimal number (Sunday = 0)
468
 
      // %x   date with date representation for locale
469
 
      // %X   time with time representation for locale
470
 
      // %y   year as a decimal number without century 
471
 
      // %Y   year as a decimal number with century
472
 
      // %z   time zone offset in hours and minutes from GMT (HHMM)
473
 
      // %Z   time zone abbreviation
474
 
 
475
 
      while (formatIdx < formatLen)
476
 
        {
477
 
          if (format[formatIdx] != '%')
478
 
            {
479
 
              // If it's not a format specifier, ignore it.
480
 
              if (isspace(format[formatIdx]))
481
 
                {
482
 
                  // Skip any amount of white space.
483
 
                  while (source[sourceIdx] != 0 && isspace(source[sourceIdx]))
484
 
                    {
485
 
                      sourceIdx++;
486
 
                    }
487
 
                }
488
 
              else
489
 
                {
490
 
                  if (sourceIdx < sourceLen)
491
 
                    {
492
 
                      if (source[sourceIdx] != format[formatIdx])
493
 
                        {
494
 
                          NSLog(@"Expected literal '%c' but got '%c'",
495
 
                            format[formatIdx], source[sourceIdx]);
496
 
                        }
497
 
                      sourceIdx++;
498
 
                    }
499
 
                }
500
 
            }
501
 
          else
502
 
            {
503
 
              // Skip '%'
504
 
              formatIdx++;
505
 
 
506
 
              switch (format[formatIdx])
507
 
                {
508
 
                  case '%':
509
 
                    // skip literal %
510
 
                    if (sourceIdx < sourceLen)
511
 
                      {
512
 
                        if (source[sourceIdx] != '%')
513
 
                          {
514
 
                            NSLog(@"Expected literal '%' but got '%c'",
515
 
                              source[sourceIdx]);
516
 
                          }
517
 
                        sourceIdx++;
518
 
                      }
519
 
                    break;
520
 
 
521
 
                  case 'a':
522
 
                    // Are Short names three chars in all locales?????
523
 
                    tmpStr[0] = toupper(source[sourceIdx]);
524
 
                    if (sourceIdx < sourceLen)
525
 
                      sourceIdx++;
526
 
                    tmpStr[1] = tolower(source[sourceIdx]);
527
 
                    if (sourceIdx < sourceLen)
528
 
                      sourceIdx++;
529
 
                    tmpStr[2] = tolower(source[sourceIdx]);
530
 
                    if (sourceIdx < sourceLen)
531
 
                      sourceIdx++;
532
 
                    tmpStr[3] = '\0';
533
 
                    {
534
 
                      NSString  *currDay;
535
 
                      NSArray   *dayNames;
536
 
 
537
 
                      currDay = [NSString stringWithCString: tmpStr];
538
 
                      dayNames = [locale objectForKey: NSShortWeekDayNameArray];
539
 
                      for (tmpIdx = 0; tmpIdx < 7; tmpIdx++)
540
 
                        {
541
 
                          if ([[dayNames objectAtIndex: tmpIdx] isEqual:
542
 
                            currDay] == YES)
543
 
                            {
544
 
                              break;
545
 
                            }
546
 
                        }
547
 
                      dayOfWeek = tmpIdx; 
548
 
                      had |= hadw;
549
 
                    }
550
 
                    break;
551
 
 
552
 
                  case 'A':
553
 
                    for (tmpIdx = sourceIdx; tmpIdx < sourceLen; tmpIdx++)
554
 
                      {
555
 
                        if (isalpha(source[tmpIdx]))
556
 
                          {
557
 
                            tmpStr[tmpIdx - sourceIdx] = source[tmpIdx];
558
 
                          }
559
 
                        else
560
 
                          {
561
 
                            break;
562
 
                          }
563
 
                      }
564
 
                    tmpStr[tmpIdx - sourceIdx] = '\0';
565
 
                    sourceIdx += tmpIdx - sourceIdx;
566
 
                    {
567
 
                      NSString  *currDay;
568
 
                      NSArray   *dayNames;
569
 
 
570
 
                      currDay = [NSString stringWithCString: tmpStr];
571
 
                      dayNames = [locale objectForKey: NSWeekDayNameArray];
572
 
                      for (tmpIdx = 0; tmpIdx < 7; tmpIdx++)
573
 
                        {
574
 
                          if ([[dayNames objectAtIndex: tmpIdx] isEqual:
575
 
                            currDay] == YES)
576
 
                            {
577
 
                              break;
578
 
                            }
579
 
                        }
580
 
                      dayOfWeek = tmpIdx;
581
 
                      had |= hadw;
582
 
                    }
583
 
                    break;
584
 
 
585
 
                  case 'b':
586
 
                    // Are Short names three chars in all locales?????
587
 
                    tmpStr[0] = toupper(source[sourceIdx]);
588
 
                    if (sourceIdx < sourceLen)
589
 
                      sourceIdx++;
590
 
                    tmpStr[1] = tolower(source[sourceIdx]);
591
 
                    if (sourceIdx < sourceLen)
592
 
                      sourceIdx++;
593
 
                    tmpStr[2] = tolower(source[sourceIdx]);
594
 
                    if (sourceIdx < sourceLen)
595
 
                      sourceIdx++;
596
 
                    tmpStr[3] = '\0';
597
 
                    {
598
 
                      NSString  *currMonth;
599
 
                      NSArray   *monthNames;
600
 
 
601
 
                      currMonth = [NSString stringWithCString: tmpStr];
602
 
                      monthNames = [locale objectForKey: NSShortMonthNameArray];
603
 
 
604
 
                      for (tmpIdx = 0; tmpIdx < 12; tmpIdx++)
605
 
                        {
606
 
                          if ([[monthNames objectAtIndex: tmpIdx]
607
 
                                    isEqual: currMonth] == YES)
608
 
                            {
609
 
                              break;
610
 
                            }
611
 
                        }
612
 
                      month = tmpIdx+1;
613
 
                      had |= hadM;
614
 
                    }
615
 
                    break;
616
 
 
617
 
                  case 'B':
618
 
                    for (tmpIdx = sourceIdx; tmpIdx < sourceLen; tmpIdx++)
619
 
                      {
620
 
                        if (isalpha(source[tmpIdx]))
621
 
                          {
622
 
                            tmpStr[tmpIdx - sourceIdx] = source[tmpIdx];
623
 
                          }
624
 
                        else
625
 
                          {
626
 
                            break;
627
 
                          }
628
 
                      }
629
 
                    tmpStr[tmpIdx - sourceIdx] = '\0';
630
 
                    sourceIdx += tmpIdx - sourceIdx;
631
 
                    {
632
 
                      NSString  *currMonth;
633
 
                      NSArray   *monthNames;
634
 
 
635
 
                      currMonth = [NSString stringWithCString: tmpStr];
636
 
                      monthNames = [locale objectForKey: NSMonthNameArray];
637
 
 
638
 
                      for (tmpIdx = 0; tmpIdx < 12; tmpIdx++)
639
 
                        {
640
 
                          if ([[monthNames objectAtIndex: tmpIdx]
641
 
                                    isEqual: currMonth] == YES)
642
 
                            {
643
 
                              break;
644
 
                            }
645
 
                        }
646
 
                      month = tmpIdx+1;
647
 
                      had |= hadM;
648
 
                    }
649
 
                    break;
650
 
 
651
 
                  case 'd': // fall through
652
 
                  case 'e':
653
 
                    sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
654
 
                    day = atoi(tmpStr);
655
 
                    had |= hadD;
656
 
                    break;
657
 
 
658
 
                  case 'F':
659
 
                    NSLog(@"%F format ignored when creating date");
660
 
                    break;
661
 
 
662
 
                  case 'I': // fall through
663
 
                    twelveHrClock = YES;
664
 
                  case 'H':
665
 
                    sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
666
 
                    hour = atoi(tmpStr);
667
 
                    had |= hadh;
668
 
                    break;
669
 
 
670
 
                  case 'j':
671
 
                    sourceIdx += getDigits(&source[sourceIdx], tmpStr, 3);
672
 
                    day = atoi(tmpStr);
673
 
                    had |= hadD;
674
 
                    break;
675
 
 
676
 
                  case 'm':
677
 
                    sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
678
 
                    month = atoi(tmpStr);
679
 
                    had |= hadM;
680
 
                    break;
681
 
 
682
 
                  case 'M':
683
 
                    sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
684
 
                    min = atoi(tmpStr);
685
 
                    had |= hadm;
686
 
                    break;
687
 
 
688
 
                  case 'p':
689
 
                    // Questionable assumption that all am/pm indicators are 2
690
 
                    // characters and in upper case....
691
 
                    tmpStr[0] = toupper(source[sourceIdx]);
692
 
                    if (sourceIdx < sourceLen)
693
 
                      sourceIdx++;
694
 
                    tmpStr[1] = toupper(source[sourceIdx]);
695
 
                    if (sourceIdx < sourceLen)
696
 
                      sourceIdx++;
697
 
                    tmpStr[2] = '\0';
698
 
                    {
699
 
                      NSString  *currAMPM;
700
 
                      NSArray   *amPMNames;
701
 
 
702
 
                      currAMPM = [NSString stringWithCString: tmpStr];
703
 
                      amPMNames = [locale objectForKey: NSAMPMDesignation];
704
 
 
705
 
                      /*
706
 
                       * The time addition is handled below because this
707
 
                       * indicator only modifies the time on a 12hour clock.
708
 
                       */
709
 
                      if ([[amPMNames objectAtIndex: 1] isEqual:
710
 
                        currAMPM] == YES)
711
 
                        {
712
 
                          ampm = YES;
713
 
                        }
714
 
                    }
715
 
                    break;
716
 
 
717
 
                  case 'S':
718
 
                    sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
719
 
                    sec = atoi(tmpStr);
720
 
                    had |= hads;
721
 
                    break;
722
 
 
723
 
                  case 'w':
724
 
                    sourceIdx += getDigits(&source[sourceIdx], tmpStr, 1);
725
 
                    dayOfWeek = atoi(tmpStr);
726
 
                    had |= hadw;
727
 
                    break;
728
 
 
729
 
                  case 'W': // Fall through
730
 
                    weekStartsMonday = 1;
731
 
                  case 'U':
732
 
                    sourceIdx += getDigits(&source[sourceIdx], tmpStr, 1);
733
 
                    julianWeeks = atoi(tmpStr);
734
 
                    break;
735
 
 
736
 
                    //  case 'x':
737
 
                    //  break;
738
 
 
739
 
                    //  case 'X':
740
 
                    //  break;
741
 
 
742
 
                  case 'y':
743
 
                    sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
744
 
                    year = atoi(tmpStr);
745
 
                    if (year >= 70)
746
 
                      {
747
 
                        year += 1900;
748
 
                      }
749
 
                    else
750
 
                      {
751
 
                        year += 2000;
752
 
                      }
753
 
                    had |= hadY;
754
 
                    break;
755
 
 
756
 
                  case 'Y':
757
 
                    sourceIdx += getDigits(&source[sourceIdx], tmpStr, 4);
758
 
                    year = atoi(tmpStr);
759
 
                    had |= hadY;
760
 
                    break;
761
 
 
762
 
                  case 'z':
763
 
                    {
764
 
                      int       sign = 1;
765
 
                      int       zone;
766
 
 
767
 
                      if (source[sourceIdx] == '+')
768
 
                        {
769
 
                          sourceIdx++;
770
 
                        }
771
 
                      else if (source[sourceIdx] == '-')
772
 
                        {
773
 
                          sign = -1;
774
 
                          sourceIdx++;
775
 
                        }
776
 
                      sourceIdx += getDigits(&source[sourceIdx], tmpStr, 4);
777
 
                      zone = atoi(tmpStr) * sign;
778
 
 
779
 
                      if ((tz = [NSTimeZone timeZoneForSecondsFromGMT: 
780
 
                        (zone / 100 * 60 + (zone % 100)) * 60]) == nil)
781
 
                        {
782
 
                          tz = [NSTimeZone localTimeZone];
783
 
                        }
784
 
                    }
785
 
                    break;
786
 
 
787
 
                  case 'Z':
788
 
                    for (tmpIdx = sourceIdx; tmpIdx < sourceLen; tmpIdx++)
789
 
                      {
790
 
                        if (isalpha(source[tmpIdx]) || source[tmpIdx] == '-'
791
 
                          || source[tmpIdx] == '+')
792
 
                          {
793
 
                            tmpStr[tmpIdx - sourceIdx] = source[tmpIdx];
794
 
                          }
795
 
                        else
796
 
                          {
797
 
                            break;
798
 
                          }
799
 
                      }
800
 
                    tmpStr[tmpIdx - sourceIdx] = '\0';
801
 
                    sourceIdx += tmpIdx - sourceIdx;
802
 
                    {
803
 
                      NSString  *z = [NSString stringWithCString: tmpStr];
804
 
 
805
 
                      tz = [NSTimeZone timeZoneWithName: z];
806
 
                      if (tz == nil)
807
 
                        {
808
 
                          tz = [NSTimeZone timeZoneWithAbbreviation: z];
809
 
                        }
810
 
                      if (tz == nil)
811
 
                        {
812
 
                          tz = [NSTimeZone localTimeZone];
813
 
                        }
814
 
                    }
815
 
                    break;
816
 
 
817
 
                  default:
818
 
                    [NSException raise: NSInvalidArgumentException
819
 
                                format: @"Invalid NSCalendar date, "
820
 
                        @"specifier %c not recognized in format %@",
821
 
                        format[formatIdx], fmt];
822
 
                }
823
 
            } 
 
658
          else
 
659
            {
 
660
              hadPercent = YES;
 
661
            }
 
662
        }
 
663
      else
 
664
        {
 
665
          if (hadPercent == YES)
 
666
            {
 
667
              NSString  *sub = nil;
 
668
 
 
669
              if (c == 'c')
 
670
                {
 
671
                  sub = [locale objectForKey: NSTimeDateFormatString];
 
672
                  if (sub == nil)
 
673
                    {
 
674
                      sub = @"%X %x";
 
675
                    }
 
676
                }
 
677
              else if (c == 'R')
 
678
                {
 
679
                  sub = @"%H:%M";
 
680
                }
 
681
              else if (c == 'r')
 
682
                {
 
683
                  sub = @"%I:%M:%S %p";
 
684
                }
 
685
              else if (c == 'X')
 
686
                {
 
687
                  sub = [locale objectForKey: NSTimeFormatString];
 
688
                  if (sub == nil)
 
689
                    {
 
690
                      sub = @"%H-%M-%S";
 
691
                    }
 
692
                }
 
693
              else if (c == 'x')
 
694
                {
 
695
                  sub = [locale objectForKey: NSShortDateFormatString];
 
696
                  if (sub == nil)
 
697
                    {
 
698
                      sub = @"%y-%m-%d";
 
699
                    }
 
700
                }
 
701
 
 
702
              if (sub != nil)
 
703
                {
 
704
                  unsigned      sLen = [sub length];
 
705
                  int   i;
 
706
 
 
707
                  if (sLen > 2)
 
708
                    {
 
709
                      [fd setLength:
 
710
                        (formatLen + sLen - 2) * sizeof(unichar)];
 
711
                      format = (unichar*)[fd mutableBytes];
 
712
                      for (i = formatLen-1; i > (int)pos; i--)
 
713
                        {
 
714
                          format[i+sLen-2] = format[i];
 
715
                        }
 
716
                    }
 
717
                  else
 
718
                    {
 
719
                      for (i = pos+1; i < (int)formatLen; i++)
 
720
                        {
 
721
                          format[i+sLen-2] = format[i];
 
722
                        }
 
723
                      [fd setLength:
 
724
                        (formatLen + sLen - 2) * sizeof(unichar)];
 
725
                      format = (unichar*)[fd mutableBytes];
 
726
                    }
 
727
                  [sub getCharacters: &format[pos-1]];
 
728
                  formatLen += sLen - 2;
 
729
                  changedFormat = YES;
 
730
                  pos -= 2;     // Re-parse the newly substituted data.
 
731
                }
 
732
            }
 
733
          hadPercent = NO;
 
734
        }
 
735
    }
 
736
 
 
737
  /*
 
738
   * Set up calendar format.
 
739
   */
 
740
  if (changedFormat == YES)
 
741
    {
 
742
      fmt = [NSString stringWithCharacters: format length: formatLen];
 
743
    }
 
744
  ASSIGN(_calendar_format, fmt);
 
745
 
 
746
  //
 
747
  // WARNING:
 
748
  //   -Most locale stuff is dubious at best.
 
749
  //   -Long day and month names depend on a non-alpha character after the
 
750
  //    last digit to work.
 
751
  //
 
752
 
 
753
  while (formatIdx < formatLen)
 
754
    {
 
755
      if (format[formatIdx] != '%')
 
756
        {
 
757
          // If it's not a format specifier, ignore it.
 
758
          if (isspace(format[formatIdx]))
 
759
            {
 
760
              // Skip any amount of white space.
 
761
              while (source[sourceIdx] != 0 && isspace(source[sourceIdx]))
 
762
                {
 
763
                  sourceIdx++;
 
764
                }
 
765
            }
 
766
          else
 
767
            {
 
768
              if (sourceIdx < sourceLen)
 
769
                {
 
770
                  if (source[sourceIdx] != format[formatIdx])
 
771
                    {
 
772
                      error = YES;
 
773
                      NSDebugMLog(
 
774
                        @"Expected literal '%c' but got '%c' parsing"
 
775
                        @"'%@' using '%@'", format[formatIdx],
 
776
                        source[sourceIdx], description, fmt);
 
777
                    }
 
778
                  sourceIdx++;
 
779
                }
 
780
            }
 
781
        }
 
782
      else
 
783
        {
 
784
          // Skip '%'
824
785
          formatIdx++;
 
786
 
 
787
          switch (format[formatIdx])
 
788
            {
 
789
              case '%':
 
790
                // skip literal %
 
791
                if (sourceIdx < sourceLen)
 
792
                  {
 
793
                    if (source[sourceIdx] != '%')
 
794
                      {
 
795
                        error = YES;
 
796
                        NSDebugMLog(
 
797
                          @"Expected literal '%%' but got '%c' parsing"
 
798
                          @"'%@' using '%@'", source[sourceIdx],
 
799
                          description, fmt);
 
800
                      }
 
801
                    sourceIdx++;
 
802
                  }
 
803
                break;
 
804
 
 
805
              case 'a':
 
806
                // Are Short names three chars in all locales?????
 
807
                tmpStr[0] = toupper(source[sourceIdx]);
 
808
                if (sourceIdx < sourceLen)
 
809
                  sourceIdx++;
 
810
                tmpStr[1] = tolower(source[sourceIdx]);
 
811
                if (sourceIdx < sourceLen)
 
812
                  sourceIdx++;
 
813
                tmpStr[2] = tolower(source[sourceIdx]);
 
814
                if (sourceIdx < sourceLen)
 
815
                  sourceIdx++;
 
816
                tmpStr[3] = '\0';
 
817
                {
 
818
                  NSString      *currDay;
 
819
                  NSArray       *dayNames;
 
820
 
 
821
                  currDay = [[NSString alloc] initWithCString: tmpStr];
 
822
                  dayNames = [locale objectForKey: NSShortWeekDayNameArray];
 
823
                  for (tmpIdx = 0; tmpIdx < 7; tmpIdx++)
 
824
                    {
 
825
                      if ([[dayNames objectAtIndex: tmpIdx] isEqual:
 
826
                        currDay] == YES)
 
827
                        {
 
828
                          break;
 
829
                        }
 
830
                    }
 
831
                  if (tmpIdx == 7)
 
832
                    {
 
833
                      error = YES;
 
834
                      NSDebugMLog(@"Day of week '%@' not found in locale",
 
835
                          currDay);
 
836
                    }
 
837
                  else
 
838
                    {
 
839
                      dayOfWeek = tmpIdx;
 
840
                      had |= hadw;
 
841
                    }
 
842
                  RELEASE(currDay);
 
843
                }
 
844
                break;
 
845
 
 
846
              case 'A':
 
847
                for (tmpIdx = sourceIdx; tmpIdx < sourceLen; tmpIdx++)
 
848
                  {
 
849
                    if (isalpha(source[tmpIdx]))
 
850
                      {
 
851
                        tmpStr[tmpIdx - sourceIdx] = source[tmpIdx];
 
852
                      }
 
853
                    else
 
854
                      {
 
855
                        break;
 
856
                      }
 
857
                  }
 
858
                tmpStr[tmpIdx - sourceIdx] = '\0';
 
859
                sourceIdx += tmpIdx - sourceIdx;
 
860
                {
 
861
                  NSString      *currDay;
 
862
                  NSArray       *dayNames;
 
863
 
 
864
                  currDay = [[NSString alloc] initWithCString: tmpStr];
 
865
                  dayNames = [locale objectForKey: NSWeekDayNameArray];
 
866
                  for (tmpIdx = 0; tmpIdx < 7; tmpIdx++)
 
867
                    {
 
868
                      if ([[dayNames objectAtIndex: tmpIdx] isEqual:
 
869
                        currDay] == YES)
 
870
                        {
 
871
                          break;
 
872
                        }
 
873
                    }
 
874
                  if (tmpIdx == 7)
 
875
                    {
 
876
                      error = YES;
 
877
                      NSDebugMLog(@"Day of week '%@' not found in locale",
 
878
                          currDay);
 
879
                    }
 
880
                  else
 
881
                    {
 
882
                      dayOfWeek = tmpIdx;
 
883
                      had |= hadw;
 
884
                    }
 
885
                  RELEASE(currDay);
 
886
                }
 
887
                break;
 
888
 
 
889
              case 'b':
 
890
                // Are Short names three chars in all locales?????
 
891
                tmpStr[0] = toupper(source[sourceIdx]);
 
892
                if (sourceIdx < sourceLen)
 
893
                  sourceIdx++;
 
894
                tmpStr[1] = tolower(source[sourceIdx]);
 
895
                if (sourceIdx < sourceLen)
 
896
                  sourceIdx++;
 
897
                tmpStr[2] = tolower(source[sourceIdx]);
 
898
                if (sourceIdx < sourceLen)
 
899
                  sourceIdx++;
 
900
                tmpStr[3] = '\0';
 
901
                {
 
902
                  NSString      *currMonth;
 
903
                  NSArray       *monthNames;
 
904
 
 
905
                  currMonth = [[NSString alloc] initWithCString: tmpStr];
 
906
                  monthNames = [locale objectForKey: NSShortMonthNameArray];
 
907
 
 
908
                  for (tmpIdx = 0; tmpIdx < 12; tmpIdx++)
 
909
                    {
 
910
                      if ([[monthNames objectAtIndex: tmpIdx]
 
911
                                isEqual: currMonth] == YES)
 
912
                        {
 
913
                          break;
 
914
                        }
 
915
                    }
 
916
                  if (tmpIdx == 12)
 
917
                    {
 
918
                      error = YES;
 
919
                      NSDebugMLog(@"Month of year '%@' not found in locale",
 
920
                          currMonth);
 
921
                    }
 
922
                  else
 
923
                    {
 
924
                      month = tmpIdx+1;
 
925
                      had |= hadM;
 
926
                    }
 
927
                  RELEASE(currMonth);
 
928
                }
 
929
                break;
 
930
 
 
931
              case 'B':
 
932
                for (tmpIdx = sourceIdx; tmpIdx < sourceLen; tmpIdx++)
 
933
                  {
 
934
                    if (isalpha(source[tmpIdx]))
 
935
                      {
 
936
                        tmpStr[tmpIdx - sourceIdx] = source[tmpIdx];
 
937
                      }
 
938
                    else
 
939
                      {
 
940
                        break;
 
941
                      }
 
942
                  }
 
943
                tmpStr[tmpIdx - sourceIdx] = '\0';
 
944
                sourceIdx += tmpIdx - sourceIdx;
 
945
                {
 
946
                  NSString      *currMonth;
 
947
                  NSArray       *monthNames;
 
948
 
 
949
                  currMonth = [[NSString alloc] initWithCString: tmpStr];
 
950
                  monthNames = [locale objectForKey: NSMonthNameArray];
 
951
 
 
952
                  for (tmpIdx = 0; tmpIdx < 12; tmpIdx++)
 
953
                    {
 
954
                      if ([[monthNames objectAtIndex: tmpIdx]
 
955
                                isEqual: currMonth] == YES)
 
956
                        {
 
957
                          break;
 
958
                        }
 
959
                    }
 
960
                  if (tmpIdx == 12)
 
961
                    {
 
962
                      error = YES;
 
963
                      NSDebugMLog(@"Month of year '%@' not found in locale",
 
964
                          currMonth);
 
965
                    }
 
966
                  else
 
967
                    {
 
968
                      month = tmpIdx+1;
 
969
                      had |= hadM;
 
970
                    }
 
971
                  RELEASE(currMonth);
 
972
                }
 
973
                break;
 
974
 
 
975
              case 'd': // fall through
 
976
              case 'e':
 
977
                sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
 
978
                day = atoi(tmpStr);
 
979
                had |= hadD;
 
980
                break;
 
981
 
 
982
              case 'F':
 
983
                sourceIdx += getDigits(&source[sourceIdx], tmpStr, 3);
 
984
                milliseconds = atoi(tmpStr);
 
985
                break;
 
986
 
 
987
              case 'I': // fall through
 
988
                twelveHrClock = YES;
 
989
              case 'H':
 
990
                sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
 
991
                hour = atoi(tmpStr);
 
992
                had |= hadh;
 
993
                break;
 
994
 
 
995
              case 'j':
 
996
                sourceIdx += getDigits(&source[sourceIdx], tmpStr, 3);
 
997
                day = atoi(tmpStr);
 
998
                had |= hadD;
 
999
                break;
 
1000
 
 
1001
              case 'm':
 
1002
                sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
 
1003
                month = atoi(tmpStr);
 
1004
                had |= hadM;
 
1005
                break;
 
1006
 
 
1007
              case 'M':
 
1008
                sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
 
1009
                min = atoi(tmpStr);
 
1010
                had |= hadm;
 
1011
                break;
 
1012
 
 
1013
              case 'p':
 
1014
                // Questionable assumption that all am/pm indicators are 2
 
1015
                // characters and in upper case....
 
1016
                tmpStr[0] = toupper(source[sourceIdx]);
 
1017
                if (sourceIdx < sourceLen)
 
1018
                  sourceIdx++;
 
1019
                tmpStr[1] = toupper(source[sourceIdx]);
 
1020
                if (sourceIdx < sourceLen)
 
1021
                  sourceIdx++;
 
1022
                tmpStr[2] = '\0';
 
1023
                {
 
1024
                  NSString      *currAMPM;
 
1025
                  NSArray       *amPMNames;
 
1026
 
 
1027
                  currAMPM = [NSString stringWithCString: tmpStr];
 
1028
                  amPMNames = [locale objectForKey: NSAMPMDesignation];
 
1029
 
 
1030
                  /*
 
1031
                   * The time addition is handled below because this
 
1032
                   * indicator only modifies the time on a 12hour clock.
 
1033
                   */
 
1034
                  if ([[amPMNames objectAtIndex: 1] isEqual:
 
1035
                    currAMPM] == YES)
 
1036
                    {
 
1037
                      ampm = YES;
 
1038
                    }
 
1039
                }
 
1040
                break;
 
1041
 
 
1042
              case 'S':
 
1043
                sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
 
1044
                sec = atoi(tmpStr);
 
1045
                had |= hads;
 
1046
                break;
 
1047
 
 
1048
              case 'w':
 
1049
                sourceIdx += getDigits(&source[sourceIdx], tmpStr, 1);
 
1050
                dayOfWeek = atoi(tmpStr);
 
1051
                had |= hadw;
 
1052
                break;
 
1053
 
 
1054
              case 'W': // Fall through
 
1055
                weekStartsMonday = 1;
 
1056
              case 'U':
 
1057
                sourceIdx += getDigits(&source[sourceIdx], tmpStr, 1);
 
1058
                julianWeeks = atoi(tmpStr);
 
1059
                break;
 
1060
 
 
1061
                //      case 'x':
 
1062
                //      break;
 
1063
 
 
1064
                //      case 'X':
 
1065
                //      break;
 
1066
 
 
1067
              case 'y':
 
1068
                sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
 
1069
                year = atoi(tmpStr);
 
1070
                if (year >= 70)
 
1071
                  {
 
1072
                    year += 1900;
 
1073
                  }
 
1074
                else
 
1075
                  {
 
1076
                    year += 2000;
 
1077
                  }
 
1078
                had |= hadY;
 
1079
                break;
 
1080
 
 
1081
              case 'Y':
 
1082
                sourceIdx += getDigits(&source[sourceIdx], tmpStr, 4);
 
1083
                year = atoi(tmpStr);
 
1084
                had |= hadY;
 
1085
                break;
 
1086
 
 
1087
              case 'z':
 
1088
                {
 
1089
                  int   sign = 1;
 
1090
                  int   zone;
 
1091
                  int   found;
 
1092
 
 
1093
                  if (source[sourceIdx] == '+')
 
1094
                    {
 
1095
                      sourceIdx++;
 
1096
                    }
 
1097
                  else if (source[sourceIdx] == '-')
 
1098
                    {
 
1099
                      sign = -1;
 
1100
                      sourceIdx++;
 
1101
                    }
 
1102
                  found = getDigits(&source[sourceIdx], tmpStr, 4);
 
1103
                  if (found > 0)
 
1104
                    {
 
1105
                      sourceIdx += found;
 
1106
                      zone = atoi(tmpStr);
 
1107
                      if (found == 2)
 
1108
                        {
 
1109
                          zone *= 100;  // Convert 2 digits to 4
 
1110
                        }
 
1111
                      tz = [NSTimeZone timeZoneForSecondsFromGMT:
 
1112
                        sign * ((zone / 100) * 60 + (zone % 100)) * 60];
 
1113
                    }
 
1114
                }
 
1115
                break;
 
1116
 
 
1117
              case 'Z':
 
1118
                for (tmpIdx = sourceIdx; tmpIdx < sourceLen; tmpIdx++)
 
1119
                  {
 
1120
                    if (isalpha(source[tmpIdx]) || source[tmpIdx] == '-'
 
1121
                      || source[tmpIdx] == '+')
 
1122
                      {
 
1123
                        tmpStr[tmpIdx - sourceIdx] = source[tmpIdx];
 
1124
                      }
 
1125
                    else
 
1126
                      {
 
1127
                        break;
 
1128
                      }
 
1129
                  }
 
1130
                tmpStr[tmpIdx - sourceIdx] = '\0';
 
1131
                sourceIdx += tmpIdx - sourceIdx;
 
1132
                {
 
1133
                  NSString      *z = [NSString stringWithCString: tmpStr];
 
1134
 
 
1135
                  /* Abbreviations aren't one-to-one with time zone names
 
1136
                     so just look for the zone named after the abbreviation,
 
1137
                     then look up the abbreviation as a last resort */
 
1138
                  tz = [NSTimeZone timeZoneWithName: z];
 
1139
                  if (tz == nil)
 
1140
                    {
 
1141
                      tz = [NSTimeZone timeZoneWithAbbreviation: z];
 
1142
                    }
 
1143
                }
 
1144
                break;
 
1145
 
 
1146
              default:
 
1147
                [NSException raise: NSInvalidArgumentException
 
1148
                            format: @"Invalid NSCalendar date, "
 
1149
                    @"specifier %c not recognized in format %@",
 
1150
                    format[formatIdx], fmt];
 
1151
            }
825
1152
        }
826
 
      RELEASE(fd);
 
1153
      formatIdx++;
 
1154
    }
 
1155
  RELEASE(fd);
827
1156
 
 
1157
  if (error == NO)
 
1158
    {
828
1159
      if (tz == nil)
829
1160
        {
830
 
          tz = [NSTimeZone localTimeZone];
 
1161
          tz = localTZ;
831
1162
        }
832
1163
 
833
1164
      if (twelveHrClock == YES)
848
1179
 
849
1180
          if ((had & (hadY|hadw)) != (hadY|hadw))
850
1181
            {
851
 
              NSCalendarDate    *now = [NSCalendarDate  date];
 
1182
              NSCalendarDate    *now = [[NSCalendarDate alloc] init];
852
1183
 
853
1184
              [now setTimeZone: gmtZone];
854
 
              if ((had | hadY) == 0)
 
1185
              if ((had & hadY) == 0)
855
1186
                {
856
1187
                  year = [now yearOfCommonEra];
857
1188
                  had |= hadY;
858
1189
                }
859
 
              if ((had | hadw) == 0)
 
1190
              if ((had & hadw) == 0)
860
1191
                {
861
1192
                  dayOfWeek = [now dayOfWeek];
862
1193
                  had |= hadw;
863
1194
                }
 
1195
              RELEASE(now);
864
1196
            }
865
1197
 
866
 
          d  = [NSCalendarDate dateWithYear: year
867
 
                                      month: 1
868
 
                                        day: 1
869
 
                                       hour: 0
870
 
                                     minute: 0
871
 
                                     second: 0
872
 
                                   timeZone: gmtZone];
 
1198
          d = [[NSCalendarDate alloc] initWithYear: year
 
1199
                                             month: 1
 
1200
                                               day: 1
 
1201
                                              hour: 0
 
1202
                                            minute: 0
 
1203
                                            second: 0
 
1204
                                          timeZone: gmtZone];
873
1205
          currDay = [d dayOfWeek];
 
1206
          RELEASE(d);
874
1207
 
875
1208
          /*
876
1209
           * The julian weeks are either sunday relative or monday relative
894
1227
        }
895
1228
 
896
1229
      /*
897
 
       * Use current date/time information for anything missing.
 
1230
       * If the year has not been set ... use this year ... as on MacOS-X
898
1231
       */
899
 
      if ((had & (hadY|hadM|hadD|hadh|hadm|hads))
900
 
        != (hadY|hadM|hadD|hadh|hadm|hads))
 
1232
      if ((had & hadY) == 0)
901
1233
        {
902
 
          NSCalendarDate        *now = [NSCalendarDate  date];
903
 
          int                   Y, M, D, h, m, s;
 
1234
          NSCalendarDate        *now = [[NSCalendarDate alloc] init];
904
1235
 
905
 
          [now setTimeZone: tz];
906
 
          [now getYear: &Y month: &M day: &D hour: &h minute: &m second: &s];
907
 
          if ((had & hadY) == 0)
908
 
            year = Y;
909
 
          if ((had & hadM) == 0)
910
 
            month = M;
911
 
          if ((had & hadD) == 0)
912
 
            day = D;
913
 
          if ((had & hadh) == 0)
914
 
            hour = h;
915
 
          if ((had & hadm) == 0)
916
 
            min = m;
917
 
          if ((had & hads) == 0)
918
 
            sec = s;
 
1236
          year = [now yearOfCommonEra];
 
1237
          RELEASE(now);
919
1238
        }
920
1239
 
921
 
      return [self initWithYear: year
 
1240
      self = [self initWithYear: year
922
1241
                          month: month
923
1242
                            day: day
924
1243
                           hour: hour
925
1244
                         minute: min
926
1245
                         second: sec
927
1246
                       timeZone: tz];
928
 
    }
 
1247
      if (self != nil)
 
1248
        {
 
1249
          _seconds_since_ref += ((float)milliseconds) / 1000.0;
 
1250
        }
 
1251
    }
 
1252
 
 
1253
  if (error == YES)
 
1254
    {
 
1255
      DESTROY(self);
 
1256
    }
 
1257
  return self;
929
1258
}
930
1259
 
931
 
 
 
1260
/**
 
1261
 * Returns an NSCalendarDate instance with the given year, month, day,
 
1262
 * hour, minute, and second, using aTimeZone.<br />
 
1263
 * The year includes the century (ie you can't just say '02' when you
 
1264
 * mean '2002').<br />
 
1265
 * The month is in the range 1 to 12,<br />
 
1266
 * The day is in the range 1 to 31,<br />
 
1267
 * The hour is in the range 0 to 23,<br />
 
1268
 * The minute is in the range 0 to 59,<br />
 
1269
 * The second is in the range 0 to 59.<br />
 
1270
 * If aTimeZone is nil, the [NSTimeZone+localTimeZone] value is used.
 
1271
 * <p>
 
1272
 *   GNUstep checks the validity of the method arguments, and unless
 
1273
 *   the base library was built with 'warn=no' it generates a warning
 
1274
 *   for bad values.  It tries to use those bad values to generate a
 
1275
 *   date anyway though, rather than failing (this also appears to be
 
1276
 *   the behavior of MacOS-X).
 
1277
 * </p>
 
1278
 * The algorithm GNUstep uses to create the date is this ...<br />
 
1279
 * <list>
 
1280
 *   <item>
 
1281
 *     Convert the broken out date values into a time interval since
 
1282
 *     the reference date, as if those values represent a GMT date/time.
 
1283
 *   </item>
 
1284
 *   <item>
 
1285
 *     Ask the time zone for the offset from GMT at the resulting date,
 
1286
 *     and apply that offset to the time interval ... so get the value
 
1287
 *     for the specified timezone.
 
1288
 *   </item>
 
1289
 *   <item>
 
1290
 *     Ask the time zone for the offset from GMT at the new date ...
 
1291
 *     in case the new date is in a different daylight savings time
 
1292
 *     band from the original date.  If this offset differs from the
 
1293
 *     previous one, apply the difference so that the result is
 
1294
 *     corrected for daylight savings.  This is the final result used.
 
1295
 *   </item>
 
1296
 *   <item>
 
1297
 *     After establishing the time interval we will use and completing
 
1298
 *     initialisation, we ask the time zone for the offset from GMT again.
 
1299
 *     If it is not the same as the last time, then the time specified by
 
1300
 *     the broken out date does not really exist ... since it's in the
 
1301
 *     period lost by the transition to daylight savings.  The resulting
 
1302
 *     date is therefore not the date that was actually asked for, but is
 
1303
 *     the best approximation we can do.  If the base library was not
 
1304
 *     built with 'warn=no' then a warning message is logged for this
 
1305
 *     condition.
 
1306
 *   </item>
 
1307
 * </list>
 
1308
 */
932
1309
- (id) initWithYear: (int)year
933
1310
              month: (unsigned int)month
934
1311
                day: (unsigned int)day
937
1314
             second: (unsigned int)second
938
1315
           timeZone: (NSTimeZone *)aTimeZone
939
1316
{
940
 
  int                   c;
941
 
  NSDate                *d;
 
1317
  unsigned int          c;
942
1318
  NSTimeInterval        s;
943
1319
  NSTimeInterval        oldOffset;
 
1320
  NSTimeInterval        newOffset;
 
1321
 
 
1322
  if (month < 1 || month > 12)
 
1323
    {
 
1324
      NSWarnMLog(@"invalid month given - %u", month);
 
1325
    }
 
1326
  c = lastDayOfGregorianMonth(month, year);
 
1327
  if (day < 1 || day > c)
 
1328
    {
 
1329
      NSWarnMLog(@"invalid day given - %u", day);
 
1330
    }
 
1331
  if (hour > 23)
 
1332
    {
 
1333
      NSWarnMLog(@"invalid hour given - %u", hour);
 
1334
    }
 
1335
  if (minute > 59)
 
1336
    {
 
1337
      NSWarnMLog(@"invalid minute given - %u", minute);
 
1338
    }
 
1339
  if (second > 59)
 
1340
    {
 
1341
      NSWarnMLog(@"invalid second given - %u", second);
 
1342
    }
944
1343
 
945
1344
  // Calculate date as GMT
946
1345
  s = GSTime(day, month, year, hour, minute, second, 0);
948
1347
  // Assign time zone detail
949
1348
  if (aTimeZone == nil)
950
1349
    {
951
 
      _time_zone = RETAIN([NSTimeZone localTimeZone]);
 
1350
      _time_zone = localTZ;     // retain is a no-op for the local timezone.
952
1351
    }
953
1352
  else
954
1353
    {
955
1354
      _time_zone = RETAIN(aTimeZone);
956
1355
    }
957
 
  d = [NSDate dateWithTimeIntervalSinceReferenceDate: s];
 
1356
  if (_calendar_format == nil)
 
1357
    {
 
1358
      _calendar_format = cformat;
 
1359
    }
 
1360
  _seconds_since_ref = s;
958
1361
 
959
 
  // Adjust date so it is correct for time zone.
960
 
  oldOffset = [_time_zone secondsFromGMTForDate: d];
 
1362
  /*
 
1363
   * Adjust date so it is correct for time zone.
 
1364
   */
 
1365
  oldOffset = offset(_time_zone, self);
961
1366
  s -= oldOffset;
962
 
  self = [self initWithTimeIntervalSinceReferenceDate: s];
963
 
 
964
 
  /* Now permit up to five cycles of adjustment to allow for daylight savings.
965
 
     NB. this depends on it being OK to call the
966
 
      [-initWithTimeIntervalSinceReferenceDate: ] method repeatedly! */
967
 
 
968
 
  for (c = 0; c < 5 && self != nil; c++)
 
1367
  _seconds_since_ref = s;
 
1368
 
 
1369
  /*
 
1370
   * See if we need to adjust for daylight savings time
 
1371
   */
 
1372
  newOffset = offset(_time_zone, self);
 
1373
  if (oldOffset != newOffset)
969
1374
    {
970
 
      int       y, m, d, h, mm, ss;
971
 
      NSTimeInterval    newOffset;
972
 
 
973
 
      [self getYear: &y month: &m day: &d hour: &h minute: &mm second: &ss];
974
 
      if (y==year && m==month && d==day && h==hour && mm==minute && ss==second)
975
 
        return self;
976
 
 
977
 
      /* Has the time-zone offset changed?  If so - adjust time for it,
978
 
         other wise -  try to adjust to the correct time. */
979
 
      newOffset = [_time_zone secondsFromGMTForDate: self];
980
 
      if (newOffset != oldOffset)
981
 
        {
982
 
          s += newOffset - oldOffset;
983
 
          oldOffset = newOffset;
984
 
        }
985
 
      else
986
 
        {
987
 
          NSTimeInterval        move;
988
 
 
989
 
          /* Do we need to go back or forwards in time?
990
 
             Shift at most two hours - we know of no daylight savings time
991
 
             which is an offset of more than two hourts */
992
 
          if (y > year)
993
 
            move = -7200.0;
994
 
          else if (y < year)
995
 
            move = +7200.0;
996
 
          else if (m > month)
997
 
            move = -7200.0;
998
 
          else if (m < month)
999
 
            move = +7200.0;
1000
 
          else if (d > day)
1001
 
            move = -7200.0;
1002
 
          else if (d < day)
1003
 
            move = +7200.0;
1004
 
          else if (h > hour || h < hour)
1005
 
            move = (hour - h)*3600.0;
1006
 
          else if (mm > minute || mm < minute)
1007
 
            move = (minute - mm)*60.0;
1008
 
          else
1009
 
            move = (second - ss);
1010
 
 
1011
 
          s += move;
1012
 
        }
1013
 
      self = [self initWithTimeIntervalSinceReferenceDate: s];
 
1375
      s -= (newOffset - oldOffset);
 
1376
      _seconds_since_ref = s;
 
1377
      oldOffset = offset(_time_zone, self);
 
1378
      /*
 
1379
       * If the adjustment puts us in another offset, we must be in the
 
1380
       * non-existent period at the start of daylight savings time.
 
1381
       */
 
1382
      if (oldOffset != newOffset)
 
1383
        {
 
1384
          NSWarnMLog(@"init non-existent time at start of daylight savings");
 
1385
        }
1014
1386
    }
 
1387
 
1015
1388
  return self;
1016
1389
}
1017
1390
 
1018
 
// Default initializer
 
1391
/**
 
1392
 * Initialises the receiver with the specified interval since the
 
1393
 * reference date.  Uses th standard format string "%Y-%m-%d %H:%M:%S %z"
 
1394
 * and the default time zone.
 
1395
 */
1019
1396
- (id) initWithTimeIntervalSinceReferenceDate: (NSTimeInterval)seconds
1020
1397
{
1021
1398
  _seconds_since_ref = seconds;
1022
1399
  if (_calendar_format == nil)
1023
 
    _calendar_format = @"%Y-%m-%d %H:%M:%S %z";
 
1400
    {
 
1401
      _calendar_format = cformat;
 
1402
    }
1024
1403
  if (_time_zone == nil)
1025
 
    _time_zone = RETAIN([NSTimeZone localTimeZone]);
 
1404
    {
 
1405
      _time_zone = localTZ;     // retain is a no-op for the local timezone.
 
1406
    }
1026
1407
  return self;
1027
1408
}
1028
1409
 
1029
 
// Retreiving Date Elements
1030
 
- (void) getYear: (int *)year
1031
 
           month: (int *)month
1032
 
             day: (int *)day
1033
 
            hour: (int *)hour
1034
 
          minute: (int *)minute
1035
 
          second: (int *)second
1036
 
{
1037
 
  int h, m;
1038
 
  double a, b, c, d = [self dayOfCommonEra];
1039
 
 
1040
 
  // Calculate year, month, and day
1041
 
  [self gregorianDateFromAbsolute: d day: day month: month year: year];
1042
 
 
1043
 
  // Calculate hour, minute, and seconds
1044
 
  d -= GREGORIAN_REFERENCE;
1045
 
  d *= 86400;
1046
 
  a = abs(d - (_seconds_since_ref+[_time_zone secondsFromGMTForDate: self]));
1047
 
  b = a / 3600;
1048
 
  *hour = (int)b;
1049
 
  h = *hour;
1050
 
  h = h * 3600;
1051
 
  b = a - h;
1052
 
  b = b / 60;
1053
 
  *minute = (int)b;
1054
 
  m = *minute;
1055
 
  m = m * 60;
1056
 
  c = a - h - m;
1057
 
  *second = (int)c;
1058
 
}
1059
 
 
 
1410
/**
 
1411
 * Return the day number (ie number of days since the start of) in the
 
1412
 * 'common' era of the receiving date.  The era starts at 1 A.D.
 
1413
 */
1060
1414
- (int) dayOfCommonEra
1061
1415
{
1062
 
  double a;
1063
 
  int r;
1064
 
 
1065
 
  // Get reference date in terms of days
1066
 
  a = (_seconds_since_ref+[_time_zone secondsFromGMTForDate: self]) / 86400.0;
1067
 
  // Offset by Gregorian reference
1068
 
  a += GREGORIAN_REFERENCE;
1069
 
  r = (int)a;
1070
 
 
1071
 
  return r;
 
1416
  NSTimeInterval        when;
 
1417
 
 
1418
  when = _seconds_since_ref + offset(_time_zone, self);
 
1419
  return dayOfCommonEra(when);
1072
1420
}
1073
1421
 
 
1422
/**
 
1423
 * Return the month (1 to 31) of the receiving date.
 
1424
 */
1074
1425
- (int) dayOfMonth
1075
1426
{
1076
1427
  int m, d, y;
 
1428
  NSTimeInterval        when;
1077
1429
 
1078
 
  [self gregorianDateFromAbsolute: [self dayOfCommonEra]
1079
 
        day: &d month: &m year: &y];
 
1430
  when = _seconds_since_ref + offset(_time_zone, self);
 
1431
  gregorianDateFromAbsolute(dayOfCommonEra(when), &d, &m, &y);
1080
1432
 
1081
1433
  return d;
1082
1434
}
1083
1435
 
 
1436
/**
 
1437
 * Return the day of the week (0 to 6) of the receiving date.
 
1438
 * <list>
 
1439
 *   <item>0 is sunday</item>
 
1440
 *   <item>1 is monday</item>
 
1441
 *   <item>2 is tuesday</item>
 
1442
 *   <item>3 is wednesday</item>
 
1443
 *   <item>4 is thursday</item>
 
1444
 *   <item>5 is friday</item>
 
1445
 *   <item>6 is saturday</item>
 
1446
 * </list>
 
1447
 */
1084
1448
- (int) dayOfWeek
1085
1449
{
1086
 
  int   d = [self dayOfCommonEra];
 
1450
  int   d;
 
1451
  NSTimeInterval        when;
 
1452
 
 
1453
  when = _seconds_since_ref + offset(_time_zone, self);
 
1454
  d = dayOfCommonEra(when);
1087
1455
 
1088
1456
  /* The era started on a sunday.
1089
1457
     Did we always have a seven day week?
1095
1463
  return d;
1096
1464
}
1097
1465
 
 
1466
/**
 
1467
 * Return the day of the year (1 to 366) of the receiving date.
 
1468
 */
1098
1469
- (int) dayOfYear
1099
1470
{
1100
1471
  int m, d, y, days, i;
 
1472
  NSTimeInterval        when;
1101
1473
 
1102
 
  [self gregorianDateFromAbsolute: [self dayOfCommonEra]
1103
 
        day: &d month: &m year: &y];
 
1474
  when = _seconds_since_ref + offset(_time_zone, self);
 
1475
  gregorianDateFromAbsolute(dayOfCommonEra(when), &d, &m, &y);
1104
1476
  days = d;
1105
1477
  for (i = m - 1;  i > 0; i--) // days in prior months this year
1106
1478
    days = days + lastDayOfGregorianMonth(i, y);
1108
1480
  return days;
1109
1481
}
1110
1482
 
 
1483
/**
 
1484
 * Return the hour of the day (0 to 23) of the receiving date.
 
1485
 */
1111
1486
- (int) hourOfDay
1112
1487
{
1113
1488
  int h;
1114
 
  double a, d = [self dayOfCommonEra];
 
1489
  double a, d;
 
1490
  NSTimeInterval        when;
 
1491
 
 
1492
  when = _seconds_since_ref + offset(_time_zone, self);
 
1493
  d = dayOfCommonEra(when);
1115
1494
  d -= GREGORIAN_REFERENCE;
1116
1495
  d *= 86400;
1117
 
  a = abs(d - (_seconds_since_ref+[_time_zone secondsFromGMTForDate: self]));
 
1496
  a = abs(d - (_seconds_since_ref + offset(_time_zone, self)));
1118
1497
  a = a / 3600;
1119
1498
  h = (int)a;
1120
1499
 
1126
1505
  return h;
1127
1506
}
1128
1507
 
 
1508
/**
 
1509
 * Return the minute of the hour (0 to 59) of the receiving date.
 
1510
 */
1129
1511
- (int) minuteOfHour
1130
1512
{
1131
1513
  int h, m;
1132
 
  double a, b, d = [self dayOfCommonEra];
 
1514
  double a, b, d;
 
1515
  NSTimeInterval        when;
 
1516
 
 
1517
  when = _seconds_since_ref + offset(_time_zone, self);
 
1518
  d = dayOfCommonEra(when);
1133
1519
  d -= GREGORIAN_REFERENCE;
1134
1520
  d *= 86400;
1135
 
  a = abs(d - (_seconds_since_ref+[_time_zone secondsFromGMTForDate: self]));
 
1521
  a = abs(d - (_seconds_since_ref + offset(_time_zone, self)));
1136
1522
  b = a / 3600;
1137
1523
  h = (int)b;
1138
1524
  h = h * 3600;
1143
1529
  return m;
1144
1530
}
1145
1531
 
 
1532
/**
 
1533
 * Return the month of the year (1 to 12) of the receiving date.
 
1534
 */
1146
1535
- (int) monthOfYear
1147
1536
{
1148
1537
  int m, d, y;
 
1538
  NSTimeInterval        when;
1149
1539
 
1150
 
  [self gregorianDateFromAbsolute: [self dayOfCommonEra]
1151
 
        day: &d month: &m year: &y];
 
1540
  when = _seconds_since_ref + offset(_time_zone, self);
 
1541
  gregorianDateFromAbsolute(dayOfCommonEra(when), &d, &m, &y);
1152
1542
 
1153
1543
  return m;
1154
1544
}
1155
1545
 
 
1546
/**
 
1547
 * Return the second of the minute (0 to 59) of the receiving date.
 
1548
 */
1156
1549
- (int) secondOfMinute
1157
1550
{
1158
1551
  int h, m, s;
1159
 
  double a, b, c, d = [self dayOfCommonEra];
 
1552
  double a, b, c, d;
 
1553
  NSTimeInterval        when;
 
1554
 
 
1555
  when = _seconds_since_ref + offset(_time_zone, self);
 
1556
  d = dayOfCommonEra(when);
1160
1557
  d -= GREGORIAN_REFERENCE;
1161
1558
  d *= 86400;
1162
 
  a = abs(d - (_seconds_since_ref+[_time_zone secondsFromGMTForDate: self]));
 
1559
  a = abs(d - (_seconds_since_ref + offset(_time_zone, self)));
1163
1560
  b = a / 3600;
1164
1561
  h = (int)b;
1165
1562
  h = h * 3600;
1173
1570
  return s;
1174
1571
}
1175
1572
 
 
1573
/**
 
1574
 * Return the year of the 'common' era of the receiving date.
 
1575
 * The era starts at 1 A.D.
 
1576
 */
1176
1577
- (int) yearOfCommonEra
1177
1578
{
1178
1579
  int m, d, y;
 
1580
  NSTimeInterval        when;
1179
1581
 
1180
 
  [self gregorianDateFromAbsolute: [self dayOfCommonEra]
1181
 
        day: &d month: &m year: &y];
 
1582
  when = _seconds_since_ref + offset(_time_zone, self);
 
1583
  gregorianDateFromAbsolute(dayOfCommonEra(when), &d, &m, &y);
1182
1584
 
1183
1585
  return y;
1184
1586
}
1185
1587
 
1186
 
// Providing Adjusted Dates
 
1588
/**
 
1589
 * This method exists solely for conformance to the OpenStep spec.
 
1590
 * Its use is deprecated ... it simply calls
 
1591
 * -dateByAddingYears:months:days:hours:minutes:seconds:
 
1592
 */
1187
1593
- (NSCalendarDate*) addYear: (int)year
1188
1594
                      month: (int)month
1189
1595
                        day: (int)day
1199
1605
                         seconds: second];
1200
1606
}
1201
1607
 
1202
 
// Getting String Descriptions of Dates
 
1608
/**
 
1609
 * Calls -descriptionWithCalendarFormat:locale: passing the receviers
 
1610
 * calendar format and a nil locale.
 
1611
 */
1203
1612
- (NSString*) description
1204
1613
{
1205
1614
  return [self descriptionWithCalendarFormat: _calendar_format locale: nil];
1206
1615
}
1207
1616
 
 
1617
/**
 
1618
 * Returns a string representation of the receiver using the specified
 
1619
 * format string.<br />
 
1620
 * Calls -descriptionWithCalendarFormat:locale: with a nil locale.
 
1621
 */
1208
1622
- (NSString*) descriptionWithCalendarFormat: (NSString *)format
1209
1623
{
1210
1624
  return [self descriptionWithCalendarFormat: format locale: nil];
1211
1625
}
1212
1626
 
1213
1627
#define UNIX_REFERENCE_INTERVAL -978307200.0
1214
 
- (NSString *)descriptionWithCalendarFormat: (NSString *)format
1215
 
                                     locale: (NSDictionary *)locale
1216
 
{
1217
 
  char buf[1024];
1218
 
  const char *f;
1219
 
  int lf;
1220
 
  BOOL mtag = NO, dtag = NO, ycent = NO;
1221
 
  BOOL mname = NO, dname = NO;
1222
 
  double s;
1223
 
  int yd = 0, md = 0, mnd = 0, sd = 0, dom = -1, dow = -1, doy = -1;
1224
 
  int hd = 0, nhd;
1225
 
  int i, j, k, z;
1226
 
 
1227
 
  if (locale == nil)
1228
 
    locale = GSUserDefaultsDictionaryRepresentation();
1229
 
  if (format == nil)
1230
 
    format = [locale objectForKey: NSTimeDateFormatString];
1231
 
 
1232
 
  // If the format is nil then return an empty string
1233
 
  if (!format)
1234
 
    return @"";
1235
 
 
1236
 
  f = [format cString];
1237
 
  lf = strlen(f);
1238
 
 
1239
 
  [self getYear: &yd month: &md day: &dom hour: &hd minute: &mnd second: &sd];
1240
 
  nhd = hd;
1241
 
 
1242
 
  // The strftime specifiers
1243
 
  // %a   abbreviated weekday name according to locale
1244
 
  // %A   full weekday name according to locale
1245
 
  // %b   abbreviated month name according to locale
1246
 
  // %B   full month name according to locale
1247
 
  // %d   day of month as decimal number (leading zero)
1248
 
  // %e   day of month as decimal number (leading space)
1249
 
  // %F   milliseconds (000 to 999)
1250
 
  // %H   hour as a decimal number using 24-hour clock
1251
 
  // %I   hour as a decimal number using 12-hour clock
1252
 
  // %j   day of year as a decimal number
1253
 
  // %m   month as decimal number
1254
 
  // %M   minute as decimal number
1255
 
  // %p   'am' or 'pm'
1256
 
  // %S   second as decimal number
1257
 
  // %U   week of the current year as decimal number (Sunday first day)
1258
 
  // %W   week of the current year as decimal number (Monday first day)
1259
 
  // %w   day of the week as decimal number (Sunday = 0)
1260
 
  // %y   year as a decimal number without century
1261
 
  // %Y   year as a decimal number with century
1262
 
  // %z   time zone offset (HHMM)
1263
 
  // %Z   time zone
1264
 
  // %%   literal % character
1265
 
 
1266
 
  // Find the order of date elements
1267
 
  // and translate format string into printf ready string
1268
 
  j = 0;
1269
 
  for (i = 0;i < lf; ++i)
1270
 
    {
 
1628
 
 
1629
typedef struct {
 
1630
  unichar       *base;
 
1631
  unichar       *t;
 
1632
  unsigned      length;
 
1633
  unsigned      offset;
 
1634
  int           yd;
 
1635
  int           md;
 
1636
  int           dom;
 
1637
  int           hd;
 
1638
  int           mnd;
 
1639
  int           sd;
 
1640
  int           mil;
 
1641
} DescriptionInfo;
 
1642
 
 
1643
static void Grow(DescriptionInfo *info, unsigned size)
 
1644
{
 
1645
  if (info->offset + size >= info->length)
 
1646
    {
 
1647
      if (info->t == info->base)
 
1648
        {
 
1649
          unichar       *old = info->t;
 
1650
 
 
1651
          info->t = NSZoneMalloc(NSDefaultMallocZone(),
 
1652
            (info->length + 512) * sizeof(unichar));
 
1653
          memcpy(info->t, old, info->length*sizeof(unichar));
 
1654
        }
 
1655
      else
 
1656
        {
 
1657
          info->t = NSZoneRealloc(NSDefaultMallocZone(), info->t,
 
1658
            (info->length + 512) * sizeof(unichar));
 
1659
        }
 
1660
      info->length += 512;
 
1661
    }
 
1662
}
 
1663
 
 
1664
- (void) _format: (NSString*)fmt
 
1665
          locale: (NSDictionary*)locale
 
1666
            info: (DescriptionInfo*)info
 
1667
{
 
1668
  unichar       fbuf[512];
 
1669
  unichar       *f = fbuf;
 
1670
  unsigned      lf = [fmt length];
 
1671
  unsigned      i = 0;
 
1672
  int           v;
 
1673
 
 
1674
  if (lf == 0)
 
1675
    {
 
1676
      return;   // Nothing to do.
 
1677
    }
 
1678
  if (lf >= sizeof(fbuf)/sizeof(unichar))
 
1679
    {
 
1680
      /*
 
1681
       * Make temporary buffer to hold format string as unicode.
 
1682
       */
 
1683
      f = (unichar*)NSZoneMalloc(NSDefaultMallocZone(), lf*sizeof(unichar));
 
1684
    }
 
1685
  [fmt getCharacters: f];
 
1686
 
 
1687
  while (i < lf)
 
1688
    {
 
1689
      BOOL      mtag = NO;
 
1690
      BOOL      dtag = NO;
 
1691
      BOOL      ycent = NO;
 
1692
      BOOL      mname = NO;
 
1693
      BOOL      dname = NO;
 
1694
      BOOL      twelve = NO;
 
1695
 
1271
1696
      // Only care about a format specifier
1272
1697
      if (f[i] == '%')
1273
1698
        {
 
1699
          i++;
1274
1700
          // check the character that comes after
1275
 
          switch (f[i+1])
 
1701
          switch (f[i++])
1276
1702
            {
1277
 
              // literal %
1278
 
            case '%':
1279
 
              ++i;
1280
 
              buf[j] = f[i];
1281
 
              ++j;
1282
 
              break;
1283
 
 
1284
 
              // is it the year
1285
 
            case 'Y':
1286
 
              ycent = YES;
1287
 
            case 'y':
1288
 
              ++i;
1289
 
              if (ycent)
1290
 
                k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%04d", yd));
1291
 
              else
1292
 
                k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%02d", yd % 100));
1293
 
              j += k;
1294
 
              break;
1295
 
 
1296
 
              // is it the month
1297
 
            case 'b':
1298
 
              mname = YES;
1299
 
            case 'B':
1300
 
              mtag = YES;    // Month is character string
1301
 
            case 'm':
1302
 
              ++i;
1303
 
              if (mtag)
1304
 
                {
1305
 
                  NSArray       *months;
1306
 
                  NSString      *name;
1307
 
 
1308
 
                  if (mname)
1309
 
                    months = [locale objectForKey: NSShortMonthNameArray];
1310
 
                  else
1311
 
                    months = [locale objectForKey: NSMonthNameArray];
1312
 
                  name = [months objectAtIndex: md-1];
1313
 
                  if (name)
1314
 
                    k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%s",
1315
 
                      [name cString]));
1316
 
                  else
1317
 
                    k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%02d", md));
1318
 
                }
1319
 
              else
1320
 
                k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%02d", md));
1321
 
              j += k;
1322
 
              break;
1323
 
 
1324
 
            case 'd':   // day of month
1325
 
              ++i;
1326
 
              k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%02d", dom));
1327
 
              j += k;
1328
 
              break;
1329
 
 
1330
 
            case 'e':   // day of month
1331
 
              ++i;
1332
 
              k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%2d", dom));
1333
 
              j += k;
1334
 
              break;
1335
 
 
1336
 
            case 'F':   // milliseconds
1337
 
              s = ([self dayOfCommonEra] - GREGORIAN_REFERENCE) * 86400.0;
1338
 
              s -= (_seconds_since_ref
1339
 
                + [_time_zone secondsFromGMTForDate: self]);
1340
 
              s = fabs(s);
1341
 
              s -= floor(s);
1342
 
              ++i;
1343
 
              k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%03d", (int)(s*1000)));
1344
 
              j += k;
1345
 
              break;
1346
 
 
1347
 
            case 'j':   // day of year
1348
 
              if (doy < 0) doy = [self dayOfYear];
1349
 
              ++i;
1350
 
              k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%02d", doy));
1351
 
              j += k;
1352
 
              break;
1353
 
 
1354
 
              // is it the week-day
1355
 
            case 'a':
1356
 
              dname = YES;
1357
 
            case 'A':
1358
 
              dtag = YES;   // Day is character string
1359
 
            case 'w':
1360
 
              {
1361
 
                ++i;
1362
 
                if (dow < 0) dow = [self dayOfWeek];
1363
 
                if (dtag)
1364
 
                  {
1365
 
                    NSArray     *days;
1366
 
                    NSString    *name;
1367
 
 
1368
 
                    if (dname)
1369
 
                      days = [locale objectForKey: NSShortWeekDayNameArray];
1370
 
                    else
1371
 
                      days = [locale objectForKey: NSWeekDayNameArray];
1372
 
                    name = [days objectAtIndex: dow];
1373
 
                    if (name)
1374
 
                      k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%s",
1375
 
                        [name cString]));
1376
 
                    else
1377
 
                      k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%01d", dow));
1378
 
                  }
1379
 
                else
1380
 
                  k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%01d", dow));
1381
 
                j += k;
1382
 
              }
1383
 
              break;
1384
 
 
1385
 
              // is it the hour
1386
 
            case 'I':
1387
 
              nhd = hd % 12;  // 12 hour clock
1388
 
              if (hd == 12)
1389
 
                nhd = 12;     // 12pm not 0pm
1390
 
            case 'H':
1391
 
              ++i;
1392
 
              k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%02d", nhd));
1393
 
              j += k;
1394
 
              break;
1395
 
 
1396
 
              // is it the minute
1397
 
            case 'M':
1398
 
              ++i;
1399
 
              k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%02d", mnd));
1400
 
              j += k;
1401
 
              break;
1402
 
 
1403
 
              // is it the second
1404
 
            case 'S':
1405
 
              ++i;
1406
 
              k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%02d", sd));
1407
 
              j += k;
1408
 
              break;
1409
 
 
1410
 
              // Is it the am/pm indicator
1411
 
            case 'p':
1412
 
              {
1413
 
                NSArray         *a = [locale objectForKey: NSAMPMDesignation];
1414
 
                NSString        *ampm;
1415
 
 
1416
 
                ++i;
1417
 
                if (hd >= 12)
1418
 
                  {
1419
 
                    if ([a count] > 1)
1420
 
                      ampm = [a objectAtIndex: 1];
1421
 
                    else
1422
 
                      ampm = @"pm";
1423
 
                  }
1424
 
                else
1425
 
                  {
1426
 
                    if ([a count] > 0)
1427
 
                      ampm = [a objectAtIndex: 0];
1428
 
                    else
1429
 
                      ampm = @"am";
1430
 
                  }
1431
 
                k = VSPRINTF_LENGTH(sprintf(&(buf[j]), [ampm cString]));
1432
 
                j += k;
1433
 
              }
1434
 
              break;
1435
 
 
1436
 
              // is it the zone name
1437
 
            case 'Z':
1438
 
              ++i;
1439
 
              k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%s",
1440
 
                          [[_time_zone abbreviationForDate: self] cString]));
1441
 
              j += k;
1442
 
              break;
1443
 
 
1444
 
            case 'z':
1445
 
              ++i;
1446
 
              z = [_time_zone secondsFromGMTForDate: self];
1447
 
              if (z < 0) {
1448
 
                z = -z;
1449
 
                z /= 60;
1450
 
                k = VSPRINTF_LENGTH(sprintf(&(buf[j]),"-%02d%02d",z/60,z%60));
1451
 
              }
1452
 
              else {
1453
 
                z /= 60;
1454
 
                k = VSPRINTF_LENGTH(sprintf(&(buf[j]),"+%02d%02d",z/60,z%60));
1455
 
              }
1456
 
              j += k;
1457
 
              break;
1458
 
 
1459
 
              // Anything else is unknown so just copy
1460
 
            default:
1461
 
              buf[j] = f[i];
1462
 
              ++i;
1463
 
              ++j;
1464
 
              buf[j] = f[i];
1465
 
              ++i;
1466
 
              ++j;
1467
 
              break;
 
1703
                // literal %
 
1704
              case '%':
 
1705
                Grow(info, 1);
 
1706
                info->t[info->offset++] = f[i-1];
 
1707
                break;
 
1708
 
 
1709
              case 'R':
 
1710
                [self _format: @"%H:%M" locale: locale info: info];
 
1711
                break;
 
1712
 
 
1713
              case 'r':
 
1714
                [self _format: @"%I:%M:%S %p" locale: locale info: info];
 
1715
                break;
 
1716
 
 
1717
              case 'c':
 
1718
                [self _format: [locale objectForKey: NSTimeFormatString]
 
1719
                       locale: locale
 
1720
                         info: info];
 
1721
                Grow(info, 1);
 
1722
                info->t[info->offset++] = ' ';
 
1723
                [self _format: [locale objectForKey: NSDateFormatString]
 
1724
                       locale: locale
 
1725
                         info: info];
 
1726
                break;
 
1727
 
 
1728
              case 'X':
 
1729
                [self _format: [locale objectForKey: NSTimeFormatString]
 
1730
                       locale: locale
 
1731
                         info: info];
 
1732
                break;
 
1733
 
 
1734
              case 'x':
 
1735
                [self _format: [locale objectForKey: NSDateFormatString]
 
1736
                       locale: locale
 
1737
                         info: info];
 
1738
                break;
 
1739
 
 
1740
                // is it the year
 
1741
              case 'Y':
 
1742
                ycent = YES;
 
1743
              case 'y':
 
1744
                v = info->yd;
 
1745
                if (v < 0)
 
1746
                  v = 0;
 
1747
                if (v > 9999)
 
1748
                  v = 9999;
 
1749
                if (ycent)
 
1750
                  {
 
1751
                    Grow(info, 4);
 
1752
                    info->t[info->offset+3] = (v%10) + '0';
 
1753
                    v /= 10;
 
1754
                    info->t[info->offset+2] = (v%10) + '0';
 
1755
                    v /= 10;
 
1756
                    info->t[info->offset+1] = (v%10) + '0';
 
1757
                    v /= 10;
 
1758
                    info->t[info->offset+0] = (v%10) + '0';
 
1759
                    info->offset += 4;
 
1760
                  }
 
1761
                else
 
1762
                  {
 
1763
                    Grow(info, 2);
 
1764
                    v = v % 100;
 
1765
                    info->t[info->offset+1] = (v%10) + '0';
 
1766
                    v /= 10;
 
1767
                    info->t[info->offset+0] = (v%10) + '0';
 
1768
                    info->offset += 2;
 
1769
                  }
 
1770
                break;
 
1771
 
 
1772
                // is it the month
 
1773
              case 'b':
 
1774
                mname = YES;
 
1775
              case 'B':
 
1776
                mtag = YES;    // Month is character string
 
1777
              case 'm':
 
1778
                if (mtag == YES)
 
1779
                  {
 
1780
                    NSArray     *months;
 
1781
 
 
1782
                    if (mname)
 
1783
                      months = [locale objectForKey: NSShortMonthNameArray];
 
1784
                    else
 
1785
                      months = [locale objectForKey: NSMonthNameArray];
 
1786
                    if (info->md > [months count])
 
1787
                      {
 
1788
                        mtag = NO;
 
1789
                      }
 
1790
                    else
 
1791
                      {
 
1792
                        NSString        *name;
 
1793
                
 
1794
                        name = [months objectAtIndex: info->md-1];
 
1795
                        v = [name length];
 
1796
                        Grow(info, v);
 
1797
                        [name getCharacters: info->t + info->offset];
 
1798
                        info->offset += v;
 
1799
                      }
 
1800
                  }
 
1801
                if (mtag == NO)
 
1802
                  {
 
1803
                    v = info->md;
 
1804
                    Grow(info, 2);
 
1805
                    v = v % 100;
 
1806
                    info->t[info->offset+1] = (v%10) + '0';
 
1807
                    v /= 10;
 
1808
                    info->t[info->offset+0] = (v%10) + '0';
 
1809
                    info->offset += 2;
 
1810
                  }
 
1811
                break;
 
1812
 
 
1813
              case 'd':         // day of month with leading zero
 
1814
                v = info->dom;
 
1815
                Grow(info, 2);
 
1816
                v = v % 100;
 
1817
                info->t[info->offset+1] = (v%10) + '0';
 
1818
                v /= 10;
 
1819
                info->t[info->offset+0] = (v%10) + '0';
 
1820
                info->offset += 2;
 
1821
                break;
 
1822
 
 
1823
              case 'e':         // day of month with leading space
 
1824
                v = info->dom;
 
1825
                Grow(info, 2);
 
1826
                v = v % 100;
 
1827
                if (v%10 == '0')
 
1828
                  {
 
1829
                    info->t[info->offset+1] = ' ';
 
1830
                  }
 
1831
                else
 
1832
                  {
 
1833
                    info->t[info->offset+1] = (v%10) + '0';
 
1834
                  }
 
1835
                v /= 10;
 
1836
                info->t[info->offset+0] = (v%10) + '0';
 
1837
                info->offset += 2;
 
1838
                break;
 
1839
 
 
1840
              case 'F':         // milliseconds
 
1841
                {
 
1842
                  double        s;
 
1843
 
 
1844
                  s = ([self dayOfCommonEra] - GREGORIAN_REFERENCE) * 86400.0;
 
1845
                  s -= (_seconds_since_ref + offset(_time_zone, self));
 
1846
                  s = fabs(s);
 
1847
                  s -= floor(s);
 
1848
                  v = (int)s;
 
1849
                }
 
1850
                Grow(info, 3);
 
1851
                info->t[info->offset+2] = (v%10) + '0';
 
1852
                v /= 10;
 
1853
                info->t[info->offset+1] = (v%10) + '0';
 
1854
                v /= 10;
 
1855
                info->t[info->offset+0] = (v%10) + '0';
 
1856
                info->offset += 3;
 
1857
                break;
 
1858
 
 
1859
              case 'j':         // day of year
 
1860
                v = [self dayOfYear];
 
1861
                Grow(info, 3);
 
1862
                info->t[info->offset+2] = (v%10) + '0';
 
1863
                v /= 10;
 
1864
                info->t[info->offset+1] = (v%10) + '0';
 
1865
                v /= 10;
 
1866
                info->t[info->offset+0] = (v%10) + '0';
 
1867
                info->offset += 3;
 
1868
                break;
 
1869
 
 
1870
                // is it the week-day
 
1871
              case 'a':
 
1872
                dname = YES;
 
1873
              case 'A':
 
1874
                dtag = YES;   // Day is character string
 
1875
              case 'w':
 
1876
                {
 
1877
                  v = [self dayOfWeek];
 
1878
                  if (dtag == YES)
 
1879
                    {
 
1880
                      NSArray   *days;
 
1881
 
 
1882
                      if (dname)
 
1883
                        days = [locale objectForKey: NSShortWeekDayNameArray];
 
1884
                      else
 
1885
                        days = [locale objectForKey: NSWeekDayNameArray];
 
1886
                      if (v < [days count])
 
1887
                        {
 
1888
                          NSString      *name;
 
1889
 
 
1890
                          name = [days objectAtIndex: v];
 
1891
                          v = [name length];
 
1892
                          Grow(info, v);
 
1893
                          [name getCharacters: info->t + info->offset];
 
1894
                          info->offset += v;
 
1895
                        }
 
1896
                      else
 
1897
                        {
 
1898
                          dtag = NO;
 
1899
                        }
 
1900
                    }
 
1901
                  if (dtag == NO)
 
1902
                    {
 
1903
                      info->t[info->offset+0] = (v%10) + '0';
 
1904
                      info->offset += 1;
 
1905
                    }
 
1906
                }
 
1907
                break;
 
1908
 
 
1909
                // is it the hour
 
1910
              case 'I':
 
1911
                twelve = YES;
 
1912
              case 'H':
 
1913
                v = info->hd;
 
1914
                if (twelve == YES)
 
1915
                  {
 
1916
                    if (info->hd == 12)
 
1917
                      {
 
1918
                        v = 12;
 
1919
                      }
 
1920
                    else
 
1921
                      {
 
1922
                        v = v % 12;
 
1923
                      }
 
1924
                  }
 
1925
                Grow(info, 2);
 
1926
                info->t[info->offset+1] = (v%10) + '0';
 
1927
                v /= 10;
 
1928
                info->t[info->offset+0] = (v%10) + '0';
 
1929
                info->offset += 2;
 
1930
                break;
 
1931
 
 
1932
                // is it the minute
 
1933
              case 'M':
 
1934
                v = info->mnd;
 
1935
                Grow(info, 2);
 
1936
                info->t[info->offset+1] = (v%10) + '0';
 
1937
                v /= 10;
 
1938
                info->t[info->offset+0] = (v%10) + '0';
 
1939
                info->offset += 2;
 
1940
                break;
 
1941
 
 
1942
                // is it the second
 
1943
              case 'S':
 
1944
                v = info->sd;
 
1945
                Grow(info, 2);
 
1946
                info->t[info->offset+1] = (v%10) + '0';
 
1947
                v /= 10;
 
1948
                info->t[info->offset+0] = (v%10) + '0';
 
1949
                info->offset += 2;
 
1950
                break;
 
1951
 
 
1952
                // Is it the am/pm indicator
 
1953
              case 'p':
 
1954
                {
 
1955
                  NSArray       *a = [locale objectForKey: NSAMPMDesignation];
 
1956
                  NSString      *ampm;
 
1957
 
 
1958
                  if (info->hd >= 12)
 
1959
                    {
 
1960
                      if ([a count] > 1)
 
1961
                        ampm = [a objectAtIndex: 1];
 
1962
                      else
 
1963
                        ampm = @"pm";
 
1964
                    }
 
1965
                  else
 
1966
                    {
 
1967
                      if ([a count] > 0)
 
1968
                        ampm = [a objectAtIndex: 0];
 
1969
                      else
 
1970
                        ampm = @"am";
 
1971
                    }
 
1972
                  v = [ampm length];
 
1973
                  Grow(info, v);
 
1974
                  [ampm getCharacters: info->t + info->offset];
 
1975
                  info->offset += v;
 
1976
                }
 
1977
                break;
 
1978
 
 
1979
                // is it the zone name
 
1980
              case 'Z':
 
1981
                {
 
1982
                  NSString      *s;
 
1983
 
 
1984
                  s = abbrev(_time_zone, self);
 
1985
                  v = [s length];
 
1986
                  Grow(info, v);
 
1987
                  [s getCharacters: info->t + info->offset];
 
1988
                  info->offset += v;
 
1989
                }
 
1990
                break;
 
1991
 
 
1992
              case 'z':
 
1993
                {
 
1994
                  int   z;
 
1995
 
 
1996
                  Grow(info, 5);
 
1997
                  z = offset(_time_zone, self);
 
1998
                  if (z < 0)
 
1999
                    {
 
2000
                      z = -z;
 
2001
                      info->t[info->offset++] = '-';
 
2002
                    }
 
2003
                  else
 
2004
                    {
 
2005
                      info->t[info->offset++] = '+';
 
2006
                    }
 
2007
                  z /= 60;      // Convert seconds to minutes.
 
2008
                  v = z / 60;
 
2009
                  info->t[info->offset+1] = (v%10) + '0';
 
2010
                  v /= 10;
 
2011
                  info->t[info->offset+0] = (v%10) + '0';
 
2012
                  info->offset += 2;
 
2013
                  v = z % 60;
 
2014
                  info->t[info->offset+1] = (v%10) + '0';
 
2015
                  v /= 10;
 
2016
                  info->t[info->offset+0] = (v%10) + '0';
 
2017
                  info->offset += 2;
 
2018
                }
 
2019
                break;
 
2020
 
 
2021
                // Anything else is unknown so just copy
 
2022
              default:
 
2023
                Grow(info, 2);
 
2024
                info->t[info->offset++] = '%';
 
2025
                info->t[info->offset++] = f[i-1];
 
2026
                break;
1468
2027
            }
1469
2028
        }
1470
2029
      else
1471
2030
        {
1472
 
          buf[j] = f[i];
1473
 
          ++j;
 
2031
          Grow(info, 1);
 
2032
          info->t[info->offset++] = f[i++];
1474
2033
        }
1475
2034
    }
1476
 
  buf[j] = '\0';
1477
 
 
1478
 
  return [NSString stringWithCString: buf];
 
2035
 
 
2036
  if (f != fbuf)
 
2037
    {
 
2038
      NSZoneFree(NSDefaultMallocZone(), f);
 
2039
    }
 
2040
}
 
2041
 
 
2042
/**
 
2043
 * Returns a string representation of the receiver using the specified
 
2044
 * format string and locale dictionary.<br />
 
2045
 * Format specifiers are -
 
2046
 * <list>
 
2047
 *   <item>
 
2048
 *     %a   abbreviated weekday name according to locale
 
2049
 *   </item>
 
2050
 *   <item>
 
2051
 *     %A   full weekday name according to locale
 
2052
 *   </item>
 
2053
 *   <item>
 
2054
 *     %b   abbreviated month name according to locale
 
2055
 *   </item>
 
2056
 *   <item>
 
2057
 *     %c   this is the same as %X %x
 
2058
 *   </item>
 
2059
 *   <item>
 
2060
 *     %B   full month name according to locale
 
2061
 *   </item>
 
2062
 *   <item>
 
2063
 *     %d   day of month as decimal number (leading zero)
 
2064
 *   </item>
 
2065
 *   <item>
 
2066
 *     %e   day of month as decimal number (leading space)
 
2067
 *   </item>
 
2068
 *   <item>
 
2069
 *     %F   milliseconds (000 to 999)
 
2070
 *   </item>
 
2071
 *   <item>
 
2072
 *     %H   hour as a decimal number using 24-hour clock
 
2073
 *   </item>
 
2074
 *   <item>
 
2075
 *     %I   hour as a decimal number using 12-hour clock
 
2076
 *   </item>
 
2077
 *   <item>
 
2078
 *     %j   day of year as a decimal number
 
2079
 *   </item>
 
2080
 *   <item>
 
2081
 *     %m   month as decimal number
 
2082
 *   </item>
 
2083
 *   <item>
 
2084
 *     %M   minute as decimal number
 
2085
 *   </item>
 
2086
 *   <item>
 
2087
 *     %p   'am' or 'pm'
 
2088
 *   </item>
 
2089
 *   <item>
 
2090
 *     %S   second as decimal number
 
2091
 *   </item>
 
2092
 *   <item>
 
2093
 *     %U   week of the current year as decimal number (Sunday first day)
 
2094
 *   </item>
 
2095
 *   <item>
 
2096
 *     %W   week of the current year as decimal number (Monday first day)
 
2097
 *   </item>
 
2098
 *   <item>
 
2099
 *     %w   day of the week as decimal number (Sunday = 0)
 
2100
 *   </item>
 
2101
 *   <item>
 
2102
 *     %x   date formatted according to the locale
 
2103
 *   </item>
 
2104
 *   <item>
 
2105
 *     %X   time formatted according to the locale
 
2106
 *   </item>
 
2107
 *   <item>
 
2108
 *     %y   year as a decimal number without century (minimum 0)
 
2109
 *   </item>
 
2110
 *   <item>
 
2111
 *     %Y   year as a decimal number with century, minimum 0, maximum 9999
 
2112
 *   </item>
 
2113
 *   <item>
 
2114
 *     %z   time zone offset (HHMM)
 
2115
 *   </item>
 
2116
 *   <item>
 
2117
 *     %Z   time zone
 
2118
 *   </item>
 
2119
 *   <item>
 
2120
 *     %%   literal % character
 
2121
 *   </item>
 
2122
 * </list>
 
2123
 */
 
2124
- (NSString*) descriptionWithCalendarFormat: (NSString*)format
 
2125
                                     locale: (NSDictionary*)locale
 
2126
{
 
2127
  unichar               tbuf[512];
 
2128
  NSString              *result;
 
2129
  DescriptionInfo       info;
 
2130
 
 
2131
  if (locale == nil)
 
2132
    locale = GSUserDefaultsDictionaryRepresentation();
 
2133
  if (format == nil)
 
2134
    format = [locale objectForKey: NSTimeDateFormatString];
 
2135
 
 
2136
  GSBreakTime(_seconds_since_ref + offset(_time_zone, self),
 
2137
    &info.yd, &info.md, &info.dom, &info.hd, &info.mnd, &info.sd, &info.mil);
 
2138
 
 
2139
  info.base = tbuf;
 
2140
  info.t = tbuf;
 
2141
  info.length = sizeof(tbuf)/sizeof(unichar);
 
2142
  info.offset = 0;
 
2143
 
 
2144
  [self _format: format locale: locale info: &info];
 
2145
 
 
2146
  result = [NSString stringWithCharacters: info.t length: info.offset];
 
2147
 
 
2148
  if (info.t != tbuf)
 
2149
    {
 
2150
      NSZoneFree(NSDefaultMallocZone(), info.t);
 
2151
    }
 
2152
 
 
2153
  return result;
1479
2154
}
1480
2155
 
1481
2156
- (id) copyWithZone: (NSZone*)zone
1490
2165
    {
1491
2166
      newDate = (NSCalendarDate*)NSCopyObject(self, 0, zone);
1492
2167
 
1493
 
      if (newDate)
 
2168
      if (newDate != nil)
1494
2169
        {
1495
 
          newDate->_calendar_format = [_calendar_format copyWithZone: zone];
1496
 
          newDate->_time_zone = RETAIN(_time_zone);
 
2170
          if (_calendar_format != cformat)
 
2171
            {
 
2172
              newDate->_calendar_format = [_calendar_format copyWithZone: zone];
 
2173
            }
 
2174
          if (_time_zone != localTZ)
 
2175
            {
 
2176
              newDate->_time_zone = RETAIN(_time_zone);
 
2177
            }
1497
2178
        }
1498
2179
    }
1499
2180
  return newDate;
1500
2181
}
1501
2182
 
 
2183
/**
 
2184
 * Returns a description of the receiver using its normal format but with
 
2185
 * the specified locale dictionary.<br />
 
2186
 * Calls -descriptionWithCalendarFormat:locale: to do this.
 
2187
 */
1502
2188
- (NSString*) descriptionWithLocale: (NSDictionary *)locale
1503
2189
{
1504
2190
  return [self descriptionWithCalendarFormat: _calendar_format locale: locale];
1505
2191
}
1506
2192
 
1507
 
// Getting and Setting Calendar Formats
 
2193
/**
 
2194
 * Returns the format string associated with the receiver.<br />
 
2195
 * See -descriptionWithCalendarFormat:locale: for details.
 
2196
 */
1508
2197
- (NSString*) calendarFormat
1509
2198
{
1510
2199
  return _calendar_format;
1511
2200
}
1512
2201
 
 
2202
/**
 
2203
 * Sets the format string associated with the receiver.<br />
 
2204
 * Providing a nil argument sets the default calendar format.<br />
 
2205
 * See -descriptionWithCalendarFormat:locale: for details.
 
2206
 */
1513
2207
- (void) setCalendarFormat: (NSString *)format
1514
2208
{
1515
 
  RELEASE(_calendar_format);
1516
 
  _calendar_format = [format copyWithZone: [self zone]];
 
2209
  if (format == nil)
 
2210
    {
 
2211
      format = cformat;
 
2212
    }
 
2213
  ASSIGNCOPY(_calendar_format, format);
1517
2214
}
1518
2215
 
1519
 
// Getting and Setting Time Zones
 
2216
/**
 
2217
 * Sets the time zone associated with the receiver.<br />
 
2218
 * Providing a nil argument sets the local time zone.
 
2219
 */
1520
2220
- (void) setTimeZone: (NSTimeZone *)aTimeZone
1521
2221
{
 
2222
  if (aTimeZone == nil)
 
2223
    {
 
2224
      aTimeZone = localTZ;
 
2225
    }
1522
2226
  ASSIGN(_time_zone, aTimeZone);
1523
2227
}
1524
2228
 
 
2229
/**
 
2230
 * Returns the time zone associated with the receiver.
 
2231
 */
1525
2232
- (NSTimeZone*) timeZone
1526
2233
{
1527
2234
  return _time_zone;
1528
2235
}
1529
2236
 
 
2237
/**
 
2238
 * Returns the time zone detail associated with the receiver.
 
2239
 */
1530
2240
- (NSTimeZoneDetail*) timeZoneDetail
1531
2241
{
1532
2242
  NSTimeZoneDetail      *detail = [_time_zone timeZoneDetailForDate: self];
1535
2245
 
1536
2246
@end
1537
2247
 
1538
 
//
1539
 
// Routines for manipulating Gregorian dates
1540
 
//
 
2248
/**
 
2249
 * Routines for manipulating Gregorian dates.
 
2250
 */
1541
2251
// The following code is based upon the source code in
1542
2252
// ``Calendrical Calculations'' by Nachum Dershowitz and Edward M. Reingold,
1543
2253
// Software---Practice & Experience, vol. 20, no. 9 (September, 1990),
1544
2254
// pp. 899--928.
1545
 
//
1546
2255
 
1547
2256
@implementation NSCalendarDate (GregorianDate)
1548
2257
 
 
2258
/**
 
2259
 * Returns the number of the last day of the month in the specified year.
 
2260
 */
1549
2261
- (int) lastDayOfGregorianMonth: (int)month year: (int)year
1550
2262
{
1551
2263
  return lastDayOfGregorianMonth(month, year);
1552
2264
}
1553
2265
 
 
2266
/**
 
2267
 * Returns the number of days since the start of the era for the specified
 
2268
 * day, month, and year.
 
2269
 */
1554
2270
- (int) absoluteGregorianDay: (int)day month: (int)month year: (int)year
1555
2271
{
1556
2272
  return absoluteGregorianDay(day, month, year);
1557
2273
}
1558
2274
 
 
2275
/**
 
2276
 * Given a day number since the start of the era, returns the date as a
 
2277
 * day, month, and year.
 
2278
 */
1559
2279
- (void) gregorianDateFromAbsolute: (int)d
1560
2280
                               day: (int *)day
1561
2281
                             month: (int *)month
1562
2282
                              year: (int *)year
1563
2283
{
1564
 
  // Search forward year by year from approximate year
1565
 
  *year = d/366;
1566
 
  while (d >= absoluteGregorianDay(1, 1, (*year)+1))
1567
 
    (*year)++;
1568
 
  // Search forward month by month from January
1569
 
  (*month) = 1;
1570
 
  while (d > absoluteGregorianDay(lastDayOfGregorianMonth(*month, *year),
1571
 
    *month, *year))
1572
 
    (*month)++;
1573
 
  *day = d - absoluteGregorianDay(1, *month, *year) + 1;
 
2284
  gregorianDateFromAbsolute(d, day, month, year);
1574
2285
}
1575
2286
 
1576
2287
@end
1577
2288
 
1578
2289
 
 
2290
/**
 
2291
 * Methods present in OpenStep but later removed from MacOS-X.
 
2292
 */
1579
2293
@implementation NSCalendarDate (OPENSTEP)
1580
2294
 
1581
 
- (NSCalendarDate *)dateByAddingYears: (int)years
 
2295
/**
 
2296
 * <p>Returns a calendar date formed by adding the specified offsets to the
 
2297
 * receiver.  The offsets are added in order, years, then months, then
 
2298
 * days, then hours then minutes then seconds, so if you add 1 month and
 
2299
 * forty days to 20th September, the result will be 9th November.
 
2300
 * </p>
 
2301
 * <p>This method understands leap years and tries to adjust for daylight
 
2302
 * savings time changes so that it preserves expected clock time.
 
2303
 * </p>
 
2304
 */
 
2305
- (NSCalendarDate*) dateByAddingYears: (int)years
1582
2306
                               months: (int)months
1583
2307
                                 days: (int)days
1584
2308
                                hours: (int)hours
1585
2309
                              minutes: (int)minutes
1586
2310
                              seconds: (int)seconds
1587
2311
{
1588
 
  int           i, year, month, day, hour, minute, second;
1589
 
 
1590
 
  [self getYear: &year
1591
 
          month: &month
1592
 
            day: &day
1593
 
           hour: &hour
1594
 
         minute: &minute
1595
 
         second: &second];
1596
 
 
1597
 
  second += seconds;
1598
 
  minute += second/60;
1599
 
  second %= 60;
1600
 
  if (second < 0)
1601
 
    {
1602
 
      minute--;
1603
 
      second += 60;
1604
 
    }
1605
 
 
1606
 
  minute += minutes;
1607
 
  hour += minute/60;
1608
 
  minute %= 60;
1609
 
  if (minute < 0)
1610
 
    {
1611
 
      hour--;
1612
 
      minute += 60;
1613
 
    }
1614
 
 
1615
 
  hour += hours;
1616
 
  day += hour/24;
1617
 
  hour %= 24;
1618
 
  if (hour < 0)
1619
 
    {
1620
 
      day--;
1621
 
      hour += 24;
1622
 
    }
1623
 
 
1624
 
  day += days;
1625
 
  if (day > 28)
1626
 
    {
1627
 
      i = [self lastDayOfGregorianMonth: month year: year];
1628
 
      while (day > i)
1629
 
        {
1630
 
          day -= i;
1631
 
          if (month < 12)
1632
 
            month++;
1633
 
          else
1634
 
            {
1635
 
              month = 1;
1636
 
              year++;
1637
 
            }
1638
 
          i = [self lastDayOfGregorianMonth: month year: year];
1639
 
        }
1640
 
    }
1641
 
  else
1642
 
    while (day < 1)
1643
 
      {
1644
 
        if (month == 1)
1645
 
          {
1646
 
            year--;
1647
 
            month = 12;
1648
 
          }
1649
 
        else
1650
 
          month--;
1651
 
        day += [self lastDayOfGregorianMonth: month year: year];
1652
 
      }
1653
 
 
1654
 
  month += months;
1655
 
  while (month > 12)
1656
 
    {
1657
 
      year++;
1658
 
      month -= 12;
1659
 
    }
1660
 
  while (month < 1)
1661
 
    {
1662
 
      year--;
1663
 
      month += 12;
1664
 
    }
1665
 
 
1666
 
  year += years;
1667
 
 
1668
 
  /*
1669
 
   * Special case - we adjusted to the correct day for the month in the
1670
 
   * starting date - but our month and year adjustment may have made that
1671
 
   * invalid for the final month and year - in which case we may have to
1672
 
   * advance to the next month.
1673
 
   */
1674
 
  if (day > 28 && day > [self lastDayOfGregorianMonth: month year: year])
1675
 
    {
1676
 
      day -= [self lastDayOfGregorianMonth: month year: year];
1677
 
      month++;
1678
 
      if (month > 12)
1679
 
        year++;
1680
 
    }
1681
 
 
1682
 
  return [NSCalendarDate dateWithYear: year
1683
 
                                month: month
1684
 
                                  day: day
1685
 
                                 hour: hour
1686
 
                               minute: minute
1687
 
                               second: second
1688
 
                             timeZone: [self timeZoneDetail]];
 
2312
  NSCalendarDate        *c;
 
2313
  NSTimeInterval        s;
 
2314
  NSTimeInterval        oldOffset;
 
2315
  NSTimeInterval        newOffset;
 
2316
  int                   i, year, month, day, hour, minute, second, mil;
 
2317
 
 
2318
  oldOffset = offset(_time_zone, self);
 
2319
  /*
 
2320
   * Break into components in GMT time zone.
 
2321
   */
 
2322
  GSBreakTime(_seconds_since_ref, &year, &month, &day, &hour, &minute,
 
2323
    &second, &mil);
 
2324
 
 
2325
  while (years != 0 || months != 0 || days != 0
 
2326
    || hours != 0 || minutes != 0 || seconds != 0)
 
2327
    {
 
2328
      year += years;
 
2329
      years = 0;
 
2330
 
 
2331
      month += months;
 
2332
      months = 0;
 
2333
      while (month > 12)
 
2334
        {
 
2335
          year++;
 
2336
          month -= 12;
 
2337
        }
 
2338
      while (month < 1)
 
2339
        {
 
2340
          year--;
 
2341
          month += 12;
 
2342
        }
 
2343
 
 
2344
      day += days;
 
2345
      days = 0;
 
2346
      if (day > 28)
 
2347
        {
 
2348
          i = lastDayOfGregorianMonth(month, year);
 
2349
          while (day > i)
 
2350
            {
 
2351
              day -= i;
 
2352
              if (month < 12)
 
2353
                {
 
2354
                  month++;
 
2355
                }
 
2356
              else
 
2357
                {
 
2358
                  month = 1;
 
2359
                  year++;
 
2360
                }
 
2361
              i = lastDayOfGregorianMonth(month, year);
 
2362
            }
 
2363
        }
 
2364
      else
 
2365
        {
 
2366
          while (day < 1)
 
2367
            {
 
2368
              if (month == 1)
 
2369
                {
 
2370
                  year--;
 
2371
                  month = 12;
 
2372
                }
 
2373
              else
 
2374
                {
 
2375
                  month--;
 
2376
                }
 
2377
              day += lastDayOfGregorianMonth(month, year);
 
2378
            }
 
2379
        }
 
2380
 
 
2381
      hour += hours;
 
2382
      hours = 0;
 
2383
      days += hour/24;
 
2384
      hour %= 24;
 
2385
      if (hour < 0)
 
2386
        {
 
2387
          days--;
 
2388
          hour += 24;
 
2389
        }
 
2390
 
 
2391
      minute += minutes;
 
2392
      minutes = 0;
 
2393
      hours += minute/60;
 
2394
      minute %= 60;
 
2395
      if (minute < 0)
 
2396
        {
 
2397
          hours++;
 
2398
          minute += 60;
 
2399
        }
 
2400
 
 
2401
      second += seconds;
 
2402
      seconds = 0;
 
2403
      minutes += second/60;
 
2404
      second %= 60;
 
2405
      if (second < 0)
 
2406
        {
 
2407
          minutes--;
 
2408
          second += 60;
 
2409
        }
 
2410
    }
 
2411
 
 
2412
  /*
 
2413
   * Reassemble in GMT time zone.
 
2414
   */
 
2415
  s = GSTime(day, month, year, hour, minute, second, mil);
 
2416
  c = [NSCalendarDate alloc];
 
2417
  c->_calendar_format = cformat;
 
2418
  c->_time_zone = RETAIN([self timeZone]);
 
2419
  c->_seconds_since_ref = s;
 
2420
 
 
2421
  /*
 
2422
   * Adjust date to try to maintain the time of day over
 
2423
   * a daylight savings time boundary if necessary.
 
2424
   */
 
2425
  newOffset = offset(_time_zone, c);
 
2426
  if (newOffset != oldOffset)
 
2427
    {
 
2428
      NSTimeInterval    tmpOffset = newOffset;
 
2429
 
 
2430
      s -= (newOffset - oldOffset);
 
2431
      c->_seconds_since_ref = s;
 
2432
      /*
 
2433
       * If the date we have lies within a missing hour at a
 
2434
       * daylight savings time transition, we use the original
 
2435
       * date rather than the adjusted one.
 
2436
       */
 
2437
      newOffset = offset(_time_zone, c);
 
2438
      if (newOffset == oldOffset)
 
2439
        {
 
2440
          s += (tmpOffset - oldOffset);
 
2441
          c->_seconds_since_ref = s;
 
2442
        }
 
2443
    }
 
2444
  return AUTORELEASE(c);
1689
2445
}
1690
2446
 
 
2447
/**
 
2448
 * Returns the number of years, months, days, hours, minutes, and seconds
 
2449
 * between the receiver and the given date.<br />
 
2450
 * If date is in the future of the receiver, the returned values will
 
2451
 * be negative (or zero), otherwise they are all positive.<br />
 
2452
 * If any of the pointers to return value in is null, the corresponding
 
2453
 * value will not be returned, and other return values will be adjusted
 
2454
 * accordingly. eg. If a difference of 1 hour was to be returned but
 
2455
 * hours is null, then the value returned in minutes will be increased
 
2456
 * by 60.
 
2457
 */
1691
2458
- (void) years: (int*)years
1692
2459
        months: (int*)months
1693
2460
          days: (int*)days
1702
2469
  int                   diff;
1703
2470
  int                   extra;
1704
2471
  int                   sign;
 
2472
  int                   mil;
1705
2473
  int                   syear, smonth, sday, shour, sminute, ssecond;
1706
2474
  int                   eyear, emonth, eday, ehour, eminute, esecond;
1707
2475
 
1709
2477
    How about daylight savings time?
1710
2478
   */
1711
2479
  if ([date isKindOfClass: [NSCalendarDate class]])
1712
 
    tmp = (NSCalendarDate*)RETAIN(date);
 
2480
    {
 
2481
      tmp = (NSCalendarDate*)RETAIN(date);
 
2482
    }
 
2483
  else if ([date isKindOfClass: [NSDate class]])
 
2484
    {
 
2485
      tmp = [[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate:
 
2486
        [date timeIntervalSinceReferenceDate]];
 
2487
    }
1713
2488
  else
1714
 
    tmp = [[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate:
1715
 
                [date timeIntervalSinceReferenceDate]];
 
2489
    {
 
2490
      tmp = nil;        // Avoid compiler warning
 
2491
      [NSException raise: NSInvalidArgumentException
 
2492
        format: @"%@ invalid date given - %@",
 
2493
        NSStringFromSelector(_cmd), date];
 
2494
    }
1716
2495
 
1717
2496
  end = (NSCalendarDate*)[self laterDate: tmp];
1718
2497
  if (end == self)
1726
2505
      sign = -1;
1727
2506
    }
1728
2507
 
1729
 
  [start getYear: &syear
1730
 
           month: &smonth
1731
 
             day: &sday
1732
 
            hour: &shour
1733
 
          minute: &sminute
1734
 
          second: &ssecond];
1735
 
  [end getYear: &eyear
1736
 
         month: &emonth
1737
 
           day: &eday
1738
 
          hour: &ehour
1739
 
        minute: &eminute
1740
 
        second: &esecond];
 
2508
  GSBreakTime(start->_seconds_since_ref + offset(start->_time_zone, start),
 
2509
    &syear, &smonth, &sday, &shour, &sminute, &ssecond, &mil);
 
2510
 
 
2511
  GSBreakTime(end->_seconds_since_ref + offset(end->_time_zone, end),
 
2512
    &eyear, &emonth, &eday, &ehour, &eminute, &esecond, &mil);
 
2513
 
 
2514
  if (esecond < ssecond)
 
2515
    {
 
2516
      eminute -= 1;
 
2517
      esecond += 60;
 
2518
    }
 
2519
  if (eminute < sminute)
 
2520
    {
 
2521
      ehour -= 1;
 
2522
      eminute += 60;
 
2523
    }
 
2524
  if (ehour < shour)
 
2525
    {
 
2526
      eday -= 1;
 
2527
      ehour += 24;
 
2528
    }
 
2529
  if (eday < sday)
 
2530
    {
 
2531
      emonth -= 1;
 
2532
      if (emonth >= 0)
 
2533
        {
 
2534
          eday += [end lastDayOfGregorianMonth: emonth year: eyear];
 
2535
        }
 
2536
      else
 
2537
        {
 
2538
          eday += 31;
 
2539
        }
 
2540
    }
 
2541
  if (emonth < smonth)
 
2542
    {
 
2543
      eyear -= 1;
 
2544
      emonth += 12;
 
2545
    }
1741
2546
 
1742
2547
  /* Calculate year difference and leave any remaining months in 'extra' */
1743
2548
  diff = eyear - syear;
1744
2549
  extra = 0;
1745
 
  if (emonth < smonth)
 
2550
  if (years != 0)
1746
2551
    {
1747
 
      diff--;
1748
 
      extra += 12;
 
2552
      *years = sign*diff;
1749
2553
    }
1750
 
  if (years)
1751
 
    *years = sign*diff;
1752
2554
  else
1753
 
    extra += diff*12;
 
2555
    {
 
2556
      extra += diff*12;
 
2557
    }
1754
2558
 
1755
2559
  /* Calculate month difference and leave any remaining days in 'extra' */
1756
2560
  diff = emonth - smonth + extra;
1757
2561
  extra = 0;
1758
 
  if (eday < sday)
 
2562
  if (months != 0)
1759
2563
    {
1760
 
      diff--;
1761
 
      extra = [end lastDayOfGregorianMonth: smonth year: syear];
 
2564
      *months = sign*diff;
1762
2565
    }
1763
 
  if (months)
1764
 
    *months = sign*diff;
1765
2566
  else
1766
2567
    {
1767
2568
      while (diff--)
1781
2582
  /* Calculate day difference and leave any remaining hours in 'extra' */
1782
2583
  diff = eday - sday + extra;
1783
2584
  extra = 0;
1784
 
  if (ehour < shour)
 
2585
  if (days != 0)
1785
2586
    {
1786
 
      diff--;
1787
 
      extra = 24;
 
2587
      *days = sign*diff;
1788
2588
    }
1789
 
  if (days)
1790
 
    *days = sign*diff;
1791
2589
  else
1792
 
    extra += diff*24;
 
2590
    {
 
2591
      extra += diff*24;
 
2592
    }
1793
2593
 
1794
2594
  /* Calculate hour difference and leave any remaining minutes in 'extra' */
1795
2595
  diff = ehour - shour + extra;
1796
2596
  extra = 0;
1797
 
  if (eminute < sminute)
 
2597
  if (hours != 0)
1798
2598
    {
1799
 
      diff--;
1800
 
      extra = 60;
 
2599
      *hours = sign*diff;
1801
2600
    }
1802
 
  if (hours)
1803
 
    *hours = sign*diff;
1804
2601
  else
1805
 
    extra += diff*60;
 
2602
    {
 
2603
      extra += diff*60;
 
2604
    }
1806
2605
 
1807
2606
  /* Calculate minute difference and leave any remaining seconds in 'extra' */
1808
2607
  diff = eminute - sminute + extra;
1809
2608
  extra = 0;
1810
 
  if (esecond < ssecond)
 
2609
  if (minutes != 0)
1811
2610
    {
1812
 
      diff--;
1813
 
      extra = 60;
 
2611
      *minutes = sign*diff;
1814
2612
    }
1815
 
  if (minutes)
1816
 
    *minutes = sign*diff;
1817
2613
  else
1818
 
    extra += diff*60;
 
2614
    {
 
2615
      extra += diff*60;
 
2616
    }
1819
2617
 
1820
2618
  diff = esecond - ssecond + extra;
1821
 
  if (seconds)
1822
 
    *seconds = sign*diff;
 
2619
  if (seconds != 0)
 
2620
    {
 
2621
      *seconds = sign*diff;
 
2622
    }
1823
2623
 
1824
2624
  RELEASE(tmp);
1825
2625
}