282
- (id) initWithString: (NSString *)description
283
calendarFormat: (NSString *)fmt
284
locale: (NSDictionary *)locale
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 -
504
* %% literal % character
507
* %a abbreviated weekday name according to locale
510
* %A full weekday name according to locale
513
* %b abbreviated month name according to locale
516
* %B full month name according to locale
522
* %d day of month as decimal number
525
* %e same as %d without leading zero (you get a leading space instead)
528
* %F milliseconds as a decimal number
531
* %H hour as a decimal number using 24-hour clock
534
* %I hour as a decimal number using 12-hour clock
537
* %j day of year as a decimal number
540
* %m month as decimal number
543
* %M minute as decimal number
549
* %S second as decimal number
552
* %U week of the current year as decimal number (Sunday first day)
555
* %W week of the current year as decimal number (Monday first day)
558
* %w day of the week as decimal number (Sunday = 0)
561
* %x date with date representation for locale
564
* %X time with time representation for locale
567
* %y year as a decimal number without century
570
* %Y year as a decimal number with century
573
* %z time zone offset in hours and minutes from GMT (HHMM)
576
* %Z time zone abbreviation
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.
588
- (id) initWithString: (NSString*)description
589
calendarFormat: (NSString*)fmt
590
locale: (NSDictionary*)locale
286
// If description does not match this format exactly, this method returns nil
287
if ([description length] == 0)
592
int milliseconds = 0;
599
NSTimeZone *tz = nil;
601
BOOL twelveHrClock = NO;
602
int julianWeeks = -1, weekStartsMonday = 0, dayOfWeek = -1;
603
const char *source = [description cString];
604
unsigned sourceLen = strlen(source);
607
unsigned formatIdx = 0;
608
unsigned sourceIdx = 0;
613
BOOL hadPercent = NO;
615
BOOL changedFormat = NO;
289
// Autorelease self because it isn't done by the calling function
290
// [[NSCalendarDate alloc] initWithString:calendarFormat:locale:];
620
locale = GSUserDefaultsDictionaryRepresentation();
296
int year = 0, month = 1, day = 1;
297
int hour = 0, min = 0, sec = 0;
298
NSTimeZone *tz = [NSTimeZone localTimeZone];
300
BOOL twelveHrClock = NO;
301
int julianWeeks = -1, weekStartsMonday = 0, dayOfWeek = -1;
302
const char *source = [description cString];
303
unsigned sourceLen = strlen(source);
306
unsigned formatIdx = 0;
307
unsigned sourceIdx = 0;
312
BOOL hadPercent = NO;
317
BOOL changedFormat = NO;
321
locale = GSUserDefaultsDictionaryRepresentation();
624
fmt = [locale objectForKey: NSTimeDateFormatString];
325
fmt = [locale objectForKey: NSTimeDateFormatString];
330
TForm = [locale objectForKey: NSTimeDateFormatString];
333
dForm = [locale objectForKey: NSShortDateFormatString];
336
tForm = [locale objectForKey: NSTimeFormatString];
341
* Get format into a buffer, leaving room for expansion in case it has
342
* escapes that need to be converted.
344
formatLen = [fmt length];
345
fd = [[NSMutableData alloc]
346
initWithLength: (formatLen + 32) * sizeof(unichar)];
347
format = (unichar*)[fd mutableBytes];
348
[fmt getCharacters: format];
351
* Expand any sequences to their basic components.
353
for (pos = 0; pos < formatLen; pos++)
630
if (description == nil)
636
* Get format into a buffer, leaving room for expansion in case it has
637
* escapes that need to be converted.
639
formatLen = [fmt length];
640
fd = [[NSMutableData alloc]
641
initWithLength: (formatLen + 32) * sizeof(unichar)];
642
format = (unichar*)[fd mutableBytes];
643
[fmt getCharacters: format];
646
* Expand any sequences to their basic components.
648
for (pos = 0; pos < formatLen; pos++)
650
unichar c = format[pos];
355
unichar c = format[pos];
359
if (hadPercent == YES)
370
if (hadPercent == YES)
384
sub = @"%I:%M:%S %p";
397
unsigned sLen = [sub length];
403
(formatLen + sLen - 2) * sizeof(unichar)];
404
format = (unichar*)[fd mutableBytes];
405
for (i = formatLen-1; i > pos; i--)
407
format[i+sLen-2] = format[i];
412
for (i = pos+1; i < formatLen; i++)
414
format[i+sLen-2] = format[i];
417
(formatLen + sLen - 2) * sizeof(unichar)];
418
format = (unichar*)[fd mutableBytes];
420
[sub getCharacters: &format[pos-1]];
421
formatLen += sLen - 2;
423
pos -= 2; // Re-parse the newly substituted data.
654
if (hadPercent == YES)
431
* Set up calendar format.
433
if (changedFormat == YES)
435
fmt = [NSString stringWithCharacters: format length: formatLen];
437
ASSIGN(_calendar_format, fmt);
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.
447
// The strftime specifiers as used by OpenStep + %U.
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
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
475
while (formatIdx < formatLen)
477
if (format[formatIdx] != '%')
479
// If it's not a format specifier, ignore it.
480
if (isspace(format[formatIdx]))
482
// Skip any amount of white space.
483
while (source[sourceIdx] != 0 && isspace(source[sourceIdx]))
490
if (sourceIdx < sourceLen)
492
if (source[sourceIdx] != format[formatIdx])
494
NSLog(@"Expected literal '%c' but got '%c'",
495
format[formatIdx], source[sourceIdx]);
506
switch (format[formatIdx])
510
if (sourceIdx < sourceLen)
512
if (source[sourceIdx] != '%')
514
NSLog(@"Expected literal '%' but got '%c'",
522
// Are Short names three chars in all locales?????
523
tmpStr[0] = toupper(source[sourceIdx]);
524
if (sourceIdx < sourceLen)
526
tmpStr[1] = tolower(source[sourceIdx]);
527
if (sourceIdx < sourceLen)
529
tmpStr[2] = tolower(source[sourceIdx]);
530
if (sourceIdx < sourceLen)
537
currDay = [NSString stringWithCString: tmpStr];
538
dayNames = [locale objectForKey: NSShortWeekDayNameArray];
539
for (tmpIdx = 0; tmpIdx < 7; tmpIdx++)
541
if ([[dayNames objectAtIndex: tmpIdx] isEqual:
553
for (tmpIdx = sourceIdx; tmpIdx < sourceLen; tmpIdx++)
555
if (isalpha(source[tmpIdx]))
557
tmpStr[tmpIdx - sourceIdx] = source[tmpIdx];
564
tmpStr[tmpIdx - sourceIdx] = '\0';
565
sourceIdx += tmpIdx - sourceIdx;
570
currDay = [NSString stringWithCString: tmpStr];
571
dayNames = [locale objectForKey: NSWeekDayNameArray];
572
for (tmpIdx = 0; tmpIdx < 7; tmpIdx++)
574
if ([[dayNames objectAtIndex: tmpIdx] isEqual:
586
// Are Short names three chars in all locales?????
587
tmpStr[0] = toupper(source[sourceIdx]);
588
if (sourceIdx < sourceLen)
590
tmpStr[1] = tolower(source[sourceIdx]);
591
if (sourceIdx < sourceLen)
593
tmpStr[2] = tolower(source[sourceIdx]);
594
if (sourceIdx < sourceLen)
601
currMonth = [NSString stringWithCString: tmpStr];
602
monthNames = [locale objectForKey: NSShortMonthNameArray];
604
for (tmpIdx = 0; tmpIdx < 12; tmpIdx++)
606
if ([[monthNames objectAtIndex: tmpIdx]
607
isEqual: currMonth] == YES)
618
for (tmpIdx = sourceIdx; tmpIdx < sourceLen; tmpIdx++)
620
if (isalpha(source[tmpIdx]))
622
tmpStr[tmpIdx - sourceIdx] = source[tmpIdx];
629
tmpStr[tmpIdx - sourceIdx] = '\0';
630
sourceIdx += tmpIdx - sourceIdx;
635
currMonth = [NSString stringWithCString: tmpStr];
636
monthNames = [locale objectForKey: NSMonthNameArray];
638
for (tmpIdx = 0; tmpIdx < 12; tmpIdx++)
640
if ([[monthNames objectAtIndex: tmpIdx]
641
isEqual: currMonth] == YES)
651
case 'd': // fall through
653
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
659
NSLog(@"%F format ignored when creating date");
662
case 'I': // fall through
665
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
671
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 3);
677
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
678
month = atoi(tmpStr);
683
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
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)
694
tmpStr[1] = toupper(source[sourceIdx]);
695
if (sourceIdx < sourceLen)
702
currAMPM = [NSString stringWithCString: tmpStr];
703
amPMNames = [locale objectForKey: NSAMPMDesignation];
706
* The time addition is handled below because this
707
* indicator only modifies the time on a 12hour clock.
709
if ([[amPMNames objectAtIndex: 1] isEqual:
718
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
724
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 1);
725
dayOfWeek = atoi(tmpStr);
729
case 'W': // Fall through
730
weekStartsMonday = 1;
732
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 1);
733
julianWeeks = atoi(tmpStr);
743
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
757
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 4);
767
if (source[sourceIdx] == '+')
771
else if (source[sourceIdx] == '-')
776
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 4);
777
zone = atoi(tmpStr) * sign;
779
if ((tz = [NSTimeZone timeZoneForSecondsFromGMT:
780
(zone / 100 * 60 + (zone % 100)) * 60]) == nil)
782
tz = [NSTimeZone localTimeZone];
788
for (tmpIdx = sourceIdx; tmpIdx < sourceLen; tmpIdx++)
790
if (isalpha(source[tmpIdx]) || source[tmpIdx] == '-'
791
|| source[tmpIdx] == '+')
793
tmpStr[tmpIdx - sourceIdx] = source[tmpIdx];
800
tmpStr[tmpIdx - sourceIdx] = '\0';
801
sourceIdx += tmpIdx - sourceIdx;
803
NSString *z = [NSString stringWithCString: tmpStr];
805
tz = [NSTimeZone timeZoneWithName: z];
808
tz = [NSTimeZone timeZoneWithAbbreviation: z];
812
tz = [NSTimeZone localTimeZone];
818
[NSException raise: NSInvalidArgumentException
819
format: @"Invalid NSCalendar date, "
820
@"specifier %c not recognized in format %@",
821
format[formatIdx], fmt];
665
if (hadPercent == YES)
671
sub = [locale objectForKey: NSTimeDateFormatString];
683
sub = @"%I:%M:%S %p";
687
sub = [locale objectForKey: NSTimeFormatString];
695
sub = [locale objectForKey: NSShortDateFormatString];
704
unsigned sLen = [sub length];
710
(formatLen + sLen - 2) * sizeof(unichar)];
711
format = (unichar*)[fd mutableBytes];
712
for (i = formatLen-1; i > (int)pos; i--)
714
format[i+sLen-2] = format[i];
719
for (i = pos+1; i < (int)formatLen; i++)
721
format[i+sLen-2] = format[i];
724
(formatLen + sLen - 2) * sizeof(unichar)];
725
format = (unichar*)[fd mutableBytes];
727
[sub getCharacters: &format[pos-1]];
728
formatLen += sLen - 2;
730
pos -= 2; // Re-parse the newly substituted data.
738
* Set up calendar format.
740
if (changedFormat == YES)
742
fmt = [NSString stringWithCharacters: format length: formatLen];
744
ASSIGN(_calendar_format, fmt);
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.
753
while (formatIdx < formatLen)
755
if (format[formatIdx] != '%')
757
// If it's not a format specifier, ignore it.
758
if (isspace(format[formatIdx]))
760
// Skip any amount of white space.
761
while (source[sourceIdx] != 0 && isspace(source[sourceIdx]))
768
if (sourceIdx < sourceLen)
770
if (source[sourceIdx] != format[formatIdx])
774
@"Expected literal '%c' but got '%c' parsing"
775
@"'%@' using '%@'", format[formatIdx],
776
source[sourceIdx], description, fmt);
787
switch (format[formatIdx])
791
if (sourceIdx < sourceLen)
793
if (source[sourceIdx] != '%')
797
@"Expected literal '%%' but got '%c' parsing"
798
@"'%@' using '%@'", source[sourceIdx],
806
// Are Short names three chars in all locales?????
807
tmpStr[0] = toupper(source[sourceIdx]);
808
if (sourceIdx < sourceLen)
810
tmpStr[1] = tolower(source[sourceIdx]);
811
if (sourceIdx < sourceLen)
813
tmpStr[2] = tolower(source[sourceIdx]);
814
if (sourceIdx < sourceLen)
821
currDay = [[NSString alloc] initWithCString: tmpStr];
822
dayNames = [locale objectForKey: NSShortWeekDayNameArray];
823
for (tmpIdx = 0; tmpIdx < 7; tmpIdx++)
825
if ([[dayNames objectAtIndex: tmpIdx] isEqual:
834
NSDebugMLog(@"Day of week '%@' not found in locale",
847
for (tmpIdx = sourceIdx; tmpIdx < sourceLen; tmpIdx++)
849
if (isalpha(source[tmpIdx]))
851
tmpStr[tmpIdx - sourceIdx] = source[tmpIdx];
858
tmpStr[tmpIdx - sourceIdx] = '\0';
859
sourceIdx += tmpIdx - sourceIdx;
864
currDay = [[NSString alloc] initWithCString: tmpStr];
865
dayNames = [locale objectForKey: NSWeekDayNameArray];
866
for (tmpIdx = 0; tmpIdx < 7; tmpIdx++)
868
if ([[dayNames objectAtIndex: tmpIdx] isEqual:
877
NSDebugMLog(@"Day of week '%@' not found in locale",
890
// Are Short names three chars in all locales?????
891
tmpStr[0] = toupper(source[sourceIdx]);
892
if (sourceIdx < sourceLen)
894
tmpStr[1] = tolower(source[sourceIdx]);
895
if (sourceIdx < sourceLen)
897
tmpStr[2] = tolower(source[sourceIdx]);
898
if (sourceIdx < sourceLen)
905
currMonth = [[NSString alloc] initWithCString: tmpStr];
906
monthNames = [locale objectForKey: NSShortMonthNameArray];
908
for (tmpIdx = 0; tmpIdx < 12; tmpIdx++)
910
if ([[monthNames objectAtIndex: tmpIdx]
911
isEqual: currMonth] == YES)
919
NSDebugMLog(@"Month of year '%@' not found in locale",
932
for (tmpIdx = sourceIdx; tmpIdx < sourceLen; tmpIdx++)
934
if (isalpha(source[tmpIdx]))
936
tmpStr[tmpIdx - sourceIdx] = source[tmpIdx];
943
tmpStr[tmpIdx - sourceIdx] = '\0';
944
sourceIdx += tmpIdx - sourceIdx;
949
currMonth = [[NSString alloc] initWithCString: tmpStr];
950
monthNames = [locale objectForKey: NSMonthNameArray];
952
for (tmpIdx = 0; tmpIdx < 12; tmpIdx++)
954
if ([[monthNames objectAtIndex: tmpIdx]
955
isEqual: currMonth] == YES)
963
NSDebugMLog(@"Month of year '%@' not found in locale",
975
case 'd': // fall through
977
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
983
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 3);
984
milliseconds = atoi(tmpStr);
987
case 'I': // fall through
990
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
996
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 3);
1002
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
1003
month = atoi(tmpStr);
1008
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
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)
1019
tmpStr[1] = toupper(source[sourceIdx]);
1020
if (sourceIdx < sourceLen)
1027
currAMPM = [NSString stringWithCString: tmpStr];
1028
amPMNames = [locale objectForKey: NSAMPMDesignation];
1031
* The time addition is handled below because this
1032
* indicator only modifies the time on a 12hour clock.
1034
if ([[amPMNames objectAtIndex: 1] isEqual:
1043
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
1049
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 1);
1050
dayOfWeek = atoi(tmpStr);
1054
case 'W': // Fall through
1055
weekStartsMonday = 1;
1057
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 1);
1058
julianWeeks = atoi(tmpStr);
1068
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 2);
1069
year = atoi(tmpStr);
1082
sourceIdx += getDigits(&source[sourceIdx], tmpStr, 4);
1083
year = atoi(tmpStr);
1093
if (source[sourceIdx] == '+')
1097
else if (source[sourceIdx] == '-')
1102
found = getDigits(&source[sourceIdx], tmpStr, 4);
1106
zone = atoi(tmpStr);
1109
zone *= 100; // Convert 2 digits to 4
1111
tz = [NSTimeZone timeZoneForSecondsFromGMT:
1112
sign * ((zone / 100) * 60 + (zone % 100)) * 60];
1118
for (tmpIdx = sourceIdx; tmpIdx < sourceLen; tmpIdx++)
1120
if (isalpha(source[tmpIdx]) || source[tmpIdx] == '-'
1121
|| source[tmpIdx] == '+')
1123
tmpStr[tmpIdx - sourceIdx] = source[tmpIdx];
1130
tmpStr[tmpIdx - sourceIdx] = '\0';
1131
sourceIdx += tmpIdx - sourceIdx;
1133
NSString *z = [NSString stringWithCString: tmpStr];
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];
1141
tz = [NSTimeZone timeZoneWithAbbreviation: z];
1147
[NSException raise: NSInvalidArgumentException
1148
format: @"Invalid NSCalendar date, "
1149
@"specifier %c not recognized in format %@",
1150
format[formatIdx], fmt];
830
tz = [NSTimeZone localTimeZone];
833
1164
if (twelveHrClock == YES)
948
1347
// Assign time zone detail
949
1348
if (aTimeZone == nil)
951
_time_zone = RETAIN([NSTimeZone localTimeZone]);
1350
_time_zone = localTZ; // retain is a no-op for the local timezone.
955
1354
_time_zone = RETAIN(aTimeZone);
957
d = [NSDate dateWithTimeIntervalSinceReferenceDate: s];
1356
if (_calendar_format == nil)
1358
_calendar_format = cformat;
1360
_seconds_since_ref = s;
959
// Adjust date so it is correct for time zone.
960
oldOffset = [_time_zone secondsFromGMTForDate: d];
1363
* Adjust date so it is correct for time zone.
1365
oldOffset = offset(_time_zone, self);
962
self = [self initWithTimeIntervalSinceReferenceDate: s];
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! */
968
for (c = 0; c < 5 && self != nil; c++)
1367
_seconds_since_ref = s;
1370
* See if we need to adjust for daylight savings time
1372
newOffset = offset(_time_zone, self);
1373
if (oldOffset != newOffset)
970
int y, m, d, h, mm, ss;
971
NSTimeInterval newOffset;
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)
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)
982
s += newOffset - oldOffset;
983
oldOffset = newOffset;
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 */
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;
1009
move = (second - ss);
1013
self = [self initWithTimeIntervalSinceReferenceDate: s];
1375
s -= (newOffset - oldOffset);
1376
_seconds_since_ref = s;
1377
oldOffset = offset(_time_zone, self);
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.
1382
if (oldOffset != newOffset)
1384
NSWarnMLog(@"init non-existent time at start of daylight savings");
1018
// Default initializer
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.
1019
1396
- (id) initWithTimeIntervalSinceReferenceDate: (NSTimeInterval)seconds
1021
1398
_seconds_since_ref = seconds;
1022
1399
if (_calendar_format == nil)
1023
_calendar_format = @"%Y-%m-%d %H:%M:%S %z";
1401
_calendar_format = cformat;
1024
1403
if (_time_zone == nil)
1025
_time_zone = RETAIN([NSTimeZone localTimeZone]);
1405
_time_zone = localTZ; // retain is a no-op for the local timezone.
1029
// Retreiving Date Elements
1030
- (void) getYear: (int *)year
1034
minute: (int *)minute
1035
second: (int *)second
1038
double a, b, c, d = [self dayOfCommonEra];
1040
// Calculate year, month, and day
1041
[self gregorianDateFromAbsolute: d day: day month: month year: year];
1043
// Calculate hour, minute, and seconds
1044
d -= GREGORIAN_REFERENCE;
1046
a = abs(d - (_seconds_since_ref+[_time_zone secondsFromGMTForDate: self]));
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.
1060
1414
- (int) dayOfCommonEra
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;
1416
NSTimeInterval when;
1418
when = _seconds_since_ref + offset(_time_zone, self);
1419
return dayOfCommonEra(when);
1423
* Return the month (1 to 31) of the receiving date.
1074
1425
- (int) dayOfMonth
1428
NSTimeInterval when;
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);
1437
* Return the day of the week (0 to 6) of the receiving date.
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>
1084
1448
- (int) dayOfWeek
1086
int d = [self dayOfCommonEra];
1451
NSTimeInterval when;
1453
when = _seconds_since_ref + offset(_time_zone, self);
1454
d = dayOfCommonEra(when);
1088
1456
/* The era started on a sunday.
1089
1457
Did we always have a seven day week?
1199
1605
seconds: second];
1202
// Getting String Descriptions of Dates
1609
* Calls -descriptionWithCalendarFormat:locale: passing the receviers
1610
* calendar format and a nil locale.
1203
1612
- (NSString*) description
1205
1614
return [self descriptionWithCalendarFormat: _calendar_format locale: nil];
1618
* Returns a string representation of the receiver using the specified
1619
* format string.<br />
1620
* Calls -descriptionWithCalendarFormat:locale: with a nil locale.
1208
1622
- (NSString*) descriptionWithCalendarFormat: (NSString *)format
1210
1624
return [self descriptionWithCalendarFormat: format locale: nil];
1213
1627
#define UNIX_REFERENCE_INTERVAL -978307200.0
1214
- (NSString *)descriptionWithCalendarFormat: (NSString *)format
1215
locale: (NSDictionary *)locale
1220
BOOL mtag = NO, dtag = NO, ycent = NO;
1221
BOOL mname = NO, dname = NO;
1223
int yd = 0, md = 0, mnd = 0, sd = 0, dom = -1, dow = -1, doy = -1;
1228
locale = GSUserDefaultsDictionaryRepresentation();
1230
format = [locale objectForKey: NSTimeDateFormatString];
1232
// If the format is nil then return an empty string
1236
f = [format cString];
1239
[self getYear: &yd month: &md day: &dom hour: &hd minute: &mnd second: &sd];
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
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)
1264
// %% literal % character
1266
// Find the order of date elements
1267
// and translate format string into printf ready string
1269
for (i = 0;i < lf; ++i)
1643
static void Grow(DescriptionInfo *info, unsigned size)
1645
if (info->offset + size >= info->length)
1647
if (info->t == info->base)
1649
unichar *old = info->t;
1651
info->t = NSZoneMalloc(NSDefaultMallocZone(),
1652
(info->length + 512) * sizeof(unichar));
1653
memcpy(info->t, old, info->length*sizeof(unichar));
1657
info->t = NSZoneRealloc(NSDefaultMallocZone(), info->t,
1658
(info->length + 512) * sizeof(unichar));
1660
info->length += 512;
1664
- (void) _format: (NSString*)fmt
1665
locale: (NSDictionary*)locale
1666
info: (DescriptionInfo*)info
1670
unsigned lf = [fmt length];
1676
return; // Nothing to do.
1678
if (lf >= sizeof(fbuf)/sizeof(unichar))
1681
* Make temporary buffer to hold format string as unicode.
1683
f = (unichar*)NSZoneMalloc(NSDefaultMallocZone(), lf*sizeof(unichar));
1685
[fmt getCharacters: f];
1271
1696
// Only care about a format specifier
1272
1697
if (f[i] == '%')
1274
1700
// check the character that comes after
1290
k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%04d", yd));
1292
k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%02d", yd % 100));
1300
mtag = YES; // Month is character string
1309
months = [locale objectForKey: NSShortMonthNameArray];
1311
months = [locale objectForKey: NSMonthNameArray];
1312
name = [months objectAtIndex: md-1];
1314
k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%s",
1317
k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%02d", md));
1320
k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%02d", md));
1324
case 'd': // day of month
1326
k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%02d", dom));
1330
case 'e': // day of month
1332
k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%2d", dom));
1336
case 'F': // milliseconds
1337
s = ([self dayOfCommonEra] - GREGORIAN_REFERENCE) * 86400.0;
1338
s -= (_seconds_since_ref
1339
+ [_time_zone secondsFromGMTForDate: self]);
1343
k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%03d", (int)(s*1000)));
1347
case 'j': // day of year
1348
if (doy < 0) doy = [self dayOfYear];
1350
k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%02d", doy));
1354
// is it the week-day
1358
dtag = YES; // Day is character string
1362
if (dow < 0) dow = [self dayOfWeek];
1369
days = [locale objectForKey: NSShortWeekDayNameArray];
1371
days = [locale objectForKey: NSWeekDayNameArray];
1372
name = [days objectAtIndex: dow];
1374
k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%s",
1377
k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%01d", dow));
1380
k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%01d", dow));
1387
nhd = hd % 12; // 12 hour clock
1389
nhd = 12; // 12pm not 0pm
1392
k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%02d", nhd));
1399
k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%02d", mnd));
1406
k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%02d", sd));
1410
// Is it the am/pm indicator
1413
NSArray *a = [locale objectForKey: NSAMPMDesignation];
1420
ampm = [a objectAtIndex: 1];
1427
ampm = [a objectAtIndex: 0];
1431
k = VSPRINTF_LENGTH(sprintf(&(buf[j]), [ampm cString]));
1436
// is it the zone name
1439
k = VSPRINTF_LENGTH(sprintf(&(buf[j]), "%s",
1440
[[_time_zone abbreviationForDate: self] cString]));
1446
z = [_time_zone secondsFromGMTForDate: self];
1450
k = VSPRINTF_LENGTH(sprintf(&(buf[j]),"-%02d%02d",z/60,z%60));
1454
k = VSPRINTF_LENGTH(sprintf(&(buf[j]),"+%02d%02d",z/60,z%60));
1459
// Anything else is unknown so just copy
1706
info->t[info->offset++] = f[i-1];
1710
[self _format: @"%H:%M" locale: locale info: info];
1714
[self _format: @"%I:%M:%S %p" locale: locale info: info];
1718
[self _format: [locale objectForKey: NSTimeFormatString]
1722
info->t[info->offset++] = ' ';
1723
[self _format: [locale objectForKey: NSDateFormatString]
1729
[self _format: [locale objectForKey: NSTimeFormatString]
1735
[self _format: [locale objectForKey: NSDateFormatString]
1752
info->t[info->offset+3] = (v%10) + '0';
1754
info->t[info->offset+2] = (v%10) + '0';
1756
info->t[info->offset+1] = (v%10) + '0';
1758
info->t[info->offset+0] = (v%10) + '0';
1765
info->t[info->offset+1] = (v%10) + '0';
1767
info->t[info->offset+0] = (v%10) + '0';
1776
mtag = YES; // Month is character string
1783
months = [locale objectForKey: NSShortMonthNameArray];
1785
months = [locale objectForKey: NSMonthNameArray];
1786
if (info->md > [months count])
1794
name = [months objectAtIndex: info->md-1];
1797
[name getCharacters: info->t + info->offset];
1806
info->t[info->offset+1] = (v%10) + '0';
1808
info->t[info->offset+0] = (v%10) + '0';
1813
case 'd': // day of month with leading zero
1817
info->t[info->offset+1] = (v%10) + '0';
1819
info->t[info->offset+0] = (v%10) + '0';
1823
case 'e': // day of month with leading space
1829
info->t[info->offset+1] = ' ';
1833
info->t[info->offset+1] = (v%10) + '0';
1836
info->t[info->offset+0] = (v%10) + '0';
1840
case 'F': // milliseconds
1844
s = ([self dayOfCommonEra] - GREGORIAN_REFERENCE) * 86400.0;
1845
s -= (_seconds_since_ref + offset(_time_zone, self));
1851
info->t[info->offset+2] = (v%10) + '0';
1853
info->t[info->offset+1] = (v%10) + '0';
1855
info->t[info->offset+0] = (v%10) + '0';
1859
case 'j': // day of year
1860
v = [self dayOfYear];
1862
info->t[info->offset+2] = (v%10) + '0';
1864
info->t[info->offset+1] = (v%10) + '0';
1866
info->t[info->offset+0] = (v%10) + '0';
1870
// is it the week-day
1874
dtag = YES; // Day is character string
1877
v = [self dayOfWeek];
1883
days = [locale objectForKey: NSShortWeekDayNameArray];
1885
days = [locale objectForKey: NSWeekDayNameArray];
1886
if (v < [days count])
1890
name = [days objectAtIndex: v];
1893
[name getCharacters: info->t + info->offset];
1903
info->t[info->offset+0] = (v%10) + '0';
1926
info->t[info->offset+1] = (v%10) + '0';
1928
info->t[info->offset+0] = (v%10) + '0';
1936
info->t[info->offset+1] = (v%10) + '0';
1938
info->t[info->offset+0] = (v%10) + '0';
1946
info->t[info->offset+1] = (v%10) + '0';
1948
info->t[info->offset+0] = (v%10) + '0';
1952
// Is it the am/pm indicator
1955
NSArray *a = [locale objectForKey: NSAMPMDesignation];
1961
ampm = [a objectAtIndex: 1];
1968
ampm = [a objectAtIndex: 0];
1974
[ampm getCharacters: info->t + info->offset];
1979
// is it the zone name
1984
s = abbrev(_time_zone, self);
1987
[s getCharacters: info->t + info->offset];
1997
z = offset(_time_zone, self);
2001
info->t[info->offset++] = '-';
2005
info->t[info->offset++] = '+';
2007
z /= 60; // Convert seconds to minutes.
2009
info->t[info->offset+1] = (v%10) + '0';
2011
info->t[info->offset+0] = (v%10) + '0';
2014
info->t[info->offset+1] = (v%10) + '0';
2016
info->t[info->offset+0] = (v%10) + '0';
2021
// Anything else is unknown so just copy
2024
info->t[info->offset++] = '%';
2025
info->t[info->offset++] = f[i-1];
2032
info->t[info->offset++] = f[i++];
1478
return [NSString stringWithCString: buf];
2038
NSZoneFree(NSDefaultMallocZone(), f);
2043
* Returns a string representation of the receiver using the specified
2044
* format string and locale dictionary.<br />
2045
* Format specifiers are -
2048
* %a abbreviated weekday name according to locale
2051
* %A full weekday name according to locale
2054
* %b abbreviated month name according to locale
2057
* %c this is the same as %X %x
2060
* %B full month name according to locale
2063
* %d day of month as decimal number (leading zero)
2066
* %e day of month as decimal number (leading space)
2069
* %F milliseconds (000 to 999)
2072
* %H hour as a decimal number using 24-hour clock
2075
* %I hour as a decimal number using 12-hour clock
2078
* %j day of year as a decimal number
2081
* %m month as decimal number
2084
* %M minute as decimal number
2090
* %S second as decimal number
2093
* %U week of the current year as decimal number (Sunday first day)
2096
* %W week of the current year as decimal number (Monday first day)
2099
* %w day of the week as decimal number (Sunday = 0)
2102
* %x date formatted according to the locale
2105
* %X time formatted according to the locale
2108
* %y year as a decimal number without century (minimum 0)
2111
* %Y year as a decimal number with century, minimum 0, maximum 9999
2114
* %z time zone offset (HHMM)
2120
* %% literal % character
2124
- (NSString*) descriptionWithCalendarFormat: (NSString*)format
2125
locale: (NSDictionary*)locale
2129
DescriptionInfo info;
2132
locale = GSUserDefaultsDictionaryRepresentation();
2134
format = [locale objectForKey: NSTimeDateFormatString];
2136
GSBreakTime(_seconds_since_ref + offset(_time_zone, self),
2137
&info.yd, &info.md, &info.dom, &info.hd, &info.mnd, &info.sd, &info.mil);
2141
info.length = sizeof(tbuf)/sizeof(unichar);
2144
[self _format: format locale: locale info: &info];
2146
result = [NSString stringWithCharacters: info.t length: info.offset];
2150
NSZoneFree(NSDefaultMallocZone(), info.t);
1481
2156
- (id) copyWithZone: (NSZone*)zone
1539
// Routines for manipulating Gregorian dates
2249
* Routines for manipulating Gregorian dates.
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.
1547
2256
@implementation NSCalendarDate (GregorianDate)
2259
* Returns the number of the last day of the month in the specified year.
1549
2261
- (int) lastDayOfGregorianMonth: (int)month year: (int)year
1551
2263
return lastDayOfGregorianMonth(month, year);
2267
* Returns the number of days since the start of the era for the specified
2268
* day, month, and year.
1554
2270
- (int) absoluteGregorianDay: (int)day month: (int)month year: (int)year
1556
2272
return absoluteGregorianDay(day, month, year);
2276
* Given a day number since the start of the era, returns the date as a
2277
* day, month, and year.
1559
2279
- (void) gregorianDateFromAbsolute: (int)d
1560
2280
day: (int *)day
1561
2281
month: (int *)month
1562
2282
year: (int *)year
1564
// Search forward year by year from approximate year
1566
while (d >= absoluteGregorianDay(1, 1, (*year)+1))
1568
// Search forward month by month from January
1570
while (d > absoluteGregorianDay(lastDayOfGregorianMonth(*month, *year),
1573
*day = d - absoluteGregorianDay(1, *month, *year) + 1;
2284
gregorianDateFromAbsolute(d, day, month, year);
2291
* Methods present in OpenStep but later removed from MacOS-X.
1579
2293
@implementation NSCalendarDate (OPENSTEP)
1581
- (NSCalendarDate *)dateByAddingYears: (int)years
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.
2301
* <p>This method understands leap years and tries to adjust for daylight
2302
* savings time changes so that it preserves expected clock time.
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
1588
int i, year, month, day, hour, minute, second;
1590
[self getYear: &year
1598
minute += second/60;
1627
i = [self lastDayOfGregorianMonth: month year: year];
1638
i = [self lastDayOfGregorianMonth: month year: year];
1651
day += [self lastDayOfGregorianMonth: month year: year];
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.
1674
if (day > 28 && day > [self lastDayOfGregorianMonth: month year: year])
1676
day -= [self lastDayOfGregorianMonth: month year: year];
1682
return [NSCalendarDate dateWithYear: year
1688
timeZone: [self timeZoneDetail]];
2314
NSTimeInterval oldOffset;
2315
NSTimeInterval newOffset;
2316
int i, year, month, day, hour, minute, second, mil;
2318
oldOffset = offset(_time_zone, self);
2320
* Break into components in GMT time zone.
2322
GSBreakTime(_seconds_since_ref, &year, &month, &day, &hour, &minute,
2325
while (years != 0 || months != 0 || days != 0
2326
|| hours != 0 || minutes != 0 || seconds != 0)
2348
i = lastDayOfGregorianMonth(month, year);
2361
i = lastDayOfGregorianMonth(month, year);
2377
day += lastDayOfGregorianMonth(month, year);
2403
minutes += second/60;
2413
* Reassemble in GMT time zone.
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;
2422
* Adjust date to try to maintain the time of day over
2423
* a daylight savings time boundary if necessary.
2425
newOffset = offset(_time_zone, c);
2426
if (newOffset != oldOffset)
2428
NSTimeInterval tmpOffset = newOffset;
2430
s -= (newOffset - oldOffset);
2431
c->_seconds_since_ref = s;
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.
2437
newOffset = offset(_time_zone, c);
2438
if (newOffset == oldOffset)
2440
s += (tmpOffset - oldOffset);
2441
c->_seconds_since_ref = s;
2444
return AUTORELEASE(c);
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
1691
2458
- (void) years: (int*)years
1692
2459
months: (int*)months
1693
2460
days: (int*)days