001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.log4j.pattern;
019
020import org.apache.log4j.helpers.LogLog;
021import org.apache.log4j.spi.LoggingEvent;
022
023import java.text.SimpleDateFormat;
024import java.text.DateFormat;
025import java.text.FieldPosition;
026import java.text.ParsePosition;
027import java.util.Date;
028import java.util.TimeZone;
029
030
031/**
032 * Convert and format the event's date in a StringBuffer.
033 *
034 * @author Ceki Gülcü
035 */
036public final class DatePatternConverter extends LoggingEventPatternConverter {
037    /**
038     * ABSOLUTE string literal.
039     */
040  private static final String ABSOLUTE_FORMAT = "ABSOLUTE";
041    /**
042     * SimpleTimePattern for ABSOLUTE.
043     */
044  private static final String ABSOLUTE_TIME_PATTERN = "HH:mm:ss,SSS";
045
046
047    /**
048     * DATE string literal.
049     */
050  private static final String DATE_AND_TIME_FORMAT = "DATE";
051    /**
052     * SimpleTimePattern for DATE.
053     */
054  private static final String DATE_AND_TIME_PATTERN = "dd MMM yyyy HH:mm:ss,SSS";
055
056    /**
057     * ISO8601 string literal.
058     */
059  private static final String ISO8601_FORMAT = "ISO8601";
060    /**
061     * SimpleTimePattern for ISO8601.
062     */
063  private static final String ISO8601_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS";
064  /**
065   * Date format.
066   */
067  private final CachedDateFormat df;
068
069    /**
070     * This class wraps a DateFormat and forces the time zone to the
071     *   default time zone before each format and parse request.
072     */
073  private static class DefaultZoneDateFormat extends DateFormat {
074     /**
075      * Serialization version ID.
076      */
077     private static final long serialVersionUID = 1;
078     /**
079         * Wrapped instance of DateFormat.
080         */
081    private final DateFormat dateFormat;
082
083        /**
084         * Construct new instance.
085         * @param format format, may not be null.
086         */
087    public DefaultZoneDateFormat(final DateFormat format) {
088        dateFormat = format;
089    }
090
091        /**
092         * @{inheritDoc}
093         */
094    public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
095        dateFormat.setTimeZone(TimeZone.getDefault());
096        return dateFormat.format(date, toAppendTo, fieldPosition);
097    }
098
099        /**
100         * @{inheritDoc}
101         */
102    public Date parse(String source, ParsePosition pos) {
103        dateFormat.setTimeZone(TimeZone.getDefault());
104        return dateFormat.parse(source, pos);
105    }
106  }
107  
108  /**
109   * Private constructor.
110   * @param options options, may be null.
111   */
112  private DatePatternConverter(final String[] options) {
113    super("Date", "date");
114
115    String patternOption;
116
117    if ((options == null) || (options.length == 0)) {
118      // the branch could be optimized, but here we are making explicit
119      // that null values for patternOption are allowed.
120      patternOption = null;
121    } else {
122      patternOption = options[0];
123    }
124
125    String pattern;
126
127    if (
128      (patternOption == null)
129        || patternOption.equalsIgnoreCase(ISO8601_FORMAT)) {
130      pattern = ISO8601_PATTERN;
131    } else if (patternOption.equalsIgnoreCase(ABSOLUTE_FORMAT)) {
132      pattern = ABSOLUTE_TIME_PATTERN;
133    } else if (patternOption.equalsIgnoreCase(DATE_AND_TIME_FORMAT)) {
134      pattern = DATE_AND_TIME_PATTERN;
135    } else {
136      pattern = patternOption;
137    }
138
139    int maximumCacheValidity = 1000;
140    DateFormat simpleFormat = null;
141
142    try {
143      simpleFormat = new SimpleDateFormat(pattern);
144      maximumCacheValidity = CachedDateFormat.getMaximumCacheValidity(pattern);
145    } catch (IllegalArgumentException e) {
146        LogLog.warn(
147          "Could not instantiate SimpleDateFormat with pattern "
148          + patternOption, e);
149
150      // default to the ISO8601 format
151      simpleFormat = new SimpleDateFormat(ISO8601_PATTERN);
152    }
153
154    // if the option list contains a TZ option, then set it.
155    if ((options != null) && (options.length > 1)) {
156      TimeZone tz = TimeZone.getTimeZone((String) options[1]);
157      simpleFormat.setTimeZone(tz);
158    } else {
159      simpleFormat = new DefaultZoneDateFormat(simpleFormat);
160    }
161
162    df = new CachedDateFormat(simpleFormat, maximumCacheValidity);
163  }
164
165  /**
166   * Obtains an instance of pattern converter.
167   * @param options options, may be null.
168   * @return instance of pattern converter.
169   */
170  public static DatePatternConverter newInstance(
171    final String[] options) {
172    return new DatePatternConverter(options);
173  }
174
175  /**
176   * {@inheritDoc}
177   */
178  public void format(final LoggingEvent event, final StringBuffer output) {
179    synchronized(this) {
180        df.format(event.timeStamp, output);
181    }
182  }
183
184  /**
185   * {@inheritDoc}
186   */
187  public void format(final Object obj, final StringBuffer output) {
188    if (obj instanceof Date) {
189      format((Date) obj, output);
190    }
191
192    super.format(obj, output);
193  }
194
195  /**
196   * Append formatted date to string buffer.
197   * @param date date
198   * @param toAppendTo buffer to which formatted date is appended.
199   */
200  public void format(final Date date, final StringBuffer toAppendTo) {
201    synchronized(this) {
202        df.format(date.getTime(), toAppendTo);
203    }
204  }
205}