/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is part of dcm4che, an implementation of DICOM(TM) in
* Java(TM), hosted at https://github.com/gunterze/dcm4che.
*
* The Initial Developer of the Original Code is
* Agfa Healthcare.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* See @authors listed below
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package org.dcm4che3.util;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import org.dcm4che3.data.DatePrecision;
/**
* @author Gunter Zeilinger <gunterze@gmail.com>
*/
public class DateUtils {
public static final Date[] EMPTY_DATES = {};
private static TimeZone cachedTimeZone;
private static Calendar cal(TimeZone tz) {
Calendar cal = (tz != null)
? new GregorianCalendar(tz)
: new GregorianCalendar();
cal.clear();
return cal;
}
private static Calendar cal(TimeZone tz, Date date) {
Calendar cal = (tz != null)
? new GregorianCalendar(tz)
: new GregorianCalendar();
cal.setTime(date);
return cal;
}
private static void ceil(Calendar cal, int field) {
cal.add(field, 1);
cal.add(Calendar.MILLISECOND, -1);
}
public static String formatDA(TimeZone tz, Date date) {
return formatDA(tz, date, new StringBuilder(8)).toString();
}
public static StringBuilder formatDA(TimeZone tz, Date date,
StringBuilder toAppendTo) {
return formatDT(cal(tz, date), toAppendTo, Calendar.DAY_OF_MONTH);
}
public static String formatTM(TimeZone tz, Date date) {
return formatTM(tz, date, new DatePrecision());
}
public static String formatTM(TimeZone tz, Date date, DatePrecision precision) {
return formatTM(cal(tz, date), new StringBuilder(10),
precision.lastField).toString();
}
private static StringBuilder formatTM(Calendar cal,
StringBuilder toAppendTo, int lastField) {
appendXX(cal.get(Calendar.HOUR_OF_DAY), toAppendTo);
if (lastField > Calendar.HOUR_OF_DAY) {
appendXX(cal.get(Calendar.MINUTE), toAppendTo);
if (lastField > Calendar.MINUTE) {
appendXX(cal.get(Calendar.SECOND), toAppendTo);
if (lastField > Calendar.SECOND) {
toAppendTo.append('.');
appendXXX(cal.get(Calendar.MILLISECOND), toAppendTo);
}
}
}
return toAppendTo;
}
public static String formatDT(TimeZone tz, Date date) {
return formatDT(tz, date, new DatePrecision());
}
public static String formatDT(TimeZone tz, Date date, DatePrecision precision) {
return formatDT(tz, date, new StringBuilder(23), precision).toString();
}
public static StringBuilder formatDT(TimeZone tz, Date date,
StringBuilder toAppendTo, DatePrecision precision) {
Calendar cal = cal(tz, date);
formatDT(cal, toAppendTo, precision.lastField);
if (precision.includeTimezone) {
int offset = cal.get(Calendar.ZONE_OFFSET)
+ cal.get(Calendar.DST_OFFSET);
appendZZZZZ(offset, toAppendTo);
}
return toAppendTo;
}
private static StringBuilder appendZZZZZ(int offset, StringBuilder sb) {
if (offset < 0) {
offset = -offset;
sb.append('-');
} else
sb.append('+');
int min = offset / 60000;
appendXX(min / 60, sb);
appendXX(min % 60, sb);
return sb;
}
/**
* Returns Timezone Offset From UTC in format {@code (+|i)HHMM} of specified
* Timezone without concerning Daylight saving time (DST).
*
* @param tz Timezone
* @return Timezone Offset From UTC in format {@code (+|i)HHMM}
*/
public static String formatTimezoneOffsetFromUTC(TimeZone tz) {
return appendZZZZZ(tz.getRawOffset(), new StringBuilder(5)).toString();
}
/**
* Returns Timezone Offset From UTC in format {@code (+|i)HHMM} of specified
* Timezone on specified date. If no date is specified, DST is considered
* for the current date.
*
* @param tz Timezone
* @param date Date or {@code null}
* @return Timezone Offset From UTC in format {@code (+|i)HHMM}
*/
public static String formatTimezoneOffsetFromUTC(TimeZone tz, Date date) {
return appendZZZZZ(tz.getOffset(date == null
? System.currentTimeMillis() : date.getTime()),
new StringBuilder(5)).toString();
}
private static StringBuilder formatDT(Calendar cal, StringBuilder toAppendTo,
int lastField) {
appendXXXX(cal.get(Calendar.YEAR), toAppendTo);
if (lastField > Calendar.YEAR) {
appendXX(cal.get(Calendar.MONTH) + 1, toAppendTo);
if (lastField > Calendar.MONTH) {
appendXX(cal.get(Calendar.DAY_OF_MONTH), toAppendTo);
if (lastField > Calendar.DAY_OF_MONTH) {
formatTM(cal, toAppendTo, lastField);
}
}
}
return toAppendTo;
}
private static void appendXXXX(int i, StringBuilder toAppendTo) {
if (i < 1000)
toAppendTo.append('0');
appendXXX(i, toAppendTo);
}
private static void appendXXX(int i, StringBuilder toAppendTo) {
if (i < 100)
toAppendTo.append('0');
appendXX(i, toAppendTo);
}
private static void appendXX(int i, StringBuilder toAppendTo) {
if (i < 10)
toAppendTo.append('0');
toAppendTo.append(i);
}
public static Date parseDA(TimeZone tz, String s) {
return parseDA(tz, s, false);
}
public static Date parseDA(TimeZone tz, String s, boolean ceil) {
Calendar cal = cal(tz);
int length = s.length();
if (!(length == 8 || length == 10 && !Character.isDigit(s.charAt(4))))
throw new IllegalArgumentException(s);
try {
int pos = 0;
cal.set(Calendar.YEAR,
Integer.parseInt(s.substring(pos, pos + 4)));
pos += 4;
if (!Character.isDigit(s.charAt(pos)))
pos++;
cal.set(Calendar.MONTH,
Integer.parseInt(s.substring(pos, pos + 2)) - 1);
pos += 2;
if (!Character.isDigit(s.charAt(pos)))
pos++;
cal.set(Calendar.DAY_OF_MONTH,
Integer.parseInt(s.substring(pos)));
if (ceil)
ceil(cal, Calendar.DAY_OF_MONTH);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(s);
}
return cal.getTime();
}
public static Date parseTM(TimeZone tz, String s, DatePrecision precision) {
return parseTM(tz, s, false, precision);
}
public static Date parseTM(TimeZone tz, String s, boolean ceil,
DatePrecision precision) {
return parseTM(cal(tz), s, ceil, precision);
}
private static Date parseTM(Calendar cal, String s, boolean ceil,
DatePrecision precision) {
int length = s.length();
int pos = 0;
if (pos + 2 > length)
throw new IllegalArgumentException(s);
try {
cal.set(precision.lastField = Calendar.HOUR_OF_DAY,
Integer.parseInt(s.substring(pos, pos + 2)));
pos += 2;
if (pos < length) {
if (!Character.isDigit(s.charAt(pos)))
pos++;
if (pos + 2 > length)
throw new IllegalArgumentException(s);
cal.set(precision.lastField = Calendar.MINUTE,
Integer.parseInt(s.substring(pos, pos + 2)));
pos += 2;
if (pos < length) {
if (!Character.isDigit(s.charAt(pos)))
pos++;
if (pos + 2 > length)
throw new IllegalArgumentException(s);
cal.set(precision.lastField = Calendar.SECOND,
Integer.parseInt(s.substring(pos, pos + 2)));
pos += 2;
if (pos < length) {
float f = Float.parseFloat(s.substring(pos));
if (f >= 1 || f < 0)
throw new IllegalArgumentException(s);
cal.set(precision.lastField = Calendar.MILLISECOND,
(int) (f * 1000));
return cal.getTime();
}
}
}
if (ceil)
ceil(cal, precision.lastField);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(s);
}
return cal.getTime();
}
public static Date parseDT(TimeZone tz, String s, DatePrecision precision) {
return parseDT(tz, s, false, precision);
}
public static TimeZone timeZone(String s) {
TimeZone tz;
if (s.length() != 5 || (tz = safeTimeZone(s)) == null)
throw new IllegalArgumentException("Illegal Timezone Offset: " + s);
return tz;
}
private static TimeZone safeTimeZone(String s) {
String tzid = tzid(s);
if (tzid == null)
return null;
TimeZone tz = cachedTimeZone;
if (tz == null || !tz.getID().equals(tzid))
cachedTimeZone = tz = TimeZone.getTimeZone(tzid);
return tz;
}
private static String tzid(String s) {
int length = s.length();
if (length > 4) {
char[] tzid = { 'G', 'M', 'T', 0, 0, 0, ':', 0, 0 };
s.getChars(length-5, length-2, tzid, 3);
s.getChars(length-2, length, tzid, 7);
if ((tzid[3] == '+' || tzid[3] == '-')
&& Character.isDigit(tzid[4])
&& Character.isDigit(tzid[5])
&& Character.isDigit(tzid[7])
&& Character.isDigit(tzid[8])) {
return new String(tzid);
}
}
return null;
}
public static Date parseDT(TimeZone tz, String s, boolean ceil,
DatePrecision precision) {
int length = s.length();
TimeZone tz1 = safeTimeZone(s);
if (precision.includeTimezone = tz1 != null) {
length -= 5;
tz = tz1;
}
Calendar cal = cal(tz);
try {
int pos = 0;
if (pos + 4 > length)
throw new IllegalArgumentException(s);
cal.set(precision.lastField = Calendar.YEAR,
Integer.parseInt(s.substring(pos, pos + 4)));
pos += 4;
if (pos < length) {
if (!Character.isDigit(s.charAt(pos)))
pos++;
if (pos + 2 > length)
throw new IllegalArgumentException(s);
cal.set(precision.lastField = Calendar.MONTH,
Integer.parseInt(s.substring(pos, pos + 2)) - 1);
pos += 2;
if (pos < length) {
if (!Character.isDigit(s.charAt(pos)))
pos++;
if (pos + 2 > length)
throw new IllegalArgumentException(s);
cal.set(precision.lastField = Calendar.DAY_OF_MONTH,
Integer.parseInt(s.substring(pos, pos + 2)));
pos += 2;
if (pos < length)
return parseTM(cal, s.substring(pos, length), ceil,
precision);
}
}
} catch (NumberFormatException e) {
throw new IllegalArgumentException(s);
}
if (ceil)
ceil(cal, precision.lastField);
return cal.getTime();
}
}