Merge pull request #3321 from enquora/CPTimezone-modernize

Delegate CPTimezone data source to host browser
This commit is contained in:
David Richardson authored and GitHub committed 2026-09-30 11:24:02 -06:00
commit e5f42bd403
4 files changed
+866 -2186

No files matched your search

+205 -205
View File
@@ -46,19 +46,19 @@ CPDateFormatterBehavior10_0 = 1000;
CPDateFormatterBehavior10_4 = 1040;
var defaultDateFormatterBehavior = CPDateFormatterBehavior10_4,
relativeDateFormating,
patternStringTokens;
relativeDateFormatting,
patternStringTokens;
var _separatorsCharacterSet = nil;
/*!
@ingroup foundation
@class CPDateFormatter
@ingroup foundation
@class CPDateFormatter
CPDateFormatter takes a CPDate value and formats it as text for
display. It also supports the converse, taking text and interpreting it as a
CPDate by configurable formatting rules.
*/
CPDateFormatter takes a CPDate value and formats it as text for
display. It also supports the converse, taking text and interpreting it as a
CPDate by configurable formatting rules.
*/
@implementation CPDateFormatter : CPFormatter
{
BOOL _allowNaturalLanguage @accessors(property=allowNaturalLanguage, readonly);
@@ -83,22 +83,22 @@ var _separatorsCharacterSet = nil;
if (self !== [CPDateFormatter class])
return;
relativeDateFormating = @{
@"fr" : [@"demain", 1, @"apr" + String.fromCharCode(233) + @"s-demain", 2, @"apr" + String.fromCharCode(233) + @"s-apr" + String.fromCharCode(233) + @"s-demain", 3, @"hier", -1, @"avant-hier", -2, @"avant-avant-hier", -3],
@"en" : [@"tomorrow", 1, @"yesterday", -1],
@"de" : [@"morgen", 1, @"gestern", -1, String.fromCharCode(129) + @"bermorgen", 2, @"vorgestern", -2],
@"es" : []
relativeDateFormatting = @{
@"fr" : [@"demain", 1, @"apr" + String.fromCharCode(233) + @"s-demain", 2, @"apr" + String.fromCharCode(233) + @"s-apr" + String.fromCharCode(233) + @"s-demain", 3, @"hier", -1, @"avant-hier", -2, @"avant-avant-hier", -3],
@"en" : [@"tomorrow", 1, @"yesterday", -1],
@"de" : [@"morgen", 1, @"gestern", -1, String.fromCharCode(129) + @"bermorgen", 2, @"vorgestern", -2],
@"es" : []
};
patternStringTokens = [@"QQQ", @"qqq", @"QQQQ", @"qqqq", @"MMM", @"MMMM", @"LLL", @"LLLL", @"E", @"EE", @"EEE", @"eee", @"eeee", @"eeeee", @"a", @"z", @"zz", @"zzz", @"zzzz", @"Z", @"ZZ", @"ZZZ", @"ZZZZ", @"ZZZZZ", @"v", @"vv", @"vvv", @"vvvv", @"V", @"VV", @"VVV", @"VVVV"];
}
/*! Return a string representation of the given date, dateStyle and timeStyle
@param date the given date
@param dateStyle the dateStyle
@param timeStyle the timeStyle
@return a CPString reprensenting the given date
*/
@param date the given date
@param dateStyle the dateStyle
@param timeStyle the timeStyle
@return a CPString reprensenting the given date
*/
+ (CPString)localizedStringFromDate:(CPDate)date dateStyle:(CPDateFormatterStyle)dateStyle timeStyle:(CPDateFormatterStyle)timeStyle
{
var formatter = [[CPDateFormatter alloc] init];
@@ -111,28 +111,29 @@ var _separatorsCharacterSet = nil;
}
/*! Not yet implemented
Return a string representation of the given template, opts and locale
@param template the template
@param opts, pass 0
@param locale the locale
@return a CPString representing the givent template
*/
Return a string representation of the given template, opts and locale
@param template the template
@param opts, pass 0
@param locale the locale
@return a CPString representing the givent template
*/
+ (CPString)dateFormatFromTemplate:(CPString)template options:(CPUInteger)opts locale:(CPLocale)locale
{
// TODO : check every template from cocoa and return a good format (have fun ^^)
// TODO : check every template from cocoa and return a good format
return template;
}
/*! Return the defaultFormatterBehavior
@return a CPDateFormatterBehavior
*/
@return a CPDateFormatterBehavior
*/
+ (CPDateFormatterBehavior)defaultFormatterBehavior
{
return defaultDateFormatterBehavior;
}
/*! Set the defaultFormatterBehavior
@param behavior
*/
@param behavior
*/
+ (void)setDefaultFormatterBehavior:(CPDateFormatterBehavior)behavior
{
defaultDateFormatterBehavior = behavior;
@@ -147,8 +148,8 @@ var _separatorsCharacterSet = nil;
}
/*! Init a dateFormatter
@return a new CPDateFormatter
*/
@return a new CPDateFormatter
*/
- (id)init
{
if (self = [super init])
@@ -163,10 +164,10 @@ var _separatorsCharacterSet = nil;
}
/*! Init a dateFormatter with a format and the naturalLanguage
@param format the format
@param flag flag representation of allowNaturalLanguage
@return a new CPDateFormatter
*/
@param format the format
@param flag flag representation of allowNaturalLanguage
@return a new CPDateFormatter
*/
- (id)initWithDateFormat:(CPString)format allowNaturalLanguage:(BOOL)flag
{
if (self = [self init])
@@ -175,31 +176,31 @@ var _separatorsCharacterSet = nil;
_allowNaturalLanguage = flag;
}
return self
return self;
}
/*! Private init
*/
*/
- (void)_init
{
var AMSymbol = [CPString stringWithFormat:@"%s", @"AM"],
PMSymbol = [CPString stringWithFormat:@"%s", @"PM"],
weekdaySymbols = [CPArray arrayWithObjects:@"Sunday", @"Monday", @"Tuesday", @"Wednesday", @"Thursday", @"Friday", @"Saturday"],
shortWeekdaySymbols = [CPArray arrayWithObjects:@"Sun", @"Mon", @"Tue", @"Wed", @"Thu", @"Fri", @"Sat"],
veryShortWeekdaySymbols = [CPArray arrayWithObjects:@"S", @"M", @"T", @"W", @"T", @"F", @"S"],
standaloneWeekdaySymbols = [CPArray arrayWithObjects:@"Sunday", @"Monday", @"Tuesday", @"Wednesday", @"Thursday", @"Friday", @"Saturday"],
shortStandaloneWeekdaySymbols = [CPArray arrayWithObjects:@"Sun", @"Mon", @"Tue", @"Wed", @"Thu", @"Fri", @"Sat"],
veryShortStandaloneWeekdaySymbols = [CPArray arrayWithObjects:@"S", @"M", @"T", @"W", @"T", @"F", @"S"],
monthSymbols = [CPArray arrayWithObjects:@"January", @"February", @"March", @"April", @"May", @"June", @"July", @"August", @"September", @"October", @"November", @"December"],
shortMonthSymbols = [CPArray arrayWithObjects:@"Jan", @"Feb", @"Mar", @"Apr", @"May", @"Jun", @"Jul", @"Aug", @"Sep", @"Oct", @"Nov", @"Dec"],
veryShortMonthSymbols = [CPArray arrayWithObjects:@"J", @"F", @"M", @"A", @"M", @"J", @"J", @"A", @"S", @"O", @"N", @"D"],
standaloneMonthSymbols = [CPArray arrayWithObjects:@"January", @"February", @"March", @"April", @"May", @"June", @"July", @"August", @"September", @"October", @"November", @"December"],
shortStandaloneMonthSymbols = [CPArray arrayWithObjects:@"Jan", @"Feb", @"Mar", @"Apr", @"May", @"Jun", @"Jul", @"Aug", @"Sep", @"Oct", @"Nov", @"Dec"],
veryShortStandaloneMonthSymbols = [CPArray arrayWithObjects:@"J", @"F", @"M", @"A", @"M", @"J", @"J", @"A", @"S", @"O", @"N", @"D"],
quarterSymbols = [CPArray arrayWithObjects:@"1st quarter", @"2nd quarter", @"3rd quarter", @"4th quarter"],
shortQuarterSymbols = [CPArray arrayWithObjects:@"Q1", @"Q2", @"Q3", @"Q4"],
standaloneQuarterSymbols = [CPArray arrayWithObjects:@"1st quarter", @"2nd quarter", @"3rd quarter", @"4th quarter"],
shortStandaloneQuarterSymbols = [CPArray arrayWithObjects:@"Q1", @"Q2", @"Q3", @"Q4"];
PMSymbol = [CPString stringWithFormat:@"%s", @"PM"],
weekdaySymbols = [CPArray arrayWithObjects:@"Sunday", @"Monday", @"Tuesday", @"Wednesday", @"Thursday", @"Friday", @"Saturday"],
shortWeekdaySymbols = [CPArray arrayWithObjects:@"Sun", @"Mon", @"Tue", @"Wed", @"Thu", @"Fri", @"Sat"],
veryShortWeekdaySymbols = [CPArray arrayWithObjects:@"S", @"M", @"T", @"W", @"T", @"F", @"S"],
standaloneWeekdaySymbols = [CPArray arrayWithObjects:@"Sunday", @"Monday", @"Tuesday", @"Wednesday", @"Thursday", @"Friday", @"Saturday"],
shortStandaloneWeekdaySymbols = [CPArray arrayWithObjects:@"Sun", @"Mon", @"Tue", @"Wed", @"Thu", @"Fri", @"Sat"],
veryShortStandaloneWeekdaySymbols = [CPArray arrayWithObjects:@"S", @"M", @"T", @"W", @"T", @"F", @"S"],
monthSymbols = [CPArray arrayWithObjects:@"January", @"February", @"March", @"April", @"May", @"June", @"July", @"August", @"September", @"October", @"November", @"December"],
shortMonthSymbols = [CPArray arrayWithObjects:@"Jan", @"Feb", @"Mar", @"Apr", @"May", @"Jun", @"Jul", @"Aug", @"Sep", @"Oct", @"Nov", @"Dec"],
veryShortMonthSymbols = [CPArray arrayWithObjects:@"J", @"F", @"M", @"A", @"M", @"J", @"J", @"A", @"S", @"O", @"N", @"D"],
standaloneMonthSymbols = [CPArray arrayWithObjects:@"January", @"February", @"March", @"April", @"May", @"June", @"July", @"August", @"September", @"October", @"November", @"December"],
shortStandaloneMonthSymbols = [CPArray arrayWithObjects:@"Jan", @"Feb", @"Mar", @"Apr", @"May", @"Jun", @"Jul", @"Aug", @"Sep", @"Oct", @"Nov", @"Dec"],
veryShortStandaloneMonthSymbols = [CPArray arrayWithObjects:@"J", @"F", @"M", @"A", @"M", @"J", @"J", @"A", @"S", @"O", @"N", @"D"],
quarterSymbols = [CPArray arrayWithObjects:@"1st quarter", @"2nd quarter", @"3rd quarter", @"4th quarter"],
shortQuarterSymbols = [CPArray arrayWithObjects:@"Q1", @"Q2", @"Q3", @"Q4"],
standaloneQuarterSymbols = [CPArray arrayWithObjects:@"1st quarter", @"2nd quarter", @"3rd quarter", @"4th quarter"],
shortStandaloneQuarterSymbols = [CPArray arrayWithObjects:@"Q1", @"Q2", @"Q3", @"Q4"];
_symbols = @{
@"root" : @{
@@ -235,7 +236,7 @@ var _separatorsCharacterSet = nil;
// MARK: Setter Getter Helper
/*! Return symbols for the given language code
*/
*/
- (CPDictionary)symbolsForLanguageCode:(CPString)languageCode
{
var languageSymbols = [_symbols valueForKey:languageCode];
@@ -250,18 +251,18 @@ var _separatorsCharacterSet = nil;
}
/*! Set the symbols for the given language code
*/
*/
- (void)setSymbols:(CPDictionary)symbols forLanguageCode:(CPString)languageCode
{
[_symbols setValue:symbols forKey:languageCode];
}
/*! Return a symbol specified by the given key for the given language code
*/
*/
- (id)symbolForKey:(CPString)aKey languageCode:(CPString)languageCode
{
var languageSymbols = [self symbolsForLanguageCode:languageCode],
symbol = [languageSymbols valueForKey:aKey];
symbol = [languageSymbols valueForKey:aKey];
if (!symbol)
{
@@ -273,7 +274,7 @@ var _separatorsCharacterSet = nil;
}
/*! Set the symbol specified by the given key for the given language code
*/
*/
- (void)setSymbol:(CPString)aSymbol forKey:(CPString)aKey languageCode:(CPString)languageCode
{
var languageSymbols = [self symbolsForLanguageCode:languageCode];
@@ -284,252 +285,252 @@ var _separatorsCharacterSet = nil;
// MARK: Setter Getter
/*! Return AMSymbol
*/
*/
- (CPString)AMSymbol
{
return [self symbolForKey:@"AMSymbol" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Set the AMSymbol
*/
*/
- (void)setAMSymbol:(CPString)aValue
{
[self setSymbol:aValue forKey:@"AMSymbol" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Return a PMSymbol
*/
*/
- (CPString)PMSymbol
{
return [self symbolForKey:@"PMSymbol" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Set the PMSymbol
*/
*/
- (void)setPMSymbol:(CPString)aValue
{
[self setSymbol:aValue forKey:@"PMSymbol" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Return the weekdaySymbols
*/
*/
- (CPArray)weekdaySymbols
{
return [self symbolForKey:@"weekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Set the weekdaySymbols
*/
*/
- (void)setWeekdaySymbols:(CPArray)aValue
{
[self setSymbol:aValue forKey:@"weekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Return a shortWeekdaySymbols
*/
*/
- (CPArray)shortWeekdaySymbols
{
return [self symbolForKey:@"shortWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Set the shortWeekdaySymbols
*/
*/
- (void)setShortWeekdaySymbols:(CPArray)aValue
{
[self setSymbol:aValue forKey:@"shortWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Return veryShortWeekdaySymbols
*/
*/
- (CPArray)veryShortWeekdaySymbols
{
return [self symbolForKey:@"veryShortWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Set the veryShortWeekdaySymbols
*/
*/
- (void)setVeryShortWeekdaySymbols:(CPArray)aValue
{
[self setSymbol:aValue forKey:@"veryShortWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Return the standaloneWeekdaySymbols
*/
*/
- (CPArray)standaloneWeekdaySymbols
{
return [self symbolForKey:@"standaloneWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Set the standaloneWeekdaySymbols
*/
*/
- (void)setStandaloneWeekdaySymbols:(CPArray)aValue
{
[self setSymbol:aValue forKey:@"standaloneWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Return the shortStandaloneWeekdaySymbols
*/
*/
- (CPArray)shortStandaloneWeekdaySymbols
{
return [self symbolForKey:@"shortStandaloneWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Set the shortStandaloneWeekdaySymbols
*/
*/
- (void)setShortStandaloneWeekdaySymbols:(CPArray)aValue
{
[self setSymbol:aValue forKey:@"shortStandaloneWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Return the veryShortStandaloneWeekdaySymbols
*/
*/
- (CPArray)veryShortStandaloneWeekdaySymbols
{
return [self symbolForKey:@"veryShortStandaloneWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Set the veryShortStandaloneWeekdaySymbols
*/
*/
- (void)setVeryShortStandaloneWeekdaySymbols:(CPArray)aValue
{
[self setSymbol:aValue forKey:@"veryShortStandaloneWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Return the monthSymbols
*/
*/
- (CPArray)monthSymbols
{
return [self symbolForKey:@"monthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Set the monthSymbols
*/
*/
- (void)setMonthSymbols:(CPArray)aValue
{
[self setSymbol:aValue forKey:@"monthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Return a shortMonthSymbols
*/
*/
- (CPArray)shortMonthSymbols
{
return [self symbolForKey:@"shortMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Set the shortMonthSymbols
*/
*/
- (void)setShortMonthSymbols:(CPArray)aValue
{
[self setSymbol:aValue forKey:@"shortMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Return veryShortMonthSymbols
*/
*/
- (CPArray)veryShortMonthSymbols
{
return [self symbolForKey:@"veryShortMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Set the veryShortMonthSymbols
*/
*/
- (void)setVeryShortMonthSymbols:(CPArray)aValue
{
[self setSymbol:aValue forKey:@"veryShortMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Return standaloneMonthSymbols
*/
*/
- (CPArray)standaloneMonthSymbols
{
return [self symbolForKey:@"standaloneMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Set the standaloneMonthSymbols
*/
*/
- (void)setStandaloneMonthSymbols:(CPArray)aValue
{
[self setSymbol:aValue forKey:@"standaloneMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Return the shortStandaloneMonthSymbols
*/
*/
- (CPArray)shortStandaloneMonthSymbols
{
return [self symbolForKey:@"shortStandaloneMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Set the shortStandaloneMonthSymbols
*/
*/
- (void)setShortStandaloneMonthSymbols:(CPArray)aValue
{
[self setSymbol:aValue forKey:@"shortStandaloneMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Return the veryShortStandaloneMonthSymbols
*/
*/
- (CPArray)veryShortStandaloneMonthSymbols
{
return [self symbolForKey:@"veryShortStandaloneMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Set the veryShortStandaloneMonthSymbols
*/
*/
- (void)setVeryShortStandaloneMonthSymbols:(CPArray)aValue
{
[self setSymbol:aValue forKey:@"veryShortStandaloneMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Return the quarterSymbols
*/
*/
- (CPArray)quarterSymbols
{
return [self symbolForKey:@"quarterSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Set the quarterSymbols
*/
*/
- (void)setQuarterSymbols:(CPArray)aValue
{
[self setSymbol:aValue forKey:@"quarterSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Return the shortQuarterSymbols
*/
*/
- (CPArray)shortQuarterSymbols
{
return [self symbolForKey:@"shortQuarterSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Set the shortQuarterSymbols
*/
*/
- (void)setShortQuarterSymbols:(CPArray)aValue
{
[self setSymbol:aValue forKey:@"shortQuarterSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Return the standaloneQuarterSymbols
*/
*/
- (CPArray)standaloneQuarterSymbols
{
return [self symbolForKey:@"standaloneQuarterSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Set the standaloneQuarterSymbols
*/
*/
- (void)setStandaloneQuarterSymbols:(CPArray)aValue
{
[self setSymbol:aValue forKey:@"standaloneQuarterSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Return the shortStandaloneQuarterSymbols
*/
*/
- (CPArray)shortStandaloneQuarterSymbols
{
return [self symbolForKey:@"shortStandaloneQuarterSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
}
/*! Set the shortStandaloneQuarterSymbols
*/
*/
- (void)setShortStandaloneQuarterSymbols:(CPArray)aValue
{
[self setSymbol:aValue forKey:@"shortStandaloneQuarterSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
@@ -540,18 +541,18 @@ var _separatorsCharacterSet = nil;
// MARK: StringFromDate methods
/*! Return a string representation of a given date.
This method returns (if possible) a representation of the given date with the dateFormat of the CPDateFormatter, otherwise it takes the dateStyle and timeStyle
@param aDate the given date
@return CPString the string representation
*/
This method returns (if possible) a representation of the given date with the dateFormat of the CPDateFormatter, otherwise it takes the dateStyle and timeStyle
@param aDate the given date
@return CPString the string representation
*/
- (CPString)stringFromDate:(CPDate)aDate
{
var format,
relativeWord,
result;
relativeWord,
result;
if (!aDate)
return;
return nil;
aDate = [aDate copy];
[aDate _dateWithTimeZone:_timeZone];
@@ -611,7 +612,7 @@ var _separatorsCharacterSet = nil;
if ([self doesRelativeDateFormatting])
{
var language = [_locale objectForKey:CPLocaleLanguageCode],
relativeWords = [relativeDateFormating valueForKey:language];
relativeWords = [relativeDateFormatting valueForKey:language];
for (var i = 1; i < [relativeWords count]; i = i + 2)
{
@@ -687,10 +688,10 @@ var _separatorsCharacterSet = nil;
}
/*! Return a string representation of the given objectValue.
This method call the method stringFromDate if possible, otherwise it returns the description of the object
@param anObject
@return a string
*/
This method call the method stringFromDate if possible, otherwise it returns the description of the object
@param anObject
@return a string
*/
- (CPString)stringForObjectValue:(id)anObject
{
if ([anObject isKindOfClass:[CPDate class]])
@@ -700,26 +701,26 @@ var _separatorsCharacterSet = nil;
}
/*! Return a string
This method call the method stringForObjectValue
@param anObject
@return a string
*/
This method call the method stringForObjectValue
@param anObject
@return a string
*/
- (CPString)editingStringForObjectValue:(id)anObject
{
return [self stringForObjectValue:anObject];
}
/*! Return a string representation of the given date and format
@patam aDate
@param aFormat
@return a string
*/
@patam aDate
@param aFormat
@return a string
*/
- (CPString)_stringFromDate:(CPDate)aDate format:(CPString)aFormat
{
var length = [aFormat length],
currentToken = [CPString new],
isTextToken = NO,
result = [CPString new];
currentToken = [CPString new],
isTextToken = NO,
result = [CPString new];
for (var i = 0; i < length; i++)
{
@@ -778,18 +779,18 @@ var _separatorsCharacterSet = nil;
}
/*! Return a string representation of the given token and date
@param aToken
@param aDate
@return a string
*/
@param aToken
@param aDate
@return a string
*/
- (CPString)_stringFromToken:(CPString)aToken date:(CPDate)aDate
{
if (![aToken length])
return aToken;
var character = [aToken characterAtIndex:0],
length = [aToken length],
timeZone = _timeZone;
length = [aToken length],
timeZone = _timeZone;
switch (character)
{
@@ -902,13 +903,13 @@ var _separatorsCharacterSet = nil;
d.setDate(d.getDate() + 4 - (d.getDay() || 7));
var yearStart = new Date(d.getFullYear(), 0, 1),
weekOfYear = Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
weekOfYear = Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
return [self _stringValueForValue:(weekOfYear + 1) length:MAX(2, length)];
case @"W":
var firstDay = new Date(aDate.getFullYear(), aDate.getMonth(), 1).getDay(),
weekOfMonth = Math.ceil((aDate.getDate() + firstDay) / 7);
weekOfMonth = Math.ceil((aDate.getDate() + firstDay) / 7);
return [self _stringValueForValue:weekOfMonth length:1];
@@ -919,14 +920,14 @@ var _separatorsCharacterSet = nil;
case @"D":
var oneJan = new Date(aDate.getFullYear(), 0, 1),
dayOfYear = Math.ceil((aDate - oneJan) / 86400000),
currentLength = [[CPString stringWithFormat:@"%i", dayOfYear] length];
dayOfYear = Math.ceil((aDate - oneJan) / 86400000),
currentLength = [[CPString stringWithFormat:@"%i", dayOfYear] length];
return [self _stringValueForValue:dayOfYear length:MAX(currentLength, MIN(3, length))];
case @"F":
var dayOfWeek = 1,
day = aDate.getDate();
day = aDate.getDate();
if (day > 7 && day < 15)
dayOfWeek = 2;
@@ -1065,10 +1066,10 @@ var _separatorsCharacterSet = nil;
case @"Z":
var seconds = [timeZone secondsFromGMT],
minutes = seconds / 60,
hours = minutes / 60,
result,
diffMinutes = (hours - parseInt(hours)) * 100 * 60 / 100;
minutes = seconds / 60,
hours = minutes / 60,
result,
diffMinutes = (hours - parseInt(hours)) * 100 * 60 / 100;
if (length <= 3)
{
@@ -1082,7 +1083,7 @@ var _separatorsCharacterSet = nil;
while ([result length] < 4)
result = @"0" + result;
if (seconds > 0)
if (seconds >= 0)
result = @"+" + result;
else
result = @"-" + result;
@@ -1164,10 +1165,10 @@ var _separatorsCharacterSet = nil;
// MARK: datefromString
/*! Return a date of the given string
This method returns (if possible) a representation of the given string with the dateFormat of the CPDateFormatter, otherwise it takes the dateStyle and timeStyle
@param aString
@return CPDate the date
*/
This method returns (if possible) a representation of the given string with the dateFormat of the CPDateFormatter, otherwise it takes the dateStyle and timeStyle
@param aString
@return CPDate the date
*/
- (CPDate)dateFromString:(CPString)aString
{
var format;
@@ -1259,11 +1260,11 @@ var _separatorsCharacterSet = nil;
}
/*! Returns a boolean if the given object has been changed or not depending of the given string (use of ref)
@param anObject the given object
@param aString
@param anError, if it returns NO the describe error will be in anError (use of ref)
@return aBoolean for the success or fail of the method
*/
@param anObject the given object
@param aString
@param anError, if it returns NO the describe error will be in anError (use of ref)
@return aBoolean for the success or fail of the method
*/
- (BOOL)getObjectValue:(idRef)anObject forString:(CPString)aString errorDescription:(CPStringRef)anError
{
var value = [self dateFromString:aString];
@@ -1281,10 +1282,10 @@ var _separatorsCharacterSet = nil;
}
/*! Return a date representation of the given string and format
@patam aDate
@param aFormat
@return a string
*/
@patam aDate
@param aFormat
@return a string
*/
- (CPDate)_dateFromString:(CPString)aString format:(CPString)aFormat
{
// Interpret @"" as the date 2000-01-01 00:00:00 +0000, like in Cocoa. No idea why they picked this particular date.
@@ -1307,10 +1308,10 @@ var _separatorsCharacterSet = nil;
return nil;
var currentToken = [CPString new],
isTextToken = NO,
tokens = [CPArray array],
dateComponents = [CPArray array],
patternTokens = [CPArray array];
isTextToken = NO,
tokens = [CPArray array],
dateComponents = [CPArray array],
patternTokens = [CPArray array];
for (var i = 0; i < [aFormat length]; i++)
{
@@ -1431,19 +1432,19 @@ var _separatorsCharacterSet = nil;
- (CPDate)_dateFromTokens:(CPArray)tokens dateComponents:(CPArray)dateComponents
{
var timeZoneseconds = [_timeZone secondsFromGMT],
dateArray = [2000, 01, 01, 00, 00, 00, @"+0000"],
isPM = NO,
dayOfYear,
dayIndexInWeek,
weekOfYear,
weekOfMonth;
dateArray = [2000, 1, 1, 0, 0, 0],
isPM = NO,
dayOfYear,
dayIndexInWeek,
weekOfYear,
weekOfMonth;
for (var i = 0; i < [tokens count]; i++)
{
var token = [tokens objectAtIndex:i],
dateComponent = [dateComponents objectAtIndex:i],
character = [token characterAtIndex:0],
length = [token length];
dateComponent = [dateComponents objectAtIndex:i],
character = [token characterAtIndex:0],
length = [token length];
switch (character)
{
@@ -1454,9 +1455,9 @@ var _separatorsCharacterSet = nil;
case @"y":
var u = _twoDigitStartDate.getFullYear() % 10,
d = parseInt(_twoDigitStartDate.getFullYear() / 10) % 10,
c = parseInt(_twoDigitStartDate.getFullYear() / 100) % 10,
m = parseInt(_twoDigitStartDate.getFullYear() / 1000) % 10;
d = parseInt(_twoDigitStartDate.getFullYear() / 10) % 10,
c = parseInt(_twoDigitStartDate.getFullYear() / 100) % 10,
m = parseInt(_twoDigitStartDate.getFullYear() / 1000) % 10;
if (length == 2 && dateComponent.length == 2)
{
@@ -1474,9 +1475,9 @@ var _separatorsCharacterSet = nil;
case @"Y":
var u = _twoDigitStartDate.getFullYear() % 10,
d = parseInt(_twoDigitStartDate.getFullYear() / 10) % 10,
c = parseInt(_twoDigitStartDate.getFullYear() / 100) % 10,
m = parseInt(_twoDigitStartDate.getFullYear() / 1000) % 10;
d = parseInt(_twoDigitStartDate.getFullYear() / 10) % 10,
c = parseInt(_twoDigitStartDate.getFullYear() / 100) % 10,
m = parseInt(_twoDigitStartDate.getFullYear() / 1000) % 10;
if (length == 2 && dateComponent.length == 2)
{
@@ -1790,7 +1791,7 @@ var _separatorsCharacterSet = nil;
return nil;
var millisecondsInDay = parseInt(dateComponent),
tmpDate = new Date();
tmpDate = new Date();
tmpDate.setHours(0);
tmpDate.setMinutes(0);
@@ -1816,8 +1817,6 @@ var _separatorsCharacterSet = nil;
if (!timeZoneseconds)
return nil;
timeZoneseconds = timeZoneseconds + 60 * 60;
break;
case @"Z":
@@ -1826,8 +1825,6 @@ var _separatorsCharacterSet = nil;
if (!timeZoneseconds)
return nil;
timeZoneseconds = timeZoneseconds + 60 * 60;
break;
case @"v":
@@ -1842,8 +1839,6 @@ var _separatorsCharacterSet = nil;
if (!timeZoneseconds)
return nil;
timeZoneseconds = timeZoneseconds + 60 * 60;
break;
case @"V":
@@ -1858,8 +1853,6 @@ var _separatorsCharacterSet = nil;
if (!timeZoneseconds)
return nil;
timeZoneseconds = timeZoneseconds + 60 * 60;
break;
default:
@@ -1912,11 +1905,15 @@ var _separatorsCharacterSet = nil;
if (isPM)
dateArray[3] += 12;
if (isNaN(parseInt(dateArray[0])) || isNaN(parseInt(dateArray[1])) || isNaN(parseInt(dateArray[2])) || isNaN(parseInt(dateArray[3])) || isNaN(parseInt(dateArray[4])) || isNaN(parseInt(dateArray[5])) || isNaN(parseInt(dateArray[6])))
if (isNaN(parseInt(dateArray[0])) || isNaN(parseInt(dateArray[1])) || isNaN(parseInt(dateArray[2])) || isNaN(parseInt(dateArray[3])) || isNaN(parseInt(dateArray[4])) || isNaN(parseInt(dateArray[5])))
return nil;
var dateResult = [[CPDate alloc] initWithString:[CPString stringWithFormat:@"%04d-%02d-%02d %02d:%02d:%02d %s", dateArray[0], dateArray[1], dateArray[2], dateArray[3], dateArray[4], dateArray[5], dateArray[6]]];
dateResult.setSeconds(dateResult.getSeconds() - timeZoneseconds + 60 * 60);
var utcTimestamp = Date.UTC(dateArray[0], dateArray[1] - 1, dateArray[2], dateArray[3], dateArray[4], dateArray[5]),
kCFAbsoluteTimeIntervalSince1970 = 978307200.0,
interval = (utcTimestamp / 1000.0) - kCFAbsoluteTimeIntervalSince1970,
dateResult = [CPDate dateWithTimeIntervalSinceReferenceDate:interval];
dateResult.setSeconds(dateResult.getSeconds() - timeZoneseconds);
return dateResult;
}
@@ -1942,29 +1939,29 @@ var _separatorsCharacterSet = nil;
}
/*! Check if we are in the american format or not. Depending on the locale
*/
*/
- (BOOL)_isAmericanFormat
{
return [[_locale objectForKey:CPLocaleCountryCode] isEqualToString:@"US"];
}
/*! Check if we are in the english format or not. Depending on the locale
*/
*/
- (BOOL)_isEnglishFormat
{
return [[_locale objectForKey:CPLocaleLanguageCode] isEqualToString:@"en"];
}
/*! Returns the number of second from a time zone (-8000 or HGP-8:35 or GMT-08:00)
*/
*/
- (int)_secondsFromTimeZoneDefaultFormatString:(CPString)aTimeZoneFormatString
{
var format = new RegExp("\\w*([HPG-GMT])?([+-])(\\d{1,2})([:])?(\\d{2})\\w*"),
result = aTimeZoneFormatString.match(new RegExp(format)),
seconds = 0;
result = aTimeZoneFormatString.match(new RegExp(format)),
seconds = 0;
if (!result)
return nil;
return 0;
seconds = result[3] * 60 * 60 + result[5] * 60;
@@ -1975,30 +1972,30 @@ var _separatorsCharacterSet = nil;
}
/*! Return the number of seconds from a timeZoneString
*/
*/
- (int)_secondsFromTimeZoneString:(CPString)aTimeZoneString style:(NSTimeZoneNameStyle)aStyle
{
var timeZone = [CPTimeZone _timeZoneFromString:aTimeZoneString style:aStyle locale:_locale];
if (!timeZone)
return nil;
return 0;
return [timeZone secondsFromGMT];
}
/*! This method is used to know if the given string match with the token.
@param aString
@param aToken
@param anIndex the current index in the string
@return an index who describes the position of the end of the word for the token
*/
@param aString
@param aToken
@param anIndex the current index in the string
@return an index who describes the position of the end of the word for the token
*/
- (int)_lastIndexMatchedString:(CPString)aString token:(CPString)aToken index:anIndex
{
var character = [aToken characterAtIndex:0],
length = [aToken length],
targetedArray,
format = new RegExp("\\w*([HPG-GMT])?([+-])(\\d{1,2})([:])?(\\d{2})\\w*"),
result = aString.match(new RegExp(format));
length = [aToken length],
targetedArray,
format = new RegExp("\\w*([HPG-GMT])?([+-])(\\d{1,2})([:])?(\\d{2})\\w*"),
result = aString.match(new RegExp(format));
switch (character)
{
@@ -2129,7 +2126,7 @@ var _separatorsCharacterSet = nil;
for (var i = 0; i < [targetedArray count]; i++)
{
var currentObject = [targetedArray objectAtIndex:i],
range = [aString rangeOfString:currentObject];
range = [aString rangeOfString:currentObject];
if (range.length == 0)
continue;
@@ -2146,12 +2143,12 @@ var _separatorsCharacterSet = nil;
@end
var CPDateFormatterDateStyleKey = @"CPDateFormatterDateStyle",
CPDateFormatterTimeStyleKey = @"CPDateFormatterTimeStyleKey",
CPDateFormatterFormatterBehaviorKey = @"CPDateFormatterFormatterBehaviorKey",
CPDateFormatterDoseRelativeDateFormattingKey = @"CPDateFormatterDoseRelativeDateFormattingKey",
CPDateFormatterDateFormatKey = @"CPDateFormatterDateFormatKey",
CPDateFormatterAllowNaturalLanguageKey = @"CPDateFormatterAllowNaturalLanguageKey",
CPDateFormatterLocaleKey = @"CPDateFormatterLocaleKey";
CPDateFormatterTimeStyleKey = @"CPDateFormatterTimeStyleKey",
CPDateFormatterFormatterBehaviorKey = @"CPDateFormatterFormatterBehaviorKey",
CPDateFormatterDoseRelativeDateFormattingKey = @"CPDateFormatterDoseRelativeDateFormattingKey",
CPDateFormatterDateFormatKey = @"CPDateFormatterDateFormatKey",
CPDateFormatterAllowNaturalLanguageKey = @"CPDateFormatterAllowNaturalLanguageKey",
CPDateFormatterLocaleKey = @"CPDateFormatterLocaleKey";
@implementation CPDateFormatter (CPCoding)
@@ -2194,14 +2191,17 @@ var CPDateFormatterDateStyleKey = @"CPDateFormatterDateStyle",
@implementation CPDate (CPTimeZone)
/*! Convert a date from a timeZone
*/
*/
- (void)_dateWithTimeZone:(CPTimeZone)aTimeZone
{
if (!aTimeZone)
return;
self.setSeconds(self.getSeconds() - [aTimeZone secondsFromGMTForDate:self]);
self.setSeconds(self.getSeconds() + [aTimeZone secondsFromGMT]);
var targetOffset = [aTimeZone secondsFromGMTForDate:self],
localOffset = - (self.getTimezoneOffset() * 60),
offsetDifference = targetOffset - localOffset;
self.setSeconds(self.getSeconds() + offsetDifference);
}
@end
+539 -441
View File
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+79 -102
View File
@@ -1,23 +1,23 @@
/* CPTimeZoneTest.j
* Foundation
*
* Created by Alexandre Wilhelm
* Copyright 2012 <alexandre.wilhelmfr@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
* Foundation
*
* Created by Alexandre Wilhelm
* Copyright 2012 <alexandre.wilhelmfr@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/Foundation.j>
@import <OJUnit/OJTestCase.j>
@@ -66,7 +66,7 @@
{
var timeZone = [CPTimeZone timeZoneWithName:@"America/Los_Angeles"];
// The current implementation can return daylight saving time or standard time depending on the current implmentation on CPDictionary
// The current implementation can return daylight saving time or standard time depending on the current date
if ([timeZone abbreviation] === @"PDT") {
[self assert:[timeZone name] equals:@"America/Los_Angeles"];
[self assert:[timeZone abbreviation] equals:@"PDT"];
@@ -96,8 +96,8 @@
{
try
{
[CPTimeZone timeZoneWithName:nil];
[self fail:"Invalid value provided for tzName"];
[CPTimeZone timeZoneWithName:nil];
[self fail:"Invalid value provided for tzName"];
}
catch (e)
{
@@ -109,7 +109,6 @@
{
var timeZone = [CPTimeZone timeZoneWithName:@"America/Los_Angeles" data:_data];
// The current implementation can return daylight saving time or standard time depending on the current implmentation on CPDictionary
if ([timeZone abbreviation] === @"PDT") {
[self assert:[timeZone name] equals:@"America/Los_Angeles"];
[self assert:[timeZone abbreviation] equals:@"PDT"];
@@ -140,8 +139,8 @@
{
try
{
[CPTimeZone timeZoneWithName:nil data:_data];
[self fail:"Invalid value provided for tzName"];
[CPTimeZone timeZoneWithName:nil data:_data];
[self fail:"Invalid value provided for tzName"];
}
catch (e)
{
@@ -151,30 +150,37 @@
- (void)testTimeZoneWithSecondsFromGMT
{
// timeZoneForSecondsFromGMT returns a fixed-offset zone. It does not
// back-resolve to geographical regions (like Pacific/Honolulu).
var timeZone = [CPTimeZone timeZoneForSecondsFromGMT:(-600 * 60)];
[self assert:[timeZone name] equals:@"Pacific/Honolulu"];
[self assert:[timeZone abbreviation] equals:@"HST"];
[self assert:[timeZone name] equals:@"GMT-10:00"];
[self assert:[timeZone abbreviation] equals:@"GMT-10:00"];
[self assert:[timeZone secondsFromGMT] equals:(-600 * 60)];
[self assert:[timeZone description] equals:@"Pacific/Honolulu (HST) offset -36000"];
[self assert:[timeZone localizedName:CPTimeZoneNameStyleStandard locale:_locale] equals:@"Hawaii-Aleutian Standard Time"];
[self assert:[timeZone localizedName:CPTimeZoneNameStyleShortStandard locale:_locale] equals:@"HST"];
[self assert:[timeZone localizedName:CPTimeZoneNameStyleDaylightSaving locale:_locale] equals:@"Hawaii-Aleutian Daylight Time"];
[self assert:[timeZone localizedName:CPTimeZoneNameStyleShortDaylightSaving locale:_locale] equals:@"HDT"];
[self assert:[timeZone localizedName:CPTimeZoneNameStyleGeneric locale:_locale] equals:@"Hawaii-Aleutian Standard Time"];
[self assert:[timeZone localizedName:CPTimeZoneNameStyleShortGeneric locale:_locale] equals:@"HST"];
[self assert:[timeZone description] equals:@"GMT-10:00 (GMT-10:00) offset -36000"];
[self assert:[timeZone localizedName:CPTimeZoneNameStyleStandard locale:_locale] equals:@"GMT-10:00"];
[self assert:[timeZone localizedName:CPTimeZoneNameStyleShortStandard locale:_locale] equals:@"GMT-10:00"];
[self assert:[timeZone localizedName:CPTimeZoneNameStyleDaylightSaving locale:_locale] equals:@"GMT-10:00"];
[self assert:[timeZone localizedName:CPTimeZoneNameStyleShortDaylightSaving locale:_locale] equals:@"GMT-10:00"];
[self assert:[timeZone localizedName:CPTimeZoneNameStyleGeneric locale:_locale] equals:@"GMT-10:00"];
[self assert:[timeZone localizedName:CPTimeZoneNameStyleShortGeneric locale:_locale] equals:@"GMT-10:00"];
}
- (void)testTimeZoneWithWrongSecondsFromGMT
{
// +/- 18 hours is the limit. -421 * 60 is valid and creates a fixed zone.
var timeZone = [CPTimeZone timeZoneForSecondsFromGMT:(-421 * 60)];
[self assert:timeZone equals:nil];
[self assert:[timeZone name] equals:@"GMT-07:01"];
[self assert:[timeZone secondsFromGMT] equals:(-421 * 60)];
// An offset strictly outside the +/- 18 hour range should return nil.
var invalidTimeZone = [CPTimeZone timeZoneForSecondsFromGMT:(19 * 3600)];
[self assert:invalidTimeZone equals:nil];
}
- (void)testInitTimeZoneWithName
{
var timeZone = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles"];
// The current implementation can return daylight saving time or standard time depending on the current implmentation on CPDictionary
if ([timeZone abbreviation] === @"PDT") {
[self assert:[timeZone name] equals:@"America/Los_Angeles"];
[self assert:[timeZone abbreviation] equals:@"PDT"];
@@ -204,8 +210,8 @@
{
try
{
[[CPTimeZone alloc] initWithName:nil];
[self fail:"Invalid value provided for tzName"];
[[CPTimeZone alloc] initWithName:nil];
[self fail:"Invalid value provided for tzName"];
}
catch (e)
{
@@ -217,7 +223,6 @@
{
var timeZone = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles" data:_data];
// The current implementation can return daylight saving time or standard time depending on the current implmentation on CPDictionary
if ([timeZone abbreviation] === @"PDT") {
[self assert:[timeZone name] equals:@"America/Los_Angeles"];
[self assert:[timeZone abbreviation] equals:@"PDT"];
@@ -248,8 +253,8 @@
{
try
{
[[CPTimeZone alloc] initWithName:nil data:_data];
[self fail:"Invalid value provided for tzName"];
[[CPTimeZone alloc] initWithName:nil data:_data];
[self fail:"Invalid value provided for tzName"];
}
catch (e)
{
@@ -260,12 +265,8 @@
- (void)testAbbreviationWithDate
{
var timeZone = [CPTimeZone localTimeZone],
abbreviation = [timeZone abbreviationForDate:_date];
abbreviation = [timeZone abbreviationForDate:_date];
// With the new robust implementation, we can't easily predict the exact
// abbreviation string without re-implementing the parsing logic.
// Instead, we verify that the returned abbreviation is valid and exists
// in the known dictionary, which is sufficient.
[self assert:(abbreviation !== nil) equals:YES];
var knownAbbreviations = [CPTimeZone abbreviationDictionary];
[self assert:[knownAbbreviations containsKey:abbreviation] equals:YES];
@@ -274,7 +275,7 @@
- (void)testAbbreviationWithNilDate
{
var timeZone = [CPTimeZone localTimeZone],
abbreviation = [timeZone abbreviationForDate:nil];
abbreviation = [timeZone abbreviationForDate:nil];
[self assert:abbreviation equals:nil];
}
@@ -283,7 +284,7 @@
- (void)testSecondsFromGMTForDate
{
var timeZone = [CPTimeZone localTimeZone],
seconds = [timeZone secondsFromGMTForDate:_date];
seconds = [timeZone secondsFromGMTForDate:_date];
[self assert:seconds equals:(_date.getTimezoneOffset() * -60)];
}
@@ -291,7 +292,7 @@
- (void)testSecondsFromGMTForDateWithNilDate
{
var timeZone = [CPTimeZone localTimeZone],
seconds = [timeZone secondsFromGMTForDate:nil];
seconds = [timeZone secondsFromGMTForDate:nil];
[self assert:seconds equals:nil];
}
@@ -300,7 +301,6 @@
{
var timeZone = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles"];
// The current implementation can return daylight saving time or standard time depending on the current implmentation on CPDictionary
if ([timeZone abbreviation] === @"PDT") {
[self assert:[timeZone secondsFromGMT] equals:(-420 * 60)];
} else {
@@ -312,7 +312,6 @@
{
var timeZone = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles"];
// The current implementation can return daylight saving time or standard time depending on the current implmentation on CPDictionary
if ([timeZone abbreviation] === @"PDT") {
[self assert:[timeZone description] equals:@"America/Los_Angeles (PDT) offset -25200"];
} else {
@@ -334,7 +333,7 @@
- (void)testEqualTrueToTimeZone
{
var timeZone1 = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles"],
timeZone2 = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles"];
timeZone2 = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles"];
[self assert:[timeZone1 isEqualToTimeZone:timeZone2] equals:YES];
}
@@ -342,25 +341,17 @@
- (void)testEqualFalseToTimeZone
{
var timeZone1 = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles"],
timeZone2 = [[CPTimeZone alloc] initWithName:@"Pacific/Honolulu"];
timeZone2 = [[CPTimeZone alloc] initWithName:@"Pacific/Honolulu"];
[self assert:[timeZone1 isEqualToTimeZone:timeZone2] equals:NO];
}
- (void)testInitWithNameRespectsDaylightSaving
{
// This test verifies that initWithName: correctly selects the abbreviation
// (e.g., PST vs. PDT) based on the current date's daylight saving status.
// 1. Test a time zone that observes DST, like America/Los_Angeles.
var laTimeZoneName = @"America/Los_Angeles";
var expectedAbbreviationLA;
try {
// This logic mirrors the _abbreviationForNameAndDate helper in
// CPTimeZone.j: read the short time zone name directly from Intl,
// as an independent check that CPTimeZone's own use of the same
// API returns the same thing, not a duplicate of an algorithm.
var options = { timeZone: laTimeZoneName, timeZoneName: 'short' };
var parts = new Intl.DateTimeFormat('en-US', options).formatToParts(new Date());
var tzPart = parts.filter(function (p) { return p.type === 'timeZoneName'; })[0];
@@ -377,44 +368,46 @@
[self assert:[timeZoneLA abbreviation] equals:expectedAbbreviationLA];
// 2. Test a time zone that does not observe DST, like Pacific/Honolulu.
var hnlTimeZoneName = @"Pacific/Honolulu";
var timeZoneHNL = [[CPTimeZone alloc] initWithName:hnlTimeZoneName];
if (timeZoneHNL == nil)
[self fail:"Time zone for Pacific/Honolulu should be created successfully."];
// For a non-DST zone, the abbreviation is constant.
[self assert:[timeZoneHNL abbreviation] equals:@"HST"];
}
// Pins the six timeDifferenceFromUTC entries corrected on CPTimeZoneTest-fix
// (MDT, MSK, NZDT, NZST, WAT, WIT) through the public API, so a regression
// back to any of the old wrong values is caught directly.
- (void)testCorrectedOffsets
{
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"MDT"] secondsFromGMT] equals:(-360 * 60)];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"MSK"] secondsFromGMT] equals:(180 * 60)];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"NZDT"] secondsFromGMT] equals:(780 * 60)];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"NZST"] secondsFromGMT] equals:(720 * 60)];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"WAT"] secondsFromGMT] equals:(60 * 60)];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"WIT"] secondsFromGMT] equals:(420 * 60)];
// Validate IANA name resolution for legacy abbreviations.
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"MDT"] name] equals:@"America/Denver"];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"MSK"] name] equals:@"Europe/Moscow"];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"NZDT"] name] equals:@"Pacific/Auckland"];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"NZST"] name] equals:@"Pacific/Auckland"];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"WAT"] name] equals:@"Africa/Lagos"];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"WIT"] name] equals:@"Asia/Jakarta"];
// Evaluate against fixed dates to prevent seasonal failures during CI runs.
var janDate = [[CPDate alloc] initWithString:@"2026-01-15 12:00:00 +0000"];
var julDate = [[CPDate alloc] initWithString:@"2026-07-15 12:00:00 +0000"];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"MDT"] secondsFromGMTForDate:julDate] equals:(-360 * 60)];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"MSK"] secondsFromGMTForDate:janDate] equals:(180 * 60)];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"NZDT"] secondsFromGMTForDate:janDate] equals:(780 * 60)];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"NZST"] secondsFromGMTForDate:julDate] equals:(720 * 60)];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"WAT"] secondsFromGMTForDate:janDate] equals:(60 * 60)];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"WIT"] secondsFromGMTForDate:janDate] equals:(420 * 60)];
}
// MSD ("Moscow Summer Time") has no correct current value: Russia abolished
// DST in 2014, so there is no live distinct summer offset for Moscow to
// assign. This pins the current, intentionally-unfixed value, so any future
// edit to it happens deliberately alongside CPTimeZone.j's comment and the
// tracked Intl redesign, not as a silent, unrelated drift.
- (void)testMSDKnownStaleValue
- (void)testMSKCorrectCurrentValue
{
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"MSD"] secondsFromGMT] equals:(240 * 60)];
// MSD/MSK resolve to Europe/Moscow.
// Russia abolished DST in 2014, so there is no distinct summer offset.
// The current permanent offset is +03:00 (10800 seconds).
var timeZone = [CPTimeZone timeZoneWithAbbreviation:@"MSD"];
[self assert:[timeZone secondsFromGMT] equals:(180 * 60)];
}
// knownTimeZoneNames should prefer the runtime's own IANA database over the
// old 48-city hardcoded list, on any engine that exposes
// Intl.supportedValuesOf. Skips itself where that API is unavailable, rather
// than asserting a specific engine capability.
- (void)testKnownTimeZoneNamesUsesIntlWhenAvailable
{
if (typeof Intl === "undefined" || typeof Intl.supportedValuesOf !== "function")
@@ -423,38 +416,22 @@
var names = [CPTimeZone knownTimeZoneNames];
[self assertTrue:([names count] > 48)
message:"knownTimeZoneNames should use Intl.supportedValuesOf when available, not the small hardcoded fallback list"];
message:"knownTimeZoneNames should use Intl.supportedValuesOf when available, not the small hardcoded fallback list"];
[self assertTrue:[names containsObject:@"Europe/Berlin"]
message:"a zone absent from the old hardcoded list should be present via Intl"];
message:"a zone absent from the old hardcoded list should be present via Intl"];
}
// Disabled: the "fr" entry in localizedName is an empty dictionary, so any
// lookup for a French locale currently returns nil regardless of style. This
// is the same static, English-only table design as before the Intl
// migration; folding it in was deferred because CPTimeZoneNameStyleStandard/
// DaylightSaving need to force a specific state independent of the current
// date, which Intl.formatToParts(date) alone can't do for an arbitrary date.
// Tracked for the larger IANA-identity redesign, not fixed here.
- (void)disabled_testLocalizedNameFrenchLocale
{
var frenchLocale = [[CPLocale alloc] initWithLocaleIdentifier:@"fr_FR"],
timeZone = [CPTimeZone timeZoneWithAbbreviation:@"PST"];
timeZone = [CPTimeZone timeZoneWithAbbreviation:@"PST"];
[self assert:[timeZone localizedName:CPTimeZoneNameStyleStandard locale:frenchLocale] equals:@"Heure normale du Pacifique"];
}
// Disabled: timeZoneForSecondsFromGMT: does a linear scan of
// timeDifferenceFromUTC and returns the first matching key, with no defined
// tie-break when more than one abbreviation shares an offset. MDT and CST
// both now correctly resolve to -360; asking for that offset silently
// returns whichever CPDictionary happens to enumerate first, not a
// documented choice. The idealized contract is that an inherently ambiguous
// query should not silently guess one answer. No fix exists for this within
// the current flat abbreviation-to-offset table design.
- (void)disabled_testTimeZoneForSecondsFromGMTOffsetCollisionIsAmbiguous
{
var timeZone = [CPTimeZone timeZoneForSecondsFromGMT:(-360 * 60)];
[self assert:timeZone equals:nil];
}