001
002 /*
003 * Copyright (C) 2011 Archie L. Cobbs. All rights reserved.
004 *
005 * $Id: DateEncoder.java 311 2012-03-26 16:18:17Z archie.cobbs $
006 */
007
008 package org.dellroad.stuff.string;
009
010 import java.text.ParseException;
011 import java.text.SimpleDateFormat;
012 import java.util.Date;
013 import java.util.TimeZone;
014 import java.util.regex.Matcher;
015 import java.util.regex.Pattern;
016
017 /**
018 * Encodes {@code Date} objects to and from strings.
019 */
020 public final class DateEncoder {
021
022 /**
023 * Regular expression matching properly encoded strings.
024 */
025 public static final String PATTERN = "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]{3})?(Z)?";
026
027 private static final String FORMAT_SECONDS = "yyyy-MM-dd'T'HH:mm:ss";
028 private static final String FORMAT_MILLIS = "yyyy-MM-dd'T'HH:mm:ss.SSS";
029 private static final String FORMAT_Z_SUFFIX = "'Z'";
030
031 private DateEncoder() {
032 }
033
034 /**
035 * Encode the given date as a string of the form {@code 2009-12-01T15:33:07.763Z}.
036 * If the fractional seconds portion is zero, it will be omitted.
037 *
038 * @param date date to encode
039 * @throws NullPointerException if {@code date} is {@code null}
040 */
041 public static String encode(Date date) {
042 String format = (date.getTime() % 1000 != 0 ? FORMAT_MILLIS : FORMAT_SECONDS) + FORMAT_Z_SUFFIX;
043 return DateEncoder.getDateFormat(format).format(date);
044 }
045
046 /**
047 * Decode the given date.
048 *
049 * @param string encoded date
050 * @throws IllegalArgumentException if {@code string} is malformed
051 * @throws NullPointerException if {@code string} is {@code null}
052 */
053 public static Date decode(String string) {
054 Matcher matcher = Pattern.compile(PATTERN).matcher(string);
055 if (!matcher.matches())
056 throw new IllegalArgumentException("malformed date string");
057 String format = matcher.group(1) != null ? FORMAT_MILLIS : FORMAT_SECONDS;
058 if (matcher.group(2) != null)
059 format += FORMAT_Z_SUFFIX;
060 try {
061 return DateEncoder.getDateFormat(format).parse(matcher.group());
062 } catch (ParseException e) {
063 throw new RuntimeException("unexpected");
064 }
065 }
066
067 /**
068 * Get a {@link SimpleDateFormat} configured with the given format and for the UTC time zone and strict parsing.
069 *
070 * @param format date format string
071 */
072 public static SimpleDateFormat getDateFormat(String format) {
073 SimpleDateFormat dateFormat = new SimpleDateFormat(format);
074 dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
075 dateFormat.setLenient(false);
076 return dateFormat;
077 }
078 }
079