2001-12-17 14:31:42 +00:00
|
|
|
/** Implementation for NSCalendarDate for GNUstep
|
2002-09-28 15:45:26 +00:00
|
|
|
Copyright (C) 1996, 1998, 1999, 2000, 2002 Free Software Foundation, Inc.
|
1996-10-31 17:13:12 +00:00
|
|
|
|
|
|
|
Author: Scott Christley <scottc@net-community.com>
|
|
|
|
Date: October 1996
|
|
|
|
|
2002-09-28 15:45:26 +00:00
|
|
|
Author: Richard Frith-Macdonald <rfm@gnu.org>
|
|
|
|
Date: September 2002
|
|
|
|
|
1996-10-31 17:13:12 +00:00
|
|
|
This file is part of the GNUstep Base Library.
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
2007-09-14 11:36:11 +00:00
|
|
|
modify it under the terms of the GNU Lesser General Public
|
1996-10-31 17:13:12 +00:00
|
|
|
License as published by the Free Software Foundation; either
|
2008-06-08 10:38:33 +00:00
|
|
|
version 2 of the License, or (at your option) any later version.
|
1996-10-31 17:13:12 +00:00
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
2019-12-09 23:36:00 +00:00
|
|
|
Lesser General Public License for more details.
|
1996-10-31 17:13:12 +00:00
|
|
|
|
2007-09-14 11:36:11 +00:00
|
|
|
You should have received a copy of the GNU Lesser General Public
|
1996-10-31 17:13:12 +00:00
|
|
|
License along with this library; if not, write to the Free
|
2024-11-07 13:37:59 +00:00
|
|
|
Software Foundation, Inc., 31 Milk Street #960789 Boston, MA 02196 USA.
|
2001-12-18 16:54:15 +00:00
|
|
|
|
|
|
|
<title>NSCalendarDate class reference</title>
|
|
|
|
$Date$ $Revision$
|
1996-10-31 17:13:12 +00:00
|
|
|
*/
|
|
|
|
|
2010-02-19 08:12:46 +00:00
|
|
|
#import "common.h"
|
2010-02-14 10:48:10 +00:00
|
|
|
#define EXPOSE_NSCalendarDate_IVARS 1
|
1997-12-11 19:09:56 +00:00
|
|
|
#include <math.h>
|
2010-02-14 10:48:10 +00:00
|
|
|
#import "Foundation/NSArray.h"
|
|
|
|
#import "Foundation/NSAutoreleasePool.h"
|
|
|
|
#import "Foundation/NSCalendarDate.h"
|
|
|
|
#import "Foundation/NSCoder.h"
|
|
|
|
#import "Foundation/NSData.h"
|
|
|
|
#import "Foundation/NSDate.h"
|
|
|
|
#import "Foundation/NSDictionary.h"
|
|
|
|
#import "Foundation/NSException.h"
|
|
|
|
#import "Foundation/NSTimeZone.h"
|
|
|
|
#import "Foundation/NSUserDefaults.h"
|
|
|
|
#import "GNUstepBase/GSObjCRuntime.h"
|
|
|
|
|
|
|
|
#import "GSPrivate.h"
|
2006-10-18 15:36:52 +00:00
|
|
|
|
2006-03-10 10:46:37 +00:00
|
|
|
#ifdef HAVE_SYS_TIME_H
|
|
|
|
#include <sys/time.h>
|
|
|
|
#endif
|
|
|
|
#include <time.h>
|
1996-10-31 17:13:12 +00:00
|
|
|
#include <stdio.h>
|
2001-05-31 22:39:16 +00:00
|
|
|
#include <ctype.h>
|
1996-10-31 17:13:12 +00:00
|
|
|
|
2002-09-30 18:34:02 +00:00
|
|
|
@class GSTimeZone;
|
2005-07-08 11:48:37 +00:00
|
|
|
@interface GSTimeZone : NSObject // Help the compiler
|
|
|
|
@end
|
2002-10-01 10:25:40 +00:00
|
|
|
@class GSAbsTimeZone;
|
2005-07-08 11:48:37 +00:00
|
|
|
@interface GSAbsTimeZone : NSObject // Help the compiler
|
|
|
|
@end
|
|
|
|
|
2002-10-01 10:25:40 +00:00
|
|
|
|
2016-03-15 19:04:51 +00:00
|
|
|
#define DISTANT_FUTURE 63113990400.0
|
|
|
|
#define DISTANT_PAST -63113817600.0
|
|
|
|
|
2002-10-01 10:25:40 +00:00
|
|
|
static NSString *cformat = @"%Y-%m-%d %H:%M:%S %z";
|
2002-09-30 18:34:02 +00:00
|
|
|
|
2002-09-30 16:54:29 +00:00
|
|
|
static NSTimeZone *localTZ = nil;
|
|
|
|
|
2009-04-27 18:04:56 +00:00
|
|
|
static Class NSCalendarDateClass;
|
2002-09-30 18:34:02 +00:00
|
|
|
static Class absClass;
|
|
|
|
static Class dstClass;
|
|
|
|
|
|
|
|
static SEL offSEL;
|
|
|
|
static int (*offIMP)(id, SEL, id);
|
|
|
|
static int (*absOffIMP)(id, SEL, id);
|
|
|
|
static int (*dstOffIMP)(id, SEL, id);
|
|
|
|
|
|
|
|
static SEL abrSEL;
|
|
|
|
static NSString* (*abrIMP)(id, SEL, id);
|
|
|
|
static NSString* (*absAbrIMP)(id, SEL, id);
|
|
|
|
static NSString* (*dstAbrIMP)(id, SEL, id);
|
|
|
|
|
2016-04-08 17:32:07 +00:00
|
|
|
/* Do not fetch the default locale unless we actually need it.
|
|
|
|
* Tries to avoid recursion when loading NSUserDefaults containing dates.
|
|
|
|
* This is also a little more efficient with many date formats.
|
|
|
|
*/
|
2016-04-08 17:28:05 +00:00
|
|
|
#define LOCALE (nil == locale ? (locale = GSPrivateDefaultLocale()) : locale)
|
|
|
|
|
2002-09-30 18:34:02 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the offset from GMT for a date in a timezone ...
|
|
|
|
* Optimize for the local timezone, and less so for the other
|
|
|
|
* base library time zone classes.
|
|
|
|
*/
|
|
|
|
static inline int
|
|
|
|
offset(NSTimeZone *tz, NSDate *d)
|
|
|
|
{
|
2002-10-01 10:25:40 +00:00
|
|
|
if (tz == nil)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (tz == localTZ && offIMP != 0)
|
2002-09-30 18:34:02 +00:00
|
|
|
{
|
|
|
|
return (*offIMP)(tz, offSEL, d);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-02-22 10:13:20 +00:00
|
|
|
Class c = object_getClass(tz);
|
2002-09-30 18:34:02 +00:00
|
|
|
|
2002-10-01 10:25:40 +00:00
|
|
|
if (c == dstClass && dstOffIMP != 0)
|
2002-09-30 18:34:02 +00:00
|
|
|
{
|
|
|
|
return (*dstOffIMP)(tz, offSEL, d);
|
|
|
|
}
|
2002-10-01 10:25:40 +00:00
|
|
|
if (c == absClass && absOffIMP != 0)
|
2002-09-30 18:34:02 +00:00
|
|
|
{
|
|
|
|
return (*absOffIMP)(tz, offSEL, d);
|
|
|
|
}
|
|
|
|
return [tz secondsFromGMTForDate: d];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the offset from GMT for a date in a timezone ...
|
|
|
|
* Optimize for the local timezone, and less so for the other
|
|
|
|
* base library time zone classes.
|
|
|
|
*/
|
|
|
|
static inline NSString*
|
|
|
|
abbrev(NSTimeZone *tz, NSDate *d)
|
|
|
|
{
|
2002-10-01 10:25:40 +00:00
|
|
|
if (tz == nil)
|
|
|
|
{
|
|
|
|
return @"GMT";
|
|
|
|
}
|
|
|
|
if (tz == localTZ && abrIMP != 0)
|
2002-09-30 18:34:02 +00:00
|
|
|
{
|
|
|
|
return (*abrIMP)(tz, abrSEL, d);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-02-22 10:13:20 +00:00
|
|
|
Class c = object_getClass(tz);
|
2002-09-30 18:34:02 +00:00
|
|
|
|
2002-10-01 10:25:40 +00:00
|
|
|
if (c == dstClass && dstAbrIMP != 0)
|
2002-09-30 18:34:02 +00:00
|
|
|
{
|
|
|
|
return (*dstAbrIMP)(tz, abrSEL, d);
|
|
|
|
}
|
2002-10-01 10:25:40 +00:00
|
|
|
if (c == absClass && absAbrIMP != 0)
|
2002-09-30 18:34:02 +00:00
|
|
|
{
|
|
|
|
return (*absAbrIMP)(tz, abrSEL, d);
|
|
|
|
}
|
|
|
|
return [tz abbreviationForDate: d];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-23 20:42:32 +00:00
|
|
|
static inline NSUInteger
|
|
|
|
lastDayOfGregorianMonth(NSUInteger month, NSUInteger year)
|
2000-06-30 15:44:30 +00:00
|
|
|
{
|
|
|
|
switch (month)
|
|
|
|
{
|
|
|
|
case 2:
|
|
|
|
if ((((year % 4) == 0) && ((year % 100) != 0))
|
|
|
|
|| ((year % 400) == 0))
|
|
|
|
return 29;
|
|
|
|
else
|
|
|
|
return 28;
|
|
|
|
case 4:
|
|
|
|
case 6:
|
|
|
|
case 9:
|
|
|
|
case 11: return 30;
|
|
|
|
default: return 31;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-23 20:42:32 +00:00
|
|
|
static inline NSUInteger
|
|
|
|
absoluteGregorianDay(NSUInteger day, NSUInteger month, NSUInteger year)
|
2000-06-30 15:44:30 +00:00
|
|
|
{
|
2006-09-14 05:12:43 +00:00
|
|
|
if (month > 1)
|
2006-09-13 21:33:38 +00:00
|
|
|
{
|
2006-09-14 05:12:43 +00:00
|
|
|
while (--month > 0)
|
|
|
|
{
|
|
|
|
day = day + lastDayOfGregorianMonth(month, year);
|
|
|
|
}
|
2006-09-13 21:33:38 +00:00
|
|
|
}
|
|
|
|
if (year > 0)
|
|
|
|
{
|
|
|
|
year--;
|
|
|
|
}
|
2000-06-30 15:44:30 +00:00
|
|
|
return
|
2006-03-08 11:48:25 +00:00
|
|
|
(day // days this year
|
|
|
|
+ 365 * year // days in previous years ignoring leap days
|
|
|
|
+ year/4 // Julian leap days before this year...
|
|
|
|
- year/100 // ...minus prior century years...
|
|
|
|
+ year/400); // ...plus prior years divisible by 400
|
2000-06-30 15:44:30 +00:00
|
|
|
}
|
|
|
|
|
2016-03-15 17:23:13 +00:00
|
|
|
static inline NSInteger
|
2004-03-03 03:37:28 +00:00
|
|
|
dayOfCommonEra(NSTimeInterval when)
|
2002-09-28 14:11:55 +00:00
|
|
|
{
|
2016-03-15 17:23:13 +00:00
|
|
|
NSInteger r;
|
2002-09-28 14:11:55 +00:00
|
|
|
|
|
|
|
// Get reference date in terms of days
|
2006-03-10 10:46:37 +00:00
|
|
|
when /= 86400.0;
|
2002-09-28 14:11:55 +00:00
|
|
|
// Offset by Gregorian reference
|
2006-03-10 10:46:37 +00:00
|
|
|
when += GREGORIAN_REFERENCE;
|
2009-02-23 20:42:32 +00:00
|
|
|
r = (NSInteger)when;
|
2002-09-28 14:11:55 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2002-09-28 09:30:38 +00:00
|
|
|
static void
|
2016-03-15 18:22:57 +00:00
|
|
|
gregorianDateFromAbsolute(NSInteger abs,
|
|
|
|
NSInteger *day, NSInteger *month, NSInteger *year)
|
2002-09-28 09:30:38 +00:00
|
|
|
{
|
2016-03-15 18:22:57 +00:00
|
|
|
NSInteger y;
|
|
|
|
NSInteger m;
|
|
|
|
|
2002-09-28 09:30:38 +00:00
|
|
|
// Search forward year by year from approximate year
|
2016-03-15 18:22:57 +00:00
|
|
|
y = abs/366;
|
|
|
|
while (abs >= absoluteGregorianDay(1, 1, y+1))
|
2002-09-28 09:30:38 +00:00
|
|
|
{
|
2016-03-15 18:22:57 +00:00
|
|
|
y++;
|
2002-09-28 09:30:38 +00:00
|
|
|
}
|
|
|
|
// Search forward month by month from January
|
2016-03-15 18:22:57 +00:00
|
|
|
m = 1;
|
|
|
|
while (abs > absoluteGregorianDay(lastDayOfGregorianMonth(m, y), m, y))
|
2002-09-28 09:30:38 +00:00
|
|
|
{
|
2016-03-15 18:22:57 +00:00
|
|
|
m++;
|
2002-09-28 09:30:38 +00:00
|
|
|
}
|
2016-03-15 18:22:57 +00:00
|
|
|
*year = y;
|
|
|
|
*month = m;
|
|
|
|
*day = abs - absoluteGregorianDay(1, m, y) + 1;
|
2002-09-28 09:30:38 +00:00
|
|
|
}
|
|
|
|
|
2002-08-08 19:25:17 +00:00
|
|
|
/**
|
|
|
|
* Convert a broken out time specification into a time interval
|
2006-03-10 10:46:37 +00:00
|
|
|
* since the reference date.
|
2000-06-30 15:44:30 +00:00
|
|
|
*/
|
2006-03-10 10:46:37 +00:00
|
|
|
static NSTimeInterval
|
2006-09-13 21:33:38 +00:00
|
|
|
GSTime(unsigned day, unsigned month, unsigned year, unsigned hour, unsigned minute, unsigned second, unsigned mil)
|
2000-06-30 15:44:30 +00:00
|
|
|
{
|
|
|
|
NSTimeInterval a;
|
|
|
|
|
|
|
|
a = (NSTimeInterval)absoluteGregorianDay(day, month, year);
|
|
|
|
|
|
|
|
// Calculate date as GMT
|
|
|
|
a -= GREGORIAN_REFERENCE;
|
|
|
|
a = (NSTimeInterval)a * 86400;
|
2002-10-05 17:47:54 +00:00
|
|
|
a += hour * 3600;
|
|
|
|
a += minute * 60;
|
|
|
|
a += second;
|
2006-04-26 13:35:10 +00:00
|
|
|
a += ((NSTimeInterval)mil)/1000.0;
|
2000-06-30 15:44:30 +00:00
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
2002-09-28 15:45:26 +00:00
|
|
|
/**
|
|
|
|
* Convert a time interval since the reference date into broken out
|
|
|
|
* elements.<br />
|
2006-03-10 10:46:37 +00:00
|
|
|
* External - so NSTimeZone can use it ... but should really be static.
|
2002-09-28 15:45:26 +00:00
|
|
|
*/
|
2002-10-05 17:47:54 +00:00
|
|
|
void
|
2016-03-15 18:22:57 +00:00
|
|
|
GSBreakTime(NSTimeInterval when,
|
|
|
|
NSInteger *year, NSInteger *month, NSInteger *day,
|
|
|
|
NSInteger *hour, NSInteger *minute, NSInteger *second, NSInteger *mil)
|
2002-09-28 09:30:38 +00:00
|
|
|
{
|
2016-03-15 17:23:13 +00:00
|
|
|
NSInteger h, m, dayOfEra;
|
2002-09-28 09:30:38 +00:00
|
|
|
double a, b, c, d;
|
|
|
|
|
2014-02-13 10:41:55 +00:00
|
|
|
/* The 0.1 constant was experimentally derived to cause our behavior
|
|
|
|
* to match Mac OS X 10.9.1.
|
|
|
|
*/
|
|
|
|
when = floor(when * 1000.0 + 0.1) / 1000.0;
|
|
|
|
|
2002-09-28 09:30:38 +00:00
|
|
|
// Get reference date in terms of days
|
|
|
|
a = when / 86400.0;
|
|
|
|
// Offset by Gregorian reference
|
|
|
|
a += GREGORIAN_REFERENCE;
|
|
|
|
// result is the day of common era.
|
2009-02-23 20:42:32 +00:00
|
|
|
dayOfEra = (NSInteger)a;
|
2002-09-28 09:30:38 +00:00
|
|
|
|
|
|
|
// Calculate year, month, and day
|
|
|
|
gregorianDateFromAbsolute(dayOfEra, day, month, year);
|
|
|
|
|
|
|
|
// Calculate hour, minute, and seconds
|
|
|
|
d = dayOfEra - GREGORIAN_REFERENCE;
|
|
|
|
d *= 86400;
|
2014-02-13 10:41:55 +00:00
|
|
|
a = fabs(d - when);
|
2002-09-28 09:30:38 +00:00
|
|
|
b = a / 3600;
|
2009-02-23 20:42:32 +00:00
|
|
|
*hour = (NSInteger)b;
|
2002-09-28 09:30:38 +00:00
|
|
|
h = *hour;
|
|
|
|
h = h * 3600;
|
|
|
|
b = a - h;
|
|
|
|
b = b / 60;
|
2009-02-23 20:42:32 +00:00
|
|
|
*minute = (NSInteger)b;
|
2002-09-28 09:30:38 +00:00
|
|
|
m = *minute;
|
|
|
|
m = m * 60;
|
|
|
|
c = a - h - m;
|
2009-02-23 20:42:32 +00:00
|
|
|
*second = (NSInteger)c;
|
2014-02-13 10:41:55 +00:00
|
|
|
*mil = (NSInteger)rint((a - h - m - *second) * 1000.0);
|
2002-09-28 09:30:38 +00:00
|
|
|
}
|
|
|
|
|
2006-03-10 10:46:37 +00:00
|
|
|
/**
|
|
|
|
* Returns the current time (seconds since reference date) as an NSTimeInterval.
|
|
|
|
*/
|
|
|
|
NSTimeInterval
|
2010-12-27 07:03:50 +00:00
|
|
|
GSPrivateTimeNow(void)
|
2006-03-10 10:46:37 +00:00
|
|
|
{
|
2009-09-12 07:37:00 +00:00
|
|
|
NSTimeInterval t;
|
2016-03-09 13:16:16 +00:00
|
|
|
#if !defined(_WIN32)
|
2006-03-10 10:46:37 +00:00
|
|
|
struct timeval tp;
|
|
|
|
|
|
|
|
gettimeofday (&tp, NULL);
|
2009-09-12 07:37:00 +00:00
|
|
|
t = (NSTimeInterval)tp.tv_sec - NSTimeIntervalSince1970;
|
|
|
|
t += (NSTimeInterval)tp.tv_usec / (NSTimeInterval)1000000.0;
|
2009-09-14 12:44:52 +00:00
|
|
|
#if 1
|
|
|
|
/* This is a workaround for a bug on some SMP intel systems where the TSC
|
|
|
|
* clock information from the processors gets out of sync and causes a
|
|
|
|
* leap of 4398 seconds into the future for an instant, and then back.
|
2009-09-14 14:37:13 +00:00
|
|
|
* If we detect a time jump back by more than the sort of small interval
|
|
|
|
* that ntpd might do (or forwards by a very large amount) we refetch the
|
|
|
|
* system time to make sure we don't have a temporary glitch.
|
2009-09-14 12:44:52 +00:00
|
|
|
*/
|
|
|
|
{
|
|
|
|
static int old = 0;
|
|
|
|
|
|
|
|
if (old == 0)
|
|
|
|
{
|
|
|
|
old = tp.tv_sec;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int diff = tp.tv_sec - old;
|
|
|
|
|
|
|
|
old = tp.tv_sec;
|
2009-09-14 14:37:13 +00:00
|
|
|
if (diff < -1 || diff > 3000)
|
2009-09-14 12:44:52 +00:00
|
|
|
{
|
|
|
|
time_t now = (time_t)tp.tv_sec;
|
|
|
|
|
|
|
|
fprintf(stderr, "WARNING: system time changed by %d seconds: %s\n",
|
|
|
|
diff, ctime(&now));
|
|
|
|
/* Get time again ... should be OK now.
|
|
|
|
*/
|
2010-12-27 07:03:50 +00:00
|
|
|
t = GSPrivateTimeNow();
|
2009-09-14 12:44:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2006-03-10 10:46:37 +00:00
|
|
|
#else
|
|
|
|
SYSTEMTIME sys_time;
|
|
|
|
/*
|
|
|
|
* Get current GMT time, convert to NSTimeInterval since reference date,
|
|
|
|
*/
|
|
|
|
GetSystemTime(&sys_time);
|
|
|
|
t = GSTime(sys_time.wDay, sys_time.wMonth, sys_time.wYear, sys_time.wHour,
|
|
|
|
sys_time.wMinute, sys_time.wSecond, sys_time.wMilliseconds);
|
2016-03-09 13:16:16 +00:00
|
|
|
#endif /* _WIN32 */
|
2009-09-12 07:37:00 +00:00
|
|
|
|
|
|
|
return t;
|
2006-03-10 10:46:37 +00:00
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* An [NSDate] subclass which understands about timezones and provides
|
|
|
|
* methods for dealing with date and time information by calendar and
|
|
|
|
* with hours minutes and seconds.
|
|
|
|
*/
|
1997-10-28 14:34:49 +00:00
|
|
|
@implementation NSCalendarDate
|
1996-10-31 17:13:12 +00:00
|
|
|
|
1999-04-19 14:29:52 +00:00
|
|
|
+ (void) initialize
|
|
|
|
{
|
2012-10-17 13:47:11 +00:00
|
|
|
if (self == [NSCalendarDate class] && nil == NSCalendarDateClass)
|
1999-04-19 14:29:52 +00:00
|
|
|
{
|
2012-10-17 13:47:11 +00:00
|
|
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
|
|
|
|
2009-04-27 18:04:56 +00:00
|
|
|
NSCalendarDateClass = self;
|
1999-04-19 14:29:52 +00:00
|
|
|
[self setVersion: 1];
|
2002-09-30 16:54:29 +00:00
|
|
|
localTZ = RETAIN([NSTimeZone localTimeZone]);
|
2002-09-30 18:34:02 +00:00
|
|
|
|
|
|
|
dstClass = [GSTimeZone class];
|
2002-10-01 10:25:40 +00:00
|
|
|
absClass = [GSAbsTimeZone class];
|
2002-09-30 18:34:02 +00:00
|
|
|
|
|
|
|
offSEL = @selector(secondsFromGMTForDate:);
|
|
|
|
offIMP = (int (*)(id,SEL,id))
|
|
|
|
[localTZ methodForSelector: offSEL];
|
|
|
|
dstOffIMP = (int (*)(id,SEL,id))
|
|
|
|
[dstClass instanceMethodForSelector: offSEL];
|
|
|
|
absOffIMP = (int (*)(id,SEL,id))
|
|
|
|
[absClass instanceMethodForSelector: offSEL];
|
|
|
|
|
|
|
|
abrSEL = @selector(abbreviationForDate:);
|
|
|
|
abrIMP = (NSString* (*)(id,SEL,id))
|
|
|
|
[localTZ methodForSelector: abrSEL];
|
|
|
|
dstAbrIMP = (NSString* (*)(id,SEL,id))
|
|
|
|
[dstClass instanceMethodForSelector: abrSEL];
|
|
|
|
absAbrIMP = (NSString* (*)(id,SEL,id))
|
|
|
|
[absClass instanceMethodForSelector: abrSEL];
|
|
|
|
|
2012-10-17 13:47:11 +00:00
|
|
|
[pool release];
|
1999-04-19 14:29:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-08-08 19:25:17 +00:00
|
|
|
/**
|
2003-01-07 15:07:06 +00:00
|
|
|
* Return an NSCalendarDate for the current date and time using the
|
|
|
|
* default timezone.
|
2002-08-08 19:25:17 +00:00
|
|
|
*/
|
1999-08-25 16:15:12 +00:00
|
|
|
+ (id) calendarDate
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
1999-04-19 14:29:52 +00:00
|
|
|
id d = [[self alloc] init];
|
|
|
|
|
|
|
|
return AUTORELEASE(d);
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Return an NSCalendarDate generated from the supplied description
|
|
|
|
* using the format specified for parsing that string.<br />
|
|
|
|
* Calls -initWithString:calendarFormat: to create the date.
|
|
|
|
*/
|
1999-08-25 16:15:12 +00:00
|
|
|
+ (id) dateWithString: (NSString *)description
|
|
|
|
calendarFormat: (NSString *)format
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2001-08-28 22:11:32 +00:00
|
|
|
NSCalendarDate *d = [[self alloc] initWithString: description
|
|
|
|
calendarFormat: format];
|
1999-04-19 14:29:52 +00:00
|
|
|
return AUTORELEASE(d);
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Return an NSCalendarDate generated from the supplied description
|
|
|
|
* using the format specified for parsing that string and interpreting
|
|
|
|
* it according to the dictionary specified.<br />
|
|
|
|
* Calls -initWithString:calendarFormat:locale: to create the date.
|
|
|
|
*/
|
1999-08-25 16:15:12 +00:00
|
|
|
+ (id) dateWithString: (NSString *)description
|
|
|
|
calendarFormat: (NSString *)format
|
|
|
|
locale: (NSDictionary *)dictionary
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2001-08-28 22:11:32 +00:00
|
|
|
NSCalendarDate *d = [[self alloc] initWithString: description
|
|
|
|
calendarFormat: format
|
|
|
|
locale: dictionary];
|
1999-04-19 14:29:52 +00:00
|
|
|
return AUTORELEASE(d);
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
2005-02-22 11:22:44 +00:00
|
|
|
* Creates and returns an NSCalendarDate from the specified values
|
2003-01-07 15:07:06 +00:00
|
|
|
* by calling -initWithYear:month:day:hour:minute:second:timeZone:
|
|
|
|
*/
|
2009-02-23 20:42:32 +00:00
|
|
|
+ (id) dateWithYear: (NSInteger)year
|
|
|
|
month: (NSUInteger)month
|
|
|
|
day: (NSUInteger)day
|
|
|
|
hour: (NSUInteger)hour
|
|
|
|
minute: (NSUInteger)minute
|
|
|
|
second: (NSUInteger)second
|
1999-08-25 16:15:12 +00:00
|
|
|
timeZone: (NSTimeZone *)aTimeZone
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2001-08-28 22:11:32 +00:00
|
|
|
NSCalendarDate *d = [[self alloc] initWithYear: year
|
2002-09-28 09:30:38 +00:00
|
|
|
month: month
|
|
|
|
day: day
|
|
|
|
hour: hour
|
|
|
|
minute: minute
|
|
|
|
second: second
|
|
|
|
timeZone: aTimeZone];
|
1999-04-19 14:29:52 +00:00
|
|
|
return AUTORELEASE(d);
|
|
|
|
}
|
|
|
|
|
2024-11-01 08:43:15 +00:00
|
|
|
- (NSTimeInterval) timeIntervalSinceReferenceDate
|
|
|
|
{
|
|
|
|
return _seconds_since_ref;
|
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Creates and returns a new NSCalendarDate object by taking the
|
|
|
|
* value of the receiver and adding the interval in seconds specified.
|
|
|
|
*/
|
1999-12-13 12:14:01 +00:00
|
|
|
- (id) addTimeInterval: (NSTimeInterval)seconds
|
2012-09-10 05:18:09 +00:00
|
|
|
{
|
|
|
|
return [self dateByAddingTimeInterval: seconds];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (Class) classForCoder
|
|
|
|
{
|
|
|
|
return [self class];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates and returns a new NSCalendarDate object by taking the
|
|
|
|
* value of the receiver and adding the interval specified.
|
|
|
|
*/
|
|
|
|
- (id) dateByAddingTimeInterval: (NSTimeInterval)seconds
|
1999-12-13 12:14:01 +00:00
|
|
|
{
|
|
|
|
id newObj = [[self class] dateWithTimeIntervalSinceReferenceDate:
|
|
|
|
[self timeIntervalSinceReferenceDate] + seconds];
|
|
|
|
|
|
|
|
[newObj setTimeZone: [self timeZoneDetail]];
|
2011-12-29 18:28:27 +00:00
|
|
|
[newObj setCalendarFormat: [self calendarFormat]];
|
1999-12-13 12:14:01 +00:00
|
|
|
|
|
|
|
return newObj;
|
|
|
|
}
|
|
|
|
|
1999-08-25 16:15:12 +00:00
|
|
|
- (id) replacementObjectForPortCoder: (NSPortCoder*)aRmc
|
1999-04-19 14:29:52 +00:00
|
|
|
{
|
|
|
|
return self;
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
1999-04-19 14:29:52 +00:00
|
|
|
- (void) encodeWithCoder: (NSCoder*)coder
|
1998-03-12 14:21:20 +00:00
|
|
|
{
|
1999-12-13 12:14:01 +00:00
|
|
|
[coder encodeValueOfObjCType: @encode(NSTimeInterval)
|
|
|
|
at: &_seconds_since_ref];
|
1999-09-16 07:21:34 +00:00
|
|
|
[coder encodeObject: _calendar_format];
|
|
|
|
[coder encodeObject: _time_zone];
|
1998-03-12 14:21:20 +00:00
|
|
|
}
|
|
|
|
|
1999-04-19 14:29:52 +00:00
|
|
|
- (id) initWithCoder: (NSCoder*)coder
|
1998-03-12 14:21:20 +00:00
|
|
|
{
|
1999-12-13 12:14:01 +00:00
|
|
|
[coder decodeValueOfObjCType: @encode(NSTimeInterval)
|
|
|
|
at: &_seconds_since_ref];
|
1999-09-16 07:21:34 +00:00
|
|
|
[coder decodeValueOfObjCType: @encode(id) at: &_calendar_format];
|
|
|
|
[coder decodeValueOfObjCType: @encode(id) at: &_time_zone];
|
1999-04-19 14:29:52 +00:00
|
|
|
return self;
|
1998-03-12 14:21:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
RELEASE(_calendar_format);
|
|
|
|
RELEASE(_time_zone);
|
1999-04-19 14:29:52 +00:00
|
|
|
[super dealloc];
|
1998-03-12 14:21:20 +00:00
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Initializes an NSCalendarDate using the specified description and the
|
|
|
|
* default calendar format and locale.<br />
|
|
|
|
* Calls -initWithString:calendarFormat:locale:
|
1999-08-25 16:15:12 +00:00
|
|
|
*/
|
|
|
|
- (id) initWithString: (NSString *)description
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
|
|
|
return [self initWithString: description
|
2002-10-01 10:25:40 +00:00
|
|
|
calendarFormat: cformat
|
1999-08-25 16:15:12 +00:00
|
|
|
locale: nil];
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Initializes an NSCalendarDate using the specified description and format
|
|
|
|
* string interpreted in the default locale.<br />
|
|
|
|
* Calls -initWithString:calendarFormat:locale:
|
|
|
|
*/
|
1999-08-25 16:15:12 +00:00
|
|
|
- (id) initWithString: (NSString *)description
|
|
|
|
calendarFormat: (NSString *)format
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
|
|
|
return [self initWithString: description
|
|
|
|
calendarFormat: format
|
1999-08-25 16:15:12 +00:00
|
|
|
locale: nil];
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
2000-09-10 19:16:22 +00:00
|
|
|
/*
|
|
|
|
* read up to the specified number of characters, terminating at a non-digit
|
|
|
|
* except for leading whitespace characters.
|
|
|
|
*/
|
2006-11-13 16:50:30 +00:00
|
|
|
static inline int getDigits(const char *from, char *to, int limit, BOOL *error)
|
2000-09-10 19:16:22 +00:00
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
int j = 0;
|
2006-11-13 16:50:30 +00:00
|
|
|
|
2000-09-10 19:16:22 +00:00
|
|
|
BOOL foundDigit = NO;
|
|
|
|
|
|
|
|
while (i < limit)
|
|
|
|
{
|
|
|
|
if (isdigit(from[i]))
|
|
|
|
{
|
|
|
|
to[j++] = from[i];
|
|
|
|
foundDigit = YES;
|
|
|
|
}
|
|
|
|
else if (isspace(from[i]))
|
|
|
|
{
|
|
|
|
if (foundDigit == YES)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
to[j] = '\0';
|
2006-11-13 16:50:30 +00:00
|
|
|
if (j == 0)
|
|
|
|
{
|
|
|
|
*error = YES; // No digits read
|
|
|
|
}
|
2000-09-10 19:16:22 +00:00
|
|
|
return i;
|
|
|
|
}
|
1999-12-10 00:59:40 +00:00
|
|
|
|
2000-11-30 12:19:50 +00:00
|
|
|
#define hadY 1
|
|
|
|
#define hadM 2
|
|
|
|
#define hadD 4
|
|
|
|
#define hadh 8
|
|
|
|
#define hadm 16
|
|
|
|
#define hads 32
|
|
|
|
#define hadw 64
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Initializes an NSCalendarDate using the specified description and format
|
|
|
|
* string interpreted in the given locale.<br />
|
2004-05-13 09:10:35 +00:00
|
|
|
* If description does not match fmt exactly, this method returns nil.<br />
|
2006-11-13 16:50:30 +00:00
|
|
|
* Excess characters in the description (after the format is matched)
|
|
|
|
* are ignored.<br />
|
2003-01-07 15:07:06 +00:00
|
|
|
* Format specifiers are -
|
|
|
|
* <list>
|
|
|
|
* <item>
|
|
|
|
* %% literal % character
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %a abbreviated weekday name according to locale
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %A full weekday name according to locale
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %b abbreviated month name according to locale
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %B full month name according to locale
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %c same as '%X %x'
|
|
|
|
* </item>
|
|
|
|
* <item>
|
2010-03-17 06:00:14 +00:00
|
|
|
* %d day of month as a two digit decimal number
|
2003-01-07 15:07:06 +00:00
|
|
|
* </item>
|
|
|
|
* <item>
|
2010-03-17 06:00:14 +00:00
|
|
|
* %e same as %d without leading zero
|
2003-01-07 15:07:06 +00:00
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %F milliseconds as a decimal number
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %H hour as a decimal number using 24-hour clock
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %I hour as a decimal number using 12-hour clock
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %j day of year as a decimal number
|
|
|
|
* </item>
|
|
|
|
* <item>
|
2010-03-17 05:45:15 +00:00
|
|
|
* %k same as %H without leading zero (leading space is used instead)
|
|
|
|
* </item>
|
|
|
|
* <item>
|
2003-01-07 15:07:06 +00:00
|
|
|
* %m month as decimal number
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %M minute as decimal number
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %p 'am' or 'pm'
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %S second as decimal number
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %U week of the current year as decimal number (Sunday first day)
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %W week of the current year as decimal number (Monday first day)
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %w day of the week as decimal number (Sunday = 0)
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %x date with date representation for locale
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %X time with time representation for locale
|
|
|
|
* </item>
|
|
|
|
* <item>
|
2005-02-22 11:22:44 +00:00
|
|
|
* %y year as a decimal number without century
|
2003-01-07 15:07:06 +00:00
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %Y year as a decimal number with century
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %z time zone offset in hours and minutes from GMT (HHMM)
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %Z time zone abbreviation
|
|
|
|
* </item>
|
|
|
|
* </list>
|
2004-05-13 09:28:52 +00:00
|
|
|
* If no year is specified in the format, the current year is assumed.<br />
|
|
|
|
* If no month is specified in the format, January is assumed.<br />
|
|
|
|
* If no day is specified in the format, 1 is assumed.<br />
|
|
|
|
* If no hour is specified in the format, 0 is assumed.<br />
|
|
|
|
* If no minute is specified in the format, 0 is assumed.<br />
|
|
|
|
* If no second is specified in the format, 0 is assumed.<br />
|
|
|
|
* If no millisecond is specified in the format, 0 is assumed.<br />
|
|
|
|
* If no timezone is specified in the format, the local timezone is assumed.
|
2010-03-17 05:45:15 +00:00
|
|
|
* <p>If GSMacOSXCompatible is YES, the %k specifier is not recognized.</p>
|
2006-11-15 16:17:23 +00:00
|
|
|
* <p>NB. Where the format calls for a numeric value and the string contains
|
|
|
|
* fewer digits than expected, the value will be accepted and left padded
|
|
|
|
* with zeros to the expected size.<br />
|
|
|
|
* For instance, the '%z' format implies four digits (two for the hour
|
|
|
|
* offset and two for the digit offset) and if the string contains '01'
|
|
|
|
* it will be treated as '0001' ie. a timezone offset of 1 minute.<br />
|
|
|
|
* Similarly, the '%F' format implies three digits, so a value of '1'
|
|
|
|
* would be treated as '001' or 1 millisecond, not a tenth of a second
|
|
|
|
* (as you might assume as '%F' is usually used after a decimal separator).
|
|
|
|
* </p>
|
2003-01-07 15:07:06 +00:00
|
|
|
*/
|
2005-02-22 11:22:44 +00:00
|
|
|
- (id) initWithString: (NSString*)description
|
2004-05-13 09:10:35 +00:00
|
|
|
calendarFormat: (NSString*)fmt
|
|
|
|
locale: (NSDictionary*)locale
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2004-05-13 09:10:35 +00:00
|
|
|
int milliseconds = 0;
|
|
|
|
int year = 1;
|
|
|
|
int month = 1;
|
|
|
|
int day = 1;
|
|
|
|
int hour = 0;
|
|
|
|
int min = 0;
|
|
|
|
int sec = 0;
|
|
|
|
NSTimeZone *tz = nil;
|
|
|
|
BOOL ampm = NO;
|
2010-08-06 16:20:19 +00:00
|
|
|
BOOL isPM = NO;
|
2005-02-22 11:22:44 +00:00
|
|
|
BOOL twelveHrClock = NO;
|
2004-05-13 09:10:35 +00:00
|
|
|
int julianWeeks = -1, weekStartsMonday = 0, dayOfWeek = -1;
|
2005-06-08 05:19:29 +00:00
|
|
|
const char *source;
|
|
|
|
unsigned sourceLen;
|
2004-05-13 09:10:35 +00:00
|
|
|
unichar *format;
|
|
|
|
unsigned formatLen;
|
|
|
|
unsigned formatIdx = 0;
|
|
|
|
unsigned sourceIdx = 0;
|
2006-11-25 05:40:19 +00:00
|
|
|
char tmpStr[120];
|
2004-05-13 09:10:35 +00:00
|
|
|
unsigned int tmpIdx;
|
2006-11-25 05:40:19 +00:00
|
|
|
unsigned int tmpEnd;
|
2004-05-13 09:10:35 +00:00
|
|
|
unsigned had = 0;
|
|
|
|
unsigned int pos;
|
|
|
|
BOOL hadPercent = NO;
|
|
|
|
NSMutableData *fd;
|
|
|
|
BOOL changedFormat = NO;
|
|
|
|
BOOL error = NO;
|
2005-02-22 11:22:44 +00:00
|
|
|
|
2005-06-08 05:19:29 +00:00
|
|
|
if (description == nil)
|
|
|
|
{
|
|
|
|
description = @"";
|
|
|
|
}
|
|
|
|
source = [description cString];
|
|
|
|
sourceLen = strlen(source);
|
2004-05-13 09:10:35 +00:00
|
|
|
if (fmt == nil)
|
2000-04-05 21:43:40 +00:00
|
|
|
{
|
2016-04-08 17:28:05 +00:00
|
|
|
fmt = [LOCALE objectForKey: NSTimeDateFormatString];
|
2000-04-05 21:43:40 +00:00
|
|
|
if (fmt == nil)
|
1999-12-13 12:14:01 +00:00
|
|
|
{
|
2004-05-13 09:10:35 +00:00
|
|
|
fmt = @"";
|
2000-04-05 21:43:40 +00:00
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
}
|
2000-11-30 12:19:50 +00:00
|
|
|
|
2004-05-13 09:10:35 +00:00
|
|
|
/*
|
|
|
|
* Get format into a buffer, leaving room for expansion in case it has
|
|
|
|
* escapes that need to be converted.
|
|
|
|
*/
|
|
|
|
formatLen = [fmt length];
|
|
|
|
fd = [[NSMutableData alloc]
|
|
|
|
initWithLength: (formatLen + 32) * sizeof(unichar)];
|
|
|
|
format = (unichar*)[fd mutableBytes];
|
|
|
|
[fmt getCharacters: format];
|
2000-11-30 12:19:50 +00:00
|
|
|
|
2004-05-13 09:10:35 +00:00
|
|
|
/*
|
|
|
|
* Expand any sequences to their basic components.
|
|
|
|
*/
|
|
|
|
for (pos = 0; pos < formatLen; pos++)
|
|
|
|
{
|
|
|
|
unichar c = format[pos];
|
2000-11-30 12:19:50 +00:00
|
|
|
|
2004-05-13 09:10:35 +00:00
|
|
|
if (c == '%')
|
|
|
|
{
|
|
|
|
if (hadPercent == YES)
|
|
|
|
{
|
|
|
|
hadPercent = NO;
|
|
|
|
}
|
|
|
|
else
|
2000-11-30 12:19:50 +00:00
|
|
|
{
|
2004-05-13 09:10:35 +00:00
|
|
|
hadPercent = YES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (hadPercent == YES)
|
|
|
|
{
|
|
|
|
NSString *sub = nil;
|
|
|
|
|
|
|
|
if (c == 'c')
|
2000-11-30 12:19:50 +00:00
|
|
|
{
|
2016-04-08 17:28:05 +00:00
|
|
|
sub = [LOCALE objectForKey: NSTimeDateFormatString];
|
2004-05-13 09:38:03 +00:00
|
|
|
if (sub == nil)
|
|
|
|
{
|
|
|
|
sub = @"%X %x";
|
|
|
|
}
|
2000-11-30 12:19:50 +00:00
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
else if (c == 'R')
|
2000-11-30 12:19:50 +00:00
|
|
|
{
|
2004-05-13 09:10:35 +00:00
|
|
|
sub = @"%H:%M";
|
2000-11-30 12:19:50 +00:00
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
else if (c == 'r')
|
2000-11-30 12:19:50 +00:00
|
|
|
{
|
2004-05-13 09:10:35 +00:00
|
|
|
sub = @"%I:%M:%S %p";
|
|
|
|
}
|
2006-01-11 08:37:16 +00:00
|
|
|
else if (c == 'T')
|
|
|
|
{
|
|
|
|
sub = @"%H:%M:%S";
|
|
|
|
}
|
|
|
|
else if (c == 't')
|
|
|
|
{
|
|
|
|
sub = @"\t";
|
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
else if (c == 'X')
|
|
|
|
{
|
2016-04-08 17:28:05 +00:00
|
|
|
sub = [LOCALE objectForKey: NSTimeFormatString];
|
2004-05-13 09:38:03 +00:00
|
|
|
if (sub == nil)
|
|
|
|
{
|
|
|
|
sub = @"%H-%M-%S";
|
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
}
|
|
|
|
else if (c == 'x')
|
|
|
|
{
|
2016-04-08 17:28:05 +00:00
|
|
|
sub = [LOCALE objectForKey: NSShortDateFormatString];
|
2004-05-13 09:38:03 +00:00
|
|
|
if (sub == nil)
|
|
|
|
{
|
|
|
|
sub = @"%y-%m-%d";
|
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
}
|
2000-11-30 12:19:50 +00:00
|
|
|
|
2004-05-13 09:10:35 +00:00
|
|
|
if (sub != nil)
|
|
|
|
{
|
|
|
|
unsigned sLen = [sub length];
|
|
|
|
int i;
|
2000-11-30 12:19:50 +00:00
|
|
|
|
2004-05-13 09:10:35 +00:00
|
|
|
if (sLen > 2)
|
2000-11-30 12:19:50 +00:00
|
|
|
{
|
2004-05-13 09:10:35 +00:00
|
|
|
[fd setLength:
|
|
|
|
(formatLen + sLen - 2) * sizeof(unichar)];
|
|
|
|
format = (unichar*)[fd mutableBytes];
|
2009-02-23 20:42:32 +00:00
|
|
|
for (i = formatLen-1; i > (NSInteger)pos; i--)
|
2000-11-30 12:19:50 +00:00
|
|
|
{
|
2004-05-13 09:10:35 +00:00
|
|
|
format[i+sLen-2] = format[i];
|
2000-11-30 12:19:50 +00:00
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-02-23 20:42:32 +00:00
|
|
|
for (i = pos+1; i < (NSInteger)formatLen; i++)
|
2000-11-30 12:19:50 +00:00
|
|
|
{
|
2004-05-13 09:10:35 +00:00
|
|
|
format[i+sLen-2] = format[i];
|
2000-11-30 12:19:50 +00:00
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
[fd setLength:
|
|
|
|
(formatLen + sLen - 2) * sizeof(unichar)];
|
|
|
|
format = (unichar*)[fd mutableBytes];
|
2000-11-30 12:19:50 +00:00
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
[sub getCharacters: &format[pos-1]];
|
|
|
|
formatLen += sLen - 2;
|
|
|
|
changedFormat = YES;
|
|
|
|
pos -= 2; // Re-parse the newly substituted data.
|
2000-11-30 12:19:50 +00:00
|
|
|
}
|
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
hadPercent = NO;
|
2000-11-30 12:19:50 +00:00
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
}
|
2000-11-30 12:19:50 +00:00
|
|
|
|
2004-05-13 09:10:35 +00:00
|
|
|
/*
|
|
|
|
* Set up calendar format.
|
|
|
|
*/
|
|
|
|
if (changedFormat == YES)
|
|
|
|
{
|
|
|
|
fmt = [NSString stringWithCharacters: format length: formatLen];
|
|
|
|
}
|
|
|
|
ASSIGN(_calendar_format, fmt);
|
2000-11-03 17:13:00 +00:00
|
|
|
|
2004-05-13 09:10:35 +00:00
|
|
|
//
|
|
|
|
// WARNING:
|
|
|
|
// -Most locale stuff is dubious at best.
|
|
|
|
// -Long day and month names depend on a non-alpha character after the
|
|
|
|
// last digit to work.
|
|
|
|
//
|
2000-04-05 21:43:40 +00:00
|
|
|
|
2006-11-13 16:50:30 +00:00
|
|
|
while (error == NO && formatIdx < formatLen)
|
2004-05-13 09:10:35 +00:00
|
|
|
{
|
|
|
|
if (format[formatIdx] != '%')
|
2000-04-05 21:43:40 +00:00
|
|
|
{
|
2004-05-13 09:10:35 +00:00
|
|
|
// If it's not a format specifier, ignore it.
|
|
|
|
if (isspace(format[formatIdx]))
|
2000-04-05 21:43:40 +00:00
|
|
|
{
|
2004-05-13 09:10:35 +00:00
|
|
|
// Skip any amount of white space.
|
|
|
|
while (source[sourceIdx] != 0 && isspace(source[sourceIdx]))
|
2000-09-10 19:16:22 +00:00
|
|
|
{
|
2004-05-13 09:10:35 +00:00
|
|
|
sourceIdx++;
|
2000-09-10 19:16:22 +00:00
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (sourceIdx < sourceLen)
|
2000-09-10 19:16:22 +00:00
|
|
|
{
|
2004-05-13 09:10:35 +00:00
|
|
|
if (source[sourceIdx] != format[formatIdx])
|
2000-09-10 19:16:22 +00:00
|
|
|
{
|
2004-05-13 09:10:35 +00:00
|
|
|
error = YES;
|
|
|
|
NSDebugMLog(
|
|
|
|
@"Expected literal '%c' but got '%c' parsing"
|
|
|
|
@"'%@' using '%@'", format[formatIdx],
|
|
|
|
source[sourceIdx], description, fmt);
|
2000-09-10 19:16:22 +00:00
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
sourceIdx++;
|
2000-09-10 19:16:22 +00:00
|
|
|
}
|
2000-04-05 21:43:40 +00:00
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Skip '%'
|
|
|
|
formatIdx++;
|
2010-03-04 08:25:52 +00:00
|
|
|
while (formatIdx < formatLen && isdigit(format[formatIdx]))
|
|
|
|
{
|
|
|
|
formatIdx++; // skip field width
|
|
|
|
}
|
|
|
|
if (formatIdx < formatLen)
|
2004-05-13 09:10:35 +00:00
|
|
|
{
|
2010-03-04 08:25:52 +00:00
|
|
|
switch (format[formatIdx])
|
|
|
|
{
|
|
|
|
case '%':
|
|
|
|
// skip literal %
|
|
|
|
if (sourceIdx < sourceLen)
|
|
|
|
{
|
|
|
|
if (source[sourceIdx] != '%')
|
|
|
|
{
|
|
|
|
error = YES;
|
|
|
|
NSDebugMLog(
|
|
|
|
@"Expected literal '%%' but got '%c' parsing"
|
|
|
|
@"'%@' using '%@'", source[sourceIdx],
|
|
|
|
description, fmt);
|
|
|
|
}
|
|
|
|
sourceIdx++;
|
|
|
|
}
|
|
|
|
else
|
2000-09-10 19:16:22 +00:00
|
|
|
{
|
2004-05-13 09:10:35 +00:00
|
|
|
error = YES;
|
|
|
|
NSDebugMLog(
|
2010-03-04 08:25:52 +00:00
|
|
|
@"Expected literal '%%' but got end of string parsing"
|
2013-07-03 06:46:41 +00:00
|
|
|
@"'%@' using '%@'", description, fmt);
|
2000-09-10 19:16:22 +00:00
|
|
|
}
|
2010-03-04 08:25:52 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'a':
|
|
|
|
/* FIXME ... Should look for all values from the locale,
|
|
|
|
* matching for longest values first, rather than (wrongly)
|
|
|
|
* assuming a fixed length of three characters.
|
|
|
|
*/
|
|
|
|
tmpStr[0] = toupper(source[sourceIdx]);
|
|
|
|
if (sourceIdx < sourceLen)
|
|
|
|
sourceIdx++;
|
|
|
|
tmpStr[1] = tolower(source[sourceIdx]);
|
|
|
|
if (sourceIdx < sourceLen)
|
|
|
|
sourceIdx++;
|
|
|
|
tmpStr[2] = tolower(source[sourceIdx]);
|
|
|
|
if (sourceIdx < sourceLen)
|
|
|
|
sourceIdx++;
|
|
|
|
tmpStr[3] = '\0';
|
2000-04-05 21:43:40 +00:00
|
|
|
{
|
2010-03-04 08:25:52 +00:00
|
|
|
NSString *currDay;
|
|
|
|
NSArray *dayNames;
|
|
|
|
|
|
|
|
currDay = [[NSString alloc] initWithCString: tmpStr];
|
2016-04-08 17:28:05 +00:00
|
|
|
dayNames = [LOCALE objectForKey: NSShortWeekDayNameArray];
|
2010-03-04 08:25:52 +00:00
|
|
|
for (tmpIdx = 0; tmpIdx < 7; tmpIdx++)
|
2000-04-05 21:43:40 +00:00
|
|
|
{
|
2010-03-04 08:25:52 +00:00
|
|
|
if ([[dayNames objectAtIndex: tmpIdx] isEqual:
|
|
|
|
currDay] == YES)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2000-04-05 21:43:40 +00:00
|
|
|
}
|
2010-03-04 08:25:52 +00:00
|
|
|
if (tmpIdx == 7)
|
|
|
|
{
|
|
|
|
error = YES;
|
|
|
|
NSDebugMLog(@"Day of week '%@' not found in locale",
|
|
|
|
currDay);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dayOfWeek = tmpIdx;
|
|
|
|
had |= hadw;
|
|
|
|
}
|
|
|
|
RELEASE(currDay);
|
2004-05-13 09:10:35 +00:00
|
|
|
}
|
2010-03-04 08:25:52 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'A':
|
|
|
|
/* FIXME ... Should look for all values from the locale,
|
|
|
|
* matching for longest values first, rather than (wrongly)
|
|
|
|
* assuming the name contains only western letters.
|
|
|
|
*/
|
|
|
|
tmpEnd = sizeof(tmpStr) - 1;
|
|
|
|
if (sourceLen - sourceIdx < tmpEnd)
|
2000-04-05 21:43:40 +00:00
|
|
|
{
|
2010-03-04 08:25:52 +00:00
|
|
|
tmpEnd = sourceLen - sourceIdx;
|
2000-04-05 21:43:40 +00:00
|
|
|
}
|
2010-03-04 08:25:52 +00:00
|
|
|
for (tmpIdx = 0; tmpIdx < tmpEnd; tmpIdx++)
|
2004-05-13 09:10:35 +00:00
|
|
|
{
|
2010-03-04 08:25:52 +00:00
|
|
|
if (isalpha(source[sourceIdx + tmpIdx]))
|
|
|
|
{
|
|
|
|
tmpStr[tmpIdx] = source[sourceIdx + tmpIdx];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
}
|
2010-03-04 08:25:52 +00:00
|
|
|
tmpStr[tmpIdx] = '\0';
|
|
|
|
sourceIdx += tmpIdx;
|
2004-05-13 09:10:35 +00:00
|
|
|
{
|
2010-03-04 08:25:52 +00:00
|
|
|
NSString *currDay;
|
|
|
|
NSArray *dayNames;
|
|
|
|
|
|
|
|
currDay = [[NSString alloc] initWithCString: tmpStr];
|
2016-04-08 17:28:05 +00:00
|
|
|
dayNames = [LOCALE objectForKey: NSWeekDayNameArray];
|
2010-03-04 08:25:52 +00:00
|
|
|
for (tmpIdx = 0; tmpIdx < 7; tmpIdx++)
|
2000-04-05 21:43:40 +00:00
|
|
|
{
|
2010-03-04 08:25:52 +00:00
|
|
|
if ([[dayNames objectAtIndex: tmpIdx] isEqual:
|
|
|
|
currDay] == YES)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2000-04-05 21:43:40 +00:00
|
|
|
}
|
2010-03-04 08:25:52 +00:00
|
|
|
if (tmpIdx == 7)
|
|
|
|
{
|
|
|
|
error = YES;
|
|
|
|
NSDebugMLog(@"Day of week '%@' not found in locale",
|
|
|
|
currDay);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dayOfWeek = tmpIdx;
|
|
|
|
had |= hadw;
|
|
|
|
}
|
|
|
|
RELEASE(currDay);
|
2004-05-13 09:10:35 +00:00
|
|
|
}
|
2010-03-04 08:25:52 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'b':
|
|
|
|
/* FIXME ... Should look for all values from the locale,
|
|
|
|
* matching for longest values first, rather than (wrongly)
|
|
|
|
* assuming a fixed length of three characters.
|
|
|
|
*/
|
|
|
|
tmpStr[0] = toupper(source[sourceIdx]);
|
|
|
|
if (sourceIdx < sourceLen)
|
|
|
|
sourceIdx++;
|
|
|
|
tmpStr[1] = tolower(source[sourceIdx]);
|
|
|
|
if (sourceIdx < sourceLen)
|
|
|
|
sourceIdx++;
|
|
|
|
tmpStr[2] = tolower(source[sourceIdx]);
|
|
|
|
if (sourceIdx < sourceLen)
|
|
|
|
sourceIdx++;
|
|
|
|
tmpStr[3] = '\0';
|
2004-05-13 09:10:35 +00:00
|
|
|
{
|
2010-03-04 08:25:52 +00:00
|
|
|
NSString *currMonth;
|
|
|
|
NSArray *monthNames;
|
2000-04-05 21:43:40 +00:00
|
|
|
|
2010-03-04 08:25:52 +00:00
|
|
|
currMonth = [[NSString alloc] initWithCString: tmpStr];
|
2016-04-08 17:28:05 +00:00
|
|
|
monthNames = [LOCALE objectForKey: NSShortMonthNameArray];
|
2000-04-05 21:43:40 +00:00
|
|
|
|
2010-03-04 08:25:52 +00:00
|
|
|
for (tmpIdx = 0; tmpIdx < 12; tmpIdx++)
|
2000-04-05 21:43:40 +00:00
|
|
|
{
|
2010-03-04 08:25:52 +00:00
|
|
|
if ([[monthNames objectAtIndex: tmpIdx]
|
|
|
|
isEqual: currMonth] == YES)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2000-04-05 21:43:40 +00:00
|
|
|
}
|
2010-03-04 08:25:52 +00:00
|
|
|
if (tmpIdx == 12)
|
|
|
|
{
|
|
|
|
error = YES;
|
|
|
|
NSDebugMLog(@"Month of year '%@' not found in locale",
|
|
|
|
currMonth);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
month = tmpIdx+1;
|
|
|
|
had |= hadM;
|
|
|
|
}
|
|
|
|
RELEASE(currMonth);
|
2004-05-13 09:10:35 +00:00
|
|
|
}
|
2010-03-04 08:25:52 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'B':
|
|
|
|
/* FIXME ... Should look for all values from the locale,
|
|
|
|
* matching for longest values first, rather than (wrongly)
|
|
|
|
* assuming the name contains only western letters.
|
|
|
|
*/
|
|
|
|
tmpEnd = sizeof(tmpStr) - 1;
|
|
|
|
if (sourceLen - sourceIdx < tmpEnd)
|
2000-04-05 21:43:40 +00:00
|
|
|
{
|
2010-03-04 08:25:52 +00:00
|
|
|
tmpEnd = sourceLen - sourceIdx;
|
2000-04-05 21:43:40 +00:00
|
|
|
}
|
2010-03-04 08:25:52 +00:00
|
|
|
for (tmpIdx = 0; tmpIdx < tmpEnd; tmpIdx++)
|
2004-05-13 09:10:35 +00:00
|
|
|
{
|
2010-03-04 08:25:52 +00:00
|
|
|
if (isalpha(source[sourceIdx + tmpIdx]))
|
|
|
|
{
|
|
|
|
tmpStr[tmpIdx] = source[sourceIdx + tmpIdx];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
}
|
2010-03-04 08:25:52 +00:00
|
|
|
tmpStr[tmpIdx] = '\0';
|
|
|
|
sourceIdx += tmpIdx;
|
|
|
|
{
|
|
|
|
NSString *currMonth;
|
|
|
|
NSArray *monthNames;
|
2000-04-05 21:43:40 +00:00
|
|
|
|
2010-03-04 08:25:52 +00:00
|
|
|
currMonth = [[NSString alloc] initWithCString: tmpStr];
|
2016-04-08 17:28:05 +00:00
|
|
|
monthNames = [LOCALE objectForKey: NSMonthNameArray];
|
2000-04-05 21:43:40 +00:00
|
|
|
|
2010-03-04 08:25:52 +00:00
|
|
|
for (tmpIdx = 0; tmpIdx < 12; tmpIdx++)
|
2000-04-05 21:43:40 +00:00
|
|
|
{
|
2010-03-04 08:25:52 +00:00
|
|
|
if ([[monthNames objectAtIndex: tmpIdx]
|
|
|
|
isEqual: currMonth] == YES)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2000-04-05 21:43:40 +00:00
|
|
|
}
|
2010-03-04 08:25:52 +00:00
|
|
|
if (tmpIdx == 12)
|
|
|
|
{
|
|
|
|
error = YES;
|
|
|
|
NSDebugMLog(@"Month of year '%@' not found in locale",
|
|
|
|
currMonth);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
month = tmpIdx+1;
|
|
|
|
had |= hadM;
|
|
|
|
}
|
|
|
|
RELEASE(currMonth);
|
2004-05-13 09:10:35 +00:00
|
|
|
}
|
2010-03-04 08:25:52 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'd': // fall through
|
|
|
|
case 'e':
|
2011-03-16 13:17:48 +00:00
|
|
|
sourceIdx
|
|
|
|
+= getDigits(&source[sourceIdx], tmpStr, 2, &error);
|
2010-03-04 08:25:52 +00:00
|
|
|
day = atoi(tmpStr);
|
|
|
|
had |= hadD;
|
|
|
|
if (error == NO && day < 1)
|
|
|
|
{
|
|
|
|
error = YES;
|
|
|
|
NSDebugMLog(@"Day of month is zero");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'F':
|
2011-03-16 13:17:48 +00:00
|
|
|
sourceIdx
|
|
|
|
+= getDigits(&source[sourceIdx], tmpStr, 3, &error);
|
2010-03-04 08:25:52 +00:00
|
|
|
milliseconds = atoi(tmpStr);
|
|
|
|
break;
|
|
|
|
|
2010-03-17 05:45:15 +00:00
|
|
|
case 'k':
|
|
|
|
// GNUstep extension, not available in Cocoa
|
|
|
|
if (GSPrivateDefaultsFlag(GSMacOSXCompatible))
|
|
|
|
{
|
|
|
|
error = YES;
|
|
|
|
NSLog(@"Invalid NSCalendar date, "
|
|
|
|
@"specifier %c not recognized in format %@",
|
|
|
|
format[formatIdx], fmt);
|
|
|
|
}
|
2010-03-04 08:25:52 +00:00
|
|
|
case 'I': // fall through
|
|
|
|
twelveHrClock = YES;
|
|
|
|
case 'H':
|
2011-03-16 13:17:48 +00:00
|
|
|
sourceIdx
|
|
|
|
+= getDigits(&source[sourceIdx], tmpStr, 2, &error);
|
2010-03-04 08:25:52 +00:00
|
|
|
hour = atoi(tmpStr);
|
|
|
|
had |= hadh;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'j':
|
2011-03-16 13:17:48 +00:00
|
|
|
sourceIdx
|
|
|
|
+= getDigits(&source[sourceIdx], tmpStr, 3, &error);
|
2010-03-04 08:25:52 +00:00
|
|
|
day = atoi(tmpStr);
|
|
|
|
had |= hadD;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'm':
|
2011-03-16 13:17:48 +00:00
|
|
|
sourceIdx
|
|
|
|
+= getDigits(&source[sourceIdx], tmpStr, 2, &error);
|
2010-03-04 08:25:52 +00:00
|
|
|
month = atoi(tmpStr);
|
|
|
|
had |= hadM;
|
|
|
|
if (error == NO && month < 1)
|
|
|
|
{
|
|
|
|
error = YES;
|
|
|
|
NSDebugMLog(@"Month of year is zero");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'M':
|
2011-03-16 13:17:48 +00:00
|
|
|
sourceIdx
|
|
|
|
+= getDigits(&source[sourceIdx], tmpStr, 2, &error);
|
2010-03-04 08:25:52 +00:00
|
|
|
min = atoi(tmpStr);
|
|
|
|
had |= hadm;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'p':
|
|
|
|
/* FIXME ... Should look for all values from the locale,
|
|
|
|
* matching for longest values first, rather than (wrongly)
|
|
|
|
* assuming the name is always two uppercase letters.
|
|
|
|
*/
|
2010-08-07 05:02:28 +00:00
|
|
|
twelveHrClock = YES;
|
2010-03-04 08:25:52 +00:00
|
|
|
tmpStr[0] = toupper(source[sourceIdx]);
|
|
|
|
if (sourceIdx < sourceLen)
|
2000-10-20 10:30:51 +00:00
|
|
|
sourceIdx++;
|
2010-03-04 08:25:52 +00:00
|
|
|
tmpStr[1] = toupper(source[sourceIdx]);
|
|
|
|
if (sourceIdx < sourceLen)
|
2000-10-20 10:30:51 +00:00
|
|
|
sourceIdx++;
|
2010-03-04 08:25:52 +00:00
|
|
|
tmpStr[2] = '\0';
|
2000-04-05 21:43:40 +00:00
|
|
|
{
|
2010-03-04 08:25:52 +00:00
|
|
|
NSString *currAMPM;
|
|
|
|
NSArray *amPMNames;
|
|
|
|
|
|
|
|
currAMPM = [NSString stringWithUTF8String: tmpStr];
|
2016-04-08 17:28:05 +00:00
|
|
|
amPMNames = [LOCALE objectForKey: NSAMPMDesignation];
|
2010-03-04 08:25:52 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The time addition is handled below because this
|
|
|
|
* indicator only modifies the time on a 12hour clock.
|
|
|
|
*/
|
2011-03-16 13:17:48 +00:00
|
|
|
if ([currAMPM caseInsensitiveCompare:
|
|
|
|
[amPMNames objectAtIndex: 0]] == NSOrderedSame)
|
2010-08-06 16:20:19 +00:00
|
|
|
{
|
|
|
|
ampm = YES;
|
|
|
|
isPM = NO;
|
|
|
|
}
|
2011-03-16 13:17:48 +00:00
|
|
|
else if ([currAMPM caseInsensitiveCompare:
|
|
|
|
[amPMNames objectAtIndex: 1]] == NSOrderedSame)
|
2010-08-06 16:20:19 +00:00
|
|
|
{
|
|
|
|
ampm = YES;
|
|
|
|
isPM = YES;
|
|
|
|
}
|
2000-04-05 21:43:40 +00:00
|
|
|
}
|
2010-03-04 08:25:52 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'S':
|
2010-11-28 09:30:05 +00:00
|
|
|
sourceIdx
|
|
|
|
+= getDigits(&source[sourceIdx], tmpStr, 2, &error);
|
2010-03-04 08:25:52 +00:00
|
|
|
sec = atoi(tmpStr);
|
|
|
|
had |= hads;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'w':
|
2010-11-28 09:30:05 +00:00
|
|
|
sourceIdx
|
|
|
|
+= getDigits(&source[sourceIdx], tmpStr, 1, &error);
|
2010-03-04 08:25:52 +00:00
|
|
|
dayOfWeek = atoi(tmpStr);
|
|
|
|
had |= hadw;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'W': // Fall through
|
|
|
|
weekStartsMonday = 1;
|
|
|
|
case 'U':
|
2010-11-28 09:30:05 +00:00
|
|
|
sourceIdx
|
|
|
|
+= getDigits(&source[sourceIdx], tmpStr, 1, &error);
|
2010-03-04 08:25:52 +00:00
|
|
|
julianWeeks = atoi(tmpStr);
|
|
|
|
break;
|
|
|
|
|
|
|
|
// case 'x':
|
|
|
|
// break;
|
|
|
|
|
|
|
|
// case 'X':
|
|
|
|
// break;
|
|
|
|
|
|
|
|
case 'y':
|
2010-11-28 09:30:05 +00:00
|
|
|
sourceIdx
|
|
|
|
+= getDigits(&source[sourceIdx], tmpStr, 2, &error);
|
2010-03-04 08:25:52 +00:00
|
|
|
year = atoi(tmpStr);
|
|
|
|
if (year >= 70)
|
2000-04-05 21:43:40 +00:00
|
|
|
{
|
2010-03-04 08:25:52 +00:00
|
|
|
year += 1900;
|
2000-04-05 21:43:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-03-04 08:25:52 +00:00
|
|
|
year += 2000;
|
2000-04-05 21:43:40 +00:00
|
|
|
}
|
2010-03-04 08:25:52 +00:00
|
|
|
had |= hadY;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'Y':
|
2010-11-28 09:30:05 +00:00
|
|
|
sourceIdx
|
|
|
|
+= getDigits(&source[sourceIdx], tmpStr, 4, &error);
|
2010-03-04 08:25:52 +00:00
|
|
|
year = atoi(tmpStr);
|
|
|
|
had |= hadY;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'z':
|
|
|
|
{
|
|
|
|
int sign = 1;
|
|
|
|
int zone;
|
|
|
|
int found;
|
|
|
|
|
|
|
|
if (source[sourceIdx] == '+')
|
|
|
|
{
|
|
|
|
sourceIdx++;
|
|
|
|
}
|
|
|
|
else if (source[sourceIdx] == '-')
|
|
|
|
{
|
|
|
|
sign = -1;
|
|
|
|
sourceIdx++;
|
|
|
|
}
|
|
|
|
found = getDigits(&source[sourceIdx], tmpStr, 4, &error);
|
|
|
|
if (found > 0)
|
|
|
|
{
|
|
|
|
sourceIdx += found;
|
|
|
|
zone = atoi(tmpStr);
|
|
|
|
if (found == 2)
|
|
|
|
{
|
|
|
|
zone *= 100; // Convert 2 digits to 4
|
|
|
|
}
|
|
|
|
tz = [NSTimeZone timeZoneForSecondsFromGMT:
|
|
|
|
sign * ((zone / 100) * 60 + (zone % 100)) * 60];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2000-04-05 21:43:40 +00:00
|
|
|
|
2010-03-04 08:25:52 +00:00
|
|
|
case 'Z':
|
|
|
|
/* Can we assume a timezone name is always space terminated?
|
|
|
|
*/
|
|
|
|
tmpEnd = sizeof(tmpStr) - 1;
|
|
|
|
if (sourceLen - sourceIdx < tmpEnd)
|
|
|
|
{
|
|
|
|
tmpEnd = sourceLen - sourceIdx;
|
|
|
|
}
|
|
|
|
for (tmpIdx = 0; tmpIdx < tmpEnd; tmpIdx++)
|
|
|
|
{
|
|
|
|
if (!isspace(source[sourceIdx + tmpIdx]))
|
|
|
|
{
|
|
|
|
tmpStr[tmpIdx] = source[sourceIdx + tmpIdx];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tmpStr[tmpIdx] = '\0';
|
|
|
|
sourceIdx += tmpIdx;
|
2000-04-05 21:43:40 +00:00
|
|
|
{
|
2010-03-04 08:25:52 +00:00
|
|
|
NSString *z = [NSString stringWithUTF8String: tmpStr];
|
|
|
|
|
|
|
|
/* Abbreviations aren't one-to-one with time zone names
|
|
|
|
so just look for the zone named after the abbreviation,
|
|
|
|
then look up the abbreviation as a last resort */
|
2010-08-06 16:20:19 +00:00
|
|
|
if ([z length] > 0)
|
|
|
|
{
|
|
|
|
tz = [NSTimeZone timeZoneWithName: z];
|
|
|
|
if (tz == nil)
|
2010-03-04 08:25:52 +00:00
|
|
|
{
|
2010-08-06 16:20:19 +00:00
|
|
|
tz = [NSTimeZone timeZoneWithAbbreviation: z];
|
|
|
|
if (tz == nil)
|
|
|
|
{
|
|
|
|
error = YES;
|
|
|
|
NSDebugMLog(@"Time zone '%@' not found", z);
|
|
|
|
}
|
2010-03-04 08:25:52 +00:00
|
|
|
}
|
2010-08-06 16:20:19 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
error = YES;
|
|
|
|
NSDebugMLog(@"Time zone not given");
|
|
|
|
}
|
2000-04-05 21:43:40 +00:00
|
|
|
}
|
2010-03-04 08:25:52 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
error = YES;
|
|
|
|
NSLog(@"Invalid NSCalendar date, "
|
|
|
|
@"specifier %c not recognized in format %@",
|
|
|
|
format[formatIdx], fmt);
|
|
|
|
break;
|
2000-02-06 14:47:42 +00:00
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
}
|
2005-02-22 11:22:44 +00:00
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
formatIdx++;
|
|
|
|
}
|
|
|
|
RELEASE(fd);
|
|
|
|
|
|
|
|
if (error == NO)
|
|
|
|
{
|
2000-11-30 12:19:50 +00:00
|
|
|
if (tz == nil)
|
|
|
|
{
|
2002-09-30 16:54:29 +00:00
|
|
|
tz = localTZ;
|
2000-11-30 12:19:50 +00:00
|
|
|
}
|
2000-04-05 21:43:40 +00:00
|
|
|
|
|
|
|
if (twelveHrClock == YES)
|
2010-08-06 16:20:19 +00:00
|
|
|
{
|
|
|
|
if (ampm == YES && isPM == YES && hour != 12)
|
|
|
|
{
|
|
|
|
hour += 12;
|
|
|
|
}
|
|
|
|
else if (ampm == YES && isPM == NO && hour == 12)
|
|
|
|
{
|
|
|
|
hour = 0; // 12 AM
|
|
|
|
}
|
|
|
|
}
|
1999-12-13 12:14:01 +00:00
|
|
|
|
2000-04-05 21:43:40 +00:00
|
|
|
if (julianWeeks != -1)
|
|
|
|
{
|
2004-05-13 09:28:52 +00:00
|
|
|
NSTimeZone *gmtZone;
|
2000-04-05 21:43:40 +00:00
|
|
|
NSCalendarDate *d;
|
2004-05-13 09:28:52 +00:00
|
|
|
int currDay;
|
2000-04-05 21:43:40 +00:00
|
|
|
|
|
|
|
gmtZone = [NSTimeZone timeZoneForSecondsFromGMT: 0];
|
2000-11-30 12:19:50 +00:00
|
|
|
|
|
|
|
if ((had & (hadY|hadw)) != (hadY|hadw))
|
|
|
|
{
|
2009-04-27 18:04:56 +00:00
|
|
|
NSCalendarDate *now = [[NSCalendarDateClass alloc] init];
|
2000-11-30 12:19:50 +00:00
|
|
|
|
|
|
|
[now setTimeZone: gmtZone];
|
2004-05-13 09:38:03 +00:00
|
|
|
if ((had & hadY) == 0)
|
2000-11-30 12:19:50 +00:00
|
|
|
{
|
|
|
|
year = [now yearOfCommonEra];
|
|
|
|
had |= hadY;
|
|
|
|
}
|
2004-05-13 09:38:03 +00:00
|
|
|
if ((had & hadw) == 0)
|
2000-11-30 12:19:50 +00:00
|
|
|
{
|
|
|
|
dayOfWeek = [now dayOfWeek];
|
|
|
|
had |= hadw;
|
|
|
|
}
|
2004-05-13 09:28:52 +00:00
|
|
|
RELEASE(now);
|
2000-11-30 12:19:50 +00:00
|
|
|
}
|
|
|
|
|
2009-04-27 18:04:56 +00:00
|
|
|
d = [[NSCalendarDateClass alloc] initWithYear: year
|
|
|
|
month: 1
|
|
|
|
day: 1
|
|
|
|
hour: 0
|
|
|
|
minute: 0
|
|
|
|
second: 0
|
|
|
|
timeZone: gmtZone];
|
2000-04-05 21:43:40 +00:00
|
|
|
currDay = [d dayOfWeek];
|
2004-05-13 09:28:52 +00:00
|
|
|
RELEASE(d);
|
2000-04-05 21:43:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The julian weeks are either sunday relative or monday relative
|
|
|
|
* but all of the day of week specifiers are sunday relative.
|
|
|
|
* This means that if no day of week specifier was used the week
|
|
|
|
* starts on monday.
|
|
|
|
*/
|
|
|
|
if (dayOfWeek == -1)
|
|
|
|
{
|
|
|
|
if (weekStartsMonday)
|
2000-02-06 14:47:42 +00:00
|
|
|
{
|
2000-04-05 21:43:40 +00:00
|
|
|
dayOfWeek = 1;
|
1999-12-13 12:14:01 +00:00
|
|
|
}
|
2000-04-05 21:43:40 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
dayOfWeek = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
day = dayOfWeek + (julianWeeks * 7 - (currDay - 1));
|
2000-11-30 12:19:50 +00:00
|
|
|
had |= hadD;
|
|
|
|
}
|
|
|
|
|
2004-05-13 09:28:52 +00:00
|
|
|
/*
|
|
|
|
* If the year has not been set ... use this year ... as on MacOS-X
|
|
|
|
*/
|
2004-05-13 09:38:03 +00:00
|
|
|
if ((had & hadY) == 0)
|
2004-05-13 09:28:52 +00:00
|
|
|
{
|
2009-04-27 18:04:56 +00:00
|
|
|
NSCalendarDate *now = [[NSCalendarDateClass alloc] init];
|
2004-05-13 09:28:52 +00:00
|
|
|
|
|
|
|
year = [now yearOfCommonEra];
|
|
|
|
RELEASE(now);
|
|
|
|
}
|
|
|
|
|
2003-04-01 11:54:11 +00:00
|
|
|
self = [self initWithYear: year
|
2000-04-20 11:03:16 +00:00
|
|
|
month: month
|
|
|
|
day: day
|
|
|
|
hour: hour
|
|
|
|
minute: min
|
|
|
|
second: sec
|
|
|
|
timeZone: tz];
|
2003-04-01 11:54:11 +00:00
|
|
|
if (self != nil)
|
|
|
|
{
|
2006-04-26 13:35:10 +00:00
|
|
|
_seconds_since_ref += ((NSTimeInterval)milliseconds) / 1000.0;
|
2003-04-01 11:54:11 +00:00
|
|
|
}
|
2000-04-05 21:43:40 +00:00
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
|
|
|
|
if (error == YES)
|
|
|
|
{
|
|
|
|
DESTROY(self);
|
|
|
|
}
|
|
|
|
return self;
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
2002-09-28 09:30:38 +00:00
|
|
|
/**
|
|
|
|
* Returns an NSCalendarDate instance with the given year, month, day,
|
|
|
|
* hour, minute, and second, using aTimeZone.<br />
|
|
|
|
* The year includes the century (ie you can't just say '02' when you
|
|
|
|
* mean '2002').<br />
|
|
|
|
* The month is in the range 1 to 12,<br />
|
2002-09-28 17:29:13 +00:00
|
|
|
* The day is in the range 1 to 31,<br />
|
2002-09-28 09:30:38 +00:00
|
|
|
* The hour is in the range 0 to 23,<br />
|
|
|
|
* The minute is in the range 0 to 59,<br />
|
|
|
|
* The second is in the range 0 to 59.<br />
|
|
|
|
* If aTimeZone is nil, the [NSTimeZone+localTimeZone] value is used.
|
2002-09-28 17:29:13 +00:00
|
|
|
* <p>
|
|
|
|
* GNUstep checks the validity of the method arguments, and unless
|
|
|
|
* the base library was built with 'warn=no' it generates a warning
|
|
|
|
* for bad values. It tries to use those bad values to generate a
|
|
|
|
* date anyway though, rather than failing (this also appears to be
|
|
|
|
* the behavior of MacOS-X).
|
|
|
|
* </p>
|
|
|
|
* The algorithm GNUstep uses to create the date is this ...<br />
|
|
|
|
* <list>
|
|
|
|
* <item>
|
|
|
|
* Convert the broken out date values into a time interval since
|
|
|
|
* the reference date, as if those values represent a GMT date/time.
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* Ask the time zone for the offset from GMT at the resulting date,
|
|
|
|
* and apply that offset to the time interval ... so get the value
|
|
|
|
* for the specified timezone.
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* Ask the time zone for the offset from GMT at the new date ...
|
|
|
|
* in case the new date is in a different daylight savings time
|
|
|
|
* band from the original date. If this offset differs from the
|
|
|
|
* previous one, apply the difference so that the result is
|
|
|
|
* corrected for daylight savings. This is the final result used.
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* After establishing the time interval we will use and completing
|
|
|
|
* initialisation, we ask the time zone for the offset from GMT again.
|
|
|
|
* If it is not the same as the last time, then the time specified by
|
|
|
|
* the broken out date does not really exist ... since it's in the
|
|
|
|
* period lost by the transition to daylight savings. The resulting
|
|
|
|
* date is therefore not the date that was actually asked for, but is
|
|
|
|
* the best approximation we can do. If the base library was not
|
|
|
|
* built with 'warn=no' then a warning message is logged for this
|
|
|
|
* condition.
|
|
|
|
* </item>
|
|
|
|
* </list>
|
2002-09-28 09:30:38 +00:00
|
|
|
*/
|
2009-02-23 20:42:32 +00:00
|
|
|
- (id) initWithYear: (NSInteger)year
|
|
|
|
month: (NSUInteger)month
|
|
|
|
day: (NSUInteger)day
|
|
|
|
hour: (NSUInteger)hour
|
|
|
|
minute: (NSUInteger)minute
|
|
|
|
second: (NSUInteger)second
|
1999-08-25 16:15:12 +00:00
|
|
|
timeZone: (NSTimeZone *)aTimeZone
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int c;
|
2001-01-30 12:05:02 +00:00
|
|
|
NSTimeInterval s;
|
|
|
|
NSTimeInterval oldOffset;
|
2002-09-28 09:30:38 +00:00
|
|
|
NSTimeInterval newOffset;
|
|
|
|
|
|
|
|
if (month < 1 || month > 12)
|
|
|
|
{
|
2013-07-03 06:46:41 +00:00
|
|
|
NSWarnMLog(@"invalid month given - %"PRIuPTR, month);
|
2002-09-28 09:30:38 +00:00
|
|
|
}
|
|
|
|
c = lastDayOfGregorianMonth(month, year);
|
|
|
|
if (day < 1 || day > c)
|
|
|
|
{
|
2013-07-03 06:46:41 +00:00
|
|
|
NSWarnMLog(@"invalid day given - %"PRIuPTR, day);
|
2002-09-28 09:30:38 +00:00
|
|
|
}
|
|
|
|
if (hour > 23)
|
|
|
|
{
|
2013-07-03 06:46:41 +00:00
|
|
|
NSWarnMLog(@"invalid hour given - %"PRIuPTR, hour);
|
2002-09-28 09:30:38 +00:00
|
|
|
}
|
|
|
|
if (minute > 59)
|
|
|
|
{
|
2013-07-03 06:46:41 +00:00
|
|
|
NSWarnMLog(@"invalid minute given - %"PRIuPTR, minute);
|
2002-09-28 09:30:38 +00:00
|
|
|
}
|
|
|
|
if (second > 59)
|
|
|
|
{
|
2013-07-03 06:46:41 +00:00
|
|
|
NSWarnMLog(@"invalid second given - %"PRIuPTR, second);
|
2002-09-28 09:30:38 +00:00
|
|
|
}
|
1996-10-31 17:13:12 +00:00
|
|
|
|
1997-10-28 14:34:49 +00:00
|
|
|
// Calculate date as GMT
|
2000-12-02 11:47:30 +00:00
|
|
|
s = GSTime(day, month, year, hour, minute, second, 0);
|
1996-10-31 17:13:12 +00:00
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
// Assign time zone detail
|
2001-01-30 12:05:02 +00:00
|
|
|
if (aTimeZone == nil)
|
|
|
|
{
|
2002-10-01 10:25:40 +00:00
|
|
|
_time_zone = localTZ; // retain is a no-op for the local timezone.
|
2001-01-30 12:05:02 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_time_zone = RETAIN(aTimeZone);
|
|
|
|
}
|
2003-07-03 20:24:11 +00:00
|
|
|
if (_calendar_format == nil)
|
|
|
|
{
|
|
|
|
_calendar_format = cformat;
|
|
|
|
}
|
2002-09-28 14:11:55 +00:00
|
|
|
_seconds_since_ref = s;
|
1996-10-31 17:13:12 +00:00
|
|
|
|
2002-09-28 14:11:55 +00:00
|
|
|
/*
|
|
|
|
* Adjust date so it is correct for time zone.
|
|
|
|
*/
|
2002-09-30 18:34:02 +00:00
|
|
|
oldOffset = offset(_time_zone, self);
|
2001-01-30 12:05:02 +00:00
|
|
|
s -= oldOffset;
|
2002-09-28 14:11:55 +00:00
|
|
|
_seconds_since_ref = s;
|
1997-10-28 14:34:49 +00:00
|
|
|
|
2002-09-28 09:30:38 +00:00
|
|
|
/*
|
|
|
|
* See if we need to adjust for daylight savings time
|
|
|
|
*/
|
2002-09-30 18:34:02 +00:00
|
|
|
newOffset = offset(_time_zone, self);
|
2002-09-28 09:30:38 +00:00
|
|
|
if (oldOffset != newOffset)
|
|
|
|
{
|
|
|
|
s -= (newOffset - oldOffset);
|
2002-09-28 14:11:55 +00:00
|
|
|
_seconds_since_ref = s;
|
2002-09-30 18:34:02 +00:00
|
|
|
oldOffset = offset(_time_zone, self);
|
2002-09-28 15:45:26 +00:00
|
|
|
/*
|
|
|
|
* If the adjustment puts us in another offset, we must be in the
|
|
|
|
* non-existent period at the start of daylight savings time.
|
|
|
|
*/
|
|
|
|
if (oldOffset != newOffset)
|
|
|
|
{
|
|
|
|
NSWarnMLog(@"init non-existent time at start of daylight savings");
|
|
|
|
}
|
2002-09-28 09:30:38 +00:00
|
|
|
}
|
|
|
|
|
1997-10-28 14:34:49 +00:00
|
|
|
return self;
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
2002-09-30 16:54:29 +00:00
|
|
|
/**
|
|
|
|
* Initialises the receiver with the specified interval since the
|
|
|
|
* reference date. Uses th standard format string "%Y-%m-%d %H:%M:%S %z"
|
|
|
|
* and the default time zone.
|
|
|
|
*/
|
1999-08-25 16:15:12 +00:00
|
|
|
- (id) initWithTimeIntervalSinceReferenceDate: (NSTimeInterval)seconds
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2008-09-23 08:22:15 +00:00
|
|
|
if (isnan(seconds))
|
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
format: @"[%@-%@] interval is not a number",
|
|
|
|
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
|
|
|
|
}
|
2016-03-15 19:04:51 +00:00
|
|
|
#if GS_SIZEOF_VOIDP == 4
|
|
|
|
if (seconds <= DISTANT_PAST)
|
|
|
|
{
|
|
|
|
seconds = DISTANT_PAST;
|
|
|
|
}
|
|
|
|
else if (seconds >= DISTANT_FUTURE)
|
|
|
|
{
|
|
|
|
seconds = DISTANT_FUTURE;
|
|
|
|
}
|
|
|
|
#endif
|
1999-09-16 07:21:34 +00:00
|
|
|
_seconds_since_ref = seconds;
|
2001-01-30 12:05:02 +00:00
|
|
|
if (_calendar_format == nil)
|
2002-09-30 16:54:29 +00:00
|
|
|
{
|
2002-10-01 10:25:40 +00:00
|
|
|
_calendar_format = cformat;
|
2002-09-30 16:54:29 +00:00
|
|
|
}
|
2001-01-30 12:05:02 +00:00
|
|
|
if (_time_zone == nil)
|
2002-09-30 16:54:29 +00:00
|
|
|
{
|
2002-10-01 10:25:40 +00:00
|
|
|
_time_zone = localTZ; // retain is a no-op for the local timezone.
|
2002-09-30 16:54:29 +00:00
|
|
|
}
|
1996-10-31 17:13:12 +00:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Return the day number (ie number of days since the start of) in the
|
|
|
|
* 'common' era of the receiving date. The era starts at 1 A.D.
|
|
|
|
*/
|
2009-02-23 20:42:32 +00:00
|
|
|
- (NSInteger) dayOfCommonEra
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2002-09-28 14:11:55 +00:00
|
|
|
NSTimeInterval when;
|
1996-10-31 17:13:12 +00:00
|
|
|
|
2002-09-30 18:34:02 +00:00
|
|
|
when = _seconds_since_ref + offset(_time_zone, self);
|
2002-09-28 14:11:55 +00:00
|
|
|
return dayOfCommonEra(when);
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Return the month (1 to 31) of the receiving date.
|
|
|
|
*/
|
2009-02-23 20:42:32 +00:00
|
|
|
- (NSInteger) dayOfMonth
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2016-03-15 18:22:57 +00:00
|
|
|
NSInteger m, d, y;
|
2002-09-28 14:11:55 +00:00
|
|
|
NSTimeInterval when;
|
1996-10-31 17:13:12 +00:00
|
|
|
|
2002-09-30 18:34:02 +00:00
|
|
|
when = _seconds_since_ref + offset(_time_zone, self);
|
2002-09-28 14:11:55 +00:00
|
|
|
gregorianDateFromAbsolute(dayOfCommonEra(when), &d, &m, &y);
|
1996-10-31 17:13:12 +00:00
|
|
|
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Return the day of the week (0 to 6) of the receiving date.
|
|
|
|
* <list>
|
|
|
|
* <item>0 is sunday</item>
|
|
|
|
* <item>1 is monday</item>
|
|
|
|
* <item>2 is tuesday</item>
|
|
|
|
* <item>3 is wednesday</item>
|
|
|
|
* <item>4 is thursday</item>
|
|
|
|
* <item>5 is friday</item>
|
|
|
|
* <item>6 is saturday</item>
|
|
|
|
* </list>
|
|
|
|
*/
|
2009-02-23 20:42:32 +00:00
|
|
|
- (NSInteger) dayOfWeek
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2016-03-15 17:23:13 +00:00
|
|
|
NSInteger d;
|
2002-09-28 14:11:55 +00:00
|
|
|
NSTimeInterval when;
|
|
|
|
|
2002-09-30 18:34:02 +00:00
|
|
|
when = _seconds_since_ref + offset(_time_zone, self);
|
2002-09-28 14:11:55 +00:00
|
|
|
d = dayOfCommonEra(when);
|
1997-10-28 14:34:49 +00:00
|
|
|
|
|
|
|
/* The era started on a sunday.
|
|
|
|
Did we always have a seven day week?
|
|
|
|
Did we lose week days changing from Julian to Gregorian?
|
|
|
|
AFAIK seven days a week is ok for all reasonable dates. */
|
|
|
|
d = d % 7;
|
|
|
|
if (d < 0)
|
|
|
|
d += 7;
|
|
|
|
return d;
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Return the day of the year (1 to 366) of the receiving date.
|
|
|
|
*/
|
2009-02-23 20:42:32 +00:00
|
|
|
- (NSInteger) dayOfYear
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2016-03-15 18:22:57 +00:00
|
|
|
NSInteger m, d, y, days, i;
|
2002-09-28 14:11:55 +00:00
|
|
|
NSTimeInterval when;
|
1996-10-31 17:13:12 +00:00
|
|
|
|
2002-09-30 18:34:02 +00:00
|
|
|
when = _seconds_since_ref + offset(_time_zone, self);
|
2002-09-28 14:11:55 +00:00
|
|
|
gregorianDateFromAbsolute(dayOfCommonEra(when), &d, &m, &y);
|
1996-10-31 17:13:12 +00:00
|
|
|
days = d;
|
|
|
|
for (i = m - 1; i > 0; i--) // days in prior months this year
|
2000-06-30 15:44:30 +00:00
|
|
|
days = days + lastDayOfGregorianMonth(i, y);
|
1996-10-31 17:13:12 +00:00
|
|
|
|
|
|
|
return days;
|
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Return the hour of the day (0 to 23) of the receiving date.
|
|
|
|
*/
|
2009-02-23 20:42:32 +00:00
|
|
|
- (NSInteger) hourOfDay
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2016-03-15 17:23:13 +00:00
|
|
|
NSInteger h;
|
2002-09-28 14:11:55 +00:00
|
|
|
double a, d;
|
|
|
|
NSTimeInterval when;
|
|
|
|
|
2002-09-30 18:34:02 +00:00
|
|
|
when = _seconds_since_ref + offset(_time_zone, self);
|
2002-09-28 14:11:55 +00:00
|
|
|
d = dayOfCommonEra(when);
|
1996-10-31 17:13:12 +00:00
|
|
|
d -= GREGORIAN_REFERENCE;
|
|
|
|
d *= 86400;
|
2015-05-02 16:44:02 +00:00
|
|
|
a = fabs(d - (_seconds_since_ref + offset(_time_zone, self)));
|
1996-10-31 17:13:12 +00:00
|
|
|
a = a / 3600;
|
2009-02-23 20:42:32 +00:00
|
|
|
h = (NSInteger)a;
|
1996-10-31 17:13:12 +00:00
|
|
|
|
|
|
|
// There is a small chance of getting
|
|
|
|
// it right at the stroke of midnight
|
|
|
|
if (h == 24)
|
|
|
|
h = 0;
|
|
|
|
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Return the minute of the hour (0 to 59) of the receiving date.
|
|
|
|
*/
|
2009-02-23 20:42:32 +00:00
|
|
|
- (NSInteger) minuteOfHour
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2016-03-15 17:23:13 +00:00
|
|
|
NSInteger h, m;
|
2002-09-28 14:11:55 +00:00
|
|
|
double a, b, d;
|
|
|
|
NSTimeInterval when;
|
|
|
|
|
2002-09-30 18:34:02 +00:00
|
|
|
when = _seconds_since_ref + offset(_time_zone, self);
|
2002-09-28 14:11:55 +00:00
|
|
|
d = dayOfCommonEra(when);
|
1996-10-31 17:13:12 +00:00
|
|
|
d -= GREGORIAN_REFERENCE;
|
|
|
|
d *= 86400;
|
2015-05-02 16:44:02 +00:00
|
|
|
a = fabs(d - (_seconds_since_ref + offset(_time_zone, self)));
|
1996-10-31 17:13:12 +00:00
|
|
|
b = a / 3600;
|
2009-02-23 20:42:32 +00:00
|
|
|
h = (NSInteger)b;
|
1996-10-31 17:13:12 +00:00
|
|
|
h = h * 3600;
|
|
|
|
b = a - h;
|
|
|
|
b = b / 60;
|
2009-02-23 20:42:32 +00:00
|
|
|
m = (NSInteger)b;
|
1996-10-31 17:13:12 +00:00
|
|
|
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Return the month of the year (1 to 12) of the receiving date.
|
|
|
|
*/
|
2009-02-23 20:42:32 +00:00
|
|
|
- (NSInteger) monthOfYear
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2016-03-15 18:22:57 +00:00
|
|
|
NSInteger m, d, y;
|
2002-09-28 14:11:55 +00:00
|
|
|
NSTimeInterval when;
|
1996-10-31 17:13:12 +00:00
|
|
|
|
2002-09-30 18:34:02 +00:00
|
|
|
when = _seconds_since_ref + offset(_time_zone, self);
|
2002-09-28 14:11:55 +00:00
|
|
|
gregorianDateFromAbsolute(dayOfCommonEra(when), &d, &m, &y);
|
1996-10-31 17:13:12 +00:00
|
|
|
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Return the second of the minute (0 to 59) of the receiving date.
|
|
|
|
*/
|
2009-02-23 20:42:32 +00:00
|
|
|
- (NSInteger) secondOfMinute
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2016-03-15 17:23:13 +00:00
|
|
|
NSInteger h, m, s;
|
2002-09-28 14:11:55 +00:00
|
|
|
double a, b, c, d;
|
|
|
|
NSTimeInterval when;
|
|
|
|
|
2002-09-30 18:34:02 +00:00
|
|
|
when = _seconds_since_ref + offset(_time_zone, self);
|
2002-09-28 14:11:55 +00:00
|
|
|
d = dayOfCommonEra(when);
|
1996-10-31 17:13:12 +00:00
|
|
|
d -= GREGORIAN_REFERENCE;
|
|
|
|
d *= 86400;
|
2015-05-02 16:44:02 +00:00
|
|
|
a = fabs(d - (_seconds_since_ref + offset(_time_zone, self)));
|
1996-10-31 17:13:12 +00:00
|
|
|
b = a / 3600;
|
2009-02-23 20:42:32 +00:00
|
|
|
h = (NSInteger)b;
|
1996-10-31 17:13:12 +00:00
|
|
|
h = h * 3600;
|
|
|
|
b = a - h;
|
|
|
|
b = b / 60;
|
2009-02-23 20:42:32 +00:00
|
|
|
m = (NSInteger)b;
|
1996-10-31 17:13:12 +00:00
|
|
|
m = m * 60;
|
|
|
|
c = a - h - m;
|
2009-02-23 20:42:32 +00:00
|
|
|
s = (NSInteger)c;
|
1996-10-31 17:13:12 +00:00
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Return the year of the 'common' era of the receiving date.
|
|
|
|
* The era starts at 1 A.D.
|
|
|
|
*/
|
2009-02-23 20:42:32 +00:00
|
|
|
- (NSInteger) yearOfCommonEra
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2016-03-15 18:22:57 +00:00
|
|
|
NSInteger m, d, y;
|
2002-09-28 14:11:55 +00:00
|
|
|
NSTimeInterval when;
|
1996-10-31 17:13:12 +00:00
|
|
|
|
2002-09-30 18:34:02 +00:00
|
|
|
when = _seconds_since_ref + offset(_time_zone, self);
|
2002-09-28 14:11:55 +00:00
|
|
|
gregorianDateFromAbsolute(dayOfCommonEra(when), &d, &m, &y);
|
1996-10-31 17:13:12 +00:00
|
|
|
|
|
|
|
return y;
|
|
|
|
}
|
|
|
|
|
2002-10-01 12:46:24 +00:00
|
|
|
/**
|
|
|
|
* This method exists solely for conformance to the OpenStep spec.
|
|
|
|
* Its use is deprecated ... it simply calls
|
|
|
|
* -dateByAddingYears:months:days:hours:minutes:seconds:
|
|
|
|
*/
|
2009-02-23 20:42:32 +00:00
|
|
|
- (NSCalendarDate*) addYear: (NSInteger)year
|
|
|
|
month: (NSInteger)month
|
|
|
|
day: (NSInteger)day
|
|
|
|
hour: (NSInteger)hour
|
|
|
|
minute: (NSInteger)minute
|
|
|
|
second: (NSInteger)second
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
1997-10-28 14:34:49 +00:00
|
|
|
return [self dateByAddingYears: year
|
|
|
|
months: month
|
|
|
|
days: day
|
|
|
|
hours: hour
|
|
|
|
minutes: minute
|
|
|
|
seconds: second];
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
2003-01-07 15:26:42 +00:00
|
|
|
/**
|
2005-11-06 13:53:40 +00:00
|
|
|
* Calls -descriptionWithCalendarFormat:locale: passing the receiver's
|
2003-01-07 15:26:42 +00:00
|
|
|
* calendar format and a nil locale.
|
|
|
|
*/
|
1999-08-25 16:15:12 +00:00
|
|
|
- (NSString*) description
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
return [self descriptionWithCalendarFormat: _calendar_format locale: nil];
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Returns a string representation of the receiver using the specified
|
|
|
|
* format string.<br />
|
|
|
|
* Calls -descriptionWithCalendarFormat:locale: with a nil locale.
|
|
|
|
*/
|
1999-08-25 16:15:12 +00:00
|
|
|
- (NSString*) descriptionWithCalendarFormat: (NSString *)format
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
1999-08-25 16:15:12 +00:00
|
|
|
return [self descriptionWithCalendarFormat: format locale: nil];
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#define UNIX_REFERENCE_INTERVAL -978307200.0
|
|
|
|
|
2004-06-21 10:56:32 +00:00
|
|
|
typedef struct {
|
|
|
|
unichar *base;
|
|
|
|
unichar *t;
|
|
|
|
unsigned length;
|
|
|
|
unsigned offset;
|
2016-03-15 18:22:57 +00:00
|
|
|
NSInteger yd;
|
|
|
|
NSInteger md;
|
|
|
|
NSInteger dom;
|
|
|
|
NSInteger hd;
|
|
|
|
NSInteger mnd;
|
|
|
|
NSInteger sd;
|
|
|
|
NSInteger mil;
|
2004-06-21 10:56:32 +00:00
|
|
|
} DescriptionInfo;
|
|
|
|
|
|
|
|
static void Grow(DescriptionInfo *info, unsigned size)
|
|
|
|
{
|
|
|
|
if (info->offset + size >= info->length)
|
|
|
|
{
|
|
|
|
if (info->t == info->base)
|
|
|
|
{
|
|
|
|
unichar *old = info->t;
|
1998-11-30 10:04:24 +00:00
|
|
|
|
2004-06-21 10:56:32 +00:00
|
|
|
info->t = NSZoneMalloc(NSDefaultMallocZone(),
|
|
|
|
(info->length + 512) * sizeof(unichar));
|
|
|
|
memcpy(info->t, old, info->length*sizeof(unichar));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
info->t = NSZoneRealloc(NSDefaultMallocZone(), info->t,
|
|
|
|
(info->length + 512) * sizeof(unichar));
|
|
|
|
}
|
|
|
|
info->length += 512;
|
|
|
|
}
|
|
|
|
}
|
1996-10-31 17:13:12 +00:00
|
|
|
|
2009-05-16 19:35:20 +00:00
|
|
|
#define MAX_FLD_WIDTH 99
|
|
|
|
|
|
|
|
static void outputValueWithFormat(int v, char *fldfmt, DescriptionInfo *info)
|
|
|
|
{
|
|
|
|
char cbuf[MAX_FLD_WIDTH + 1];
|
|
|
|
int idx = 0;
|
|
|
|
|
2011-03-07 15:34:06 +00:00
|
|
|
snprintf((char*)cbuf, sizeof(cbuf), fldfmt, v);
|
2009-05-16 19:35:20 +00:00
|
|
|
Grow(info, strlen((char*)cbuf));
|
|
|
|
while (cbuf[idx] != '\0')
|
|
|
|
{
|
|
|
|
info->t[info->offset++] = cbuf[idx++];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-06-21 10:56:32 +00:00
|
|
|
- (void) _format: (NSString*)fmt
|
|
|
|
locale: (NSDictionary*)locale
|
|
|
|
info: (DescriptionInfo*)info
|
|
|
|
{
|
|
|
|
unichar fbuf[512];
|
|
|
|
unichar *f = fbuf;
|
|
|
|
unsigned lf = [fmt length];
|
|
|
|
unsigned i = 0;
|
|
|
|
int v;
|
1998-11-30 10:04:24 +00:00
|
|
|
|
2004-06-21 10:56:32 +00:00
|
|
|
if (lf == 0)
|
|
|
|
{
|
|
|
|
return; // Nothing to do.
|
|
|
|
}
|
|
|
|
if (lf >= sizeof(fbuf)/sizeof(unichar))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Make temporary buffer to hold format string as unicode.
|
|
|
|
*/
|
|
|
|
f = (unichar*)NSZoneMalloc(NSDefaultMallocZone(), lf*sizeof(unichar));
|
|
|
|
}
|
2005-02-22 11:22:44 +00:00
|
|
|
[fmt getCharacters: f];
|
1996-10-31 17:13:12 +00:00
|
|
|
|
2004-03-05 11:17:03 +00:00
|
|
|
while (i < lf)
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2016-04-19 08:02:13 +00:00
|
|
|
NSString *str;
|
2004-06-21 10:56:32 +00:00
|
|
|
BOOL mtag = NO;
|
|
|
|
BOOL dtag = NO;
|
|
|
|
BOOL ycent = NO;
|
|
|
|
BOOL mname = NO;
|
|
|
|
BOOL dname = NO;
|
|
|
|
BOOL twelve = NO;
|
2010-03-17 05:45:15 +00:00
|
|
|
BOOL hspc = NO;
|
2009-05-16 19:35:20 +00:00
|
|
|
char fldfmt[8];
|
|
|
|
int fmtlen = 0;
|
|
|
|
int width = 0;
|
2005-02-22 11:22:44 +00:00
|
|
|
|
1996-10-31 17:13:12 +00:00
|
|
|
// Only care about a format specifier
|
|
|
|
if (f[i] == '%')
|
|
|
|
{
|
2004-06-21 10:56:32 +00:00
|
|
|
i++;
|
2016-04-19 08:02:13 +00:00
|
|
|
fldfmt[fmtlen++] = '%';
|
|
|
|
// field width specified
|
|
|
|
while (fmtlen < 5 && f[i] >= '0' && f[i] <= '9')
|
2009-05-16 19:35:20 +00:00
|
|
|
{
|
|
|
|
fldfmt[fmtlen++] = f[i];
|
|
|
|
width = 10 * width + f[i] - '0';
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
if (fmtlen >= 5 || width > MAX_FLD_WIDTH)
|
|
|
|
{
|
2016-04-19 08:02:13 +00:00
|
|
|
/* ignore formats that specify field width
|
|
|
|
* greater than the max allowed.
|
|
|
|
* set i back so all ignored characters will
|
|
|
|
* be copied
|
|
|
|
*/
|
|
|
|
i -= fmtlen;
|
2009-05-16 19:35:20 +00:00
|
|
|
}
|
|
|
|
|
1996-10-31 17:13:12 +00:00
|
|
|
// check the character that comes after
|
2004-06-21 10:56:32 +00:00
|
|
|
switch (f[i++])
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2004-05-13 09:10:35 +00:00
|
|
|
// literal %
|
|
|
|
case '%':
|
2004-06-21 10:56:32 +00:00
|
|
|
Grow(info, 1);
|
|
|
|
info->t[info->offset++] = f[i-1];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'R':
|
|
|
|
[self _format: @"%H:%M" locale: locale info: info];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'r':
|
|
|
|
[self _format: @"%I:%M:%S %p" locale: locale info: info];
|
2004-05-13 09:10:35 +00:00
|
|
|
break;
|
|
|
|
|
2006-03-08 11:48:25 +00:00
|
|
|
case 'T':
|
2008-10-16 04:36:42 +00:00
|
|
|
[self _format: @"%H:%M:%S" locale: locale info: info];
|
2006-03-08 11:48:25 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 't':
|
|
|
|
Grow(info, 1);
|
|
|
|
info->t[info->offset++] = '\t';
|
|
|
|
break;
|
|
|
|
|
2004-05-13 09:10:35 +00:00
|
|
|
case 'c':
|
2016-04-19 08:02:13 +00:00
|
|
|
str = (NSString*)[LOCALE objectForKey: NSTimeFormatString];
|
|
|
|
[self _format: str locale: locale info: info];
|
2004-06-21 10:56:32 +00:00
|
|
|
Grow(info, 1);
|
|
|
|
info->t[info->offset++] = ' ';
|
2016-04-19 08:02:13 +00:00
|
|
|
str = (NSString*)[LOCALE objectForKey: NSDateFormatString];
|
|
|
|
[self _format: str locale: locale info: info];
|
2004-06-21 10:56:32 +00:00
|
|
|
break;
|
|
|
|
|
2004-05-13 09:10:35 +00:00
|
|
|
case 'X':
|
2016-04-19 08:02:13 +00:00
|
|
|
str = (NSString*)[LOCALE objectForKey: NSTimeFormatString];
|
|
|
|
[self _format: str locale: locale info: info];
|
2004-06-21 10:56:32 +00:00
|
|
|
break;
|
|
|
|
|
2004-05-13 09:10:35 +00:00
|
|
|
case 'x':
|
2016-04-19 08:02:13 +00:00
|
|
|
str = (NSString*)[LOCALE objectForKey: NSDateFormatString];
|
|
|
|
[self _format: str locale: locale info: info];
|
2004-06-21 10:56:32 +00:00
|
|
|
break;
|
|
|
|
|
2004-05-13 09:10:35 +00:00
|
|
|
// is it the year
|
|
|
|
case 'Y':
|
|
|
|
ycent = YES;
|
|
|
|
case 'y':
|
2004-06-21 10:56:32 +00:00
|
|
|
v = info->yd;
|
2004-05-13 09:10:35 +00:00
|
|
|
if (ycent)
|
2004-06-21 10:56:32 +00:00
|
|
|
{
|
2011-03-09 10:24:18 +00:00
|
|
|
if (fmtlen == 1)
|
2005-03-15 16:40:07 +00:00
|
|
|
{
|
2011-03-09 10:24:18 +00:00
|
|
|
// no format width specified; supply default
|
2009-05-16 19:35:20 +00:00
|
|
|
fldfmt[fmtlen++] = '0';
|
|
|
|
fldfmt[fmtlen++] = '4';
|
2005-03-15 16:40:07 +00:00
|
|
|
}
|
2004-06-21 10:56:32 +00:00
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
else
|
2004-06-21 10:56:32 +00:00
|
|
|
{
|
2005-03-15 16:40:07 +00:00
|
|
|
if (v < 0) v = -v;
|
2004-06-21 10:56:32 +00:00
|
|
|
v = v % 100;
|
2011-03-09 10:24:18 +00:00
|
|
|
if (fmtlen == 1)
|
2009-05-16 19:35:20 +00:00
|
|
|
{
|
2011-03-09 10:24:18 +00:00
|
|
|
// no format width specified; supply default
|
2009-05-16 19:35:20 +00:00
|
|
|
fldfmt[fmtlen++] = '0';
|
|
|
|
fldfmt[fmtlen++] = '2';
|
|
|
|
}
|
2004-06-21 10:56:32 +00:00
|
|
|
}
|
2009-05-16 19:35:20 +00:00
|
|
|
fldfmt[fmtlen++] = 'd';
|
2011-03-09 10:24:18 +00:00
|
|
|
fldfmt[fmtlen] = 0;
|
2009-05-16 19:35:20 +00:00
|
|
|
outputValueWithFormat(v, fldfmt, info);
|
2004-05-13 09:10:35 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// is it the month
|
|
|
|
case 'b':
|
|
|
|
mname = YES;
|
|
|
|
case 'B':
|
|
|
|
mtag = YES; // Month is character string
|
|
|
|
case 'm':
|
2004-06-21 10:56:32 +00:00
|
|
|
if (mtag == YES)
|
1998-11-30 10:04:24 +00:00
|
|
|
{
|
2004-05-13 09:10:35 +00:00
|
|
|
NSArray *months;
|
1998-11-30 10:04:24 +00:00
|
|
|
|
2004-05-13 09:10:35 +00:00
|
|
|
if (mname)
|
2016-04-08 17:28:05 +00:00
|
|
|
months = [LOCALE objectForKey: NSShortMonthNameArray];
|
1998-11-30 10:04:24 +00:00
|
|
|
else
|
2016-04-08 17:28:05 +00:00
|
|
|
months = [LOCALE objectForKey: NSMonthNameArray];
|
2004-12-01 15:43:34 +00:00
|
|
|
if (info->md > [months count])
|
2004-06-21 10:56:32 +00:00
|
|
|
{
|
|
|
|
mtag = NO;
|
|
|
|
}
|
1998-12-17 09:16:26 +00:00
|
|
|
else
|
2004-06-21 10:56:32 +00:00
|
|
|
{
|
|
|
|
NSString *name;
|
2005-02-22 11:22:44 +00:00
|
|
|
|
2004-06-21 10:56:32 +00:00
|
|
|
name = [months objectAtIndex: info->md-1];
|
|
|
|
v = [name length];
|
|
|
|
Grow(info, v);
|
|
|
|
[name getCharacters: info->t + info->offset];
|
|
|
|
info->offset += v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (mtag == NO)
|
|
|
|
{
|
|
|
|
v = info->md;
|
|
|
|
v = v % 100;
|
2011-03-09 10:24:18 +00:00
|
|
|
if (fmtlen == 1)
|
2009-05-16 19:35:20 +00:00
|
|
|
{
|
2011-03-09 10:24:18 +00:00
|
|
|
// no format width specified; supply default
|
2009-05-16 19:35:20 +00:00
|
|
|
fldfmt[fmtlen++] = '0';
|
|
|
|
fldfmt[fmtlen++] = '2';
|
|
|
|
}
|
|
|
|
fldfmt[fmtlen++] = 'd';
|
2011-03-09 10:24:18 +00:00
|
|
|
fldfmt[fmtlen] = 0;
|
2009-05-16 19:35:20 +00:00
|
|
|
outputValueWithFormat(v, fldfmt, info);
|
1998-11-30 10:04:24 +00:00
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
break;
|
1996-10-31 17:13:12 +00:00
|
|
|
|
2004-06-21 10:56:32 +00:00
|
|
|
case 'd': // day of month with leading zero
|
|
|
|
v = info->dom;
|
|
|
|
v = v % 100;
|
2011-03-09 10:24:18 +00:00
|
|
|
if (fmtlen == 1) // no format width specified; supply default
|
|
|
|
{
|
|
|
|
fldfmt[fmtlen++] = '0';
|
|
|
|
fldfmt[fmtlen++] = '2';
|
|
|
|
}
|
|
|
|
fldfmt[fmtlen++] = 'd';
|
|
|
|
fldfmt[fmtlen] = 0;
|
|
|
|
outputValueWithFormat(v, fldfmt, info);
|
2004-05-13 09:10:35 +00:00
|
|
|
break;
|
1996-10-31 17:13:12 +00:00
|
|
|
|
2004-06-21 10:56:32 +00:00
|
|
|
case 'e': // day of month with leading space
|
|
|
|
v = info->dom;
|
|
|
|
v = v % 100;
|
2011-03-09 10:24:18 +00:00
|
|
|
if (fmtlen == 1) // no format width specified; supply default
|
|
|
|
{
|
|
|
|
fldfmt[fmtlen++] = '1'; // no leading space, just like Cocoa
|
|
|
|
}
|
|
|
|
fldfmt[fmtlen++] = 'd';
|
|
|
|
fldfmt[fmtlen] = 0;
|
|
|
|
outputValueWithFormat(v, fldfmt, info);
|
2004-05-13 09:10:35 +00:00
|
|
|
break;
|
1996-10-31 17:13:12 +00:00
|
|
|
|
2004-05-13 09:10:35 +00:00
|
|
|
case 'F': // milliseconds
|
2014-02-13 10:41:55 +00:00
|
|
|
v = info->mil;
|
2009-05-16 19:35:20 +00:00
|
|
|
if (fmtlen == 1) // no format width specified; supply default
|
|
|
|
{
|
|
|
|
fldfmt[fmtlen++] = '0';
|
|
|
|
fldfmt[fmtlen++] = '3';
|
|
|
|
}
|
|
|
|
fldfmt[fmtlen++] = 'd';
|
2011-03-09 10:24:18 +00:00
|
|
|
fldfmt[fmtlen] = 0;
|
2009-05-16 19:35:20 +00:00
|
|
|
outputValueWithFormat(v, fldfmt, info);
|
2004-05-13 09:10:35 +00:00
|
|
|
break;
|
1996-10-31 17:13:12 +00:00
|
|
|
|
2004-05-13 09:10:35 +00:00
|
|
|
case 'j': // day of year
|
2004-06-21 10:56:32 +00:00
|
|
|
v = [self dayOfYear];
|
2009-05-16 19:35:20 +00:00
|
|
|
if (fmtlen == 1) // no format width specified; supply default
|
|
|
|
{
|
|
|
|
fldfmt[fmtlen++] = '0';
|
|
|
|
fldfmt[fmtlen++] = '3';
|
|
|
|
}
|
|
|
|
fldfmt[fmtlen++] = 'd';
|
2011-03-09 10:24:18 +00:00
|
|
|
fldfmt[fmtlen] = 0;
|
2009-05-16 19:35:20 +00:00
|
|
|
outputValueWithFormat(v, fldfmt, info);
|
2004-05-13 09:10:35 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// is it the week-day
|
|
|
|
case 'a':
|
|
|
|
dname = YES;
|
|
|
|
case 'A':
|
|
|
|
dtag = YES; // Day is character string
|
|
|
|
case 'w':
|
|
|
|
{
|
2004-06-21 10:56:32 +00:00
|
|
|
v = [self dayOfWeek];
|
|
|
|
if (dtag == YES)
|
2004-05-13 09:10:35 +00:00
|
|
|
{
|
|
|
|
NSArray *days;
|
1998-11-30 10:04:24 +00:00
|
|
|
|
2004-05-13 09:10:35 +00:00
|
|
|
if (dname)
|
2016-04-08 17:28:05 +00:00
|
|
|
days = [LOCALE objectForKey: NSShortWeekDayNameArray];
|
2004-05-13 09:10:35 +00:00
|
|
|
else
|
2016-04-08 17:28:05 +00:00
|
|
|
days = [LOCALE objectForKey: NSWeekDayNameArray];
|
2004-06-21 10:56:32 +00:00
|
|
|
if (v < [days count])
|
|
|
|
{
|
|
|
|
NSString *name;
|
|
|
|
|
|
|
|
name = [days objectAtIndex: v];
|
|
|
|
v = [name length];
|
|
|
|
Grow(info, v);
|
|
|
|
[name getCharacters: info->t + info->offset];
|
|
|
|
info->offset += v;
|
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
else
|
2004-06-21 10:56:32 +00:00
|
|
|
{
|
|
|
|
dtag = NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (dtag == NO)
|
|
|
|
{
|
2011-03-09 10:24:18 +00:00
|
|
|
if (fmtlen == 1)
|
2009-05-16 19:35:20 +00:00
|
|
|
{
|
2011-03-09 10:24:18 +00:00
|
|
|
// no format width specified; supply default
|
2009-05-16 19:35:20 +00:00
|
|
|
fldfmt[fmtlen++] = '1';
|
2011-03-09 10:24:18 +00:00
|
|
|
}
|
2009-05-16 19:35:20 +00:00
|
|
|
fldfmt[fmtlen++] = 'd';
|
2011-03-09 10:24:18 +00:00
|
|
|
fldfmt[fmtlen] = 0;
|
2009-05-16 19:35:20 +00:00
|
|
|
outputValueWithFormat(v, fldfmt, info);
|
2004-05-13 09:10:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// is it the hour
|
|
|
|
case 'I':
|
2004-06-21 10:56:32 +00:00
|
|
|
twelve = YES;
|
2010-03-17 05:45:15 +00:00
|
|
|
case 'k':
|
|
|
|
if (twelve == NO)
|
|
|
|
hspc = YES;
|
2004-05-13 09:10:35 +00:00
|
|
|
case 'H':
|
2004-06-21 10:56:32 +00:00
|
|
|
v = info->hd;
|
|
|
|
if (twelve == YES)
|
|
|
|
{
|
2010-03-03 19:24:56 +00:00
|
|
|
if (info->hd == 12 || info->hd == 0)
|
2004-06-21 10:56:32 +00:00
|
|
|
{
|
|
|
|
v = 12;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
v = v % 12;
|
|
|
|
}
|
|
|
|
}
|
2009-05-16 19:35:20 +00:00
|
|
|
if (fmtlen == 1) // no format width specified; supply default
|
|
|
|
{
|
2010-03-17 05:45:15 +00:00
|
|
|
if (hspc == YES)
|
|
|
|
fldfmt[fmtlen++] = '2'; // ensure a leading space
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fldfmt[fmtlen++] = '0';
|
|
|
|
fldfmt[fmtlen++] = '2';
|
|
|
|
}
|
|
|
|
|
2009-05-16 19:35:20 +00:00
|
|
|
}
|
|
|
|
fldfmt[fmtlen++] = 'd';
|
2011-03-09 10:24:18 +00:00
|
|
|
fldfmt[fmtlen] = 0;
|
2010-03-17 05:45:15 +00:00
|
|
|
if (GSPrivateDefaultsFlag(GSMacOSXCompatible)
|
|
|
|
&& hspc == YES)
|
|
|
|
{
|
|
|
|
Grow(info, 2);
|
|
|
|
info->t[info->offset++] = '%';
|
|
|
|
info->t[info->offset++] = f[i-1];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
outputValueWithFormat(v, fldfmt, info);
|
2004-05-13 09:10:35 +00:00
|
|
|
break;
|
1997-09-13 17:52:31 +00:00
|
|
|
|
2004-05-13 09:10:35 +00:00
|
|
|
// is it the minute
|
|
|
|
case 'M':
|
2004-06-21 10:56:32 +00:00
|
|
|
v = info->mnd;
|
2009-05-16 19:35:20 +00:00
|
|
|
if (fmtlen == 1) // no format width specified; supply default
|
|
|
|
{
|
|
|
|
fldfmt[fmtlen++] = '0';
|
|
|
|
fldfmt[fmtlen++] = '2';
|
|
|
|
}
|
|
|
|
fldfmt[fmtlen++] = 'd';
|
2011-03-09 10:24:18 +00:00
|
|
|
fldfmt[fmtlen] = 0;
|
2009-05-16 19:35:20 +00:00
|
|
|
outputValueWithFormat(v, fldfmt, info);
|
2004-05-13 09:10:35 +00:00
|
|
|
break;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
2004-05-13 09:10:35 +00:00
|
|
|
// is it the second
|
|
|
|
case 'S':
|
2004-06-21 10:56:32 +00:00
|
|
|
v = info->sd;
|
2009-05-16 19:35:20 +00:00
|
|
|
if (fmtlen == 1) // no format width specified; supply default
|
|
|
|
{
|
|
|
|
fldfmt[fmtlen++] = '0';
|
|
|
|
fldfmt[fmtlen++] = '2';
|
|
|
|
}
|
|
|
|
fldfmt[fmtlen++] = 'd';
|
2011-03-09 10:24:18 +00:00
|
|
|
fldfmt[fmtlen] = 0;
|
2009-05-16 19:35:20 +00:00
|
|
|
outputValueWithFormat(v, fldfmt, info);
|
2004-05-13 09:10:35 +00:00
|
|
|
break;
|
1997-10-28 14:34:49 +00:00
|
|
|
|
2004-05-13 09:10:35 +00:00
|
|
|
// Is it the am/pm indicator
|
|
|
|
case 'p':
|
|
|
|
{
|
2016-04-08 17:28:05 +00:00
|
|
|
NSArray *a = [LOCALE objectForKey: NSAMPMDesignation];
|
2004-05-13 09:10:35 +00:00
|
|
|
NSString *ampm;
|
|
|
|
|
2004-06-21 10:56:32 +00:00
|
|
|
if (info->hd >= 12)
|
2004-05-13 09:10:35 +00:00
|
|
|
{
|
|
|
|
if ([a count] > 1)
|
|
|
|
ampm = [a objectAtIndex: 1];
|
|
|
|
else
|
|
|
|
ampm = @"pm";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ([a count] > 0)
|
|
|
|
ampm = [a objectAtIndex: 0];
|
|
|
|
else
|
|
|
|
ampm = @"am";
|
|
|
|
}
|
2004-06-21 10:56:32 +00:00
|
|
|
v = [ampm length];
|
|
|
|
Grow(info, v);
|
|
|
|
[ampm getCharacters: info->t + info->offset];
|
|
|
|
info->offset += v;
|
2004-05-13 09:10:35 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// is it the zone name
|
|
|
|
case 'Z':
|
2004-06-21 10:56:32 +00:00
|
|
|
{
|
|
|
|
NSString *s;
|
|
|
|
|
|
|
|
s = abbrev(_time_zone, self);
|
|
|
|
v = [s length];
|
|
|
|
Grow(info, v);
|
|
|
|
[s getCharacters: info->t + info->offset];
|
|
|
|
info->offset += v;
|
|
|
|
}
|
2004-05-13 09:10:35 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'z':
|
2004-06-21 10:56:32 +00:00
|
|
|
{
|
|
|
|
int z;
|
|
|
|
|
|
|
|
Grow(info, 5);
|
|
|
|
z = offset(_time_zone, self);
|
|
|
|
if (z < 0)
|
|
|
|
{
|
|
|
|
z = -z;
|
|
|
|
info->t[info->offset++] = '-';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
info->t[info->offset++] = '+';
|
|
|
|
}
|
|
|
|
z /= 60; // Convert seconds to minutes.
|
|
|
|
v = z / 60;
|
|
|
|
info->t[info->offset+1] = (v%10) + '0';
|
|
|
|
v /= 10;
|
|
|
|
info->t[info->offset+0] = (v%10) + '0';
|
|
|
|
info->offset += 2;
|
|
|
|
v = z % 60;
|
|
|
|
info->t[info->offset+1] = (v%10) + '0';
|
|
|
|
v /= 10;
|
|
|
|
info->t[info->offset+0] = (v%10) + '0';
|
|
|
|
info->offset += 2;
|
2004-05-13 09:10:35 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Anything else is unknown so just copy
|
|
|
|
default:
|
2004-06-21 10:56:32 +00:00
|
|
|
Grow(info, 2);
|
|
|
|
info->t[info->offset++] = '%';
|
|
|
|
info->t[info->offset++] = f[i-1];
|
2004-05-13 09:10:35 +00:00
|
|
|
break;
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-06-21 10:56:32 +00:00
|
|
|
Grow(info, 1);
|
|
|
|
info->t[info->offset++] = f[i++];
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-06-21 10:56:32 +00:00
|
|
|
if (f != fbuf)
|
|
|
|
{
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a string representation of the receiver using the specified
|
|
|
|
* format string and locale dictionary.<br />
|
|
|
|
* Format specifiers are -
|
|
|
|
* <list>
|
|
|
|
* <item>
|
|
|
|
* %a abbreviated weekday name according to locale
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %A full weekday name according to locale
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %b abbreviated month name according to locale
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %c this is the same as %X %x
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %B full month name according to locale
|
|
|
|
* </item>
|
|
|
|
* <item>
|
2010-03-17 06:00:14 +00:00
|
|
|
* %d day of month as two digit decimal number (leading zero)
|
2004-06-21 10:56:32 +00:00
|
|
|
* </item>
|
|
|
|
* <item>
|
2010-03-17 06:00:14 +00:00
|
|
|
* %e day of month as decimal number (without leading zero)
|
2004-06-21 10:56:32 +00:00
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %F milliseconds (000 to 999)
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %H hour as a decimal number using 24-hour clock
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %I hour as a decimal number using 12-hour clock
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %j day of year as a decimal number
|
|
|
|
* </item>
|
|
|
|
* <item>
|
2010-03-17 05:45:15 +00:00
|
|
|
* %k same as %H with leading space instead of zero
|
|
|
|
* </item>
|
|
|
|
* <item>
|
2004-06-21 10:56:32 +00:00
|
|
|
* %m month as decimal number
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %M minute as decimal number
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %p 'am' or 'pm'
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %S second as decimal number
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %U week of the current year as decimal number (Sunday first day)
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %W week of the current year as decimal number (Monday first day)
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %w day of the week as decimal number (Sunday = 0)
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %x date formatted according to the locale
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %X time formatted according to the locale
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %y year as a decimal number without century (minimum 0)
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %Y year as a decimal number with century, minimum 0, maximum 9999
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %z time zone offset (HHMM)
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %Z time zone
|
|
|
|
* </item>
|
|
|
|
* <item>
|
|
|
|
* %% literal % character
|
|
|
|
* </item>
|
|
|
|
* </list>
|
2010-03-17 05:45:15 +00:00
|
|
|
*
|
|
|
|
* <p>NB. If GSMacOSCompatible is set to YES, the %k specifier is not
|
|
|
|
* recognized.</p>
|
2004-06-21 10:56:32 +00:00
|
|
|
*/
|
|
|
|
- (NSString*) descriptionWithCalendarFormat: (NSString*)format
|
|
|
|
locale: (NSDictionary*)locale
|
|
|
|
{
|
|
|
|
unichar tbuf[512];
|
|
|
|
NSString *result;
|
|
|
|
DescriptionInfo info;
|
|
|
|
|
|
|
|
if (format == nil)
|
2016-04-08 17:28:05 +00:00
|
|
|
format = [LOCALE objectForKey: NSTimeDateFormatString];
|
2004-06-21 10:56:32 +00:00
|
|
|
|
|
|
|
GSBreakTime(_seconds_since_ref + offset(_time_zone, self),
|
|
|
|
&info.yd, &info.md, &info.dom, &info.hd, &info.mnd, &info.sd, &info.mil);
|
|
|
|
|
|
|
|
info.base = tbuf;
|
|
|
|
info.t = tbuf;
|
|
|
|
info.length = sizeof(tbuf)/sizeof(unichar);
|
|
|
|
info.offset = 0;
|
|
|
|
|
|
|
|
[self _format: format locale: locale info: &info];
|
|
|
|
|
|
|
|
result = [NSString stringWithCharacters: info.t length: info.offset];
|
|
|
|
|
|
|
|
if (info.t != tbuf)
|
|
|
|
{
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), info.t);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
1999-04-19 14:29:52 +00:00
|
|
|
- (id) copyWithZone: (NSZone*)zone
|
1998-10-15 05:03:16 +00:00
|
|
|
{
|
1999-04-21 20:16:25 +00:00
|
|
|
NSCalendarDate *newDate;
|
1998-10-15 05:03:16 +00:00
|
|
|
|
1999-04-21 20:16:25 +00:00
|
|
|
if (NSShouldRetainWithZone(self, zone))
|
|
|
|
{
|
|
|
|
newDate = RETAIN(self);
|
1998-10-15 05:03:16 +00:00
|
|
|
}
|
1999-04-21 20:16:25 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
newDate = (NSCalendarDate*)NSCopyObject(self, 0, zone);
|
1998-10-15 05:03:16 +00:00
|
|
|
|
2002-10-01 10:25:40 +00:00
|
|
|
if (newDate != nil)
|
1999-04-21 20:16:25 +00:00
|
|
|
{
|
2002-10-01 10:25:40 +00:00
|
|
|
if (_calendar_format != cformat)
|
|
|
|
{
|
|
|
|
newDate->_calendar_format = [_calendar_format copyWithZone: zone];
|
|
|
|
}
|
|
|
|
if (_time_zone != localTZ)
|
|
|
|
{
|
|
|
|
newDate->_time_zone = RETAIN(_time_zone);
|
|
|
|
}
|
1998-10-15 05:03:16 +00:00
|
|
|
}
|
|
|
|
}
|
1999-04-21 20:16:25 +00:00
|
|
|
return newDate;
|
1998-10-15 05:03:16 +00:00
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Returns a description of the receiver using its normal format but with
|
|
|
|
* the specified locale dictionary.<br />
|
|
|
|
* Calls -descriptionWithCalendarFormat:locale: to do this.
|
|
|
|
*/
|
2011-02-23 11:52:17 +00:00
|
|
|
- (NSString*) descriptionWithLocale: (id)locale
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
return [self descriptionWithCalendarFormat: _calendar_format locale: locale];
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Returns the format string associated with the receiver.<br />
|
|
|
|
* See -descriptionWithCalendarFormat:locale: for details.
|
|
|
|
*/
|
1999-08-25 16:15:12 +00:00
|
|
|
- (NSString*) calendarFormat
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
return _calendar_format;
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Sets the format string associated with the receiver.<br />
|
2003-10-25 05:54:39 +00:00
|
|
|
* Providing a nil argument sets the default calendar format.<br />
|
2003-01-07 15:07:06 +00:00
|
|
|
* See -descriptionWithCalendarFormat:locale: for details.
|
|
|
|
*/
|
1999-08-25 16:15:12 +00:00
|
|
|
- (void) setCalendarFormat: (NSString *)format
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2003-10-25 05:54:39 +00:00
|
|
|
if (format == nil)
|
|
|
|
{
|
|
|
|
format = cformat;
|
|
|
|
}
|
|
|
|
ASSIGNCOPY(_calendar_format, format);
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
2003-10-25 05:54:39 +00:00
|
|
|
* Sets the time zone associated with the receiver.<br />
|
|
|
|
* Providing a nil argument sets the local time zone.
|
2003-01-07 15:07:06 +00:00
|
|
|
*/
|
1999-08-25 16:15:12 +00:00
|
|
|
- (void) setTimeZone: (NSTimeZone *)aTimeZone
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2003-10-25 05:54:39 +00:00
|
|
|
if (aTimeZone == nil)
|
|
|
|
{
|
|
|
|
aTimeZone = localTZ;
|
|
|
|
}
|
2001-01-30 12:05:02 +00:00
|
|
|
ASSIGN(_time_zone, aTimeZone);
|
|
|
|
}
|
1999-08-25 16:15:12 +00:00
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Returns the time zone associated with the receiver.
|
|
|
|
*/
|
2001-01-30 12:05:02 +00:00
|
|
|
- (NSTimeZone*) timeZone
|
|
|
|
{
|
|
|
|
return _time_zone;
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Returns the time zone detail associated with the receiver.
|
|
|
|
*/
|
1999-08-25 16:15:12 +00:00
|
|
|
- (NSTimeZoneDetail*) timeZoneDetail
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2001-01-30 12:05:02 +00:00
|
|
|
NSTimeZoneDetail *detail = [_time_zone timeZoneDetailForDate: self];
|
|
|
|
return detail;
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
2003-01-07 15:26:42 +00:00
|
|
|
/**
|
2004-06-22 22:40:40 +00:00
|
|
|
* Routines for manipulating Gregorian dates.
|
2003-01-07 15:26:42 +00:00
|
|
|
*/
|
1996-10-31 17:13:12 +00:00
|
|
|
// The following code is based upon the source code in
|
|
|
|
// ``Calendrical Calculations'' by Nachum Dershowitz and Edward M. Reingold,
|
|
|
|
// Software---Practice & Experience, vol. 20, no. 9 (September, 1990),
|
|
|
|
// pp. 899--928.
|
|
|
|
|
|
|
|
@implementation NSCalendarDate (GregorianDate)
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Returns the number of the last day of the month in the specified year.
|
|
|
|
*/
|
2009-02-23 20:42:32 +00:00
|
|
|
- (NSInteger) lastDayOfGregorianMonth: (NSInteger)month year: (NSInteger)year
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2000-06-30 15:44:30 +00:00
|
|
|
return lastDayOfGregorianMonth(month, year);
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Returns the number of days since the start of the era for the specified
|
|
|
|
* day, month, and year.
|
|
|
|
*/
|
2009-02-23 20:42:32 +00:00
|
|
|
- (NSInteger) absoluteGregorianDay: (NSInteger)day
|
|
|
|
month: (NSInteger)month
|
|
|
|
year: (NSInteger)year
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2000-06-30 15:44:30 +00:00
|
|
|
return absoluteGregorianDay(day, month, year);
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
2003-01-09 16:03:50 +00:00
|
|
|
* Given a day number since the start of the era, returns the date as a
|
2003-01-07 15:07:06 +00:00
|
|
|
* day, month, and year.
|
|
|
|
*/
|
2009-02-23 20:42:32 +00:00
|
|
|
- (void) gregorianDateFromAbsolute: (NSInteger)d
|
|
|
|
day: (NSInteger *)day
|
|
|
|
month: (NSInteger *)month
|
|
|
|
year: (NSInteger *)year
|
1996-10-31 17:13:12 +00:00
|
|
|
{
|
2016-03-15 18:22:57 +00:00
|
|
|
gregorianDateFromAbsolute(d, day, month, year);
|
1996-10-31 17:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
1997-10-28 14:34:49 +00:00
|
|
|
|
|
|
|
|
2003-01-07 15:26:42 +00:00
|
|
|
/**
|
2004-06-22 22:40:40 +00:00
|
|
|
* Methods present in OpenStep but later removed from MacOS-X.
|
2003-01-07 15:26:42 +00:00
|
|
|
*/
|
1997-10-28 14:34:49 +00:00
|
|
|
@implementation NSCalendarDate (OPENSTEP)
|
|
|
|
|
2009-02-23 20:42:32 +00:00
|
|
|
- (NSCalendarDate*) dateByAddingYears: (NSInteger)years
|
|
|
|
months: (NSInteger)months
|
|
|
|
days: (NSInteger)days
|
|
|
|
hours: (NSInteger)hours
|
|
|
|
minutes: (NSInteger)minutes
|
|
|
|
seconds: (NSInteger)seconds
|
1997-10-28 14:34:49 +00:00
|
|
|
{
|
2002-09-28 09:30:38 +00:00
|
|
|
NSCalendarDate *c;
|
|
|
|
NSTimeInterval s;
|
|
|
|
NSTimeInterval oldOffset;
|
|
|
|
NSTimeInterval newOffset;
|
2016-03-15 18:22:57 +00:00
|
|
|
NSInteger i, year, month, day, hour, minute, second, mil;
|
1997-10-28 14:34:49 +00:00
|
|
|
|
2006-10-23 12:24:52 +00:00
|
|
|
/* Apply timezone offset to _seconds_since_ref from GMT to local time,
|
|
|
|
* then break into components in local time zone.
|
2002-09-28 09:30:38 +00:00
|
|
|
*/
|
2006-10-23 12:24:52 +00:00
|
|
|
oldOffset = offset(_time_zone, self);
|
|
|
|
s = _seconds_since_ref + oldOffset;
|
|
|
|
GSBreakTime(s, &year, &month, &day, &hour, &minute, &second, &mil);
|
1997-10-28 14:34:49 +00:00
|
|
|
|
2006-10-23 12:24:52 +00:00
|
|
|
/* Apply required offsets to get new local time.
|
|
|
|
*/
|
2002-09-28 09:30:38 +00:00
|
|
|
while (years != 0 || months != 0 || days != 0
|
|
|
|
|| hours != 0 || minutes != 0 || seconds != 0)
|
1997-10-28 14:34:49 +00:00
|
|
|
{
|
2002-09-28 09:30:38 +00:00
|
|
|
year += years;
|
|
|
|
years = 0;
|
1997-10-28 14:34:49 +00:00
|
|
|
|
2002-09-28 09:30:38 +00:00
|
|
|
month += months;
|
|
|
|
months = 0;
|
|
|
|
while (month > 12)
|
1997-10-28 14:34:49 +00:00
|
|
|
{
|
2002-09-28 09:30:38 +00:00
|
|
|
year++;
|
|
|
|
month -= 12;
|
|
|
|
}
|
|
|
|
while (month < 1)
|
|
|
|
{
|
|
|
|
year--;
|
|
|
|
month += 12;
|
|
|
|
}
|
|
|
|
|
|
|
|
day += days;
|
|
|
|
days = 0;
|
|
|
|
if (day > 28)
|
|
|
|
{
|
|
|
|
i = lastDayOfGregorianMonth(month, year);
|
|
|
|
while (day > i)
|
1997-10-28 14:34:49 +00:00
|
|
|
{
|
2002-09-28 09:30:38 +00:00
|
|
|
day -= i;
|
|
|
|
if (month < 12)
|
|
|
|
{
|
|
|
|
month++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
month = 1;
|
|
|
|
year++;
|
|
|
|
}
|
|
|
|
i = lastDayOfGregorianMonth(month, year);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while (day < 1)
|
|
|
|
{
|
|
|
|
if (month == 1)
|
|
|
|
{
|
|
|
|
year--;
|
|
|
|
month = 12;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
month--;
|
|
|
|
}
|
|
|
|
day += lastDayOfGregorianMonth(month, year);
|
1997-10-28 14:34:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-09-28 09:30:38 +00:00
|
|
|
hour += hours;
|
|
|
|
hours = 0;
|
|
|
|
days += hour/24;
|
|
|
|
hour %= 24;
|
|
|
|
if (hour < 0)
|
|
|
|
{
|
|
|
|
days--;
|
|
|
|
hour += 24;
|
|
|
|
}
|
|
|
|
|
|
|
|
minute += minutes;
|
|
|
|
minutes = 0;
|
|
|
|
hours += minute/60;
|
|
|
|
minute %= 60;
|
|
|
|
if (minute < 0)
|
|
|
|
{
|
2005-08-25 08:05:31 +00:00
|
|
|
hours--;
|
2002-09-28 09:30:38 +00:00
|
|
|
minute += 60;
|
|
|
|
}
|
|
|
|
|
|
|
|
second += seconds;
|
|
|
|
seconds = 0;
|
|
|
|
minutes += second/60;
|
|
|
|
second %= 60;
|
|
|
|
if (second < 0)
|
|
|
|
{
|
|
|
|
minutes--;
|
|
|
|
second += 60;
|
|
|
|
}
|
1997-10-28 14:34:49 +00:00
|
|
|
}
|
|
|
|
|
2002-09-28 09:30:38 +00:00
|
|
|
/*
|
2006-10-23 12:24:52 +00:00
|
|
|
* Reassemble and apply original timezone offset to get
|
|
|
|
* _seconds_since_ref back to GMT.
|
2002-09-28 09:30:38 +00:00
|
|
|
*/
|
|
|
|
s = GSTime(day, month, year, hour, minute, second, mil);
|
2006-10-23 12:24:52 +00:00
|
|
|
s -= oldOffset;
|
2009-04-27 18:04:56 +00:00
|
|
|
c = [NSCalendarDateClass alloc];
|
|
|
|
c->_calendar_format = [_calendar_format copy];
|
|
|
|
c->_time_zone = [_time_zone copy];
|
2002-09-28 09:30:38 +00:00
|
|
|
c->_seconds_since_ref = s;
|
1997-10-28 14:34:49 +00:00
|
|
|
|
1999-05-27 09:41:31 +00:00
|
|
|
/*
|
2002-09-28 09:30:38 +00:00
|
|
|
* Adjust date to try to maintain the time of day over
|
|
|
|
* a daylight savings time boundary if necessary.
|
1999-05-27 09:41:31 +00:00
|
|
|
*/
|
2002-09-30 18:34:02 +00:00
|
|
|
newOffset = offset(_time_zone, c);
|
2002-09-28 09:30:38 +00:00
|
|
|
if (newOffset != oldOffset)
|
1999-05-27 09:41:31 +00:00
|
|
|
{
|
2002-09-28 09:30:38 +00:00
|
|
|
NSTimeInterval tmpOffset = newOffset;
|
1999-05-27 09:41:31 +00:00
|
|
|
|
2002-09-28 09:30:38 +00:00
|
|
|
s -= (newOffset - oldOffset);
|
|
|
|
c->_seconds_since_ref = s;
|
|
|
|
/*
|
|
|
|
* If the date we have lies within a missing hour at a
|
|
|
|
* daylight savings time transition, we use the original
|
|
|
|
* date rather than the adjusted one.
|
|
|
|
*/
|
2002-09-30 18:34:02 +00:00
|
|
|
newOffset = offset(_time_zone, c);
|
2002-09-28 09:30:38 +00:00
|
|
|
if (newOffset == oldOffset)
|
|
|
|
{
|
|
|
|
s += (tmpOffset - oldOffset);
|
|
|
|
c->_seconds_since_ref = s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return AUTORELEASE(c);
|
1997-10-28 14:34:49 +00:00
|
|
|
}
|
|
|
|
|
2003-01-07 15:07:06 +00:00
|
|
|
/**
|
|
|
|
* Returns the number of years, months, days, hours, minutes, and seconds
|
2003-09-10 07:59:14 +00:00
|
|
|
* between the receiver and the given date.<br />
|
|
|
|
* If date is in the future of the receiver, the returned values will
|
|
|
|
* be negative (or zero), otherwise they are all positive.<br />
|
|
|
|
* If any of the pointers to return value in is null, the corresponding
|
|
|
|
* value will not be returned, and other return values will be adjusted
|
|
|
|
* accordingly. eg. If a difference of 1 hour was to be returned but
|
|
|
|
* hours is null, then the value returned in minutes will be increased
|
|
|
|
* by 60.
|
2003-01-07 15:07:06 +00:00
|
|
|
*/
|
2009-02-23 20:42:32 +00:00
|
|
|
- (void) years: (NSInteger*)years
|
|
|
|
months: (NSInteger*)months
|
|
|
|
days: (NSInteger*)days
|
|
|
|
hours: (NSInteger*)hours
|
|
|
|
minutes: (NSInteger*)minutes
|
|
|
|
seconds: (NSInteger*)seconds
|
1997-10-28 14:34:49 +00:00
|
|
|
sinceDate: (NSDate*)date
|
|
|
|
{
|
|
|
|
NSCalendarDate *start;
|
|
|
|
NSCalendarDate *end;
|
|
|
|
NSCalendarDate *tmp;
|
|
|
|
int diff;
|
|
|
|
int extra;
|
|
|
|
int sign;
|
2016-03-15 18:22:57 +00:00
|
|
|
NSInteger mil;
|
|
|
|
NSInteger syear, smonth, sday, shour, sminute, ssecond;
|
|
|
|
NSInteger eyear, emonth, eday, ehour, eminute, esecond;
|
1997-10-28 14:34:49 +00:00
|
|
|
|
|
|
|
/* FIXME What if the two dates are in different time zones?
|
|
|
|
How about daylight savings time?
|
|
|
|
*/
|
2009-04-27 18:04:56 +00:00
|
|
|
if ([date isKindOfClass: NSCalendarDateClass])
|
2003-07-28 16:44:24 +00:00
|
|
|
{
|
|
|
|
tmp = (NSCalendarDate*)RETAIN(date);
|
|
|
|
}
|
2002-08-27 13:47:58 +00:00
|
|
|
else if ([date isKindOfClass: [NSDate class]])
|
2003-07-28 16:44:24 +00:00
|
|
|
{
|
2009-04-27 18:04:56 +00:00
|
|
|
tmp = [[NSCalendarDateClass alloc] initWithTimeIntervalSinceReferenceDate:
|
2003-07-28 16:44:24 +00:00
|
|
|
[date timeIntervalSinceReferenceDate]];
|
|
|
|
}
|
2002-08-27 13:47:58 +00:00
|
|
|
else
|
2003-07-28 16:44:24 +00:00
|
|
|
{
|
|
|
|
tmp = nil; // Avoid compiler warning
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
format: @"%@ invalid date given - %@",
|
|
|
|
NSStringFromSelector(_cmd), date];
|
|
|
|
}
|
1997-10-28 14:34:49 +00:00
|
|
|
|
|
|
|
end = (NSCalendarDate*)[self laterDate: tmp];
|
|
|
|
if (end == self)
|
|
|
|
{
|
|
|
|
start = tmp;
|
|
|
|
sign = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
start = self;
|
|
|
|
sign = -1;
|
|
|
|
}
|
|
|
|
|
2002-09-30 18:34:02 +00:00
|
|
|
GSBreakTime(start->_seconds_since_ref + offset(start->_time_zone, start),
|
2002-09-28 15:45:26 +00:00
|
|
|
&syear, &smonth, &sday, &shour, &sminute, &ssecond, &mil);
|
|
|
|
|
2002-09-30 18:34:02 +00:00
|
|
|
GSBreakTime(end->_seconds_since_ref + offset(end->_time_zone, end),
|
2002-09-28 15:45:26 +00:00
|
|
|
&eyear, &emonth, &eday, &ehour, &eminute, &esecond, &mil);
|
1997-10-28 14:34:49 +00:00
|
|
|
|
2003-09-10 07:59:14 +00:00
|
|
|
if (esecond < ssecond)
|
|
|
|
{
|
|
|
|
eminute -= 1;
|
|
|
|
esecond += 60;
|
|
|
|
}
|
|
|
|
if (eminute < sminute)
|
|
|
|
{
|
|
|
|
ehour -= 1;
|
|
|
|
eminute += 60;
|
|
|
|
}
|
|
|
|
if (ehour < shour)
|
|
|
|
{
|
|
|
|
eday -= 1;
|
|
|
|
ehour += 24;
|
|
|
|
}
|
|
|
|
if (eday < sday)
|
|
|
|
{
|
|
|
|
emonth -= 1;
|
|
|
|
if (emonth >= 0)
|
|
|
|
{
|
|
|
|
eday += [end lastDayOfGregorianMonth: emonth year: eyear];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
eday += 31;
|
|
|
|
}
|
|
|
|
}
|
2005-07-25 16:00:52 +00:00
|
|
|
if (emonth < smonth || (emonth == smonth && eday < sday))
|
2003-09-10 07:59:14 +00:00
|
|
|
{
|
|
|
|
eyear -= 1;
|
|
|
|
emonth += 12;
|
|
|
|
}
|
|
|
|
|
1997-10-28 14:34:49 +00:00
|
|
|
/* Calculate year difference and leave any remaining months in 'extra' */
|
|
|
|
diff = eyear - syear;
|
|
|
|
extra = 0;
|
2003-09-10 07:59:14 +00:00
|
|
|
if (years != 0)
|
1997-10-28 14:34:49 +00:00
|
|
|
{
|
2003-09-10 07:59:14 +00:00
|
|
|
*years = sign*diff;
|
1997-10-28 14:34:49 +00:00
|
|
|
}
|
|
|
|
else
|
2003-09-10 07:59:14 +00:00
|
|
|
{
|
|
|
|
extra += diff*12;
|
|
|
|
}
|
1997-10-28 14:34:49 +00:00
|
|
|
|
|
|
|
/* Calculate month difference and leave any remaining days in 'extra' */
|
|
|
|
diff = emonth - smonth + extra;
|
|
|
|
extra = 0;
|
2003-09-10 07:59:14 +00:00
|
|
|
if (months != 0)
|
1997-10-28 14:34:49 +00:00
|
|
|
{
|
2003-09-10 07:59:14 +00:00
|
|
|
*months = sign*diff;
|
1997-10-28 14:34:49 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-07-25 16:00:52 +00:00
|
|
|
while (diff-- > 0)
|
1999-04-23 23:17:32 +00:00
|
|
|
{
|
|
|
|
int tmpmonth = emonth - diff - 1;
|
|
|
|
int tmpyear = eyear;
|
|
|
|
|
|
|
|
while (tmpmonth < 1)
|
|
|
|
{
|
|
|
|
tmpmonth += 12;
|
|
|
|
tmpyear--;
|
|
|
|
}
|
2000-06-30 15:44:30 +00:00
|
|
|
extra += lastDayOfGregorianMonth(tmpmonth, tmpyear);
|
1999-04-23 23:17:32 +00:00
|
|
|
}
|
1997-10-28 14:34:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate day difference and leave any remaining hours in 'extra' */
|
|
|
|
diff = eday - sday + extra;
|
|
|
|
extra = 0;
|
2003-09-10 07:59:14 +00:00
|
|
|
if (days != 0)
|
1997-10-28 14:34:49 +00:00
|
|
|
{
|
2003-09-10 07:59:14 +00:00
|
|
|
*days = sign*diff;
|
1997-10-28 14:34:49 +00:00
|
|
|
}
|
|
|
|
else
|
2003-09-10 07:59:14 +00:00
|
|
|
{
|
|
|
|
extra += diff*24;
|
|
|
|
}
|
1997-10-28 14:34:49 +00:00
|
|
|
|
|
|
|
/* Calculate hour difference and leave any remaining minutes in 'extra' */
|
|
|
|
diff = ehour - shour + extra;
|
|
|
|
extra = 0;
|
2003-09-10 07:59:14 +00:00
|
|
|
if (hours != 0)
|
1997-10-28 14:34:49 +00:00
|
|
|
{
|
2003-09-10 07:59:14 +00:00
|
|
|
*hours = sign*diff;
|
1997-10-28 14:34:49 +00:00
|
|
|
}
|
|
|
|
else
|
2003-09-10 07:59:14 +00:00
|
|
|
{
|
|
|
|
extra += diff*60;
|
|
|
|
}
|
1997-10-28 14:34:49 +00:00
|
|
|
|
|
|
|
/* Calculate minute difference and leave any remaining seconds in 'extra' */
|
|
|
|
diff = eminute - sminute + extra;
|
|
|
|
extra = 0;
|
2003-09-10 07:59:14 +00:00
|
|
|
if (minutes != 0)
|
1997-10-28 14:34:49 +00:00
|
|
|
{
|
2003-09-10 07:59:14 +00:00
|
|
|
*minutes = sign*diff;
|
1997-10-28 14:34:49 +00:00
|
|
|
}
|
|
|
|
else
|
2003-09-10 07:59:14 +00:00
|
|
|
{
|
|
|
|
extra += diff*60;
|
|
|
|
}
|
1997-10-28 14:34:49 +00:00
|
|
|
|
|
|
|
diff = esecond - ssecond + extra;
|
2003-09-10 07:59:14 +00:00
|
|
|
if (seconds != 0)
|
|
|
|
{
|
|
|
|
*seconds = sign*diff;
|
|
|
|
}
|
1997-10-28 14:34:49 +00:00
|
|
|
|
1999-04-19 14:29:52 +00:00
|
|
|
RELEASE(tmp);
|
1997-10-28 14:34:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|