001: /*
002: * Copyright (c) 2007, intarsys consulting GmbH
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * - Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * - 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: *
014: * - Neither the name of intarsys nor the names of its contributors may be used
015: * to endorse or promote products derived from this software without specific
016: * prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028: * POSSIBILITY OF SUCH DAMAGE.
029: */
030: package de.intarsys.pdf.cds;
031:
032: import java.text.DateFormat;
033: import java.text.Format;
034: import java.text.ParseException;
035: import java.text.SimpleDateFormat;
036: import java.util.Date;
037: import java.util.GregorianCalendar;
038: import java.util.TimeZone;
039: import java.util.regex.Matcher;
040: import java.util.regex.Pattern;
041:
042: import de.intarsys.pdf.cos.COSString;
043:
044: /**
045: * The implementation for a date string based on a {@link
046: * de.intarsys.pdf.cos.COSString}.
047: *
048: * <p>
049: * The string follows the format defined in [PDF], chapter 3.8.2.
050: * </p>
051: */
052: public class CDSDate extends CDSBase {
053: public static final String DATE_FORMAT = "'D':yyyyMMddHHmmss"; //$NON-NLS-1$
054:
055: // YYYY MM DD HH mm SS O HH ' mm '
056: public static final Pattern DatePattern = Pattern
057: .compile("D:(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\+|-|Z|z)?(\\d{2})?\\'?(\\d{2})?.*"); //$NON-NLS-1$
058:
059: private static final DateFormat PDF_DATE_FORMAT = new SimpleDateFormat(
060: DATE_FORMAT);
061:
062: private static final DateFormat dateFormat = DateFormat
063: .getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
064:
065: /**
066: * Create a {@link CDSDate} from a {@link COSString}
067: *
068: * @param string
069: * The base string.
070: * @return Create a {@link CDSDate} from a {@link COSString}
071: */
072: static public CDSDate createFromCOS(COSString string) {
073: if (string == null) {
074: return null;
075: }
076: return new CDSDate(string);
077: }
078:
079: /**
080: * Format a {@link CDSDate} using the default format.
081: *
082: * @param cdsDate
083: * The {@link CDSDate} to be formatted.
084: * @return A formatted {@link String} representation.
085: */
086: public static String format(CDSDate cdsDate) {
087: synchronized (dateFormat) {
088: return format(dateFormat, cdsDate);
089: }
090: }
091:
092: /**
093: * Format a {@link CDSDate} using <code>format</code>.
094: *
095: * @param format
096: * The format to be used for formatting
097: * @param cdsDate
098: * The date to be formatted
099: * @return A formatted {@link String} representation of <code>cdsDate</code>
100: */
101: public static String format(Format format, CDSDate cdsDate) {
102: String strDate = ""; //$NON-NLS-1$
103: if (cdsDate != null) {
104: try {
105: strDate = format.format(cdsDate.toDate());
106: } catch (ParseException e) {
107: strDate = cdsDate.toString();
108: }
109: }
110: return strDate;
111: }
112:
113: /**
114: * Create a {@link Date} using the default format.
115: *
116: * @param string
117: * The date string.
118: * @return The parsed {@link Date}
119: * @throws ParseException
120: */
121: public static Date toDate(String string) throws ParseException {
122: synchronized (PDF_DATE_FORMAT) {
123: return PDF_DATE_FORMAT.parse(string);
124: }
125: }
126:
127: /**
128: * Create a {@link Date} using the default format using the timezone.
129: *
130: * @param string
131: * The date string.
132: * @return The parsed {@link Date}
133: * @throws ParseException
134: */
135: public static Date toDateWithZone(String string)
136: throws ParseException {
137: Matcher m = DatePattern.matcher(string);
138: if (m.matches()) {
139: int year = Integer.valueOf(m.group(1)).intValue();
140: int month = Integer.valueOf(m.group(2)).intValue() - 1;
141: int day = Integer.valueOf(m.group(3)).intValue();
142: int hour = Integer.valueOf(m.group(4)).intValue();
143: int min = Integer.valueOf(m.group(5)).intValue();
144: int sec = Integer.valueOf(m.group(6)).intValue();
145: int hourOffset = 0;
146: int minOffset = 0;
147:
148: if (m.group(9) != null) {
149: minOffset = Integer.valueOf(m.group(9)).intValue();
150: }
151: if (m.group(8) != null) {
152: hourOffset = Integer.valueOf(m.group(8)).intValue();
153: }
154: if (m.group(7) != null) {
155: String o = m.group(7);
156: if (o.toLowerCase().equals("z")) { //$NON-NLS-1$
157: hourOffset = 0;
158: minOffset = 0;
159: } else if ("-".equals(o)) { //$NON-NLS-1$
160: hourOffset *= -1;
161: minOffset *= -1;
162: }
163: hour -= hourOffset;
164: min -= minOffset;
165: }
166:
167: // add local time zone
168: int offset = TimeZone.getDefault().getOffset(
169: System.currentTimeMillis());
170:
171: // determine if the requested date was in DST, if so add offset
172: boolean nowDST = TimeZone.getDefault().inDaylightTime(
173: new Date());
174: GregorianCalendar testDate = new GregorianCalendar(year,
175: month, day);
176: boolean testDST = TimeZone.getDefault().inDaylightTime(
177: testDate.getTime());
178: if (nowDST) {
179: if (!testDST) {
180: offset = offset
181: - TimeZone.getDefault().getDSTSavings();
182: }
183: } else {
184: if (testDST) {
185: offset = offset
186: + TimeZone.getDefault().getDSTSavings();
187: }
188: }
189: offset /= (60 * 1000);
190: hourOffset = Math.abs(offset / 60);
191: minOffset = offset % 60;
192: if (offset < 0) {
193: hour -= hourOffset;
194: min -= minOffset;
195: } else {
196: hour += hourOffset;
197: min += minOffset;
198: }
199: GregorianCalendar greg = new GregorianCalendar(year, month,
200: day, hour, min, sec);
201: return greg.getTime();
202: }
203: throw new ParseException(
204: "can't parse date string '" + string + "'", 0); //$NON-NLS-1$ //$NON-NLS-2$
205: }
206:
207: public static String toString(Date date) {
208: synchronized (PDF_DATE_FORMAT) {
209: return PDF_DATE_FORMAT.format(date);
210: }
211: }
212:
213: public static String toStringWithZone(Date date) {
214: return toStringWithZone(date, TimeZone.getDefault());
215: }
216:
217: public static String toStringWithZone(Date date, TimeZone timeZone) {
218: StringBuilder result = new StringBuilder();
219: result.append(toString(date));
220: int offset = timeZone.getOffset(date.getTime());
221: if (offset < 0) {
222: result.append("-"); //$NON-NLS-1$
223: } else if (offset > 0) {
224: result.append("+"); //$NON-NLS-1$
225: } else {
226: result.append("Z"); //$NON-NLS-1$
227: }
228: int hours = Math.abs(offset / (60 * 60 * 1000));
229: if ((hours / 10) < 1) {
230: result.append("0"); //$NON-NLS-1$
231: }
232: result.append(hours);
233: // TODO 1 @kkr day light saving time
234: result.append("'00'"); //$NON-NLS-1$
235: return result.toString();
236: }
237:
238: /**
239: * Create a new date object with the current system date set
240: */
241: public CDSDate() {
242: super (COSString.create(toStringWithZone(new Date())));
243: }
244:
245: /**
246: * Create a new date object with the date defined in
247: * <code>newDateString</code>.
248: *
249: * @param newDateString
250: * The string representation of the new CDSDate.
251: */
252: protected CDSDate(COSString newDateString) {
253: super (newDateString);
254: }
255:
256: /**
257: * The {@link String} representation of this.
258: *
259: * @return The {@link String} representation of this.
260: */
261: public String stringValue() {
262: return ((COSString) cosGetObject()).stringValue();
263: }
264:
265: /**
266: * The {@link Date} represented by this.
267: *
268: * @return The {@link Date} represented by this.
269: * @throws ParseException
270: */
271: public Date toDate() throws ParseException {
272: return toDateWithZone(stringValue());
273: }
274:
275: /**
276: * A formatted {@link String} representation of this.
277: *
278: * @return A formatted {@link String} representation of this.
279: */
280: public String toFormattedString() {
281: return format(this);
282: }
283: }
|