You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ba...@apache.org on 2012/01/26 08:00:27 UTC
svn commit: r1236055 [2/2] - in /commons/proper/lang/trunk: ./
src/main/java/org/apache/commons/lang3/time/
src/test/java/org/apache/commons/lang3/time/
Added: commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java?rev=1236055&view=auto
==============================================================================
--- commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java (added)
+++ commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java Thu Jan 26 07:00:26 2012
@@ -0,0 +1,1209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.time;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.text.DateFormat;
+import java.text.DateFormatSymbols;
+import java.text.FieldPosition;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.commons.lang3.Validate;
+
+/**
+ * <p>FastDatePrinter is a fast and thread-safe version of
+ * {@link java.text.SimpleDateFormat}.</p>
+ *
+ * <p>This class can be used as a direct replacement to
+ * {@code SimpleDateFormat} in most formatting situations.
+ * This class is especially useful in multi-threaded server environments.
+ * {@code SimpleDateFormat} is not thread-safe in any JDK version,
+ * nor will it be as Sun have closed the bug/RFE.
+ * </p>
+ *
+ * <p>Only formatting is supported, but all patterns are compatible with
+ * SimpleDateFormat (except time zones and some year patterns - see below).</p>
+ *
+ * <p>Java 1.4 introduced a new pattern letter, {@code 'Z'}, to represent
+ * time zones in RFC822 format (eg. {@code +0800} or {@code -1100}).
+ * This pattern letter can be used here (on all JDK versions).</p>
+ *
+ * <p>In addition, the pattern {@code 'ZZ'} has been made to represent
+ * ISO8601 full format time zones (eg. {@code +08:00} or {@code -11:00}).
+ * This introduces a minor incompatibility with Java 1.4, but at a gain of
+ * useful functionality.</p>
+ *
+ * <p>Javadoc cites for the year pattern: <i>For formatting, if the number of
+ * pattern letters is 2, the year is truncated to 2 digits; otherwise it is
+ * interpreted as a number.</i> Starting with Java 1.7 a pattern of 'Y' or
+ * 'YYY' will be formatted as '2003', while it was '03' in former Java
+ * versions. FastDatePrinter implements the behavior of Java 7.</p>
+ *
+ * @since 3.2
+ */
+public class FastDatePrinter implements DatePrinter, Serializable {
+ // A lot of the speed in this class comes from caching, but some comes
+ // from the special int to StringBuffer conversion.
+ //
+ // The following produces a padded 2 digit number:
+ // buffer.append((char)(value / 10 + '0'));
+ // buffer.append((char)(value % 10 + '0'));
+ //
+ // Note that the fastest append to StringBuffer is a single char (used here).
+ // Note that Integer.toString() is not called, the conversion is simply
+ // taking the value and adding (mathematically) the ASCII value for '0'.
+ // So, don't change this code! It works and is very fast.
+
+ /**
+ * Required for serialization support.
+ *
+ * @see java.io.Serializable
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * FULL locale dependent date or time style.
+ */
+ public static final int FULL = DateFormat.FULL;
+ /**
+ * LONG locale dependent date or time style.
+ */
+ public static final int LONG = DateFormat.LONG;
+ /**
+ * MEDIUM locale dependent date or time style.
+ */
+ public static final int MEDIUM = DateFormat.MEDIUM;
+ /**
+ * SHORT locale dependent date or time style.
+ */
+ public static final int SHORT = DateFormat.SHORT;
+
+ /**
+ * The pattern.
+ */
+ private final String mPattern;
+ /**
+ * The time zone.
+ */
+ private final TimeZone mTimeZone;
+ /**
+ * The locale.
+ */
+ private final Locale mLocale;
+ /**
+ * The parsed rules.
+ */
+ private transient Rule[] mRules;
+ /**
+ * The estimated maximum length.
+ */
+ private transient int mMaxLengthEstimate;
+
+ // Constructor
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Constructs a new FastDatePrinter.</p>
+ *
+ * @param pattern {@link java.text.SimpleDateFormat} compatible pattern
+ * @param timeZone non-null time zone to use
+ * @param locale non-null locale to use
+ * @throws NullPointerException if pattern, timeZone, or locale is null.
+ */
+ protected FastDatePrinter(String pattern, TimeZone timeZone, Locale locale) {
+ mPattern = pattern;
+ mTimeZone = timeZone;
+ mLocale = locale;
+
+ init();
+ }
+
+ /**
+ * <p>Initializes the instance for first use.</p>
+ */
+ private void init() {
+ List<Rule> rulesList = parsePattern();
+ mRules = rulesList.toArray(new Rule[rulesList.size()]);
+
+ int len = 0;
+ for (int i=mRules.length; --i >= 0; ) {
+ len += mRules[i].estimateLength();
+ }
+
+ mMaxLengthEstimate = len;
+ }
+
+ // Parse the pattern
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Returns a list of Rules given a pattern.</p>
+ *
+ * @return a {@code List} of Rule objects
+ * @throws IllegalArgumentException if pattern is invalid
+ */
+ protected List<Rule> parsePattern() {
+ DateFormatSymbols symbols = new DateFormatSymbols(mLocale);
+ List<Rule> rules = new ArrayList<Rule>();
+
+ String[] ERAs = symbols.getEras();
+ String[] months = symbols.getMonths();
+ String[] shortMonths = symbols.getShortMonths();
+ String[] weekdays = symbols.getWeekdays();
+ String[] shortWeekdays = symbols.getShortWeekdays();
+ String[] AmPmStrings = symbols.getAmPmStrings();
+
+ int length = mPattern.length();
+ int[] indexRef = new int[1];
+
+ for (int i = 0; i < length; i++) {
+ indexRef[0] = i;
+ String token = parseToken(mPattern, indexRef);
+ i = indexRef[0];
+
+ int tokenLen = token.length();
+ if (tokenLen == 0) {
+ break;
+ }
+
+ Rule rule;
+ char c = token.charAt(0);
+
+ switch (c) {
+ case 'G': // era designator (text)
+ rule = new TextField(Calendar.ERA, ERAs);
+ break;
+ case 'y': // year (number)
+ if (tokenLen == 2) {
+ rule = TwoDigitYearField.INSTANCE;
+ } else {
+ rule = selectNumberRule(Calendar.YEAR, tokenLen < 4 ? 4 : tokenLen);
+ }
+ break;
+ case 'M': // month in year (text and number)
+ if (tokenLen >= 4) {
+ rule = new TextField(Calendar.MONTH, months);
+ } else if (tokenLen == 3) {
+ rule = new TextField(Calendar.MONTH, shortMonths);
+ } else if (tokenLen == 2) {
+ rule = TwoDigitMonthField.INSTANCE;
+ } else {
+ rule = UnpaddedMonthField.INSTANCE;
+ }
+ break;
+ case 'd': // day in month (number)
+ rule = selectNumberRule(Calendar.DAY_OF_MONTH, tokenLen);
+ break;
+ case 'h': // hour in am/pm (number, 1..12)
+ rule = new TwelveHourField(selectNumberRule(Calendar.HOUR, tokenLen));
+ break;
+ case 'H': // hour in day (number, 0..23)
+ rule = selectNumberRule(Calendar.HOUR_OF_DAY, tokenLen);
+ break;
+ case 'm': // minute in hour (number)
+ rule = selectNumberRule(Calendar.MINUTE, tokenLen);
+ break;
+ case 's': // second in minute (number)
+ rule = selectNumberRule(Calendar.SECOND, tokenLen);
+ break;
+ case 'S': // millisecond (number)
+ rule = selectNumberRule(Calendar.MILLISECOND, tokenLen);
+ break;
+ case 'E': // day in week (text)
+ rule = new TextField(Calendar.DAY_OF_WEEK, tokenLen < 4 ? shortWeekdays : weekdays);
+ break;
+ case 'D': // day in year (number)
+ rule = selectNumberRule(Calendar.DAY_OF_YEAR, tokenLen);
+ break;
+ case 'F': // day of week in month (number)
+ rule = selectNumberRule(Calendar.DAY_OF_WEEK_IN_MONTH, tokenLen);
+ break;
+ case 'w': // week in year (number)
+ rule = selectNumberRule(Calendar.WEEK_OF_YEAR, tokenLen);
+ break;
+ case 'W': // week in month (number)
+ rule = selectNumberRule(Calendar.WEEK_OF_MONTH, tokenLen);
+ break;
+ case 'a': // am/pm marker (text)
+ rule = new TextField(Calendar.AM_PM, AmPmStrings);
+ break;
+ case 'k': // hour in day (1..24)
+ rule = new TwentyFourHourField(selectNumberRule(Calendar.HOUR_OF_DAY, tokenLen));
+ break;
+ case 'K': // hour in am/pm (0..11)
+ rule = selectNumberRule(Calendar.HOUR, tokenLen);
+ break;
+ case 'z': // time zone (text)
+ if (tokenLen >= 4) {
+ rule = new TimeZoneNameRule(mTimeZone, mLocale, TimeZone.LONG);
+ } else {
+ rule = new TimeZoneNameRule(mTimeZone, mLocale, TimeZone.SHORT);
+ }
+ break;
+ case 'Z': // time zone (value)
+ if (tokenLen == 1) {
+ rule = TimeZoneNumberRule.INSTANCE_NO_COLON;
+ } else {
+ rule = TimeZoneNumberRule.INSTANCE_COLON;
+ }
+ break;
+ case '\'': // literal text
+ String sub = token.substring(1);
+ if (sub.length() == 1) {
+ rule = new CharacterLiteral(sub.charAt(0));
+ } else {
+ rule = new StringLiteral(sub);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Illegal pattern component: " + token);
+ }
+
+ rules.add(rule);
+ }
+
+ return rules;
+ }
+
+ /**
+ * <p>Performs the parsing of tokens.</p>
+ *
+ * @param pattern the pattern
+ * @param indexRef index references
+ * @return parsed token
+ */
+ protected String parseToken(String pattern, int[] indexRef) {
+ StringBuilder buf = new StringBuilder();
+
+ int i = indexRef[0];
+ int length = pattern.length();
+
+ char c = pattern.charAt(i);
+ if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z') {
+ // Scan a run of the same character, which indicates a time
+ // pattern.
+ buf.append(c);
+
+ while (i + 1 < length) {
+ char peek = pattern.charAt(i + 1);
+ if (peek == c) {
+ buf.append(c);
+ i++;
+ } else {
+ break;
+ }
+ }
+ } else {
+ // This will identify token as text.
+ buf.append('\'');
+
+ boolean inLiteral = false;
+
+ for (; i < length; i++) {
+ c = pattern.charAt(i);
+
+ if (c == '\'') {
+ if (i + 1 < length && pattern.charAt(i + 1) == '\'') {
+ // '' is treated as escaped '
+ i++;
+ buf.append(c);
+ } else {
+ inLiteral = !inLiteral;
+ }
+ } else if (!inLiteral &&
+ (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z')) {
+ i--;
+ break;
+ } else {
+ buf.append(c);
+ }
+ }
+ }
+
+ indexRef[0] = i;
+ return buf.toString();
+ }
+
+ /**
+ * <p>Gets an appropriate rule for the padding required.</p>
+ *
+ * @param field the field to get a rule for
+ * @param padding the padding required
+ * @return a new rule with the correct padding
+ */
+ protected NumberRule selectNumberRule(int field, int padding) {
+ switch (padding) {
+ case 1:
+ return new UnpaddedNumberField(field);
+ case 2:
+ return new TwoDigitNumberField(field);
+ default:
+ return new PaddedNumberField(field, padding);
+ }
+ }
+
+ // Format methods
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Formats a {@code Date}, {@code Calendar} or
+ * {@code Long} (milliseconds) object.</p>
+ *
+ * @param obj the object to format
+ * @param toAppendTo the buffer to append to
+ * @param pos the position - ignored
+ * @return the buffer passed in
+ */
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
+ if (obj instanceof Date) {
+ return format((Date) obj, toAppendTo);
+ } else if (obj instanceof Calendar) {
+ return format((Calendar) obj, toAppendTo);
+ } else if (obj instanceof Long) {
+ return format(((Long) obj).longValue(), toAppendTo);
+ } else {
+ throw new IllegalArgumentException("Unknown class: " +
+ (obj == null ? "<null>" : obj.getClass().getName()));
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.commons.lang3.time.DatePrinter#format(long)
+ */
+ @Override
+ public String format(long millis) {
+ return format(new Date(millis));
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.commons.lang3.time.DatePrinter#format(java.util.Date)
+ */
+ @Override
+ public String format(Date date) {
+ Calendar c = new GregorianCalendar(mTimeZone, mLocale); // hard code GregorianCalendar
+ c.setTime(date);
+ return applyRules(c, new StringBuffer(mMaxLengthEstimate)).toString();
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.commons.lang3.time.DatePrinter#format(java.util.Calendar)
+ */
+ @Override
+ public String format(Calendar calendar) {
+ return format(calendar, new StringBuffer(mMaxLengthEstimate)).toString();
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.commons.lang3.time.DatePrinter#format(long, java.lang.StringBuffer)
+ */
+ @Override
+ public StringBuffer format(long millis, StringBuffer buf) {
+ return format(new Date(millis), buf);
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.commons.lang3.time.DatePrinter#format(java.util.Date, java.lang.StringBuffer)
+ */
+ @Override
+ public StringBuffer format(Date date, StringBuffer buf) {
+ Calendar c = new GregorianCalendar(mTimeZone, mLocale); // hard code GregorianCalendar
+ c.setTime(date);
+ return applyRules(c, buf);
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.commons.lang3.time.DatePrinter#format(java.util.Calendar, java.lang.StringBuffer)
+ */
+ @Override
+ public StringBuffer format(Calendar calendar, StringBuffer buf) {
+ return applyRules(calendar, buf);
+ }
+
+ /**
+ * <p>Performs the formatting by applying the rules to the
+ * specified calendar.</p>
+ *
+ * @param calendar the calendar to format
+ * @param buf the buffer to format into
+ * @return the specified string buffer
+ */
+ protected StringBuffer applyRules(Calendar calendar, StringBuffer buf) {
+ for (Rule rule : mRules) {
+ rule.appendTo(buf, calendar);
+ }
+ return buf;
+ }
+
+ // Accessors
+ //-----------------------------------------------------------------------
+ /* (non-Javadoc)
+ * @see org.apache.commons.lang3.time.DatePrinter#getPattern()
+ */
+ @Override
+ public String getPattern() {
+ return mPattern;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.commons.lang3.time.DatePrinter#getTimeZone()
+ */
+ @Override
+ public TimeZone getTimeZone() {
+ return mTimeZone;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.commons.lang3.time.DatePrinter#getLocale()
+ */
+ @Override
+ public Locale getLocale() {
+ return mLocale;
+ }
+
+ /**
+ * <p>Gets an estimate for the maximum string length that the
+ * formatter will produce.</p>
+ *
+ * <p>The actual formatted length will almost always be less than or
+ * equal to this amount.</p>
+ *
+ * @return the maximum formatted length
+ */
+ public int getMaxLengthEstimate() {
+ return mMaxLengthEstimate;
+ }
+
+ // Basics
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Compares two objects for equality.</p>
+ *
+ * @param obj the object to compare to
+ * @return {@code true} if equal
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof FastDatePrinter == false) {
+ return false;
+ }
+ FastDatePrinter other = (FastDatePrinter) obj;
+ return mPattern.equals(other.mPattern)
+ && mTimeZone.equals(other.mTimeZone)
+ && mLocale.equals(other.mLocale);
+ }
+
+ /**
+ * <p>Returns a hashcode compatible with equals.</p>
+ *
+ * @return a hashcode compatible with equals
+ */
+ @Override
+ public int hashCode() {
+ return mPattern.hashCode() + 13 * (mTimeZone.hashCode() + 13 * mLocale.hashCode());
+ }
+
+ /**
+ * <p>Gets a debugging string version of this formatter.</p>
+ *
+ * @return a debugging string
+ */
+ @Override
+ public String toString() {
+ return "FastDatePrinter[" + mPattern + "," + mLocale + "," + mTimeZone.getID() + "]";
+ }
+
+ // Serializing
+ //-----------------------------------------------------------------------
+ /**
+ * Create the object after serialization. This implementation reinitializes the
+ * transient properties.
+ *
+ * @param in ObjectInputStream from which the object is being deserialized.
+ * @throws IOException if there is an IO issue.
+ * @throws ClassNotFoundException if a class cannot be found.
+ */
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ init();
+ }
+
+ // Rules
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Inner class defining a rule.</p>
+ */
+ private interface Rule {
+ /**
+ * Returns the estimated lentgh of the result.
+ *
+ * @return the estimated length
+ */
+ int estimateLength();
+
+ /**
+ * Appends the value of the specified calendar to the output buffer based on the rule implementation.
+ *
+ * @param buffer the output buffer
+ * @param calendar calendar to be appended
+ */
+ void appendTo(StringBuffer buffer, Calendar calendar);
+ }
+
+ /**
+ * <p>Inner class defining a numeric rule.</p>
+ */
+ private interface NumberRule extends Rule {
+ /**
+ * Appends the specified value to the output buffer based on the rule implementation.
+ *
+ * @param buffer the output buffer
+ * @param value the value to be appended
+ */
+ void appendTo(StringBuffer buffer, int value);
+ }
+
+ /**
+ * <p>Inner class to output a constant single character.</p>
+ */
+ private static class CharacterLiteral implements Rule {
+ private final char mValue;
+
+ /**
+ * Constructs a new instance of {@code CharacterLiteral}
+ * to hold the specified value.
+ *
+ * @param value the character literal
+ */
+ CharacterLiteral(char value) {
+ mValue = value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int estimateLength() {
+ return 1;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ buffer.append(mValue);
+ }
+ }
+
+ /**
+ * <p>Inner class to output a constant string.</p>
+ */
+ private static class StringLiteral implements Rule {
+ private final String mValue;
+
+ /**
+ * Constructs a new instance of {@code StringLiteral}
+ * to hold the specified value.
+ *
+ * @param value the string literal
+ */
+ StringLiteral(String value) {
+ mValue = value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int estimateLength() {
+ return mValue.length();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ buffer.append(mValue);
+ }
+ }
+
+ /**
+ * <p>Inner class to output one of a set of values.</p>
+ */
+ private static class TextField implements Rule {
+ private final int mField;
+ private final String[] mValues;
+
+ /**
+ * Constructs an instance of {@code TextField}
+ * with the specified field and values.
+ *
+ * @param field the field
+ * @param values the field values
+ */
+ TextField(int field, String[] values) {
+ mField = field;
+ mValues = values;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int estimateLength() {
+ int max = 0;
+ for (int i=mValues.length; --i >= 0; ) {
+ int len = mValues[i].length();
+ if (len > max) {
+ max = len;
+ }
+ }
+ return max;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ buffer.append(mValues[calendar.get(mField)]);
+ }
+ }
+
+ /**
+ * <p>Inner class to output an unpadded number.</p>
+ */
+ private static class UnpaddedNumberField implements NumberRule {
+ private final int mField;
+
+ /**
+ * Constructs an instance of {@code UnpadedNumberField} with the specified field.
+ *
+ * @param field the field
+ */
+ UnpaddedNumberField(int field) {
+ mField = field;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int estimateLength() {
+ return 4;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ appendTo(buffer, calendar.get(mField));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final void appendTo(StringBuffer buffer, int value) {
+ if (value < 10) {
+ buffer.append((char)(value + '0'));
+ } else if (value < 100) {
+ buffer.append((char)(value / 10 + '0'));
+ buffer.append((char)(value % 10 + '0'));
+ } else {
+ buffer.append(Integer.toString(value));
+ }
+ }
+ }
+
+ /**
+ * <p>Inner class to output an unpadded month.</p>
+ */
+ private static class UnpaddedMonthField implements NumberRule {
+ static final UnpaddedMonthField INSTANCE = new UnpaddedMonthField();
+
+ /**
+ * Constructs an instance of {@code UnpaddedMonthField}.
+ *
+ */
+ UnpaddedMonthField() {
+ super();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int estimateLength() {
+ return 2;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ appendTo(buffer, calendar.get(Calendar.MONTH) + 1);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final void appendTo(StringBuffer buffer, int value) {
+ if (value < 10) {
+ buffer.append((char)(value + '0'));
+ } else {
+ buffer.append((char)(value / 10 + '0'));
+ buffer.append((char)(value % 10 + '0'));
+ }
+ }
+ }
+
+ /**
+ * <p>Inner class to output a padded number.</p>
+ */
+ private static class PaddedNumberField implements NumberRule {
+ private final int mField;
+ private final int mSize;
+
+ /**
+ * Constructs an instance of {@code PaddedNumberField}.
+ *
+ * @param field the field
+ * @param size size of the output field
+ */
+ PaddedNumberField(int field, int size) {
+ if (size < 3) {
+ // Should use UnpaddedNumberField or TwoDigitNumberField.
+ throw new IllegalArgumentException();
+ }
+ mField = field;
+ mSize = size;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int estimateLength() {
+ return 4;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ appendTo(buffer, calendar.get(mField));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final void appendTo(StringBuffer buffer, int value) {
+ if (value < 100) {
+ for (int i = mSize; --i >= 2; ) {
+ buffer.append('0');
+ }
+ buffer.append((char)(value / 10 + '0'));
+ buffer.append((char)(value % 10 + '0'));
+ } else {
+ int digits;
+ if (value < 1000) {
+ digits = 3;
+ } else {
+ Validate.isTrue(value > -1, "Negative values should not be possible", value);
+ digits = Integer.toString(value).length();
+ }
+ for (int i = mSize; --i >= digits; ) {
+ buffer.append('0');
+ }
+ buffer.append(Integer.toString(value));
+ }
+ }
+ }
+
+ /**
+ * <p>Inner class to output a two digit number.</p>
+ */
+ private static class TwoDigitNumberField implements NumberRule {
+ private final int mField;
+
+ /**
+ * Constructs an instance of {@code TwoDigitNumberField} with the specified field.
+ *
+ * @param field the field
+ */
+ TwoDigitNumberField(int field) {
+ mField = field;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int estimateLength() {
+ return 2;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ appendTo(buffer, calendar.get(mField));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final void appendTo(StringBuffer buffer, int value) {
+ if (value < 100) {
+ buffer.append((char)(value / 10 + '0'));
+ buffer.append((char)(value % 10 + '0'));
+ } else {
+ buffer.append(Integer.toString(value));
+ }
+ }
+ }
+
+ /**
+ * <p>Inner class to output a two digit year.</p>
+ */
+ private static class TwoDigitYearField implements NumberRule {
+ static final TwoDigitYearField INSTANCE = new TwoDigitYearField();
+
+ /**
+ * Constructs an instance of {@code TwoDigitYearField}.
+ */
+ TwoDigitYearField() {
+ super();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int estimateLength() {
+ return 2;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ appendTo(buffer, calendar.get(Calendar.YEAR) % 100);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final void appendTo(StringBuffer buffer, int value) {
+ buffer.append((char)(value / 10 + '0'));
+ buffer.append((char)(value % 10 + '0'));
+ }
+ }
+
+ /**
+ * <p>Inner class to output a two digit month.</p>
+ */
+ private static class TwoDigitMonthField implements NumberRule {
+ static final TwoDigitMonthField INSTANCE = new TwoDigitMonthField();
+
+ /**
+ * Constructs an instance of {@code TwoDigitMonthField}.
+ */
+ TwoDigitMonthField() {
+ super();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int estimateLength() {
+ return 2;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ appendTo(buffer, calendar.get(Calendar.MONTH) + 1);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final void appendTo(StringBuffer buffer, int value) {
+ buffer.append((char)(value / 10 + '0'));
+ buffer.append((char)(value % 10 + '0'));
+ }
+ }
+
+ /**
+ * <p>Inner class to output the twelve hour field.</p>
+ */
+ private static class TwelveHourField implements NumberRule {
+ private final NumberRule mRule;
+
+ /**
+ * Constructs an instance of {@code TwelveHourField} with the specified
+ * {@code NumberRule}.
+ *
+ * @param rule the rule
+ */
+ TwelveHourField(NumberRule rule) {
+ mRule = rule;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int estimateLength() {
+ return mRule.estimateLength();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ int value = calendar.get(Calendar.HOUR);
+ if (value == 0) {
+ value = calendar.getLeastMaximum(Calendar.HOUR) + 1;
+ }
+ mRule.appendTo(buffer, value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void appendTo(StringBuffer buffer, int value) {
+ mRule.appendTo(buffer, value);
+ }
+ }
+
+ /**
+ * <p>Inner class to output the twenty four hour field.</p>
+ */
+ private static class TwentyFourHourField implements NumberRule {
+ private final NumberRule mRule;
+
+ /**
+ * Constructs an instance of {@code TwentyFourHourField} with the specified
+ * {@code NumberRule}.
+ *
+ * @param rule the rule
+ */
+ TwentyFourHourField(NumberRule rule) {
+ mRule = rule;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int estimateLength() {
+ return mRule.estimateLength();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ int value = calendar.get(Calendar.HOUR_OF_DAY);
+ if (value == 0) {
+ value = calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1;
+ }
+ mRule.appendTo(buffer, value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void appendTo(StringBuffer buffer, int value) {
+ mRule.appendTo(buffer, value);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+
+ private static ConcurrentMap<TimeZoneDisplayKey, String> cTimeZoneDisplayCache =
+ new ConcurrentHashMap<TimeZoneDisplayKey, String>(7);
+ /**
+ * <p>Gets the time zone display name, using a cache for performance.</p>
+ *
+ * @param tz the zone to query
+ * @param daylight true if daylight savings
+ * @param style the style to use {@code TimeZone.LONG} or {@code TimeZone.SHORT}
+ * @param locale the locale to use
+ * @return the textual name of the time zone
+ */
+ static String getTimeZoneDisplay(TimeZone tz, boolean daylight, int style, Locale locale) {
+ TimeZoneDisplayKey key = new TimeZoneDisplayKey(tz, daylight, style, locale);
+ String value = cTimeZoneDisplayCache.get(key);
+ if (value == null) {
+ // This is a very slow call, so cache the results.
+ value = tz.getDisplayName(daylight, style, locale);
+ String prior = cTimeZoneDisplayCache.putIfAbsent(key, value);
+ if (prior != null) {
+ value= prior;
+ }
+ }
+ return value;
+ }
+
+ /**
+ * <p>Inner class to output a time zone name.</p>
+ */
+ private static class TimeZoneNameRule implements Rule {
+ private final TimeZone mTimeZone;
+ private final String mStandard;
+ private final String mDaylight;
+
+ /**
+ * Constructs an instance of {@code TimeZoneNameRule} with the specified properties.
+ *
+ * @param timeZone the time zone
+ * @param locale the locale
+ * @param style the style
+ */
+ TimeZoneNameRule(TimeZone timeZone, Locale locale, int style) {
+ mTimeZone = timeZone;
+
+ mStandard = getTimeZoneDisplay(timeZone, false, style, locale);
+ mDaylight = getTimeZoneDisplay(timeZone, true, style, locale);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int estimateLength() {
+ return Math.max(mStandard.length(), mDaylight.length());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ if (mTimeZone.useDaylightTime() && calendar.get(Calendar.DST_OFFSET) != 0) {
+ buffer.append(mDaylight);
+ } else {
+ buffer.append(mStandard);
+ }
+ }
+ }
+
+ /**
+ * <p>Inner class to output a time zone as a number {@code +/-HHMM}
+ * or {@code +/-HH:MM}.</p>
+ */
+ private static class TimeZoneNumberRule implements Rule {
+ static final TimeZoneNumberRule INSTANCE_COLON = new TimeZoneNumberRule(true);
+ static final TimeZoneNumberRule INSTANCE_NO_COLON = new TimeZoneNumberRule(false);
+
+ final boolean mColon;
+
+ /**
+ * Constructs an instance of {@code TimeZoneNumberRule} with the specified properties.
+ *
+ * @param colon add colon between HH and MM in the output if {@code true}
+ */
+ TimeZoneNumberRule(boolean colon) {
+ mColon = colon;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int estimateLength() {
+ return 5;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void appendTo(StringBuffer buffer, Calendar calendar) {
+ int offset = calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET);
+
+ if (offset < 0) {
+ buffer.append('-');
+ offset = -offset;
+ } else {
+ buffer.append('+');
+ }
+
+ int hours = offset / (60 * 60 * 1000);
+ buffer.append((char)(hours / 10 + '0'));
+ buffer.append((char)(hours % 10 + '0'));
+
+ if (mColon) {
+ buffer.append(':');
+ }
+
+ int minutes = offset / (60 * 1000) - 60 * hours;
+ buffer.append((char)(minutes / 10 + '0'));
+ buffer.append((char)(minutes % 10 + '0'));
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ /**
+ * <p>Inner class that acts as a compound key for time zone names.</p>
+ */
+ private static class TimeZoneDisplayKey {
+ private final TimeZone mTimeZone;
+ private final int mStyle;
+ private final Locale mLocale;
+
+ /**
+ * Constructs an instance of {@code TimeZoneDisplayKey} with the specified properties.
+ *
+ * @param timeZone the time zone
+ * @param daylight adjust the style for daylight saving time if {@code true}
+ * @param style the timezone style
+ * @param locale the timezone locale
+ */
+ TimeZoneDisplayKey(TimeZone timeZone,
+ boolean daylight, int style, Locale locale) {
+ mTimeZone = timeZone;
+ if (daylight) {
+ style |= 0x80000000;
+ }
+ mStyle = style;
+ mLocale = locale;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return (mStyle * 31 + mLocale.hashCode() ) * 31 + mTimeZone.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof TimeZoneDisplayKey) {
+ TimeZoneDisplayKey other = (TimeZoneDisplayKey)obj;
+ return
+ mTimeZone.equals(other.mTimeZone) &&
+ mStyle == other.mStyle &&
+ mLocale.equals(other.mLocale);
+ }
+ return false;
+ }
+ }
+}
Propchange: commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FastDatePrinter.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FormatCache.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FormatCache.java?rev=1236055&r1=1236054&r2=1236055&view=diff
==============================================================================
--- commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FormatCache.java (original)
+++ commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/time/FormatCache.java Thu Jan 26 07:00:26 2012
@@ -41,7 +41,7 @@ abstract class FormatCache<F extends For
private final ConcurrentMap<MultipartKey, F> cInstanceCache
= new ConcurrentHashMap<MultipartKey, F>(7);
- private final ConcurrentMap<MultipartKey, String> cDateTimeInstanceCache
+ private static final ConcurrentMap<MultipartKey, String> cDateTimeInstanceCache
= new ConcurrentHashMap<MultipartKey, String>(7);
/**
@@ -120,6 +120,20 @@ abstract class FormatCache<F extends For
if (locale == null) {
locale = Locale.getDefault();
}
+ String pattern = getPatternForStyle(dateStyle, timeStyle, locale);
+ return getInstance(pattern, timeZone, locale);
+ }
+
+ /**
+ * <p>Gets a date/time format for the specified styles and locale.</p>
+ *
+ * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT, null indicates no date in format
+ * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no time in format
+ * @param locale The non-null locale of the desired format
+ * @return a localized standard date/time format
+ * @throws IllegalArgumentException if the Locale has no date/time pattern defined
+ */
+ public static String getPatternForStyle(Integer dateStyle, Integer timeStyle, Locale locale) {
MultipartKey key = new MultipartKey(dateStyle, timeStyle, locale);
String pattern = cDateTimeInstanceCache.get(key);
@@ -147,8 +161,7 @@ abstract class FormatCache<F extends For
throw new IllegalArgumentException("No date time pattern for locale: " + locale);
}
}
-
- return getInstance(pattern, timeZone, locale);
+ return pattern;
}
// ----------------------------------------------------------------------
@@ -172,12 +185,9 @@ abstract class FormatCache<F extends For
*/
@Override
public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if ( obj instanceof MultipartKey == false ) {
- return false;
- }
+ // Eliminate the usual boilerplate because
+ // this inner static class is only used in a generic ConcurrentHashMap
+ // which will not compare against other Object types
return Arrays.equals(keys, ((MultipartKey)obj).keys);
}
Modified: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormatTest.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormatTest.java?rev=1236055&r1=1236054&r2=1236055&view=diff
==============================================================================
--- commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormatTest.java (original)
+++ commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormatTest.java Thu Jan 26 07:00:26 2012
@@ -16,16 +16,25 @@
*/
package org.apache.commons.lang3.time;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.text.Format;
+import java.text.ParseException;
import java.text.SimpleDateFormat;
-import java.util.Calendar;
import java.util.Date;
-import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
-import junit.framework.TestCase;
-
-import org.apache.commons.lang3.SerializationUtils;
+import org.junit.Test;
/**
* Unit tests {@link org.apache.commons.lang3.time.FastDateFormat}.
@@ -33,19 +42,21 @@ import org.apache.commons.lang3.Serializ
* @since 2.0
* @version $Id$
*/
-public class FastDateFormatTest extends TestCase {
-
- public FastDateFormatTest(String name) {
- super(name);
- }
+public class FastDateFormatTest {
+ /*
+ * Only the cache methods need to be tested here.
+ * The print methods are tested by {@link FastDateFormat_PrinterTest}
+ * and the parse methods are tested by {@link FastDateFormat_ParserTest}
+ */
+ @Test
public void test_getInstance() {
FastDateFormat format1 = FastDateFormat.getInstance();
FastDateFormat format2 = FastDateFormat.getInstance();
assertSame(format1, format2);
- assertEquals(new SimpleDateFormat().toPattern(), format1.getPattern());
}
+ @Test
public void test_getInstance_String() {
FastDateFormat format1 = FastDateFormat.getInstance("MM/DD/yyyy");
FastDateFormat format2 = FastDateFormat.getInstance("MM-DD-yyyy");
@@ -58,6 +69,7 @@ public class FastDateFormatTest extends
assertEquals(TimeZone.getDefault(), format2.getTimeZone());
}
+ @Test
public void test_getInstance_String_TimeZone() {
Locale realDefaultLocale = Locale.getDefault();
TimeZone realDefaultZone = TimeZone.getDefault();
@@ -86,6 +98,7 @@ public class FastDateFormatTest extends
}
}
+ @Test
public void test_getInstance_String_Locale() {
Locale realDefaultLocale = Locale.getDefault();
try {
@@ -103,6 +116,7 @@ public class FastDateFormatTest extends
}
}
+ @Test
public void test_changeDefault_Locale_DateInstance() {
Locale realDefaultLocale = Locale.getDefault();
try {
@@ -123,6 +137,7 @@ public class FastDateFormatTest extends
}
}
+ @Test
public void test_changeDefault_Locale_DateTimeInstance() {
Locale realDefaultLocale = Locale.getDefault();
try {
@@ -143,6 +158,7 @@ public class FastDateFormatTest extends
}
}
+ @Test
public void test_getInstance_String_TimeZone_Locale() {
Locale realDefaultLocale = Locale.getDefault();
TimeZone realDefaultZone = TimeZone.getDefault();
@@ -168,154 +184,128 @@ public class FastDateFormatTest extends
Locale.setDefault(realDefaultLocale);
TimeZone.setDefault(realDefaultZone);
}
- }
-
- public void testFormat() {
- Locale realDefaultLocale = Locale.getDefault();
- TimeZone realDefaultZone = TimeZone.getDefault();
- try {
- Locale.setDefault(Locale.US);
- TimeZone.setDefault(TimeZone.getTimeZone("America/New_York"));
+ }
- GregorianCalendar cal1 = new GregorianCalendar(2003, 0, 10, 15, 33, 20);
- GregorianCalendar cal2 = new GregorianCalendar(2003, 6, 10, 9, 00, 00);
- Date date1 = cal1.getTime();
- Date date2 = cal2.getTime();
- long millis1 = date1.getTime();
- long millis2 = date2.getTime();
-
- FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss");
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
- assertEquals(sdf.format(date1), fdf.format(date1));
- assertEquals("2003-01-10T15:33:20", fdf.format(date1));
- assertEquals("2003-01-10T15:33:20", fdf.format(cal1));
- assertEquals("2003-01-10T15:33:20", fdf.format(millis1));
- assertEquals("2003-07-10T09:00:00", fdf.format(date2));
- assertEquals("2003-07-10T09:00:00", fdf.format(cal2));
- assertEquals("2003-07-10T09:00:00", fdf.format(millis2));
-
- fdf = FastDateFormat.getInstance("Z");
- assertEquals("-0500", fdf.format(date1));
- assertEquals("-0500", fdf.format(cal1));
- assertEquals("-0500", fdf.format(millis1));
-
- assertEquals("-0400", fdf.format(date2));
- assertEquals("-0400", fdf.format(cal2));
- assertEquals("-0400", fdf.format(millis2));
-
- fdf = FastDateFormat.getInstance("ZZ");
- assertEquals("-05:00", fdf.format(date1));
- assertEquals("-05:00", fdf.format(cal1));
- assertEquals("-05:00", fdf.format(millis1));
-
- assertEquals("-04:00", fdf.format(date2));
- assertEquals("-04:00", fdf.format(cal2));
- assertEquals("-04:00", fdf.format(millis2));
-
- String pattern = "GGGG GGG GG G yyyy yyy yy y MMMM MMM MM M" +
- " dddd ddd dd d DDDD DDD DD D EEEE EEE EE E aaaa aaa aa a zzzz zzz zz z";
- fdf = FastDateFormat.getInstance(pattern);
- sdf = new SimpleDateFormat(pattern);
- // SDF bug fix starting with Java 7
- assertEquals(sdf.format(date1).replaceAll("2003 03 03 03", "2003 2003 03 2003"), fdf.format(date1));
- assertEquals(sdf.format(date2).replaceAll("2003 03 03 03", "2003 2003 03 2003"), fdf.format(date2));
- } finally {
- Locale.setDefault(realDefaultLocale);
- TimeZone.setDefault(realDefaultZone);
+ @Test
+ public void testCheckDefaults() {
+ FastDateFormat format = FastDateFormat.getInstance();
+ FastDateFormat medium = FastDateFormat.getDateTimeInstance(FastDateFormat.SHORT, FastDateFormat.SHORT);
+ assertEquals(medium, format);
+
+ SimpleDateFormat sdf = new SimpleDateFormat();
+ assertEquals(sdf.toPattern(), format.getPattern());
+
+ assertEquals(Locale.getDefault(), format.getLocale());
+ assertEquals(TimeZone.getDefault(), format.getTimeZone());
+ }
+
+ @Test
+ public void testCheckDifferingStyles() {
+ FastDateFormat shortShort = FastDateFormat.getDateTimeInstance(FastDateFormat.SHORT, FastDateFormat.SHORT, Locale.US);
+ FastDateFormat shortLong = FastDateFormat.getDateTimeInstance(FastDateFormat.SHORT, FastDateFormat.LONG, Locale.US);
+ FastDateFormat longShort = FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.SHORT, Locale.US);
+ FastDateFormat longLong = FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.LONG, Locale.US);
+
+ assertFalse(shortShort.equals(shortLong));
+ assertFalse(shortShort.equals(longShort));
+ assertFalse(shortShort.equals(longLong));
+ assertFalse(shortLong.equals(longShort));
+ assertFalse(shortLong.equals(longLong));
+ assertFalse(longShort.equals(longLong));
+ }
+
+ @Test
+ public void testDateDefaults() {
+ assertEquals(FastDateFormat.getDateInstance(FastDateFormat.LONG, Locale.CANADA),
+ FastDateFormat.getDateInstance(FastDateFormat.LONG, TimeZone.getDefault(), Locale.CANADA));
+
+ assertEquals(FastDateFormat.getDateInstance(FastDateFormat.LONG, TimeZone.getTimeZone("America/New_York")),
+ FastDateFormat.getDateInstance(FastDateFormat.LONG, TimeZone.getTimeZone("America/New_York"), Locale.getDefault()));
+
+ assertEquals(FastDateFormat.getDateInstance(FastDateFormat.LONG),
+ FastDateFormat.getDateInstance(FastDateFormat.LONG, TimeZone.getDefault(), Locale.getDefault()));
+ }
+
+ @Test
+ public void testTimeDefaults() {
+ assertEquals(FastDateFormat.getTimeInstance(FastDateFormat.LONG, Locale.CANADA),
+ FastDateFormat.getTimeInstance(FastDateFormat.LONG, TimeZone.getDefault(), Locale.CANADA));
+
+ assertEquals(FastDateFormat.getTimeInstance(FastDateFormat.LONG, TimeZone.getTimeZone("America/New_York")),
+ FastDateFormat.getTimeInstance(FastDateFormat.LONG, TimeZone.getTimeZone("America/New_York"), Locale.getDefault()));
+
+ assertEquals(FastDateFormat.getTimeInstance(FastDateFormat.LONG),
+ FastDateFormat.getTimeInstance(FastDateFormat.LONG, TimeZone.getDefault(), Locale.getDefault()));
+ }
+
+ @Test
+ public void testTimeDateDefaults() {
+ assertEquals(FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.MEDIUM, Locale.CANADA),
+ FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.MEDIUM, TimeZone.getDefault(), Locale.CANADA));
+
+ assertEquals(FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.MEDIUM, TimeZone.getTimeZone("America/New_York")),
+ FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.MEDIUM, TimeZone.getTimeZone("America/New_York"), Locale.getDefault()));
+
+ assertEquals(FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.MEDIUM),
+ FastDateFormat.getDateTimeInstance(FastDateFormat.LONG, FastDateFormat.MEDIUM, TimeZone.getDefault(), Locale.getDefault()));
+ }
+
+ @Test
+ public void testParseSync() throws ParseException, InterruptedException {
+ final FastDateFormat formatter= FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS Z");
+
+ long sdfTime= measureTime(formatter, new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS Z") {
+ private static final long serialVersionUID = 1L; // because SimpleDateFormat is serializable
+
+ @Override
+ public Object parseObject(String formattedDate) throws ParseException {
+ synchronized(this) {
+ return super.parse(formattedDate);
+ }
+ }
+ });
+
+ long fdfTime= measureTime(formatter, FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS Z"));
+
+ String times= "FastDateParser:"+fdfTime+" SimpleDateFormat:"+sdfTime;
+ System.out.println(times);
+ }
+
+ final static private int NTHREADS= 10;
+ final static private int NROUNDS= 10000;
+
+ private long measureTime(final Format formatter, final Format parser) throws ParseException, InterruptedException {
+ final ExecutorService pool = Executors.newFixedThreadPool(NTHREADS);
+ final AtomicInteger failures= new AtomicInteger(0);
+ final AtomicLong totalElapsed= new AtomicLong(0);
+
+ for(int i= 0; i<NTHREADS; ++i) {
+ pool.submit(new Runnable() {
+ public void run() {
+ for(int i= 0; i<NROUNDS; ++i) {
+ try {
+ Date date= new Date();
+ String formattedDate= formatter.format(date);
+ long start= System.currentTimeMillis();
+ Object pd= parser.parseObject(formattedDate);
+ totalElapsed.addAndGet(System.currentTimeMillis()-start);
+ if(!date.equals(pd)) {
+ failures.incrementAndGet();
+ }
+ } catch (Exception e) {
+ failures.incrementAndGet();
+ e.printStackTrace();
+ }
+ }
+ }
+ });
}
- }
-
- /**
- * Test case for {@link FastDateFormat#getDateInstance(int, java.util.Locale)}.
- */
- public void testShortDateStyleWithLocales() {
- Locale usLocale = Locale.US;
- Locale swedishLocale = new Locale("sv", "SE");
- Calendar cal = Calendar.getInstance();
- cal.set(2004, 1, 3);
- FastDateFormat fdf = FastDateFormat.getDateInstance(FastDateFormat.SHORT, usLocale);
- assertEquals("2/3/04", fdf.format(cal));
-
- fdf = FastDateFormat.getDateInstance(FastDateFormat.SHORT, swedishLocale);
- assertEquals("2004-02-03", fdf.format(cal));
-
- }
-
- /**
- * Tests that pre-1000AD years get padded with yyyy
- */
- public void testLowYearPadding() {
- Calendar cal = Calendar.getInstance();
- FastDateFormat format = FastDateFormat.getInstance("yyyy/MM/DD");
-
- cal.set(1,0,1);
- assertEquals("0001/01/01", format.format(cal));
- cal.set(10,0,1);
- assertEquals("0010/01/01", format.format(cal));
- cal.set(100,0,1);
- assertEquals("0100/01/01", format.format(cal));
- cal.set(999,0,1);
- assertEquals("0999/01/01", format.format(cal));
- }
- /**
- * Show Bug #39410 is solved
- */
- public void testMilleniumBug() {
- Calendar cal = Calendar.getInstance();
- FastDateFormat format = FastDateFormat.getInstance("dd.MM.yyyy");
-
- cal.set(1000,0,1);
- assertEquals("01.01.1000", format.format(cal));
- }
-
- /**
- * testLowYearPadding showed that the date was buggy
- * This test confirms it, getting 366 back as a date
- */
- public void testSimpleDate() {
- Calendar cal = Calendar.getInstance();
- FastDateFormat format = FastDateFormat.getInstance("yyyy/MM/dd");
-
- cal.set(2004,11,31);
- assertEquals("2004/12/31", format.format(cal));
- cal.set(999,11,31);
- assertEquals("0999/12/31", format.format(cal));
- cal.set(1,2,2);
- assertEquals("0001/03/02", format.format(cal));
- }
-
- public void testLang303() {
- Calendar cal = Calendar.getInstance();
- cal.set(2004,11,31);
-
- FastDateFormat format = FastDateFormat.getInstance("yyyy/MM/dd");
- String output = format.format(cal);
-
- format = (FastDateFormat) SerializationUtils.deserialize( SerializationUtils.serialize( format ) );
- assertEquals(output, format.format(cal));
- }
-
- public void testLang538() {
- // more commonly constructed with: cal = new GregorianCalendar(2009, 9, 16, 8, 42, 16)
- // for the unit test to work in any time zone, constructing with GMT-8 rather than default locale time zone
- GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT-8"));
- cal.clear();
- cal.set(2009, 9, 16, 8, 42, 16);
-
- FastDateFormat format = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", TimeZone.getTimeZone("GMT"));
- assertEquals("dateTime", "2009-10-16T16:42:16.000Z", format.format(cal.getTime()));
- assertEquals("dateTime", "2009-10-16T08:42:16.000Z", format.format(cal));
- }
-
- public void testLang645() {
- Locale locale = new Locale("sv", "SE");
-
- Calendar cal = Calendar.getInstance();
- cal.set(2010, 0, 1, 12, 0, 0);
- Date d = cal.getTime();
-
- FastDateFormat fdf = FastDateFormat.getInstance("EEEE', week 'ww", locale);
-
- assertEquals("fredag, week 53", fdf.format(d));
+ pool.shutdown();
+ if(!pool.awaitTermination(20, TimeUnit.SECONDS)) {
+ pool.shutdownNow();
+ fail("did not complete tasks");
+ }
+ assertEquals(0, failures.get());
+ return totalElapsed.get();
}
}
Added: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormat_ParserTest.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormat_ParserTest.java?rev=1236055&view=auto
==============================================================================
--- commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormat_ParserTest.java (added)
+++ commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormat_ParserTest.java Thu Jan 26 07:00:26 2012
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.time;
+
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * Unit tests for the parse methods of FastDateFormat
+ *
+ * @since 3.2
+ */
+public class FastDateFormat_ParserTest extends FastDateParserTest {
+
+ @Override
+ protected DateParser getInstance(String format, TimeZone timeZone, Locale locale) {
+ return FastDateFormat.getInstance(format, timeZone, locale);
+ }
+}
Propchange: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormat_ParserTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormat_PrinterTest.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormat_PrinterTest.java?rev=1236055&view=auto
==============================================================================
--- commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormat_PrinterTest.java (added)
+++ commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormat_PrinterTest.java Thu Jan 26 07:00:26 2012
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.time;
+
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * Unit tests for the print methods of FastDateFormat
+ *
+ * @since 3.2
+ */
+public class FastDateFormat_PrinterTest extends FastDatePrinterTest {
+
+ @Override
+ protected DatePrinter getInstance(String format, TimeZone timeZone, Locale locale) {
+ return FastDateFormat.getInstance(format, timeZone, locale);
+ }
+}
Propchange: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateFormat_PrinterTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java?rev=1236055&view=auto
==============================================================================
--- commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java (added)
+++ commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java Thu Jan 26 07:00:26 2012
@@ -0,0 +1,364 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional inparserion regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.time;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.Serializable;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.apache.commons.lang3.SerializationUtils;
+import org.junit.Test;
+
+/**
+ * Unit tests {@link org.apache.commons.lang3.time.FastDateParser}.
+ *
+ * @since 3.2
+ */
+public class FastDateParserTest {
+ private static final String yMdHmsSZ = "yyyy-MM-dd'T'HH:mm:ss.SSS Z";
+ private static final String DMY_DOT = "dd.MM.yyyy";
+ private static final String YMD_SLASH = "yyyy/MM/dd";
+ private static final String MDY_DASH = "MM-DD-yyyy";
+ private static final String MDY_SLASH = "MM/DD/yyyy";
+ private static final TimeZone REYKJAVIK = TimeZone.getTimeZone("Atlantic/Reykjavik");
+ private static final TimeZone NEW_YORK = TimeZone.getTimeZone("America/New_York");
+ private static final Locale SWEDEN = new Locale("sv", "SE");
+
+ DateParser getInstance(String format) {
+ return getInstance(format, TimeZone.getDefault(), Locale.getDefault());
+ }
+
+ private DateParser getDateInstance(int dateStyle, Locale locale) {
+ return getInstance(FormatCache.getPatternForStyle(dateStyle, null, locale), TimeZone.getDefault(), Locale.getDefault());
+ }
+
+ private DateParser getInstance(String format, Locale locale) {
+ return getInstance(format, TimeZone.getDefault(), locale);
+ }
+
+ private DateParser getInstance(String format, TimeZone timeZone) {
+ return getInstance(format, timeZone, Locale.getDefault());
+ }
+
+ /**
+ * Override this method in derived tests to change the construction of instances
+ * @param format
+ * @param timeZone
+ * @param locale
+ * @return
+ */
+ protected DateParser getInstance(String format, TimeZone timeZone, Locale locale) {
+ return new FastDateParser(format, timeZone, locale);
+ }
+
+ @Test
+ public void test_Equality_Hash() {
+ DateParser[] parsers= {
+ getInstance(yMdHmsSZ, NEW_YORK, Locale.US),
+ getInstance(DMY_DOT, NEW_YORK, Locale.US),
+ getInstance(YMD_SLASH, NEW_YORK, Locale.US),
+ getInstance(MDY_DASH, NEW_YORK, Locale.US),
+ getInstance(MDY_SLASH, NEW_YORK, Locale.US),
+ getInstance(MDY_SLASH, REYKJAVIK, Locale.US),
+ getInstance(MDY_SLASH, REYKJAVIK, SWEDEN)
+ };
+
+ Map<DateParser,Integer> map= new HashMap<DateParser,Integer>();
+ int i= 0;
+ for(DateParser parser:parsers) {
+ map.put(parser, i++);
+ }
+
+ i= 0;
+ for(DateParser parser:parsers) {
+ assertEquals(i++, (int)map.get(parser));
+ }
+ }
+
+ @Test
+ public void testParseZone() throws ParseException {
+ Calendar cal= Calendar.getInstance(NEW_YORK, Locale.US);
+ cal.clear();
+ cal.set(2003, 6, 10, 16, 33, 20);
+
+ DateParser fdf = getInstance(yMdHmsSZ, NEW_YORK, Locale.US);
+
+ assertEquals(cal.getTime(), fdf.parse("2003-07-10T15:33:20.000 -0500"));
+ assertEquals(cal.getTime(), fdf.parse("2003-07-10T15:33:20.000 GMT-05:00"));
+ assertEquals(cal.getTime(), fdf.parse("2003-07-10T16:33:20.000 Eastern Daylight Time"));
+ assertEquals(cal.getTime(), fdf.parse("2003-07-10T16:33:20.000 EDT"));
+
+ cal.setTimeZone(TimeZone.getTimeZone("GMT-3"));
+ cal.set(2003, 1, 10, 9, 0, 0);
+
+ assertEquals(cal.getTime(), fdf.parse("2003-02-10T09:00:00.000 -0300"));
+
+ cal.setTimeZone(TimeZone.getTimeZone("GMT+5"));
+ cal.set(2003, 1, 10, 15, 5, 6);
+
+ assertEquals(cal.getTime(), fdf.parse("2003-02-10T15:05:06.000 +0500"));
+ }
+
+ @Test
+ public void testParseLongShort() throws ParseException {
+ Calendar cal= Calendar.getInstance(NEW_YORK, Locale.US);
+ cal.clear();
+ cal.set(2003, 1, 10, 15, 33, 20);
+ cal.set(Calendar.MILLISECOND, 989);
+ cal.setTimeZone(NEW_YORK);
+
+ DateParser fdf = getInstance("yyyy GGGG MMMM dddd aaaa EEEE HHHH mmmm ssss SSSS ZZZZ", NEW_YORK, Locale.US);
+
+ assertEquals(cal.getTime(), fdf.parse("2003 AD February 0010 PM Monday 0015 0033 0020 0989 GMT-05:00"));
+ cal.set(Calendar.ERA, GregorianCalendar.BC);
+
+ Date parse = fdf.parse("2003 BC February 0010 PM Saturday 0015 0033 0020 0989 GMT-05:00");
+ assertEquals(cal.getTime(), parse);
+
+ fdf = getInstance("y G M d a E H m s S Z");
+ assertEquals(cal.getTime(), fdf.parse("03 BC 2 10 PM Sat 15 33 20 989 -0500"));
+
+ cal.set(Calendar.ERA, GregorianCalendar.AD);
+ assertEquals(cal.getTime(), fdf.parse("03 AD 2 10 PM Saturday 15 33 20 989 -0500"));
+ }
+
+ @Test
+ public void testAmPm() throws ParseException {
+ Calendar cal= Calendar.getInstance(NEW_YORK, Locale.US);
+ cal.clear();
+
+ DateParser h = getInstance("yyyy-MM-dd hh a mm:ss", NEW_YORK, Locale.US);
+ DateParser K = getInstance("yyyy-MM-dd KK a mm:ss", NEW_YORK, Locale.US);
+ DateParser k = getInstance("yyyy-MM-dd kk:mm:ss", NEW_YORK, Locale.US);
+ DateParser H = getInstance("yyyy-MM-dd HH:mm:ss", NEW_YORK, Locale.US);
+
+ cal.set(2010, 7, 1, 0, 33, 20);
+ assertEquals(cal.getTime(), h.parse("2010-08-01 12 AM 33:20"));
+ assertEquals(cal.getTime(), K.parse("2010-08-01 0 AM 33:20"));
+ assertEquals(cal.getTime(), k.parse("2010-08-01 00:33:20"));
+ assertEquals(cal.getTime(), H.parse("2010-08-01 00:33:20"));
+
+ cal.set(2010, 7, 1, 3, 33, 20);
+ assertEquals(cal.getTime(), h.parse("2010-08-01 3 AM 33:20"));
+ assertEquals(cal.getTime(), K.parse("2010-08-01 3 AM 33:20"));
+ assertEquals(cal.getTime(), k.parse("2010-08-01 03:33:20"));
+ assertEquals(cal.getTime(), H.parse("2010-08-01 03:33:20"));
+
+ cal.set(2010, 7, 1, 15, 33, 20);
+ assertEquals(cal.getTime(), h.parse("2010-08-01 3 PM 33:20"));
+ assertEquals(cal.getTime(), K.parse("2010-08-01 3 PM 33:20"));
+ assertEquals(cal.getTime(), k.parse("2010-08-01 15:33:20"));
+ assertEquals(cal.getTime(), H.parse("2010-08-01 15:33:20"));
+
+ cal.set(2010, 7, 1, 12, 33, 20);
+ assertEquals(cal.getTime(), h.parse("2010-08-01 12 PM 33:20"));
+ assertEquals(cal.getTime(), K.parse("2010-08-01 0 PM 33:20"));
+ assertEquals(cal.getTime(), k.parse("2010-08-01 12:33:20"));
+ assertEquals(cal.getTime(), H.parse("2010-08-01 12:33:20"));
+ }
+
+ @Test
+ public void testLocales() throws ParseException {
+
+ for(Locale locale : Locale.getAvailableLocales()) {
+ Calendar cal= Calendar.getInstance(NEW_YORK, Locale.US);
+ cal.clear();
+ cal.set(2003, 1, 10);
+
+ try {
+ String longFormat= "GGGG/yyyy/MMMM/dddd/aaaa/EEEE/ZZZZ";
+ SimpleDateFormat sdf = new SimpleDateFormat(longFormat, locale);
+ DateParser fdf = getInstance(longFormat, locale);
+
+ checkParse(cal, sdf, fdf);
+
+ cal.set(Calendar.ERA, GregorianCalendar.BC);
+ checkParse(cal, sdf, fdf);
+
+ String shortFormat= "G/y/M/d/a/E/Z";
+ sdf = new SimpleDateFormat(shortFormat, locale);
+ fdf = getInstance(shortFormat, locale);
+ checkParse(cal, sdf, fdf);
+
+ cal.set(Calendar.ERA, GregorianCalendar.AD);
+ checkParse(cal, sdf, fdf);
+ }
+ catch(ParseException ex) {
+ // TODO: why do ja_JP_JP, hi_IN, th_TH, and th_TH_TH fail?
+ System.out.println("Locale "+locale+ " failed");
+ }
+ }
+ }
+
+ private void checkParse(Calendar cal, SimpleDateFormat sdf, DateParser fdf) throws ParseException {
+ String formattedDate= sdf.format(cal.getTime());
+ Date expectedTime = sdf.parse(formattedDate);
+ Date actualTime = fdf.parse(formattedDate);
+ assertEquals(expectedTime, actualTime);
+ }
+
+ @Test
+ public void testParseNumerics() throws ParseException {
+ Calendar cal= Calendar.getInstance(NEW_YORK, Locale.US);
+ cal.clear();
+ cal.set(2003, 1, 10, 15, 33, 20);
+ cal.set(Calendar.MILLISECOND, 989);
+
+ DateParser fdf = getInstance("yyyyMMddHHmmssSSS", NEW_YORK, Locale.US);
+ assertEquals(cal.getTime(), fdf.parse("20030210153320989"));
+ }
+
+ @Test
+ public void testQuotes() throws ParseException {
+ Calendar cal= Calendar.getInstance(NEW_YORK, Locale.US);
+ cal.clear();
+ cal.set(2003, 1, 10, 15, 33, 20);
+ cal.set(Calendar.MILLISECOND, 989);
+
+ DateParser fdf = getInstance("''yyyyMMdd'A''B'HHmmssSSS''", NEW_YORK, Locale.US);
+ assertEquals(cal.getTime(), fdf.parse("'20030210A'B153320989'"));
+ }
+
+ @Test
+ public void testDayOf() throws ParseException {
+ Calendar cal= Calendar.getInstance(NEW_YORK, Locale.US);
+ cal.clear();
+ cal.set(2003, 1, 10);
+
+ DateParser fdf = getInstance("W w F D y", NEW_YORK, Locale.US);
+ assertEquals(cal.getTime(), fdf.parse("3 7 2 41 03"));
+ }
+
+ /**
+ * Test case for {@link FastDateParser#getDateInstance(int, java.util.Locale)}.
+ * @throws ParseException
+ */
+ @Test
+ public void testShortDateStyleWithLocales() throws ParseException {
+ DateParser fdf = getDateInstance(FastDateFormat.SHORT, Locale.US);
+ Calendar cal = Calendar.getInstance();
+ cal.clear();
+
+ cal.set(2004, 1, 3);
+ assertEquals(cal.getTime(), fdf.parse("2/3/04"));
+
+ fdf = getDateInstance(FastDateFormat.SHORT, SWEDEN);
+ assertEquals(cal.getTime(), fdf.parse("2004-02-03"));
+ }
+
+ /**
+ * Tests that pre-1000AD years get padded with yyyy
+ * @throws ParseException
+ */
+ @Test
+ public void testLowYearPadding() throws ParseException {
+ DateParser parser = getInstance(YMD_SLASH);
+ Calendar cal = Calendar.getInstance();
+ cal.clear();
+
+ cal.set(1,0,1);
+ assertEquals(cal.getTime(), parser.parse("0001/01/01"));
+ cal.set(10,0,1);
+ assertEquals(cal.getTime(), parser.parse("0010/01/01"));
+ cal.set(100,0,1);
+ assertEquals(cal.getTime(), parser.parse("0100/01/01"));
+ cal.set(999,0,1);
+ assertEquals(cal.getTime(), parser.parse("0999/01/01"));
+ }
+
+ /**
+ * @throws ParseException
+ */
+ @Test
+ public void testMilleniumBug() throws ParseException {
+ DateParser parser = getInstance(DMY_DOT);
+ Calendar cal = Calendar.getInstance();
+ cal.clear();
+
+ cal.set(1000,0,1);
+ assertEquals(cal.getTime(), parser.parse("01.01.1000"));
+ }
+
+ @Test
+ public void testLang303() throws ParseException {
+ DateParser parser = getInstance(YMD_SLASH);
+ Calendar cal = Calendar.getInstance();
+ cal.set(2004,11,31);
+
+ Date date = parser.parse("2004/11/31");
+
+ parser = (DateParser) SerializationUtils.deserialize( SerializationUtils.serialize( (Serializable)parser ) );
+ assertEquals(date, parser.parse("2004/11/31"));
+ }
+
+ @Test
+ public void testLang538() throws ParseException {
+ DateParser parser = getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", TimeZone.getTimeZone("GMT"));
+
+ Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT-8"));
+ cal.clear();
+ cal.set(2009, 9, 16, 8, 42, 16);
+
+ assertEquals(cal.getTime(), parser.parse("2009-10-16T16:42:16.000Z"));
+ }
+
+ @Test
+ public void testEquals() {
+ DateParser parser1= getInstance(YMD_SLASH);
+ DateParser parser2= getInstance(YMD_SLASH);
+
+ assertEquals(parser1, parser2);
+ assertEquals(parser1.hashCode(), parser2.hashCode());
+
+ assertFalse(parser1.equals(new Object()));
+ }
+
+ @Test
+ public void testToStringContainsName() {
+ DateParser parser= getInstance(YMD_SLASH);
+ assertTrue(parser.toString().startsWith("FastDate"));
+ }
+
+ @Test
+ public void testPatternMatches() {
+ DateParser parser= getInstance(yMdHmsSZ);
+ assertEquals(yMdHmsSZ, parser.getPattern());
+ }
+
+ @Test
+ public void testLocaleMatches() {
+ DateParser parser= getInstance(yMdHmsSZ, SWEDEN);
+ assertEquals(SWEDEN, parser.getLocale());
+ }
+
+ @Test
+ public void testTimeZoneMatches() {
+ DateParser parser= getInstance(yMdHmsSZ, REYKJAVIK);
+ assertEquals(REYKJAVIK, parser.getTimeZone());
+ }
+}
Propchange: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDateParserTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDatePrinterTest.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDatePrinterTest.java?rev=1236055&view=auto
==============================================================================
--- commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDatePrinterTest.java (added)
+++ commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDatePrinterTest.java Thu Jan 26 07:00:26 2012
@@ -0,0 +1,263 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.time;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.Serializable;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.apache.commons.lang3.SerializationUtils;
+import org.junit.Test;
+
+/**
+ * Unit tests {@link org.apache.commons.lang3.time.FastDatePrinter}.
+ *
+ * @since 3.0
+ */
+public class FastDatePrinterTest {
+
+ private static final String YYYY_MM_DD = "yyyy/MM/dd";
+ private static final TimeZone NEW_YORK = TimeZone.getTimeZone("America/New_York");
+ private static final Locale SWEDEN = new Locale("sv", "SE");
+
+ DatePrinter getInstance(String format) {
+ return getInstance(format, TimeZone.getDefault(), Locale.getDefault());
+ }
+
+ private DatePrinter getDateInstance(int dateStyle, Locale locale) {
+ return getInstance(FormatCache.getPatternForStyle(dateStyle, null, locale), TimeZone.getDefault(), Locale.getDefault());
+ }
+
+ private DatePrinter getInstance(String format, Locale locale) {
+ return getInstance(format, TimeZone.getDefault(), locale);
+ }
+
+ private DatePrinter getInstance(String format, TimeZone timeZone) {
+ return getInstance(format, timeZone, Locale.getDefault());
+ }
+
+ /**
+ * Override this method in derived tests to change the construction of instances
+ * @param format
+ * @param timeZone
+ * @param locale
+ * @return
+ */
+ protected DatePrinter getInstance(String format, TimeZone timeZone, Locale locale) {
+ return new FastDatePrinter(format, timeZone, locale);
+ }
+
+ @Test
+ public void testFormat() {
+ Locale realDefaultLocale = Locale.getDefault();
+ TimeZone realDefaultZone = TimeZone.getDefault();
+ try {
+ Locale.setDefault(Locale.US);
+ TimeZone.setDefault(NEW_YORK);
+
+ GregorianCalendar cal1 = new GregorianCalendar(2003, 0, 10, 15, 33, 20);
+ GregorianCalendar cal2 = new GregorianCalendar(2003, 6, 10, 9, 00, 00);
+ Date date1 = cal1.getTime();
+ Date date2 = cal2.getTime();
+ long millis1 = date1.getTime();
+ long millis2 = date2.getTime();
+
+ DatePrinter fdf = getInstance("yyyy-MM-dd'T'HH:mm:ss");
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+ assertEquals(sdf.format(date1), fdf.format(date1));
+ assertEquals("2003-01-10T15:33:20", fdf.format(date1));
+ assertEquals("2003-01-10T15:33:20", fdf.format(cal1));
+ assertEquals("2003-01-10T15:33:20", fdf.format(millis1));
+ assertEquals("2003-07-10T09:00:00", fdf.format(date2));
+ assertEquals("2003-07-10T09:00:00", fdf.format(cal2));
+ assertEquals("2003-07-10T09:00:00", fdf.format(millis2));
+
+ fdf = getInstance("Z");
+ assertEquals("-0500", fdf.format(date1));
+ assertEquals("-0500", fdf.format(cal1));
+ assertEquals("-0500", fdf.format(millis1));
+
+ assertEquals("-0400", fdf.format(date2));
+ assertEquals("-0400", fdf.format(cal2));
+ assertEquals("-0400", fdf.format(millis2));
+
+ fdf = getInstance("ZZ");
+ assertEquals("-05:00", fdf.format(date1));
+ assertEquals("-05:00", fdf.format(cal1));
+ assertEquals("-05:00", fdf.format(millis1));
+
+ assertEquals("-04:00", fdf.format(date2));
+ assertEquals("-04:00", fdf.format(cal2));
+ assertEquals("-04:00", fdf.format(millis2));
+
+ String pattern = "GGGG GGG GG G yyyy yyy yy y MMMM MMM MM M" +
+ " dddd ddd dd d DDDD DDD DD D EEEE EEE EE E aaaa aaa aa a zzzz zzz zz z";
+ fdf = getInstance(pattern);
+ sdf = new SimpleDateFormat(pattern);
+ // SDF bug fix starting with Java 7
+ assertEquals(sdf.format(date1).replaceAll("2003 03 03 03", "2003 2003 03 2003"), fdf.format(date1));
+ assertEquals(sdf.format(date2).replaceAll("2003 03 03 03", "2003 2003 03 2003"), fdf.format(date2));
+ } finally {
+ Locale.setDefault(realDefaultLocale);
+ TimeZone.setDefault(realDefaultZone);
+ }
+ }
+
+ /**
+ * Test case for {@link FastDatePrinter#getDateInstance(int, java.util.Locale)}.
+ */
+ @Test
+ public void testShortDateStyleWithLocales() {
+ Locale usLocale = Locale.US;
+ Locale swedishLocale = new Locale("sv", "SE");
+ Calendar cal = Calendar.getInstance();
+ cal.set(2004, 1, 3);
+ DatePrinter fdf = getDateInstance(FastDateFormat.SHORT, usLocale);
+ assertEquals("2/3/04", fdf.format(cal));
+
+ fdf = getDateInstance(FastDateFormat.SHORT, swedishLocale);
+ assertEquals("2004-02-03", fdf.format(cal));
+
+ }
+
+ /**
+ * Tests that pre-1000AD years get padded with yyyy
+ */
+ @Test
+ public void testLowYearPadding() {
+ Calendar cal = Calendar.getInstance();
+ DatePrinter format = getInstance(YYYY_MM_DD);
+
+ cal.set(1,0,1);
+ assertEquals("0001/01/01", format.format(cal));
+ cal.set(10,0,1);
+ assertEquals("0010/01/01", format.format(cal));
+ cal.set(100,0,1);
+ assertEquals("0100/01/01", format.format(cal));
+ cal.set(999,0,1);
+ assertEquals("0999/01/01", format.format(cal));
+ }
+ /**
+ * Show Bug #39410 is solved
+ */
+ @Test
+ public void testMilleniumBug() {
+ Calendar cal = Calendar.getInstance();
+ DatePrinter format = getInstance("dd.MM.yyyy");
+
+ cal.set(1000,0,1);
+ assertEquals("01.01.1000", format.format(cal));
+ }
+
+ /**
+ * testLowYearPadding showed that the date was buggy
+ * This test confirms it, getting 366 back as a date
+ */
+ @Test
+ public void testSimpleDate() {
+ Calendar cal = Calendar.getInstance();
+ DatePrinter format = getInstance(YYYY_MM_DD);
+
+ cal.set(2004,11,31);
+ assertEquals("2004/12/31", format.format(cal));
+ cal.set(999,11,31);
+ assertEquals("0999/12/31", format.format(cal));
+ cal.set(1,2,2);
+ assertEquals("0001/03/02", format.format(cal));
+ }
+
+ @Test
+ public void testLang303() {
+ Calendar cal = Calendar.getInstance();
+ cal.set(2004,11,31);
+
+ DatePrinter format = getInstance(YYYY_MM_DD);
+ String output = format.format(cal);
+
+ format = (DatePrinter) SerializationUtils.deserialize( SerializationUtils.serialize( (Serializable)format ) );
+ assertEquals(output, format.format(cal));
+ }
+
+ @Test
+ public void testLang538() {
+ // more commonly constructed with: cal = new GregorianCalendar(2009, 9, 16, 8, 42, 16)
+ // for the unit test to work in any time zone, constructing with GMT-8 rather than default locale time zone
+ GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT-8"));
+ cal.clear();
+ cal.set(2009, 9, 16, 8, 42, 16);
+
+ DatePrinter format = getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", TimeZone.getTimeZone("GMT"));
+ assertEquals("dateTime", "2009-10-16T16:42:16.000Z", format.format(cal.getTime()));
+ assertEquals("dateTime", "2009-10-16T08:42:16.000Z", format.format(cal));
+ }
+
+ @Test
+ public void testLang645() {
+ Locale locale = new Locale("sv", "SE");
+
+ Calendar cal = Calendar.getInstance();
+ cal.set(2010, 0, 1, 12, 0, 0);
+ Date d = cal.getTime();
+
+ DatePrinter fdf = getInstance("EEEE', week 'ww", locale);
+
+ assertEquals("fredag, week 53", fdf.format(d));
+ }
+
+ @Test
+ public void testEquals() {
+ DatePrinter printer1= getInstance(YYYY_MM_DD);
+ DatePrinter printer2= getInstance(YYYY_MM_DD);
+
+ assertEquals(printer1, printer2);
+ assertEquals(printer1.hashCode(), printer2.hashCode());
+
+ assertFalse(printer1.equals(new Object()));
+ }
+
+ @Test
+ public void testToStringContainsName() {
+ DatePrinter printer= getInstance(YYYY_MM_DD);
+ assertTrue(printer.toString().startsWith("FastDate"));
+ }
+
+ @Test
+ public void testPatternMatches() {
+ DatePrinter printer= getInstance(YYYY_MM_DD);
+ assertEquals(YYYY_MM_DD, printer.getPattern());
+ }
+
+ @Test
+ public void testLocaleMatches() {
+ DatePrinter printer= getInstance(YYYY_MM_DD, SWEDEN);
+ assertEquals(SWEDEN, printer.getLocale());
+ }
+
+ @Test
+ public void testTimeZoneMatches() {
+ DatePrinter printer= getInstance(YYYY_MM_DD, NEW_YORK);
+ assertEquals(NEW_YORK, printer.getTimeZone());
+ }
+}
Propchange: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/time/FastDatePrinterTest.java
------------------------------------------------------------------------------
svn:eol-style = native