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 }