001package org.apache.commons.ssl.org.bouncycastle.asn1.cms;
002
003import java.text.ParseException;
004import java.text.SimpleDateFormat;
005import java.util.Date;
006import java.util.Locale;
007import java.util.SimpleTimeZone;
008
009import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Choice;
010import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1GeneralizedTime;
011import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Object;
012import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Primitive;
013import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1TaggedObject;
014import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1UTCTime;
015import org.apache.commons.ssl.org.bouncycastle.asn1.DERGeneralizedTime;
016import org.apache.commons.ssl.org.bouncycastle.asn1.DERUTCTime;
017
018/**
019 * <a href="http://tools.ietf.org/html/rfc5652#section-11.3">RFC 5652</a>:
020 * Dual-mode timestamp format producing either UTCTIme or GeneralizedTime.
021 * <p>
022 * <pre>
023 * Time ::= CHOICE {
024 *     utcTime        UTCTime,
025 *     generalTime    GeneralizedTime }
026 * </pre>
027 * <p>
028 * This has a constructor using java.util.Date for input which generates
029 * a {@link org.bouncycastle.asn1.DERUTCTime DERUTCTime} object if the
030 * supplied datetime is in range 1950-01-01-00:00:00 UTC until 2049-12-31-23:59:60 UTC.
031 * If the datetime value is outside that range, the generated object will be
032 * {@link org.bouncycastle.asn1.DERGeneralizedTime DERGeneralizedTime}.
033 */
034public class Time
035    extends ASN1Object
036    implements ASN1Choice
037{
038    ASN1Primitive time;
039
040    public static Time getInstance(
041        ASN1TaggedObject obj,
042        boolean          explicit)
043    {
044        return getInstance(obj.getObject());
045    }
046
047    /**
048     * @deprecated use getInstance()
049     */
050    public Time(
051        ASN1Primitive   time)
052    {
053        if (!(time instanceof ASN1UTCTime)
054            && !(time instanceof ASN1GeneralizedTime))
055        {
056            throw new IllegalArgumentException("unknown object passed to Time");
057        }
058
059        this.time = time; 
060    }
061
062    /**
063     * Creates a time object from a given date - if the date is between 1950
064     * and 2049 a UTCTime object is generated, otherwise a GeneralizedTime
065     * is used.
066     *
067     * @param time a date object representing the time of interest.
068     */
069    public Time(
070        Date    time)
071    {
072        SimpleTimeZone      tz = new SimpleTimeZone(0, "Z");
073        SimpleDateFormat    dateF = new SimpleDateFormat("yyyyMMddHHmmss");
074
075        dateF.setTimeZone(tz);
076
077        String  d = dateF.format(time) + "Z";
078        int     year = Integer.parseInt(d.substring(0, 4));
079
080        if (year < 1950 || year > 2049)
081        {
082            this.time = new DERGeneralizedTime(d);
083        }
084        else
085        {
086            this.time = new DERUTCTime(d.substring(2));
087        }
088    }
089
090    /**
091     * Creates a time object from a given date and locale - if the date is between 1950
092     * and 2049 a UTCTime object is generated, otherwise a GeneralizedTime
093     * is used. You may need to use this constructor if the default locale
094     * doesn't use a Gregorian calender so that the GeneralizedTime produced is compatible with other ASN.1 implementations.
095     *
096     * @param time a date object representing the time of interest.
097     * @param locale an appropriate Locale for producing an ASN.1 GeneralizedTime value.
098     */
099    public Time(
100        Date    time,
101        Locale locale)
102    {
103        SimpleTimeZone      tz = new SimpleTimeZone(0, "Z");
104        SimpleDateFormat    dateF = new SimpleDateFormat("yyyyMMddHHmmss", locale);
105
106        dateF.setTimeZone(tz);
107
108        String  d = dateF.format(time) + "Z";
109        int     year = Integer.parseInt(d.substring(0, 4));
110
111        if (year < 1950 || year > 2049)
112        {
113            this.time = new DERGeneralizedTime(d);
114        }
115        else
116        {
117            this.time = new DERUTCTime(d.substring(2));
118        }
119    }
120
121    /**
122     * Return a Time object from the given object.
123     * <p>
124     * Accepted inputs:
125     * <ul>
126     * <li> null &rarr; null
127     * <li> {@link Time} object
128     * <li> {@link org.bouncycastle.asn1.DERUTCTime DERUTCTime} object
129     * <li> {@link org.bouncycastle.asn1.DERGeneralizedTime DERGeneralizedTime} object
130     * </ul>
131     *
132     * @param obj the object we want converted.
133     * @exception IllegalArgumentException if the object cannot be converted.
134     */
135    public static Time getInstance(
136        Object  obj)
137    {
138        if (obj == null || obj instanceof Time)
139        {
140            return (Time)obj;
141        }
142        else if (obj instanceof ASN1UTCTime)
143        {
144            return new Time((ASN1UTCTime)obj);
145        }
146        else if (obj instanceof ASN1GeneralizedTime)
147        {
148            return new Time((ASN1GeneralizedTime)obj);
149        }
150
151        throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
152    }
153
154    /**
155     * Get the date+tine as a String in full form century format.
156     */
157    public String getTime()
158    {
159        if (time instanceof ASN1UTCTime)
160        {
161            return ((ASN1UTCTime)time).getAdjustedTime();
162        }
163        else
164        {
165            return ((ASN1GeneralizedTime)time).getTime();
166        }
167    }
168
169    /**
170     * Get java.util.Date version of date+time.
171     */
172    public Date getDate()
173    {
174        try
175        {
176            if (time instanceof ASN1UTCTime)
177            {
178                return ((ASN1UTCTime)time).getAdjustedDate();
179            }
180            else
181            {
182                return ((ASN1GeneralizedTime)time).getDate();
183            }
184        }
185        catch (ParseException e)
186        {         // this should never happen
187            throw new IllegalStateException("invalid date string: " + e.getMessage());
188        }
189    }
190
191    /**
192     * Produce an object suitable for an ASN1OutputStream.
193     */
194    public ASN1Primitive toASN1Primitive()
195    {
196        return time;
197    }
198}