001 package ezvcard.util;
002
003 import java.text.DateFormat;
004 import java.text.ParseException;
005 import java.util.Date;
006 import java.util.TimeZone;
007
008 /*
009 Copyright (c) 2013, Michael Angstadt
010 All rights reserved.
011
012 Redistribution and use in source and binary forms, with or without
013 modification, are permitted provided that the following conditions are met:
014
015 1. Redistributions of source code must retain the above copyright notice, this
016 list of conditions and the following disclaimer.
017 2. Redistributions in binary form must reproduce the above copyright notice,
018 this list of conditions and the following disclaimer in the documentation
019 and/or other materials provided with the distribution.
020
021 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
022 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
023 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
024 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
025 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
026 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
027 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
028 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
029 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
030 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031
032 The views and conclusions contained in the software and documentation are those
033 of the authors and should not be interpreted as representing official policies,
034 either expressed or implied, of the FreeBSD Project.
035 */
036
037 /**
038 * Helper class that formats and parses vCard dates. vCard dates adhere to the
039 * ISO8601 date format standard.
040 * @author Michael Angstadt
041 */
042 public class VCardDateFormatter {
043 /**
044 * Formats a date for inclusion in a vCard.
045 * @param date the date to format
046 * @param format the format to use
047 * @return the formatted date
048 */
049 public static String format(Date date, ISOFormat format) {
050 return format(date, format, TimeZone.getDefault());
051 }
052
053 /**
054 * Formats a date for inclusion in a vCard.
055 * @param date the date to format
056 * @param format the format to use
057 * @param timeZone the time zone to format the date in. This will be ignored
058 * if the specified ISOFormat is a "UTC" format
059 * @return the formatted date
060 */
061 public static String format(Date date, ISOFormat format, TimeZone timeZone) {
062 switch (format) {
063 case UTC_TIME_BASIC:
064 case UTC_TIME_EXTENDED:
065 timeZone = TimeZone.getTimeZone("UTC");
066 break;
067 }
068
069 DateFormat df = format.getFormatDateFormat();
070 df.setTimeZone(timeZone);
071 String str = df.format(date);
072
073 switch (format) {
074 case TIME_EXTENDED:
075 //add a colon to the timezone
076 //example: converts "2012-07-05T22:31:41-0400" to "2012-07-05T22:31:41-04:00"
077 str = str.replaceAll("([-\\+]\\d{2})(\\d{2})$", "$1:$2");
078 break;
079 }
080
081 return str;
082 }
083
084 /**
085 * Parses a vCard date.
086 * @param dateStr the date string to parse
087 * @return the parsed date
088 * @throws IllegalArgumentException if the date string isn't in one of the
089 * accepted ISO8601 formats
090 */
091 public static Date parse(String dateStr) {
092 //find out what ISOFormat the date is in
093 ISOFormat format = null;
094 for (ISOFormat f : ISOFormat.values()) {
095 if (f.matches(dateStr)) {
096 format = f;
097 break;
098 }
099 }
100 if (format == null) {
101 throw new IllegalArgumentException("Date string is not in a valid ISO-8601 format.");
102 }
103
104 //tweak the date string to make it work with SimpleDateFormat
105 switch (format) {
106 case TIME_EXTENDED:
107 case HCARD_TIME_TAG:
108 //SimpleDateFormat doesn't recognize timezone offsets that have colons
109 //so remove the colon from the timezone offset
110 dateStr = dateStr.replaceAll("([-\\+]\\d{2}):(\\d{2})$", "$1$2");
111 break;
112 case UTC_TIME_BASIC:
113 case UTC_TIME_EXTENDED:
114 //SimpleDateFormat doesn't recognize "Z"
115 dateStr = dateStr.replace("Z", "+0000");
116 break;
117 }
118
119 //parse the date
120 DateFormat df = format.getParseDateFormat();
121 try {
122 return df.parse(dateStr);
123 } catch (ParseException e) {
124 //should never be thrown because the string is checked against a regex
125 throw new RuntimeException(e);
126 }
127 }
128
129 /**
130 * Gets the {@link TimeZone} object that corresponds to the given ID.
131 * @param timezoneId the timezone ID (e.g. "America/New_York")
132 * @return the timezone object or null if not found
133 */
134 public static TimeZone parseTimeZoneId(String timezoneId) {
135 TimeZone timezone = TimeZone.getTimeZone(timezoneId);
136 return "GMT".equals(timezone.getID()) ? null : timezone;
137 }
138
139 private VCardDateFormatter() {
140 //hide constructor
141 }
142 }