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

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [java/] [text/] [SimpleDateFormat.java] - Blame information for rev 771

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

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