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