001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyrighted [year] [name of copyright owner]".
013 *
014 * Copyright © 2011-2015 ForgeRock AS. All rights reserved.
015 */
016package org.forgerock.audit.util;
017
018import java.util.Date;
019
020import org.joda.time.Chronology;
021import org.joda.time.DateTime;
022import org.joda.time.DateTimeZone;
023import org.joda.time.chrono.ISOChronology;
024import org.joda.time.format.DateTimeFormatter;
025import org.joda.time.format.ISODateTimeFormat;
026
027/**
028 * Manages timestamp strings in ISO8601 format.
029 * <p>
030 * <b>Example:</b> <br>
031 * 2011-09-09T14:58:17.6+02:00 <br>
032 * 2011-09-09T14:58:17.65+02:00 <br>
033 * 2011-09-09T14:58:17.654+02:00 <br>
034 * 2011-09-09T14:58:17.654Z
035 */
036public final class DateUtil {
037    private Chronology chrono;
038
039    /**
040     * Fetches a DateUtil that is set in the default timezone.
041     *
042     * @return DateUtil set in the default timezone
043     */
044    public static DateUtil getDateUtil() {
045        return new DateUtil();
046    }
047
048    /**
049     * Returns a DateUtil using a specified timezone.
050     *
051     * @param zone
052     *            string representation of a timezone i.e. "UTC" or "Asia/Tokyo"
053     * @return DateUtil set with the supplied timezone
054     */
055    public static DateUtil getDateUtil(final String zone) {
056        return new DateUtil(zone);
057    }
058
059    /**
060     * Creates a DateUtil using a specified timezone.
061     *
062     * @param zone
063     *            DateTimeZone object
064     * @return DateUtil set with the supplied timezone
065     */
066    public static DateUtil getDateUtil(final DateTimeZone zone) {
067        return new DateUtil(zone);
068    }
069
070    /**
071     * Creates a DateUtil using the default timezone and generates ISO8601
072     * timestamps.
073     */
074    private DateUtil() {
075        this(DateTimeZone.getDefault());
076    }
077
078    /**
079     * Creates a DateUtil using a specified timezone and generates ISO8601
080     * timestamps.
081     *
082     * @param zone
083     *            string representation of a timezone. i.e. "UTC" or
084     *            "Asia/Tokyo"
085     */
086    private DateUtil(final String zone) {
087        this(DateTimeZone.forID(zone));
088    }
089
090    /**
091     * Creates a DateUtil using a specified timezone and generates ISO8601
092     * timestamps.
093     *
094     * @param zone
095     *            timezone object
096     */
097    private DateUtil(final DateTimeZone zone) {
098        chrono = ISOChronology.getInstance(zone);
099    }
100
101    /**
102     * Generate a formatted timestamp for the current time.
103     *
104     * @return String containing a timestamp
105     */
106    public String now() {
107        return new DateTime(chrono).toString();
108    }
109
110    /**
111     * Get current time.
112     * @return get current time as a DateTime object
113     */
114    public DateTime currentDateTime() {
115        return new DateTime(chrono);
116    }
117
118    /**
119     * Formats a given DateTime into a timestamp.
120     *
121     * @param date
122     *            DateTime object to convert
123     * @return String containing the formatted timestamp
124     */
125    public String formatDateTime(final DateTime date) {
126        return date.withChronology(chrono).toString();
127    }
128
129    /**
130     * Formats a given date into a timestamp.
131     *
132     * @param date
133     *            date object to convert
134     * @return String containing the formatted timestamp
135     */
136    public String formatDateTime(final Date date) {
137        final DateTime dt = new DateTime(date, chrono);
138        return dt.toString();
139    }
140
141    /**
142     * Formats a given timestamp into a ISO8601 timestamp.
143     *
144     * @param timestamp
145     *            the timestamp to convert
146     * @return String containing the formatted timestamp
147     */
148    public String formatDateTime(final long timestamp) {
149        final DateTime dt = new DateTime(timestamp, chrono);
150        return dt.toString();
151    }
152
153    /**
154     * Parses an ISO8601 compliant timestamp into a DateTime object.
155     *
156     * @param timestamp
157     *            timestamp to parse
158     * @return DateTime using the zone and chronology indicated by the timestamp
159     */
160    public DateTime parseTimestamp(final String timestamp) {
161        final DateTimeFormatter parser = ISODateTimeFormat.dateTime();
162        return parser.withOffsetParsed().parseDateTime(timestamp);
163    }
164
165    /**
166     * Parses an ISO8601 compliant timestamp into a DateTime object.
167     *
168     * Checks the length of the timestamp and returns null if the string is not
169     * ISO8601 compliant Accepted formats:
170     *
171     * <pre>
172     * yyyy-MM-ddTHH:mm:ss.SSSZ,
173     * yyyy-MM-ddTHH:mm:ss.SSS+00,
174     * yyyy-MM-ddTHH:mm:ss.SSS+00:00
175     * </pre>
176     *
177     * @param timestamp
178     *            timestamp to parse
179     * @return the timestamp as a DateTime object
180     */
181    public DateTime parseIfDate(final String timestamp) {
182        DateTime d = null;
183        if (timestamp.length() > 23 && timestamp.length() < 30) {
184            try {
185                d = parseTimestamp(timestamp);
186            } catch (IllegalArgumentException e) {
187                /* ignore */
188            }
189        }
190        return d;
191    }
192
193    /**
194     * return the number of days between the two dates.
195     *
196     * @param start
197     *            Start date
198     * @param end
199     *            End date
200     * @param includeDay
201     *            include both Days (increase the result with one)
202     * @return number of days
203     */
204    public static int getDateDifferenceInDays(final Date start, final Date end, final Boolean includeDay) {
205        Integer result = null;
206        if (start != null && end != null) {
207            final Long l = 86400000L;
208            final Long r = (end.getTime() - start.getTime()) / l;
209            result = r.intValue();
210            if (includeDay) {
211                result++;
212            }
213        }
214        return result;
215    }
216}
217