Files
cappuccino/Foundation/CPDateFormatter.j
T

2196 lines
67 KiB
Plaintext

/*
* CPDateFormatter.j
* Foundation
*
* Created by Alexander Ljungberg.
* Copyright 2012, SlevenBits Ltd.
*
* 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 "CPArray.j"
@import "CPDate.j"
@import "CPString.j"
@import "CPFormatter.j"
@import "CPTimeZone.j"
@import "CPLocale.j"
@class CPNull
@class CPDictionary
@global CPLocaleLanguageCode
@global CPLocaleCountryCode
@typedef CPDateFormatterStyle
CPDateFormatterNoStyle = 0;
CPDateFormatterShortStyle = 1;
CPDateFormatterMediumStyle = 2;
CPDateFormatterLongStyle = 3;
CPDateFormatterFullStyle = 4;
@typedef CPDateFormatterBehavior
CPDateFormatterBehaviorDefault = 0;
CPDateFormatterBehavior10_0 = 1000;
CPDateFormatterBehavior10_4 = 1040;
var defaultDateFormatterBehavior = CPDateFormatterBehavior10_4,
relativeDateFormating,
patternStringTokens;
var _separatorsCharacterSet = nil;
/*!
@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.
*/
@implementation CPDateFormatter : CPFormatter
{
BOOL _allowNaturalLanguage @accessors(property=allowNaturalLanguage, readonly);
BOOL _doesRelativeDateFormatting @accessors(property=doesRelativeDateFormatting);
CPDate _defaultDate @accessors(property=defaultDate);
CPDate _twoDigitStartDate @accessors(property=twoDigitStartDate);
CPDateFormatterBehavior _formatterBehavior @accessors(property=formatterBehavior);
CPDateFormatterStyle _dateStyle @accessors(property=dateStyle);
CPDateFormatterStyle _timeStyle @accessors(property=timeStyle);
CPLocale _locale @accessors(property=locale);
CPString _AMSymbol @accessors(property=AMSymbol);
CPString _dateFormat @accessors(property=dateFormat);
CPString _PMSymbol @accessors(property=PMSymbol);
CPTimeZone _timeZone @accessors(property=timeZone);
CPDictionary _symbols;
}
+ (void)initialize
{
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" : []
};
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
*/
+ (CPString)localizedStringFromDate:(CPDate)date dateStyle:(CPDateFormatterStyle)dateStyle timeStyle:(CPDateFormatterStyle)timeStyle
{
var formatter = [[CPDateFormatter alloc] init];
[formatter setFormatterBehavior:CPDateFormatterBehavior10_4];
[formatter setDateStyle:dateStyle];
[formatter setTimeStyle:timeStyle];
return [formatter stringForObjectValue:date];
}
/*! 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
*/
+ (CPString)dateFormatFromTemplate:(CPString)template options:(CPUInteger)opts locale:(CPLocale)locale
{
// TODO : check every template from cocoa and return a good format (have fun ^^)
}
/*! Return the defaultFormatterBehavior
@return a CPDateFormatterBehavior
*/
+ (CPDateFormatterBehavior)defaultFormatterBehavior
{
return defaultDateFormatterBehavior;
}
/*! Set the defaultFormatterBehavior
@param behavior
*/
+ (void)setDefaultFormatterBehavior:(CPDateFormatterBehavior)behavior
{
defaultDateFormatterBehavior = behavior;
}
+ (CPCharacterSet)_separatorsCharacterSet
{
if (_separatorsCharacterSet == nil)
_separatorsCharacterSet = [CPCharacterSet characterSetWithCharactersInString:@" ,:/-."];
return _separatorsCharacterSet;
}
/*! Init a dateFormatter
@return a new CPDateFormatter
*/
- (id)init
{
if (self = [super init])
{
_dateStyle = nil;
_timeStyle = nil;
[self _init];
}
return self;
}
/*! Init a dateFormatter with a format and the naturalLanguage
@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])
{
_dateFormat = format;
_allowNaturalLanguage = flag;
}
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"];
_symbols = @{
@"root" : @{
@"AMSymbol" : AMSymbol,
@"PMSymbol" : PMSymbol,
@"weekdaySymbols" : weekdaySymbols,
@"shortWeekdaySymbols" : shortWeekdaySymbols,
@"veryShortWeekdaySymbols" : veryShortWeekdaySymbols,
@"standaloneWeekdaySymbols" : standaloneWeekdaySymbols,
@"shortStandaloneWeekdaySymbols" : shortStandaloneWeekdaySymbols,
@"veryShortStandaloneWeekdaySymbols" : veryShortStandaloneWeekdaySymbols,
@"monthSymbols" : monthSymbols,
@"shortMonthSymbols" : shortMonthSymbols,
@"veryShortMonthSymbols" : veryShortMonthSymbols,
@"standaloneMonthSymbols" : standaloneMonthSymbols,
@"shortStandaloneMonthSymbols" : shortStandaloneMonthSymbols,
@"veryShortStandaloneMonthSymbols" : veryShortStandaloneMonthSymbols,
@"quarterSymbols" : quarterSymbols,
@"shortQuarterSymbols" : shortQuarterSymbols,
@"standaloneQuarterSymbols" : standaloneQuarterSymbols,
@"shortStandaloneQuarterSymbols" : shortStandaloneQuarterSymbols
}
};
_timeZone = [CPTimeZone systemTimeZone];
_twoDigitStartDate = [[CPDate alloc] initWithString:@"1950-01-01 00:00:00 +0000"];
_locale = [CPLocale currentLocale];
}
#pragma mark -
#pragma mark Setter Getter Helper
/*! Return symbols for the given language code
*/
- (CPDictionary)symbolsForLanguageCode:(CPString)languageCode
{
var languageSymbols = [_symbols valueForKey:languageCode];
if (!languageSymbols)
{
languageSymbols = [self symbolsForLanguageCode:@"root"];
[self setSymbols:languageSymbols forLanguageCode:languageCode];
}
return languageSymbols;
}
/*! 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];
if (!symbol)
{
symbol = [self symbolForKey:aKey languageCode:@"root"];
[self setSymbol:symbol forKey:aKey languageCode:languageCode];
}
return symbol;
}
/*! 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];
[languageSymbols setValue:aSymbol forKey:aKey];
}
#pragma mark -
#pragma 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]];
}
#pragma mark -
#pragma 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
*/
- (CPString)stringFromDate:(CPDate)aDate
{
var format,
relativeWord,
result;
if (!aDate)
return;
aDate = [aDate copy];
[aDate _dateWithTimeZone:_timeZone];
if (_dateFormat)
return [self _stringFromDate:aDate format:_dateFormat];
switch (_dateStyle)
{
case CPDateFormatterNoStyle:
format = @"";
break;
case CPDateFormatterShortStyle:
if ([self _isAmericanFormat])
format = @"M/d/yy";
else
{
if ([_locale objectForKey:CPLocaleLanguageCode] === 'de')
format = @"dd.MM.yy";
else
format = @"dd/MM/yy";
}
break;
case CPDateFormatterMediumStyle:
if ([self _isAmericanFormat])
format = @"MMM d, Y";
else
format = @"d MMM Y";
break;
case CPDateFormatterLongStyle:
if ([self _isAmericanFormat])
format = @"MMMM d, Y";
else
format = @"d MMMM Y";
break;
case CPDateFormatterFullStyle:
if ([self _isAmericanFormat])
format = @"EEEE, MMMM d, Y";
else
format = @"EEEE d MMMM Y";
break;
default:
format = @"";
}
if ([self doesRelativeDateFormatting])
{
var language = [_locale objectForKey:CPLocaleLanguageCode],
relativeWords = [relativeDateFormating valueForKey:language];
for (var i = 1; i < [relativeWords count]; i = i + 2)
{
var date = [CPDate date];
[date _dateWithTimeZone:_timeZone];
date.setHours(12);
date.setMinutes(0);
date.setSeconds(0);
date.setDate([relativeWords objectAtIndex:i] + date.getDate());
if (date.getDate() == aDate.getDate() && date.getMonth() == aDate.getMonth() && date.getFullYear() == aDate.getFullYear())
{
relativeWord = [relativeWords objectAtIndex:(i - 1)];
format = @"";
break;
}
}
}
if ((relativeWord || format.length) && _timeStyle != CPDateFormatterNoStyle)
format += @" ";
switch (_timeStyle)
{
case CPDateFormatterNoStyle:
format += @"";
break;
case CPDateFormatterShortStyle:
if ([self _isEnglishFormat])
format += @"h:mm a";
else
format += @"H:mm";
break;
case CPDateFormatterMediumStyle:
if ([self _isEnglishFormat])
format += @"h:mm:ss a";
else
format += @"H:mm:ss";
break;
case CPDateFormatterLongStyle:
if ([self _isEnglishFormat])
format += @"h:mm:ss a z";
else
format += @"H:mm:ss z";
break;
case CPDateFormatterFullStyle:
if ([self _isEnglishFormat])
format += @"h:mm:ss a zzzz";
else
format += @"h:mm:ss zzzz";
break;
default:
format += @"";
}
result = [self _stringFromDate:aDate format:format];
if (relativeWord)
result = relativeWord + result;
return result;
}
/*! 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
*/
- (CPString)stringForObjectValue:(id)anObject
{
if ([anObject isKindOfClass:[CPDate class]])
return [self stringFromDate:anObject];
else
return nil;
}
/*! 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
*/
- (CPString)_stringFromDate:(CPDate)aDate format:(CPString)aFormat
{
var length = [aFormat length],
currentToken = [CPString new],
isTextToken = NO,
result = [CPString new];
for (var i = 0; i < length; i++)
{
var character = [aFormat characterAtIndex:i];
if (isTextToken)
{
if ([character isEqualToString:@"'"])
{
isTextToken = NO;
result += currentToken;
currentToken = [CPString new];
}
else
{
currentToken += character;
}
continue;
}
if ([character isEqualToString:@"'"])
{
if (!isTextToken)
{
isTextToken = YES;
result += currentToken;
currentToken = [CPString new];
}
continue;
}
if ([[CPDateFormatter _separatorsCharacterSet] characterIsMember:character])
{
result += [self _stringFromToken:currentToken date:aDate];
result += character;
currentToken = [CPString new];
}
else
{
if ([currentToken length] && ![[currentToken characterAtIndex:0] isEqualToString:character])
{
result += [self _stringFromToken:currentToken date:aDate];
currentToken = [CPString new];
}
currentToken += character;
if (i == (length - 1))
result += [self _stringFromToken:currentToken date:aDate];
}
}
return result;
}
/*! Return a string representation of the given token and date
@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;
switch (character)
{
case @"G":
// TODO
CPLog.warn(@"Token not yet implemented " + aToken);
return [CPString new];
case @"y":
var currentLength = [[CPString stringWithFormat:@"%i", aDate.getFullYear()] length];
return [self _stringValueForValue:aDate.getFullYear() length:(length == 2)?length:currentLength];
case @"Y":
var currentLength = [[CPString stringWithFormat:@"%i", aDate.getFullYear()] length];
return [self _stringValueForValue:aDate.getFullYear() length:(length == 2)?length:currentLength];
case @"u":
// TODO
CPLog.warn(@"Token not yet implemented " + aToken);
return [CPString new];
case @"U":
// TODO
CPLog.warn(@"Token not yet implemented " + aToken);
return [CPString new];
case @"Q":
var quarter = 1;
if (aDate.getMonth() < 6 && aDate.getMonth() > 2)
quarter = 2;
if (aDate.getMonth() > 5 && aDate.getMonth() < 9)
quarter = 3;
if (aDate.getMonth() >= 9)
quarter = 4;
if (length <= 2)
return [self _stringValueForValue:quarter length:MIN(2,length)];
if (length == 3)
return [[self shortQuarterSymbols] objectAtIndex:(quarter - 1)];
if (length >= 4)
return [[self quarterSymbols] objectAtIndex:(quarter - 1)];
case @"q":
var quarter = 1;
if (aDate.getMonth() < 6 && aDate.getMonth() > 2)
quarter = 2;
if (aDate.getMonth() > 5 && aDate.getMonth() < 9)
quarter = 3;
if (aDate.getMonth() >= 9)
quarter = 4;
if (length <= 2)
return [self _stringValueForValue:quarter length:MIN(2,length)];
if (length == 3)
return [[self shortStandaloneQuarterSymbols] objectAtIndex:(quarter - 1)];
if (length >= 4)
return [[self standaloneQuarterSymbols] objectAtIndex:(quarter - 1)];
case @"M":
var currentLength = [[CPString stringWithFormat:@"%i", aDate.getMonth() + 1] length];
if (length <= 2)
return [self _stringValueForValue:(aDate.getMonth() + 1) length:MAX(currentLength,length)];
if (length == 3)
return [[self shortMonthSymbols] objectAtIndex:aDate.getMonth()];
if (length == 4)
return [[self monthSymbols] objectAtIndex:aDate.getMonth()];
if (length >= 5)
return [[self veryShortMonthSymbols] objectAtIndex:aDate.getMonth()];
case @"L":
var currentLength = [[CPString stringWithFormat:@"%i", aDate.getMonth() + 1] length];
if (length <= 2)
return [self _stringValueForValue:(aDate.getMonth() + 1) length:MAX(currentLength,length)];
if (length == 3)
return [[self shortStandaloneMonthSymbols] objectAtIndex:aDate.getMonth()];
if (length == 4)
return [[self standaloneMonthSymbols] objectAtIndex:aDate.getMonth()];
if (length >= 5)
return [[self veryShortStandaloneMonthSymbols] objectAtIndex:aDate.getMonth()];
case @"I":
// Deprecated
CPLog.warn(@"Depreacted - Token not yet implemented " + aToken);
return [CPString new];
case @"w":
var d = [aDate copy];
d.setHours(0, 0, 0);
d.setDate(d.getDate() + 4 - (d.getDay() || 7));
var yearStart = new Date(d.getFullYear(), 0, 1),
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);
return [self _stringValueForValue:weekOfMonth length:1];
case @"d":
var currentLength = [[CPString stringWithFormat:@"%i", aDate.getDate()] length];
return [self _stringValueForValue:aDate.getDate() length:MAX(length, currentLength)];
case @"D":
var oneJan = new Date(aDate.getFullYear(), 0, 1),
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();
if (day > 7 && day < 15)
dayOfWeek = 2;
if (day > 14 && day < 22)
dayOfWeek = 3;
if (day > 21 && day < 29)
dayOfWeek = 4;
if (day > 28)
dayOfWeek = 5;
return [self _stringValueForValue:dayOfWeek length:1];
case @"g":
CPLog.warn(@"Token not yet implemented " + aToken);
return [CPString new];
case @"E":
var day = aDate.getDay();
if (length <= 3)
return [[self shortWeekdaySymbols] objectAtIndex:day];
if (length == 4)
return [[self weekdaySymbols] objectAtIndex:day];
if (length >= 5)
return [[self veryShortWeekdaySymbols] objectAtIndex:day];
case @"e":
var day = aDate.getDay();
if (length <= 2)
return [self _stringValueForValue:(day + 1) length:MIN(2, length)];
if (length == 3)
return [[self shortWeekdaySymbols] objectAtIndex:day];
if (length == 4)
return [[self weekdaySymbols] objectAtIndex:day];
if (length >= 5)
return [[self veryShortWeekdaySymbols] objectAtIndex:day];
case @"c":
var day = aDate.getDay();
if (length <= 2)
return [self _stringValueForValue:(day + 1) length:aDate.getDay().toString().length];
if (length == 3)
return [[self shortStandaloneWeekdaySymbols] objectAtIndex:day];
if (length == 4)
return [[self standaloneWeekdaySymbols] objectAtIndex:day];
if (length >= 5)
return [[self veryShortStandaloneWeekdaySymbols] objectAtIndex:day];
case @"a":
if (aDate.getHours() > 11)
return [self PMSymbol];
else
return [self AMSymbol];
case @"h":
var hours = aDate.getHours();
if ([self _isAmericanFormat] || [self _isEnglishFormat])
{
if (hours == 0)
hours = 12;
else if (hours > 12)
hours = hours - 12;
}
var currentLength = [[CPString stringWithFormat:@"%i", hours] length];
return [self _stringValueForValue:hours length:MAX(currentLength, MIN(2, length))];
case @"H":
var currentLength = [[CPString stringWithFormat:@"%i", aDate.getHours()] length];
return [self _stringValueForValue:aDate.getHours() length:MAX(currentLength, MIN(2, length))];
case @"K":
var hours = aDate.getHours();
if (hours > 12)
hours -= 12;
var currentLength = [[CPString stringWithFormat:@"%i", hours] length];
return [self _stringValueForValue:hours length:MAX(currentLength, MIN(2, length))];
case @"k":
var hours = aDate.getHours();
if (aDate.getHours() == 0)
hours = 24;
var currentLength = [[CPString stringWithFormat:@"%i", hours] length];
return [self _stringValueForValue:hours length:MAX(currentLength, MIN(2, length))];
case @"j":
CPLog.warn(@"Token not yet implemented " + aToken);
return [CPString new];
case @"m":
var currentLength = [[CPString stringWithFormat:@"%i", aDate.getMinutes()] length];
return [self _stringValueForValue:aDate.getMinutes() length:MAX(currentLength, MIN(2, length))];
case @"s":
var currentLength = [[CPString stringWithFormat:@"%i", aDate.getMinutes()] length];
return [self _stringValueForValue:aDate.getSeconds() length:MIN(2, length)];
case @"S":
return [self _stringValueForValue:aDate.getMilliseconds() length:length];
case @"A":
var value = aDate.getHours() * 60 * 60 * 1000 + aDate.getMinutes() * 60 * 1000 + aDate.getSeconds() * 1000 + aDate.getMilliseconds();
return [self _stringValueForValue:value length:value.toString().length];
case @"z":
if (length <= 3)
return [timeZone localizedName:CPTimeZoneNameStyleShortDaylightSaving locale:_locale];
else
return [timeZone localizedName:CPTimeZoneNameStyleDaylightSaving locale:_locale];
case @"Z":
var seconds = [timeZone secondsFromGMT],
minutes = seconds / 60,
hours = minutes / 60,
result,
diffMinutes = (hours - parseInt(hours)) * 100 * 60 / 100;
if (length <= 3)
{
result = diffMinutes.toString();
while ([result length] < 2)
result = @"0" + result;
result = ABS(parseInt(hours)) + result;
while ([result length] < 4)
result = @"0" + result;
if (seconds > 0)
result = @"+" + result;
else
result = @"-" + result;
return result;
}
else if (length == 4)
{
result = diffMinutes.toString();
while ([result length] < 2)
result = @"0" + result;
result = @":" + result;
result = ABS(parseInt(hours)) + result;
while ([result length] < 5)
result = @"0" + result;
if (seconds > 0)
result = @"+" + result;
else
result = @"-" + result;
return @"GMT" + result;
}
else
{
result = diffMinutes.toString();
while ([result length] < 2)
result = @"0" + result;
result = @":" + result;
result = ABS(parseInt(hours)) + result;
while ([result length] < 5)
result = @"0" + result;
if (seconds > 0)
result = @"+" + result;
else
result = @"-" + result;
return result;
}
case @"v":
if (length == 1)
return [timeZone localizedName:CPTimeZoneNameStyleShortGeneric locale:_locale];
else if (length == 4)
return [timeZone localizedName:CPTimeZoneNameStyleGeneric locale:_locale];
return @" ";
case @"V":
if (length == 1)
{
return [timeZone localizedName:CPTimeZoneNameStyleShortDaylightSaving locale:_locale];
}
else if (length == 4)
{
CPLog.warn(@"No pattern found for " + aToken);
return @"";
}
return @" ";
default:
CPLog.warn(@"No pattern found for " + aToken);
return aToken;
}
return [CPString new];
}
#pragma mark -
#pragma 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
*/
- (CPDate)dateFromString:(CPString)aString
{
var format;
if (_dateFormat != nil)
return [self _dateFromString:aString format:_dateFormat];
switch (_dateStyle)
{
case CPDateFormatterNoStyle:
format = @"";
break;
case CPDateFormatterShortStyle:
if ([self _isAmericanFormat])
format = @"M/d/yy";
else
format = @"dd/MM/yy";
break;
case CPDateFormatterMediumStyle:
if ([self _isAmericanFormat])
format = @"MMM d, Y";
else
format = @"d MMM Y";
break;
case CPDateFormatterLongStyle:
if ([self _isAmericanFormat])
format = @"MMMM d, Y";
else
format = @"d MMMM Y";
break;
case CPDateFormatterFullStyle:
if ([self _isAmericanFormat])
format = @"EEEE, MMMM d, Y";
else
format = @"EEEE d MMMM Y";
break;
default:
format = @"";
}
switch (_timeStyle)
{
case CPDateFormatterNoStyle:
format += @"";
break;
case CPDateFormatterShortStyle:
if ([self _isEnglishFormat])
format += @" h:mm a";
else
format += @" H:mm";
break;
case CPDateFormatterMediumStyle:
if ([self _isEnglishFormat])
format += @" h:mm:ss a";
else
format += @" H:mm:ss";
break;
case CPDateFormatterLongStyle:
if ([self _isEnglishFormat])
format += @" h:mm:ss a z";
else
format += @" H:mm:ss z";
break;
case CPDateFormatterFullStyle:
if ([self _isEnglishFormat])
format += @" h:mm:ss a zzzz";
else
format += @" h:mm:ss zzzz";
break;
default:
format += @"";
}
return [self _dateFromString:aString format:format];
}
/*! 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
*/
- (BOOL)getObjectValue:(idRef)anObject forString:(CPString)aString errorDescription:(CPStringRef)anError
{
var value = [self dateFromString:aString];
@deref(anObject) = value;
if (!value)
{
if (anError)
@deref(anError) = @"The value \"" + aString + "\" is invalid.";
return NO;
}
return YES;
}
/*! Return a date representation of the given string and format
@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.
if (!aString)
return [[CPDate alloc] initWithTimeIntervalSinceReferenceDate:-31622400];
if (aFormat == nil)
return nil;
var currentToken = [CPString new],
isTextToken = NO,
tokens = [CPArray array],
dateComponents = [CPArray array],
patternTokens = [CPArray array];
for (var i = 0; i < [aFormat length]; i++)
{
var character = [aFormat characterAtIndex:i];
if (isTextToken)
{
if ([character isEqualToString:@"'"])
currentToken = [CPString new];
continue;
}
if ([character isEqualToString:@"'"])
{
if (!isTextToken)
isTextToken = YES;
continue;
}
if ([character isEqualToString:@","] || [character isEqualToString:@":"] || [character isEqualToString:@"/"] || [character isEqualToString:@"-"] || [character isEqualToString:@" "])
{
[tokens addObject:currentToken];
if ([patternStringTokens containsObject:currentToken])
[patternTokens addObject:[tokens count] - 1];
currentToken = [CPString new];
}
else
{
if ([currentToken length] && ![[currentToken characterAtIndex:0] isEqualToString:character])
{
[tokens addObject:currentToken];
if ([patternStringTokens containsObject:currentToken])
[patternTokens addObject:[tokens count] - 1];
currentToken = [CPString new];
}
currentToken += character;
if (i == ([aFormat length] - 1))
{
[tokens addObject:currentToken];
if ([patternStringTokens containsObject:currentToken])
[patternTokens addObject:[tokens count] - 1];
}
}
}
isTextToken = NO;
currentToken = [CPString new];
var currentIndexSpecialPattern = 0;
if ([patternTokens count] == 0)
[patternTokens addObject:CPNotFound];
for (var i = 0; i < [aString length]; i++)
{
var character = [aString characterAtIndex:i];
if (isTextToken)
{
if ([character isEqualToString:@"'"])
currentToken = [CPString new];
continue;
}
if ([character isEqualToString:@"'"])
{
if (!isTextToken)
isTextToken = YES;
continue;
}
// Need to do this to check if the word match with the token. We can get some words with space...
if ([dateComponents count] == [patternTokens objectAtIndex:currentIndexSpecialPattern])
{
var j = [self _lastIndexMatchedString:aString token:[tokens objectAtIndex:[dateComponents count]] index:i];
if (j == CPNotFound)
return nil;
currentIndexSpecialPattern++;
[dateComponents addObject:[aString substringWithRange:CPMakeRange(i, (j - i))]];
i = j;
continue;
}
if ([character isEqualToString:@","] || [character isEqualToString:@":"] || [character isEqualToString:@"/"] || [character isEqualToString:@"-"] || [character isEqualToString:@" "])
{
[dateComponents addObject:currentToken];
currentToken = [CPString new];
}
else
{
currentToken += character;
if (i == ([aString length] - 1))
[dateComponents addObject:currentToken];
}
}
if ([dateComponents count] != [tokens count])
return nil;
return [self _dateFromTokens:tokens dateComponents:dateComponents];
}
- (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;
for (var i = 0; i < [tokens count]; i++)
{
var token = [tokens objectAtIndex:i],
dateComponent = [dateComponents objectAtIndex:i],
character = [token characterAtIndex:0],
length = [token length];
switch (character)
{
case @"G":
// TODO
CPLog.warn(@"Token not yet implemented " + token);
break;
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;
if (length == 2 && dateComponent.length == 2)
{
if ((u + d * 10) >= parseInt(dateComponent))
dateArray[0] = (c + 1) * 100 + m * 1000 + parseInt(dateComponent);
else
dateArray[0] = c * 100 + m * 1000 + parseInt(dateComponent);
}
else
{
dateArray[0] = parseInt(dateComponent);
}
break;
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;
if (length == 2 && dateComponent.length == 2)
{
if ((u + d * 10) >= parseInt(dateComponent))
dateArray[0] = (c + 1) * 100 + m * 1000 + parseInt(dateComponent);
else
dateArray[0] = c * 100 + m * 1000 + parseInt(dateComponent);
}
else
{
dateArray[0] = parseInt(dateComponent);
}
break;
case @"u":
// TODO
CPLog.warn(@"Token not yet implemented " + token);
break;
case @"U":
// TODO
CPLog.warn(@"Token not yet implemented " + token);
break;
case @"Q":
var month;
if (length <= 2)
month = (parseInt(dateComponent) - 1) * 3;
if (length == 3)
{
if (![[self shortQuarterSymbols] containsObject:dateComponent])
return nil;
month = [[self shortQuarterSymbols] indexOfObject:dateComponent] * 3;
}
if (length >= 4)
{
if (![[self quarterSymbols] containsObject:dateComponent])
return nil;
month = [[self quarterSymbols] indexOfObject:dateComponent] * 3;
}
if (month > 11)
return nil;
dateArray[1] = month + 1;
break;
case @"q":
var month;
if (length <= 2)
month = (parseInt(dateComponent) - 1) * 3;
if (length == 3)
{
if (![[self shortQuarterSymbols] containsObject:dateComponent])
return nil;
month = [[self shortQuarterSymbols] indexOfObject:dateComponent] * 3;
}
if (length >= 4)
{
if (![[self quarterSymbols] containsObject:dateComponent])
return nil;
month = [[self quarterSymbols] indexOfObject:dateComponent] * 3;
}
if (month > 11)
return nil;
dateArray[1] = month + 1;
break;
case @"M":
var month;
if (length <= 2)
month = parseInt(dateComponent);
if (length == 3)
{
if (![[self shortMonthSymbols] containsObject:dateComponent])
return nil;
month = [[self shortMonthSymbols] indexOfObject:dateComponent] + 1;
}
if (length == 4)
{
if (![[self monthSymbols] containsObject:dateComponent])
return nil;
month = [[self monthSymbols] indexOfObject:dateComponent] + 1;
}
if (month > 12 || length >= 5)
return nil;
dateArray[1] = month;
break;
case @"L":
var month;
if (length <= 2)
month = parseInt(dateComponent);
if (length == 3)
{
if (![[self shortStandaloneMonthSymbols] containsObject:dateComponent])
return nil;
month = [[self shortStandaloneMonthSymbols] indexOfObject:dateComponent] + 1;
}
if (length == 4)
{
if (![[self standaloneMonthSymbols] containsObject:dateComponent])
return nil;
month = [[self standaloneMonthSymbols] indexOfObject:dateComponent] + 1;
}
if (month > 12 || length >= 5)
return nil;
dateArray[1] = month;
break;
case @"I":
// Deprecated
CPLog.warn(@"Depreacted - Token not yet implemented " + token);
break;
case @"w":
if (dateComponent > 52)
return nil;
weekOfYear = dateComponent;
break;
case @"W":
if (dateComponent > 52)
return nil;
weekOfMonth = dateComponent;
break;
case @"d":
dateArray[2] = parseInt(dateComponent);
break;
case @"D":
if (isNaN(parseInt(dateComponent)) || parseInt(dateComponent) > 345)
return nil;
dayOfYear = parseInt(dateComponent);
break;
case @"F":
if (isNaN(parseInt(dateComponent)) || parseInt(dateComponent) > 5 || parseInt(dateComponent) == 0)
return nil;
if (parseInt(dateComponent) == 1)
dateArray[2] = 1;
if (parseInt(dateComponent) == 2)
dateArray[2] = 8;
if (parseInt(dateComponent) == 3)
dateArray[2] = 15;
if (parseInt(dateComponent) == 4)
dateArray[2] = 22;
if (parseInt(dateComponent) == 5)
dateArray[2] = 29;
break;
case @"g":
CPLog.warn(@"Token not yet implemented " + token);
break;
case @"E":
if (length <= 3)
dayIndexInWeek = [[self shortWeekdaySymbols] indexOfObject:dateComponent];
if (length == 4)
dayIndexInWeek = [[self weekdaySymbols] indexOfObject:dateComponent];
if (dayIndexInWeek == CPNotFound || length >= 5)
return nil;
break;
case @"e":
if (length <= 2 && isNaN(parseInt(dateComponent)))
return nil;
if (length <= 2)
dayIndexInWeek = parseInt(dateComponent);
if (length == 3)
dayIndexInWeek = [[self shortWeekdaySymbols] indexOfObject:dateComponent];
if (length == 4)
dayIndexInWeek = [[self weekdaySymbols] indexOfObject:dateComponent];
if (dayIndexInWeek == CPNotFound || length >= 5)
return nil;
break;
case @"c":
if (length <= 2 && isNaN(parseInt(dateComponent)))
return nil;
if (length <= 2)
dayIndexInWeek = dateComponent;
if (length == 3)
dayIndexInWeek = [[self shortStandaloneWeekdaySymbols] indexOfObject:dateComponent];
if (length == 4)
dayIndexInWeek = [[self standaloneWeekdaySymbols] indexOfObject:dateComponent];
if (length == 5)
dayIndexInWeek = [[self veryShortStandaloneWeekdaySymbols] indexOfObject:dateComponent];
if (dayIndexInWeek == CPNotFound || length >= 5)
return nil;
break;
case @"a":
if (![dateComponent isEqualToString:[self PMSymbol]] && ![dateComponent isEqualToString:[self AMSymbol]])
return nil;
if ([dateComponent isEqualToString:[self PMSymbol]])
isPM = YES;
break;
case @"h":
if (parseInt(dateComponent) < 0 || parseInt(dateComponent) > 12)
return nil;
dateArray[3] = parseInt(dateComponent);
break;
case @"H":
if (parseInt(dateComponent) < 0 || parseInt(dateComponent) > 23)
return nil;
dateArray[3] = parseInt(dateComponent);
break;
case @"K":
if (parseInt(dateComponent) < 0 || parseInt(dateComponent) > 11)
return nil;
dateArray[3] = parseInt(dateComponent);
break;
case @"k":
if (parseInt(dateComponent) < 0 || parseInt(dateComponent) > 12)
return nil;
dateArray[3] = parseInt(dateComponent);
break;
case @"j":
CPLog.warn(@"Token not yet implemented " + token);
break;
case @"m":
var minutes = parseInt(dateComponent);
if (minutes > 59)
return nil;
dateArray[4] = minutes;
break;
case @"s":
var seconds = parseInt(dateComponent);
if (seconds > 59)
return nil;
dateArray[5] = seconds;
break;
case @"S":
if (isNaN(parseInt(dateComponent)))
return nil;
break;
case @"A":
if (isNaN(parseInt(dateComponent)))
return nil;
var millisecondsInDay = parseInt(dateComponent),
tmpDate = new Date();
tmpDate.setHours(0);
tmpDate.setMinutes(0);
tmpDate.setSeconds(0);
tmpDate.setMilliseconds(0);
tmpDate.setMilliseconds(millisecondsInDay);
dateArray[3] = tmpDate.getHours();
dateArray[4] = tmpDate.getMinutes();
dateArray[5] = tmpDate.getSeconds();
break;
case @"z":
if (length < 4)
timeZoneseconds = [self _secondsFromTimeZoneString:dateComponent style:CPTimeZoneNameStyleShortDaylightSaving];
else
timeZoneseconds = [self _secondsFromTimeZoneString:dateComponent style:CPTimeZoneNameStyleDaylightSaving];
if (!timeZoneseconds)
timeZoneseconds = [self _secondsFromTimeZoneDefaultFormatString:dateComponent];
if (!timeZoneseconds)
return nil;
timeZoneseconds = timeZoneseconds + 60 * 60;
break;
case @"Z":
timeZoneseconds = [self _secondsFromTimeZoneDefaultFormatString:dateComponent];
if (!timeZoneseconds)
return nil;
timeZoneseconds = timeZoneseconds + 60 * 60;
break;
case @"v":
if (length <= 3)
timeZoneseconds = [self _secondsFromTimeZoneString:dateComponent style:CPTimeZoneNameStyleShortGeneric];
else
timeZoneseconds = [self _secondsFromTimeZoneString:dateComponent style:CPTimeZoneNameStyleGeneric];
if (!timeZoneseconds && length == 4)
timeZoneseconds = [self _secondsFromTimeZoneDefaultFormatString:dateComponent];
if (!timeZoneseconds)
return nil;
timeZoneseconds = timeZoneseconds + 60 * 60;
break;
case @"V":
if (length <= 3)
timeZoneseconds = [self _secondsFromTimeZoneString:dateComponent style:CPTimeZoneNameStyleShortStandard];
else
timeZoneseconds = [self _secondsFromTimeZoneString:dateComponent style:CPTimeZoneNameStyleStandard];
if (!timeZoneseconds)
timeZoneseconds = [self _secondsFromTimeZoneDefaultFormatString:dateComponent];
if (!timeZoneseconds)
return nil;
timeZoneseconds = timeZoneseconds + 60 * 60;
break;
default:
CPLog.warn(@"No pattern found for " + token);
return nil;
}
}
// Make the calcul day of the year
if (dayOfYear)
{
var tmpDate = new Date();
tmpDate.setFullYear(dateArray[0]);
tmpDate.setMonth(0);
tmpDate.setDate(dayOfYear);
dateArray[1] = tmpDate.getMonth() + 1;
dateArray[2] = tmpDate.getDate();
}
if (weekOfMonth)
dateArray[2] = (weekOfMonth - 1) * 7 + 1;
if (weekOfYear)
{
var tmpDate = new Date();
tmpDate.setFullYear(dateArray[0]);
tmpDate.setMonth(0);
tmpDate.setDate(1);
while (tmpDate.getDay() != 0)
tmpDate.setDate(tmpDate.getDate() + 1);
tmpDate.setDate(tmpDate.getDate() + (weekOfYear - 1) * 7);
dateArray[1] = tmpDate.getMonth() + 1;
dateArray[2] = tmpDate.getDate() - 1;
}
// Check if the day is possible in the current month
var tmpDate = new Date();
tmpDate.setMonth(dateArray[1] - 1);
tmpDate.setFullYear(dateArray[0]);
if (dateArray[2] <= 0 || dateArray[2] > [tmpDate _daysInMonth])
return nil;
// PM hours
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])))
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);
return dateResult;
}
#pragma mark -
#pragma mark Utils
- (CPString)_stringValueForValue:(id)aValue length:(int)length
{
var string = [CPString stringWithFormat:@"%i", aValue];
if ([string length] == length)
return string;
if ([string length] > length)
return [string substringFromIndex:([string length] - length)];
while ([string length] < length)
string = [CPString stringWithFormat:@"0%s", string];
return string;
}
/*! 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;
if (!result)
return nil;
seconds = result[3] * 60 * 60 + result[5] * 60;
if ([result[2] isEqualToString:@"-"])
seconds = -seconds;
return seconds;
}
/*! 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 [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
*/
- (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));
switch (character)
{
case @"Q":
if (length == 3)
targetedArray = [self shortQuarterSymbols];
if (length >= 4)
targetedArray = [self quarterSymbols];
break;
case @"q":
if (length == 3)
targetedArray = [self shortStandaloneQuarterSymbols];
if (length >= 4)
targetedArray = [self standaloneQuarterSymbols];
break;
case @"M":
if (length == 3)
targetedArray = [self shortMonthSymbols];
if (length == 4)
targetedArray = [self monthSymbols];
if (length >= 5)
targetedArray = [self veryShortMonthSymbols];
break;
case @"L":
if (length == 3)
targetedArray = [self shortStandaloneMonthSymbols];
if (length == 4)
targetedArray = [self standaloneMonthSymbols];
if (length >= 5)
targetedArray = [self veryShortStandaloneMonthSymbols];
break;
case @"E":
if (length <= 3)
targetedArray = [self shortWeekdaySymbols];
if (length == 4)
targetedArray = [self weekdaySymbols];
if (length >= 5)
targetedArray = [self veryShortWeekdaySymbols];
break;
case @"e":
if (length == 3)
targetedArray = [self shortWeekdaySymbols];
if (length == 4)
targetedArray = [self weekdaySymbols];
if (length >= 5)
targetedArray = [self veryShortWeekdaySymbols];
break;
case @"c":
if (length == 3)
targetedArray = [self shortStandaloneWeekdaySymbols];
if (length == 4)
targetedArray = [self standaloneWeekdaySymbols];
if (length >= 5)
targetedArray = [self veryShortStandaloneWeekdaySymbols];
break;
case @"a":
targetedArray = [[self PMSymbol], [self AMSymbol]];
break;
case @"z":
if (length <= 3)
targetedArray = [CPTimeZone _namesForStyle:CPTimeZoneNameStyleShortDaylightSaving locale:_locale];
else
targetedArray = [CPTimeZone _namesForStyle:CPTimeZoneNameStyleDaylightSaving locale:_locale];
if (result)
return anIndex + [result objectAtIndex:0].length;
break;
case @"Z":
if (result)
return anIndex + [result objectAtIndex:0].length;
return CPNotFound;
case @"v":
if (length == 1)
targetedArray = [CPTimeZone _namesForStyle:CPTimeZoneNameStyleShortGeneric locale:_locale];
else if (length == 4)
targetedArray = [CPTimeZone _namesForStyle:CPTimeZoneNameStyleGeneric locale:_locale];
if (result)
return anIndex + [result objectAtIndex:0].length;
break;
case @"V":
if (length == 1)
targetedArray = [CPTimeZone _namesForStyle:CPTimeZoneNameStyleShortStandard locale:_locale];
if (result)
return anIndex + [result objectAtIndex:0].length;
break;
default:
CPLog.warn(@"No pattern found for " + aToken);
return CPNotFound;
}
for (var i = 0; i < [targetedArray count]; i++)
{
var currentObject = [targetedArray objectAtIndex:i],
range = [aString rangeOfString:currentObject];
if (range.length == 0)
continue;
character = [aString characterAtIndex:(anIndex + range.length)];
if ([character isEqualToString:@"'"] || [character isEqualToString:@","] || [character isEqualToString:@":"] || [character isEqualToString:@"/"] || [character isEqualToString:@"-"] || [character isEqualToString:@" "] || [character isEqualToString:@""])
return anIndex + range.length;
}
return CPNotFound;
}
@end
var CPDateFormatterDateStyleKey = @"CPDateFormatterDateStyle",
CPDateFormatterTimeStyleKey = @"CPDateFormatterTimeStyleKey",
CPDateFormatterFormatterBehaviorKey = @"CPDateFormatterFormatterBehaviorKey",
CPDateFormatterDoseRelativeDateFormattingKey = @"CPDateFormatterDoseRelativeDateFormattingKey",
CPDateFormatterDateFormatKey = @"CPDateFormatterDateFormatKey",
CPDateFormatterAllowNaturalLanguageKey = @"CPDateFormatterAllowNaturalLanguageKey",
CPDateFormatterLocaleKey = @"CPDateFormatterLocaleKey";
@implementation CPDateFormatter (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super initWithCoder:aCoder];
if (self)
{
_allowNaturalLanguage = [aCoder decodeBoolForKey:CPDateFormatterAllowNaturalLanguageKey];
_dateFormat = [aCoder decodeObjectForKey:CPDateFormatterDateFormatKey];
_dateStyle = [aCoder decodeIntForKey:CPDateFormatterDateStyleKey];
_doesRelativeDateFormatting = [aCoder decodeBoolForKey:CPDateFormatterDoseRelativeDateFormattingKey];
_formatterBehavior = [aCoder decodeIntForKey:CPDateFormatterFormatterBehaviorKey];
_locale = [aCoder decodeObjectForKey:CPDateFormatterLocaleKey];
_timeStyle = [aCoder decodeIntForKey:CPDateFormatterTimeStyleKey];
}
[self _init];
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeBool:_allowNaturalLanguage forKey:CPDateFormatterAllowNaturalLanguageKey];
[aCoder encodeInt:_dateStyle forKey:CPDateFormatterDateStyleKey];
[aCoder encodeObject:_dateFormat forKey:CPDateFormatterDateFormatKey];
[aCoder encodeBool:_doesRelativeDateFormatting forKey:CPDateFormatterDoseRelativeDateFormattingKey];
[aCoder encodeInt:_formatterBehavior forKey:CPDateFormatterFormatterBehaviorKey];
[aCoder encodeInt:_locale forKey:CPDateFormatterLocaleKey];
[aCoder encodeInt:_timeStyle forKey:CPDateFormatterTimeStyleKey];
}
@end
@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]);
}
@end