DERUTCTime.java revision e1142c149e244797ce73b0e7fad40816e447a817
1package org.bouncycastle.asn1;
2
3import java.io.IOException;
4import java.text.ParseException;
5import java.text.SimpleDateFormat;
6import java.util.Date;
7import java.util.SimpleTimeZone;
8
9import org.bouncycastle.util.Arrays;
10import org.bouncycastle.util.Strings;
11
12/**
13 * UTC time object.
14 */
15public class DERUTCTime
16    extends ASN1Primitive
17{
18    private byte[]      time;
19
20    /**
21     * return an UTC Time from the passed in object.
22     *
23     * @exception IllegalArgumentException if the object cannot be converted.
24     */
25    public static ASN1UTCTime getInstance(
26        Object  obj)
27    {
28        if (obj == null || obj instanceof ASN1UTCTime)
29        {
30            return (ASN1UTCTime)obj;
31        }
32
33        if (obj instanceof DERUTCTime)
34        {
35            return new ASN1UTCTime(((DERUTCTime)obj).time);
36        }
37
38        if (obj instanceof byte[])
39        {
40            try
41            {
42                return (ASN1UTCTime)fromByteArray((byte[])obj);
43            }
44            catch (Exception e)
45            {
46                throw new IllegalArgumentException("encoding error in getInstance: " + e.toString());
47            }
48        }
49
50        throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
51    }
52
53    /**
54     * return an UTC Time from a tagged object.
55     *
56     * @param obj the tagged object holding the object we want
57     * @param explicit true if the object is meant to be explicitly
58     *              tagged false otherwise.
59     * @exception IllegalArgumentException if the tagged object cannot
60     *               be converted.
61     */
62    public static ASN1UTCTime getInstance(
63        ASN1TaggedObject obj,
64        boolean          explicit)
65    {
66        ASN1Object o = obj.getObject();
67
68        if (explicit || o instanceof ASN1UTCTime)
69        {
70            return getInstance(o);
71        }
72        else
73        {
74            return new ASN1UTCTime(((ASN1OctetString)o).getOctets());
75        }
76    }
77
78    /**
79     * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were
80     * never encoded. When you're creating one of these objects from scratch, that's
81     * what you want to use, otherwise we'll try to deal with whatever gets read from
82     * the input stream... (this is why the input format is different from the getTime()
83     * method output).
84     * <p>
85     *
86     * @param time the time string.
87     */
88    public DERUTCTime(
89        String  time)
90    {
91        this.time = Strings.toByteArray(time);
92        try
93        {
94            this.getDate();
95        }
96        catch (ParseException e)
97        {
98            throw new IllegalArgumentException("invalid date string: " + e.getMessage());
99        }
100    }
101
102    /**
103     * base constructer from a java.util.date object
104     */
105    public DERUTCTime(
106        Date time)
107    {
108        SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmss'Z'");
109
110        dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
111
112        this.time = Strings.toByteArray(dateF.format(time));
113    }
114
115    DERUTCTime(
116        byte[]  time)
117    {
118        this.time = time;
119    }
120
121    /**
122     * return the time as a date based on whatever a 2 digit year will return. For
123     * standardised processing use getAdjustedDate().
124     *
125     * @return the resulting date
126     * @exception ParseException if the date string cannot be parsed.
127     */
128    public Date getDate()
129        throws ParseException
130    {
131        SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmssz");
132
133        return dateF.parse(getTime());
134    }
135
136    /**
137     * return the time as an adjusted date
138     * in the range of 1950 - 2049.
139     *
140     * @return a date in the range of 1950 to 2049.
141     * @exception ParseException if the date string cannot be parsed.
142     */
143    public Date getAdjustedDate()
144        throws ParseException
145    {
146        SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
147
148        dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
149
150        return dateF.parse(getAdjustedTime());
151    }
152
153    /**
154     * return the time - always in the form of
155     *  YYMMDDhhmmssGMT(+hh:mm|-hh:mm).
156     * <p>
157     * Normally in a certificate we would expect "Z" rather than "GMT",
158     * however adding the "GMT" means we can just use:
159     * <pre>
160     *     dateF = new SimpleDateFormat("yyMMddHHmmssz");
161     * </pre>
162     * To read in the time and get a date which is compatible with our local
163     * time zone.
164     * <p>
165     * <b>Note:</b> In some cases, due to the local date processing, this
166     * may lead to unexpected results. If you want to stick the normal
167     * convention of 1950 to 2049 use the getAdjustedTime() method.
168     */
169    public String getTime()
170    {
171        String stime = Strings.fromByteArray(time);
172
173        //
174        // standardise the format.
175        //
176        if (stime.indexOf('-') < 0 && stime.indexOf('+') < 0)
177        {
178            if (stime.length() == 11)
179            {
180                return stime.substring(0, 10) + "00GMT+00:00";
181            }
182            else
183            {
184                return stime.substring(0, 12) + "GMT+00:00";
185            }
186        }
187        else
188        {
189            int index = stime.indexOf('-');
190            if (index < 0)
191            {
192                index = stime.indexOf('+');
193            }
194            String d = stime;
195
196            if (index == stime.length() - 3)
197            {
198                d += "00";
199            }
200
201            if (index == 10)
202            {
203                return d.substring(0, 10) + "00GMT" + d.substring(10, 13) + ":" + d.substring(13, 15);
204            }
205            else
206            {
207                return d.substring(0, 12) + "GMT" + d.substring(12, 15) + ":" +  d.substring(15, 17);
208            }
209        }
210    }
211
212    /**
213     * return a time string as an adjusted date with a 4 digit year. This goes
214     * in the range of 1950 - 2049.
215     */
216    public String getAdjustedTime()
217    {
218        String   d = this.getTime();
219
220        if (d.charAt(0) < '5')
221        {
222            return "20" + d;
223        }
224        else
225        {
226            return "19" + d;
227        }
228    }
229
230    boolean isConstructed()
231    {
232        return false;
233    }
234
235    int encodedLength()
236    {
237        int length = time.length;
238
239        return 1 + StreamUtil.calculateBodyLength(length) + length;
240    }
241
242    void encode(
243        ASN1OutputStream  out)
244        throws IOException
245    {
246        out.write(BERTags.UTC_TIME);
247
248        int length = time.length;
249
250        out.writeLength(length);
251
252        for (int i = 0; i != length; i++)
253        {
254            out.write((byte)time[i]);
255        }
256    }
257
258    boolean asn1Equals(
259        ASN1Primitive o)
260    {
261        if (!(o instanceof DERUTCTime))
262        {
263            return false;
264        }
265
266        return Arrays.areEqual(time, ((DERUTCTime)o).time);
267    }
268
269    public int hashCode()
270    {
271        return Arrays.hashCode(time);
272    }
273
274    public String toString()
275    {
276      return Strings.fromByteArray(time);
277    }
278}
279