1 // Copyright 2012 Google Inc. All Rights Reserved.
3 package com
.google
.appengine
.api
.search
;
5 import com
.google
.apphosting
.api
.AppEngineInternal
;
7 import java
.text
.DateFormat
;
8 import java
.text
.ParsePosition
;
9 import java
.text
.SimpleDateFormat
;
10 import java
.util
.Calendar
;
11 import java
.util
.Date
;
12 import java
.util
.GregorianCalendar
;
13 import java
.util
.Locale
;
14 import java
.util
.TimeZone
;
17 * A utility class that centralizes processing of dates.
21 public final class DateUtil
{
24 * The milliseconds in a day.
26 public static final int MILLISECONDS_IN_DAY
= 24 * 60 * 60 * 1000;
31 private static final ThreadLocal
<TimeZone
> UTC_TZ
=
32 new ThreadLocal
<TimeZone
>() {
33 @Override protected TimeZone
initialValue() {
34 return TimeZone
.getTimeZone("UTC");
38 private static DateFormat
getDateFormat(String formatString
) {
39 DateFormat format
= new SimpleDateFormat(formatString
, Locale
.US
);
40 format
.setTimeZone(UTC_TZ
.get());
44 private static final ThreadLocal
<DateFormat
> ISO8601_SIMPLE
=
45 new ThreadLocal
<DateFormat
>() {
46 @Override protected DateFormat
initialValue() {
47 return getDateFormat("yyyy-MM-dd");
51 private static final ThreadLocal
<DateFormat
> ISO8601_DATE_TIME_SIMPLE
=
52 new ThreadLocal
<DateFormat
>() {
53 @Override protected DateFormat
initialValue() {
54 return getDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
58 private static final ThreadLocal
<DateFormat
> ISO8601_DATE_TIME_SIMPLE_ERA
=
59 new ThreadLocal
<DateFormat
>() {
60 @Override protected DateFormat
initialValue() {
61 return getDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'G");
69 * Get a UTC calendar with Locale US.
71 private static Calendar
getCalendarUTC() {
72 return new GregorianCalendar(UTC_TZ
.get(), Locale
.US
);
76 * Formats a date according ISO 8601 full date time format. Currently,
77 * this is used to print dates for error messages.
79 * @param date the date to format as a string
80 * @return a string representing the date in ISO 8601 format
82 public static String
formatDateTime(Date date
) {
86 return isBeforeCommonEra(date
) ?
87 ISO8601_DATE_TIME_SIMPLE_ERA
.get().format(date
) :
88 ISO8601_DATE_TIME_SIMPLE
.get().format(date
);
92 * Returns true if the date is before the common era.
94 private static boolean isBeforeCommonEra(Date date
) {
95 Calendar cal
= getCalendarUTC();
97 return cal
.get(Calendar
.ERA
) == GregorianCalendar
.BC
;
101 * Parses an ISO 8601 into a {@link Date} object.
103 * This function is only used for deserializing legacy "yyyy-MM-dd"
104 * formatted dates stored in backend. These are currently not supporting
105 * BC Era dates, so neither will this function.
107 * @param dateString the ISO 8601 formatted string for a date
108 * @return the {@link Date} parsed from the date string
110 private static Date
parseDate(String dateString
) {
111 ParsePosition pos
= new ParsePosition(0);
112 Date d
= ISO8601_SIMPLE
.get().parse(dateString
, pos
);
113 if (pos
.getIndex() < dateString
.length()) {
114 throw new IllegalArgumentException(
115 String
.format("Failed to parse date string \"%s\"", dateString
));
121 * Converts date into a string containing the milliseconds since the UNIX Epoch.
123 * @param date the date to serialize as a string
124 * @return a string representing the date as milliseconds since the UNIX Epoch
126 public static String
serializeDate(Date date
) {
127 return date
== null ?
"" : Long
.toString(date
.getTime());
131 * Converts a string containing the milliseconds since the UNIX Epoch into a Date.
133 * Two formats of date string are supported: "yyyy-MM-dd" and a long. Eventually,
134 * the "yyyy-MM-dd" format support will be removed.
136 * @param date the date string to deserialize into a date
139 public static Date
deserializeDate(String date
) {
143 if (date
.startsWith("-")) {
144 if (date
.length() > 1 && date
.indexOf('-', 1) >= 0) {
145 return parseDate(date
);
148 if (date
.indexOf('-') > 0) {
149 return parseDate(date
);
152 return new Date(Long
.parseLong(date
));
156 * Constructs a {@link Date} set to the UNIX Epoch plus days plus milliseconds.
158 * @param days the number of days to add to the UNIX Epoch to
160 * @param milliseconds the number of milliseconds to add to the date
161 * @return the Date with number of days plus Epoch
163 public static final Date
getEpochPlusDays(int days
, int milliseconds
) {
164 Calendar cal
= getCalendarUTC();
165 cal
.setTimeInMillis(0L);
166 cal
.add(Calendar
.DATE
, days
);
167 cal
.add(Calendar
.MILLISECOND
, milliseconds
);
168 return cal
.getTime();