org.cesecore.util.ValidityDate.java Source code

Java tutorial

Introduction

Here is the source code for org.cesecore.util.ValidityDate.java

Source

/*************************************************************************
 *                                                                       *
 *  CESeCore: CE Security Core                                           *
 *                                                                       *
 *  This software is free software; you can redistribute it and/or       *
 *  modify it under the terms of the GNU Lesser General Public           *
 *  License as published by the Free Software Foundation; either         *
 *  version 2.1 of the License, or any later version.                    *
 *                                                                       *
 *  See terms of license at gnu.org.                                     *
 *                                                                       *
 *************************************************************************/

package org.cesecore.util;

import java.text.ParseException;
import java.util.Date;
import java.util.TimeZone;

import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.lang.time.FastDateFormat;
import org.apache.log4j.Logger;

/**
 * Class for encoding and decoding certificate validity and end date.
 * 
 * @version $Id: ValidityDate.java 18778 2014-04-09 14:31:14Z samuellb $
 */
public class ValidityDate {
    /** The date and time format defined in ISO8601. The 'T' can be omitted (and we do to save some parsing cycles). */
    public static final String ISO8601_DATE_FORMAT = "yyyy-MM-dd HH:mm:ssZZ";
    public static final TimeZone TIMEZONE_UTC = TimeZone.getTimeZone("UTC");
    public static final TimeZone TIMEZONE_SERVER = TimeZone.getDefault();

    private static final Logger log = Logger.getLogger(ValidityDate.class);
    // Time format for storage where implied timezone is UTC
    private static final String[] IMPLIED_UTC_PATTERN = { "yyyy-MM-dd HH:mm" };
    private static final String[] IMPLIED_UTC_PATTERN_TZ = { "yyyy-MM-dd HH:mmZZ" };
    // Time format for human interactions
    private static final String[] ISO8601_PATTERNS = {
            // These must have timezone on date-only format also, since it has a time also (which is 00:00).
            // If the timezone is omitted then the string "+00:00" is appended to the date before parsing
            ISO8601_DATE_FORMAT, "yyyy-MM-dd HH:mmZZ", "yyyy-MM-ddZZ" };

    // Can't be instantiated
    private ValidityDate() {
    }

    /** Parse a String in the format "yyyy-MM-dd HH:mm" as a date with implied TimeZone UTC. */
    public static Date parseAsUTC(final String dateString) throws ParseException {
        return DateUtils.parseDateStrictly(dateString + "+00:00", IMPLIED_UTC_PATTERN_TZ);
    }

    /** Parse a String in the format "yyyy-MM-dd HH:mm:ssZZ". The hour/minutes, seconds and timezone are optional parts. */
    public static Date parseAsIso8601(final String dateString) throws ParseException {
        try {
            return DateUtils.parseDateStrictly(dateString, ISO8601_PATTERNS);
        } catch (ParseException e) {
            // Try again with timezone. In DateUtils, the default timezone seems to be the server
            // timezone and not UTC, so we can't have date formats without "ZZ".
            return DateUtils.parseDateStrictly(dateString + "+00:00", ISO8601_PATTERNS);
        }
    }

    /** Convert a Date to the format "yyyy-MM-dd HH:mm" with implied TimeZone UTC. */
    public static String formatAsUTC(final Date date) {
        return FastDateFormat.getInstance(IMPLIED_UTC_PATTERN[0], TIMEZONE_UTC).format(date);
    }

    /** Convert a absolute number of milliseconds to the format "yyyy-MM-dd HH:mm" with implied TimeZone UTC. */
    public static String formatAsUTC(final long millis) {
        return FastDateFormat.getInstance(IMPLIED_UTC_PATTERN[0], TIMEZONE_UTC).format(millis);
    }

    /** Convert a Date to the format "yyyy-MM-dd HH:mm:ssZZ" (the T is not required). The server's time zone is used. */
    public static String formatAsISO8601(final Date date, final TimeZone timeZone) {
        return FastDateFormat.getInstance(ISO8601_PATTERNS[0], timeZone).format(date);
    }

    /** Convert a Date in milliseconds to the format "yyyy-MM-dd HH:mm:ssZZ". The server's time zone is used. */
    public static String formatAsISO8601ServerTZ(final long millis, final TimeZone timeZone) {
        return FastDateFormat.getInstance(ISO8601_PATTERNS[0], TIMEZONE_SERVER).format(millis);
    }

    /** Convert a the format "yyyy-MM-dd HH:mm:ssZZ" to "yyyy-MM-dd HH:mm" with implied TimeZone UTC. */
    public static String getImpliedUTCFromISO8601(final String dateString) throws ParseException {
        return formatAsUTC(parseAsIso8601(dateString));
    }

    /** Convert a the format "yyyy-MM-dd HH:mm" with implied TimeZone UTC to "yyyy-MM-dd HH:mm:ssZZ". */
    public static String getISO8601FromImpliedUTC(final String dateString, final TimeZone timeZone)
            throws ParseException {
        return formatAsISO8601(parseAsUTC(dateString), timeZone);
    }

    /**
     * Encoding of the validity for a CA or certificate profile. Either delta time or end date.
     * @param sValidity *y *mo *d or absolute date in the form "yyyy-MM-dd HH:mm:ssZZ"
     * @return delta time in days if h*m*d*; milliseconds since epoch if valid absolute date; -1 if neither
     * @throws IllegalArgumentException if the argument is null
     */
    public static long encode(final String sValidity) {
        long returnValue = -1;
        // Try '*y *mo *d'-format
        final YearMonthDayTime yearMonthDayTime = YearMonthDayTime.getInstance(sValidity);
        if (yearMonthDayTime != null) {
            long days = yearMonthDayTime.daysFrom(new Date());
            if (isDeltaTime(days)) {
                returnValue = days;
            } else {
                returnValue = Long.valueOf(Integer.MAX_VALUE - 1);
                log.info(sValidity + " was parsed as a relative time, but is too far in the future. Limiting to "
                        + returnValue + " days.");
            }
        } else {
            // Try to parse the time in the format yyyy-MM-dd HH:mm:ssZZ
            try {
                returnValue = parseAsIso8601(sValidity).getTime();
            } catch (ParseException e) {
                if (log.isDebugEnabled()) {
                    final Date exampleDate = new Date();
                    log.debug("Not possible to decode the date '" + sValidity + "'. Example: The date '"
                            + exampleDate + "' should be encoded as '" + formatAsUTC(exampleDate) + "'");
                }
            }
        }
        return returnValue;
    }

    /**
     * Decodes encoded value to string in the form "yyyy-MM-dd HH:mm:ssZZ" or "1234d" (relative days).
     * @param lEncoded If this is below Integer.MAX_VALUE it is interpreted as a number of days to firstDate, otherwise an unix timestamp.
     */
    public static String getString(final long lEncoded) {
        if (isDeltaTime(lEncoded)) {
            return "" + lEncoded + YearMonthDayTime.TYPE_DAYS;
        }
        return formatAsISO8601ServerTZ(lEncoded, TIMEZONE_SERVER);
    }

    /**
     * Decodes encoded value to Date.
     * @param lEncoded encoded value. If this is below Integer.MAX_VALUE it is interpreted as a number of days to firstDate, otherwise an unix timestamp.
     * @param firstDate date to be used if encoded value is a delta time. Can never be null.
     */
    public static Date getDate(final long lEncoded, final Date firstDate) {
        if (isDeltaTime(lEncoded)) {
            return new Date(firstDate.getTime() + (lEncoded * 24 * 60 * 60 * 1000));
        }
        return new Date(lEncoded);
    }

    /** If below the integer capacity we have stored a relative date in days, otherwise it is an absolute time in milliseconds. */
    private static boolean isDeltaTime(final long lEncoded) {
        return lEncoded < Integer.MAX_VALUE; // This could probably be <= instead??
    }

    /**
     * Parse a date as either "yyyy-MM-dd HH:mm:ssZZ" or a relative hex encoded UNIX time stamp (in seconds).
     * Use for parsing of the build time property "ca.toolateexpiredate" in ejbca.properties.
     * @return the date or the largest possible Date if unable to parse the argument.
     */
    public static Date parseCaLatestValidDateTime(final String sDate) {
        Date tooLateExpireDate = null;
        if (sDate.length() > 0) {
            //First, try to parse the date in ISO8601 date time format.
            try {
                return parseAsIso8601(sDate);
            } catch (ParseException e) {
                log.debug("tooLateExpireDate could not be parsed as an ISO8601 date: " + e.getMessage());
            }
            // Second, try to parse it as a hexadecimal value (without markers of any kind.. just a raw value).
            if (tooLateExpireDate == null) {
                try {
                    tooLateExpireDate = new Date(Long.parseLong(sDate, 16) * 1000);
                } catch (NumberFormatException e) {
                    log.debug("tooLateExpireDate could not be parsed as a hex value: " + e.getMessage());
                }
            }
        }
        if (tooLateExpireDate == null) {
            log.debug("Using default value for ca.toolateexpiredate.");
            tooLateExpireDate = new Date(Long.MAX_VALUE);
        } else if (log.isDebugEnabled()) {
            log.debug("tooLateExpireData is set to: " + tooLateExpireDate);
        }
        return tooLateExpireDate;
    }
}