blob: ebc6f08be108b5ebf26a3afefc5b2bae5daae6aa [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.util;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/** Date Format Cache.
* Computes String representations of Dates and caches
* the results so that subsequent requests within the same second
* will be fast.
*
* Only format strings that contain either "ss". Sub second formatting is
* not handled.
*
* The timezone of the date may be included as an ID with the "zzz"
* format string or as an offset with the "ZZZ" format string.
*
* If consecutive calls are frequently very different, then this
* may be a little slower than a normal DateFormat.
*/
public class DateCache
{
public static final String DEFAULT_FORMAT="EEE MMM dd HH:mm:ss zzz yyyy";
private final String _formatString;
private final String _tzFormatString;
private final SimpleDateFormat _tzFormat;
private final Locale _locale ;
private volatile Tick _tick;
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
public static class Tick
{
final long _seconds;
final String _string;
public Tick(long seconds, String string)
{
_seconds = seconds;
_string = string;
}
}
/* ------------------------------------------------------------ */
/** Constructor.
* Make a DateCache that will use a default format. The default format
* generates the same results as Date.toString().
*/
public DateCache()
{
this(DEFAULT_FORMAT);
}
/* ------------------------------------------------------------ */
/** Constructor.
* Make a DateCache that will use the given format
* @param format the format to use
*/
public DateCache(String format)
{
this(format,null,TimeZone.getDefault());
}
/* ------------------------------------------------------------ */
public DateCache(String format,Locale l)
{
this(format,l,TimeZone.getDefault());
}
/* ------------------------------------------------------------ */
public DateCache(String format,Locale l,String tz)
{
this(format,l,TimeZone.getTimeZone(tz));
}
/* ------------------------------------------------------------ */
public DateCache(String format,Locale l,TimeZone tz)
{
_formatString=format;
_locale = l;
int zIndex = _formatString.indexOf( "ZZZ" );
if( zIndex >= 0 )
{
String ss1 = _formatString.substring( 0, zIndex );
String ss2 = _formatString.substring( zIndex+3 );
int tzOffset = tz.getRawOffset();
StringBuilder sb = new StringBuilder(_formatString.length()+10);
sb.append(ss1);
sb.append("'");
if( tzOffset >= 0 )
sb.append( '+' );
else
{
tzOffset = -tzOffset;
sb.append( '-' );
}
int raw = tzOffset / (1000*60); // Convert to seconds
int hr = raw / 60;
int min = raw % 60;
if( hr < 10 )
sb.append( '0' );
sb.append( hr );
if( min < 10 )
sb.append( '0' );
sb.append( min );
sb.append( '\'' );
sb.append(ss2);
_tzFormatString=sb.toString();
}
else
_tzFormatString=_formatString;
if( _locale != null )
{
_tzFormat=new SimpleDateFormat(_tzFormatString,_locale);
}
else
{
_tzFormat=new SimpleDateFormat(_tzFormatString);
}
_tzFormat.setTimeZone(tz);
_tick=null;
}
/* ------------------------------------------------------------ */
public TimeZone getTimeZone()
{
return _tzFormat.getTimeZone();
}
/* ------------------------------------------------------------ */
/** Format a date according to our stored formatter.
* @param inDate the Date
* @return Formatted date
*/
public String format(Date inDate)
{
long seconds = inDate.getTime() / 1000;
Tick tick=_tick;
// Is this the cached time
if (tick==null || seconds!=tick._seconds)
{
// It's a cache miss
synchronized (this)
{
return _tzFormat.format(inDate);
}
}
return tick._string;
}
/* ------------------------------------------------------------ */
/** Format a date according to our stored formatter.
* If it happens to be in the same second as the last formatNow
* call, then the format is reused.
* @param inDate the date in milliseconds since unix epoch
* @return Formatted date
*/
public String format(long inDate)
{
long seconds = inDate / 1000;
Tick tick=_tick;
// Is this the cached time
if (tick==null || seconds!=tick._seconds)
{
// It's a cache miss
Date d = new Date(inDate);
synchronized (this)
{
return _tzFormat.format(d);
}
}
return tick._string;
}
/* ------------------------------------------------------------ */
/** Format a date according to our stored formatter.
* The passed time is expected to be close to the current time, so it is
* compared to the last value passed and if it is within the same second,
* the format is reused. Otherwise a new cached format is created.
* @param now the milliseconds since unix epoch
* @return Formatted date
*/
public String formatNow(long now)
{
long seconds = now / 1000;
Tick tick=_tick;
// Is this the cached time
if (tick!=null && tick._seconds==seconds)
return tick._string;
return formatTick(now)._string;
}
/* ------------------------------------------------------------ */
public String now()
{
return formatNow(System.currentTimeMillis());
}
/* ------------------------------------------------------------ */
public Tick tick()
{
return formatTick(System.currentTimeMillis());
}
/* ------------------------------------------------------------ */
protected Tick formatTick(long now)
{
long seconds = now / 1000;
// Synchronize to protect _tzFormat
synchronized (this)
{
// recheck the tick, to save multiple formats
if (_tick==null || _tick._seconds!=seconds)
{
String s= _tzFormat.format(new Date(now));
return _tick=new Tick(seconds,s);
}
return _tick;
}
}
/* ------------------------------------------------------------ */
public String getFormatString()
{
return _formatString;
}
}