001 /**
002 * Copyright (c) 2010 Yahoo! Inc. All rights reserved.
003 * Licensed under the Apache License, Version 2.0 (the "License");
004 * you may not use this file except in compliance with the License.
005 * You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software
010 * distributed under the License is distributed on an "AS IS" BASIS,
011 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 * See the License for the specific language governing permissions and
013 * limitations under the License. See accompanying LICENSE file.
014 */
015 package org.apache.oozie.util;
016
017 import java.sql.Timestamp;
018 import java.text.DateFormat;
019 import java.text.ParsePosition;
020 import java.text.SimpleDateFormat;
021 import java.util.Calendar;
022 import java.util.Date;
023 import java.util.GregorianCalendar;
024 import java.util.Locale;
025 import java.util.TimeZone;
026
027 import org.apache.oozie.coord.TimeUnit;
028
029 public class DateUtils {
030
031 private static final String[] W3CDATETIME_MASKS = {"yyyy-MM-dd'T'HH:mmz"};
032
033 /**
034 * Parses a Date out of a String with a date in W3C date-time format.
035 * <p/>
036 * It parsers the following formats:
037 * <ul>
038 * <li>"yyyy-MM-dd'T'HH:mm:ssz"</li>
039 * <li>"yyyy-MM-dd'T'HH:mmz"</li>
040 * <li>"yyyy-MM-dd"</li>
041 * <li>"yyyy-MM"</li>
042 * <li>"yyyy"</li>
043 * </ul>
044 * <p/>
045 * Refer to the java.text.SimpleDateFormat javadocs for details on the
046 * format of each element.
047 * <p/>
048 *
049 * @param sDate string to parse for a date.
050 * @return the Date represented by the given W3C date-time string. It
051 * returns <b>null</b> if it was not possible to parse the given
052 * string into a Date.
053 */
054 /*
055 * public static Date parseW3CDateTime(String sDate) { // if sDate has time
056 * on it, it injects 'GTM' before de TZ displacement to // allow the
057 * SimpleDateFormat parser to parse it properly int tIndex =
058 * sDate.indexOf("T"); if (tIndex > -1) { if (sDate.endsWith("Z")) { sDate =
059 * sDate.substring(0, sDate.length() - 1) + "+00:00"; } int tzdIndex =
060 * sDate.indexOf("+", tIndex); if (tzdIndex == -1) { tzdIndex =
061 * sDate.indexOf("-", tIndex); } if (tzdIndex > -1) { String pre =
062 * sDate.substring(0, tzdIndex); int secFraction = pre.indexOf(","); if
063 * (secFraction > -1) { pre = pre.substring(0, secFraction); } String post =
064 * sDate.substring(tzdIndex); sDate = pre + "GMT" + post; } } else { sDate
065 * += "T00:00GMT"; } return parseUsingMask(W3CDATETIME_MASKS, sDate); }
066 */
067 /**
068 * Parses a Date out of a string using an array of masks. <p/> It uses the masks in order until one of them succedes
069 * or all fail. <p/>
070 *
071 * @param masks array of masks to use for parsing the string
072 * @param sDate string to parse for a date.
073 * @return the Date represented by the given string using one of the given masks. It returns <b>null</b> if it was
074 * not possible to parse the the string with any of the masks.
075 */
076 private static Date parseUsingMask(String[] masks, String sDate) {
077 sDate = (sDate != null) ? sDate.trim() : null;
078 ParsePosition pp;
079 Date d = null;
080 if (sDate != null) {
081 for (int i = 0; d == null && i < masks.length; i++) {
082 DateFormat df = new SimpleDateFormat(masks[i], Locale.US);
083 df.setLenient(true);
084 pp = new ParsePosition(0);
085 d = df.parse(sDate, pp);
086 if (pp.getIndex() != sDate.length()) {
087 d = null;
088 }
089 }
090 }
091 return d;
092 }
093
094 private static final TimeZone UTC = getTimeZone("UTC");
095
096 private static DateFormat getISO8601DateFormat() {
097 DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'");
098 dateFormat.setTimeZone(UTC);
099 return dateFormat;
100 }
101
102 public static TimeZone getTimeZone(String tzId) {
103 if (tzId == null) {
104 throw new IllegalArgumentException("Invalid TimeZone: " + tzId);
105 }
106 TimeZone tz = TimeZone.getTimeZone(tzId);
107 if (!tz.getID().equals(tzId)) {
108 throw new IllegalArgumentException("Invalid TimeZone: " + tzId);
109 }
110 return tz;
111 }
112
113 public static Date parseDateUTC(String s) throws Exception {
114 return getISO8601DateFormat().parse(s);
115 }
116
117 public static String formatDateUTC(Date d) throws Exception {
118 return (d != null) ? getISO8601DateFormat().format(d) : "NULL";
119 }
120
121 public static String formatDateUTC(Calendar c) throws Exception {
122 return (c != null) ? formatDateUTC(c.getTime()) : "NULL";
123 }
124
125 /**
126 * This function returns number of hour in a day when given a Calendar with appropriate TZ. It consider DST to find
127 * the number of hours. Generally it is 24. At some tZ, in one day of a year it is 23 and another day it is 25
128 *
129 * @param cal: The date for which the number of hours is requested
130 * @return number of hour in that day.
131 */
132 public static int hoursInDay(Calendar cal) {
133 Calendar localCal = new GregorianCalendar(cal.getTimeZone());
134 localCal.set(Calendar.MILLISECOND, 0);
135 localCal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), 0, 30, 0);
136 localCal.add(Calendar.HOUR_OF_DAY, 24);
137 switch (localCal.get(Calendar.HOUR_OF_DAY)) {
138 case 1:
139 return 23;
140 case 23:
141 return 25;
142 default: // Case 0
143 return 24;
144 }
145 }
146
147 /**
148 * Determine whether a specific date is on DST change day
149 *
150 * @param cal: Date to know if it is DST change day. Appropriate TZ is specified
151 * @return true , if it DST change date otherwise false
152 */
153 public static boolean isDSTChangeDay(Calendar cal) {
154 return hoursInDay(cal) != 24;
155 }
156
157 /**
158 * Move the any date-time to the end of the duration. If endOfFlag == day, move the date to the end of day (24:00 on
159 * the same day or 00:00 on the next day) If endOf Flag = month. move the date to then end of current month
160 * Otherwise do nothing
161 *
162 * @param cal : Date-time needs to be moved to the end
163 * @param endOfFlag : day (for end of day) or month (for end of month) or empty
164 */
165 public static void moveToEnd(Calendar cal, TimeUnit endOfFlag) {
166 // TODO: Both logic needs to be checked
167 if (endOfFlag == TimeUnit.END_OF_DAY) { // 24:00:00
168 cal.add(Calendar.DAY_OF_MONTH, 1);
169 // cal.set(Calendar.HOUR_OF_DAY, cal
170 // .getActualMaximum(Calendar.HOUR_OF_DAY) + 1);// TODO:
171 cal.set(Calendar.HOUR_OF_DAY, 0);
172 cal.set(Calendar.MINUTE, 0);
173 cal.set(Calendar.SECOND, 0);
174 }
175 else {
176 if (endOfFlag == TimeUnit.END_OF_MONTH) {
177 cal.add(Calendar.MONTH, 1);
178 cal.set(Calendar.DAY_OF_MONTH, 1);
179 cal.set(Calendar.HOUR_OF_DAY, 0);
180 cal.set(Calendar.MINUTE, 0);
181 cal.set(Calendar.SECOND, 0);
182 }
183 }
184 }
185
186 /**
187 * Create a Calendar instance using the specified date and Time zone
188 * @param dateString
189 * @param tz : TimeZone
190 * @return appropriate Calendar object
191 * @throws Exception
192 */
193 public static Calendar getCalendar(String dateString, TimeZone tz) throws Exception {
194 Date date = DateUtils.parseDateUTC(dateString);
195 Calendar calDate = Calendar.getInstance();
196 calDate.setTime(date);
197 calDate.setTimeZone(tz);
198 return calDate;
199 }
200
201 /**
202 * Create a Calendar instance for UTC time zone using the specified date.
203 * @param dateString
204 * @return appropriate Calendar object
205 * @throws Exception
206 */
207 public static Calendar getCalendar(String dateString) throws Exception {
208 return getCalendar(dateString, DateUtils.getTimeZone("UTC"));
209 }
210
211 /**
212 * Convert java.sql.Timestamp to java.util.Date
213 *
214 * @param timestamp java.sql.Timestamp
215 * @return java.util.Date
216 */
217 public static java.util.Date toDate(java.sql.Timestamp timestamp) {
218 if (timestamp != null) {
219 long milliseconds = timestamp.getTime();
220 return new java.util.Date(milliseconds);
221 }
222 return null;
223 }
224
225 /**
226 * Convert java.util.Date to java.sql.Timestamp
227 *
228 * @param d java.util.Date
229 * @return java.sql.Timestamp
230 */
231 public static Timestamp convertDateToTimestamp(Date d) {
232 if (d != null) {
233 return new Timestamp(d.getTime());
234 }
235 return null;
236 }
237
238 /**
239 * Return the UTC date and time in W3C format down to second
240 * (yyyy-MM-ddTHH:mm:ssZ). i.e.: 1997-07-16T19:20:30Z
241 *
242 * @return the formatted time string.
243 */
244 public static String convertDateToString(Date date) {
245 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'");
246 sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
247 return sdf.format(date);
248 }
249 }