API  0.9.8
 All Classes Files Functions Variables Typedefs Macros Groups Pages
CPDateFormatter.j
Go to the documentation of this file.
1 /*
2  * CPDateFormatter.j
3  * Foundation
4  *
5  * Created by Alexander Ljungberg.
6  * Copyright 2012, SlevenBits Ltd.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
26 @global CPLocaleCountryCode
27 
28 @typedef CPDateFormatterStyle
34 
35 @typedef CPDateFormatterBehavior
39 
43 
51 @implementation CPDateFormatter : CPFormatter
52 {
53  BOOL _allowNaturalLanguage;
54  BOOL _doesRelativeDateFormatting;
55  CPDate _defaultDate;
56  CPDate _twoDigitStartDate;
57  CPDateFormatterBehavior _formatterBehavior;
58  CPDateFormatterStyle _dateStyle;
59  CPDateFormatterStyle _timeStyle;
60  CPLocale _locale;
61  CPString _AMSymbol;
62  CPString _dateFormat;
63  CPString _PMSymbol;
64  CPTimeZone _timeZone;
65 
66  CPDictionary _symbols;
67 }
68 
69 
70 + (void)initialize
71 {
72  if (self !== [CPDateFormatter class])
73  return;
74 
76  @"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],
77  @"en" : [@"tomorrow", 1, @"yesterday", -1],
78  @"de" : [],
79  @"es" : []
80  };
81 
82  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"];
83 }
84 
91 + (CPString)localizedStringFromDate:(CPDate)date dateStyle:(CPDateFormatterStyle)dateStyle timeStyle:(CPDateFormatterStyle)timeStyle
92 {
93  var formatter = [[CPDateFormatter alloc] init];
94 
95  [formatter setFormatterBehavior:CPDateFormatterBehavior10_4];
96  [formatter setDateStyle:dateStyle];
97  [formatter setTimeStyle:timeStyle];
98 
99  return [formatter stringForObjectValue:date];
100 }
101 
109 + (CPString)dateFormatFromTemplate:(CPString)template options:(CPUInteger)opts locale:(CPLocale)locale
110 {
111  // TODO : check every template from cocoa and return a good format (have fun ^^)
112 }
113 
117 + (CPDateFormatterBehavior)defaultFormatterBehavior
118 {
120 }
121 
125 + (void)setDefaultFormatterBehavior:(CPDateFormatterBehavior)behavior
126 {
127  defaultDateFormatterBehavior = behavior;
128 }
129 
133 - (id)init
134 {
135  if (self = [super init])
136  {
137  _dateStyle = nil;
138  _timeStyle = nil;
139 
140  [self _init];
141  }
142 
143  return self;
144 }
145 
151 - (id)initWithDateFormat:(CPString)format allowNaturalLanguage:(BOOL)flag
152 {
153  if (self = [self init])
154  {
155  _dateFormat = format;
156  _allowNaturalLanguage = flag;
157  }
158 
159  return self
160 }
161 
164 - (void)_init
165 {
166  var AMSymbol = [CPString stringWithFormat:@"%s", @"AM"],
167  PMSymbol = [CPString stringWithFormat:@"%s", @"PM"],
168  weekdaySymbols = [CPArray arrayWithObjects:@"Sunday", @"Monday", @"Tuesday", @"Wednesday", @"Thursday", @"Friday", @"Saturday"],
169  shortWeekdaySymbols = [CPArray arrayWithObjects:@"Sun", @"Mon", @"Tue", @"Wed", @"Thu", @"Fri", @"Sat"],
170  veryShortWeekdaySymbols = [CPArray arrayWithObjects:@"S", @"M", @"T", @"W", @"T", @"F", @"S"],
171  standaloneWeekdaySymbols = [CPArray arrayWithObjects:@"Sunday", @"Monday", @"Tuesday", @"Wednesday", @"Thursday", @"Friday", @"Saturday"],
172  shortStandaloneWeekdaySymbols = [CPArray arrayWithObjects:@"Sun", @"Mon", @"Tue", @"Wed", @"Thu", @"Fri", @"Sat"],
173  veryShortStandaloneWeekdaySymbols = [CPArray arrayWithObjects:@"S", @"M", @"T", @"W", @"T", @"F", @"S"],
174  monthSymbols = [CPArray arrayWithObjects:@"January", @"February", @"March", @"April", @"May", @"June", @"July", @"August", @"September", @"October", @"November", @"December"],
175  shortMonthSymbols = [CPArray arrayWithObjects:@"Jan", @"Feb", @"Mar", @"Apr", @"May", @"Jun", @"Jul", @"Aug", @"Sep", @"Oct", @"Nov", @"Dec"],
176  veryShortMonthSymbols = [CPArray arrayWithObjects:@"J", @"F", @"M", @"A", @"M", @"J", @"J", @"A", @"S", @"O", @"N", @"D"],
177  standaloneMonthSymbols = [CPArray arrayWithObjects:@"January", @"February", @"March", @"April", @"May", @"June", @"July", @"August", @"September", @"October", @"November", @"December"],
178  shortStandaloneMonthSymbols = [CPArray arrayWithObjects:@"Jan", @"Feb", @"Mar", @"Apr", @"May", @"Jun", @"Jul", @"Aug", @"Sep", @"Oct", @"Nov", @"Dec"],
179  veryShortStandaloneMonthSymbols = [CPArray arrayWithObjects:@"J", @"F", @"M", @"A", @"M", @"J", @"J", @"A", @"S", @"O", @"N", @"D"],
180  quarterSymbols = [CPArray arrayWithObjects:@"1st quarter", @"2nd quarter", @"3rd quarter", @"4th quarter"],
181  shortQuarterSymbols = [CPArray arrayWithObjects:@"Q1", @"Q2", @"Q3", @"Q4"],
182  standaloneQuarterSymbols = [CPArray arrayWithObjects:@"1st quarter", @"2nd quarter", @"3rd quarter", @"4th quarter"],
183  shortStandaloneQuarterSymbols = [CPArray arrayWithObjects:@"Q1", @"Q2", @"Q3", @"Q4"];
184 
185  _symbols = @{
186  @"root" : @{
187  @"AMSymbol" : AMSymbol,
188  @"PMSymbol" : PMSymbol,
189  @"weekdaySymbols" : weekdaySymbols,
190  @"shortWeekdaySymbols" : shortWeekdaySymbols,
191  @"veryShortWeekdaySymbols" : veryShortWeekdaySymbols,
192  @"standaloneWeekdaySymbols" : standaloneWeekdaySymbols,
193  @"shortStandaloneWeekdaySymbols" : shortStandaloneWeekdaySymbols,
194  @"veryShortStandaloneWeekdaySymbols" : veryShortStandaloneWeekdaySymbols,
195  @"monthSymbols" : monthSymbols,
196  @"shortMonthSymbols" : shortMonthSymbols,
197  @"veryShortMonthSymbols" : veryShortMonthSymbols,
198  @"standaloneMonthSymbols" : standaloneMonthSymbols,
199  @"shortStandaloneMonthSymbols" : shortStandaloneMonthSymbols,
200  @"veryShortStandaloneMonthSymbols" : veryShortStandaloneMonthSymbols,
201  @"quarterSymbols" : quarterSymbols,
202  @"shortQuarterSymbols" : shortQuarterSymbols,
203  @"standaloneQuarterSymbols" : standaloneQuarterSymbols,
204  @"shortStandaloneQuarterSymbols" : shortStandaloneQuarterSymbols
205  }
206 
207  };
208 
209  _timeZone = [CPTimeZone systemTimeZone];
210  _twoDigitStartDate = [[CPDate alloc] initWithString:@"1950-01-01 00:00:00 +0000"];
211  _locale = [CPLocale currentLocale];
212 }
213 
214 
215 #pragma mark -
216 #pragma mark Setter Getter Helper
217 
220 - (CPDictionary)symbolsForLanguageCode:(CPString)languageCode
221 {
222  var languageSymbols = [_symbols valueForKey:languageCode];
223 
224  if (!languageSymbols)
225  {
226  languageSymbols = [self symbolsForLanguageCode:@"root"];
227  [self setSymbols:languageSymbols forLanguageCode:languageCode];
228  }
229 
230  return languageSymbols;
231 }
232 
235 - (void)setSymbols:(CPDictionary)symbols forLanguageCode:(CPString)languageCode
236 {
237  [_symbols setValue:symbols forKey:languageCode];
238 }
239 
242 - (id)symbolForKey:(CPString)aKey languageCode:(CPString)languageCode
243 {
244  var languageSymbols = [self symbolsForLanguageCode:languageCode],
245  symbol = [languageSymbols valueForKey:aKey];
246 
247  if (!symbol)
248  {
249  symbol = [self symbolForKey:aKey languageCode:@"root"];
250  [self setSymbol:symbol forKey:aKey languageCode:languageCode];
251  }
252 
253  return symbol;
254 }
255 
258 - (void)setSymbol:(CPString)aSymbol forKey:(CPString)aKey languageCode:(CPString)languageCode
259 {
260  var languageSymbols = [self symbolsForLanguageCode:languageCode];
261  [languageSymbols setValue:aSymbol forKey:aKey];
262 }
263 
264 #pragma mark -
265 #pragma mark Setter Getter
266 
269 - (CPString)AMSymbol
270 {
271  return [self symbolForKey:@"AMSymbol" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
272 }
273 
276 - (void)setAMSymbol:(CPString)aValue
277 {
278  [self setSymbol:aValue forKey:@"AMSymbol" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
279 }
280 
283 - (CPString)PMSymbol
284 {
285  return [self symbolForKey:@"PMSymbol" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
286 }
287 
290 - (void)setPMSymbol:(CPString)aValue
291 {
292  [self setSymbol:aValue forKey:@"PMSymbol" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
293 }
294 
297 - (CPArray)weekdaySymbols
298 {
299  return [self symbolForKey:@"weekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
300 }
301 
304 - (void)setWeekdaySymbols:(CPArray)aValue
305 {
306  [self setSymbol:aValue forKey:@"weekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
307 }
308 
311 - (CPArray)shortWeekdaySymbols
312 {
313  return [self symbolForKey:@"shortWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
314 }
315 
318 - (void)setShortWeekdaySymbols:(CPArray)aValue
319 {
320  [self setSymbol:aValue forKey:@"shortWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
321 }
322 
325 - (CPArray)veryShortWeekdaySymbols
326 {
327  return [self symbolForKey:@"veryShortWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
328 }
329 
332 - (void)setVeryShortWeekdaySymbols:(CPArray)aValue
333 {
334  [self setSymbol:aValue forKey:@"veryShortWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
335 }
336 
339 - (CPArray)standaloneWeekdaySymbols
340 {
341  return [self symbolForKey:@"standaloneWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
342 }
343 
346 - (void)setStandaloneWeekdaySymbols:(CPArray)aValue
347 {
348  [self setSymbol:aValue forKey:@"standaloneWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
349 }
350 
353 - (CPArray)shortStandaloneWeekdaySymbols
354 {
355  return [self symbolForKey:@"shortStandaloneWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
356 }
357 
360 - (void)setShortStandaloneWeekdaySymbols:(CPArray)aValue
361 {
362  [self setSymbol:aValue forKey:@"shortStandaloneWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
363 }
364 
367 - (CPArray)veryShortStandaloneWeekdaySymbols
368 {
369  return [self symbolForKey:@"veryShortStandaloneWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
370 }
371 
374 - (void)setVeryShortStandaloneWeekdaySymbols:(CPArray)aValue
375 {
376  [self setSymbol:aValue forKey:@"veryShortStandaloneWeekdaySymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
377 }
378 
381 - (CPArray)monthSymbols
382 {
383  return [self symbolForKey:@"monthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
384 }
385 
388 - (void)setMonthSymbols:(CPArray)aValue
389 {
390  [self setSymbol:aValue forKey:@"monthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
391 }
392 
395 - (CPArray)shortMonthSymbols
396 {
397  return [self symbolForKey:@"shortMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
398 }
399 
402 - (void)setShortMonthSymbols:(CPArray)aValue
403 {
404  [self setSymbol:aValue forKey:@"shortMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
405 }
406 
409 - (CPArray)veryShortMonthSymbols
410 {
411  return [self symbolForKey:@"veryShortMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
412 }
413 
416 - (void)setVeryShortMonthSymbols:(CPArray)aValue
417 {
418  [self setSymbol:aValue forKey:@"veryShortMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
419 }
420 
423 - (CPArray)standaloneMonthSymbols
424 {
425  return [self symbolForKey:@"standaloneMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
426 }
427 
430 - (void)setStandaloneMonthSymbols:(CPArray)aValue
431 {
432  [self setSymbol:aValue forKey:@"standaloneMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
433 }
434 
437 - (CPArray)shortStandaloneMonthSymbols
438 {
439  return [self symbolForKey:@"shortStandaloneMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
440 }
441 
444 - (void)setShortStandaloneMonthSymbols:(CPArray)aValue
445 {
446  [self setSymbol:aValue forKey:@"shortStandaloneMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
447 }
448 
451 - (CPArray)veryShortStandaloneMonthSymbols
452 {
453  return [self symbolForKey:@"veryShortStandaloneMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
454 }
455 
458 - (void)setVeryShortStandaloneMonthSymbols:(CPArray)aValue
459 {
460  [self setSymbol:aValue forKey:@"veryShortStandaloneMonthSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
461 }
462 
465 - (CPArray)quarterSymbols
466 {
467  return [self symbolForKey:@"quarterSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
468 }
469 
472 - (void)setQuarterSymbols:(CPArray)aValue
473 {
474  [self setSymbol:aValue forKey:@"quarterSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
475 }
476 
479 - (CPArray)shortQuarterSymbols
480 {
481  return [self symbolForKey:@"shortQuarterSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
482 }
483 
486 - (void)setShortQuarterSymbols:(CPArray)aValue
487 {
488  [self setSymbol:aValue forKey:@"shortQuarterSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
489 }
490 
493 - (CPArray)standaloneQuarterSymbols
494 {
495  return [self symbolForKey:@"standaloneQuarterSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
496 }
497 
500 - (void)setStandaloneQuarterSymbols:(CPArray)aValue
501 {
502  [self setSymbol:aValue forKey:@"standaloneQuarterSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
503 }
504 
507 - (CPArray)shortStandaloneQuarterSymbols
508 {
509  return [self symbolForKey:@"shortStandaloneQuarterSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
510 }
511 
514 - (void)setShortStandaloneQuarterSymbols:(CPArray)aValue
515 {
516  [self setSymbol:aValue forKey:@"shortStandaloneQuarterSymbols" languageCode:[_locale objectForKey:CPLocaleLanguageCode]];
517 }
518 
519 
520 #pragma mark -
521 #pragma mark StringFromDate methods
522 
528 - (CPString)stringFromDate:(CPDate)aDate
529 {
530  var format,
531  relativeWord,
532  result;
533 
534  if (!aDate)
535  return;
536 
537  aDate = [aDate copy];
538  [aDate _dateWithTimeZone:_timeZone];
539 
540  if (_dateFormat)
541  return [self _stringFromDate:aDate format:_dateFormat];
542 
543  switch (_dateStyle)
544  {
546  format = @"";
547  break;
548 
550  if ([self _isAmericanFormat])
551  format = @"M/d/yy";
552  else
553  format = @"dd/MM/yy";
554 
555  break;
556 
558  if ([self _isAmericanFormat])
559  format = @"MMM d, Y";
560  else
561  format = @"d MMM Y";
562 
563  break;
564 
566  if ([self _isAmericanFormat])
567  format = @"MMMM d, Y";
568  else
569  format = @"d MMMM Y";
570 
571  break;
572 
574  if ([self _isAmericanFormat])
575  format = @"EEEE, MMMM d, Y";
576  else
577  format = @"EEEE d MMMM Y";
578 
579  break;
580 
581  default:
582  format = @"";
583  }
584 
585 
586  if ([self doesRelativeDateFormatting])
587  {
588  var language = [_locale objectForKey:CPLocaleLanguageCode],
589  relativeWords = [relativeDateFormating valueForKey:language];
590 
591  for (var i = 1; i < [relativeWords count]; i = i + 2)
592  {
593  var date = [CPDate date];
594  [date _dateWithTimeZone:_timeZone];
595 
596  date.setHours(12);
597  date.setMinutes(0);
598  date.setSeconds(0);
599 
600  date.setDate([relativeWords objectAtIndex:i] + date.getDate());
601 
602  if (date.getDate() == aDate.getDate() && date.getMonth() == aDate.getMonth() && date.getFullYear() == aDate.getFullYear())
603  {
604  relativeWord = [relativeWords objectAtIndex:(i - 1)];
605  format = @"";
606  break;
607  }
608  }
609  }
610 
611  if ((relativeWord || format.length) && _timeStyle != CPDateFormatterNoStyle)
612  format += @" ";
613 
614  switch (_timeStyle)
615  {
617  format += @"";
618  break;
619 
621  if ([self _isEnglishFormat])
622  format += @"h:mm a";
623  else
624  format += @"H:mm";
625 
626  break;
627 
629  if ([self _isEnglishFormat])
630  format += @"h:mm:ss a";
631  else
632  format += @"H:mm:ss"
633 
634  break;
635 
637  if ([self _isEnglishFormat])
638  format += @"h:mm:ss a z";
639  else
640  format += @"H:mm:ss z";
641 
642  break;
643 
645  if ([self _isEnglishFormat])
646  format += @"h:mm:ss a zzzz";
647  else
648  format += @"h:mm:ss zzzz";
649 
650  break;
651 
652  default:
653  format += @"";
654  }
655 
656  result = [self _stringFromDate:aDate format:format];
657 
658  if (relativeWord)
659  result = relativeWord + result;
660 
661  return result;
662 }
663 
669 - (CPString)stringForObjectValue:(id)anObject
670 {
671  if ([anObject isKindOfClass:[CPDate class]])
672  return [self stringFromDate:anObject];
673  else
674  return nil;
675 }
676 
682 - (CPString)editingStringForObjectValue:(id)anObject
683 {
684  return [self stringForObjectValue:anObject];
685 }
686 
692 - (CPString)_stringFromDate:(CPDate)aDate format:(CPString)aFormat
693 {
694  var length = [aFormat length],
695  currentToken = [CPString new],
696  isTextToken = NO,
697  result = [CPString new];
698 
699  for (var i = 0; i < length; i++)
700  {
701  var character = [aFormat characterAtIndex:i];
702 
703  if (isTextToken)
704  {
705  if ([character isEqualToString:@"'"])
706  {
707  isTextToken = NO;
708  result += currentToken;
709  currentToken = [CPString new];
710  }
711  else
712  {
713  currentToken += character;
714  }
715 
716  continue;
717  }
718 
719  if ([character isEqualToString:@"'"])
720  {
721  if (!isTextToken)
722  {
723  isTextToken = YES;
724  result += currentToken;
725  currentToken = [CPString new];
726  }
727 
728  continue;
729  }
730 
731  if ([character isEqualToString:@","] || [character isEqualToString:@":"] || [character isEqualToString:@"/"] || [character isEqualToString:@"-"] || [character isEqualToString:@" "])
732  {
733  result += [self _stringFromToken:currentToken date:aDate];
734  result += character;
735  currentToken = [CPString new];
736  }
737  else
738  {
739  if ([currentToken length] && ![[currentToken characterAtIndex:0] isEqualToString:character])
740  {
741  result += [self _stringFromToken:currentToken date:aDate];
742  currentToken = [CPString new];
743  }
744 
745  currentToken += character;
746 
747  if (i == (length - 1))
748  result += [self _stringFromToken:currentToken date:aDate];
749  }
750  }
751 
752  return result;
753 }
754 
760 - (CPString)_stringFromToken:(CPString)aToken date:(CPDate)aDate
761 {
762  if (![aToken length])
763  return aToken;
764 
765  var character = [aToken characterAtIndex:0],
766  length = [aToken length],
767  timeZone = _timeZone;
768 
769  switch (character)
770  {
771  case @"G":
772  // TODO
773  CPLog.warn(@"Token not yet implemented " + aToken);
774  return [CPString new];
775 
776  case @"y":
777  var currentLength = [[CPString stringWithFormat:@"%i", aDate.getFullYear()] length];
778 
779  return [self _stringValueForValue:aDate.getFullYear() length:(length == 2)?length:currentLength];
780 
781  case @"Y":
782  var currentLength = [[CPString stringWithFormat:@"%i", aDate.getFullYear()] length];
783 
784  return [self _stringValueForValue:aDate.getFullYear() length:(length == 2)?length:currentLength];
785 
786  case @"u":
787  // TODO
788  CPLog.warn(@"Token not yet implemented " + aToken);
789  return [CPString new];
790 
791  case @"U":
792  // TODO
793  CPLog.warn(@"Token not yet implemented " + aToken);
794  return [CPString new];
795 
796  case @"Q":
797  var quarter = 1;
798 
799  if (aDate.getMonth() < 6 && aDate.getMonth() > 2)
800  quarter = 2;
801 
802  if (aDate.getMonth() > 5 && aDate.getMonth() < 9)
803  quarter = 3;
804 
805  if (aDate.getMonth() >= 9)
806  quarter = 4;
807 
808  if (length <= 2)
809  return [self _stringValueForValue:quarter length:MIN(2,length)];
810 
811  if (length == 3)
812  return [[self shortQuarterSymbols] objectAtIndex:(quarter - 1)];
813 
814  if (length >= 4)
815  return [[self quarterSymbols] objectAtIndex:(quarter - 1)];
816 
817  case @"q":
818  var quarter = 1;
819 
820  if (aDate.getMonth() < 6 && aDate.getMonth() > 2)
821  quarter = 2;
822 
823  if (aDate.getMonth() > 5 && aDate.getMonth() < 9)
824  quarter = 3;
825 
826  if (aDate.getMonth() >= 9)
827  quarter = 4;
828 
829  if (length <= 2)
830  return [self _stringValueForValue:quarter length:MIN(2,length)];
831 
832  if (length == 3)
833  return [[self shortStandaloneQuarterSymbols] objectAtIndex:(quarter - 1)];
834 
835  if (length >= 4)
836  return [[self standaloneQuarterSymbols] objectAtIndex:(quarter - 1)];
837 
838  case @"M":
839  var currentLength = [[CPString stringWithFormat:@"%i", aDate.getMonth() + 1] length];
840 
841  if (length <= 2)
842  return [self _stringValueForValue:(aDate.getMonth() + 1) length:MAX(currentLength,length)];
843 
844  if (length == 3)
845  return [[self shortMonthSymbols] objectAtIndex:aDate.getMonth()];
846 
847  if (length == 4)
848  return [[self monthSymbols] objectAtIndex:aDate.getMonth()];
849 
850  if (length >= 5)
851  return [[self veryShortMonthSymbols] objectAtIndex:aDate.getMonth()];
852 
853  case @"L":
854  var currentLength = [[CPString stringWithFormat:@"%i", aDate.getMonth() + 1] length];
855 
856  if (length <= 2)
857  return [self _stringValueForValue:(aDate.getMonth() + 1) length:MAX(currentLength,length)];
858 
859  if (length == 3)
860  return [[self shortStandaloneMonthSymbols] objectAtIndex:aDate.getMonth()];
861 
862  if (length == 4)
863  return [[self standaloneMonthSymbols] objectAtIndex:aDate.getMonth()];
864 
865  if (length >= 5)
866  return [[self veryShortStandaloneMonthSymbols] objectAtIndex:aDate.getMonth()];
867 
868  case @"I":
869  // Deprecated
870  CPLog.warn(@"Depreacted - Token not yet implemented " + aToken);
871  return [CPString new];
872 
873  case @"w":
874  var d = [aDate copy];
875 
876  d.setHours(0, 0, 0);
877  d.setDate(d.getDate() + 4 - (d.getDay() || 7));
878 
879  var yearStart = new Date(d.getFullYear(), 0, 1),
880  weekOfYear = Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
881 
882  return [self _stringValueForValue:(weekOfYear + 1) length:MAX(2, length)];
883 
884  case @"W":
885  var firstDay = new Date(aDate.getFullYear(), aDate.getMonth(), 1).getDay(),
886  weekOfMonth = Math.ceil((aDate.getDate() + firstDay) / 7);
887 
888  return [self _stringValueForValue:weekOfMonth length:1];
889 
890  case @"d":
891  var currentLength = [[CPString stringWithFormat:@"%i", aDate.getDate()] length];
892 
893  return [self _stringValueForValue:aDate.getDate() length:MAX(length, currentLength)];
894 
895  case @"D":
896  var oneJan = new Date(aDate.getFullYear(), 0, 1),
897  dayOfYear = Math.ceil((aDate - oneJan) / 86400000),
898  currentLength = [[CPString stringWithFormat:@"%i", dayOfYear] length];
899 
900  return [self _stringValueForValue:dayOfYear length:MAX(currentLength, MIN(3, length))];
901 
902  case @"F":
903  var dayOfWeek = 1,
904  day = aDate.getDate();
905 
906  if (day > 7 && day < 15)
907  dayOfWeek = 2;
908 
909  if (day > 14 && day < 22)
910  dayOfWeek = 3;
911 
912  if (day > 21 && day < 29)
913  dayOfWeek = 4;
914 
915  if (day > 28)
916  dayOfWeek = 5;
917 
918  return [self _stringValueForValue:dayOfWeek length:1];
919 
920  case @"g":
921  CPLog.warn(@"Token not yet implemented " + aToken);
922  return [CPString new];
923 
924  case @"E":
925  var day = aDate.getDay();
926 
927  if (length <= 3)
928  return [[self shortWeekdaySymbols] objectAtIndex:day];
929 
930  if (length == 4)
931  return [[self weekdaySymbols] objectAtIndex:day];
932 
933  if (length >= 5)
934  return [[self veryShortWeekdaySymbols] objectAtIndex:day];
935 
936  case @"e":
937  var day = aDate.getDay();
938 
939  if (length <= 2)
940  return [self _stringValueForValue:(day + 1) length:MIN(2, length)];
941 
942  if (length == 3)
943  return [[self shortWeekdaySymbols] objectAtIndex:day];
944 
945  if (length == 4)
946  return [[self weekdaySymbols] objectAtIndex:day];
947 
948  if (length >= 5)
949  return [[self veryShortWeekdaySymbols] objectAtIndex:day];
950 
951  case @"c":
952  var day = aDate.getDay();
953 
954  if (length <= 2)
955  return [self _stringValueForValue:(day + 1) length:aDate.getDay().toString().length];
956 
957  if (length == 3)
958  return [[self shortStandaloneWeekdaySymbols] objectAtIndex:day];
959 
960  if (length == 4)
961  return [[self standaloneWeekdaySymbols] objectAtIndex:day];
962 
963  if (length >= 5)
964  return [[self veryShortStandaloneWeekdaySymbols] objectAtIndex:day];
965 
966  case @"a":
967 
968  if (aDate.getHours() > 11)
969  return [self PMSymbol];
970  else
971  return [self AMSymbol];
972 
973  case @"h":
974  var hours = aDate.getHours();
975 
976  if ([self _isAmericanFormat] || [self _isEnglishFormat])
977  {
978  if (hours == 0)
979  hours = 12;
980  else if (hours > 12)
981  hours = hours - 12;
982  }
983 
984  var currentLength = [[CPString stringWithFormat:@"%i", hours] length];
985 
986  return [self _stringValueForValue:hours length:MAX(currentLength, MIN(2, length))];
987 
988  case @"H":
989  var currentLength = [[CPString stringWithFormat:@"%i", aDate.getHours()] length];
990 
991  return [self _stringValueForValue:aDate.getHours() length:MAX(currentLength, MIN(2, length))];
992 
993  case @"K":
994  var hours = aDate.getHours();
995 
996  if (hours > 12)
997  hours -= 12;
998 
999  var currentLength = [[CPString stringWithFormat:@"%i", hours] length];
1000 
1001  return [self _stringValueForValue:hours length:MAX(currentLength, MIN(2, length))];
1002 
1003  case @"k":
1004  var hours = aDate.getHours();
1005 
1006  if (aDate.getHours() == 0)
1007  hours = 24;
1008 
1009  var currentLength = [[CPString stringWithFormat:@"%i", hours] length];
1010 
1011  return [self _stringValueForValue:hours length:MAX(currentLength, MIN(2, length))];
1012 
1013  case @"j":
1014  CPLog.warn(@"Token not yet implemented " + aToken);
1015  return [CPString new];
1016 
1017  case @"m":
1018  var currentLength = [[CPString stringWithFormat:@"%i", aDate.getMinutes()] length];
1019 
1020  return [self _stringValueForValue:aDate.getMinutes() length:MAX(currentLength, MIN(2, length))];
1021 
1022  case @"s":
1023  var currentLength = [[CPString stringWithFormat:@"%i", aDate.getMinutes()] length];
1024 
1025  return [self _stringValueForValue:aDate.getSeconds() length:MIN(2, length)];
1026 
1027  case @"S":
1028  return [self _stringValueForValue:aDate.getMilliseconds() length:length];
1029 
1030  case @"A":
1031  var value = aDate.getHours() * 60 * 60 * 1000 + aDate.getMinutes() * 60 * 1000 + aDate.getSeconds() * 1000 + aDate.getMilliseconds();
1032 
1033  return [self _stringValueForValue:value length:value.toString().length];
1034 
1035  case @"z":
1036  if (length <= 3)
1037  return [timeZone localizedName:CPTimeZoneNameStyleShortDaylightSaving locale:_locale];
1038  else
1039  return [timeZone localizedName:CPTimeZoneNameStyleDaylightSaving locale:_locale];
1040 
1041  case @"Z":
1042  var seconds = [timeZone secondsFromGMT],
1043  minutes = seconds / 60,
1044  hours = minutes / 60,
1045  result,
1046  diffMinutes = (hours - parseInt(hours)) * 100 * 60 / 100;
1047 
1048  if (length <= 3)
1049  {
1050  result = diffMinutes.toString();
1051 
1052  while ([result length] < 2)
1053  result = @"0" + result;
1054 
1055  result = ABS(parseInt(hours)) + result;
1056 
1057  while ([result length] < 4)
1058  result = @"0" + result;
1059 
1060  if (seconds > 0)
1061  result = @"+" + result;
1062  else
1063  result = @"-" + result;
1064 
1065  return result;
1066  }
1067  else if (length == 4)
1068  {
1069  result = diffMinutes.toString();
1070 
1071  while ([result length] < 2)
1072  result = @"0" + result;
1073 
1074  result = @":" + result;
1075  result = ABS(parseInt(hours)) + result;
1076 
1077  while ([result length] < 5)
1078  result = @"0" + result;
1079 
1080  if (seconds > 0)
1081  result = @"+" + result;
1082  else
1083  result = @"-" + result;
1084 
1085  return @"GMT" + result;
1086  }
1087  else
1088  {
1089  result = diffMinutes.toString();
1090 
1091  while ([result length] < 2)
1092  result = @"0" + result;
1093 
1094  result = @":" + result;
1095  result = ABS(parseInt(hours)) + result;
1096 
1097  while ([result length] < 5)
1098  result = @"0" + result;
1099 
1100  if (seconds > 0)
1101  result = @"+" + result;
1102  else
1103  result = @"-" + result;
1104 
1105  return result;
1106  }
1107 
1108  case @"v":
1109  if (length == 1)
1110  return [timeZone localizedName:CPTimeZoneNameStyleShortGeneric locale:_locale];
1111  else if (length == 4)
1112  return [timeZone localizedName:CPTimeZoneNameStyleGeneric locale:_locale];
1113 
1114  return @" ";
1115 
1116  case @"V":
1117  if (length == 1)
1118  {
1119  return [timeZone localizedName:CPTimeZoneNameStyleShortDaylightSaving locale:_locale];
1120  }
1121  else if (length == 4)
1122  {
1123  CPLog.warn(@"No pattern found for " + aToken);
1124  return @"";
1125  }
1126 
1127  return @" ";
1128 
1129  default:
1130  CPLog.warn(@"No pattern found for " + aToken);
1131  return aToken;
1132  }
1133 
1134  return [CPString new];
1135 }
1136 
1137 
1138 #pragma mark -
1139 #pragma mark datefromString
1140 
1146 - (CPDate)dateFromString:(CPString)aString
1147 {
1148  var format;
1149 
1150  if (_dateFormat != nil)
1151  return [self _dateFromString:aString format:_dateFormat];
1152 
1153  switch (_dateStyle)
1154  {
1156  format = @"";
1157  break;
1158 
1160  if ([self _isAmericanFormat])
1161  format = @"M/d/yy";
1162  else
1163  format = @"dd/MM/yy";
1164 
1165  break;
1166 
1168  if ([self _isAmericanFormat])
1169  format = @"MMM d, Y";
1170  else
1171  format = @"d MMM Y";
1172 
1173  break;
1174 
1176  if ([self _isAmericanFormat])
1177  format = @"MMMM d, Y";
1178  else
1179  format = @"d MMMM Y";
1180 
1181  break;
1182 
1184  if ([self _isAmericanFormat])
1185  format = @"EEEE, MMMM d, Y";
1186  else
1187  format = @"EEEE d MMMM Y";
1188 
1189  break;
1190 
1191  default:
1192  format = @"";
1193  }
1194 
1195  switch (_timeStyle)
1196  {
1198  format += @"";
1199  break;
1200 
1202  if ([self _isEnglishFormat])
1203  format += @" h:mm a";
1204  else
1205  format += @" H:mm";
1206  break;
1207 
1209  if ([self _isEnglishFormat])
1210  format += @" h:mm:ss a";
1211  else
1212  format += @" H:mm:ss"
1213  break;
1214 
1216  if ([self _isEnglishFormat])
1217  format += @" h:mm:ss a z";
1218  else
1219  format += @" H:mm:ss z";
1220  break;
1221 
1223  if ([self _isEnglishFormat])
1224  format += @" h:mm:ss a zzzz";
1225  else
1226  format += @" h:mm:ss zzzz";
1227  break;
1228 
1229  default:
1230  format += @"";
1231  }
1232 
1233  return [self _dateFromString:aString format:format];
1234 }
1235 
1242 - (BOOL)getObjectValue:(idRef)anObject forString:(CPString)aString errorDescription:(CPStringRef)anError
1243 {
1244  var value = [self dateFromString:aString];
1245  @deref(anObject) = value;
1246 
1247  if (!value)
1248  {
1249  if (anError)
1250  @deref(anError) = @"The value \"" + aString + "\" is invalid.";
1251 
1252  return NO;
1253  }
1254 
1255  return YES;
1256 }
1257 
1263 - (CPDate)_dateFromString:(CPString)aString format:(CPString)aFormat
1264 {
1265  // Interpret @"" as the date 2000-01-01 00:00:00 +0000, like in Cocoa. No idea why they picked this particular date.
1266  if (!aString)
1268 
1269  if (aFormat == nil)
1270  return nil;
1271 
1272  var currentToken = [CPString new],
1273  isTextToken = NO,
1274  tokens = [CPArray array],
1275  dateComponents = [CPArray array],
1276  patternTokens = [CPArray array];
1277 
1278  for (var i = 0; i < [aFormat length]; i++)
1279  {
1280  var character = [aFormat characterAtIndex:i];
1281 
1282  if (isTextToken)
1283  {
1284  if ([character isEqualToString:@"'"])
1285  currentToken = [CPString new];
1286 
1287  continue;
1288  }
1289 
1290  if ([character isEqualToString:@"'"])
1291  {
1292  if (!isTextToken)
1293  isTextToken = YES;
1294 
1295  continue;
1296  }
1297 
1298  if ([character isEqualToString:@","] || [character isEqualToString:@":"] || [character isEqualToString:@"/"] || [character isEqualToString:@"-"] || [character isEqualToString:@" "])
1299  {
1300  [tokens addObject:currentToken];
1301 
1302  if ([patternStringTokens containsObject:currentToken])
1303  [patternTokens addObject:[tokens count] - 1];
1304 
1305  currentToken = [CPString new];
1306  }
1307  else
1308  {
1309  if ([currentToken length] && ![[currentToken characterAtIndex:0] isEqualToString:character])
1310  {
1311  [tokens addObject:currentToken];
1312 
1313  if ([patternStringTokens containsObject:currentToken])
1314  [patternTokens addObject:[tokens count] - 1];
1315 
1316  currentToken = [CPString new];
1317  }
1318 
1319  currentToken += character;
1320 
1321  if (i == ([aFormat length] - 1))
1322  {
1323  [tokens addObject:currentToken];
1324 
1325  if ([patternStringTokens containsObject:currentToken])
1326  [patternTokens addObject:[tokens count] - 1];
1327  }
1328  }
1329  }
1330 
1331  isTextToken = NO;
1332  currentToken = [CPString new];
1333 
1334  var currentIndexSpecialPattern = 0;
1335 
1336  if ([patternTokens count] == 0)
1337  [patternTokens addObject:CPNotFound];
1338 
1339  for (var i = 0; i < [aString length]; i++)
1340  {
1341  var character = [aString characterAtIndex:i];
1342 
1343  if (isTextToken)
1344  {
1345  if ([character isEqualToString:@"'"])
1346  currentToken = [CPString new];
1347 
1348  continue;
1349  }
1350 
1351  if ([character isEqualToString:@"'"])
1352  {
1353  if (!isTextToken)
1354  isTextToken = YES;
1355 
1356  continue;
1357  }
1358 
1359  // Need to do this to check if the word match with the token. We can get some words with space...
1360  if ([dateComponents count] == [patternTokens objectAtIndex:currentIndexSpecialPattern])
1361  {
1362  var j = [self _lastIndexMatchedString:aString token:[tokens objectAtIndex:[dateComponents count]] index:i];
1363 
1364  if (j == CPNotFound)
1365  return nil;
1366 
1367  currentIndexSpecialPattern++;
1368  [dateComponents addObject:[aString substringWithRange:CPMakeRange(i, (j - i))]];
1369  i = j;
1370 
1371  continue;
1372  }
1373 
1374  if ([character isEqualToString:@","] || [character isEqualToString:@":"] || [character isEqualToString:@"/"] || [character isEqualToString:@"-"] || [character isEqualToString:@" "])
1375  {
1376  [dateComponents addObject:currentToken];
1377  currentToken = [CPString new];
1378  }
1379  else
1380  {
1381  currentToken += character;
1382 
1383  if (i == ([aString length] - 1))
1384  [dateComponents addObject:currentToken];
1385  }
1386  }
1387 
1388  if ([dateComponents count] != [tokens count])
1389  return nil;
1390 
1391  return [self _dateFromTokens:tokens dateComponents:dateComponents];
1392 }
1393 
1394 - (CPDate)_dateFromTokens:(CPArray)tokens dateComponents:(CPArray)dateComponents
1395 {
1396  var timeZoneseconds = [_timeZone secondsFromGMT],
1397  dateArray = [2000, 01, 01, 00, 00, 00, @"+0000"],
1398  isPM = NO,
1399  dayOfYear,
1400  dayIndexInWeek,
1401  weekOfYear,
1402  weekOfMonth;
1403 
1404  for (var i = 0; i < [tokens count]; i++)
1405  {
1406  var token = [tokens objectAtIndex:i],
1407  dateComponent = [dateComponents objectAtIndex:i],
1408  character = [token characterAtIndex:0],
1409  length = [token length];
1410 
1411  switch (character)
1412  {
1413  case @"G":
1414  // TODO
1415  CPLog.warn(@"Token not yet implemented " + token);
1416  break;
1417 
1418  case @"y":
1419  var u = _twoDigitStartDate.getFullYear() % 10,
1420  d = parseInt(_twoDigitStartDate.getFullYear() / 10) % 10,
1421  c = parseInt(_twoDigitStartDate.getFullYear() / 100) % 10,
1422  m = parseInt(_twoDigitStartDate.getFullYear() / 1000) % 10;
1423 
1424  if (length == 2 && dateComponent.length == 2)
1425  {
1426  if ((u + d * 10) >= parseInt(dateComponent))
1427  dateArray[0] = (c + 1) * 100 + m * 1000 + parseInt(dateComponent);
1428  else
1429  dateArray[0] = c * 100 + m * 1000 + parseInt(dateComponent);
1430  }
1431  else
1432  {
1433  dateArray[0] = parseInt(dateComponent);
1434  }
1435 
1436  break;
1437 
1438  case @"Y":
1439  var u = _twoDigitStartDate.getFullYear() % 10,
1440  d = parseInt(_twoDigitStartDate.getFullYear() / 10) % 10,
1441  c = parseInt(_twoDigitStartDate.getFullYear() / 100) % 10,
1442  m = parseInt(_twoDigitStartDate.getFullYear() / 1000) % 10;
1443 
1444  if (length == 2 && dateComponent.length == 2)
1445  {
1446  if ((u + d * 10) >= parseInt(dateComponent))
1447  dateArray[0] = (c + 1) * 100 + m * 1000 + parseInt(dateComponent);
1448  else
1449  dateArray[0] = c * 100 + m * 1000 + parseInt(dateComponent);
1450  }
1451  else
1452  {
1453  dateArray[0] = parseInt(dateComponent);
1454  }
1455 
1456  break;
1457 
1458  case @"u":
1459  // TODO
1460  CPLog.warn(@"Token not yet implemented " + token);
1461  break;
1462 
1463  case @"U":
1464  // TODO
1465  CPLog.warn(@"Token not yet implemented " + token);
1466  break;
1467 
1468  case @"Q":
1469  var month;
1470 
1471  if (length <= 2)
1472  month = (parseInt(dateComponent) - 1) * 3;
1473 
1474  if (length == 3)
1475  {
1476  if (![[self shortQuarterSymbols] containsObject:dateComponent])
1477  return nil;
1478 
1479  month = [[self shortQuarterSymbols] indexOfObject:dateComponent] * 3;
1480  }
1481 
1482  if (length >= 4)
1483  {
1484  if (![[self quarterSymbols] containsObject:dateComponent])
1485  return nil;
1486 
1487  month = [[self quarterSymbols] indexOfObject:dateComponent] * 3;
1488  }
1489 
1490  if (month > 11)
1491  return nil;
1492 
1493  dateArray[1] = month + 1;
1494  break;
1495 
1496  case @"q":
1497  var month;
1498 
1499  if (length <= 2)
1500  month = (parseInt(dateComponent) - 1) * 3;
1501 
1502  if (length == 3)
1503  {
1504  if (![[self shortQuarterSymbols] containsObject:dateComponent])
1505  return nil;
1506 
1507  month = [[self shortQuarterSymbols] indexOfObject:dateComponent] * 3;
1508  }
1509 
1510  if (length >= 4)
1511  {
1512  if (![[self quarterSymbols] containsObject:dateComponent])
1513  return nil;
1514 
1515  month = [[self quarterSymbols] indexOfObject:dateComponent] * 3;
1516  }
1517 
1518  if (month > 11)
1519  return nil;
1520 
1521  dateArray[1] = month + 1;
1522  break;
1523 
1524  case @"M":
1525  var month;
1526 
1527  if (length <= 2)
1528  month = parseInt(dateComponent)
1529 
1530  if (length == 3)
1531  {
1532  if (![[self shortMonthSymbols] containsObject:dateComponent])
1533  return nil;
1534 
1535  month = [[self shortMonthSymbols] indexOfObject:dateComponent] + 1;
1536  }
1537 
1538  if (length == 4)
1539  {
1540  if (![[self monthSymbols] containsObject:dateComponent])
1541  return nil;
1542 
1543  month = [[self monthSymbols] indexOfObject:dateComponent] + 1;
1544  }
1545 
1546  if (month > 11 || length >= 5)
1547  return nil;
1548 
1549  dateArray[1] = month;
1550  break;
1551 
1552  case @"L":
1553  var month;
1554 
1555  if (length <= 2)
1556  month = parseInt(dateComponent);
1557 
1558  if (length == 3)
1559  {
1560  if (![[self shortStandaloneMonthSymbols] containsObject:dateComponent])
1561  return nil;
1562 
1563  month = [[self shortStandaloneMonthSymbols] indexOfObject:dateComponent] + 1;
1564  }
1565 
1566  if (length == 4)
1567  {
1568  if (![[self standaloneMonthSymbols] containsObject:dateComponent])
1569  return nil;
1570 
1571  month = [[self standaloneMonthSymbols] indexOfObject:dateComponent] + 1;
1572  }
1573 
1574  if (month > 11 || length >= 5)
1575  return nil;
1576 
1577  dateArray[1] = month;
1578  break;
1579 
1580  case @"I":
1581  // Deprecated
1582  CPLog.warn(@"Depreacted - Token not yet implemented " + token);
1583  break;
1584 
1585  case @"w":
1586  if (dateComponent > 52)
1587  return nil;
1588 
1589  weekOfYear = dateComponent;
1590  break;
1591 
1592  case @"W":
1593  if (dateComponent > 52)
1594  return nil;
1595 
1596  weekOfMonth = dateComponent;
1597  break;
1598 
1599  case @"d":
1600  dateArray[2] = parseInt(dateComponent);
1601  break;
1602 
1603  case @"D":
1604  if (isNaN(parseInt(dateComponent)) || parseInt(dateComponent) > 345)
1605  return nil;
1606 
1607  dayOfYear = parseInt(dateComponent);
1608  break;
1609 
1610  case @"F":
1611  if (isNaN(parseInt(dateComponent)) || parseInt(dateComponent) > 5 || parseInt(dateComponent) == 0)
1612  return nil;
1613 
1614  if (parseInt(dateComponent) == 1)
1615  dateArray[2] = 1;
1616 
1617  if (parseInt(dateComponent) == 2)
1618  dateArray[2] = 8;
1619 
1620  if (parseInt(dateComponent) == 3)
1621  dateArray[2] = 15;
1622 
1623  if (parseInt(dateComponent) == 4)
1624  dateArray[2] = 22;
1625 
1626  if (parseInt(dateComponent) == 5)
1627  dateArray[2] = 29;
1628 
1629  break;
1630 
1631  case @"g":
1632  CPLog.warn(@"Token not yet implemented " + token);
1633  break;
1634 
1635  case @"E":
1636  if (length <= 3)
1637  dayIndexInWeek = [[self shortWeekdaySymbols] indexOfObject:dateComponent];
1638 
1639  if (length == 4)
1640  dayIndexInWeek = [[self weekdaySymbols] indexOfObject:dateComponent];
1641 
1642  if (dayIndexInWeek == CPNotFound || length >= 5)
1643  return nil;
1644 
1645  break;
1646 
1647  case @"e":
1648  if (length <= 2 && isNaN(parseInt(dateComponent)))
1649  return nil;
1650 
1651  if (length <= 2)
1652  dayIndexInWeek = parseInt(dateComponent);
1653 
1654  if (length == 3)
1655  dayIndexInWeek = [[self shortWeekdaySymbols] indexOfObject:dateComponent];
1656 
1657  if (length == 4)
1658  dayIndexInWeek = [[self weekdaySymbols] indexOfObject:dateComponent];
1659 
1660  if (dayIndexInWeek == CPNotFound || length >= 5)
1661  return nil;
1662 
1663  break;
1664 
1665  case @"c":
1666  if (length <= 2 && isNaN(parseInt(dateComponent)))
1667  return nil;
1668 
1669  if (length <= 2)
1670  dayIndexInWeek = dateComponent;
1671 
1672  if (length == 3)
1673  dayIndexInWeek = [[self shortStandaloneWeekdaySymbols] indexOfObject:dateComponent];
1674 
1675  if (length == 4)
1676  dayIndexInWeek = [[self standaloneWeekdaySymbols] indexOfObject:dateComponent];
1677 
1678  if (length == 5)
1679  dayIndexInWeek = [[self veryShortStandaloneWeekdaySymbols] indexOfObject:dateComponent];
1680 
1681  if (dayIndexInWeek == CPNotFound || length >= 5)
1682  return nil;
1683 
1684  break;
1685 
1686  case @"a":
1687  if (![dateComponent isEqualToString:[self PMSymbol]] && ![dateComponent isEqualToString:[self AMSymbol]])
1688  return nil;
1689 
1690  if ([dateComponent isEqualToString:[self PMSymbol]])
1691  isPM = YES;
1692 
1693  break;
1694 
1695  case @"h":
1696  if (parseInt(dateComponent) < 0 || parseInt(dateComponent) > 12)
1697  return nil;
1698 
1699  dateArray[3] = parseInt(dateComponent);
1700  break;
1701 
1702  case @"H":
1703  if (parseInt(dateComponent) < 0 || parseInt(dateComponent) > 23)
1704  return nil;
1705 
1706  dateArray[3] = parseInt(dateComponent);
1707  break;
1708 
1709  case @"K":
1710  if (parseInt(dateComponent) < 0 || parseInt(dateComponent) > 11)
1711  return nil;
1712 
1713  dateArray[3] = parseInt(dateComponent);
1714  break;
1715 
1716  case @"k":
1717  if (parseInt(dateComponent) < 0 || parseInt(dateComponent) > 12)
1718  return nil;
1719 
1720  dateArray[3] = parseInt(dateComponent);
1721  break;
1722 
1723  case @"j":
1724  CPLog.warn(@"Token not yet implemented " + token);
1725  break;
1726 
1727  case @"m":
1728  var minutes = parseInt(dateComponent);
1729 
1730  if (minutes > 59)
1731  return nil;
1732 
1733  dateArray[4] = minutes;
1734  break;
1735 
1736  case @"s":
1737  var seconds = parseInt(dateComponent);
1738 
1739  if (seconds > 59)
1740  return nil;
1741 
1742  dateArray[5] = seconds;
1743  break;
1744 
1745  case @"S":
1746  if (isNaN(parseInt(dateComponent)))
1747  return nil;
1748 
1749  break;
1750 
1751  case @"A":
1752  if (isNaN(parseInt(dateComponent)))
1753  return nil;
1754 
1755  var millisecondsInDay = parseInt(dateComponent),
1756  tmpDate = new Date();
1757 
1758  tmpDate.setHours(0);
1759  tmpDate.setMinutes(0);
1760  tmpDate.setSeconds(0);
1761  tmpDate.setMilliseconds(0);
1762 
1763  tmpDate.setMilliseconds(millisecondsInDay);
1764 
1765  dateArray[3] = tmpDate.getHours();
1766  dateArray[4] = tmpDate.getMinutes();
1767  dateArray[5] = tmpDate.getSeconds();
1768  break;
1769 
1770  case @"z":
1771  if (length < 4)
1772  timeZoneseconds = [self _secondsFromTimeZoneString:dateComponent style:CPTimeZoneNameStyleShortDaylightSaving];
1773  else
1774  timeZoneseconds = [self _secondsFromTimeZoneString:dateComponent style:CPTimeZoneNameStyleDaylightSaving];
1775 
1776  if (!timeZoneseconds)
1777  timeZoneseconds = [self _secondsFromTimeZoneDefaultFormatString:dateComponent];
1778 
1779  if (!timeZoneseconds)
1780  return nil;
1781 
1782  timeZoneseconds = timeZoneseconds + 60 * 60;
1783 
1784  break;
1785 
1786  case @"Z":
1787  timeZoneseconds = [self _secondsFromTimeZoneDefaultFormatString:dateComponent];
1788 
1789  if (!timeZoneseconds)
1790  return nil;
1791 
1792  timeZoneseconds = timeZoneseconds + 60 * 60;
1793 
1794  break;
1795 
1796  case @"v":
1797  if (length <= 3)
1798  timeZoneseconds = [self _secondsFromTimeZoneString:dateComponent style:CPTimeZoneNameStyleShortGeneric];
1799  else
1800  timeZoneseconds = [self _secondsFromTimeZoneString:dateComponent style:CPTimeZoneNameStyleGeneric];
1801 
1802  if (!timeZoneseconds && length == 4)
1803  timeZoneseconds = [self _secondsFromTimeZoneDefaultFormatString:dateComponent];
1804 
1805  if (!timeZoneseconds)
1806  return nil;
1807 
1808  timeZoneseconds = timeZoneseconds + 60 * 60;
1809 
1810  break;
1811 
1812  case @"V":
1813  if (length <= 3)
1814  timeZoneseconds = [self _secondsFromTimeZoneString:dateComponent style:CPTimeZoneNameStyleShortStandard];
1815  else
1816  timeZoneseconds = [self _secondsFromTimeZoneString:dateComponent style:CPTimeZoneNameStyleStandard];
1817 
1818  if (!timeZoneseconds)
1819  timeZoneseconds = [self _secondsFromTimeZoneDefaultFormatString:dateComponent];
1820 
1821  if (!timeZoneseconds)
1822  return nil;
1823 
1824  timeZoneseconds = timeZoneseconds + 60 * 60;
1825 
1826  break;
1827 
1828  default:
1829  CPLog.warn(@"No pattern found for " + token);
1830  return nil;
1831  }
1832  }
1833 
1834  // Make the calcul day of the year
1835  if (dayOfYear)
1836  {
1837  var tmpDate = new Date();
1838  tmpDate.setFullYear(dateArray[0]);
1839  tmpDate.setMonth(0);
1840 
1841  tmpDate.setDate(dayOfYear)
1842 
1843  dateArray[1] = tmpDate.getMonth() + 1;
1844  dateArray[2] = tmpDate.getDate();
1845  }
1846 
1847  if (weekOfMonth)
1848  dateArray[2] = (weekOfMonth - 1) * 7 + 1;
1849 
1850  if (weekOfYear)
1851  {
1852  var tmpDate = new Date();
1853  tmpDate.setFullYear(dateArray[0]);
1854  tmpDate.setMonth(0);
1855  tmpDate.setDate(1);
1856 
1857  while (tmpDate.getDay() != 0)
1858  tmpDate.setDate(tmpDate.getDate() + 1);
1859 
1860  tmpDate.setDate(tmpDate.getDate() + (weekOfYear - 1) * 7);
1861 
1862  dateArray[1] = tmpDate.getMonth() + 1;
1863  dateArray[2] = tmpDate.getDate() - 1;
1864  }
1865 
1866  // Check if the day is possible in the current month
1867  var tmpDate = new Date();
1868  tmpDate.setMonth(dateArray[1] - 1);
1869  tmpDate.setFullYear(dateArray[0]);
1870 
1871  if (dateArray[2] <= 0 || dateArray[2] > [tmpDate _daysInMonth])
1872  return nil;
1873 
1874  // PM hours
1875  if (isPM)
1876  dateArray[3] += 12;
1877 
1878  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])))
1879  return nil;
1880 
1881  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]]];
1882  dateResult.setSeconds(dateResult.getSeconds() - timeZoneseconds + 60 * 60);
1883 
1884  return dateResult;
1885 }
1886 
1887 
1888 #pragma mark -
1889 #pragma mark Utils
1890 
1891 - (CPString)_stringValueForValue:(id)aValue length:(int)length
1892 {
1893  var string = [CPString stringWithFormat:@"%i", aValue];
1894 
1895  if ([string length] == length)
1896  return string;
1897 
1898  if ([string length] > length)
1899  return [string substringFromIndex:([string length] - length)];
1900 
1901  while ([string length] < length)
1902  string = [CPString stringWithFormat:@"0%s", string];
1903 
1904  return string;
1905 }
1906 
1909 - (BOOL)_isAmericanFormat
1910 {
1911  return [[_locale objectForKey:CPLocaleCountryCode] isEqualToString:@"US"];
1912 }
1913 
1916 - (BOOL)_isEnglishFormat
1917 {
1918  return [[_locale objectForKey:CPLocaleLanguageCode] isEqualToString:@"en"];
1919 }
1920 
1923 - (int)_secondsFromTimeZoneDefaultFormatString:(CPString)aTimeZoneFormatString
1924 {
1925  var format = /\w*([HPG-GMT])?([+-])(\d{1,2})([:])?(\d{2})\w*/,
1926  result = aTimeZoneFormatString.match(new RegExp(format)),
1927  seconds = 0;
1928 
1929  if (!result)
1930  return nil;
1931 
1932  seconds = result[3] * 60 * 60 + result[5] * 60;
1933 
1934  if ([result[2] isEqualToString:@"-"])
1935  seconds = -seconds;
1936 
1937  return seconds;
1938 }
1939 
1942 - (int)_secondsFromTimeZoneString:(CPString)aTimeZoneString style:(NSTimeZoneNameStyle)aStyle
1943 {
1944  var timeZone = [CPTimeZone _timeZoneFromString:aTimeZoneString style:aStyle locale:_locale];
1945 
1946  if (!timeZone)
1947  return nil;
1948 
1949  return [timeZone secondsFromGMT];
1950 }
1951 
1958 - (int)_lastIndexMatchedString:(CPString)aString token:(CPString)aToken index:anIndex
1959 {
1960  var character = [aToken characterAtIndex:0],
1961  length = [aToken length],
1962  targetedArray,
1963  format = /\w*([HPG-GMT])?([+-])(\d{1,2})([:])?(\d{2})\w*/,
1964  result = aString.match(new RegExp(format));
1965 
1966  switch (character)
1967  {
1968  case @"Q":
1969  if (length == 3)
1970  targetedArray = [self shortQuarterSymbols];
1971 
1972  if (length >= 4)
1973  targetedArray = [self quarterSymbols];
1974 
1975  break;
1976 
1977  case @"q":
1978  if (length == 3)
1979  targetedArray = [self shortStandaloneQuarterSymbols];
1980 
1981  if (length >= 4)
1982  targetedArray = [self standaloneQuarterSymbols];
1983 
1984  break;
1985 
1986  case @"M":
1987  if (length == 3)
1988  targetedArray = [self shortMonthSymbols];
1989 
1990  if (length == 4)
1991  targetedArray = [self monthSymbols];
1992 
1993  if (length >= 5)
1994  targetedArray = [self veryShortMonthSymbols];
1995 
1996  break;
1997 
1998  case @"L":
1999  if (length == 3)
2000  targetedArray = [self shortStandaloneMonthSymbols];
2001 
2002  if (length == 4)
2003  targetedArray = [self standaloneMonthSymbols];
2004 
2005  if (length >= 5)
2006  targetedArray = [self veryShortStandaloneMonthSymbols];
2007 
2008  break;
2009 
2010  case @"E":
2011  if (length <= 3)
2012  targetedArray = [self shortWeekdaySymbols];
2013 
2014  if (length == 4)
2015  targetedArray = [self weekdaySymbols];
2016 
2017  if (length >= 5)
2018  targetedArray = [self veryShortWeekdaySymbols];
2019 
2020  break;
2021 
2022  case @"e":
2023  if (length == 3)
2024  targetedArray = [self shortWeekdaySymbols];
2025 
2026  if (length == 4)
2027  targetedArray = [self weekdaySymbols];
2028 
2029  if (length >= 5)
2030  targetedArray = [self veryShortWeekdaySymbols];
2031 
2032  break;
2033 
2034  case @"c":
2035  if (length == 3)
2036  targetedArray = [self shortStandaloneWeekdaySymbols];
2037 
2038  if (length == 4)
2039  targetedArray = [self standaloneWeekdaySymbols];
2040 
2041  if (length >= 5)
2042  targetedArray = [self veryShortStandaloneWeekdaySymbols];
2043 
2044  break;
2045 
2046  case @"a":
2047  targetedArray = [[self PMSymbol], [self AMSymbol]];
2048  break;
2049 
2050  case @"z":
2051  if (length <= 3)
2052  targetedArray = [CPTimeZone _namesForStyle:CPTimeZoneNameStyleShortDaylightSaving locale:_locale];
2053  else
2054  targetedArray = [CPTimeZone _namesForStyle:CPTimeZoneNameStyleDaylightSaving locale:_locale];
2055 
2056  if (result)
2057  return anIndex + [result objectAtIndex:0].length;
2058 
2059  break;
2060 
2061  case @"Z":
2062  if (result)
2063  return anIndex + [result objectAtIndex:0].length;
2064 
2065  return CPNotFound;
2066 
2067  case @"v":
2068  if (length == 1)
2069  targetedArray = [CPTimeZone _namesForStyle:CPTimeZoneNameStyleShortGeneric locale:_locale];
2070  else if (length == 4)
2071  targetedArray = [CPTimeZone _namesForStyle:CPTimeZoneNameStyleGeneric locale:_locale];
2072 
2073  if (result)
2074  return anIndex + [result objectAtIndex:0].length;
2075 
2076  break;
2077 
2078  case @"V":
2079  if (length == 1)
2080  targetedArray = [CPTimeZone _namesForStyle:CPTimeZoneNameStyleShortStandard locale:_locale];
2081 
2082  if (result)
2083  return anIndex + [result objectAtIndex:0].length;
2084 
2085  break;
2086 
2087  default:
2088  CPLog.warn(@"No pattern found for " + aToken);
2089  return CPNotFound;
2090  }
2091 
2092  for (var i = 0; i < [targetedArray count]; i++)
2093  {
2094  var currentObject = [targetedArray objectAtIndex:i],
2095  range = [aString rangeOfString:currentObject];
2096 
2097  if (range.length == 0)
2098  continue;
2099 
2100  character = [aString characterAtIndex:(anIndex + range.length)];
2101 
2102  if ([character isEqualToString:@"'"] || [character isEqualToString:@","] || [character isEqualToString:@":"] || [character isEqualToString:@"/"] || [character isEqualToString:@"-"] || [character isEqualToString:@" "] || [character isEqualToString:@""])
2103  return anIndex + range.length;
2104  }
2105 
2106  return CPNotFound;
2107 }
2108 
2109 @end
2110 
2111 var CPDateFormatterDateStyleKey = @"CPDateFormatterDateStyle",
2112  CPDateFormatterTimeStyleKey = @"CPDateFormatterTimeStyleKey",
2113  CPDateFormatterFormatterBehaviorKey = @"CPDateFormatterFormatterBehaviorKey",
2114  CPDateFormatterDoseRelativeDateFormattingKey = @"CPDateFormatterDoseRelativeDateFormattingKey",
2115  CPDateFormatterDateFormatKey = @"CPDateFormatterDateFormatKey",
2116  CPDateFormatterAllowNaturalLanguageKey = @"CPDateFormatterAllowNaturalLanguageKey",
2117  CPDateFormatterLocaleKey = @"CPDateFormatterLocaleKey";
2118 
2120 
2121 - (id)initWithCoder:(CPCoder)aCoder
2122 {
2123  self = [super initWithCoder:aCoder];
2124 
2125  if (self)
2126  {
2127  _allowNaturalLanguage = [aCoder decodeBoolForKey:CPDateFormatterAllowNaturalLanguageKey];
2128  _dateFormat = [aCoder decodeObjectForKey:CPDateFormatterDateFormatKey];
2129  _dateStyle = [aCoder decodeIntForKey:CPDateFormatterDateStyleKey];
2130  _doesRelativeDateFormatting = [aCoder decodeBoolForKey:CPDateFormatterDoseRelativeDateFormattingKey];
2131  _formatterBehavior = [aCoder decodeIntForKey:CPDateFormatterFormatterBehaviorKey];
2132  _locale = [aCoder decodeObjectForKey:CPDateFormatterLocaleKey];
2133  _timeStyle = [aCoder decodeIntForKey:CPDateFormatterTimeStyleKey];
2134  }
2135 
2136  [self _init];
2137 
2138  return self;
2139 }
2140 
2141 - (void)encodeWithCoder:(CPCoder)aCoder
2142 {
2143  [super encodeWithCoder:aCoder];
2144 
2145  [aCoder encodeBool:_allowNaturalLanguage forKey:CPDateFormatterAllowNaturalLanguageKey];
2146  [aCoder encodeInt:_dateStyle forKey:CPDateFormatterDateStyleKey];
2147  [aCoder encodeObject:_dateFormat forKey:CPDateFormatterDateFormatKey];
2148  [aCoder encodeBool:_doesRelativeDateFormatting forKey:CPDateFormatterDoseRelativeDateFormattingKey];
2149  [aCoder encodeInt:_formatterBehavior forKey:CPDateFormatterFormatterBehaviorKey];
2150  [aCoder encodeInt:_locale forKey:CPDateFormatterLocaleKey];
2151  [aCoder encodeInt:_timeStyle forKey:CPDateFormatterTimeStyleKey];
2152 }
2153 
2154 @end
2155 
2156 
2157 @implementation CPDate (CPTimeZone)
2158 
2161 - (void)_dateWithTimeZone:(CPTimeZone)aTimeZone
2162 {
2163  if (!aTimeZone)
2164  return;
2165 
2166  self.setSeconds(self.getSeconds() - [aTimeZone secondsFromGMTForDate:self]);
2167  self.setSeconds(self.getSeconds() + [aTimeZone secondsFromGMT]);
2168 }
2169 
2170 @end
2171 
2173 
2177 - (BOOL)allowNaturalLanguage
2178 {
2179  return _allowNaturalLanguage;
2180 }
2181 
2185 - (BOOL)doesRelativeDateFormatting
2186 {
2187  return _doesRelativeDateFormatting;
2188 }
2189 
2193 - (void)setDoesRelativeDateFormatting:(BOOL)aValue
2194 {
2195  _doesRelativeDateFormatting = aValue;
2196 }
2197 
2201 - (CPDate)defaultDate
2202 {
2203  return _defaultDate;
2204 }
2205 
2209 - (void)setDefaultDate:(CPDate)aValue
2210 {
2211  _defaultDate = aValue;
2212 }
2213 
2217 - (CPDate)twoDigitStartDate
2218 {
2219  return _twoDigitStartDate;
2220 }
2221 
2225 - (void)setTwoDigitStartDate:(CPDate)aValue
2226 {
2227  _twoDigitStartDate = aValue;
2228 }
2229 
2233 - (CPDateFormatterBehavior)formatterBehavior
2234 {
2235  return _formatterBehavior;
2236 }
2237 
2241 - (void)setFormatterBehavior:(CPDateFormatterBehavior)aValue
2242 {
2243  _formatterBehavior = aValue;
2244 }
2245 
2249 - (CPDateFormatterStyle)dateStyle
2250 {
2251  return _dateStyle;
2252 }
2253 
2257 - (void)setDateStyle:(CPDateFormatterStyle)aValue
2258 {
2259  _dateStyle = aValue;
2260 }
2261 
2265 - (CPDateFormatterStyle)timeStyle
2266 {
2267  return _timeStyle;
2268 }
2269 
2273 - (void)setTimeStyle:(CPDateFormatterStyle)aValue
2274 {
2275  _timeStyle = aValue;
2276 }
2277 
2281 - (CPLocale)locale
2282 {
2283  return _locale;
2284 }
2285 
2289 - (void)setLocale:(CPLocale)aValue
2290 {
2291  _locale = aValue;
2292 }
2293 
2297 - (CPString)AMSymbol
2298 {
2299  return _AMSymbol;
2300 }
2301 
2305 - (void)setAMSymbol:(CPString)aValue
2306 {
2307  _AMSymbol = aValue;
2308 }
2309 
2313 - (CPString)dateFormat
2314 {
2315  return _dateFormat;
2316 }
2317 
2321 - (void)setDateFormat:(CPString)aValue
2322 {
2323  _dateFormat = aValue;
2324 }
2325 
2329 - (CPString)PMSymbol
2330 {
2331  return _PMSymbol;
2332 }
2333 
2337 - (void)setPMSymbol:(CPString)aValue
2338 {
2339  _PMSymbol = aValue;
2340 }
2341 
2345 - (CPTimeZone)timeZone
2346 {
2347  return _timeZone;
2348 }
2349 
2353 - (void)setTimeZone:(CPTimeZone)aValue
2354 {
2355  _timeZone = aValue;
2356 }
2357 
2358 @end