001: /**
002: * Copyright (c) 2003-2006, www.pdfbox.org
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions are met:
007: *
008: * 1. Redistributions of source code must retain the above copyright notice,
009: * this list of conditions and the following disclaimer.
010: * 2. Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: * 3. Neither the name of pdfbox; nor the names of its
014: * contributors may be used to endorse or promote products derived from this
015: * software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
021: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: *
028: * http://www.pdfbox.org
029: *
030: */package org.pdfbox.util;
031:
032: import java.text.ParseException;
033: import java.text.SimpleDateFormat;
034:
035: import java.io.IOException;
036:
037: import java.util.Calendar;
038: import java.util.Date;
039: import java.util.GregorianCalendar;
040: import java.util.SimpleTimeZone;
041: import java.util.TimeZone;
042:
043: import org.pdfbox.cos.COSString;
044:
045: /**
046: * This class is used to convert dates to strings and back using the PDF
047: * date standards. Date are described in PDFReference1.4 section 3.8.2
048: *
049: * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
050: * @version $Revision: 1.13 $
051: */
052: public class DateConverter {
053: private static final SimpleDateFormat PDF_DATE_FORMAT = new SimpleDateFormat(
054: "yyyyMMddHHmmss");
055:
056: //The Date format is supposed to be the PDF_DATE_FORMAT, but not all PDF documents
057: //will use that date, so I have added a couple other potential formats
058: //to try if the original one does not work.
059: private static final SimpleDateFormat[] POTENTIAL_FORMATS = new SimpleDateFormat[] {
060: new SimpleDateFormat("EEEE, dd MMM yyyy hh:mm:ss a"),
061: new SimpleDateFormat("EEEE, MMM dd, yyyy hh:mm:ss a"),
062: new SimpleDateFormat("MM/dd/yyyy hh:mm:ss"),
063: new SimpleDateFormat("MM/dd/yyyy") };
064:
065: private static final SimpleDateFormat ISO_8601_DATE_FORMAT = new SimpleDateFormat(
066: "yyyy-MM-dd'T'HH:mm:ss");
067:
068: private DateConverter() {
069: //utility class should not be constructed.
070: }
071:
072: /**
073: * This will convert the calendar to a string.
074: *
075: * @param date The date to convert to a string.
076: *
077: * @return The date as a String to be used in a PDF document.
078: */
079: public static String toString(Calendar date) {
080: String retval = null;
081: if (date != null) {
082: StringBuffer buffer = new StringBuffer();
083: TimeZone zone = date.getTimeZone();
084: long offsetInMinutes = zone.getOffset(date
085: .getTimeInMillis()) / 1000 / 60;
086: long hours = Math.abs(offsetInMinutes / 60);
087: long minutes = Math.abs(offsetInMinutes % 60);
088: buffer.append("D:");
089: buffer.append(PDF_DATE_FORMAT.format(date.getTime()));
090: if (offsetInMinutes == 0) {
091: buffer.append("Z");
092: } else if (offsetInMinutes < 0) {
093: buffer.append("-");
094: } else {
095: buffer.append("+");
096: }
097: if (hours < 10) {
098: buffer.append("0");
099: }
100: buffer.append(hours);
101: buffer.append("'");
102: if (minutes < 10) {
103: buffer.append("0");
104: }
105: buffer.append(minutes);
106: buffer.append("'");
107: retval = buffer.toString();
108:
109: }
110: return retval;
111: }
112:
113: /**
114: * This will convert a string to a calendar.
115: *
116: * @param date The string representation of the calendar.
117: *
118: * @return The calendar that this string represents.
119: *
120: * @throws IOException If the date string is not in the correct format.
121: */
122: public static Calendar toCalendar(COSString date)
123: throws IOException {
124: Calendar retval = null;
125: if (date != null) {
126: retval = toCalendar(date.getString());
127: }
128:
129: return retval;
130: }
131:
132: /**
133: * This will convert a string to a calendar.
134: *
135: * @param date The string representation of the calendar.
136: *
137: * @return The calendar that this string represents.
138: *
139: * @throws IOException If the date string is not in the correct format.
140: */
141: public static Calendar toCalendar(String date) throws IOException {
142: Calendar retval = null;
143: if (date != null && date.trim().length() > 0) {
144: //these are the default values
145: int year = 0;
146: int month = 1;
147: int day = 1;
148: int hour = 0;
149: int minute = 0;
150: int second = 0;
151: //first string off the prefix if it exists
152: try {
153: SimpleTimeZone zone = null;
154: if (date.startsWith("D:")) {
155: date = date.substring(2, date.length());
156: }
157: if (date.length() < 4) {
158: throw new IOException(
159: "Error: Invalid date format '" + date + "'");
160: }
161: year = Integer.parseInt(date.substring(0, 4));
162: if (date.length() >= 6) {
163: month = Integer.parseInt(date.substring(4, 6));
164: }
165: if (date.length() >= 8) {
166: day = Integer.parseInt(date.substring(6, 8));
167: }
168: if (date.length() >= 10) {
169: hour = Integer.parseInt(date.substring(8, 10));
170: }
171: if (date.length() >= 12) {
172: minute = Integer.parseInt(date.substring(10, 12));
173: }
174: if (date.length() >= 14) {
175: second = Integer.parseInt(date.substring(12, 14));
176: }
177: retval = new GregorianCalendar(year, month - 1, day,
178: hour, minute, second);
179: if (date.length() >= 15) {
180: char sign = date.charAt(14);
181: if (sign == 'Z') {
182: zone = new SimpleTimeZone(0, "Unknown");
183: } else {
184: int hours = 0;
185: int minutes = 0;
186: if (date.length() >= 17) {
187: if (sign == '+') {
188: //parseInt cannot handle the + sign
189: hours = Integer.parseInt(date
190: .substring(15, 17));
191: } else {
192: hours = Integer.parseInt(date
193: .substring(14, 17));
194: }
195: }
196: if (date.length() > 20) {
197: minutes = Integer.parseInt(date.substring(
198: 18, 20));
199: }
200: zone = new SimpleTimeZone(hours * 60 * 60
201: * 1000 + minutes * 60 * 1000, "Unknown");
202: }
203: retval.setTimeZone(zone);
204: }
205: } catch (NumberFormatException e) {
206: for (int i = 0; retval == null
207: && i < POTENTIAL_FORMATS.length; i++) {
208: try {
209: Date utilDate = POTENTIAL_FORMATS[i]
210: .parse(date);
211: retval = new GregorianCalendar();
212: retval.setTime(utilDate);
213: } catch (ParseException pe) {
214: //ignore and move to next potential format
215: }
216: }
217: if (retval == null) {
218: //we didn't find a valid date format so throw an exception
219: throw new IOException("Error converting date:"
220: + date);
221: }
222: }
223: }
224: return retval;
225: }
226:
227: /**
228: * Convert the date to iso 8601 string format.
229: *
230: * @param cal The date to convert.
231: * @return The date represented as an ISO 8601 string.
232: */
233: public static String toISO8601(Calendar cal) {
234: StringBuffer retval = new StringBuffer();
235: retval.append(ISO_8601_DATE_FORMAT.format(cal.getTime()));
236: int timeZone = cal.get(Calendar.ZONE_OFFSET);
237: if (timeZone < 0) {
238: retval.append("-");
239: } else {
240: retval.append("+");
241: }
242: timeZone = Math.abs(timeZone);
243: //milliseconds/1000 = seconds = seconds / 60 = minutes = minutes/60 = hours
244: int hours = timeZone / 1000 / 60 / 60;
245: int minutes = (timeZone - (hours * 1000 * 60 * 60)) / 1000 / 1000;
246: if (hours < 10) {
247: retval.append("0");
248: }
249: retval.append(Integer.toString(hours));
250: retval.append(":");
251: if (minutes < 10) {
252: retval.append("0");
253: }
254: retval.append(Integer.toString(minutes));
255:
256: return retval.toString();
257: }
258: }
|