package net.sourceforge.cardme.util;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.regex.Pattern;
/*
* Copyright 2011 George El-Haddad. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY GEORGE EL-HADDAD ''AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GEORGE EL-HADDAD OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of George El-Haddad.
*/
/**
*
* @author George El-Haddad
* <br/>
* Sep 21, 2006
*
* <p>Utility class to help with ISO related formatting.</p>
*/
public final class ISOUtils {
private static final Pattern ISO8601_UTC_TIME_BASIC_REGEX = Pattern.compile("\\d{8}T\\d{6}Z");
private static final Pattern ISO8601_UTC_TIME_EXTENDED_REGEX = Pattern.compile("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z");
private static final Pattern ISO8601_TIME_BASIC_REGEX = Pattern.compile("\\d{8}T\\d{6}[-\\+]\\d{4}");
private static final Pattern ISO8601_TIME_EXTENDED_REGEX = Pattern.compile("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[-\\+]\\d{2}:\\d{2}");
private static final Pattern ISO8601_DATE_BASIC_REGEX = Pattern.compile("\\d{8}");
private static final Pattern ISO8601_DATE_EXTENDED_REGEX = Pattern.compile("\\d{4}-\\d{2}-\\d{2}");
private ISOUtils() {
//hide the constructor
}
/**
* <p>
* Given a calendar object and a specific ISO format it will construct an
* ISO8601 calendar date or datetime string.
* </p>
* <p>
* If an {@link ISOFormat} of {@link ISOFormat.ISO8601_UTC_TIME_BASIC
* ISO8601_UTC_TIME_BASIC} or {@link ISOFormat.ISO8601_UTC_TIME_EXTENDED
* ISO8601_UTC_TIME_EXTENDED} is specified, then the timezone in the
* Calendar object will be converted to UTC.
* </p>
* <p>
* If any other {@link ISOFormat} is specified, then the date will be
* formatted according to the timezone in the Calendar object.
* </p>
*
* @param time the date/datetime
* @param format the date format to use
* @return the formatted date
*/
public static String formatISO8601Date(Calendar time, ISOFormat format) {
String formatStr;
TimeZone timezone = time.getTimeZone();
switch (format)
{
case ISO8601_BASIC:
case ISO8601_DATE_BASIC:
{
formatStr = "yyyyMMdd";
break;
}
case ISO8601_EXTENDED:
case ISO8601_DATE_EXTENDED:
{
formatStr = "yyyy-MM-dd";
break;
}
case ISO8601_TIME_BASIC:
{
formatStr = "yyyyMMdd'T'HHmmssZ";
break;
}
case ISO8601_TIME_EXTENDED:
{
formatStr = "yyyy-MM-dd'T'HH:mm:ssZ";
break;
}
case ISO8601_UTC_TIME_BASIC:
{
formatStr = "yyyyMMdd'T'HHmmss'Z'";
timezone = TimeZone.getTimeZone("UTC");
break;
}
case ISO8601_UTC_TIME_EXTENDED:
{
formatStr = "yyyy-MM-dd'T'HH:mm:ss'Z'";
timezone = TimeZone.getTimeZone("UTC");
break;
}
default:
{
throw new IllegalArgumentException("The given ISOFormat is not supported: " + format);
}
}
DateFormat df = new SimpleDateFormat(formatStr);
df.setTimeZone(timezone);
String str = df.format(time.getTime());
switch (format)
{
case ISO8601_TIME_EXTENDED:
{
//add a colon to the timezone (SimpleDateFormat cannot do this)
//example: converts "2012-07-05T22:31:41-0400" to "2012-07-05T22:31:41-04:00"
str = str.replaceAll("([-\\+]\\d{2})(\\d{2})$", "$1:$2");
break;
}
}
return str;
}
/**
* <p>
* Converts a TimeZone object to a string that's in ISO-8601 format. It can
* be either basic or extended format.
* </p>
*
* @param timeZone the timezone to format
* @param extended true to use "extended" format, false not to. Extended
* format will put a colon between the hour and minute.
* @return the formatted timezone (e.g. "+0530" or "+05:30")
*/
public static String formatISO8601TimeZone(TimeZone timeZone, boolean extended)
{
StringBuilder sb = new StringBuilder();
boolean positive = timeZone.getRawOffset() >= 0;
int hours = Math.abs(((timeZone.getRawOffset() / 1000) / 60) / 60);
int minutes = Math.abs((timeZone.getRawOffset() / 1000) / 60) % 60;
sb.append(positive ? '+' : '-');
if (hours < 10){
sb.append('0');
}
sb.append(hours);
if (extended){
sb.append(':');
}
if (minutes < 10){
sb.append('0');
}
sb.append(minutes);
return sb.toString();
}
/**
* <p>
* Parses a date that's in ISO8601 format.
* </p>
*
* @param dateStr the date string (e.g. "2012-07-01T10:31:22+02:00")
* @return the parsed date (converted to the local timezone)
* @throws ParseException if there's a problem parsing the date
* @throws IllegalArgumentException if the date string is not recognized as
* an ISO8601 date
*/
public static Calendar parseISO8601Date(String dateStr) throws ParseException {
String format;
if (ISO8601_DATE_BASIC_REGEX.matcher(dateStr).matches()) {
//Example: 19960415
format = "yyyyMMdd";
}
else if (ISO8601_DATE_EXTENDED_REGEX.matcher(dateStr).matches()) {
//Example: 1996-04-15
format = "yyyy-MM-dd";
}
else if (ISO8601_TIME_BASIC_REGEX.matcher(dateStr).matches()) {
//Example: 19960415T231000-0600
format = "yyyyMMdd'T'HHmmssZ";
}
else if (ISO8601_TIME_EXTENDED_REGEX.matcher(dateStr).matches()) {
//Example: 1996-04-15T23:10:00-06:00
//SimpleDateFormat doesn't recognize timezone offsets that have colons
//so remove the colon from the timezone offset
dateStr = dateStr.replaceAll("([-\\+]\\d{2}):(\\d{2})$", "$1$2");
format = "yyyy-MM-dd'T'HH:mm:ssZ";
}
else if (ISO8601_UTC_TIME_BASIC_REGEX.matcher(dateStr).matches()) {
//Example: 19960415T231000Z
//SimpleDateFormat doesn't recognize "Z"
dateStr = dateStr.replace("Z", "+0000");
format = "yyyyMMdd'T'HHmmssZ";
}
else if (ISO8601_UTC_TIME_EXTENDED_REGEX.matcher(dateStr).matches()) {
//Example: 1996-04-15T23:10:00Z
//SimpleDateFormat doesn't recognize "Z"
dateStr = dateStr.replace("Z", "+0000");
format = "yyyy-MM-dd'T'HH:mm:ssZ";
}
else {
throw new IllegalArgumentException("Date string is not in a valid ISO-8601 format.");
}
DateFormat df = new SimpleDateFormat(format);
Date date = df.parse(dateStr);
Calendar c = Calendar.getInstance();
c.setTime(date);
return c;
}
}