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 private static DateFormat getSpecificDateFormat(String format) { 103 DateFormat dateFormat = new SimpleDateFormat(format); 104 dateFormat.setTimeZone(UTC); 105 return dateFormat; 106 } 107 108 public static TimeZone getTimeZone(String tzId) { 109 if (tzId == null) { 110 throw new IllegalArgumentException("Invalid TimeZone: " + tzId); 111 } 112 TimeZone tz = TimeZone.getTimeZone(tzId); 113 if (!tz.getID().equals(tzId)) { 114 throw new IllegalArgumentException("Invalid TimeZone: " + tzId); 115 } 116 return tz; 117 } 118 119 public static Date parseDateUTC(String s) throws Exception { 120 return getISO8601DateFormat().parse(s); 121 } 122 123 public static String formatDateUTC(Date d) throws Exception { 124 return (d != null) ? getISO8601DateFormat().format(d) : "NULL"; 125 } 126 127 public static String formatDateCustom(Date d, String format) throws Exception { 128 return (d != null) ? getSpecificDateFormat(format).format(d) : "NULL"; 129 } 130 131 public static String formatDateUTC(Calendar c) throws Exception { 132 return (c != null) ? formatDateUTC(c.getTime()) : "NULL"; 133 } 134 135 /** 136 * This function returns number of hour in a day when given a Calendar with appropriate TZ. It consider DST to find 137 * 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 138 * 139 * @param cal: The date for which the number of hours is requested 140 * @return number of hour in that day. 141 */ 142 public static int hoursInDay(Calendar cal) { 143 Calendar localCal = new GregorianCalendar(cal.getTimeZone()); 144 localCal.set(Calendar.MILLISECOND, 0); 145 localCal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), 0, 30, 0); 146 localCal.add(Calendar.HOUR_OF_DAY, 24); 147 switch (localCal.get(Calendar.HOUR_OF_DAY)) { 148 case 1: 149 return 23; 150 case 23: 151 return 25; 152 default: // Case 0 153 return 24; 154 } 155 } 156 157 /** 158 * Determine whether a specific date is on DST change day 159 * 160 * @param cal: Date to know if it is DST change day. Appropriate TZ is specified 161 * @return true , if it DST change date otherwise false 162 */ 163 public static boolean isDSTChangeDay(Calendar cal) { 164 return hoursInDay(cal) != 24; 165 } 166 167 /** 168 * 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 169 * the same day or 00:00 on the next day) If endOf Flag = month. move the date to then end of current month 170 * Otherwise do nothing 171 * 172 * @param cal : Date-time needs to be moved to the end 173 * @param endOfFlag : day (for end of day) or month (for end of month) or empty 174 */ 175 public static void moveToEnd(Calendar cal, TimeUnit endOfFlag) { 176 // TODO: Both logic needs to be checked 177 if (endOfFlag == TimeUnit.END_OF_DAY) { // 24:00:00 178 cal.add(Calendar.DAY_OF_MONTH, 1); 179 // cal.set(Calendar.HOUR_OF_DAY, cal 180 // .getActualMaximum(Calendar.HOUR_OF_DAY) + 1);// TODO: 181 cal.set(Calendar.HOUR_OF_DAY, 0); 182 cal.set(Calendar.MINUTE, 0); 183 cal.set(Calendar.SECOND, 0); 184 } 185 else { 186 if (endOfFlag == TimeUnit.END_OF_MONTH) { 187 cal.add(Calendar.MONTH, 1); 188 cal.set(Calendar.DAY_OF_MONTH, 1); 189 cal.set(Calendar.HOUR_OF_DAY, 0); 190 cal.set(Calendar.MINUTE, 0); 191 cal.set(Calendar.SECOND, 0); 192 } 193 } 194 } 195 196 /** 197 * Create a Calendar instance using the specified date and Time zone 198 * @param dateString 199 * @param tz : TimeZone 200 * @return appropriate Calendar object 201 * @throws Exception 202 */ 203 public static Calendar getCalendar(String dateString, TimeZone tz) throws Exception { 204 Date date = DateUtils.parseDateUTC(dateString); 205 Calendar calDate = Calendar.getInstance(); 206 calDate.setTime(date); 207 calDate.setTimeZone(tz); 208 return calDate; 209 } 210 211 /** 212 * Create a Calendar instance for UTC time zone using the specified date. 213 * @param dateString 214 * @return appropriate Calendar object 215 * @throws Exception 216 */ 217 public static Calendar getCalendar(String dateString) throws Exception { 218 return getCalendar(dateString, DateUtils.getTimeZone("UTC")); 219 } 220 221 /** 222 * Convert java.sql.Timestamp to java.util.Date 223 * 224 * @param timestamp java.sql.Timestamp 225 * @return java.util.Date 226 */ 227 public static java.util.Date toDate(java.sql.Timestamp timestamp) { 228 if (timestamp != null) { 229 long milliseconds = timestamp.getTime(); 230 return new java.util.Date(milliseconds); 231 } 232 return null; 233 } 234 235 /** 236 * Convert java.util.Date to java.sql.Timestamp 237 * 238 * @param d java.util.Date 239 * @return java.sql.Timestamp 240 */ 241 public static Timestamp convertDateToTimestamp(Date d) { 242 if (d != null) { 243 return new Timestamp(d.getTime()); 244 } 245 return null; 246 } 247 248 /** 249 * Return the UTC date and time in W3C format down to second 250 * (yyyy-MM-ddTHH:mm:ssZ). i.e.: 1997-07-16T19:20:30Z 251 * 252 * @return the formatted time string. 253 */ 254 public static String convertDateToString(Date date) { 255 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"); 256 sdf.setTimeZone(TimeZone.getTimeZone("UTC")); 257 return sdf.format(date); 258 } 259 }