001 package biweekly.util; 002 003 import java.text.DecimalFormat; 004 import java.text.NumberFormat; 005 import java.util.Calendar; 006 import java.util.Date; 007 import java.util.TimeZone; 008 import java.util.regex.Matcher; 009 import java.util.regex.Pattern; 010 011 /* 012 Copyright (c) 2013, Michael Angstadt 013 All rights reserved. 014 015 Redistribution and use in source and binary forms, with or without 016 modification, are permitted provided that the following conditions are met: 017 018 1. Redistributions of source code must retain the above copyright notice, this 019 list of conditions and the following disclaimer. 020 2. Redistributions in binary form must reproduce the above copyright notice, 021 this list of conditions and the following disclaimer in the documentation 022 and/or other materials provided with the distribution. 023 024 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 025 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 026 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 027 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 028 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 029 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 030 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 031 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 032 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 033 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 034 */ 035 036 /** 037 * <p> 038 * Contains the raw components of a date-time value. 039 * </p> 040 * <p> 041 * <b>Examples:</b> 042 * 043 * <pre class="brush:java"> 044 * //July 22, 2013 at 17:25 045 * DateTimeComponents components = new DateTimeComponents(2013, 07, 22, 17, 25, 0, false); 046 * 047 * //parsing a date string (accepts basic and extended formats) 048 * DateTimeComponents components = DateTimeComponents.parse("20130722T172500"); 049 * 050 * //converting to date string 051 * DateTimeComponents components = new DateTimeComponents(2013, 07, 22, 17, 25, 0, false); 052 * String str = components.toString(true); //"2013-07-22T17:25:00" 053 * 054 * //converting to a Date object 055 * DateTimeComponents components = new DateTimeComponents(2013, 07, 22, 17, 25, 0, false); 056 * Date date = components.toDate(); 057 * 058 * </pre> 059 * 060 * </p> 061 * @author Michael Angstadt 062 */ 063 public final class DateTimeComponents { 064 private static final Pattern regex = Pattern.compile("^(\\d{4})-?(\\d{2})-?(\\d{2})(T(\\d{2}):?(\\d{2}):?(\\d{2})(Z?))?.*"); 065 private final int year, month, date, hour, minute, second; 066 private final boolean utc; 067 068 /** 069 * Parses the components out of a date-time string. 070 * @param dateString the date-time string (basic and extended formats 071 * supported, e.g. "20130331T020000" or "2013-03-31T02:00:00") 072 * @return the parsed components 073 * @throws IllegalArgumentException if the date string cannot be parsed 074 */ 075 public static DateTimeComponents parse(String dateString) { 076 Matcher m = regex.matcher(dateString); 077 if (!m.find()) { 078 throw new IllegalArgumentException("Cannot parse date: " + dateString); 079 } 080 081 int i = 1; 082 083 int year = Integer.parseInt(m.group(i++)); 084 085 int month = Integer.parseInt(m.group(i++)); 086 087 int date = Integer.parseInt(m.group(i++)); 088 089 i++; //skip 090 091 String hourStr = m.group(i++); 092 int hour = (hourStr == null) ? 0 : Integer.parseInt(hourStr); 093 094 String minuteStr = m.group(i++); 095 int minute = (minuteStr == null) ? 0 : Integer.parseInt(minuteStr); 096 097 String secondStr = m.group(i++); 098 int second = (secondStr == null) ? 0 : Integer.parseInt(secondStr); 099 100 boolean utc = "Z".equals(m.group(i++)); 101 102 return new DateTimeComponents(year, month, date, hour, minute, second, utc); 103 } 104 105 /** 106 * Copies an existing DateTimeComponents object. 107 * @param original the object to copy from 108 * @param year the new year value or null not to change 109 * @param month the new month value or null not to change 110 * @param date the new date value or null not to change 111 * @param hour the new hour value or null not to change 112 * @param minute the new minute value or null not to change 113 * @param second the new second value or null not to change 114 * @param utc true if the time is in UTC, false if not, or null not to 115 * change 116 */ 117 public DateTimeComponents(DateTimeComponents original, Integer year, Integer month, Integer date, Integer hour, Integer minute, Integer second, Boolean utc) { 118 //@formatter:off 119 this( 120 (year == null) ? original.year : year, 121 (month == null) ? original.month : month, 122 (date == null) ? original.date : date, 123 (hour == null) ? original.hour : hour, 124 (minute == null) ? original.minute : minute, 125 (second == null) ? original.second : second, 126 (utc == null) ? original.utc : utc 127 ); 128 //@formatter:on 129 } 130 131 /** 132 * Creates a new set of date-time components. 133 * @param year the year (e.g. "2013") 134 * @param month the month (e.g. "1" for January) 135 * @param date the date of the month (e.g. "15") 136 * @param hour the hour (e.g. "13") 137 * @param minute the minute 138 * @param second the second 139 * @param utc true if the time is in UTC, false if not 140 */ 141 public DateTimeComponents(int year, int month, int date, int hour, int minute, int second, boolean utc) { 142 this.year = year; 143 this.month = month; 144 this.date = date; 145 this.hour = hour; 146 this.minute = minute; 147 this.second = second; 148 this.utc = utc; 149 } 150 151 /** 152 * Gets the year component. 153 * @return the year 154 */ 155 public int getYear() { 156 return year; 157 } 158 159 /** 160 * Gets the month component. 161 * @return the month (e.g. "1" for January) 162 */ 163 public int getMonth() { 164 return month; 165 } 166 167 /** 168 * Gets the date component 169 * @return the date 170 */ 171 public int getDate() { 172 return date; 173 } 174 175 /** 176 * Gets the hour component 177 * @return the hour 178 */ 179 public int getHour() { 180 return hour; 181 } 182 183 /** 184 * Gets the minute component. 185 * @return the minute 186 */ 187 public int getMinute() { 188 return minute; 189 } 190 191 /** 192 * Gets the second component. 193 * @return the second 194 */ 195 public int getSecond() { 196 return second; 197 } 198 199 /** 200 * Gets whether the time is in UTC or not 201 * @return true if the time is in UTC, false if not 202 */ 203 public boolean isUtc() { 204 return utc; 205 } 206 207 /** 208 * Converts the date-time components to a string using "basic" format. 209 * @return the date string 210 */ 211 @Override 212 public String toString() { 213 return toString(false); 214 } 215 216 /** 217 * Converts the date-time components to a string. 218 * @param extended true to use extended format, false to use basic 219 * @return the date string 220 */ 221 public String toString(boolean extended) { 222 NumberFormat nf = new DecimalFormat("00"); 223 String dash = extended ? "-" : ""; 224 String colon = extended ? ":" : ""; 225 String z = utc ? "Z" : ""; 226 227 return year + dash + nf.format(month) + dash + nf.format(date) + "T" + nf.format(hour) + colon + nf.format(minute) + colon + nf.format(second) + z; 228 } 229 230 /** 231 * Converts the date-time components to a {@link Date} object. 232 * @return the date object 233 */ 234 public Date toDate() { 235 TimeZone tz = utc ? TimeZone.getTimeZone("UTC") : TimeZone.getDefault(); 236 Calendar c = Calendar.getInstance(tz); 237 c.clear(); 238 c.set(Calendar.YEAR, year); 239 c.set(Calendar.MONTH, month - 1); 240 c.set(Calendar.DATE, date); 241 c.set(Calendar.HOUR_OF_DAY, hour); 242 c.set(Calendar.MINUTE, minute); 243 c.set(Calendar.SECOND, second); 244 return c.getTime(); 245 } 246 247 @Override 248 public int hashCode() { 249 final int prime = 31; 250 int result = 1; 251 result = prime * result + date; 252 result = prime * result + hour; 253 result = prime * result + minute; 254 result = prime * result + month; 255 result = prime * result + second; 256 result = prime * result + (utc ? 1231 : 1237); 257 result = prime * result + year; 258 return result; 259 } 260 261 @Override 262 public boolean equals(Object obj) { 263 if (this == obj) 264 return true; 265 if (obj == null) 266 return false; 267 if (getClass() != obj.getClass()) 268 return false; 269 DateTimeComponents other = (DateTimeComponents) obj; 270 if (date != other.date) 271 return false; 272 if (hour != other.hour) 273 return false; 274 if (minute != other.minute) 275 return false; 276 if (month != other.month) 277 return false; 278 if (second != other.second) 279 return false; 280 if (utc != other.utc) 281 return false; 282 if (year != other.year) 283 return false; 284 return true; 285 } 286 }