Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / search / DateUtil.java
blob07a5bff05d2831dac6857dde36ed73ae7e065226
1 // Copyright 2012 Google Inc. All Rights Reserved.
3 package com.google.appengine.api.search;
5 import java.text.DateFormat;
6 import java.text.ParsePosition;
7 import java.text.SimpleDateFormat;
8 import java.util.Calendar;
9 import java.util.Date;
10 import java.util.GregorianCalendar;
11 import java.util.Locale;
12 import java.util.TimeZone;
14 /**
15 * A utility class that centralizes processing of dates.
18 public final class DateUtil {
20 /**
21 * The milliseconds in a day.
23 private static final int MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
25 /**
26 * The UTC time zone.
28 private static final ThreadLocal<TimeZone> UTC_TZ =
29 new ThreadLocal<TimeZone>() {
30 @Override protected TimeZone initialValue() {
31 return TimeZone.getTimeZone("UTC");
35 /**
36 * The maximum date that can be stored in a date field.
38 public static final Date MAX_DATE = getEpochPlusDays(Integer.MAX_VALUE, MILLISECONDS_IN_DAY - 1);
40 /**
41 * The minimum date that can be stored in a date field.
43 public static final Date MIN_DATE = getEpochPlusDays(Integer.MIN_VALUE, 0);
45 private static DateFormat getDateFormat(String formatString) {
46 DateFormat format = new SimpleDateFormat(formatString, Locale.US);
47 format.setTimeZone(UTC_TZ.get());
48 return format;
51 private static final ThreadLocal<DateFormat> ISO8601_SIMPLE =
52 new ThreadLocal<DateFormat>() {
53 @Override protected DateFormat initialValue() {
54 return getDateFormat("yyyy-MM-dd");
58 private static final ThreadLocal<DateFormat> ISO8601_DATE_TIME_SIMPLE =
59 new ThreadLocal<DateFormat>() {
60 @Override protected DateFormat initialValue() {
61 return getDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
65 private static final ThreadLocal<DateFormat> ISO8601_DATE_TIME_SIMPLE_ERA =
66 new ThreadLocal<DateFormat>() {
67 @Override protected DateFormat initialValue() {
68 return getDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'G");
72 private DateUtil() {
75 /**
76 * Get a UTC calendar with Locale US.
78 private static Calendar getCalendarUTC() {
79 return new GregorianCalendar(UTC_TZ.get(), Locale.US);
82 /**
83 * Formats a date according ISO 8601 full date time format. Currently,
84 * this is used to print dates for error messages.
86 * @param date the date to format as a string
87 * @return a string representing the date in ISO 8601 format
89 public static String formatDateTime(Date date) {
90 if (date == null) {
91 return null;
93 return isBeforeCommonEra(date) ?
94 ISO8601_DATE_TIME_SIMPLE_ERA.get().format(date) :
95 ISO8601_DATE_TIME_SIMPLE.get().format(date);
98 /**
99 * Returns true if the date is before the common era.
101 private static boolean isBeforeCommonEra(Date date) {
102 Calendar cal = getCalendarUTC();
103 cal.setTime(date);
104 return cal.get(Calendar.ERA) == GregorianCalendar.BC;
108 * Parses an ISO 8601 into a {@link Date} object.
110 * This function is only used for deserializing legacy "yyyy-MM-dd"
111 * formatted dates stored in backend. These are currently not supporting
112 * BC Era dates, so neither will this function.
114 * @param dateString the ISO 8601 formatted string for a date
115 * @return the {@link Date} parsed from the date string
117 private static Date parseDate(String dateString) {
118 ParsePosition pos = new ParsePosition(0);
119 Date d = ISO8601_SIMPLE.get().parse(dateString, pos);
120 if (pos.getIndex() < dateString.length()) {
121 throw new IllegalArgumentException(
122 String.format("Failed to parse date string \"%s\"", dateString));
124 return d;
128 * Converts date into a string containing the milliseconds since the UNIX Epoch.
130 * @param date the date to serialize as a string
131 * @return a string representing the date as milliseconds since the UNIX Epoch
133 public static String serializeDate(Date date) {
134 return date == null ? "" : Long.toString(date.getTime());
138 * Converts a string containing the milliseconds since the UNIX Epoch into a Date.
140 * Two formats of date string are supported: "yyyy-MM-dd" and a long. Eventually,
141 * the "yyyy-MM-dd" format support will be removed.
143 * @param date the date string to deserialize into a date
144 * @return a date
146 public static Date deserializeDate(String date) {
147 if (date == null) {
148 return null;
150 if (date.startsWith("-")) {
151 if (date.length() > 1 && date.indexOf('-', 1) >= 0) {
152 return parseDate(date);
154 } else {
155 if (date.indexOf('-') > 0) {
156 return parseDate(date);
159 return new Date(Long.parseLong(date));
163 * Truncates given date leaving date elements lesser than the
164 * specified field set to 0. For example, if you wish to remove
165 * time component from the date use the following:
166 * <pre>
167 * Date d = ...
168 * Date yearMonthDay = Field.truncate(d, Calendar.DAY_OF_MONTH);
169 * </pre>
171 * @param date the date to be truncated
172 * @param field the least significant field to be left untouched
173 * @return the date with fields less significant than field set to 0
174 * @throws IllegalArgumentException if field is not a valid datetime field.
175 * @deprecated as of 1.7.2 this is no longer required for Date fields
177 @Deprecated
178 static Date truncate(Date date, int field) {
179 if (Calendar.MILLISECOND == field) {
180 return date;
182 Calendar cal = getCalendarUTC();
183 cal.setTime(date);
184 cal.set(Calendar.MILLISECOND, 0);
185 if (Calendar.SECOND == field) {
186 return cal.getTime();
188 cal.set(Calendar.SECOND, 0);
189 if (Calendar.MINUTE == field) {
190 return cal.getTime();
192 cal.set(Calendar.MINUTE, 0);
193 if (Calendar.HOUR_OF_DAY == field) {
194 return cal.getTime();
196 cal.set(Calendar.HOUR_OF_DAY, 0);
197 if (Calendar.DAY_OF_MONTH == field) {
198 return cal.getTime();
200 cal.set(Calendar.DAY_OF_MONTH, 1);
201 if (Calendar.MONTH == field) {
202 return cal.getTime();
204 cal.set(Calendar.MONTH, 0);
205 if (Calendar.YEAR == field) {
206 return cal.getTime();
208 throw new IllegalArgumentException("Invalid field value " + field);
212 * Constructs a {@link} set to the UNIX Epoch plus days plus milliseconds.
214 * @param days the number of days to add to the UNIX Epoch to
215 * get a Date
216 * @param milliseconds the number of milliseconds to add to the date
217 * @return the Date with number of days plus Epoch
219 private static final Date getEpochPlusDays(int days, int milliseconds) {
220 Calendar cal = getCalendarUTC();
221 cal.setTimeInMillis(0L);
222 cal.add(Calendar.DATE, days);
223 cal.add(Calendar.MILLISECOND, milliseconds);
224 return cal.getTime();