001/*
002 * $Id: PdfDate.java 4784 2011-03-15 08:33:00Z blowagie $
003 *
004 * This file is part of the iText (R) project.
005 * Copyright (c) 1998-2011 1T3XT BVBA
006 * Authors: Bruno Lowagie, Paulo Soares, et al.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU Affero General Public License version 3
010 * as published by the Free Software Foundation with the addition of the
011 * following permission added to Section 15 as permitted in Section 7(a):
012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
014 *
015 * This program is distributed in the hope that it will be useful, but
016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
017 * or FITNESS FOR A PARTICULAR PURPOSE.
018 * See the GNU Affero General Public License for more details.
019 * You should have received a copy of the GNU Affero General Public License
020 * along with this program; if not, see http://www.gnu.org/licenses or write to
021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
022 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
023 * http://itextpdf.com/terms-of-use/
024 *
025 * The interactive user interfaces in modified source and object code versions
026 * of this program must display Appropriate Legal Notices, as required under
027 * Section 5 of the GNU Affero General Public License.
028 *
029 * In accordance with Section 7(b) of the GNU Affero General Public License,
030 * a covered work must retain the producer line in every PDF that is created
031 * or manipulated using iText.
032 *
033 * You can be released from the requirements of the license by purchasing
034 * a commercial license. Buying such a license is mandatory as soon as you
035 * develop commercial activities involving the iText software without
036 * disclosing the source code of your own applications.
037 * These activities include: offering paid services to customers as an ASP,
038 * serving PDFs on the fly in a web application, shipping iText with a closed
039 * source product.
040 *
041 * For more information, please contact iText Software Corp. at this
042 * address: sales@itextpdf.com
043 */
044package com.itextpdf.text.pdf;
045
046import java.util.Calendar;
047import java.util.GregorianCalendar;
048import java.util.SimpleTimeZone;
049
050/**
051 * <CODE>PdfDate</CODE> is the PDF date object.
052 * <P>
053 * PDF defines a standard date format. The PDF date format closely follows the format
054 * defined by the international standard ASN.1 (Abstract Syntax Notation One, defined
055 * in CCITT X.208 or ISO/IEC 8824). A date is a <CODE>PdfString</CODE> of the form:
056 * <P><BLOCKQUOTE>
057 * (D:YYYYMMDDHHmmSSOHH'mm')
058 * </BLOCKQUOTE><P>
059 * This object is described in the 'Portable Document Format Reference Manual version 1.3'
060 * section 7.2 (page 183-184)
061 *
062 * @see         PdfString
063 * @see         java.util.GregorianCalendar
064 */
065
066public class PdfDate extends PdfString {
067
068    private static final int DATE_SPACE[] = {Calendar.YEAR, 4, 0, Calendar.MONTH, 2, -1, Calendar.DAY_OF_MONTH, 2, 0,
069        Calendar.HOUR_OF_DAY, 2, 0, Calendar.MINUTE, 2, 0, Calendar.SECOND, 2, 0};
070    
071    // constructors
072    
073/**
074 * Constructs a <CODE>PdfDate</CODE>-object.
075 *
076 * @param               d                       the date that has to be turned into a <CODE>PdfDate</CODE>-object
077 */
078    
079    public PdfDate(Calendar d) {
080        super();
081        StringBuffer date = new StringBuffer("D:");
082        date.append(setLength(d.get(Calendar.YEAR), 4));
083        date.append(setLength(d.get(Calendar.MONTH) + 1, 2));
084        date.append(setLength(d.get(Calendar.DATE), 2));
085        date.append(setLength(d.get(Calendar.HOUR_OF_DAY), 2));
086        date.append(setLength(d.get(Calendar.MINUTE), 2));
087        date.append(setLength(d.get(Calendar.SECOND), 2));
088        int timezone = (d.get(Calendar.ZONE_OFFSET) + d.get(Calendar.DST_OFFSET)) / (60 * 60 * 1000);
089        if (timezone == 0) {
090            date.append('Z');
091        }
092        else if (timezone < 0) {
093            date.append('-');
094            timezone = -timezone;
095        }
096        else {
097            date.append('+');
098        }
099        if (timezone != 0) {
100            date.append(setLength(timezone, 2)).append('\'');
101            int zone = Math.abs((d.get(Calendar.ZONE_OFFSET) + d.get(Calendar.DST_OFFSET)) / (60 * 1000)) - (timezone * 60);
102            date.append(setLength(zone, 2)).append('\'');
103        }
104        value = date.toString();
105    }
106    
107/**
108 * Constructs a <CODE>PdfDate</CODE>-object, representing the current day and time.
109 */
110    
111    public PdfDate() {
112        this(new GregorianCalendar());
113    }
114    
115/**
116 * Adds a number of leading zeros to a given <CODE>String</CODE> in order to get a <CODE>String</CODE>
117 * of a certain length.
118 *
119 * @param               i               a given number
120 * @param               length          the length of the resulting <CODE>String</CODE>
121 * @return              the resulting <CODE>String</CODE>
122 */
123    
124    private String setLength(int i, int length) { // 1.3-1.4 problem fixed by Finn Bock
125        StringBuffer tmp = new StringBuffer();
126        tmp.append(i);
127        while (tmp.length() < length) {
128            tmp.insert(0, "0");
129        }
130        tmp.setLength(length);
131        return tmp.toString();
132    }
133    
134    /**
135     * Gives the W3C format of the PdfDate.
136     * @return a formatted date
137     */
138    public String getW3CDate() {
139        return getW3CDate(value);
140    }
141    
142    /**
143     * Gives the W3C format of the PdfDate.
144     * @param d the date in the format D:YYYYMMDDHHmmSSOHH'mm'
145     * @return a formatted date
146     */
147    public static String getW3CDate(String d) {
148        if (d.startsWith("D:"))
149            d = d.substring(2);
150        StringBuffer sb = new StringBuffer();
151        if (d.length() < 4)
152            return "0000";
153        sb.append(d.substring(0, 4)); //year
154        d = d.substring(4);
155        if (d.length() < 2)
156            return sb.toString();
157        sb.append('-').append(d.substring(0, 2)); //month
158        d = d.substring(2);
159        if (d.length() < 2)
160            return sb.toString();
161        sb.append('-').append(d.substring(0, 2)); //day
162        d = d.substring(2);
163        if (d.length() < 2)
164            return sb.toString();
165        sb.append('T').append(d.substring(0, 2)); //hour
166        d = d.substring(2);
167        if (d.length() < 2) {
168            sb.append(":00Z");
169            return sb.toString();
170        }
171        sb.append(':').append(d.substring(0, 2)); //minute
172        d = d.substring(2);
173        if (d.length() < 2) {
174            sb.append('Z');
175            return sb.toString();
176        }
177        sb.append(':').append(d.substring(0, 2)); //second
178        d = d.substring(2);
179        if (d.startsWith("-") || d.startsWith("+")) {
180            String sign = d.substring(0, 1);
181            d = d.substring(1);
182            String h = "00";
183            String m = "00";
184            if (d.length() >= 2) {
185                h = d.substring(0, 2);
186                if (d.length() > 2) {
187                    d = d.substring(3);
188                    if (d.length() >= 2)
189                        m = d.substring(0, 2);
190                }
191                sb.append(sign).append(h).append(':').append(m);
192                return sb.toString();
193            }
194        }
195        sb.append('Z');
196        return sb.toString();
197    }
198    
199    /**
200     * Converts a PDF string representing a date into a Calendar.
201     * @param s the PDF string representing a date
202     * @return a <CODE>Calendar</CODE> representing the date or <CODE>null</CODE> if the string
203     * was not a date
204     */    
205    public static Calendar decode(String s) {
206        try {
207            if (s.startsWith("D:"))
208                s = s.substring(2);
209            GregorianCalendar calendar;
210            int slen = s.length();
211            int idx = s.indexOf('Z');
212            if (idx >= 0) {
213                slen = idx;
214                calendar = new GregorianCalendar(new SimpleTimeZone(0, "ZPDF"));
215            }
216            else {
217                int sign = 1;
218                idx = s.indexOf('+');
219                if (idx < 0) {
220                    idx = s.indexOf('-');
221                    if (idx >= 0)
222                        sign = -1;
223                }
224                if (idx < 0)
225                    calendar = new GregorianCalendar();
226                else {
227                    int offset = Integer.parseInt(s.substring(idx + 1, idx + 3)) * 60;
228                    if (idx + 5 < s.length())
229                        offset += Integer.parseInt(s.substring(idx + 4, idx + 6));
230                    calendar = new GregorianCalendar(new SimpleTimeZone(offset * sign * 60000, "ZPDF"));
231                    slen = idx;
232                }
233            }
234            calendar.clear();
235            idx = 0;
236            for (int k = 0; k < DATE_SPACE.length; k += 3) {
237                if (idx >= slen)
238                    break;
239                calendar.set(DATE_SPACE[k], Integer.parseInt(s.substring(idx, idx + DATE_SPACE[k + 1])) + DATE_SPACE[k + 2]);
240                idx += DATE_SPACE[k + 1];
241            }
242            return calendar;
243        }
244        catch (Exception e) {
245            return null;
246        }
247    }
248}