OpenCores
URL https://opencores.org/ocsvn/scarts/scarts/trunk

Subversion Repositories scarts

[/] [scarts/] [trunk/] [toolchain/] [scarts-gcc/] [gcc-4.1.1/] [libjava/] [classpath/] [java/] [text/] [SimpleDateFormat.java] - Blame information for rev 14

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 14 jlechner
/* SimpleDateFormat.java -- A class for parsing/formating simple
2
   date constructs
3
   Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005
4
   Free Software Foundation, Inc.
5
 
6
This file is part of GNU Classpath.
7
 
8
GNU Classpath is free software; you can redistribute it and/or modify
9
it under the terms of the GNU General Public License as published by
10
the Free Software Foundation; either version 2, or (at your option)
11
any later version.
12
 
13
GNU Classpath is distributed in the hope that it will be useful, but
14
WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
General Public License for more details.
17
 
18
You should have received a copy of the GNU General Public License
19
along with GNU Classpath; see the file COPYING.  If not, write to the
20
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21
02110-1301 USA.
22
 
23
Linking this library statically or dynamically with other modules is
24
making a combined work based on this library.  Thus, the terms and
25
conditions of the GNU General Public License cover the whole
26
combination.
27
 
28
As a special exception, the copyright holders of this library give you
29
permission to link this library with independent modules to produce an
30
executable, regardless of the license terms of these independent
31
modules, and to copy and distribute the resulting executable under
32
terms of your choice, provided that you also meet, for each linked
33
independent module, the terms and conditions of the license of that
34
module.  An independent module is a module which is not derived from
35
or based on this library.  If you modify this library, you may extend
36
this exception to your version of the library, but you are not
37
obligated to do so.  If you do not wish to do so, delete this
38
exception statement from your version. */
39
 
40
 
41
package java.text;
42
 
43
import gnu.java.text.AttributedFormatBuffer;
44
import gnu.java.text.FormatBuffer;
45
import gnu.java.text.FormatCharacterIterator;
46
import gnu.java.text.StringFormatBuffer;
47
 
48
import java.io.IOException;
49
import java.io.InvalidObjectException;
50
import java.io.ObjectInputStream;
51
import java.util.ArrayList;
52
import java.util.Calendar;
53
import java.util.Date;
54
import java.util.GregorianCalendar;
55
import java.util.Iterator;
56
import java.util.Locale;
57
import java.util.TimeZone;
58
import java.util.regex.Matcher;
59
import java.util.regex.Pattern;
60
 
61
/**
62
 * SimpleDateFormat provides convenient methods for parsing and formatting
63
 * dates using Gregorian calendars (see java.util.GregorianCalendar).
64
 */
65
public class SimpleDateFormat extends DateFormat
66
{
67
  /**
68
   * This class is used by <code>SimpleDateFormat</code> as a
69
   * compiled representation of a format string.  The field
70
   * ID, size, and character used are stored for each sequence
71
   * of pattern characters.
72
   */
73
  private class CompiledField
74
  {
75
    /**
76
     * The ID of the field within the local pattern characters.
77
     * Package private for use in out class.
78
     */
79
    int field;
80
 
81
    /**
82
     * The size of the character sequence.
83
     * Package private for use in out class.
84
     */
85
    int size;
86
 
87
    /**
88
     * The character used.
89
     */
90
    private char character;
91
 
92
    /**
93
     * Constructs a compiled field using the
94
     * the given field ID, size and character
95
     * values.
96
     *
97
     * @param f the field ID.
98
     * @param s the size of the field.
99
     * @param c the character used.
100
     */
101
    public CompiledField(int f, int s, char c)
102
    {
103
      field = f;
104
      size = s;
105
      character = c;
106
    }
107
 
108
    /**
109
     * Retrieves the ID of the field relative to
110
     * the local pattern characters.
111
     */
112
    public int getField()
113
    {
114
      return field;
115
    }
116
 
117
    /**
118
     * Retrieves the size of the character sequence.
119
     */
120
    public int getSize()
121
    {
122
      return size;
123
    }
124
 
125
    /**
126
     * Retrieves the character used in the sequence.
127
     */
128
    public char getCharacter()
129
    {
130
      return character;
131
    }
132
 
133
    /**
134
     * Returns a <code>String</code> representation
135
     * of the compiled field, primarily for debugging
136
     * purposes.
137
     *
138
     * @return a <code>String</code> representation.
139
     */
140
    public String toString()
141
    {
142
      StringBuffer builder;
143
 
144
      builder = new StringBuffer(getClass().getName());
145
      builder.append("[field=");
146
      builder.append(field);
147
      builder.append(", size=");
148
      builder.append(size);
149
      builder.append(", character=");
150
      builder.append(character);
151
      builder.append("]");
152
 
153
      return builder.toString();
154
    }
155
  }
156
 
157
  /**
158
   * A list of <code>CompiledField</code>s,
159
   * representing the compiled version of the pattern.
160
   *
161
   * @see CompiledField
162
   * @serial Ignored.
163
   */
164
  private transient ArrayList tokens;
165
 
166
  /**
167
   * The localised data used in formatting,
168
   * such as the day and month names in the local
169
   * language, and the localized pattern characters.
170
   *
171
   * @see DateFormatSymbols
172
   * @serial The localisation data.  May not be null.
173
   */
174
  private DateFormatSymbols formatData;
175
 
176
  /**
177
   * The date representing the start of the century
178
   * used for interpreting two digit years.  For
179
   * example, 24/10/2004 would cause two digit
180
   * years to be interpreted as representing
181
   * the years between 2004 and 2104.
182
   *
183
   * @see #get2DigitYearStart()
184
   * @see #set2DigitYearStart(java.util.Date)
185
   * @see Date
186
   * @serial The start date of the century for parsing two digit years.
187
   *         May not be null.
188
   */
189
  private Date defaultCenturyStart;
190
 
191
  /**
192
   * The year at which interpretation of two
193
   * digit years starts.
194
   *
195
   * @see #get2DigitYearStart()
196
   * @see #set2DigitYearStart(java.util.Date)
197
   * @serial Ignored.
198
   */
199
  private transient int defaultCentury;
200
 
201
  /**
202
   * The non-localized pattern string.  This
203
   * only ever contains the pattern characters
204
   * stored in standardChars.  Localized patterns
205
   * are translated to this form.
206
   *
207
   * @see #applyPattern(String)
208
   * @see #applyLocalizedPattern(String)
209
   * @see #toPattern()
210
   * @see #toLocalizedPattern()
211
   * @serial The non-localized pattern string.  May not be null.
212
   */
213
  private String pattern;
214
 
215
  /**
216
   * The version of serialized data used by this class.
217
   * Version 0 only includes the pattern and formatting
218
   * data.  Version 1 adds the start date for interpreting
219
   * two digit years.
220
   *
221
   * @serial This specifies the version of the data being serialized.
222
   *         Version 0 (or no version) specifies just <code>pattern</code>
223
   *         and <code>formatData</code>.  Version 1 adds
224
   *         the <code>defaultCenturyStart</code>.  This implementation
225
   *         always writes out version 1 data.
226
   */
227
  private int serialVersionOnStream = 1; // 0 indicates JDK1.1.3 or earlier
228
 
229
  /**
230
   * For compatability.
231
   */
232
  private static final long serialVersionUID = 4774881970558875024L;
233
 
234
  // This string is specified in the root of the CLDR.  We set it here
235
  // rather than doing a DateFormatSymbols(Locale.US).getLocalPatternChars()
236
  // since someone could theoretically change those values (though unlikely).
237
  private static final String standardChars = "GyMdkHmsSEDFwWahKzYeugAZ";
238
 
239
  /**
240
   * Reads the serialized version of this object.
241
   * If the serialized data is only version 0,
242
   * then the date for the start of the century
243
   * for interpreting two digit years is computed.
244
   * The pattern is parsed and compiled following the process
245
   * of reading in the serialized data.
246
   *
247
   * @param stream the object stream to read the data from.
248
   * @throws IOException if an I/O error occurs.
249
   * @throws ClassNotFoundException if the class of the serialized data
250
   *         could not be found.
251
   * @throws InvalidObjectException if the pattern is invalid.
252
   */
253
  private void readObject(ObjectInputStream stream)
254
    throws IOException, ClassNotFoundException
255
  {
256
    stream.defaultReadObject();
257
    if (serialVersionOnStream < 1)
258
      {
259
        computeCenturyStart ();
260
        serialVersionOnStream = 1;
261
      }
262
    else
263
      // Ensure that defaultCentury gets set.
264
      set2DigitYearStart(defaultCenturyStart);
265
 
266
    // Set up items normally taken care of by the constructor.
267
    tokens = new ArrayList();
268
    try
269
      {
270
        compileFormat(pattern);
271
      }
272
    catch (IllegalArgumentException e)
273
      {
274
        throw new InvalidObjectException("The stream pattern was invalid.");
275
      }
276
  }
277
 
278
  /**
279
   * Compiles the supplied non-localized pattern into a form
280
   * from which formatting and parsing can be performed.
281
   * This also detects errors in the pattern, which will
282
   * be raised on later use of the compiled data.
283
   *
284
   * @param pattern the non-localized pattern to compile.
285
   * @throws IllegalArgumentException if the pattern is invalid.
286
   */
287
  private void compileFormat(String pattern)
288
  {
289
    // Any alphabetical characters are treated as pattern characters
290
    // unless enclosed in single quotes.
291
 
292
    char thisChar;
293
    int pos;
294
    int field;
295
    CompiledField current = null;
296
 
297
    for (int i = 0; i < pattern.length(); i++)
298
      {
299
        thisChar = pattern.charAt(i);
300
        field = standardChars.indexOf(thisChar);
301
        if (field == -1)
302
          {
303
            current = null;
304
            if ((thisChar >= 'A' && thisChar <= 'Z')
305
                || (thisChar >= 'a' && thisChar <= 'z'))
306
              {
307
                // Not a valid letter
308
                throw new IllegalArgumentException("Invalid letter "
309
                                                   + thisChar +
310
                                                   "encountered at character "
311
                                                   + i + ".");
312
              }
313
            else if (thisChar == '\'')
314
              {
315
                // Quoted text section; skip to next single quote
316
                pos = pattern.indexOf('\'', i + 1);
317
                // First look for '' -- meaning a single quote.
318
                if (pos == i + 1)
319
                  tokens.add("'");
320
                else
321
                  {
322
                    // Look for the terminating quote.  However, if we
323
                    // see a '', that represents a literal quote and
324
                    // we must iterate.
325
                    StringBuffer buf = new StringBuffer();
326
                    int oldPos = i + 1;
327
                    do
328
                      {
329
                        if (pos == -1)
330
                          throw new IllegalArgumentException("Quotes starting at character "
331
                                                             + i +
332
                                                             " not closed.");
333
                        buf.append(pattern.substring(oldPos, pos));
334
                        if (pos + 1 >= pattern.length()
335
                            || pattern.charAt(pos + 1) != '\'')
336
                          break;
337
                        buf.append('\'');
338
                        oldPos = pos + 2;
339
                        pos = pattern.indexOf('\'', pos + 2);
340
                      }
341
                    while (true);
342
                    tokens.add(buf.toString());
343
                  }
344
                i = pos;
345
              }
346
            else
347
              {
348
                // A special character
349
                tokens.add(new Character(thisChar));
350
              }
351
          }
352
        else
353
          {
354
            // A valid field
355
            if ((current != null) && (field == current.field))
356
              current.size++;
357
            else
358
              {
359
                current = new CompiledField(field, 1, thisChar);
360
                tokens.add(current);
361
              }
362
          }
363
      }
364
  }
365
 
366
  /**
367
   * Returns a string representation of this
368
   * class.
369
   *
370
   * @return a string representation of the <code>SimpleDateFormat</code>
371
   *         instance.
372
   */
373
  public String toString()
374
  {
375
    StringBuffer output = new StringBuffer(getClass().getName());
376
    output.append("[tokens=");
377
    output.append(tokens);
378
    output.append(", formatData=");
379
    output.append(formatData);
380
    output.append(", defaultCenturyStart=");
381
    output.append(defaultCenturyStart);
382
    output.append(", defaultCentury=");
383
    output.append(defaultCentury);
384
    output.append(", pattern=");
385
    output.append(pattern);
386
    output.append(", serialVersionOnStream=");
387
    output.append(serialVersionOnStream);
388
    output.append(", standardChars=");
389
    output.append(standardChars);
390
    output.append("]");
391
    return output.toString();
392
  }
393
 
394
  /**
395
   * Constructs a SimpleDateFormat using the default pattern for
396
   * the default locale.
397
   */
398
  public SimpleDateFormat()
399
  {
400
    /*
401
     * There does not appear to be a standard API for determining
402
     * what the default pattern for a locale is, so use package-scope
403
     * variables in DateFormatSymbols to encapsulate this.
404
     */
405
    super();
406
    Locale locale = Locale.getDefault();
407
    calendar = new GregorianCalendar(locale);
408
    computeCenturyStart();
409
    tokens = new ArrayList();
410
    formatData = new DateFormatSymbols(locale);
411
    pattern = (formatData.dateFormats[DEFAULT] + ' '
412
               + formatData.timeFormats[DEFAULT]);
413
    compileFormat(pattern);
414
    numberFormat = NumberFormat.getInstance(locale);
415
    numberFormat.setGroupingUsed (false);
416
    numberFormat.setParseIntegerOnly (true);
417
    numberFormat.setMaximumFractionDigits (0);
418
  }
419
 
420
  /**
421
   * Creates a date formatter using the specified non-localized pattern,
422
   * with the default DateFormatSymbols for the default locale.
423
   *
424
   * @param pattern the pattern to use.
425
   * @throws NullPointerException if the pattern is null.
426
   * @throws IllegalArgumentException if the pattern is invalid.
427
   */
428
  public SimpleDateFormat(String pattern)
429
  {
430
    this(pattern, Locale.getDefault());
431
  }
432
 
433
  /**
434
   * Creates a date formatter using the specified non-localized pattern,
435
   * with the default DateFormatSymbols for the given locale.
436
   *
437
   * @param pattern the non-localized pattern to use.
438
   * @param locale the locale to use for the formatting symbols.
439
   * @throws NullPointerException if the pattern is null.
440
   * @throws IllegalArgumentException if the pattern is invalid.
441
   */
442
  public SimpleDateFormat(String pattern, Locale locale)
443
  {
444
    super();
445
    calendar = new GregorianCalendar(locale);
446
    computeCenturyStart();
447
    tokens = new ArrayList();
448
    formatData = new DateFormatSymbols(locale);
449
    compileFormat(pattern);
450
    this.pattern = pattern;
451
    numberFormat = NumberFormat.getInstance(locale);
452
    numberFormat.setGroupingUsed (false);
453
    numberFormat.setParseIntegerOnly (true);
454
    numberFormat.setMaximumFractionDigits (0);
455
  }
456
 
457
  /**
458
   * Creates a date formatter using the specified non-localized
459
   * pattern. The specified DateFormatSymbols will be used when
460
   * formatting.
461
   *
462
   * @param pattern the non-localized pattern to use.
463
   * @param formatData the formatting symbols to use.
464
   * @throws NullPointerException if the pattern or formatData is null.
465
   * @throws IllegalArgumentException if the pattern is invalid.
466
   */
467
  public SimpleDateFormat(String pattern, DateFormatSymbols formatData)
468
  {
469
    super();
470
    calendar = new GregorianCalendar();
471
    computeCenturyStart ();
472
    tokens = new ArrayList();
473
    if (formatData == null)
474
      throw new NullPointerException("formatData");
475
    this.formatData = formatData;
476
    compileFormat(pattern);
477
    this.pattern = pattern;
478
    numberFormat = NumberFormat.getInstance();
479
    numberFormat.setGroupingUsed (false);
480
    numberFormat.setParseIntegerOnly (true);
481
    numberFormat.setMaximumFractionDigits (0);
482
  }
483
 
484
  /**
485
   * This method returns a string with the formatting pattern being used
486
   * by this object.  This string is unlocalized.
487
   *
488
   * @return The format string.
489
   */
490
  public String toPattern()
491
  {
492
    return pattern;
493
  }
494
 
495
  /**
496
   * This method returns a string with the formatting pattern being used
497
   * by this object.  This string is localized.
498
   *
499
   * @return The format string.
500
   */
501
  public String toLocalizedPattern()
502
  {
503
    String localChars = formatData.getLocalPatternChars();
504
    return translateLocalizedPattern(pattern, standardChars, localChars);
505
  }
506
 
507
  /**
508
   * This method sets the formatting pattern that should be used by this
509
   * object.  This string is not localized.
510
   *
511
   * @param pattern The new format pattern.
512
   * @throws NullPointerException if the pattern is null.
513
   * @throws IllegalArgumentException if the pattern is invalid.
514
   */
515
  public void applyPattern(String pattern)
516
  {
517
    tokens = new ArrayList();
518
    compileFormat(pattern);
519
    this.pattern = pattern;
520
  }
521
 
522
  /**
523
   * This method sets the formatting pattern that should be used by this
524
   * object.  This string is localized.
525
   *
526
   * @param pattern The new format pattern.
527
   * @throws NullPointerException if the pattern is null.
528
   * @throws IllegalArgumentException if the pattern is invalid.
529
   */
530
  public void applyLocalizedPattern(String pattern)
531
  {
532
    String localChars = formatData.getLocalPatternChars();
533
    pattern = translateLocalizedPattern(pattern, localChars, standardChars);
534
    applyPattern(pattern);
535
  }
536
 
537
  /**
538
   * Translates either from or to a localized variant of the pattern
539
   * string.  For example, in the German locale, 't' (for 'tag') is
540
   * used instead of 'd' (for 'date').  This method translates
541
   * a localized pattern (such as 'ttt') to a non-localized pattern
542
   * (such as 'ddd'), or vice versa.  Non-localized patterns use
543
   * a standard set of characters, which match those of the U.S. English
544
   * locale.
545
   *
546
   * @param pattern the pattern to translate.
547
   * @param oldChars the old set of characters (used in the pattern).
548
   * @param newChars the new set of characters (which will be used in the
549
   *                 pattern).
550
   * @return a version of the pattern using the characters in
551
   *         <code>newChars</code>.
552
   */
553
  private String translateLocalizedPattern(String pattern,
554
                                           String oldChars, String newChars)
555
  {
556
    int len = pattern.length();
557
    StringBuffer buf = new StringBuffer(len);
558
    boolean quoted = false;
559
    for (int i = 0;  i < len;  i++)
560
      {
561
        char ch = pattern.charAt(i);
562
        if (ch == '\'')
563
          quoted = ! quoted;
564
        if (! quoted)
565
          {
566
            int j = oldChars.indexOf(ch);
567
            if (j >= 0)
568
              ch = newChars.charAt(j);
569
          }
570
        buf.append(ch);
571
      }
572
    return buf.toString();
573
  }
574
 
575
  /**
576
   * Returns the start of the century used for two digit years.
577
   *
578
   * @return A <code>Date</code> representing the start of the century
579
   * for two digit years.
580
   */
581
  public Date get2DigitYearStart()
582
  {
583
    return defaultCenturyStart;
584
  }
585
 
586
  /**
587
   * Sets the start of the century used for two digit years.
588
   *
589
   * @param date A <code>Date</code> representing the start of the century for
590
   * two digit years.
591
   */
592
  public void set2DigitYearStart(Date date)
593
  {
594
    defaultCenturyStart = date;
595
    calendar.clear();
596
    calendar.setTime(date);
597
    int year = calendar.get(Calendar.YEAR);
598
    defaultCentury = year - (year % 100);
599
  }
600
 
601
  /**
602
   * This method returns a copy of the format symbol information used
603
   * for parsing and formatting dates.
604
   *
605
   * @return a copy of the date format symbols.
606
   */
607
  public DateFormatSymbols getDateFormatSymbols()
608
  {
609
    return (DateFormatSymbols) formatData.clone();
610
  }
611
 
612
  /**
613
   * This method sets the format symbols information used for parsing
614
   * and formatting dates.
615
   *
616
   * @param formatData The date format symbols.
617
   * @throws NullPointerException if <code>formatData</code> is null.
618
   */
619
   public void setDateFormatSymbols(DateFormatSymbols formatData)
620
   {
621
     if (formatData == null)
622
       {
623
         throw new
624
           NullPointerException("The supplied format data was null.");
625
       }
626
     this.formatData = formatData;
627
   }
628
 
629
  /**
630
   * This methods tests whether the specified object is equal to this
631
   * object.  This will be true if and only if the specified object:
632
   * <p>
633
   * <ul>
634
   * <li>Is not <code>null</code>.</li>
635
   * <li>Is an instance of <code>SimpleDateFormat</code>.</li>
636
   * <li>Is equal to this object at the superclass (i.e., <code>DateFormat</code>)
637
   *     level.</li>
638
   * <li>Has the same formatting pattern.</li>
639
   * <li>Is using the same formatting symbols.</li>
640
   * <li>Is using the same century for two digit years.</li>
641
   * </ul>
642
   *
643
   * @param o The object to compare for equality against.
644
   *
645
   * @return <code>true</code> if the specified object is equal to this object,
646
   * <code>false</code> otherwise.
647
   */
648
  public boolean equals(Object o)
649
  {
650
    if (!super.equals(o))
651
      return false;
652
 
653
    if (!(o instanceof SimpleDateFormat))
654
      return false;
655
 
656
    SimpleDateFormat sdf = (SimpleDateFormat)o;
657
 
658
    if (defaultCentury != sdf.defaultCentury)
659
      return false;
660
 
661
    if (!toPattern().equals(sdf.toPattern()))
662
      return false;
663
 
664
    if (!getDateFormatSymbols().equals(sdf.getDateFormatSymbols()))
665
      return false;
666
 
667
    return true;
668
  }
669
 
670
  /**
671
   * This method returns a hash value for this object.
672
   *
673
   * @return A hash value for this object.
674
   */
675
  public int hashCode()
676
  {
677
    return super.hashCode() ^ toPattern().hashCode() ^ defaultCentury ^
678
      getDateFormatSymbols().hashCode();
679
  }
680
 
681
 
682
  /**
683
   * Formats the date input according to the format string in use,
684
   * appending to the specified StringBuffer.  The input StringBuffer
685
   * is returned as output for convenience.
686
   */
687
  private void formatWithAttribute(Date date, FormatBuffer buffer, FieldPosition pos)
688
  {
689
    String temp;
690
    AttributedCharacterIterator.Attribute attribute;
691
    calendar.setTime(date);
692
 
693
    // go through vector, filling in fields where applicable, else toString
694
    Iterator iter = tokens.iterator();
695
    while (iter.hasNext())
696
      {
697
        Object o = iter.next();
698
        if (o instanceof CompiledField)
699
          {
700
            CompiledField cf = (CompiledField) o;
701
            int beginIndex = buffer.length();
702
 
703
            switch (cf.getField())
704
              {
705
              case ERA_FIELD:
706
                buffer.append (formatData.eras[calendar.get (Calendar.ERA)], DateFormat.Field.ERA);
707
                break;
708
              case YEAR_FIELD:
709
                // If we have two digits, then we truncate.  Otherwise, we
710
                // use the size of the pattern, and zero pad.
711
                buffer.setDefaultAttribute (DateFormat.Field.YEAR);
712
                if (cf.getSize() == 2)
713
                  {
714
                    temp = "00"+String.valueOf (calendar.get (Calendar.YEAR));
715
                    buffer.append (temp.substring (temp.length() - 2));
716
                  }
717
                else
718
                  withLeadingZeros (calendar.get (Calendar.YEAR), cf.getSize(), buffer);
719
                break;
720
              case MONTH_FIELD:
721
                buffer.setDefaultAttribute (DateFormat.Field.MONTH);
722
                if (cf.getSize() < 3)
723
                  withLeadingZeros (calendar.get (Calendar.MONTH) + 1, cf.getSize(), buffer);
724
                else if (cf.getSize() < 4)
725
                  buffer.append (formatData.shortMonths[calendar.get (Calendar.MONTH)]);
726
                else
727
                  buffer.append (formatData.months[calendar.get (Calendar.MONTH)]);
728
                break;
729
              case DATE_FIELD:
730
                buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_MONTH);
731
                withLeadingZeros (calendar.get (Calendar.DATE), cf.getSize(), buffer);
732
                break;
733
              case HOUR_OF_DAY1_FIELD: // 1-24
734
                buffer.setDefaultAttribute(DateFormat.Field.HOUR_OF_DAY1);
735
                withLeadingZeros ( ((calendar.get (Calendar.HOUR_OF_DAY) + 23) % 24) + 1,
736
                                   cf.getSize(), buffer);
737
                break;
738
              case HOUR_OF_DAY0_FIELD: // 0-23
739
                buffer.setDefaultAttribute (DateFormat.Field.HOUR_OF_DAY0);
740
                withLeadingZeros (calendar.get (Calendar.HOUR_OF_DAY), cf.getSize(), buffer);
741
                break;
742
              case MINUTE_FIELD:
743
                buffer.setDefaultAttribute (DateFormat.Field.MINUTE);
744
                withLeadingZeros (calendar.get (Calendar.MINUTE),
745
                                  cf.getSize(), buffer);
746
                break;
747
              case SECOND_FIELD:
748
                buffer.setDefaultAttribute (DateFormat.Field.SECOND);
749
                withLeadingZeros(calendar.get (Calendar.SECOND),
750
                                 cf.getSize(), buffer);
751
                break;
752
              case MILLISECOND_FIELD:
753
                buffer.setDefaultAttribute (DateFormat.Field.MILLISECOND);
754
                withLeadingZeros (calendar.get (Calendar.MILLISECOND), cf.getSize(), buffer);
755
                break;
756
              case DAY_OF_WEEK_FIELD:
757
                buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK);
758
                if (cf.getSize() < 4)
759
                  buffer.append (formatData.shortWeekdays[calendar.get (Calendar.DAY_OF_WEEK)]);
760
                else
761
                  buffer.append (formatData.weekdays[calendar.get (Calendar.DAY_OF_WEEK)]);
762
                break;
763
              case DAY_OF_YEAR_FIELD:
764
                buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_YEAR);
765
                withLeadingZeros (calendar.get (Calendar.DAY_OF_YEAR), cf.getSize(), buffer);
766
                break;
767
              case DAY_OF_WEEK_IN_MONTH_FIELD:
768
                buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK_IN_MONTH);
769
                withLeadingZeros (calendar.get (Calendar.DAY_OF_WEEK_IN_MONTH),
770
                                 cf.getSize(), buffer);
771
                break;
772
              case WEEK_OF_YEAR_FIELD:
773
                buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_YEAR);
774
                withLeadingZeros (calendar.get (Calendar.WEEK_OF_YEAR),
775
                                  cf.getSize(), buffer);
776
                break;
777
              case WEEK_OF_MONTH_FIELD:
778
                buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_MONTH);
779
                withLeadingZeros (calendar.get (Calendar.WEEK_OF_MONTH),
780
                                  cf.getSize(), buffer);
781
                break;
782
              case AM_PM_FIELD:
783
                buffer.setDefaultAttribute (DateFormat.Field.AM_PM);
784
                buffer.append (formatData.ampms[calendar.get (Calendar.AM_PM)]);
785
                break;
786
              case HOUR1_FIELD: // 1-12
787
                buffer.setDefaultAttribute (DateFormat.Field.HOUR1);
788
                withLeadingZeros (((calendar.get (Calendar.HOUR) + 11) % 12) + 1,
789
                                  cf.getSize(), buffer);
790
                break;
791
              case HOUR0_FIELD: // 0-11
792
                buffer.setDefaultAttribute (DateFormat.Field.HOUR0);
793
                withLeadingZeros (calendar.get (Calendar.HOUR), cf.getSize(), buffer);
794
                break;
795
              case TIMEZONE_FIELD:
796
                buffer.setDefaultAttribute (DateFormat.Field.TIME_ZONE);
797
                TimeZone zone = calendar.getTimeZone();
798
                boolean isDST = calendar.get (Calendar.DST_OFFSET) != 0;
799
                // FIXME: XXX: This should be a localized time zone.
800
                String zoneID = zone.getDisplayName
801
                  (isDST, cf.getSize() > 3 ? TimeZone.LONG : TimeZone.SHORT);
802
                buffer.append (zoneID);
803
                break;
804
              case RFC822_TIMEZONE_FIELD:
805
                buffer.setDefaultAttribute(DateFormat.Field.RFC822_TIME_ZONE);
806
                int pureMinutes = (calendar.get(Calendar.ZONE_OFFSET) +
807
                                   calendar.get(Calendar.DST_OFFSET)) / (1000 * 60);
808
                String sign = (pureMinutes < 0) ? "-" : "+";
809
                int hours = pureMinutes / 60;
810
                int minutes = pureMinutes % 60;
811
                buffer.append(sign);
812
                withLeadingZeros(hours, 2, buffer);
813
                withLeadingZeros(minutes, 2, buffer);
814
                break;
815
              default:
816
                throw new IllegalArgumentException ("Illegal pattern character " +
817
                                                    cf.getCharacter());
818
              }
819
            if (pos != null && (buffer.getDefaultAttribute() == pos.getFieldAttribute()
820
                                || cf.getField() == pos.getField()))
821
              {
822
                pos.setBeginIndex(beginIndex);
823
                pos.setEndIndex(buffer.length());
824
              }
825
          }
826
      else
827
        {
828
          buffer.append(o.toString(), null);
829
        }
830
      }
831
  }
832
 
833
  public StringBuffer format(Date date, StringBuffer buffer, FieldPosition pos)
834
  {
835
    formatWithAttribute(date, new StringFormatBuffer (buffer), pos);
836
 
837
    return buffer;
838
  }
839
 
840
  public AttributedCharacterIterator formatToCharacterIterator(Object date)
841
    throws IllegalArgumentException
842
  {
843
    if (date == null)
844
      throw new NullPointerException("null argument");
845
    if (!(date instanceof Date))
846
      throw new IllegalArgumentException("argument should be an instance of java.util.Date");
847
 
848
    AttributedFormatBuffer buf = new AttributedFormatBuffer();
849
    formatWithAttribute((Date)date, buf,
850
                        null);
851
    buf.sync();
852
 
853
    return new FormatCharacterIterator(buf.getBuffer().toString(),
854
                                       buf.getRanges(),
855
                                       buf.getAttributes());
856
  }
857
 
858
  private void withLeadingZeros(int value, int length, FormatBuffer buffer)
859
  {
860
    String valStr = String.valueOf(value);
861
    for (length -= valStr.length(); length > 0; length--)
862
      buffer.append('0');
863
    buffer.append(valStr);
864
  }
865
 
866
  private boolean expect(String source, ParsePosition pos, char ch)
867
  {
868
    int x = pos.getIndex();
869
    boolean r = x < source.length() && source.charAt(x) == ch;
870
    if (r)
871
      pos.setIndex(x + 1);
872
    else
873
      pos.setErrorIndex(x);
874
    return r;
875
  }
876
 
877
  /**
878
   * This method parses the specified string into a date.
879
   *
880
   * @param dateStr The date string to parse.
881
   * @param pos The input and output parse position
882
   *
883
   * @return The parsed date, or <code>null</code> if the string cannot be
884
   * parsed.
885
   */
886
  public Date parse (String dateStr, ParsePosition pos)
887
  {
888
    int fmt_index = 0;
889
    int fmt_max = pattern.length();
890
 
891
    calendar.clear();
892
    boolean saw_timezone = false;
893
    int quote_start = -1;
894
    boolean is2DigitYear = false;
895
    try
896
      {
897
        for (; fmt_index < fmt_max; ++fmt_index)
898
          {
899
            char ch = pattern.charAt(fmt_index);
900
            if (ch == '\'')
901
              {
902
                int index = pos.getIndex();
903
                if (fmt_index < fmt_max - 1
904
                    && pattern.charAt(fmt_index + 1) == '\'')
905
                  {
906
                    if (! expect (dateStr, pos, ch))
907
                      return null;
908
                    ++fmt_index;
909
                  }
910
                else
911
                  quote_start = quote_start < 0 ? fmt_index : -1;
912
                continue;
913
              }
914
 
915
            if (quote_start != -1
916
                || ((ch < 'a' || ch > 'z')
917
                    && (ch < 'A' || ch > 'Z')))
918
              {
919
                if (! expect (dateStr, pos, ch))
920
                  return null;
921
                continue;
922
              }
923
 
924
            // We've arrived at a potential pattern character in the
925
            // pattern.
926
            int fmt_count = 1;
927
            while (++fmt_index < fmt_max && pattern.charAt(fmt_index) == ch)
928
              {
929
                ++fmt_count;
930
              }
931
 
932
            // We might need to limit the number of digits to parse in
933
            // some cases.  We look to the next pattern character to
934
            // decide.
935
            boolean limit_digits = false;
936
            if (fmt_index < fmt_max
937
                && standardChars.indexOf(pattern.charAt(fmt_index)) >= 0)
938
              limit_digits = true;
939
            --fmt_index;
940
 
941
            // We can handle most fields automatically: most either are
942
            // numeric or are looked up in a string vector.  In some cases
943
            // we need an offset.  When numeric, `offset' is added to the
944
            // resulting value.  When doing a string lookup, offset is the
945
            // initial index into the string array.
946
            int calendar_field;
947
            boolean is_numeric = true;
948
            int offset = 0;
949
            boolean maybe2DigitYear = false;
950
            boolean oneBasedHour = false;
951
            boolean oneBasedHourOfDay = false;
952
            Integer simpleOffset;
953
            String[] set1 = null;
954
            String[] set2 = null;
955
            switch (ch)
956
              {
957
              case 'd':
958
                calendar_field = Calendar.DATE;
959
                break;
960
              case 'D':
961
                calendar_field = Calendar.DAY_OF_YEAR;
962
                break;
963
              case 'F':
964
                calendar_field = Calendar.DAY_OF_WEEK_IN_MONTH;
965
                break;
966
              case 'E':
967
                is_numeric = false;
968
                offset = 1;
969
                calendar_field = Calendar.DAY_OF_WEEK;
970
                set1 = formatData.getWeekdays();
971
                set2 = formatData.getShortWeekdays();
972
                break;
973
              case 'w':
974
                calendar_field = Calendar.WEEK_OF_YEAR;
975
                break;
976
              case 'W':
977
                calendar_field = Calendar.WEEK_OF_MONTH;
978
                break;
979
              case 'M':
980
                calendar_field = Calendar.MONTH;
981
                if (fmt_count <= 2)
982
                  offset = -1;
983
                else
984
                  {
985
                    is_numeric = false;
986
                    set1 = formatData.getMonths();
987
                    set2 = formatData.getShortMonths();
988
                  }
989
                break;
990
              case 'y':
991
                calendar_field = Calendar.YEAR;
992
                if (fmt_count <= 2)
993
                  maybe2DigitYear = true;
994
                break;
995
              case 'K':
996
                calendar_field = Calendar.HOUR;
997
                break;
998
              case 'h':
999
                calendar_field = Calendar.HOUR;
1000
                oneBasedHour = true;
1001
                break;
1002
              case 'H':
1003
                calendar_field = Calendar.HOUR_OF_DAY;
1004
                break;
1005
              case 'k':
1006
                calendar_field = Calendar.HOUR_OF_DAY;
1007
                oneBasedHourOfDay = true;
1008
                break;
1009
              case 'm':
1010
                calendar_field = Calendar.MINUTE;
1011
                break;
1012
              case 's':
1013
                calendar_field = Calendar.SECOND;
1014
                break;
1015
              case 'S':
1016
                calendar_field = Calendar.MILLISECOND;
1017
                break;
1018
              case 'a':
1019
                is_numeric = false;
1020
                calendar_field = Calendar.AM_PM;
1021
                set1 = formatData.getAmPmStrings();
1022
                break;
1023
              case 'z':
1024
              case 'Z':
1025
                // We need a special case for the timezone, because it
1026
                // uses a different data structure than the other cases.
1027
                is_numeric = false;
1028
                calendar_field = Calendar.ZONE_OFFSET;
1029
                String[][] zoneStrings = formatData.getZoneStrings();
1030
                int zoneCount = zoneStrings.length;
1031
                int index = pos.getIndex();
1032
                boolean found_zone = false;
1033
                simpleOffset = computeOffset(dateStr.substring(index), pos);
1034
                if (simpleOffset != null)
1035
                  {
1036
                    found_zone = true;
1037
                    saw_timezone = true;
1038
                    calendar.set(Calendar.DST_OFFSET, 0);
1039
                    offset = simpleOffset.intValue();
1040
                  }
1041
                else
1042
                  {
1043
                    for (int j = 0;  j < zoneCount;  j++)
1044
                      {
1045
                        String[] strings = zoneStrings[j];
1046
                        int k;
1047
                        for (k = 0; k < strings.length; ++k)
1048
                          {
1049
                            if (dateStr.startsWith(strings[k], index))
1050
                              break;
1051
                          }
1052
                        if (k != strings.length)
1053
                          {
1054
                            found_zone = true;
1055
                            saw_timezone = true;
1056
                            TimeZone tz = TimeZone.getTimeZone (strings[0]);
1057
                            // Check if it's a DST zone or ordinary 
1058
                            if(k == 3 || k == 4)
1059
                              calendar.set (Calendar.DST_OFFSET, tz.getDSTSavings());
1060
                            else
1061
                              calendar.set (Calendar.DST_OFFSET, 0);
1062
                            offset = tz.getRawOffset ();
1063
                            pos.setIndex(index + strings[k].length());
1064
                            break;
1065
                          }
1066
                      }
1067
                  }
1068
                if (! found_zone)
1069
                  {
1070
                        pos.setErrorIndex(pos.getIndex());
1071
                        return null;
1072
                  }
1073
                break;
1074
              default:
1075
                pos.setErrorIndex(pos.getIndex());
1076
                return null;
1077
              }
1078
 
1079
            // Compute the value we should assign to the field.
1080
            int value;
1081
            int index = -1;
1082
            if (is_numeric)
1083
              {
1084
                numberFormat.setMinimumIntegerDigits(fmt_count);
1085
                if (limit_digits)
1086
                  numberFormat.setMaximumIntegerDigits(fmt_count);
1087
                if (maybe2DigitYear)
1088
                  index = pos.getIndex();
1089
                Number n = numberFormat.parse(dateStr, pos);
1090
                if (pos == null || ! (n instanceof Long))
1091
                  return null;
1092
                value = n.intValue() + offset;
1093
              }
1094
            else if (set1 != null)
1095
              {
1096
                index = pos.getIndex();
1097
                int i;
1098
                boolean found = false;
1099
                for (i = offset; i < set1.length; ++i)
1100
                  {
1101
                    if (set1[i] != null)
1102
                      if (dateStr.toUpperCase().startsWith(set1[i].toUpperCase(),
1103
                                                           index))
1104
                        {
1105
                          found = true;
1106
                          pos.setIndex(index + set1[i].length());
1107
                          break;
1108
                        }
1109
                  }
1110
                if (!found && set2 != null)
1111
                  {
1112
                    for (i = offset; i < set2.length; ++i)
1113
                      {
1114
                        if (set2[i] != null)
1115
                          if (dateStr.toUpperCase().startsWith(set2[i].toUpperCase(),
1116
                                                               index))
1117
                            {
1118
                              found = true;
1119
                              pos.setIndex(index + set2[i].length());
1120
                              break;
1121
                            }
1122
                      }
1123
                  }
1124
                if (!found)
1125
                  {
1126
                    pos.setErrorIndex(index);
1127
                    return null;
1128
                  }
1129
                value = i;
1130
              }
1131
            else
1132
              value = offset;
1133
 
1134
            if (maybe2DigitYear)
1135
              {
1136
                // Parse into default century if the numeric year string has 
1137
                // exactly 2 digits.
1138
                int digit_count = pos.getIndex() - index;
1139
                if (digit_count == 2)
1140
                  {
1141
                    is2DigitYear = true;
1142
                    value += defaultCentury;
1143
                  }
1144
              }
1145
 
1146
            // Calendar uses 0-based hours. 
1147
            // I.e. 00:00 AM is midnight, not 12 AM or 24:00
1148
            if (oneBasedHour && value == 12)
1149
              value = 0;
1150
 
1151
            if (oneBasedHourOfDay && value == 24)
1152
              value = 0;
1153
 
1154
            // Assign the value and move on.
1155
            calendar.set(calendar_field, value);
1156
          }
1157
 
1158
        if (is2DigitYear)
1159
          {
1160
            // Apply the 80-20 heuristic to dermine the full year based on 
1161
            // defaultCenturyStart. 
1162
            int year = calendar.get(Calendar.YEAR);
1163
            if (calendar.getTime().compareTo(defaultCenturyStart) < 0)
1164
              calendar.set(Calendar.YEAR, year + 100);
1165
          }
1166
        if (! saw_timezone)
1167
          {
1168
            // Use the real rules to determine whether or not this
1169
            // particular time is in daylight savings.
1170
            calendar.clear (Calendar.DST_OFFSET);
1171
            calendar.clear (Calendar.ZONE_OFFSET);
1172
          }
1173
        return calendar.getTime();
1174
      }
1175
    catch (IllegalArgumentException x)
1176
      {
1177
        pos.setErrorIndex(pos.getIndex());
1178
        return null;
1179
      }
1180
      }
1181
 
1182
  /**
1183
   * <p>
1184
   * Computes the time zone offset in milliseconds
1185
   * relative to GMT, based on the supplied
1186
   * <code>String</code> representation.
1187
   * </p>
1188
   * <p>
1189
   * The supplied <code>String</code> must be a three
1190
   * or four digit signed number, with an optional 'GMT'
1191
   * prefix.  The first one or two digits represents the hours,
1192
   * while the last two represent the minutes.  The
1193
   * two sets of digits can optionally be separated by
1194
   * ':'.  The mandatory sign prefix (either '+' or '-')
1195
   * indicates the direction of the offset from GMT.
1196
   * </p>
1197
   * <p>
1198
   * For example, 'GMT+0200' specifies 2 hours after
1199
   * GMT, while '-05:00' specifies 5 hours prior to
1200
   * GMT.  The special case of 'GMT' alone can be used
1201
   * to represent the offset, 0.
1202
   * </p>
1203
   * <p>
1204
   * If the <code>String</code> can not be parsed,
1205
   * the result will be null.  The resulting offset
1206
   * is wrapped in an <code>Integer</code> object, in
1207
   * order to allow such failure to be represented.
1208
   * </p>
1209
   *
1210
   * @param zoneString a string in the form
1211
   *        (GMT)? sign hours : minutes
1212
   *        where sign = '+' or '-', hours
1213
   *        is a one or two digits representing
1214
   *        a number between 0 and 23, and
1215
   *        minutes is two digits representing
1216
   *        a number between 0 and 59.
1217
   * @return the parsed offset, or null if parsing
1218
   *         failed.
1219
   */
1220
  private Integer computeOffset(String zoneString, ParsePosition pos)
1221
  {
1222
    Pattern pattern =
1223
      Pattern.compile("(GMT)?([+-])([012])?([0-9]):?([0-9]{2})");
1224
    Matcher matcher = pattern.matcher(zoneString);
1225
 
1226
    // Match from start, but ignore trailing parts
1227
    boolean hasAll = matcher.lookingAt();
1228
    try
1229
      {
1230
        // Do we have at least the sign, hour and minute?
1231
        matcher.group(2);
1232
        matcher.group(4);
1233
        matcher.group(5);
1234
      }
1235
    catch (IllegalStateException ise)
1236
      {
1237
        hasAll = false;
1238
      }
1239
    if (hasAll)
1240
      {
1241
        int sign = matcher.group(2).equals("+") ? 1 : -1;
1242
        int hour = Integer.parseInt(matcher.group(4));
1243
        if (!matcher.group(3).equals(""))
1244
          hour += (Integer.parseInt(matcher.group(3)) * 10);
1245
        int minutes = Integer.parseInt(matcher.group(5));
1246
 
1247
        if (hour > 23)
1248
          return null;
1249
        int offset = sign * ((hour * 60) + minutes) * 60000;
1250
 
1251
        // advance the index
1252
        pos.setIndex(pos.getIndex() + matcher.end());
1253
        return new Integer(offset);
1254
      }
1255
    else if (zoneString.startsWith("GMT"))
1256
      {
1257
        pos.setIndex(pos.getIndex() + 3);
1258
        return new Integer(0);
1259
      }
1260
    return null;
1261
  }
1262
 
1263
  // Compute the start of the current century as defined by
1264
  // get2DigitYearStart.
1265
  private void computeCenturyStart()
1266
  {
1267
    int year = calendar.get(Calendar.YEAR);
1268
    calendar.set(Calendar.YEAR, year - 80);
1269
    set2DigitYearStart(calendar.getTime());
1270
  }
1271
 
1272
  /**
1273
   * Returns a copy of this instance of
1274
   * <code>SimpleDateFormat</code>.  The copy contains
1275
   * clones of the formatting symbols and the 2-digit
1276
   * year century start date.
1277
   */
1278
  public Object clone()
1279
  {
1280
    SimpleDateFormat clone = (SimpleDateFormat) super.clone();
1281
    clone.setDateFormatSymbols((DateFormatSymbols) formatData.clone());
1282
    clone.set2DigitYearStart((Date) defaultCenturyStart.clone());
1283
    return clone;
1284
  }
1285
 
1286
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.