/**
* GenericFormatter
* Copyright 2011 by Michael Peter Christen
* First released 2.1.2011 at http://yacy.net
*
* $LastChangedDate$
* $LastChangedRevision$
* $LastChangedBy$
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program in the file lgpl21.txt
* If not, see <http://www.gnu.org/licenses/>.
*/
package net.yacy.cora.date;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import net.yacy.cora.util.NumberTools;
public class GenericFormatter extends AbstractFormatter implements DateFormatter {
public static final String PATTERN_SHORT_DAY = "yyyyMMdd";
public static final String PATTERN_SHORT_MINUTE = "yyyyMMddHHmm";
public static final String PATTERN_SHORT_SECOND = "yyyyMMddHHmmss";
public static final String PATTERN_SHORT_MILSEC = "yyyyMMddHHmmssSSS";
public static final String PATTERN_RFC1123_SHORT = "EEE, dd MMM yyyy";
public static final String PATTERN_ANSIC = "EEE MMM d HH:mm:ss yyyy";
public static final String PATTERN_SIMPLE = "yyyy/MM/dd HH:mm:ss";
public static final SimpleDateFormat FORMAT_SHORT_DAY = new SimpleDateFormat(PATTERN_SHORT_DAY, Locale.US);
public static final SimpleDateFormat FORMAT_SHORT_MINUTE = new SimpleDateFormat(PATTERN_SHORT_MINUTE, Locale.US);
public static final SimpleDateFormat FORMAT_SHORT_SECOND = new SimpleDateFormat(PATTERN_SHORT_SECOND, Locale.US);
public static final SimpleDateFormat FORMAT_SHORT_MILSEC = new SimpleDateFormat(PATTERN_SHORT_MILSEC, Locale.US);
public static final SimpleDateFormat FORMAT_RFC1123_SHORT = new SimpleDateFormat(PATTERN_RFC1123_SHORT, Locale.US);
public static final SimpleDateFormat FORMAT_ANSIC = new SimpleDateFormat(PATTERN_ANSIC, Locale.US);
public static final SimpleDateFormat FORMAT_SIMPLE = new SimpleDateFormat(PATTERN_SIMPLE, Locale.US);
static {
// we want GMT times on the formats as well as they don't support any timezone
FORMAT_SHORT_DAY.setTimeZone(UTCtimeZone);
FORMAT_SHORT_SECOND.setTimeZone(UTCtimeZone);
FORMAT_SHORT_MILSEC.setTimeZone(UTCtimeZone);
}
public static final long time_second = 1000L;
public static final long time_minute = 60000L;
public static final long time_hour = 60 * time_minute;
public static final long time_day = 24 * time_hour;
public static final GenericFormatter SHORT_DAY_FORMATTER = new GenericFormatter(FORMAT_SHORT_DAY, time_minute);
public static final GenericFormatter SHORT_MINUTE_FORMATTER = new GenericFormatter(FORMAT_SHORT_MINUTE, time_second);
public static final GenericFormatter SHORT_SECOND_FORMATTER = new GenericFormatter(FORMAT_SHORT_SECOND, time_second);
public static final GenericFormatter SHORT_MILSEC_FORMATTER = new GenericFormatter(FORMAT_SHORT_MILSEC, 1);
public static final GenericFormatter RFC1123_SHORT_FORMATTER = new GenericFormatter(FORMAT_RFC1123_SHORT, time_minute);
public static final GenericFormatter ANSIC_FORMATTER = new GenericFormatter(FORMAT_ANSIC, time_second);
public static final GenericFormatter SIMPLE_FORMATTER = new GenericFormatter(FORMAT_SIMPLE, time_second);
private final SimpleDateFormat dateFormat;
private final long maxCacheDiff;
public GenericFormatter(final SimpleDateFormat dateFormat, final long maxCacheDiff) {
this.dateFormat = (SimpleDateFormat) dateFormat.clone(); // avoid concurrency locking effects
this.last_time = 0;
this.last_format = "";
this.maxCacheDiff = maxCacheDiff;
}
/**
* Note: The short day format doesn't include any timezone information. This method
* transforms the date into the GMT/UTC timezone. Example: If the local system time is,
* 2007-12-18 01:15:00 +0200, then the resulting String will be "2007-12-17".
* In case you need a format with a timezone offset, use {@link #formatShortDay(TimeZone)}
* @return a String representation of the current system date in GMT using the
* short day format, e.g. "20071218".
*/
@Override
public String format(final Date date) {
if (date == null) return "";
synchronized (this.dateFormat) {
// calculate the date
return this.dateFormat.format(date);
}
}
@Override
public String format() {
if (Math.abs(System.currentTimeMillis() - this.last_time) < this.maxCacheDiff) return this.last_format;
// threads that had been waiting here may use the cache now instead of calculating the date again
final long time = System.currentTimeMillis();
// if the cache is not fresh, calculate the date
synchronized (this.dateFormat) {
if (Math.abs(time - this.last_time) < this.maxCacheDiff) return this.last_format;
this.last_format = this.dateFormat.format(new Date(time));
}
this.last_time = time;
return this.last_format;
}
/**
* Parse a String representation of a Date in short day format assuming the date
* is aligned to the GMT/UTC timezone. An example for such a date string is "20071218".
* @see #formatShortDay()
* @throws ParseException The exception is thrown if an error occured during while parsing
* the String.
*/
@Override
public Calendar parse(final String timeString, final int timezoneOffset) throws ParseException {
synchronized (this.dateFormat) {
Calendar cal = Calendar.getInstance(UTCtimeZone);
cal.setTime(this.dateFormat.parse(timeString));
cal.add(Calendar.MINUTE, timezoneOffset); // add a correction; i.e. for UTC+1 -60 minutes is added to patch a time given in UTC+1 to the actual time at UTC
return cal;
}
}
/**
* Like {@link #parseShortSecond(String)} using additional timezone information provided in an
* offset String, like "+0100" for CET.
* @throws ParseException
*/
public Calendar parse(final String timeString, final String UTCOffset) throws ParseException {
if (timeString == null || timeString.isEmpty()) { return Calendar.getInstance(UTCtimeZone); }
if (UTCOffset == null || UTCOffset.isEmpty()) { return Calendar.getInstance(UTCtimeZone); }
return parse(timeString, UTCDiff(UTCOffset)); // offset expected in min
}
/**
* Calculates the time offset in minutes given as timezoneoffsetstring (diffString)
* e.g. "+0300" returns 180
*
* @param diffString with fixed timezone format
* @return parsed timezone string in minutes
*/
private static int UTCDiff(final String diffString) {
if (diffString.length() != 5) throw new IllegalArgumentException("UTC String malformed (wrong size):" + diffString);
boolean ahead = true;
if (diffString.length() > 0 && diffString.charAt(0) == '+') ahead = true;
else if (diffString.length() > 0 && diffString.charAt(0) == '-') ahead = false;
else throw new IllegalArgumentException("UTC String malformed (wrong sign):" + diffString);
final int oh = NumberTools.parseIntDecSubstring(diffString, 1, 3);
final int om = NumberTools.parseIntDecSubstring(diffString, 3);
return (int) ( ((ahead) ? 1 : -1) * (oh * 60 + om));
}
/**
* get the difference of this servers time zone to UTC/GMT in milliseconds
* @return
*/
private static long UTCDiff() {
// DST_OFFSET is dependent on the time of the Calendar, so it has to be updated
// to get the correct current offset
synchronized (testCalendar) {
testCalendar.setTimeInMillis(System.currentTimeMillis());
final long zoneOffsetHours = testCalendar.get(Calendar.ZONE_OFFSET);
final long DSTOffsetHours = testCalendar.get(Calendar.DST_OFFSET);
return zoneOffsetHours + DSTOffsetHours;
}
}
public static String UTCDiffString() {
// we express the UTC Difference in 5 digits:
// SHHMM
// S ::= '+'|'-'
// HH ::= '00'|'01'|'02'|'03'|'04'|'05'|'06'|'07'|'08'|'09'|'10'|'11'|'12'
// MM ::= '00'|'15'|'30'|'45'
// since there are some places on earth where there is a time shift of half an hour
// we need too show also the minutes of the time shift
// Examples: http://www.timeanddate.com/library/abbreviations/timezones/
final long offsetHours = UTCDiff();
final StringBuilder sb = new StringBuilder(5);
if (offsetHours < 0) {
sb.append('-');
} else {
sb.append('+');
}
sb.append(D2.format(Math.abs((int) (offsetHours / AbstractFormatter.hourMillis))));
sb.append(D2.format(Math.abs((int) (offsetHours / AbstractFormatter.minuteMillis)) % 60));
return sb.toString();
}
private final static DecimalFormat D2 = new DecimalFormat("00");
public static void main(String[] args) {
System.out.println(UTCDiffString());
}
}