/* * ==================================================================== * Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://svnkit.com/license.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * ==================================================================== */ package org.tmatesoft.svn.core.internal.util; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.text.FieldPosition; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.wc.ISVNOptions; import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; import org.tmatesoft.svn.util.SVNDebugLog; import org.tmatesoft.svn.util.SVNLogType; /** * @author TMate Software Ltd. * @version 1.3 */ public class SVNDate extends Date { private static final long serialVersionUID = 4845L; public static final SVNDate NULL = new SVNDate(0, 0); private static final Calendar CALENDAR = Calendar.getInstance(TimeZone.getTimeZone("GMT"), new Locale("en", "US")); static final DateFormat SVN_FORMAT = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSS"); private static final DateFormat ISO8601_FORMAT = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSS'000Z'"); private static final DateFormat RFC1123_FORMAT = new SimpleDateFormat( "EEE, d MMM yyyy HH:mm:ss z", Locale.US); private static final DateFormat CUSTOM_FORMAT = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss Z (EE, d MMM yyyy)", Locale.getDefault()); private static final DateFormat HUMAN_FORMAT = new SimpleDateFormat( "yyyy-MM-dd' 'HH:mm:ss' 'ZZZZ' ('E', 'dd' 'MMM' 'yyyy')'"); private static final DateFormat SHORT_FORMAT = new SimpleDateFormat( "yyyy-MM-dd' 'HH:mm:ss'Z'"); private static final DateFormat CONSOLE_DIFF_DATE_FORMAT = new SimpleDateFormat( "EEE' 'MMM' 'dd' 'HH:mm:ss' 'yyyy"); private static final DateFormat CONSOLE_LONG_DATE_FORMAT = new SimpleDateFormat( "MM' 'dd' 'yyyy"); private static final DateFormat CONSOLE_SHORT_DATE_FORMAT = new SimpleDateFormat( "MM' 'dd' 'HH:mm"); public static final char[] DATE_SEPARATORS = {'-', '-', 'T', ':', ':', '.', 'Z'}; static { SVN_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT")); ISO8601_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT")); RFC1123_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT")); HUMAN_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT")); SHORT_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT")); CUSTOM_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT")); } private int myMicroSeconds; public SVNDate(long time, int micro) { super((1000 * time + micro) / 1000); myMicroSeconds = micro >= 0 ? micro % 1000 : 1000 + (micro % 1000); } public String format() { StringBuffer formatted = new StringBuffer(); synchronized (SVN_FORMAT) { SVN_FORMAT.format(this, formatted, new FieldPosition(0)); } int m1 = myMicroSeconds % 10; int m2 = (myMicroSeconds / 10) % 10; int m3 = (myMicroSeconds) / 100; formatted.append(m3); formatted.append(m2); formatted.append(m1); formatted.append('Z'); return formatted.toString(); } public static String formatDate(Date date) { return formatDate(date, false); } public static String formatDate(Date date, boolean formatZeroDate) { if (date == null) { return null; } else if (!formatZeroDate && date.getTime() == 0) { return null; } if (date instanceof SVNDate) { SVNDate extendedDate = (SVNDate) date; return extendedDate.format(); } synchronized (ISO8601_FORMAT) { return ISO8601_FORMAT.format(date); } } public static String formatRFC1123Date(Date date) { if (date == null) { return null; } synchronized (RFC1123_FORMAT) { return RFC1123_FORMAT.format(date); } } public static String formatHumanDate(Date date, ISVNOptions options) { DateFormat df = HUMAN_FORMAT; if (options != null && options.getKeywordDateFormat() != null) { df = options.getKeywordDateFormat(); } synchronized (df) { return df.format(date != null ? date : NULL); } } public static String formatShortDate(Date date) { synchronized (SHORT_FORMAT) { return SHORT_FORMAT.format(date != null ? date : NULL); } } public static String formatCustomDate(Date date) { if (date == null) { return null; } synchronized (CUSTOM_FORMAT) { return CUSTOM_FORMAT.format(date); } } public static String formatConsoleDiffDate(Date date){ if (date == null) { return null; } synchronized (CONSOLE_DIFF_DATE_FORMAT) { return CONSOLE_DIFF_DATE_FORMAT.format(date); } } public static String formatConsoleLongDate(Date date){ if (date == null) { return null; } synchronized (CONSOLE_LONG_DATE_FORMAT) { return CONSOLE_LONG_DATE_FORMAT.format(date); } } public static String formatConsoleShortDate(Date date){ if (date == null) { return null; } synchronized (CONSOLE_SHORT_DATE_FORMAT) { return CONSOLE_SHORT_DATE_FORMAT.format(date); } } public static SVNDate parseDate(String str) { if (str == null) { return NULL; } try { return parseDatestamp(str); } catch (Throwable th) { SVNDebugLog.getDefaultLog().logFine(SVNLogType.DEFAULT, th); } return NULL; } public static Date parseDateString(String str) throws SVNException { try { return parseDatestamp(str); } catch (SVNException svne) { throw svne; } catch (Throwable th) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.BAD_DATE); SVNErrorManager.error(err, th, SVNLogType.DEFAULT); } return NULL; } private static SVNDate parseDatestamp(String str) throws SVNException { if (str == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.BAD_DATE); SVNErrorManager.error(err, SVNLogType.DEFAULT); } int index = 0; int charIndex = 0; int startIndex = 0; int[] result = new int[7]; int microseconds = 0; int timeZoneInd = -1; while (index < DATE_SEPARATORS.length && charIndex < str.length()) { if (str.charAt(charIndex) == '-') { if (index > 1) { timeZoneInd = charIndex; } } else if (str.charAt(charIndex) == '+') { timeZoneInd = charIndex; } if (str.charAt(charIndex) == DATE_SEPARATORS[index] || (index == 5 && str.charAt(charIndex) == DATE_SEPARATORS[index + 1])) { if (index == 5 && str.charAt(charIndex) == DATE_SEPARATORS[index + 1]) { index++; } String segment = str.substring(startIndex, charIndex); if (segment.length() == 0) { result[index] = 0; } else if (index + 1 < DATE_SEPARATORS.length) { result[index] = Integer.parseInt(segment); } else { result[index] = Integer.parseInt(segment.substring(0, Math.min(3, segment.length()))); if (segment.length() > 3) { microseconds = Integer.parseInt(segment.substring(3)); } } startIndex = charIndex + 1; index++; } charIndex++; } if (index < DATE_SEPARATORS.length) { String segment = str.substring(startIndex); if (segment.length() == 0) { result[index] = 0; } else { result[index] = Integer.parseInt(segment); } } int year = result[0]; int month = result[1]; int date = result[2]; int hour = result[3]; int min = result[4]; int sec = result[5]; int ms = result[6]; String timeZoneId = null; if (timeZoneInd != -1 && timeZoneInd < str.length() - 1 && str.indexOf('Z') == -1 && str.indexOf('z') == -1) { timeZoneId = "GMT" + str.substring(timeZoneInd); } synchronized (CALENDAR) { CALENDAR.clear(); TimeZone oldTimeZone = null; if (timeZoneId != null) { oldTimeZone = CALENDAR.getTimeZone(); CALENDAR.setTimeZone(TimeZone.getTimeZone(timeZoneId)); } else if (str.indexOf('Z') == -1 && str.indexOf('z') == -1) { oldTimeZone = CALENDAR.getTimeZone(); CALENDAR.setTimeZone(TimeZone.getDefault()); } CALENDAR.set(year, month - 1, date, hour, min, sec); CALENDAR.set(Calendar.MILLISECOND, ms); SVNDate resultDate = new SVNDate(CALENDAR.getTimeInMillis(), microseconds); if (oldTimeZone != null) { CALENDAR.setTimeZone(oldTimeZone); } return resultDate; } } public static long parseDateAsMilliseconds(String str) { if (str == null) { return -1; } int index = 0; int charIndex = 0; int startIndex = 0; int[] result = new int[7]; while (index < DATE_SEPARATORS.length && charIndex < str.length()) { if (str.charAt(charIndex) == DATE_SEPARATORS[index]) { String segment = str.substring(startIndex, charIndex); if (segment.length() == 0) { result[index] = 0; } else if (index + 1 < DATE_SEPARATORS.length) { try { result[index] = Integer.parseInt(segment); } catch (NumberFormatException nfe) { return -1; } } else { try { result[index] = Integer.parseInt(segment.substring(0, Math.min(3, segment.length()))); } catch (NumberFormatException nfe) { return -1; } } startIndex = charIndex + 1; index++; } charIndex++; } int year = result[0]; int month = result[1]; int date = result[2]; int hour = result[3]; int min = result[4]; int sec = result[5]; int ms = result[6]; synchronized (CALENDAR) { CALENDAR.clear(); CALENDAR.set(year, month - 1, date, hour, min, sec); CALENDAR.set(Calendar.MILLISECOND, ms); return CALENDAR.getTimeInMillis(); } } public static SVNDate fromDate(Date d) { if (d == null) { return SVNDate.NULL; } if (d instanceof SVNDate) { return (SVNDate) d; } return new SVNDate(d.getTime(), 0); } public int hashCode() { return 31 * super.hashCode() + myMicroSeconds; } public boolean equals(Object obj) { if (this == obj) { return true; } else if (obj instanceof SVNDate) { SVNDate date = (SVNDate) obj; return getTime() == date.getTime() && myMicroSeconds == date.myMicroSeconds; } return super.equals(obj); } public boolean before(Date when) { if (super.equals(when) && when instanceof SVNDate) { return myMicroSeconds < ((SVNDate) when).myMicroSeconds; } return super.before(when); } public boolean after(Date when) { if (super.equals(when) && when instanceof SVNDate) { return myMicroSeconds > ((SVNDate) when).myMicroSeconds; } return super.after(when); } public int compareTo(Date anotherDate) { int result = super.compareTo(anotherDate); if (result == 0 && anotherDate instanceof SVNDate) { SVNDate date = (SVNDate) anotherDate; return (myMicroSeconds < date.myMicroSeconds ? -1 : (myMicroSeconds == date.myMicroSeconds ? 0 : 1)); } return result; } public long getTimeInMicros() { return 1000 * getTime() + myMicroSeconds; } }