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