jtulach@1334: /*
jtulach@1334: * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
jtulach@1334: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jtulach@1334: *
jtulach@1334: * This code is free software; you can redistribute it and/or modify it
jtulach@1334: * under the terms of the GNU General Public License version 2 only, as
jtulach@1334: * published by the Free Software Foundation. Oracle designates this
jtulach@1334: * particular file as subject to the "Classpath" exception as provided
jtulach@1334: * by Oracle in the LICENSE file that accompanied this code.
jtulach@1334: *
jtulach@1334: * This code is distributed in the hope that it will be useful, but WITHOUT
jtulach@1334: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jtulach@1334: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
jtulach@1334: * version 2 for more details (a copy is included in the LICENSE file that
jtulach@1334: * accompanied this code).
jtulach@1334: *
jtulach@1334: * You should have received a copy of the GNU General Public License version
jtulach@1334: * 2 along with this work; if not, write to the Free Software Foundation,
jtulach@1334: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jtulach@1334: *
jtulach@1334: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jtulach@1334: * or visit www.oracle.com if you need additional information or have any
jtulach@1334: * questions.
jtulach@1334: */
jtulach@1334:
jtulach@1334: /*
jtulach@1334: * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
jtulach@1334: * (C) Copyright IBM Corp. 1996-1998 - All Rights Reserved
jtulach@1334: *
jtulach@1334: * The original version of this source code and documentation is copyrighted
jtulach@1334: * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
jtulach@1334: * materials are provided under terms of a License Agreement between Taligent
jtulach@1334: * and Sun. This technology is protected by multiple US and International
jtulach@1334: * patents. This notice and attribution to Taligent may not be removed.
jtulach@1334: * Taligent is a registered trademark of Taligent, Inc.
jtulach@1334: *
jtulach@1334: */
jtulach@1334:
jtulach@1334: package java.text;
jtulach@1334:
jtulach@1334: import java.io.IOException;
jtulach@1334: import java.io.InvalidObjectException;
jtulach@1334: import java.io.ObjectInputStream;
jtulach@1334: import java.util.Calendar;
jtulach@1334: import java.util.Date;
jtulach@1334: import java.util.GregorianCalendar;
jtulach@1334: import java.util.Locale;
jtulach@1334: import java.util.Map;
jtulach@1334: import java.util.MissingResourceException;
jtulach@1334: import java.util.ResourceBundle;
jtulach@1334: import java.util.SimpleTimeZone;
jtulach@1334: import java.util.TimeZone;
jtulach@1334: import java.util.concurrent.ConcurrentHashMap;
jtulach@1334: import java.util.concurrent.ConcurrentMap;
jtulach@1334: import sun.util.calendar.CalendarUtils;
jtulach@1334: import sun.util.calendar.ZoneInfoFile;
jtulach@1334: import sun.util.resources.LocaleData;
jtulach@1334:
jtulach@1334: import static java.text.DateFormatSymbols.*;
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * SimpleDateFormat
is a concrete class for formatting and
jtulach@1334: * parsing dates in a locale-sensitive manner. It allows for formatting
jtulach@1334: * (date -> text), parsing (text -> date), and normalization.
jtulach@1334: *
jtulach@1334: *
jtulach@1334: * SimpleDateFormat
allows you to start by choosing
jtulach@1334: * any user-defined patterns for date-time formatting. However, you
jtulach@1334: * are encouraged to create a date-time formatter with either
jtulach@1334: * getTimeInstance
, getDateInstance
, or
jtulach@1334: * getDateTimeInstance
in DateFormat
. Each
jtulach@1334: * of these class methods can return a date/time formatter initialized
jtulach@1334: * with a default format pattern. You may modify the format pattern
jtulach@1334: * using the applyPattern
methods as desired.
jtulach@1334: * For more information on using these methods, see
jtulach@1334: * {@link DateFormat}.
jtulach@1334: *
jtulach@1334: *
jtulach@1334: * Date and time formats are specified by date and time pattern
jtulach@1334: * strings.
jtulach@1334: * Within date and time pattern strings, unquoted letters from
jtulach@1334: * 'A'
to 'Z'
and from 'a'
to
jtulach@1334: * 'z'
are interpreted as pattern letters representing the
jtulach@1334: * components of a date or time string.
jtulach@1334: * Text can be quoted using single quotes ('
) to avoid
jtulach@1334: * interpretation.
jtulach@1334: * "''"
represents a single quote.
jtulach@1334: * All other characters are not interpreted; they're simply copied into the
jtulach@1334: * output string during formatting or matched against the input string
jtulach@1334: * during parsing.
jtulach@1334: *
jtulach@1334: * The following pattern letters are defined (all other characters from
jtulach@1334: * 'A'
to 'Z'
and from 'a'
to
jtulach@1334: * 'z'
are reserved):
jtulach@1334: *
jtulach@1334: *jtulach@1334: * Pattern letters are usually repeated, as their number determines the jtulach@1334: * exact presentation: jtulach@1334: *jtulach@1334: *
jtulach@1334: *jtulach@1334: * Letter jtulach@1334: * Date or Time Component jtulach@1334: * Presentation jtulach@1334: * Examples jtulach@1334: * jtulach@1334: * G
jtulach@1334: *Era designator jtulach@1334: * Text jtulach@1334: * AD
jtulach@1334: *jtulach@1334: * y
jtulach@1334: *Year jtulach@1334: * Year jtulach@1334: * 1996
;96
jtulach@1334: *jtulach@1334: * Y
jtulach@1334: *Week year jtulach@1334: * Year jtulach@1334: * 2009
;09
jtulach@1334: *jtulach@1334: * M
jtulach@1334: *Month in year jtulach@1334: * Month jtulach@1334: * July
;Jul
;07
jtulach@1334: *jtulach@1334: * w
jtulach@1334: *Week in year jtulach@1334: * Number jtulach@1334: * 27
jtulach@1334: *jtulach@1334: * W
jtulach@1334: *Week in month jtulach@1334: * Number jtulach@1334: * 2
jtulach@1334: *jtulach@1334: * D
jtulach@1334: *Day in year jtulach@1334: * Number jtulach@1334: * 189
jtulach@1334: *jtulach@1334: * d
jtulach@1334: *Day in month jtulach@1334: * Number jtulach@1334: * 10
jtulach@1334: *jtulach@1334: * F
jtulach@1334: *Day of week in month jtulach@1334: * Number jtulach@1334: * 2
jtulach@1334: *jtulach@1334: * E
jtulach@1334: *Day name in week jtulach@1334: * Text jtulach@1334: * Tuesday
;Tue
jtulach@1334: *jtulach@1334: * u
jtulach@1334: *Day number of week (1 = Monday, ..., 7 = Sunday) jtulach@1334: * Number jtulach@1334: * 1
jtulach@1334: *jtulach@1334: * a
jtulach@1334: *Am/pm marker jtulach@1334: * Text jtulach@1334: * PM
jtulach@1334: *jtulach@1334: * H
jtulach@1334: *Hour in day (0-23) jtulach@1334: * Number jtulach@1334: * 0
jtulach@1334: *jtulach@1334: * k
jtulach@1334: *Hour in day (1-24) jtulach@1334: * Number jtulach@1334: * 24
jtulach@1334: *jtulach@1334: * K
jtulach@1334: *Hour in am/pm (0-11) jtulach@1334: * Number jtulach@1334: * 0
jtulach@1334: *jtulach@1334: * h
jtulach@1334: *Hour in am/pm (1-12) jtulach@1334: * Number jtulach@1334: * 12
jtulach@1334: *jtulach@1334: * m
jtulach@1334: *Minute in hour jtulach@1334: * Number jtulach@1334: * 30
jtulach@1334: *jtulach@1334: * s
jtulach@1334: *Second in minute jtulach@1334: * Number jtulach@1334: * 55
jtulach@1334: *jtulach@1334: * S
jtulach@1334: *Millisecond jtulach@1334: * Number jtulach@1334: * 978
jtulach@1334: *jtulach@1334: * z
jtulach@1334: *Time zone jtulach@1334: * General time zone jtulach@1334: * Pacific Standard Time
;PST
;GMT-08:00
jtulach@1334: *jtulach@1334: * Z
jtulach@1334: *Time zone jtulach@1334: * RFC 822 time zone jtulach@1334: * -0800
jtulach@1334: *jtulach@1334: * X
jtulach@1334: *Time zone jtulach@1334: * ISO 8601 time zone jtulach@1334: * -08
;-0800
;-08:00
jtulach@1334: *
SimpleDateFormat
must interpret the abbreviated year
jtulach@1334: * relative to some century. It does this by adjusting dates to be
jtulach@1334: * within 80 years before and 20 years after the time the SimpleDateFormat
jtulach@1334: * instance is created. For example, using a pattern of "MM/dd/yy" and a
jtulach@1334: * SimpleDateFormat
instance created on Jan 1, 1997, the string
jtulach@1334: * "01/11/12" would be interpreted as Jan 11, 2012 while the string "05/04/64"
jtulach@1334: * would be interpreted as May 4, 1964.
jtulach@1334: * During parsing, only strings consisting of exactly two digits, as defined by
jtulach@1334: * {@link Character#isDigit(char)}, will be parsed into the default century.
jtulach@1334: * Any other numeric string, such as a one digit string, a three or more digit
jtulach@1334: * string, or a two digit string that isn't all digits (for example, "-1"), is
jtulach@1334: * interpreted literally. So "01/02/3" or "01/02/003" are parsed, using the
jtulach@1334: * same pattern, as Jan 2, 3 AD. Likewise, "01/02/-3" is parsed as Jan 2, 4 BC.
jtulach@1334: * jtulach@1334: * GMTOffsetTimeZone: jtulach@1334: *jtulach@1334: * Hours must be between 0 and 23, and Minutes must be between jtulach@1334: * 00 and 59. The format is locale independent and digits must be taken jtulach@1334: * from the Basic Latin block of the Unicode standard. jtulach@1334: *GMT
Sign Hours:
Minutes jtulach@1334: * Sign: one of jtulach@1334: *+ -
jtulach@1334: * Hours: jtulach@1334: * Digit jtulach@1334: * Digit Digit jtulach@1334: * Minutes: jtulach@1334: * Digit Digit jtulach@1334: * Digit: one of jtulach@1334: *0 1 2 3 4 5 6 7 8 9
For parsing, RFC 822 time zones are also
jtulach@1334: * accepted.
jtulach@1334: * RFC822TimeZone: jtulach@1334: * Sign TwoDigitHours Minutes jtulach@1334: * TwoDigitHours: jtulach@1334: * Digit Digitjtulach@1334: * TwoDigitHours must be between 00 and 23. Other definitions jtulach@1334: * are as for general time zones. jtulach@1334: * jtulach@1334: *
For parsing, general time zones are also jtulach@1334: * accepted. jtulach@1334: *
jtulach@1334: * ISO8601TimeZone: jtulach@1334: * OneLetterISO8601TimeZone jtulach@1334: * TwoLetterISO8601TimeZone jtulach@1334: * ThreeLetterISO8601TimeZone jtulach@1334: * OneLetterISO8601TimeZone: jtulach@1334: * Sign TwoDigitHours jtulach@1334: * {@code Z} jtulach@1334: * TwoLetterISO8601TimeZone: jtulach@1334: * Sign TwoDigitHours Minutes jtulach@1334: * {@code Z} jtulach@1334: * ThreeLetterISO8601TimeZone: jtulach@1334: * Sign TwoDigitHours {@code :} Minutes jtulach@1334: * {@code Z}jtulach@1334: * Other definitions are as for general time zones or jtulach@1334: * RFC 822 time zones. jtulach@1334: * jtulach@1334: *
For formatting, if the offset value from GMT is 0, {@code "Z"} is jtulach@1334: * produced. If the number of pattern letters is 1, any fraction of an hour jtulach@1334: * is ignored. For example, if the pattern is {@code "X"} and the time zone is jtulach@1334: * {@code "GMT+05:30"}, {@code "+05"} is produced. jtulach@1334: * jtulach@1334: *
For parsing, {@code "Z"} is parsed as the UTC time zone designator. jtulach@1334: * General time zones are not accepted. jtulach@1334: * jtulach@1334: *
If the number of pattern letters is 4 or more, {@link jtulach@1334: * IllegalArgumentException} is thrown when constructing a {@code jtulach@1334: * SimpleDateFormat} or {@linkplain #applyPattern(String) applying a jtulach@1334: * pattern}. jtulach@1334: *
SimpleDateFormat
also supports localized date and time
jtulach@1334: * pattern strings. In these strings, the pattern letters described above
jtulach@1334: * may be replaced with other, locale dependent, pattern letters.
jtulach@1334: * SimpleDateFormat
does not deal with the localization of text
jtulach@1334: * other than the pattern letters; that's up to the client of the class.
jtulach@1334: * jtulach@1334: * jtulach@1334: *
jtulach@1334: *jtulach@1334: * jtulach@1334: *jtulach@1334: *
jtulach@1334: *jtulach@1334: * Date and Time Pattern jtulach@1334: * Result jtulach@1334: * jtulach@1334: * "yyyy.MM.dd G 'at' HH:mm:ss z"
jtulach@1334: *2001.07.04 AD at 12:08:56 PDT
jtulach@1334: *jtulach@1334: * "EEE, MMM d, ''yy"
jtulach@1334: *Wed, Jul 4, '01
jtulach@1334: *jtulach@1334: * "h:mm a"
jtulach@1334: *12:08 PM
jtulach@1334: *jtulach@1334: * "hh 'o''clock' a, zzzz"
jtulach@1334: *12 o'clock PM, Pacific Daylight Time
jtulach@1334: *jtulach@1334: * "K:mm a, z"
jtulach@1334: *0:08 PM, PDT
jtulach@1334: *jtulach@1334: * "yyyyy.MMMMM.dd GGG hh:mm aaa"
jtulach@1334: *02001.July.04 AD 12:08 PM
jtulach@1334: *jtulach@1334: * "EEE, d MMM yyyy HH:mm:ss Z"
jtulach@1334: *Wed, 4 Jul 2001 12:08:56 -0700
jtulach@1334: *jtulach@1334: * "yyMMddHHmmssZ"
jtulach@1334: *010704120856-0700
jtulach@1334: *jtulach@1334: * "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
jtulach@1334: *2001-07-04T12:08:56.235-0700
jtulach@1334: *jtulach@1334: * "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"
jtulach@1334: *2001-07-04T12:08:56.235-07:00
jtulach@1334: *jtulach@1334: * "YYYY-'W'ww-u"
jtulach@1334: *2001-W27-3
jtulach@1334: *
jtulach@1334: * Date formats are not synchronized. jtulach@1334: * It is recommended to create separate format instances for each thread. jtulach@1334: * If multiple threads access a format concurrently, it must be synchronized jtulach@1334: * externally. jtulach@1334: * jtulach@1334: * @see Java Tutorial jtulach@1334: * @see java.util.Calendar jtulach@1334: * @see java.util.TimeZone jtulach@1334: * @see DateFormat jtulach@1334: * @see DateFormatSymbols jtulach@1334: * @author Mark Davis, Chen-Lieh Huang, Alan Liu jtulach@1334: */ jtulach@1334: public class SimpleDateFormat extends DateFormat { jtulach@1334: jtulach@1334: // the official serial version ID which says cryptically jtulach@1334: // which version we're compatible with jtulach@1334: static final long serialVersionUID = 4774881970558875024L; jtulach@1334: jtulach@1334: // the internal serial version which says which version was written jtulach@1334: // - 0 (default) for version up to JDK 1.1.3 jtulach@1334: // - 1 for version from JDK 1.1.4, which includes a new field jtulach@1334: static final int currentSerialVersion = 1; jtulach@1334: jtulach@1334: /** jtulach@1334: * The version of the serialized data on the stream. Possible values: jtulach@1334: *
defaultCenturyStart
on stream.
jtulach@1334: * defaultCenturyStart
.
jtulach@1334: * serialVersionOnStream
jtulach@1334: * is written.
jtulach@1334: * @serial
jtulach@1334: * @since JDK1.1.4
jtulach@1334: */
jtulach@1334: private int serialVersionOnStream = currentSerialVersion;
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * The pattern string of this formatter. This is always a non-localized
jtulach@1334: * pattern. May not be null. See class documentation for details.
jtulach@1334: * @serial
jtulach@1334: */
jtulach@1334: private String pattern;
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Saved numberFormat and pattern.
jtulach@1334: * @see SimpleDateFormat#checkNegativeNumberExpression
jtulach@1334: */
jtulach@1334: transient private NumberFormat originalNumberFormat;
jtulach@1334: transient private String originalNumberPattern;
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * The minus sign to be used with format and parse.
jtulach@1334: */
jtulach@1334: transient private char minusSign = '-';
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * True when a negative sign follows a number.
jtulach@1334: * (True as default in Arabic.)
jtulach@1334: */
jtulach@1334: transient private boolean hasFollowingMinusSign = false;
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * The compiled pattern.
jtulach@1334: */
jtulach@1334: transient private char[] compiledPattern;
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Tags for the compiled pattern.
jtulach@1334: */
jtulach@1334: private final static int TAG_QUOTE_ASCII_CHAR = 100;
jtulach@1334: private final static int TAG_QUOTE_CHARS = 101;
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Locale dependent digit zero.
jtulach@1334: * @see #zeroPaddingNumber
jtulach@1334: * @see java.text.DecimalFormatSymbols#getZeroDigit
jtulach@1334: */
jtulach@1334: transient private char zeroDigit;
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * The symbols used by this formatter for week names, month names,
jtulach@1334: * etc. May not be null.
jtulach@1334: * @serial
jtulach@1334: * @see java.text.DateFormatSymbols
jtulach@1334: */
jtulach@1334: private DateFormatSymbols formatData;
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * We map dates with two-digit years into the century starting at
jtulach@1334: * defaultCenturyStart
, which may be any date. May
jtulach@1334: * not be null.
jtulach@1334: * @serial
jtulach@1334: * @since JDK1.1.4
jtulach@1334: */
jtulach@1334: private Date defaultCenturyStart;
jtulach@1334:
jtulach@1334: transient private int defaultCenturyStartYear;
jtulach@1334:
jtulach@1334: private static final int MILLIS_PER_MINUTE = 60 * 1000;
jtulach@1334:
jtulach@1334: // For time zones that have no names, use strings GMT+minutes and
jtulach@1334: // GMT-minutes. For instance, in France the time zone is GMT+60.
jtulach@1334: private static final String GMT = "GMT";
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Cache to hold the DateTimePatterns of a Locale.
jtulach@1334: */
jtulach@1334: private static final ConcurrentMapSimpleDateFormat
. The value may be null if this object
jtulach@1334: * has been created by an older SimpleDateFormat
and
jtulach@1334: * deserialized.
jtulach@1334: *
jtulach@1334: * @serial
jtulach@1334: * @since 1.6
jtulach@1334: */
jtulach@1334: private Locale locale;
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Indicates whether this SimpleDateFormat
should use
jtulach@1334: * the DateFormatSymbols. If true, the format and parse methods
jtulach@1334: * use the DateFormatSymbols values. If false, the format and
jtulach@1334: * parse methods call Calendar.getDisplayName or
jtulach@1334: * Calendar.getDisplayNames.
jtulach@1334: */
jtulach@1334: transient boolean useDateFormatSymbols;
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Constructs a SimpleDateFormat
using the default pattern and
jtulach@1334: * date format symbols for the default locale.
jtulach@1334: * Note: This constructor may not support all locales.
jtulach@1334: * For full coverage, use the factory methods in the {@link DateFormat}
jtulach@1334: * class.
jtulach@1334: */
jtulach@1334: public SimpleDateFormat() {
jtulach@1334: this(SHORT, SHORT, Locale.getDefault(Locale.Category.FORMAT));
jtulach@1334: }
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Constructs a SimpleDateFormat
using the given pattern and
jtulach@1334: * the default date format symbols for the default locale.
jtulach@1334: * Note: This constructor may not support all locales.
jtulach@1334: * For full coverage, use the factory methods in the {@link DateFormat}
jtulach@1334: * class.
jtulach@1334: *
jtulach@1334: * @param pattern the pattern describing the date and time format
jtulach@1334: * @exception NullPointerException if the given pattern is null
jtulach@1334: * @exception IllegalArgumentException if the given pattern is invalid
jtulach@1334: */
jtulach@1334: public SimpleDateFormat(String pattern)
jtulach@1334: {
jtulach@1334: this(pattern, Locale.getDefault(Locale.Category.FORMAT));
jtulach@1334: }
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Constructs a SimpleDateFormat
using the given pattern and
jtulach@1334: * the default date format symbols for the given locale.
jtulach@1334: * Note: This constructor may not support all locales.
jtulach@1334: * For full coverage, use the factory methods in the {@link DateFormat}
jtulach@1334: * class.
jtulach@1334: *
jtulach@1334: * @param pattern the pattern describing the date and time format
jtulach@1334: * @param locale the locale whose date format symbols should be used
jtulach@1334: * @exception NullPointerException if the given pattern or locale is null
jtulach@1334: * @exception IllegalArgumentException if the given pattern is invalid
jtulach@1334: */
jtulach@1334: public SimpleDateFormat(String pattern, Locale locale)
jtulach@1334: {
jtulach@1334: if (pattern == null || locale == null) {
jtulach@1334: throw new NullPointerException();
jtulach@1334: }
jtulach@1334:
jtulach@1334: initializeCalendar(locale);
jtulach@1334: this.pattern = pattern;
jtulach@1334: this.formatData = DateFormatSymbols.getInstanceRef(locale);
jtulach@1334: this.locale = locale;
jtulach@1334: initialize(locale);
jtulach@1334: }
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Constructs a SimpleDateFormat
using the given pattern and
jtulach@1334: * date format symbols.
jtulach@1334: *
jtulach@1334: * @param pattern the pattern describing the date and time format
jtulach@1334: * @param formatSymbols the date format symbols to be used for formatting
jtulach@1334: * @exception NullPointerException if the given pattern or formatSymbols is null
jtulach@1334: * @exception IllegalArgumentException if the given pattern is invalid
jtulach@1334: */
jtulach@1334: public SimpleDateFormat(String pattern, DateFormatSymbols formatSymbols)
jtulach@1334: {
jtulach@1334: if (pattern == null || formatSymbols == null) {
jtulach@1334: throw new NullPointerException();
jtulach@1334: }
jtulach@1334:
jtulach@1334: this.pattern = pattern;
jtulach@1334: this.formatData = (DateFormatSymbols) formatSymbols.clone();
jtulach@1334: this.locale = Locale.getDefault(Locale.Category.FORMAT);
jtulach@1334: initializeCalendar(this.locale);
jtulach@1334: initialize(this.locale);
jtulach@1334: useDateFormatSymbols = true;
jtulach@1334: }
jtulach@1334:
jtulach@1334: /* Package-private, called by DateFormat factory methods */
jtulach@1334: SimpleDateFormat(int timeStyle, int dateStyle, Locale loc) {
jtulach@1334: if (loc == null) {
jtulach@1334: throw new NullPointerException();
jtulach@1334: }
jtulach@1334:
jtulach@1334: this.locale = loc;
jtulach@1334: // initialize calendar and related fields
jtulach@1334: initializeCalendar(loc);
jtulach@1334:
jtulach@1334: /* try the cache first */
jtulach@1334: String[] dateTimePatterns = cachedLocaleData.get(loc);
jtulach@1334: if (dateTimePatterns == null) { /* cache miss */
jtulach@1334: ResourceBundle r = LocaleData.getDateFormatData(loc);
jtulach@1334: if (!isGregorianCalendar()) {
jtulach@1334: try {
jtulach@1334: dateTimePatterns = r.getStringArray(getCalendarName() + ".DateTimePatterns");
jtulach@1334: } catch (MissingResourceException e) {
jtulach@1334: }
jtulach@1334: }
jtulach@1334: if (dateTimePatterns == null) {
jtulach@1334: dateTimePatterns = r.getStringArray("DateTimePatterns");
jtulach@1334: }
jtulach@1334: /* update cache */
jtulach@1334: cachedLocaleData.putIfAbsent(loc, dateTimePatterns);
jtulach@1334: }
jtulach@1334: formatData = DateFormatSymbols.getInstanceRef(loc);
jtulach@1334: if ((timeStyle >= 0) && (dateStyle >= 0)) {
jtulach@1334: Object[] dateTimeArgs = {dateTimePatterns[timeStyle],
jtulach@1334: dateTimePatterns[dateStyle + 4]};
jtulach@1334: pattern = MessageFormat.format(dateTimePatterns[8], dateTimeArgs);
jtulach@1334: }
jtulach@1334: else if (timeStyle >= 0) {
jtulach@1334: pattern = dateTimePatterns[timeStyle];
jtulach@1334: }
jtulach@1334: else if (dateStyle >= 0) {
jtulach@1334: pattern = dateTimePatterns[dateStyle + 4];
jtulach@1334: }
jtulach@1334: else {
jtulach@1334: throw new IllegalArgumentException("No date or time style specified");
jtulach@1334: }
jtulach@1334:
jtulach@1334: initialize(loc);
jtulach@1334: }
jtulach@1334:
jtulach@1334: /* Initialize compiledPattern and numberFormat fields */
jtulach@1334: private void initialize(Locale loc) {
jtulach@1334: // Verify and compile the given pattern.
jtulach@1334: compiledPattern = compile(pattern);
jtulach@1334:
jtulach@1334: /* try the cache first */
jtulach@1334: numberFormat = cachedNumberFormatData.get(loc);
jtulach@1334: if (numberFormat == null) { /* cache miss */
jtulach@1334: numberFormat = NumberFormat.getIntegerInstance(loc);
jtulach@1334: numberFormat.setGroupingUsed(false);
jtulach@1334:
jtulach@1334: /* update cache */
jtulach@1334: cachedNumberFormatData.putIfAbsent(loc, numberFormat);
jtulach@1334: }
jtulach@1334: numberFormat = (NumberFormat) numberFormat.clone();
jtulach@1334:
jtulach@1334: initializeDefaultCentury();
jtulach@1334: }
jtulach@1334:
jtulach@1334: private void initializeCalendar(Locale loc) {
jtulach@1334: if (calendar == null) {
jtulach@1334: assert loc != null;
jtulach@1334: // The format object must be constructed using the symbols for this zone.
jtulach@1334: // However, the calendar should use the current default TimeZone.
jtulach@1334: // If this is not contained in the locale zone strings, then the zone
jtulach@1334: // will be formatted using generic GMT+/-H:MM nomenclature.
jtulach@1334: calendar = Calendar.getInstance(TimeZone.getDefault(), loc);
jtulach@1334: }
jtulach@1334: }
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Returns the compiled form of the given pattern. The syntax of
jtulach@1334: * the compiled pattern is:
jtulach@1334: * jtulach@1334: * CompiledPattern: jtulach@1334: * EntryList jtulach@1334: * EntryList: jtulach@1334: * Entry jtulach@1334: * EntryList Entry jtulach@1334: * Entry: jtulach@1334: * TagField jtulach@1334: * TagField data jtulach@1334: * TagField: jtulach@1334: * Tag Length jtulach@1334: * TaggedData jtulach@1334: * Tag: jtulach@1334: * pattern_char_index jtulach@1334: * TAG_QUOTE_CHARS jtulach@1334: * Length: jtulach@1334: * short_length jtulach@1334: * long_length jtulach@1334: * TaggedData: jtulach@1334: * TAG_QUOTE_ASCII_CHAR ascii_char jtulach@1334: * jtulach@1334: *jtulach@1334: * jtulach@1334: * where `short_length' is an 8-bit unsigned integer between 0 and jtulach@1334: * 254. `long_length' is a sequence of an 8-bit integer 255 and a jtulach@1334: * 32-bit signed integer value which is split into upper and lower jtulach@1334: * 16-bit fields in two char's. `pattern_char_index' is an 8-bit jtulach@1334: * integer between 0 and 18. `ascii_char' is an 7-bit ASCII jtulach@1334: * character value. `data' depends on its Tag value. jtulach@1334: *
jtulach@1334: * If Length is short_length, Tag and short_length are packed in a jtulach@1334: * single char, as illustrated below. jtulach@1334: *
jtulach@1334: * char[0] = (Tag << 8) | short_length; jtulach@1334: *jtulach@1334: * jtulach@1334: * If Length is long_length, Tag and 255 are packed in the first jtulach@1334: * char and a 32-bit integer, as illustrated below. jtulach@1334: *
jtulach@1334: * char[0] = (Tag << 8) | 255; jtulach@1334: * char[1] = (char) (long_length >>> 16); jtulach@1334: * char[2] = (char) (long_length & 0xffff); jtulach@1334: *jtulach@1334: *
jtulach@1334: * If Tag is a pattern_char_index, its Length is the number of jtulach@1334: * pattern characters. For example, if the given pattern is jtulach@1334: * "yyyy", Tag is 1 and Length is 4, followed by no data. jtulach@1334: *
jtulach@1334: * If Tag is TAG_QUOTE_CHARS, its Length is the number of char's
jtulach@1334: * following the TagField. For example, if the given pattern is
jtulach@1334: * "'o''clock'", Length is 7 followed by a char sequence of
jtulach@1334: * o&nbs;'&nbs;c&nbs;l&nbs;o&nbs;c&nbs;k
.
jtulach@1334: *
jtulach@1334: * TAG_QUOTE_ASCII_CHAR is a special tag and has an ASCII
jtulach@1334: * character in place of Length. For example, if the given pattern
jtulach@1334: * is "'o'", the TaggedData entry is
jtulach@1334: * ((TAG_QUOTE_ASCII_CHAR&nbs;<<&nbs;8)&nbs;|&nbs;'o')
.
jtulach@1334: *
jtulach@1334: * @exception NullPointerException if the given pattern is null
jtulach@1334: * @exception IllegalArgumentException if the given pattern is invalid
jtulach@1334: */
jtulach@1334: private char[] compile(String pattern) {
jtulach@1334: int length = pattern.length();
jtulach@1334: boolean inQuote = false;
jtulach@1334: StringBuilder compiledPattern = new StringBuilder(length * 2);
jtulach@1334: StringBuilder tmpBuffer = null;
jtulach@1334: int count = 0;
jtulach@1334: int lastTag = -1;
jtulach@1334:
jtulach@1334: for (int i = 0; i < length; i++) {
jtulach@1334: char c = pattern.charAt(i);
jtulach@1334:
jtulach@1334: if (c == '\'') {
jtulach@1334: // '' is treated as a single quote regardless of being
jtulach@1334: // in a quoted section.
jtulach@1334: if ((i + 1) < length) {
jtulach@1334: c = pattern.charAt(i + 1);
jtulach@1334: if (c == '\'') {
jtulach@1334: i++;
jtulach@1334: if (count != 0) {
jtulach@1334: encode(lastTag, count, compiledPattern);
jtulach@1334: lastTag = -1;
jtulach@1334: count = 0;
jtulach@1334: }
jtulach@1334: if (inQuote) {
jtulach@1334: tmpBuffer.append(c);
jtulach@1334: } else {
jtulach@1334: compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c));
jtulach@1334: }
jtulach@1334: continue;
jtulach@1334: }
jtulach@1334: }
jtulach@1334: if (!inQuote) {
jtulach@1334: if (count != 0) {
jtulach@1334: encode(lastTag, count, compiledPattern);
jtulach@1334: lastTag = -1;
jtulach@1334: count = 0;
jtulach@1334: }
jtulach@1334: if (tmpBuffer == null) {
jtulach@1334: tmpBuffer = new StringBuilder(length);
jtulach@1334: } else {
jtulach@1334: tmpBuffer.setLength(0);
jtulach@1334: }
jtulach@1334: inQuote = true;
jtulach@1334: } else {
jtulach@1334: int len = tmpBuffer.length();
jtulach@1334: if (len == 1) {
jtulach@1334: char ch = tmpBuffer.charAt(0);
jtulach@1334: if (ch < 128) {
jtulach@1334: compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | ch));
jtulach@1334: } else {
jtulach@1334: compiledPattern.append((char)(TAG_QUOTE_CHARS << 8 | 1));
jtulach@1334: compiledPattern.append(ch);
jtulach@1334: }
jtulach@1334: } else {
jtulach@1334: encode(TAG_QUOTE_CHARS, len, compiledPattern);
jtulach@1334: compiledPattern.append(tmpBuffer);
jtulach@1334: }
jtulach@1334: inQuote = false;
jtulach@1334: }
jtulach@1334: continue;
jtulach@1334: }
jtulach@1334: if (inQuote) {
jtulach@1334: tmpBuffer.append(c);
jtulach@1334: continue;
jtulach@1334: }
jtulach@1334: if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {
jtulach@1334: if (count != 0) {
jtulach@1334: encode(lastTag, count, compiledPattern);
jtulach@1334: lastTag = -1;
jtulach@1334: count = 0;
jtulach@1334: }
jtulach@1334: if (c < 128) {
jtulach@1334: // In most cases, c would be a delimiter, such as ':'.
jtulach@1334: compiledPattern.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c));
jtulach@1334: } else {
jtulach@1334: // Take any contiguous non-ASCII alphabet characters and
jtulach@1334: // put them in a single TAG_QUOTE_CHARS.
jtulach@1334: int j;
jtulach@1334: for (j = i + 1; j < length; j++) {
jtulach@1334: char d = pattern.charAt(j);
jtulach@1334: if (d == '\'' || (d >= 'a' && d <= 'z' || d >= 'A' && d <= 'Z')) {
jtulach@1334: break;
jtulach@1334: }
jtulach@1334: }
jtulach@1334: compiledPattern.append((char)(TAG_QUOTE_CHARS << 8 | (j - i)));
jtulach@1334: for (; i < j; i++) {
jtulach@1334: compiledPattern.append(pattern.charAt(i));
jtulach@1334: }
jtulach@1334: i--;
jtulach@1334: }
jtulach@1334: continue;
jtulach@1334: }
jtulach@1334:
jtulach@1334: int tag;
jtulach@1334: if ((tag = DateFormatSymbols.patternChars.indexOf(c)) == -1) {
jtulach@1334: throw new IllegalArgumentException("Illegal pattern character " +
jtulach@1334: "'" + c + "'");
jtulach@1334: }
jtulach@1334: if (lastTag == -1 || lastTag == tag) {
jtulach@1334: lastTag = tag;
jtulach@1334: count++;
jtulach@1334: continue;
jtulach@1334: }
jtulach@1334: encode(lastTag, count, compiledPattern);
jtulach@1334: lastTag = tag;
jtulach@1334: count = 1;
jtulach@1334: }
jtulach@1334:
jtulach@1334: if (inQuote) {
jtulach@1334: throw new IllegalArgumentException("Unterminated quote");
jtulach@1334: }
jtulach@1334:
jtulach@1334: if (count != 0) {
jtulach@1334: encode(lastTag, count, compiledPattern);
jtulach@1334: }
jtulach@1334:
jtulach@1334: // Copy the compiled pattern to a char array
jtulach@1334: int len = compiledPattern.length();
jtulach@1334: char[] r = new char[len];
jtulach@1334: compiledPattern.getChars(0, len, r, 0);
jtulach@1334: return r;
jtulach@1334: }
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Encodes the given tag and length and puts encoded char(s) into buffer.
jtulach@1334: */
jtulach@1334: private static final void encode(int tag, int length, StringBuilder buffer) {
jtulach@1334: if (tag == PATTERN_ISO_ZONE && length >= 4) {
jtulach@1334: throw new IllegalArgumentException("invalid ISO 8601 format: length=" + length);
jtulach@1334: }
jtulach@1334: if (length < 255) {
jtulach@1334: buffer.append((char)(tag << 8 | length));
jtulach@1334: } else {
jtulach@1334: buffer.append((char)((tag << 8) | 0xff));
jtulach@1334: buffer.append((char)(length >>> 16));
jtulach@1334: buffer.append((char)(length & 0xffff));
jtulach@1334: }
jtulach@1334: }
jtulach@1334:
jtulach@1334: /* Initialize the fields we use to disambiguate ambiguous years. Separate
jtulach@1334: * so we can call it from readObject().
jtulach@1334: */
jtulach@1334: private void initializeDefaultCentury() {
jtulach@1334: calendar.setTimeInMillis(System.currentTimeMillis());
jtulach@1334: calendar.add( Calendar.YEAR, -80 );
jtulach@1334: parseAmbiguousDatesAsAfter(calendar.getTime());
jtulach@1334: }
jtulach@1334:
jtulach@1334: /* Define one-century window into which to disambiguate dates using
jtulach@1334: * two-digit years.
jtulach@1334: */
jtulach@1334: private void parseAmbiguousDatesAsAfter(Date startDate) {
jtulach@1334: defaultCenturyStart = startDate;
jtulach@1334: calendar.setTime(startDate);
jtulach@1334: defaultCenturyStartYear = calendar.get(Calendar.YEAR);
jtulach@1334: }
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Sets the 100-year period 2-digit years will be interpreted as being in
jtulach@1334: * to begin on the date the user specifies.
jtulach@1334: *
jtulach@1334: * @param startDate During parsing, two digit years will be placed in the range
jtulach@1334: * startDate
to startDate + 100 years
.
jtulach@1334: * @see #get2DigitYearStart
jtulach@1334: * @since 1.2
jtulach@1334: */
jtulach@1334: public void set2DigitYearStart(Date startDate) {
jtulach@1334: parseAmbiguousDatesAsAfter(new Date(startDate.getTime()));
jtulach@1334: }
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Returns the beginning date of the 100-year period 2-digit years are interpreted
jtulach@1334: * as being within.
jtulach@1334: *
jtulach@1334: * @return the start of the 100-year period into which two digit years are
jtulach@1334: * parsed
jtulach@1334: * @see #set2DigitYearStart
jtulach@1334: * @since 1.2
jtulach@1334: */
jtulach@1334: public Date get2DigitYearStart() {
jtulach@1334: return (Date) defaultCenturyStart.clone();
jtulach@1334: }
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Formats the given Date
into a date/time string and appends
jtulach@1334: * the result to the given StringBuffer
.
jtulach@1334: *
jtulach@1334: * @param date the date-time value to be formatted into a date-time string.
jtulach@1334: * @param toAppendTo where the new date-time text is to be appended.
jtulach@1334: * @param pos the formatting position. On input: an alignment field,
jtulach@1334: * if desired. On output: the offsets of the alignment field.
jtulach@1334: * @return the formatted date-time string.
jtulach@1334: * @exception NullPointerException if the given {@code date} is {@code null}.
jtulach@1334: */
jtulach@1334: public StringBuffer format(Date date, StringBuffer toAppendTo,
jtulach@1334: FieldPosition pos)
jtulach@1334: {
jtulach@1334: pos.beginIndex = pos.endIndex = 0;
jtulach@1334: return format(date, toAppendTo, pos.getFieldDelegate());
jtulach@1334: }
jtulach@1334:
jtulach@1334: // Called from Format after creating a FieldDelegate
jtulach@1334: private StringBuffer format(Date date, StringBuffer toAppendTo,
jtulach@1334: FieldDelegate delegate) {
jtulach@1334: // Convert input date to time field list
jtulach@1334: calendar.setTime(date);
jtulach@1334:
jtulach@1334: boolean useDateFormatSymbols = useDateFormatSymbols();
jtulach@1334:
jtulach@1334: for (int i = 0; i < compiledPattern.length; ) {
jtulach@1334: int tag = compiledPattern[i] >>> 8;
jtulach@1334: int count = compiledPattern[i++] & 0xff;
jtulach@1334: if (count == 255) {
jtulach@1334: count = compiledPattern[i++] << 16;
jtulach@1334: count |= compiledPattern[i++];
jtulach@1334: }
jtulach@1334:
jtulach@1334: switch (tag) {
jtulach@1334: case TAG_QUOTE_ASCII_CHAR:
jtulach@1334: toAppendTo.append((char)count);
jtulach@1334: break;
jtulach@1334:
jtulach@1334: case TAG_QUOTE_CHARS:
jtulach@1334: toAppendTo.append(compiledPattern, i, count);
jtulach@1334: i += count;
jtulach@1334: break;
jtulach@1334:
jtulach@1334: default:
jtulach@1334: subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
jtulach@1334: break;
jtulach@1334: }
jtulach@1334: }
jtulach@1334: return toAppendTo;
jtulach@1334: }
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Formats an Object producing an AttributedCharacterIterator
.
jtulach@1334: * You can use the returned AttributedCharacterIterator
jtulach@1334: * to build the resulting String, as well as to determine information
jtulach@1334: * about the resulting String.
jtulach@1334: *
jtulach@1334: * Each attribute key of the AttributedCharacterIterator will be of type
jtulach@1334: * DateFormat.Field
, with the corresponding attribute value
jtulach@1334: * being the same as the attribute key.
jtulach@1334: *
jtulach@1334: * @exception NullPointerException if obj is null.
jtulach@1334: * @exception IllegalArgumentException if the Format cannot format the
jtulach@1334: * given object, or if the Format's pattern string is invalid.
jtulach@1334: * @param obj The object to format
jtulach@1334: * @return AttributedCharacterIterator describing the formatted value.
jtulach@1334: * @since 1.4
jtulach@1334: */
jtulach@1334: public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
jtulach@1334: StringBuffer sb = new StringBuffer();
jtulach@1334: CharacterIteratorFieldDelegate delegate = new
jtulach@1334: CharacterIteratorFieldDelegate();
jtulach@1334:
jtulach@1334: if (obj instanceof Date) {
jtulach@1334: format((Date)obj, sb, delegate);
jtulach@1334: }
jtulach@1334: else if (obj instanceof Number) {
jtulach@1334: format(new Date(((Number)obj).longValue()), sb, delegate);
jtulach@1334: }
jtulach@1334: else if (obj == null) {
jtulach@1334: throw new NullPointerException(
jtulach@1334: "formatToCharacterIterator must be passed non-null object");
jtulach@1334: }
jtulach@1334: else {
jtulach@1334: throw new IllegalArgumentException(
jtulach@1334: "Cannot format given Object as a Date");
jtulach@1334: }
jtulach@1334: return delegate.getIterator(sb.toString());
jtulach@1334: }
jtulach@1334:
jtulach@1334: // Map index into pattern character string to Calendar field number
jtulach@1334: private static final int[] PATTERN_INDEX_TO_CALENDAR_FIELD =
jtulach@1334: {
jtulach@1334: Calendar.ERA, Calendar.YEAR, Calendar.MONTH, Calendar.DATE,
jtulach@1334: Calendar.HOUR_OF_DAY, Calendar.HOUR_OF_DAY, Calendar.MINUTE,
jtulach@1334: Calendar.SECOND, Calendar.MILLISECOND, Calendar.DAY_OF_WEEK,
jtulach@1334: Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK_IN_MONTH,
jtulach@1334: Calendar.WEEK_OF_YEAR, Calendar.WEEK_OF_MONTH,
jtulach@1334: Calendar.AM_PM, Calendar.HOUR, Calendar.HOUR, Calendar.ZONE_OFFSET,
jtulach@1334: Calendar.ZONE_OFFSET,
jtulach@1334: // Pseudo Calendar fields
jtulach@1334: CalendarBuilder.WEEK_YEAR,
jtulach@1334: CalendarBuilder.ISO_DAY_OF_WEEK,
jtulach@1334: Calendar.ZONE_OFFSET
jtulach@1334: };
jtulach@1334:
jtulach@1334: // Map index into pattern character string to DateFormat field number
jtulach@1334: private static final int[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD = {
jtulach@1334: DateFormat.ERA_FIELD, DateFormat.YEAR_FIELD, DateFormat.MONTH_FIELD,
jtulach@1334: DateFormat.DATE_FIELD, DateFormat.HOUR_OF_DAY1_FIELD,
jtulach@1334: DateFormat.HOUR_OF_DAY0_FIELD, DateFormat.MINUTE_FIELD,
jtulach@1334: DateFormat.SECOND_FIELD, DateFormat.MILLISECOND_FIELD,
jtulach@1334: DateFormat.DAY_OF_WEEK_FIELD, DateFormat.DAY_OF_YEAR_FIELD,
jtulach@1334: DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD, DateFormat.WEEK_OF_YEAR_FIELD,
jtulach@1334: DateFormat.WEEK_OF_MONTH_FIELD, DateFormat.AM_PM_FIELD,
jtulach@1334: DateFormat.HOUR1_FIELD, DateFormat.HOUR0_FIELD,
jtulach@1334: DateFormat.TIMEZONE_FIELD, DateFormat.TIMEZONE_FIELD,
jtulach@1334: DateFormat.YEAR_FIELD, DateFormat.DAY_OF_WEEK_FIELD,
jtulach@1334: DateFormat.TIMEZONE_FIELD
jtulach@1334: };
jtulach@1334:
jtulach@1334: // Maps from DecimalFormatSymbols index to Field constant
jtulach@1334: private static final Field[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID = {
jtulach@1334: Field.ERA, Field.YEAR, Field.MONTH, Field.DAY_OF_MONTH,
jtulach@1334: Field.HOUR_OF_DAY1, Field.HOUR_OF_DAY0, Field.MINUTE,
jtulach@1334: Field.SECOND, Field.MILLISECOND, Field.DAY_OF_WEEK,
jtulach@1334: Field.DAY_OF_YEAR, Field.DAY_OF_WEEK_IN_MONTH,
jtulach@1334: Field.WEEK_OF_YEAR, Field.WEEK_OF_MONTH,
jtulach@1334: Field.AM_PM, Field.HOUR1, Field.HOUR0, Field.TIME_ZONE,
jtulach@1334: Field.TIME_ZONE,
jtulach@1334: Field.YEAR, Field.DAY_OF_WEEK,
jtulach@1334: Field.TIME_ZONE
jtulach@1334: };
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Private member function that does the real date/time formatting.
jtulach@1334: */
jtulach@1334: private void subFormat(int patternCharIndex, int count,
jtulach@1334: FieldDelegate delegate, StringBuffer buffer,
jtulach@1334: boolean useDateFormatSymbols)
jtulach@1334: {
jtulach@1334: int maxIntCount = Integer.MAX_VALUE;
jtulach@1334: String current = null;
jtulach@1334: int beginOffset = buffer.length();
jtulach@1334:
jtulach@1334: int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
jtulach@1334: int value;
jtulach@1334: if (field == CalendarBuilder.WEEK_YEAR) {
jtulach@1334: if (calendar.isWeekDateSupported()) {
jtulach@1334: value = calendar.getWeekYear();
jtulach@1334: } else {
jtulach@1334: // use calendar year 'y' instead
jtulach@1334: patternCharIndex = PATTERN_YEAR;
jtulach@1334: field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
jtulach@1334: value = calendar.get(field);
jtulach@1334: }
jtulach@1334: } else if (field == CalendarBuilder.ISO_DAY_OF_WEEK) {
jtulach@1334: value = CalendarBuilder.toISODayOfWeek(calendar.get(Calendar.DAY_OF_WEEK));
jtulach@1334: } else {
jtulach@1334: value = calendar.get(field);
jtulach@1334: }
jtulach@1334:
jtulach@1334: int style = (count >= 4) ? Calendar.LONG : Calendar.SHORT;
jtulach@1334: if (!useDateFormatSymbols && field != CalendarBuilder.ISO_DAY_OF_WEEK) {
jtulach@1334: current = calendar.getDisplayName(field, style, locale);
jtulach@1334: }
jtulach@1334:
jtulach@1334: // Note: zeroPaddingNumber() assumes that maxDigits is either
jtulach@1334: // 2 or maxIntCount. If we make any changes to this,
jtulach@1334: // zeroPaddingNumber() must be fixed.
jtulach@1334:
jtulach@1334: switch (patternCharIndex) {
jtulach@1334: case PATTERN_ERA: // 'G'
jtulach@1334: if (useDateFormatSymbols) {
jtulach@1334: String[] eras = formatData.getEras();
jtulach@1334: if (value < eras.length)
jtulach@1334: current = eras[value];
jtulach@1334: }
jtulach@1334: if (current == null)
jtulach@1334: current = "";
jtulach@1334: break;
jtulach@1334:
jtulach@1334: case PATTERN_WEEK_YEAR: // 'Y'
jtulach@1334: case PATTERN_YEAR: // 'y'
jtulach@1334: if (calendar instanceof GregorianCalendar) {
jtulach@1334: if (count != 2)
jtulach@1334: zeroPaddingNumber(value, count, maxIntCount, buffer);
jtulach@1334: else // count == 2
jtulach@1334: zeroPaddingNumber(value, 2, 2, buffer); // clip 1996 to 96
jtulach@1334: } else {
jtulach@1334: if (current == null) {
jtulach@1334: zeroPaddingNumber(value, style == Calendar.LONG ? 1 : count,
jtulach@1334: maxIntCount, buffer);
jtulach@1334: }
jtulach@1334: }
jtulach@1334: break;
jtulach@1334:
jtulach@1334: case PATTERN_MONTH: // 'M'
jtulach@1334: if (useDateFormatSymbols) {
jtulach@1334: String[] months;
jtulach@1334: if (count >= 4) {
jtulach@1334: months = formatData.getMonths();
jtulach@1334: current = months[value];
jtulach@1334: } else if (count == 3) {
jtulach@1334: months = formatData.getShortMonths();
jtulach@1334: current = months[value];
jtulach@1334: }
jtulach@1334: } else {
jtulach@1334: if (count < 3) {
jtulach@1334: current = null;
jtulach@1334: }
jtulach@1334: }
jtulach@1334: if (current == null) {
jtulach@1334: zeroPaddingNumber(value+1, count, maxIntCount, buffer);
jtulach@1334: }
jtulach@1334: break;
jtulach@1334:
jtulach@1334: case PATTERN_HOUR_OF_DAY1: // 'k' 1-based. eg, 23:59 + 1 hour =>> 24:59
jtulach@1334: if (current == null) {
jtulach@1334: if (value == 0)
jtulach@1334: zeroPaddingNumber(calendar.getMaximum(Calendar.HOUR_OF_DAY)+1,
jtulach@1334: count, maxIntCount, buffer);
jtulach@1334: else
jtulach@1334: zeroPaddingNumber(value, count, maxIntCount, buffer);
jtulach@1334: }
jtulach@1334: break;
jtulach@1334:
jtulach@1334: case PATTERN_DAY_OF_WEEK: // 'E'
jtulach@1334: if (useDateFormatSymbols) {
jtulach@1334: String[] weekdays;
jtulach@1334: if (count >= 4) {
jtulach@1334: weekdays = formatData.getWeekdays();
jtulach@1334: current = weekdays[value];
jtulach@1334: } else { // count < 4, use abbreviated form if exists
jtulach@1334: weekdays = formatData.getShortWeekdays();
jtulach@1334: current = weekdays[value];
jtulach@1334: }
jtulach@1334: }
jtulach@1334: break;
jtulach@1334:
jtulach@1334: case PATTERN_AM_PM: // 'a'
jtulach@1334: if (useDateFormatSymbols) {
jtulach@1334: String[] ampm = formatData.getAmPmStrings();
jtulach@1334: current = ampm[value];
jtulach@1334: }
jtulach@1334: break;
jtulach@1334:
jtulach@1334: case PATTERN_HOUR1: // 'h' 1-based. eg, 11PM + 1 hour =>> 12 AM
jtulach@1334: if (current == null) {
jtulach@1334: if (value == 0)
jtulach@1334: zeroPaddingNumber(calendar.getLeastMaximum(Calendar.HOUR)+1,
jtulach@1334: count, maxIntCount, buffer);
jtulach@1334: else
jtulach@1334: zeroPaddingNumber(value, count, maxIntCount, buffer);
jtulach@1334: }
jtulach@1334: break;
jtulach@1334:
jtulach@1334: case PATTERN_ZONE_NAME: // 'z'
jtulach@1334: if (current == null) {
jtulach@1334: if (formatData.locale == null || formatData.isZoneStringsSet) {
jtulach@1334: int zoneIndex =
jtulach@1334: formatData.getZoneIndex(calendar.getTimeZone().getID());
jtulach@1334: if (zoneIndex == -1) {
jtulach@1334: value = calendar.get(Calendar.ZONE_OFFSET) +
jtulach@1334: calendar.get(Calendar.DST_OFFSET);
jtulach@1334: buffer.append(ZoneInfoFile.toCustomID(value));
jtulach@1334: } else {
jtulach@1334: int index = (calendar.get(Calendar.DST_OFFSET) == 0) ? 1: 3;
jtulach@1334: if (count < 4) {
jtulach@1334: // Use the short name
jtulach@1334: index++;
jtulach@1334: }
jtulach@1334: String[][] zoneStrings = formatData.getZoneStringsWrapper();
jtulach@1334: buffer.append(zoneStrings[zoneIndex][index]);
jtulach@1334: }
jtulach@1334: } else {
jtulach@1334: TimeZone tz = calendar.getTimeZone();
jtulach@1334: boolean daylight = (calendar.get(Calendar.DST_OFFSET) != 0);
jtulach@1334: int tzstyle = (count < 4 ? TimeZone.SHORT : TimeZone.LONG);
jtulach@1334: buffer.append(tz.getDisplayName(daylight, tzstyle, formatData.locale));
jtulach@1334: }
jtulach@1334: }
jtulach@1334: break;
jtulach@1334:
jtulach@1334: case PATTERN_ZONE_VALUE: // 'Z' ("-/+hhmm" form)
jtulach@1334: value = (calendar.get(Calendar.ZONE_OFFSET) +
jtulach@1334: calendar.get(Calendar.DST_OFFSET)) / 60000;
jtulach@1334:
jtulach@1334: int width = 4;
jtulach@1334: if (value >= 0) {
jtulach@1334: buffer.append('+');
jtulach@1334: } else {
jtulach@1334: width++;
jtulach@1334: }
jtulach@1334:
jtulach@1334: int num = (value / 60) * 100 + (value % 60);
jtulach@1334: CalendarUtils.sprintf0d(buffer, num, width);
jtulach@1334: break;
jtulach@1334:
jtulach@1334: case PATTERN_ISO_ZONE: // 'X'
jtulach@1334: value = calendar.get(Calendar.ZONE_OFFSET)
jtulach@1334: + calendar.get(Calendar.DST_OFFSET);
jtulach@1334:
jtulach@1334: if (value == 0) {
jtulach@1334: buffer.append('Z');
jtulach@1334: break;
jtulach@1334: }
jtulach@1334:
jtulach@1334: value /= 60000;
jtulach@1334: if (value >= 0) {
jtulach@1334: buffer.append('+');
jtulach@1334: } else {
jtulach@1334: buffer.append('-');
jtulach@1334: value = -value;
jtulach@1334: }
jtulach@1334:
jtulach@1334: CalendarUtils.sprintf0d(buffer, value / 60, 2);
jtulach@1334: if (count == 1) {
jtulach@1334: break;
jtulach@1334: }
jtulach@1334:
jtulach@1334: if (count == 3) {
jtulach@1334: buffer.append(':');
jtulach@1334: }
jtulach@1334: CalendarUtils.sprintf0d(buffer, value % 60, 2);
jtulach@1334: break;
jtulach@1334:
jtulach@1334: default:
jtulach@1334: // case PATTERN_DAY_OF_MONTH: // 'd'
jtulach@1334: // case PATTERN_HOUR_OF_DAY0: // 'H' 0-based. eg, 23:59 + 1 hour =>> 00:59
jtulach@1334: // case PATTERN_MINUTE: // 'm'
jtulach@1334: // case PATTERN_SECOND: // 's'
jtulach@1334: // case PATTERN_MILLISECOND: // 'S'
jtulach@1334: // case PATTERN_DAY_OF_YEAR: // 'D'
jtulach@1334: // case PATTERN_DAY_OF_WEEK_IN_MONTH: // 'F'
jtulach@1334: // case PATTERN_WEEK_OF_YEAR: // 'w'
jtulach@1334: // case PATTERN_WEEK_OF_MONTH: // 'W'
jtulach@1334: // case PATTERN_HOUR0: // 'K' eg, 11PM + 1 hour =>> 0 AM
jtulach@1334: // case PATTERN_ISO_DAY_OF_WEEK: // 'u' pseudo field, Monday = 1, ..., Sunday = 7
jtulach@1334: if (current == null) {
jtulach@1334: zeroPaddingNumber(value, count, maxIntCount, buffer);
jtulach@1334: }
jtulach@1334: break;
jtulach@1334: } // switch (patternCharIndex)
jtulach@1334:
jtulach@1334: if (current != null) {
jtulach@1334: buffer.append(current);
jtulach@1334: }
jtulach@1334:
jtulach@1334: int fieldID = PATTERN_INDEX_TO_DATE_FORMAT_FIELD[patternCharIndex];
jtulach@1334: Field f = PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID[patternCharIndex];
jtulach@1334:
jtulach@1334: delegate.formatted(fieldID, f, f, beginOffset, buffer.length(), buffer);
jtulach@1334: }
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Formats a number with the specified minimum and maximum number of digits.
jtulach@1334: */
jtulach@1334: private final void zeroPaddingNumber(int value, int minDigits, int maxDigits, StringBuffer buffer)
jtulach@1334: {
jtulach@1334: // Optimization for 1, 2 and 4 digit numbers. This should
jtulach@1334: // cover most cases of formatting date/time related items.
jtulach@1334: // Note: This optimization code assumes that maxDigits is
jtulach@1334: // either 2 or Integer.MAX_VALUE (maxIntCount in format()).
jtulach@1334: try {
jtulach@1334: if (zeroDigit == 0) {
jtulach@1334: zeroDigit = ((DecimalFormat)numberFormat).getDecimalFormatSymbols().getZeroDigit();
jtulach@1334: }
jtulach@1334: if (value >= 0) {
jtulach@1334: if (value < 100 && minDigits >= 1 && minDigits <= 2) {
jtulach@1334: if (value < 10) {
jtulach@1334: if (minDigits == 2) {
jtulach@1334: buffer.append(zeroDigit);
jtulach@1334: }
jtulach@1334: buffer.append((char)(zeroDigit + value));
jtulach@1334: } else {
jtulach@1334: buffer.append((char)(zeroDigit + value / 10));
jtulach@1334: buffer.append((char)(zeroDigit + value % 10));
jtulach@1334: }
jtulach@1334: return;
jtulach@1334: } else if (value >= 1000 && value < 10000) {
jtulach@1334: if (minDigits == 4) {
jtulach@1334: buffer.append((char)(zeroDigit + value / 1000));
jtulach@1334: value %= 1000;
jtulach@1334: buffer.append((char)(zeroDigit + value / 100));
jtulach@1334: value %= 100;
jtulach@1334: buffer.append((char)(zeroDigit + value / 10));
jtulach@1334: buffer.append((char)(zeroDigit + value % 10));
jtulach@1334: return;
jtulach@1334: }
jtulach@1334: if (minDigits == 2 && maxDigits == 2) {
jtulach@1334: zeroPaddingNumber(value % 100, 2, 2, buffer);
jtulach@1334: return;
jtulach@1334: }
jtulach@1334: }
jtulach@1334: }
jtulach@1334: } catch (Exception e) {
jtulach@1334: }
jtulach@1334:
jtulach@1334: numberFormat.setMinimumIntegerDigits(minDigits);
jtulach@1334: numberFormat.setMaximumIntegerDigits(maxDigits);
jtulach@1334: numberFormat.format((long)value, buffer, DontCareFieldPosition.INSTANCE);
jtulach@1334: }
jtulach@1334:
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Parses text from a string to produce a Date
.
jtulach@1334: *
jtulach@1334: * The method attempts to parse text starting at the index given by
jtulach@1334: * pos
.
jtulach@1334: * If parsing succeeds, then the index of pos
is updated
jtulach@1334: * to the index after the last character used (parsing does not necessarily
jtulach@1334: * use all characters up to the end of the string), and the parsed
jtulach@1334: * date is returned. The updated pos
can be used to
jtulach@1334: * indicate the starting point for the next call to this method.
jtulach@1334: * If an error occurs, then the index of pos
is not
jtulach@1334: * changed, the error index of pos
is set to the index of
jtulach@1334: * the character where the error occurred, and null is returned.
jtulach@1334: *
jtulach@1334: *
This parsing operation uses the {@link DateFormat#calendar
jtulach@1334: * calendar} to produce a {@code Date}. All of the {@code
jtulach@1334: * calendar}'s date-time fields are {@linkplain Calendar#clear()
jtulach@1334: * cleared} before parsing, and the {@code calendar}'s default
jtulach@1334: * values of the date-time fields are used for any missing
jtulach@1334: * date-time information. For example, the year value of the
jtulach@1334: * parsed {@code Date} is 1970 with {@link GregorianCalendar} if
jtulach@1334: * no year value is given from the parsing operation. The {@code
jtulach@1334: * TimeZone} value may be overwritten, depending on the given
jtulach@1334: * pattern and the time zone value in {@code text}. Any {@code
jtulach@1334: * TimeZone} value that has previously been set by a call to
jtulach@1334: * {@link #setTimeZone(java.util.TimeZone) setTimeZone} may need
jtulach@1334: * to be restored for further operations.
jtulach@1334: *
jtulach@1334: * @param text A
jtulach@1334: * @exception InvalidObjectException if the pattern is invalid
jtulach@1334: */
jtulach@1334: private void readObject(ObjectInputStream stream)
jtulach@1334: throws IOException, ClassNotFoundException {
jtulach@1334: stream.defaultReadObject();
jtulach@1334:
jtulach@1334: try {
jtulach@1334: compiledPattern = compile(pattern);
jtulach@1334: } catch (Exception e) {
jtulach@1334: throw new InvalidObjectException("invalid pattern");
jtulach@1334: }
jtulach@1334:
jtulach@1334: if (serialVersionOnStream < 1) {
jtulach@1334: // didn't have defaultCenturyStart field
jtulach@1334: initializeDefaultCentury();
jtulach@1334: }
jtulach@1334: else {
jtulach@1334: // fill in dependent transient field
jtulach@1334: parseAmbiguousDatesAsAfter(defaultCenturyStart);
jtulach@1334: }
jtulach@1334: serialVersionOnStream = currentSerialVersion;
jtulach@1334:
jtulach@1334: // If the deserialized object has a SimpleTimeZone, try
jtulach@1334: // to replace it with a ZoneInfo equivalent in order to
jtulach@1334: // be compatible with the SimpleTimeZone-based
jtulach@1334: // implementation as much as possible.
jtulach@1334: TimeZone tz = getTimeZone();
jtulach@1334: if (tz instanceof SimpleTimeZone) {
jtulach@1334: String id = tz.getID();
jtulach@1334: TimeZone zi = TimeZone.getTimeZone(id);
jtulach@1334: if (zi != null && zi.hasSameRules(tz) && zi.getID().equals(id)) {
jtulach@1334: setTimeZone(zi);
jtulach@1334: }
jtulach@1334: }
jtulach@1334: }
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Analyze the negative subpattern of DecimalFormat and set/update values
jtulach@1334: * as necessary.
jtulach@1334: */
jtulach@1334: private void checkNegativeNumberExpression() {
jtulach@1334: if ((numberFormat instanceof DecimalFormat) &&
jtulach@1334: !numberFormat.equals(originalNumberFormat)) {
jtulach@1334: String numberPattern = ((DecimalFormat)numberFormat).toPattern();
jtulach@1334: if (!numberPattern.equals(originalNumberPattern)) {
jtulach@1334: hasFollowingMinusSign = false;
jtulach@1334:
jtulach@1334: int separatorIndex = numberPattern.indexOf(';');
jtulach@1334: // If the negative subpattern is not absent, we have to analayze
jtulach@1334: // it in order to check if it has a following minus sign.
jtulach@1334: if (separatorIndex > -1) {
jtulach@1334: int minusIndex = numberPattern.indexOf('-', separatorIndex);
jtulach@1334: if ((minusIndex > numberPattern.lastIndexOf('0')) &&
jtulach@1334: (minusIndex > numberPattern.lastIndexOf('#'))) {
jtulach@1334: hasFollowingMinusSign = true;
jtulach@1334: minusSign = ((DecimalFormat)numberFormat).getDecimalFormatSymbols().getMinusSign();
jtulach@1334: }
jtulach@1334: }
jtulach@1334: originalNumberPattern = numberPattern;
jtulach@1334: }
jtulach@1334: originalNumberFormat = numberFormat;
jtulach@1334: }
jtulach@1334: }
jtulach@1334:
jtulach@1334: }
String
, part of which should be parsed.
jtulach@1334: * @param pos A ParsePosition
object with index and error
jtulach@1334: * index information as described above.
jtulach@1334: * @return A Date
parsed from the string. In case of
jtulach@1334: * error, returns null.
jtulach@1334: * @exception NullPointerException if text
or pos
is null.
jtulach@1334: */
jtulach@1334: public Date parse(String text, ParsePosition pos)
jtulach@1334: {
jtulach@1334: checkNegativeNumberExpression();
jtulach@1334:
jtulach@1334: int start = pos.index;
jtulach@1334: int oldStart = start;
jtulach@1334: int textLength = text.length();
jtulach@1334:
jtulach@1334: boolean[] ambiguousYear = {false};
jtulach@1334:
jtulach@1334: CalendarBuilder calb = new CalendarBuilder();
jtulach@1334:
jtulach@1334: for (int i = 0; i < compiledPattern.length; ) {
jtulach@1334: int tag = compiledPattern[i] >>> 8;
jtulach@1334: int count = compiledPattern[i++] & 0xff;
jtulach@1334: if (count == 255) {
jtulach@1334: count = compiledPattern[i++] << 16;
jtulach@1334: count |= compiledPattern[i++];
jtulach@1334: }
jtulach@1334:
jtulach@1334: switch (tag) {
jtulach@1334: case TAG_QUOTE_ASCII_CHAR:
jtulach@1334: if (start >= textLength || text.charAt(start) != (char)count) {
jtulach@1334: pos.index = oldStart;
jtulach@1334: pos.errorIndex = start;
jtulach@1334: return null;
jtulach@1334: }
jtulach@1334: start++;
jtulach@1334: break;
jtulach@1334:
jtulach@1334: case TAG_QUOTE_CHARS:
jtulach@1334: while (count-- > 0) {
jtulach@1334: if (start >= textLength || text.charAt(start) != compiledPattern[i++]) {
jtulach@1334: pos.index = oldStart;
jtulach@1334: pos.errorIndex = start;
jtulach@1334: return null;
jtulach@1334: }
jtulach@1334: start++;
jtulach@1334: }
jtulach@1334: break;
jtulach@1334:
jtulach@1334: default:
jtulach@1334: // Peek the next pattern to determine if we need to
jtulach@1334: // obey the number of pattern letters for
jtulach@1334: // parsing. It's required when parsing contiguous
jtulach@1334: // digit text (e.g., "20010704") with a pattern which
jtulach@1334: // has no delimiters between fields, like "yyyyMMdd".
jtulach@1334: boolean obeyCount = false;
jtulach@1334:
jtulach@1334: // In Arabic, a minus sign for a negative number is put after
jtulach@1334: // the number. Even in another locale, a minus sign can be
jtulach@1334: // put after a number using DateFormat.setNumberFormat().
jtulach@1334: // If both the minus sign and the field-delimiter are '-',
jtulach@1334: // subParse() needs to determine whether a '-' after a number
jtulach@1334: // in the given text is a delimiter or is a minus sign for the
jtulach@1334: // preceding number. We give subParse() a clue based on the
jtulach@1334: // information in compiledPattern.
jtulach@1334: boolean useFollowingMinusSignAsDelimiter = false;
jtulach@1334:
jtulach@1334: if (i < compiledPattern.length) {
jtulach@1334: int nextTag = compiledPattern[i] >>> 8;
jtulach@1334: if (!(nextTag == TAG_QUOTE_ASCII_CHAR ||
jtulach@1334: nextTag == TAG_QUOTE_CHARS)) {
jtulach@1334: obeyCount = true;
jtulach@1334: }
jtulach@1334:
jtulach@1334: if (hasFollowingMinusSign &&
jtulach@1334: (nextTag == TAG_QUOTE_ASCII_CHAR ||
jtulach@1334: nextTag == TAG_QUOTE_CHARS)) {
jtulach@1334: int c;
jtulach@1334: if (nextTag == TAG_QUOTE_ASCII_CHAR) {
jtulach@1334: c = compiledPattern[i] & 0xff;
jtulach@1334: } else {
jtulach@1334: c = compiledPattern[i+1];
jtulach@1334: }
jtulach@1334:
jtulach@1334: if (c == minusSign) {
jtulach@1334: useFollowingMinusSignAsDelimiter = true;
jtulach@1334: }
jtulach@1334: }
jtulach@1334: }
jtulach@1334: start = subParse(text, start, tag, count, obeyCount,
jtulach@1334: ambiguousYear, pos,
jtulach@1334: useFollowingMinusSignAsDelimiter, calb);
jtulach@1334: if (start < 0) {
jtulach@1334: pos.index = oldStart;
jtulach@1334: return null;
jtulach@1334: }
jtulach@1334: }
jtulach@1334: }
jtulach@1334:
jtulach@1334: // At this point the fields of Calendar have been set. Calendar
jtulach@1334: // will fill in default values for missing fields when the time
jtulach@1334: // is computed.
jtulach@1334:
jtulach@1334: pos.index = start;
jtulach@1334:
jtulach@1334: Date parsedDate;
jtulach@1334: try {
jtulach@1334: parsedDate = calb.establish(calendar).getTime();
jtulach@1334: // If the year value is ambiguous,
jtulach@1334: // then the two-digit year == the default start year
jtulach@1334: if (ambiguousYear[0]) {
jtulach@1334: if (parsedDate.before(defaultCenturyStart)) {
jtulach@1334: parsedDate = calb.addYear(100).establish(calendar).getTime();
jtulach@1334: }
jtulach@1334: }
jtulach@1334: }
jtulach@1334: // An IllegalArgumentException will be thrown by Calendar.getTime()
jtulach@1334: // if any fields are out of range, e.g., MONTH == 17.
jtulach@1334: catch (IllegalArgumentException e) {
jtulach@1334: pos.errorIndex = start;
jtulach@1334: pos.index = oldStart;
jtulach@1334: return null;
jtulach@1334: }
jtulach@1334:
jtulach@1334: return parsedDate;
jtulach@1334: }
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Private code-size reduction function used by subParse.
jtulach@1334: * @param text the time text being parsed.
jtulach@1334: * @param start where to start parsing.
jtulach@1334: * @param field the date field being parsed.
jtulach@1334: * @param data the string array to parsed.
jtulach@1334: * @return the new start position if matching succeeded; a negative number
jtulach@1334: * indicating matching failure, otherwise.
jtulach@1334: */
jtulach@1334: private int matchString(String text, int start, int field, String[] data, CalendarBuilder calb)
jtulach@1334: {
jtulach@1334: int i = 0;
jtulach@1334: int count = data.length;
jtulach@1334:
jtulach@1334: if (field == Calendar.DAY_OF_WEEK) i = 1;
jtulach@1334:
jtulach@1334: // There may be multiple strings in the data[] array which begin with
jtulach@1334: // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
jtulach@1334: // We keep track of the longest match, and return that. Note that this
jtulach@1334: // unfortunately requires us to test all array elements.
jtulach@1334: int bestMatchLength = 0, bestMatch = -1;
jtulach@1334: for (; iSimpleDateFormat
. This also
jtulach@1334: * clones the format's date format symbols.
jtulach@1334: *
jtulach@1334: * @return a clone of this SimpleDateFormat
jtulach@1334: */
jtulach@1334: public Object clone() {
jtulach@1334: SimpleDateFormat other = (SimpleDateFormat) super.clone();
jtulach@1334: other.formatData = (DateFormatSymbols) formatData.clone();
jtulach@1334: return other;
jtulach@1334: }
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Returns the hash code value for this SimpleDateFormat
object.
jtulach@1334: *
jtulach@1334: * @return the hash code value for this SimpleDateFormat
object.
jtulach@1334: */
jtulach@1334: public int hashCode()
jtulach@1334: {
jtulach@1334: return pattern.hashCode();
jtulach@1334: // just enough fields for a reasonable distribution
jtulach@1334: }
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * Compares the given object with this SimpleDateFormat
for
jtulach@1334: * equality.
jtulach@1334: *
jtulach@1334: * @return true if the given object is equal to this
jtulach@1334: * SimpleDateFormat
jtulach@1334: */
jtulach@1334: public boolean equals(Object obj)
jtulach@1334: {
jtulach@1334: if (!super.equals(obj)) return false; // super does class check
jtulach@1334: SimpleDateFormat that = (SimpleDateFormat) obj;
jtulach@1334: return (pattern.equals(that.pattern)
jtulach@1334: && formatData.equals(that.formatData));
jtulach@1334: }
jtulach@1334:
jtulach@1334: /**
jtulach@1334: * After reading an object from the input stream, the format
jtulach@1334: * pattern in the object is verified.
jtulach@1334: *