com.fasterxml.jackson.databind.util.ISO8601Utils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
package com.fasterxml.jackson.databind.util;
import java.util.*;
/**
* Utilities methods for manipulating dates in iso8601 format. This is much much faster and GC friendly than
* using SimpleDateFormat so highly suitable if you (un)serialize lots of date objects.
*/
public class ISO8601Utils {
/**
* ID to represent the 'GMT' string
*/
private static final String GMT_ID = "GMT";
/**
* The GMT timezone
*/
private static final TimeZone TIMEZONE_GMT = TimeZone.getTimeZone(GMT_ID);
/*
/**********************************************************
/* Static factories
/**********************************************************
*/
/**
* Accessor for static GMT timezone instance.
*/
public static TimeZone timeZoneGMT() {
return TIMEZONE_GMT;
}
/*
/**********************************************************
/* Formatting
/**********************************************************
*/
/**
* Format a date into 'yyyy-MM-ddThh:mm:ssZ' (GMT timezone, no milliseconds precision)
*
* @param date the date to format
* @return the date formatted as 'yyyy-MM-ddThh:mm:ssZ'
*/
public static String format(Date date) {
return format(date, false, TIMEZONE_GMT);
}
/**
* Format a date into 'yyyy-MM-ddThh:mm:ss[.sss]Z' (GMT timezone)
*
* @param date the date to format
* @param millis true to include millis precision otherwise false
* @return the date formatted as 'yyyy-MM-ddThh:mm:ss[.sss]Z'
*/
public static String format(Date date, boolean millis) {
return format(date, millis, TIMEZONE_GMT);
}
/**
* Format date into yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm]
*
* @param date the date to format
* @param millis true to include millis precision otherwise false
* @param tz timezone to use for the formatting (GMT will produce 'Z')
* @return the date formatted as yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm]
*/
public static String format(Date date, boolean millis, TimeZone tz) {
Calendar calendar = new GregorianCalendar(tz, Locale.US);
calendar.setTime(date);
// estimate capacity of buffer as close as we can (yeah, that's pedantic ;)
int capacity = "yyyy-MM-ddThh:mm:ss".length();
capacity += millis ? ".sss".length() : 0;
capacity += tz.getRawOffset() == 0 ? "Z".length() : "+hh:mm".length();
StringBuilder formatted = new StringBuilder(capacity);
padInt(formatted, calendar.get(Calendar.YEAR), "yyyy".length());
formatted.append('-');
padInt(formatted, calendar.get(Calendar.MONTH) + 1, "MM".length());
formatted.append('-');
padInt(formatted, calendar.get(Calendar.DAY_OF_MONTH), "dd".length());
formatted.append('T');
padInt(formatted, calendar.get(Calendar.HOUR_OF_DAY), "hh".length());
formatted.append(':');
padInt(formatted, calendar.get(Calendar.MINUTE), "mm".length());
formatted.append(':');
padInt(formatted, calendar.get(Calendar.SECOND), "ss".length());
if (millis) {
formatted.append('.');
padInt(formatted, calendar.get(Calendar.MILLISECOND), "sss".length());
}
int offset = tz.getOffset(calendar.getTimeInMillis());
if (offset != 0) {
int hours = Math.abs((offset / (60 * 1000)) / 60);
int minutes = Math.abs((offset / (60 * 1000)) % 60);
formatted.append(offset < 0 ? '-' : '+');
padInt(formatted, hours, "hh".length());
formatted.append(':');
padInt(formatted, minutes, "mm".length());
} else {
formatted.append('Z');
}
return formatted.toString();
}
/*
/**********************************************************
/* Parsing
/**********************************************************
*/
/**
* Parse a date from ISO-8601 formatted string. It expects a format yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm]
*
* @param date ISO string to parse in the appropriate format.
* @return the parsed date
* @throws IllegalArgumentException if the date is not in the appropriate format
*/
public static Date parse(String date)
{
Exception fail = null;
try {
int offset = 0;
// extract year
int year = parseInt(date, offset, offset += 4);
checkOffset(date, offset, '-');
// extract month
int month = parseInt(date, offset += 1, offset += 2);
checkOffset(date, offset, '-');
// extract day
int day = parseInt(date, offset += 1, offset += 2);
checkOffset(date, offset, 'T');
// extract hours, minutes, seconds and milliseconds
int hour = parseInt(date, offset += 1, offset += 2);
checkOffset(date, offset, ':');
int minutes = parseInt(date, offset += 1, offset += 2);
checkOffset(date, offset, ':');
int seconds = parseInt(date, offset += 1, offset += 2);
// milliseconds can be optional in the format
int milliseconds = 0; // always use 0 otherwise returned date will include millis of current time
if (date.charAt(offset) == '.') {
checkOffset(date, offset, '.');
milliseconds = parseInt(date, offset += 1, offset += 3);
}
// extract timezone
String timezoneId;
char timezoneIndicator = date.charAt(offset);
if (timezoneIndicator == '+' || timezoneIndicator == '-') {
timezoneId = GMT_ID + date.substring(offset);
} else if (timezoneIndicator == 'Z') {
timezoneId = GMT_ID;
} else {
throw new IndexOutOfBoundsException("Invalid time zone indicator " + timezoneIndicator);
}
TimeZone timezone = TimeZone.getTimeZone(timezoneId);
if (!timezone.getID().equals(timezoneId)) {
throw new IndexOutOfBoundsException();
}
Calendar calendar = new GregorianCalendar(timezone);
calendar.setLenient(false);
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month - 1);
calendar.set(Calendar.DAY_OF_MONTH, day);
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minutes);
calendar.set(Calendar.SECOND, seconds);
calendar.set(Calendar.MILLISECOND, milliseconds);
return calendar.getTime();
} catch (IndexOutOfBoundsException e) {
fail = e;
} catch (NumberFormatException e) {
fail = e;
} catch (IllegalArgumentException e) {
fail = e;
}
String input = (date == null) ? null : ('"'+date+"'");
throw new IllegalArgumentException("Failed to parse date ["+input
+"]: "+fail.getMessage(), fail);
}
/**
* Check if the expected character exist at the given offset of the
*
* @param value the string to check at the specified offset
* @param offset the offset to look for the expected character
* @param expected the expected character
* @throws IndexOutOfBoundsException if the expected character is not found
*/
private static void checkOffset(String value, int offset, char expected) throws IndexOutOfBoundsException {
char found = value.charAt(offset);
if (found != expected) {
throw new IndexOutOfBoundsException("Expected '" + expected + "' character but found '" + found + "'");
}
}
/**
* Parse an integer located between 2 given offsets in a string
*
* @param value the string to parse
* @param beginIndex the start index for the integer in the string
* @param endIndex the end index for the integer in the string
* @return the int
* @throws NumberFormatException if the value is not a number
*/
private static int parseInt(String value, int beginIndex, int endIndex) throws NumberFormatException {
if (beginIndex < 0 || endIndex > value.length() || beginIndex > endIndex) {
throw new NumberFormatException(value);
}
// use same logic as in Integer.parseInt() but less generic we're not supporting negative values
int i = beginIndex;
int result = 0;
int digit;
if (i < endIndex) {
digit = Character.digit(value.charAt(i++), 10);
if (digit < 0) {
throw new NumberFormatException("Invalid number: " + value);
}
result = -digit;
}
while (i < endIndex) {
digit = Character.digit(value.charAt(i++), 10);
if (digit < 0) {
throw new NumberFormatException("Invalid number: " + value);
}
result *= 10;
result -= digit;
}
return -result;
}
/**
* Zero pad a number to a specified length
*
* @param buffer buffer to use for padding
* @param value the integer value to pad if necessary.
* @param length the length of the string we should zero pad
*/
private static void padInt(StringBuilder buffer, int value, int length) {
String strValue = Integer.toString(value);
for (int i = length - strValue.length(); i > 0; i--) {
buffer.append('0');
}
buffer.append(strValue);
}
}