API  0.9.7
 All Classes Files Functions Variables 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 @class CPNull
25 
26 @global CPLocaleLanguageCode
27 @global CPLocaleCountryCode
28 
29 CPDateFormatterNoStyle = 0;
30 CPDateFormatterShortStyle = 1;
31 CPDateFormatterMediumStyle = 2;
32 CPDateFormatterLongStyle = 3;
33 CPDateFormatterFullStyle = 4;
34 
35 CPDateFormatterBehaviorDefault = 0;
36 CPDateFormatterBehavior10_0 = 1000;
37 CPDateFormatterBehavior10_4 = 1040;
38 
39 var defaultDateFormatterBehavior = CPDateFormatterBehavior10_4,
40  relativeDateFormating,
41  patternStringTokens;
42 
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 
75  relativeDateFormating = @{
76  @"fr" : [@"demain", 1440, @"apr" + String.fromCharCode(233) + @"s-demain", 2880, @"apr" + String.fromCharCode(233) + @"s-apr" + String.fromCharCode(233) + @"s-demain", 4320, @"hier", -1440, @"avant-hier", -2880, @"avant-avant-hier", -4320],
77  @"en" : [@"tomorrow", 1440, @"yesterday", -1440],
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 {
119  return defaultDateFormatterBehavior;
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  @"en" : @{
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  @"fr" : @{},
207  @"es" : @{},
208  @"de" : @{}
209  };
210 
211  _timeZone = [CPTimeZone systemTimeZone];
212  _twoDigitStartDate = [[CPDate alloc] initWithString:@"1950-01-01 00:00:00 +0000"];
213  _locale = [CPLocale currentLocale];
214 }
215 
216 
217 #pragma mark -
218 #pragma mark Setter Getter
219 
222 - (CPString)AMSymbol
223 {
224  return [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] valueForKey:@"AMSymbol"];
225 }
226 
229 - (void)setAMSymbol:(CPString)aValue
230 {
231  [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] setValue:aValue forKey:@"AMSymbol"];
232 }
233 
236 - (CPString)PMSymbol
237 {
238  return [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] valueForKey:@"PMSymbol"];
239 }
240 
243 - (void)setPMSymbol:(CPString)aValue
244 {
245  [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] setValue:aValue forKey:@"PMSymbol"];
246 }
247 
250 - (CPArray)weekdaySymbols
251 {
252  return [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] valueForKey:@"weekdaySymbols"];
253 }
254 
257 - (void)setWeekdaySymbols:(CPArray)aValue
258 {
259  [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] setValue:aValue forKey:@"weekdaySymbols"];
260 }
261 
264 - (CPArray)shortWeekdaySymbols
265 {
266  return [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] valueForKey:@"shortWeekdaySymbols"];
267 }
268 
271 - (void)setShortWeekdaySymbols:(CPArray)aValue
272 {
273  [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] setValue:aValue forKey:@"shortWeekdaySymbols"];
274 }
275 
278 - (CPArray)veryShortWeekdaySymbols
279 {
280  return [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] valueForKey:@"veryShortWeekdaySymbols"];
281 }
282 
285 - (void)setVeryShortWeekdaySymbols:(CPArray)aValue
286 {
287  [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] setValue:aValue forKey:@"veryShortWeekdaySymbols"];
288 }
289 
292 - (CPArray)standaloneWeekdaySymbols
293 {
294  return [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] valueForKey:@"standaloneWeekdaySymbols"];
295 }
296 
299 - (void)setStandaloneWeekdaySymbols:(CPArray)aValue
300 {
301  [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] setValue:aValue forKey:@"standaloneWeekdaySymbols"];
302 }
303 
306 - (CPArray)shortStandaloneWeekdaySymbols
307 {
308  return [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] valueForKey:@"shortStandaloneWeekdaySymbols"];
309 }
310 
313 - (void)setShortStandaloneWeekdaySymbols:(CPArray)aValue
314 {
315  [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] setValue:aValue forKey:@"shortStandaloneWeekdaySymbols"];
316 }
317 
320 - (CPArray)veryShortStandaloneWeekdaySymbols
321 {
322  return [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] valueForKey:@"veryShortStandaloneWeekdaySymbols"];
323 }
324 
327 - (void)setVeryShortStandaloneWeekdaySymbols:(CPArray)aValue
328 {
329  [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] setValue:aValue forKey:@"veryShortStandaloneWeekdaySymbols"];
330 }
331 
334 - (CPArray)monthSymbols
335 {
336  return [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] valueForKey:@"monthSymbols"];
337 }
338 
341 - (void)setMonthSymbols:(CPArray)aValue
342 {
343  [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] setValue:aValue forKey:@"monthSymbols"];
344 }
345 
348 - (CPArray)shortMonthSymbols
349 {
350  return [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] valueForKey:@"shortMonthSymbols"];
351 }
352 
355 - (void)setShortMonthSymbols:(CPArray)aValue
356 {
357  [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] setValue:aValue forKey:@"shortMonthSymbols"];
358 }
359 
362 - (CPArray)veryShortMonthSymbols
363 {
364  return [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] valueForKey:@"veryShortMonthSymbols"];
365 }
366 
369 - (void)setVeryShortMonthSymbols:(CPArray)aValue
370 {
371  [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] setValue:aValue forKey:@"veryShortMonthSymbols"];
372 }
373 
376 - (CPArray)standaloneMonthSymbols
377 {
378  return [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] valueForKey:@"standaloneMonthSymbols"];
379 }
380 
383 - (void)setStandaloneMonthSymbols:(CPArray)aValue
384 {
385  [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] setValue:aValue forKey:@"standaloneMonthSymbols"];
386 }
387 
390 - (CPArray)shortStandaloneMonthSymbols
391 {
392  return [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] valueForKey:@"shortStandaloneMonthSymbols"];
393 }
394 
397 - (void)setShortStandaloneMonthSymbols:(CPArray)aValue
398 {
399  [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] setValue:aValue forKey:@"shortStandaloneMonthSymbols"];
400 }
401 
404 - (CPArray)veryShortStandaloneMonthSymbols
405 {
406  return [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] valueForKey:@"veryShortStandaloneMonthSymbols"];
407 }
408 
411 - (void)setVeryShortStandaloneMonthSymbols:(CPArray)aValue
412 {
413  [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] setValue:aValue forKey:@"veryShortStandaloneMonthSymbols"];
414 }
415 
418 - (CPArray)quarterSymbols
419 {
420  return [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] valueForKey:@"quarterSymbols"];
421 }
422 
425 - (void)setQuarterSymbols:(CPArray)aValue
426 {
427  [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] setValue:aValue forKey:@"quarterSymbols"];
428 }
429 
432 - (CPArray)shortQuarterSymbols
433 {
434  return [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] valueForKey:@"shortQuarterSymbols"];
435 }
436 
439 - (void)setShortQuarterSymbols:(CPArray)aValue
440 {
441  [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] setValue:aValue forKey:@"shortQuarterSymbols"];
442 }
443 
446 - (CPArray)standaloneQuarterSymbols
447 {
448  return [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] valueForKey:@"standaloneQuarterSymbols"];
449 }
450 
453 - (void)setStandaloneQuarterSymbols:(CPArray)aValue
454 {
455  [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] setValue:aValue forKey:@"standaloneQuarterSymbols"];
456 }
457 
460 - (CPArray)shortStandaloneQuarterSymbols
461 {
462  return [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] valueForKey:@"shortStandaloneQuarterSymbols"];
463 }
464 
467 - (void)setShortStandaloneQuarterSymbols:(CPArray)aValue
468 {
469  [[_symbols valueForKey:[_locale objectForKey:CPLocaleLanguageCode]] setValue:aValue forKey:@"shortStandaloneQuarterSymbols"];
470 }
471 
472 
473 #pragma mark -
474 #pragma mark StringFromDate methods
475 
481 - (CPString)stringFromDate:(CPDate)aDate
482 {
483  var format,
484  relativeWord,
485  result;
486 
487  if (!aDate)
488  return;
489 
490  aDate = [aDate copy];
491  [aDate _dateWithTimeZone:_timeZone];
492 
493  if (_dateFormat)
494  return [self _stringFromDate:aDate format:_dateFormat];
495 
496  switch (_dateStyle)
497  {
498  case CPDateFormatterNoStyle:
499  format = @"";
500  break;
501 
502  case CPDateFormatterShortStyle:
503  if ([self _isAmericanFormat])
504  format = @"M/d/yy";
505  else
506  format = @"dd/MM/yy";
507 
508  break;
509 
510  case CPDateFormatterMediumStyle:
511  if ([self _isAmericanFormat])
512  format = @"MMM d, Y";
513  else
514  format = @"d MMM Y";
515 
516  break;
517 
518  case CPDateFormatterLongStyle:
519  if ([self _isAmericanFormat])
520  format = @"MMMM d, Y";
521  else
522  format = @"d MMMM Y";
523 
524  break;
525 
526  case CPDateFormatterFullStyle:
527  if ([self _isAmericanFormat])
528  format = @"EEEE, MMMM d, Y";
529  else
530  format = @"EEEE d MMMM Y";
531 
532  break;
533 
534  default:
535  format = @"";
536  }
537 
538 
539  if ([self doesRelativeDateFormatting])
540  {
541  var language = [_locale objectForKey:CPLocaleLanguageCode],
542  relativeWords = [relativeDateFormating valueForKey:language];
543 
544  for (var i = 1; i < [relativeWords count]; i = i + 2)
545  {
546  var date = [CPDate date];
547  [date _dateWithTimeZone:_timeZone];
548 
549  date.setHours(aDate.getHours());
550  date.setMinutes(aDate.getMinutes());
551  date.setSeconds(aDate.getSeconds());
552 
553  date.setMinutes([relativeWords objectAtIndex:i]);
554 
555  if (date.getDate() == aDate.getDate() && date.getMonth() && aDate.getMonth() && date.getFullYear() == aDate.getFullYear())
556  {
557  relativeWord = [relativeWords objectAtIndex:(i - 1)];
558  format = @"";
559  break;
560  }
561  }
562  }
563 
564  if ((relativeWord || format.length) && _timeStyle != CPDateFormatterNoStyle)
565  format += @" ";
566 
567  switch (_timeStyle)
568  {
569  case CPDateFormatterNoStyle:
570  format += @"";
571  break;
572 
573  case CPDateFormatterShortStyle:
574  if ([self _isEnglishFormat])
575  format += @"h:mm a";
576  else
577  format += @"H:mm";
578 
579  break;
580 
581  case CPDateFormatterMediumStyle:
582  if ([self _isEnglishFormat])
583  format += @"h:mm:ss a";
584  else
585  format += @"H:mm:ss"
586 
587  break;
588 
589  case CPDateFormatterLongStyle:
590  if ([self _isEnglishFormat])
591  format += @"h:mm:ss a z";
592  else
593  format += @"H:mm:ss z";
594 
595  break;
596 
597  case CPDateFormatterFullStyle:
598  if ([self _isEnglishFormat])
599  format += @"h:mm:ss a zzzz";
600  else
601  format += @"h:mm:ss zzzz";
602 
603  break;
604 
605  default:
606  format += @"";
607  }
608 
609  result = [self _stringFromDate:aDate format:format];
610 
611  if (relativeWord)
612  result = relativeWord + result;
613 
614  return result;
615 }
616 
622 - (CPString)stringForObjectValue:(id)anObject
623 {
624  if ([anObject isKindOfClass:[CPDate class]])
625  return [self stringFromDate:anObject];
626  else
627  return nil;
628 }
629 
635 - (CPString)editingStringForObjectValue:(id)anObject
636 {
637  return [self stringForObjectValue:anObject];
638 }
639 
645 - (CPString)_stringFromDate:(CPDate)aDate format:(CPString)aFormat
646 {
647  var length = [aFormat length],
648  currentToken = [CPString new],
649  isTextToken = NO,
650  result = [CPString new];
651 
652  for (var i = 0; i < length; i++)
653  {
654  var character = [aFormat characterAtIndex:i];
655 
656  if (isTextToken)
657  {
658  if ([character isEqualToString:@"'"])
659  {
660  isTextToken = NO;
661  result += currentToken;
662  currentToken = [CPString new];
663  }
664  else
665  {
666  currentToken += character;
667  }
668 
669  continue;
670  }
671 
672  if ([character isEqualToString:@"'"])
673  {
674  if (!isTextToken)
675  {
676  isTextToken = YES;
677  result += currentToken;
678  currentToken = [CPString new];
679  }
680 
681  continue;
682  }
683 
684  if ([character isEqualToString:@","] || [character isEqualToString:@":"] || [character isEqualToString:@"/"] || [character isEqualToString:@"-"] || [character isEqualToString:@" "])
685  {
686  result += [self _stringFromToken:currentToken date:aDate];
687  result += character;
688  currentToken = [CPString new];
689  }
690  else
691  {
692  if ([currentToken length] && ![[currentToken characterAtIndex:0] isEqualToString:character])
693  {
694  result += [self _stringFromToken:currentToken date:aDate];
695  currentToken = [CPString new];
696  }
697 
698  currentToken += character;
699 
700  if (i == (length - 1))
701  result += [self _stringFromToken:currentToken date:aDate];
702  }
703  }
704 
705  return result;
706 }
707 
713 - (CPString)_stringFromToken:(CPString)aToken date:(CPDate)aDate
714 {
715  if (![aToken length])
716  return aToken;
717 
718  var character = [aToken characterAtIndex:0],
719  length = [aToken length],
720  timeZone = _timeZone;
721 
722  switch (character)
723  {
724  case @"G":
725  // TODO
726  CPLog.warn(@"Token not yet implemented " + aToken);
727  return [CPString new];
728 
729  case @"y":
730  var currentLength = [[CPString stringWithFormat:@"%i", aDate.getFullYear()] length];
731 
732  return [self _stringValueForValue:aDate.getFullYear() length:(length == 2)?length:currentLength];
733 
734  case @"Y":
735  var currentLength = [[CPString stringWithFormat:@"%i", aDate.getFullYear()] length];
736 
737  return [self _stringValueForValue:aDate.getFullYear() length:(length == 2)?length:currentLength];
738 
739  case @"u":
740  // TODO
741  CPLog.warn(@"Token not yet implemented " + aToken);
742  return [CPString new];
743 
744  case @"U":
745  // TODO
746  CPLog.warn(@"Token not yet implemented " + aToken);
747  return [CPString new];
748 
749  case @"Q":
750  var quarter = 1;
751 
752  if (aDate.getMonth() < 6 && aDate.getMonth() > 2)
753  quarter = 2;
754 
755  if (aDate.getMonth() > 5 && aDate.getMonth() < 9)
756  quarter = 3;
757 
758  if (aDate.getMonth() >= 9)
759  quarter = 4;
760 
761  if (length <= 2)
762  return [self _stringValueForValue:quarter length:MIN(2,length)];
763 
764  if (length == 3)
765  return [[self shortQuarterSymbols] objectAtIndex:(quarter - 1)];
766 
767  if (length >= 4)
768  return [[self quarterSymbols] objectAtIndex:(quarter - 1)];
769 
770  case @"q":
771  var quarter = 1;
772 
773  if (aDate.getMonth() < 6 && aDate.getMonth() > 2)
774  quarter = 2;
775 
776  if (aDate.getMonth() > 5 && aDate.getMonth() < 9)
777  quarter = 3;
778 
779  if (aDate.getMonth() >= 9)
780  quarter = 4;
781 
782  if (length <= 2)
783  return [self _stringValueForValue:quarter length:MIN(2,length)];
784 
785  if (length == 3)
786  return [[self shortStandaloneQuarterSymbols] objectAtIndex:(quarter - 1)];
787 
788  if (length >= 4)
789  return [[self standaloneQuarterSymbols] objectAtIndex:(quarter - 1)];
790 
791  case @"M":
792  var currentLength = [[CPString stringWithFormat:@"%i", aDate.getMonth() + 1] length];
793 
794  if (length <= 2)
795  return [self _stringValueForValue:(aDate.getMonth() + 1) length:MAX(currentLength,length)];
796 
797  if (length == 3)
798  return [[self shortMonthSymbols] objectAtIndex:aDate.getMonth()];
799 
800  if (length == 4)
801  return [[self monthSymbols] objectAtIndex:aDate.getMonth()];
802 
803  if (length >= 5)
804  return [[self veryShortMonthSymbols] objectAtIndex:aDate.getMonth()];
805 
806  case @"L":
807  var currentLength = [[CPString stringWithFormat:@"%i", aDate.getMonth() + 1] length];
808 
809  if (length <= 2)
810  return [self _stringValueForValue:(aDate.getMonth() + 1) length:MAX(currentLength,length)];
811 
812  if (length == 3)
813  return [[self shortStandaloneMonthSymbols] objectAtIndex:aDate.getMonth()];
814 
815  if (length == 4)
816  return [[self standaloneMonthSymbols] objectAtIndex:aDate.getMonth()];
817 
818  if (length >= 5)
819  return [[self veryShortStandaloneMonthSymbols] objectAtIndex:aDate.getMonth()];
820 
821  case @"I":
822  // Deprecated
823  CPLog.warn(@"Depreacted - Token not yet implemented " + aToken);
824  return [CPString new];
825 
826  case @"w":
827  var d = [aDate copy];
828 
829  d.setHours(0, 0, 0);
830  d.setDate(d.getDate() + 4 - (d.getDay() || 7));
831 
832  var yearStart = new Date(d.getFullYear(), 0, 1),
833  weekOfYear = Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
834 
835  return [self _stringValueForValue:(weekOfYear + 1) length:MAX(2, length)];
836 
837  case @"W":
838  var firstDay = new Date(aDate.getFullYear(), aDate.getMonth(), 1).getDay(),
839  weekOfMonth = Math.ceil((aDate.getDate() + firstDay) / 7);
840 
841  return [self _stringValueForValue:weekOfMonth length:1];
842 
843  case @"d":
844  var currentLength = [[CPString stringWithFormat:@"%i", aDate.getDate()] length];
845 
846  return [self _stringValueForValue:aDate.getDate() length:MAX(length, currentLength)];
847 
848  case @"D":
849  var oneJan = new Date(aDate.getFullYear(), 0, 1),
850  dayOfYear = Math.ceil((aDate - oneJan) / 86400000),
851  currentLength = [[CPString stringWithFormat:@"%i", dayOfYear] length];
852 
853  return [self _stringValueForValue:dayOfYear length:MAX(currentLength, MIN(3, length))];
854 
855  case @"F":
856  var dayOfWeek = 1,
857  day = aDate.getDate();
858 
859  if (day > 7 && day < 15)
860  dayOfWeek = 2;
861 
862  if (day > 14 && day < 22)
863  dayOfWeek = 3;
864 
865  if (day > 21 && day < 29)
866  dayOfWeek = 4;
867 
868  if (day > 28)
869  dayOfWeek = 5;
870 
871  return [self _stringValueForValue:dayOfWeek length:1];
872 
873  case @"g":
874  CPLog.warn(@"Token not yet implemented " + aToken);
875  return [CPString new];
876 
877  case @"E":
878  var day = aDate.getDay();
879 
880  if (length <= 3)
881  return [[self shortWeekdaySymbols] objectAtIndex:day];
882 
883  if (length == 4)
884  return [[self weekdaySymbols] objectAtIndex:day];
885 
886  if (length >= 5)
887  return [[self veryShortWeekdaySymbols] objectAtIndex:day];
888 
889  case @"e":
890  var day = aDate.getDay();
891 
892  if (length <= 2)
893  return [self _stringValueForValue:(day + 1) length:MIN(2, length)];
894 
895  if (length == 3)
896  return [[self shortWeekdaySymbols] objectAtIndex:day];
897 
898  if (length == 4)
899  return [[self weekdaySymbols] objectAtIndex:day];
900 
901  if (length >= 5)
902  return [[self veryShortWeekdaySymbols] objectAtIndex:day];
903 
904  case @"c":
905  var day = aDate.getDay();
906 
907  if (length <= 2)
908  return [self _stringValueForValue:(day + 1) length:aDate.getDay().toString().length];
909 
910  if (length == 3)
911  return [[self shortStandaloneWeekdaySymbols] objectAtIndex:day];
912 
913  if (length == 4)
914  return [[self standaloneWeekdaySymbols] objectAtIndex:day];
915 
916  if (length >= 5)
917  return [[self veryShortStandaloneWeekdaySymbols] objectAtIndex:day];
918 
919  case @"a":
920 
921  if (aDate.getHours() > 11)
922  return [self PMSymbol];
923  else
924  return [self AMSymbol];
925 
926  case @"h":
927  var hours = aDate.getHours();
928 
929  if ([self _isAmericanFormat] || [self _isEnglishFormat])
930  {
931  if (hours == 0)
932  hours = 12;
933  else if (hours > 12)
934  hours = hours - 12;
935  }
936 
937  var currentLength = [[CPString stringWithFormat:@"%i", hours] length];
938 
939  return [self _stringValueForValue:hours length:MAX(currentLength, MIN(2, length))];
940 
941  case @"H":
942  var currentLength = [[CPString stringWithFormat:@"%i", aDate.getHours()] length];
943 
944  return [self _stringValueForValue:aDate.getHours() length:MAX(currentLength, MIN(2, length))];
945 
946  case @"K":
947  var hours = aDate.getHours();
948 
949  if (hours > 12)
950  hours -= 12;
951 
952  var currentLength = [[CPString stringWithFormat:@"%i", hours] length];
953 
954  return [self _stringValueForValue:hours length:MAX(currentLength, MIN(2, length))];
955 
956  case @"k":
957  var hours = aDate.getHours();
958 
959  if (aDate.getHours() == 0)
960  hours = 24;
961 
962  var currentLength = [[CPString stringWithFormat:@"%i", hours] length];
963 
964  return [self _stringValueForValue:hours length:MAX(currentLength, MIN(2, length))];
965 
966  case @"j":
967  CPLog.warn(@"Token not yet implemented " + aToken);
968  return [CPString new];
969 
970  case @"m":
971  var currentLength = [[CPString stringWithFormat:@"%i", aDate.getMinutes()] length];
972 
973  return [self _stringValueForValue:aDate.getMinutes() length:MAX(currentLength, MIN(2, length))];
974 
975  case @"s":
976  var currentLength = [[CPString stringWithFormat:@"%i", aDate.getMinutes()] length];
977 
978  return [self _stringValueForValue:aDate.getSeconds() length:MIN(2, length)];
979 
980  case @"S":
981  return [self _stringValueForValue:aDate.getMilliseconds() length:length];
982 
983  case @"A":
984  var value = aDate.getHours() * 60 * 60 * 1000 + aDate.getMinutes() * 60 * 1000 + aDate.getSeconds() * 1000 + aDate.getMilliseconds();
985 
986  return [self _stringValueForValue:value length:value.toString().length];
987 
988  case @"z":
989  if (length <= 3)
990  return [timeZone localizedName:CPTimeZoneNameStyleShortDaylightSaving locale:_locale];
991  else
992  return [timeZone localizedName:CPTimeZoneNameStyleDaylightSaving locale:_locale];
993 
994  case @"Z":
995  var seconds = [timeZone secondsFromGMT],
996  minutes = seconds / 60,
997  hours = minutes / 60,
998  result,
999  diffMinutes = (hours - parseInt(hours)) * 100 * 60 / 100;
1000 
1001  if (length <= 3)
1002  {
1003  result = diffMinutes.toString();
1004 
1005  while ([result length] < 2)
1006  result = @"0" + result;
1007 
1008  result = ABS(parseInt(hours)) + result;
1009 
1010  while ([result length] < 4)
1011  result = @"0" + result;
1012 
1013  if (seconds > 0)
1014  result = @"+" + result;
1015  else
1016  result = @"-" + result;
1017 
1018  return result;
1019  }
1020  else if (length == 4)
1021  {
1022  result = diffMinutes.toString();
1023 
1024  while ([result length] < 2)
1025  result = @"0" + result;
1026 
1027  result = @":" + result;
1028  result = ABS(parseInt(hours)) + result;
1029 
1030  while ([result length] < 5)
1031  result = @"0" + result;
1032 
1033  if (seconds > 0)
1034  result = @"+" + result;
1035  else
1036  result = @"-" + result;
1037 
1038  return @"GMT" + result;
1039  }
1040  else
1041  {
1042  result = diffMinutes.toString();
1043 
1044  while ([result length] < 2)
1045  result = @"0" + result;
1046 
1047  result = @":" + result;
1048  result = ABS(parseInt(hours)) + result;
1049 
1050  while ([result length] < 5)
1051  result = @"0" + result;
1052 
1053  if (seconds > 0)
1054  result = @"+" + result;
1055  else
1056  result = @"-" + result;
1057 
1058  return result;
1059  }
1060 
1061  case @"v":
1062  if (length == 1)
1063  return [timeZone localizedName:CPTimeZoneNameStyleShortGeneric locale:_locale];
1064  else if (length == 4)
1065  return [timeZone localizedName:CPTimeZoneNameStyleGeneric locale:_locale];
1066 
1067  return @" ";
1068 
1069  case @"V":
1070  if (length == 1)
1071  {
1072  return [timeZone localizedName:CPTimeZoneNameStyleShortDaylightSaving locale:_locale];
1073  }
1074  else if (length == 4)
1075  {
1076  CPLog.warn(@"No pattern found for " + aToken);
1077  return @"";
1078  }
1079 
1080  return @" ";
1081 
1082  default:
1083  CPLog.warn(@"No pattern found for " + aToken);
1084  return aToken;
1085  }
1086 
1087  return [CPString new];
1088 }
1089 
1090 
1091 #pragma mark -
1092 #pragma mark datefromString
1093 
1099 - (CPDate)dateFromString:(CPString)aString
1100 {
1101  if (aString == nil)
1102  return nil;
1103 
1104  var format;
1105 
1106  if (_dateFormat != nil)
1107  return [self _dateFromString:aString format:_dateFormat];
1108 
1109  switch (_dateStyle)
1110  {
1111  case CPDateFormatterNoStyle:
1112  format = @"";
1113  break;
1114 
1115  case CPDateFormatterShortStyle:
1116  if ([self _isAmericanFormat])
1117  format = @"M/d/yy";
1118  else
1119  format = @"dd/MM/yy";
1120 
1121  break;
1122 
1123  case CPDateFormatterMediumStyle:
1124  if ([self _isAmericanFormat])
1125  format = @"MMM d, Y";
1126  else
1127  format = @"d MMM Y";
1128 
1129  break;
1130 
1131  case CPDateFormatterLongStyle:
1132  if ([self _isAmericanFormat])
1133  format = @"MMMM d, Y";
1134  else
1135  format = @"d MMMM Y";
1136 
1137  break;
1138 
1139  case CPDateFormatterFullStyle:
1140  if ([self _isAmericanFormat])
1141  format = @"EEEE, MMMM d, Y";
1142  else
1143  format = @"EEEE d MMMM Y";
1144 
1145  break;
1146 
1147  default:
1148  format = @"";
1149  }
1150 
1151  switch (_timeStyle)
1152  {
1153  case CPDateFormatterNoStyle:
1154  format += @"";
1155  break;
1156 
1157  case CPDateFormatterShortStyle:
1158  if ([self _isEnglishFormat])
1159  format += @" h:mm a";
1160  else
1161  format += @" H:mm";
1162  break;
1163 
1164  case CPDateFormatterMediumStyle:
1165  if ([self _isEnglishFormat])
1166  format += @" h:mm:ss a";
1167  else
1168  format += @" H:mm:ss"
1169  break;
1170 
1171  case CPDateFormatterLongStyle:
1172  if ([self _isEnglishFormat])
1173  format += @" h:mm:ss a z";
1174  else
1175  format += @" H:mm:ss z";
1176  break;
1177 
1178  case CPDateFormatterFullStyle:
1179  if ([self _isEnglishFormat])
1180  format += @" h:mm:ss a zzzz";
1181  else
1182  format += @" h:mm:ss zzzz";
1183  break;
1184 
1185  default:
1186  format += @"";
1187  }
1188 
1189  return [self _dateFromString:aString format:format];
1190 }
1191 
1198 - (BOOL)getObjectValue:(idRef)anObject forString:(CPString)aString errorDescription:(CPStringRef)anError
1199 {
1200  var value = [self dateFromString:aString];
1201  @deref(anObject) = value;
1202 
1203  if (!value)
1204  {
1205  @deref(anError) = @"The value \"" + aString + "\" is invalid.";
1206  return NO;
1207  }
1208 
1209  return YES;
1210 }
1211 
1217 - (CPDate)_dateFromString:(CPString)aString format:(CPString)aFormat
1218 {
1219  if (aString == nil || aFormat == nil)
1220  return nil;
1221 
1222  var currentToken = [CPString new],
1223  isTextToken = NO,
1224  tokens = [CPArray array],
1225  dateComponents = [CPArray array],
1226  patternTokens = [CPArray array];
1227 
1228  for (var i = 0; i < [aFormat length]; i++)
1229  {
1230  var character = [aFormat characterAtIndex:i];
1231 
1232  if (isTextToken)
1233  {
1234  if ([character isEqualToString:@"'"])
1235  currentToken = [CPString new];
1236 
1237  continue;
1238  }
1239 
1240  if ([character isEqualToString:@"'"])
1241  {
1242  if (!isTextToken)
1243  isTextToken = YES;
1244 
1245  continue;
1246  }
1247 
1248  if ([character isEqualToString:@","] || [character isEqualToString:@":"] || [character isEqualToString:@"/"] || [character isEqualToString:@"-"] || [character isEqualToString:@" "])
1249  {
1250  [tokens addObject:currentToken];
1251 
1252  if ([patternStringTokens containsObject:currentToken])
1253  [patternTokens addObject:[tokens count] - 1];
1254 
1255  currentToken = [CPString new];
1256  }
1257  else
1258  {
1259  if ([currentToken length] && ![[currentToken characterAtIndex:0] isEqualToString:character])
1260  {
1261  [tokens addObject:currentToken];
1262 
1263  if ([patternStringTokens containsObject:currentToken])
1264  [patternTokens addObject:[tokens count] - 1];
1265 
1266  currentToken = [CPString new];
1267  }
1268 
1269  currentToken += character;
1270 
1271  if (i == ([aFormat length] - 1))
1272  {
1273  [tokens addObject:currentToken];
1274 
1275  if ([patternStringTokens containsObject:currentToken])
1276  [patternTokens addObject:[tokens count] - 1];
1277  }
1278  }
1279  }
1280 
1281  isTextToken = NO;
1282  currentToken = [CPString new];
1283 
1284  var currentIndexSpecialPattern = 0;
1285 
1286  if ([patternTokens count] == 0)
1287  [patternTokens addObject:CPNotFound];
1288 
1289  for (var i = 0; i < [aString length]; i++)
1290  {
1291  var character = [aString characterAtIndex:i];
1292 
1293  if (isTextToken)
1294  {
1295  if ([character isEqualToString:@"'"])
1296  currentToken = [CPString new];
1297 
1298  continue;
1299  }
1300 
1301  if ([character isEqualToString:@"'"])
1302  {
1303  if (!isTextToken)
1304  isTextToken = YES;
1305 
1306  continue;
1307  }
1308 
1309  // Need to do this to check if the word match with the token. We can get some words with space...
1310  if ([dateComponents count] == [patternTokens objectAtIndex:currentIndexSpecialPattern])
1311  {
1312  var j = [self _lastIndexMatchedString:aString token:[tokens objectAtIndex:[dateComponents count]] index:i];
1313 
1314  if (j == CPNotFound)
1315  return nil;
1316 
1317  currentIndexSpecialPattern++;
1318  [dateComponents addObject:[aString substringWithRange:CPMakeRange(i, (j - i))]];
1319  i = j;
1320 
1321  continue;
1322  }
1323 
1324  if ([character isEqualToString:@","] || [character isEqualToString:@":"] || [character isEqualToString:@"/"] || [character isEqualToString:@"-"] || [character isEqualToString:@" "])
1325  {
1326  [dateComponents addObject:currentToken];
1327  currentToken = [CPString new];
1328  }
1329  else
1330  {
1331  currentToken += character;
1332 
1333  if (i == ([aString length] - 1))
1334  [dateComponents addObject:currentToken];
1335  }
1336  }
1337 
1338  if ([dateComponents count] != [tokens count])
1339  return nil;
1340 
1341  return [self _dateFromTokens:tokens dateComponents:dateComponents];
1342 }
1343 
1344 - (CPDate)_dateFromTokens:(CPArray)tokens dateComponents:(CPArray)dateComponents
1345 {
1346  var timeZoneseconds = [_timeZone secondsFromGMT],
1347  dateArray = [2000, 01, 01, 00, 00, 00, @"+0000"],
1348  isPM = NO,
1349  dayOfYear,
1350  dayIndexInWeek,
1351  weekOfYear,
1352  weekOfMonth;
1353 
1354  for (var i = 0; i < [tokens count]; i++)
1355  {
1356  var token = [tokens objectAtIndex:i],
1357  dateComponent = [dateComponents objectAtIndex:i],
1358  character = [token characterAtIndex:0],
1359  length = [token length];
1360 
1361  switch (character)
1362  {
1363  case @"G":
1364  // TODO
1365  CPLog.warn(@"Token not yet implemented " + token);
1366  break;
1367 
1368  case @"y":
1369  var u = _twoDigitStartDate.getFullYear() % 10,
1370  d = parseInt(_twoDigitStartDate.getFullYear() / 10) % 10,
1371  c = parseInt(_twoDigitStartDate.getFullYear() / 100) % 10,
1372  m = parseInt(_twoDigitStartDate.getFullYear() / 1000) % 10;
1373 
1374  if (length == 2 && dateComponent.length == 2)
1375  {
1376  if ((u + d * 10) >= parseInt(dateComponent))
1377  dateArray[0] = (c + 1) * 100 + m * 1000 + parseInt(dateComponent);
1378  else
1379  dateArray[0] = c * 100 + m * 1000 + parseInt(dateComponent);
1380  }
1381  else
1382  {
1383  dateArray[0] = parseInt(dateComponent);
1384  }
1385 
1386  break;
1387 
1388  case @"Y":
1389  var u = _twoDigitStartDate.getFullYear() % 10,
1390  d = parseInt(_twoDigitStartDate.getFullYear() / 10) % 10,
1391  c = parseInt(_twoDigitStartDate.getFullYear() / 100) % 10,
1392  m = parseInt(_twoDigitStartDate.getFullYear() / 1000) % 10;
1393 
1394  if (length == 2 && dateComponent.length == 2)
1395  {
1396  if ((u + d * 10) >= parseInt(dateComponent))
1397  dateArray[0] = (c + 1) * 100 + m * 1000 + parseInt(dateComponent);
1398  else
1399  dateArray[0] = c * 100 + m * 1000 + parseInt(dateComponent);
1400  }
1401  else
1402  {
1403  dateArray[0] = parseInt(dateComponent);
1404  }
1405 
1406  break;
1407 
1408  case @"u":
1409  // TODO
1410  CPLog.warn(@"Token not yet implemented " + token);
1411  break;
1412 
1413  case @"U":
1414  // TODO
1415  CPLog.warn(@"Token not yet implemented " + token);
1416  break;
1417 
1418  case @"Q":
1419  var month;
1420 
1421  if (length <= 2)
1422  month = (parseInt(dateComponent) - 1) * 3;
1423 
1424  if (length == 3)
1425  {
1426  if (![[self shortQuarterSymbols] containsObject:dateComponent])
1427  return nil;
1428 
1429  month = [[self shortQuarterSymbols] indexOfObject:dateComponent] * 3;
1430  }
1431 
1432  if (length >= 4)
1433  {
1434  if (![[self quarterSymbols] containsObject:dateComponent])
1435  return nil;
1436 
1437  month = [[self quarterSymbols] indexOfObject:dateComponent] * 3;
1438  }
1439 
1440  if (month > 11)
1441  return nil;
1442 
1443  dateArray[1] = month + 1;
1444  break;
1445 
1446  case @"q":
1447  var month;
1448 
1449  if (length <= 2)
1450  month = (parseInt(dateComponent) - 1) * 3;
1451 
1452  if (length == 3)
1453  {
1454  if (![[self shortQuarterSymbols] containsObject:dateComponent])
1455  return nil;
1456 
1457  month = [[self shortQuarterSymbols] indexOfObject:dateComponent] * 3;
1458  }
1459 
1460  if (length >= 4)
1461  {
1462  if (![[self quarterSymbols] containsObject:dateComponent])
1463  return nil;
1464 
1465  month = [[self quarterSymbols] indexOfObject:dateComponent] * 3;
1466  }
1467 
1468  if (month > 11)
1469  return nil;
1470 
1471  dateArray[1] = month + 1;
1472  break;
1473 
1474  case @"M":
1475  var month;
1476 
1477  if (length <= 2)
1478  month = parseInt(dateComponent)
1479 
1480  if (length == 3)
1481  {
1482  if (![[self shortMonthSymbols] containsObject:dateComponent])
1483  return nil;
1484 
1485  month = [[self shortMonthSymbols] indexOfObject:dateComponent] + 1;
1486  }
1487 
1488  if (length == 4)
1489  {
1490  if (![[self monthSymbols] containsObject:dateComponent])
1491  return nil;
1492 
1493  month = [[self monthSymbols] indexOfObject:dateComponent] + 1;
1494  }
1495 
1496  if (month > 11 || length >= 5)
1497  return nil;
1498 
1499  dateArray[1] = month;
1500  break;
1501 
1502  case @"L":
1503  var month;
1504 
1505  if (length <= 2)
1506  month = parseInt(dateComponent);
1507 
1508  if (length == 3)
1509  {
1510  if (![[self shortStandaloneMonthSymbols] containsObject:dateComponent])
1511  return nil;
1512 
1513  month = [[self shortStandaloneMonthSymbols] indexOfObject:dateComponent] + 1;
1514  }
1515 
1516  if (length == 4)
1517  {
1518  if (![[self standaloneMonthSymbols] containsObject:dateComponent])
1519  return nil;
1520 
1521  month = [[self standaloneMonthSymbols] indexOfObject:dateComponent] + 1;
1522  }
1523 
1524  if (month > 11 || length >= 5)
1525  return nil;
1526 
1527  dateArray[1] = month;
1528  break;
1529 
1530  case @"I":
1531  // Deprecated
1532  CPLog.warn(@"Depreacted - Token not yet implemented " + token);
1533  break;
1534 
1535  case @"w":
1536  if (dateComponent > 52)
1537  return nil;
1538 
1539  weekOfYear = dateComponent;
1540  break;
1541 
1542  case @"W":
1543  if (dateComponent > 52)
1544  return nil;
1545 
1546  weekOfMonth = dateComponent;
1547  break;
1548 
1549  case @"d":
1550  dateArray[2] = parseInt(dateComponent);
1551  break;
1552 
1553  case @"D":
1554  if (isNaN(parseInt(dateComponent)) || parseInt(dateComponent) > 345)
1555  return nil;
1556 
1557  dayOfYear = parseInt(dateComponent);
1558  break;
1559 
1560  case @"F":
1561  if (isNaN(parseInt(dateComponent)) || parseInt(dateComponent) > 5 || parseInt(dateComponent) == 0)
1562  return nil;
1563 
1564  if (parseInt(dateComponent) == 1)
1565  dateArray[2] = 1;
1566 
1567  if (parseInt(dateComponent) == 2)
1568  dateArray[2] = 8;
1569 
1570  if (parseInt(dateComponent) == 3)
1571  dateArray[2] = 15;
1572 
1573  if (parseInt(dateComponent) == 4)
1574  dateArray[2] = 22;
1575 
1576  if (parseInt(dateComponent) == 5)
1577  dateArray[2] = 29;
1578 
1579  break;
1580 
1581  case @"g":
1582  CPLog.warn(@"Token not yet implemented " + token);
1583  break;
1584 
1585  case @"E":
1586  if (length <= 3)
1587  dayIndexInWeek = [[self shortWeekdaySymbols] indexOfObject:dateComponent];
1588 
1589  if (length == 4)
1590  dayIndexInWeek = [[self weekdaySymbols] indexOfObject:dateComponent];
1591 
1592  if (dayIndexInWeek == CPNotFound || length >= 5)
1593  return nil;
1594 
1595  break;
1596 
1597  case @"e":
1598  if (length <= 2 && isNaN(parseInt(dateComponent)))
1599  return nil;
1600 
1601  if (length <= 2)
1602  dayIndexInWeek = parseInt(dateComponent);
1603 
1604  if (length == 3)
1605  dayIndexInWeek = [[self shortWeekdaySymbols] indexOfObject:dateComponent];
1606 
1607  if (length == 4)
1608  dayIndexInWeek = [[self weekdaySymbols] indexOfObject:dateComponent];
1609 
1610  if (dayIndexInWeek == CPNotFound || length >= 5)
1611  return nil;
1612 
1613  break;
1614 
1615  case @"c":
1616  if (length <= 2 && isNaN(parseInt(dateComponent)))
1617  return nil;
1618 
1619  if (length <= 2)
1620  dayIndexInWeek = dateComponent;
1621 
1622  if (length == 3)
1623  dayIndexInWeek = [[self shortStandaloneWeekdaySymbols] indexOfObject:dateComponent];
1624 
1625  if (length == 4)
1626  dayIndexInWeek = [[self standaloneWeekdaySymbols] indexOfObject:dateComponent];
1627 
1628  if (length == 5)
1629  dayIndexInWeek = [[self veryShortStandaloneWeekdaySymbols] indexOfObject:dateComponent];
1630 
1631  if (dayIndexInWeek == CPNotFound || length >= 5)
1632  return nil;
1633 
1634  break;
1635 
1636  case @"a":
1637  if (![dateComponent isEqualToString:[self PMSymbol]] && ![dateComponent isEqualToString:[self AMSymbol]])
1638  return nil;
1639 
1640  if ([dateComponent isEqualToString:[self PMSymbol]])
1641  isPM = YES;
1642 
1643  break;
1644 
1645  case @"h":
1646  if (parseInt(dateComponent) < 0 || parseInt(dateComponent) > 12)
1647  return nil;
1648 
1649  dateArray[3] = parseInt(dateComponent);
1650  break;
1651 
1652  case @"H":
1653  if (parseInt(dateComponent) < 0 || parseInt(dateComponent) > 23)
1654  return nil;
1655 
1656  dateArray[3] = parseInt(dateComponent);
1657  break;
1658 
1659  case @"K":
1660  if (parseInt(dateComponent) < 0 || parseInt(dateComponent) > 11)
1661  return nil;
1662 
1663  dateArray[3] = parseInt(dateComponent);
1664  break;
1665 
1666  case @"k":
1667  if (parseInt(dateComponent) < 0 || parseInt(dateComponent) > 12)
1668  return nil;
1669 
1670  dateArray[3] = parseInt(dateComponent);
1671  break;
1672 
1673  case @"j":
1674  CPLog.warn(@"Token not yet implemented " + token);
1675  break;
1676 
1677  case @"m":
1678  var minutes = parseInt(dateComponent);
1679 
1680  if (minutes > 59)
1681  return nil;
1682 
1683  dateArray[4] = minutes;
1684  break;
1685 
1686  case @"s":
1687  var seconds = parseInt(dateComponent);
1688 
1689  if (seconds > 59)
1690  return nil;
1691 
1692  dateArray[5] = seconds;
1693  break;
1694 
1695  case @"S":
1696  if (isNaN(parseInt(dateComponent)))
1697  return nil;
1698 
1699  break;
1700 
1701  case @"A":
1702  if (isNaN(parseInt(dateComponent)))
1703  return nil;
1704 
1705  var millisecondsInDay = parseInt(dateComponent),
1706  tmpDate = new Date();
1707 
1708  tmpDate.setHours(0);
1709  tmpDate.setMinutes(0);
1710  tmpDate.setSeconds(0);
1711  tmpDate.setMilliseconds(0);
1712 
1713  tmpDate.setMilliseconds(millisecondsInDay);
1714 
1715  dateArray[3] = tmpDate.getHours();
1716  dateArray[4] = tmpDate.getMinutes();
1717  dateArray[5] = tmpDate.getSeconds();
1718  break;
1719 
1720  case @"z":
1721  if (length < 4)
1722  timeZoneseconds = [self _secondsFromTimeZoneString:dateComponent style:CPTimeZoneNameStyleShortDaylightSaving];
1723  else
1724  timeZoneseconds = [self _secondsFromTimeZoneString:dateComponent style:CPTimeZoneNameStyleDaylightSaving];
1725 
1726  if (!timeZoneseconds)
1727  timeZoneseconds = [self _secondsFromTimeZoneDefaultFormatString:dateComponent];
1728 
1729  if (!timeZoneseconds)
1730  return nil;
1731 
1732  timeZoneseconds = timeZoneseconds + 60 * 60;
1733 
1734  break;
1735 
1736  case @"Z":
1737  timeZoneseconds = [self _secondsFromTimeZoneDefaultFormatString:dateComponent];
1738 
1739  if (!timeZoneseconds)
1740  return nil;
1741 
1742  timeZoneseconds = timeZoneseconds + 60 * 60;
1743 
1744  break;
1745 
1746  case @"v":
1747  if (length <= 3)
1748  timeZoneseconds = [self _secondsFromTimeZoneString:dateComponent style:CPTimeZoneNameStyleShortGeneric];
1749  else
1750  timeZoneseconds = [self _secondsFromTimeZoneString:dateComponent style:CPTimeZoneNameStyleGeneric];
1751 
1752  if (!timeZoneseconds && length == 4)
1753  timeZoneseconds = [self _secondsFromTimeZoneDefaultFormatString:dateComponent];
1754 
1755  if (!timeZoneseconds)
1756  return nil;
1757 
1758  timeZoneseconds = timeZoneseconds + 60 * 60;
1759 
1760  break;
1761 
1762  case @"V":
1763  if (length <= 3)
1764  timeZoneseconds = [self _secondsFromTimeZoneString:dateComponent style:CPTimeZoneNameStyleShortStandard];
1765  else
1766  timeZoneseconds = [self _secondsFromTimeZoneString:dateComponent style:CPTimeZoneNameStyleStandard];
1767 
1768  if (!timeZoneseconds)
1769  timeZoneseconds = [self _secondsFromTimeZoneDefaultFormatString:dateComponent];
1770 
1771  if (!timeZoneseconds)
1772  return nil;
1773 
1774  timeZoneseconds = timeZoneseconds + 60 * 60;
1775 
1776  break;
1777 
1778  default:
1779  CPLog.warn(@"No pattern found for " + token);
1780  return nil;
1781  }
1782  }
1783 
1784  // Make the calcul day of the year
1785  if (dayOfYear)
1786  {
1787  var tmpDate = new Date();
1788  tmpDate.setFullYear(dateArray[0]);
1789  tmpDate.setMonth(0);
1790 
1791  tmpDate.setDate(dayOfYear)
1792 
1793  dateArray[1] = tmpDate.getMonth() + 1;
1794  dateArray[2] = tmpDate.getDate();
1795  }
1796 
1797  if (weekOfMonth)
1798  dateArray[2] = (weekOfMonth - 1) * 7 + 1;
1799 
1800  if (weekOfYear)
1801  {
1802  var tmpDate = new Date();
1803  tmpDate.setFullYear(dateArray[0]);
1804  tmpDate.setMonth(0);
1805  tmpDate.setDate(1);
1806 
1807  while (tmpDate.getDay() != 0)
1808  tmpDate.setDate(tmpDate.getDate() + 1);
1809 
1810  tmpDate.setDate(tmpDate.getDate() + (weekOfYear - 1) * 7);
1811 
1812  dateArray[1] = tmpDate.getMonth() + 1;
1813  dateArray[2] = tmpDate.getDate() - 1;
1814  }
1815 
1816  // Check if the day is possible in the current month
1817  var tmpDate = new Date();
1818  tmpDate.setMonth(dateArray[1] - 1);
1819  tmpDate.setFullYear(dateArray[0]);
1820 
1821  if (dateArray[2] <= 0 || dateArray[2] > [tmpDate _daysInMonth])
1822  return nil;
1823 
1824  // PM hours
1825  if (isPM)
1826  dateArray[3] += 12;
1827 
1828  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])))
1829  return nil;
1830 
1831  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]]];
1832  dateResult.setSeconds(dateResult.getSeconds() - timeZoneseconds + 60 * 60);
1833 
1834  return dateResult;
1835 }
1836 
1837 
1838 #pragma mark -
1839 #pragma mark Utils
1840 
1841 - (CPString)_stringValueForValue:(id)aValue length:(int)length
1842 {
1843  var string = [CPString stringWithFormat:@"%i", aValue];
1844 
1845  if ([string length] == length)
1846  return string;
1847 
1848  if ([string length] > length)
1849  return [string substringFromIndex:([string length] - length)];
1850 
1851  while ([string length] < length)
1852  string = [CPString stringWithFormat:@"0%s", string];
1853 
1854  return string;
1855 }
1856 
1859 - (BOOL)_isAmericanFormat
1860 {
1861  return [[_locale objectForKey:CPLocaleCountryCode] isEqualToString:@"US"];
1862 }
1863 
1866 - (BOOL)_isEnglishFormat
1867 {
1868  return [[_locale objectForKey:CPLocaleLanguageCode] isEqualToString:@"en"];
1869 }
1870 
1873 - (int)_secondsFromTimeZoneDefaultFormatString:(CPString)aTimeZoneFormatString
1874 {
1875  var format = /\w*([HPG-GMT])?([+-])(\d{1,2})([:])?(\d{2})\w*/,
1876  result = aTimeZoneFormatString.match(new RegExp(format)),
1877  seconds = 0;
1878 
1879  if (!result)
1880  return nil;
1881 
1882  seconds = result[3] * 60 * 60 + result[5] * 60;
1883 
1884  if ([result[2] isEqualToString:@"-"])
1885  seconds = -seconds;
1886 
1887  return seconds;
1888 }
1889 
1892 - (int)_secondsFromTimeZoneString:(CPString)aTimeZoneString style:(NSTimeZoneNameStyle)aStyle
1893 {
1894  var timeZone = [CPTimeZone _timeZoneFromString:aTimeZoneString style:aStyle locale:_locale];
1895 
1896  if (!timeZone)
1897  return nil;
1898 
1899  return [timeZone secondsFromGMT];
1900 }
1901 
1908 - (int)_lastIndexMatchedString:(CPString)aString token:(CPString)aToken index:anIndex
1909 {
1910  var character = [aToken characterAtIndex:0],
1911  length = [aToken length],
1912  targetedArray,
1913  format = /\w*([HPG-GMT])?([+-])(\d{1,2})([:])?(\d{2})\w*/,
1914  result = aString.match(new RegExp(format));
1915 
1916  switch (character)
1917  {
1918  case @"Q":
1919  if (length == 3)
1920  targetedArray = [self shortQuarterSymbols];
1921 
1922  if (length >= 4)
1923  targetedArray = [self quarterSymbols];
1924 
1925  break;
1926 
1927  case @"q":
1928  if (length == 3)
1929  targetedArray = [self shortStandaloneQuarterSymbols];
1930 
1931  if (length >= 4)
1932  targetedArray = [self standaloneQuarterSymbols];
1933 
1934  break;
1935 
1936  case @"M":
1937  if (length == 3)
1938  targetedArray = [self shortMonthSymbols];
1939 
1940  if (length == 4)
1941  targetedArray = [self monthSymbols];
1942 
1943  if (length >= 5)
1944  targetedArray = [self veryShortMonthSymbols];
1945 
1946  break;
1947 
1948  case @"L":
1949  if (length == 3)
1950  targetedArray = [self shortStandaloneMonthSymbols];
1951 
1952  if (length == 4)
1953  targetedArray = [self standaloneMonthSymbols];
1954 
1955  if (length >= 5)
1956  targetedArray = [self veryShortStandaloneMonthSymbols];
1957 
1958  break;
1959 
1960  case @"E":
1961  if (length <= 3)
1962  targetedArray = [self shortWeekdaySymbols];
1963 
1964  if (length == 4)
1965  targetedArray = [self weekdaySymbols];
1966 
1967  if (length >= 5)
1968  targetedArray = [self veryShortWeekdaySymbols];
1969 
1970  break;
1971 
1972  case @"e":
1973  if (length == 3)
1974  targetedArray = [self shortWeekdaySymbols];
1975 
1976  if (length == 4)
1977  targetedArray = [self weekdaySymbols];
1978 
1979  if (length >= 5)
1980  targetedArray = [self veryShortWeekdaySymbols];
1981 
1982  break;
1983 
1984  case @"c":
1985  if (length == 3)
1986  targetedArray = [self shortStandaloneWeekdaySymbols];
1987 
1988  if (length == 4)
1989  targetedArray = [self standaloneWeekdaySymbols];
1990 
1991  if (length >= 5)
1992  targetedArray = [self veryShortStandaloneWeekdaySymbols];
1993 
1994  break;
1995 
1996  case @"a":
1997  targetedArray = [[self PMSymbol], [self AMSymbol]];
1998  break;
1999 
2000  case @"z":
2001  if (length <= 3)
2002  targetedArray = [CPTimeZone _namesForStyle:CPTimeZoneNameStyleShortDaylightSaving locale:_locale];
2003  else
2004  targetedArray = [CPTimeZone _namesForStyle:CPTimeZoneNameStyleDaylightSaving locale:_locale];
2005 
2006  if (result)
2007  return anIndex + [result objectAtIndex:0].length;
2008 
2009  break;
2010 
2011  case @"Z":
2012  if (result)
2013  return anIndex + [result objectAtIndex:0].length;
2014 
2015  return CPNotFound;
2016 
2017  case @"v":
2018  if (length == 1)
2019  targetedArray = [CPTimeZone _namesForStyle:CPTimeZoneNameStyleShortGeneric locale:_locale];
2020  else if (length == 4)
2021  targetedArray = [CPTimeZone _namesForStyle:CPTimeZoneNameStyleGeneric locale:_locale];
2022 
2023  if (result)
2024  return anIndex + [result objectAtIndex:0].length;
2025 
2026  break;
2027 
2028  case @"V":
2029  if (length == 1)
2030  targetedArray = [CPTimeZone _namesForStyle:CPTimeZoneNameStyleShortStandard locale:_locale];
2031 
2032  if (result)
2033  return anIndex + [result objectAtIndex:0].length;
2034 
2035  break;
2036 
2037  default:
2038  CPLog.warn(@"No pattern found for " + aToken);
2039  return CPNotFound;
2040  }
2041 
2042  for (var i = 0; i < [targetedArray count]; i++)
2043  {
2044  var currentObject = [targetedArray objectAtIndex:i],
2045  range = [aString rangeOfString:currentObject];
2046 
2047  if (range.length == 0)
2048  continue;
2049 
2050  character = [aString characterAtIndex:(anIndex + range.length)];
2051 
2052  if ([character isEqualToString:@"'"] || [character isEqualToString:@","] || [character isEqualToString:@":"] || [character isEqualToString:@"/"] || [character isEqualToString:@"-"] || [character isEqualToString:@" "] || [character isEqualToString:@""])
2053  return anIndex + range.length;
2054  }
2055 
2056  return CPNotFound;
2057 }
2058 
2059 @end
2060 
2061 var CPDateFormatterDateStyleKey = @"CPDateFormatterDateStyle",
2062  CPDateFormatterTimeStyleKey = @"CPDateFormatterTimeStyleKey",
2063  CPDateFormatterFormatterBehaviorKey = @"CPDateFormatterFormatterBehaviorKey",
2064  CPDateFormatterDoseRelativeDateFormattingKey = @"CPDateFormatterDoseRelativeDateFormattingKey",
2065  CPDateFormatterDateFormatKey = @"CPDateFormatterDateFormatKey",
2066  CPDateFormatterAllowNaturalLanguageKey = @"CPDateFormatterAllowNaturalLanguageKey",
2067  CPDateFormatterLocaleKey = @"CPDateFormatterLocaleKey";
2068 
2069 @implementation CPDateFormatter (CPCoding)
2070 
2071 - (id)initWithCoder:(CPCoder)aCoder
2072 {
2073  self = [super initWithCoder:aCoder];
2074 
2075  if (self)
2076  {
2077  _allowNaturalLanguage = [aCoder decodeBoolForKey:CPDateFormatterAllowNaturalLanguageKey];
2078  _dateFormat = [aCoder decodeObjectForKey:CPDateFormatterDateFormatKey];
2079  _dateStyle = [aCoder decodeIntForKey:CPDateFormatterDateStyleKey];
2080  _doesRelativeDateFormatting = [aCoder decodeBoolForKey:CPDateFormatterDoseRelativeDateFormattingKey];
2081  _formatterBehavior = [aCoder decodeIntForKey:CPDateFormatterFormatterBehaviorKey];
2082  _locale = [aCoder decodeObjectForKey:CPDateFormatterLocaleKey];
2083  _timeStyle = [aCoder decodeIntForKey:CPDateFormatterTimeStyleKey];
2084  }
2085 
2086  [self _init];
2087 
2088  return self;
2089 }
2090 
2091 - (void)encodeWithCoder:(CPCoder)aCoder
2092 {
2093  [super encodeWithCoder:aCoder];
2094 
2095  [aCoder encodeBool:_allowNaturalLanguage forKey:CPDateFormatterAllowNaturalLanguageKey];
2096  [aCoder encodeInt:_dateStyle forKey:CPDateFormatterDateStyleKey];
2097  [aCoder encodeObject:_dateFormat forKey:CPDateFormatterDateFormatKey];
2098  [aCoder encodeBool:_doesRelativeDateFormatting forKey:CPDateFormatterDoseRelativeDateFormattingKey];
2099  [aCoder encodeInt:_formatterBehavior forKey:CPDateFormatterFormatterBehaviorKey];
2100  [aCoder encodeInt:_locale forKey:CPDateFormatterLocaleKey];
2101  [aCoder encodeInt:_timeStyle forKey:CPDateFormatterTimeStyleKey];
2102 }
2103 
2104 @end
2105 
2106 
2107 @implementation CPDate (CPTimeZone)
2108 
2111 - (void)_dateWithTimeZone:(CPTimeZone)aTimeZone
2112 {
2113  self.setSeconds(self.getSeconds() - [aTimeZone secondsFromGMTForDate:self]);
2114  self.setSeconds(self.getSeconds() + [aTimeZone secondsFromGMT]);
2115 }
2116 
2117 @end
2118 
2119 @implementation CPDateFormatter (CPSynthesizedAccessors)
2120 
2124 - (BOOL)allowNaturalLanguage
2125 {
2126  return _allowNaturalLanguage;
2127 }
2128 
2132 - (BOOL)doesRelativeDateFormatting
2133 {
2134  return _doesRelativeDateFormatting;
2135 }
2136 
2140 - (void)setDoesRelativeDateFormatting:(BOOL)aValue
2141 {
2142  _doesRelativeDateFormatting = aValue;
2143 }
2144 
2148 - (CPDate)defaultDate
2149 {
2150  return _defaultDate;
2151 }
2152 
2156 - (void)setDefaultDate:(CPDate)aValue
2157 {
2158  _defaultDate = aValue;
2159 }
2160 
2164 - (CPDate)twoDigitStartDate
2165 {
2166  return _twoDigitStartDate;
2167 }
2168 
2172 - (void)setTwoDigitStartDate:(CPDate)aValue
2173 {
2174  _twoDigitStartDate = aValue;
2175 }
2176 
2180 - (CPDateFormatterBehavior)formatterBehavior
2181 {
2182  return _formatterBehavior;
2183 }
2184 
2188 - (void)setFormatterBehavior:(CPDateFormatterBehavior)aValue
2189 {
2190  _formatterBehavior = aValue;
2191 }
2192 
2196 - (CPDateFormatterStyle)dateStyle
2197 {
2198  return _dateStyle;
2199 }
2200 
2204 - (void)setDateStyle:(CPDateFormatterStyle)aValue
2205 {
2206  _dateStyle = aValue;
2207 }
2208 
2212 - (CPDateFormatterStyle)timeStyle
2213 {
2214  return _timeStyle;
2215 }
2216 
2220 - (void)setTimeStyle:(CPDateFormatterStyle)aValue
2221 {
2222  _timeStyle = aValue;
2223 }
2224 
2228 - (CPLocale)locale
2229 {
2230  return _locale;
2231 }
2232 
2236 - (void)setLocale:(CPLocale)aValue
2237 {
2238  _locale = aValue;
2239 }
2240 
2244 - (CPString)AMSymbol
2245 {
2246  return _AMSymbol;
2247 }
2248 
2252 - (void)setAMSymbol:(CPString)aValue
2253 {
2254  _AMSymbol = aValue;
2255 }
2256 
2260 - (CPString)dateFormat
2261 {
2262  return _dateFormat;
2263 }
2264 
2268 - (void)setDateFormat:(CPString)aValue
2269 {
2270  _dateFormat = aValue;
2271 }
2272 
2276 - (CPString)PMSymbol
2277 {
2278  return _PMSymbol;
2279 }
2280 
2284 - (void)setPMSymbol:(CPString)aValue
2285 {
2286  _PMSymbol = aValue;
2287 }
2288 
2292 - (CPTimeZone)timeZone
2293 {
2294  return _timeZone;
2295 }
2296 
2300 - (void)setTimeZone:(CPTimeZone)aValue
2301 {
2302  _timeZone = aValue;
2303 }
2304 
2305 @end