1package org.bouncycastle.asn1.cms;
2
3import java.text.ParseException;
4import java.text.SimpleDateFormat;
5import java.util.Calendar;
6import java.util.Date;
7import java.util.Locale;
8import java.util.SimpleTimeZone;
9
10import org.bouncycastle.asn1.ASN1Choice;
11import org.bouncycastle.asn1.ASN1GeneralizedTime;
12import org.bouncycastle.asn1.ASN1Object;
13import org.bouncycastle.asn1.ASN1Primitive;
14import org.bouncycastle.asn1.ASN1TaggedObject;
15import org.bouncycastle.asn1.ASN1UTCTime;
16import org.bouncycastle.asn1.DERGeneralizedTime;
17import org.bouncycastle.asn1.DERUTCTime;
18
19/**
20 * <a href="http://tools.ietf.org/html/rfc5652#section-11.3">RFC 5652</a>:
21 * Dual-mode timestamp format producing either UTCTIme or GeneralizedTime.
22 * <p>
23 * <pre>
24 * Time ::= CHOICE {
25 *     utcTime        UTCTime,
26 *     generalTime    GeneralizedTime }
27 * </pre>
28 * <p>
29 * This has a constructor using java.util.Date for input which generates
30 * a {@link org.bouncycastle.asn1.DERUTCTime DERUTCTime} object if the
31 * supplied datetime is in range 1950-01-01-00:00:00 UTC until 2049-12-31-23:59:60 UTC.
32 * If the datetime value is outside that range, the generated object will be
33 * {@link org.bouncycastle.asn1.DERGeneralizedTime DERGeneralizedTime}.
34 */
35public class Time
36    extends ASN1Object
37    implements ASN1Choice
38{
39    ASN1Primitive time;
40
41    public static Time getInstance(
42        ASN1TaggedObject obj,
43        boolean          explicit)
44    {
45        return getInstance(obj.getObject());
46    }
47
48    /**
49     * @deprecated use getInstance()
50     */
51    public Time(
52        ASN1Primitive   time)
53    {
54        if (!(time instanceof ASN1UTCTime)
55            && !(time instanceof ASN1GeneralizedTime))
56        {
57            throw new IllegalArgumentException("unknown object passed to Time");
58        }
59
60        this.time = time;
61    }
62
63    /**
64     * Creates a time object from a given date - if the date is between 1950
65     * and 2049 a UTCTime object is generated, otherwise a GeneralizedTime
66     * is used.
67     *
68     * @param time a date object representing the time of interest.
69     */
70    public Time(
71        Date    time)
72    {
73        SimpleTimeZone      tz = new SimpleTimeZone(0, "Z");
74        // BEGIN android-changed
75        // Was: SimpleDateFormat    dateF = new SimpleDateFormat("yyyyMMddHHmmss");
76        SimpleDateFormat    dateF = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US);
77        // END android-changed
78
79        dateF.setTimeZone(tz);
80
81        String  d = dateF.format(time) + "Z";
82        int     year = Integer.parseInt(d.substring(0, 4));
83
84        if (year < 1950 || year > 2049)
85        {
86            this.time = new DERGeneralizedTime(d);
87        }
88        else
89        {
90            this.time = new DERUTCTime(d.substring(2));
91        }
92    }
93
94    /**
95     * Creates a time object from a given date and locale - if the date is between 1950
96     * and 2049 a UTCTime object is generated, otherwise a GeneralizedTime
97     * is used. You may need to use this constructor if the default locale
98     * doesn't use a Gregorian calender so that the GeneralizedTime produced is compatible with other ASN.1 implementations.
99     *
100     * @param time a date object representing the time of interest.
101     * @param locale an appropriate Locale for producing an ASN.1 GeneralizedTime value.
102     */
103    public Time(
104        Date    time,
105        Locale locale)
106    {
107        SimpleTimeZone      tz = new SimpleTimeZone(0, "Z");
108        // BEGIN android-changed
109        // Was: SimpleDateFormat    dateF = new SimpleDateFormat("yyyyMMddHHmmss", locale);
110        SimpleDateFormat    dateF = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US);
111        dateF.setCalendar(Calendar.getInstance(locale));
112        // END android-changed
113
114        dateF.setTimeZone(tz);
115
116        String  d = dateF.format(time) + "Z";
117        int     year = Integer.parseInt(d.substring(0, 4));
118
119        if (year < 1950 || year > 2049)
120        {
121            this.time = new DERGeneralizedTime(d);
122        }
123        else
124        {
125            this.time = new DERUTCTime(d.substring(2));
126        }
127    }
128
129    /**
130     * Return a Time object from the given object.
131     * <p>
132     * Accepted inputs:
133     * <ul>
134     * <li> null &rarr; null
135     * <li> {@link Time} object
136     * <li> {@link org.bouncycastle.asn1.DERUTCTime DERUTCTime} object
137     * <li> {@link org.bouncycastle.asn1.DERGeneralizedTime DERGeneralizedTime} object
138     * </ul>
139     *
140     * @param obj the object we want converted.
141     * @exception IllegalArgumentException if the object cannot be converted.
142     */
143    public static Time getInstance(
144        Object  obj)
145    {
146        if (obj == null || obj instanceof Time)
147        {
148            return (Time)obj;
149        }
150        else if (obj instanceof ASN1UTCTime)
151        {
152            return new Time((ASN1UTCTime)obj);
153        }
154        else if (obj instanceof ASN1GeneralizedTime)
155        {
156            return new Time((ASN1GeneralizedTime)obj);
157        }
158
159        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
160    }
161
162    /**
163     * Get the date+tine as a String in full form century format.
164     */
165    public String getTime()
166    {
167        if (time instanceof ASN1UTCTime)
168        {
169            return ((ASN1UTCTime)time).getAdjustedTime();
170        }
171        else
172        {
173            return ((ASN1GeneralizedTime)time).getTime();
174        }
175    }
176
177    /**
178     * Get java.util.Date version of date+time.
179     */
180    public Date getDate()
181    {
182        try
183        {
184            if (time instanceof ASN1UTCTime)
185            {
186                return ((ASN1UTCTime)time).getAdjustedDate();
187            }
188            else
189            {
190                return ((ASN1GeneralizedTime)time).getDate();
191            }
192        }
193        catch (ParseException e)
194        {         // this should never happen
195            throw new IllegalStateException("invalid date string: " + e.getMessage());
196        }
197    }
198
199    /**
200     * Produce an object suitable for an ASN1OutputStream.
201     */
202    public ASN1Primitive toASN1Primitive()
203    {
204        return time;
205    }
206}
207