001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 * 
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 * 
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 * 
019 */
020package org.apache.directory.shared.kerberos;
021
022
023import java.text.ParseException;
024import java.util.Calendar;
025import java.util.Date;
026import java.util.Locale;
027import java.util.TimeZone;
028
029import org.apache.directory.api.util.Strings;
030
031
032/**
033 * An specialization of the ASN.1 GeneralTime. The Kerberos time contains date and
034 * time up to the seconds, but with no fractional seconds. It's also always
035 * expressed as UTC timeZone, thus the 'Z' at the end of its string representation.
036 * 
037 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
038 */
039public class KerberosTime implements Comparable<KerberosTime>, java.io.Serializable
040{
041    /**
042     * 
043     */
044    private static final long serialVersionUID = -7541256140193748103L;
045
046    /** The UTC timeZone */
047    private static final TimeZone UTC = TimeZone.getTimeZone( "UTC" );
048
049    /** The KerberosTime as a String*/
050    private String date;
051
052    /** The kerberosTime, as a long */
053    private long kerberosTime;
054
055    /** Constant for the {@link KerberosTime} "infinity." */
056    public static final KerberosTime INFINITY = new KerberosTime( Long.MAX_VALUE );
057
058    /** The number of milliseconds in a minute. */
059    public static final int MINUTE = 60000;
060
061    /** The number of milliseconds in a day. */
062    public static final int DAY = MINUTE * 1440;
063
064    /** The number of milliseconds in a week. */
065    public static final int WEEK = MINUTE * 10080;
066
067
068    /**
069     * Creates a new instance of a KerberosTime object
070     */
071    public KerberosTime()
072    {
073        kerberosTime = ( System.currentTimeMillis() / 1000L ) * 1000L; // drop the ms
074        convertInternal( kerberosTime );
075    }
076
077
078    /**
079     * Creates a new instance of a KerberosTime object
080     * 
081     * @param date the KerberosTime to store
082     */
083    public KerberosTime( String date )
084    {
085        try
086        {
087            setDate( date );
088        }
089        catch ( ParseException pe )
090        {
091            throw new IllegalArgumentException( "Bad time : " + date );
092        }
093    }
094
095
096    /**
097     * Creates a new instance of a KerberosTime object
098     */
099    public KerberosTime( long date )
100    {
101        convertInternal( date );
102    }
103
104
105    /**
106     * Creates a new instance of KerberosTime.
107     *
108     * @param time
109     */
110    public KerberosTime( Date time )
111    {
112        kerberosTime = ( time.getTime() / 1000L ) * 1000L; // drop the ms
113        convertInternal( kerberosTime );
114    }
115
116
117    /**
118     * converts the given milliseconds time to seconds and
119     * also formats the time to the generalized form
120     * 
121     * @param date the time in milliseconds
122     */
123    private void convertInternal( long date )
124    {
125        Calendar calendar = Calendar.getInstance( UTC, Locale.ROOT );
126        calendar.setTimeInMillis( date );
127
128        synchronized ( KerberosUtils.UTC_DATE_FORMAT )
129        {
130            this.date = KerberosUtils.UTC_DATE_FORMAT.format( calendar.getTime() );
131        }
132
133        kerberosTime = ( calendar.getTimeInMillis() / 1000L ) * 1000L; // drop the ms
134    }
135
136
137    /**
138     * Returns the {@link KerberosTime} as a long.
139     *
140     * @return The {@link KerberosTime} as a long.
141     */
142    public long getTime()
143    {
144        return kerberosTime;
145    }
146
147
148    /**
149     * Returns the {@link KerberosTime} as a {@link Date}.
150     *
151     * @return The {@link KerberosTime} as a {@link Date}.
152     */
153    public Date toDate()
154    {
155        return new Date( kerberosTime );
156    }
157
158
159    /**
160     * Returns the {@link KerberosTime} for a given zulu time.
161     *
162     * @param zuluTime
163     * @return The {@link KerberosTime}.
164     * @throws ParseException
165     */
166    public static KerberosTime getTime( String zuluTime ) throws ParseException
167    {
168        Date date = null;
169
170        synchronized ( KerberosUtils.UTC_DATE_FORMAT )
171        {
172            date = KerberosUtils.UTC_DATE_FORMAT.parse( zuluTime );
173        }
174
175        return new KerberosTime( date );
176    }
177
178
179    /**
180     * Sets the date if it's a valid KerberosTime
181     * @param date The date to store
182     */
183    public void setDate( String date ) throws ParseException
184    {
185        synchronized ( KerberosUtils.UTC_DATE_FORMAT )
186        {
187            kerberosTime = KerberosUtils.UTC_DATE_FORMAT.parse( date ).getTime();
188        }
189
190        convertInternal( kerberosTime );
191    }
192
193
194    /**
195     * @return The date as a byte[]
196     */
197    public byte[] getBytes()
198    {
199        return Strings.getBytesUtf8( date );
200    }
201
202
203    /**
204     * @return The stored date
205     */
206    public String getDate()
207    {
208        return date;
209    }
210
211
212    @Override
213    public int hashCode()
214    {
215        return ( int ) kerberosTime;
216    }
217
218
219    @Override
220    public boolean equals( Object obj )
221    {
222        if ( this == obj )
223        {
224            return true;
225        }
226
227        if ( !( obj instanceof KerberosTime ) )
228        {
229            return true;
230        }
231
232        KerberosTime other = ( KerberosTime ) obj;
233
234        return kerberosTime == other.kerberosTime;
235    }
236
237
238    /**
239     * Returns whether this {@link KerberosTime} is within the given clockskew.
240     *
241     * @param clockSkew
242     * @return true if this {@link KerberosTime} is within the given clockskew.
243     */
244    public boolean isInClockSkew( long clockSkew )
245    {
246        // The KerberosTime does not have milliseconds
247        long delta = Math.abs( kerberosTime - System.currentTimeMillis() );
248
249        return delta < clockSkew;
250    }
251
252
253    /**
254     * compares current kerberos time with the given kerberos time
255     * @param that the kerberos time against which the current kerberos time is compared
256     * @return 0 if both times are equal,<br>
257     *         -1 if current time is less than the given time and<br>
258     *         1 if the given time is greater than the current time
259     */
260    public int compareTo( KerberosTime that )
261    {
262        final int BEFORE = -1;
263        final int EQUAL = 0;
264        final int AFTER = 1;
265
266        // this optimization is usually worthwhile, and can always be added
267        if ( this == that )
268        {
269            return EQUAL;
270        }
271
272        // primitive numbers follow this form
273        if ( this.kerberosTime < that.kerberosTime )
274        {
275            return BEFORE;
276        }
277
278        if ( this.kerberosTime > that.kerberosTime )
279        {
280            return AFTER;
281        }
282
283        return EQUAL;
284    }
285
286
287    /**
288     * checks if the current kerberos time is less or equal than the given kerberos time
289     * @param ktime the kerberos time against which the current kerberos time needs to be compared
290     * @return true if current kerberos time is less or equal than the given kerberos time, false otherwise
291     */
292    public boolean lessThan( KerberosTime ktime )
293    {
294        return kerberosTime <= ktime.kerberosTime;
295    }
296
297
298    /**
299     * checks if the current kerberos time is greater than the given kerberos time
300     * @param ktime the kerberos time against which the currnet kerberos time needs to be compared
301     * @return true if current kerberos time is greater than the given kerberos time, false otherwise
302     */
303    public boolean greaterThan( KerberosTime ktime )
304    {
305        return kerberosTime > ktime.kerberosTime;
306    }
307
308
309    /**
310     * Returns whether this {@link KerberosTime} is zero.
311     *
312     * @return true if this {@link KerberosTime} is zero.
313     */
314    public boolean isZero()
315    {
316        return kerberosTime == 0;
317    }
318
319
320    /**
321     * {@inheritDoc}
322     */
323    public String toString()
324    {
325        return date;
326    }
327}