org.apache.streams.data.util.RFC3339Utils.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.streams.data.util.RFC3339Utils.java

Source

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.streams.data.util;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.DateTimeFormatterBuilder;
import org.joda.time.format.DateTimeParser;

import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Parses and formats dates to Joda Time {@link org.joda.time.DateTime} and to RFC3339 compatible Strings
 */
public class RFC3339Utils {

    private static final RFC3339Utils INSTANCE = new RFC3339Utils();

    public static RFC3339Utils getInstance() {
        return INSTANCE;
    }

    private static final String BASE = "^[0-9]{4}\\-[0-9]{2}\\-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}";
    private static final String TZ = "[+-][0-9]{2}:?[0-9]{2}$";
    private static final String SUB_SECOND = "\\.([0-9]*)";
    private static final String UTC = "Z$";

    private static final Pattern MILLIS = Pattern.compile("^[0-9]*$");
    private static final Pattern UTC_STANDARD = Pattern.compile(BASE + UTC);
    private static final Pattern UTC_SUB_SECOND = Pattern.compile(BASE + SUB_SECOND + UTC);
    private static final Pattern LOCAL_STANDARD = Pattern.compile(BASE + TZ);
    private static final Pattern LOCAL_SUB_SECOND = Pattern.compile(BASE + SUB_SECOND + TZ);

    private static final String BASE_FMT = "yyyy-MM-dd'T'HH:mm:ss";
    public static final DateTimeFormatter UTC_STANDARD_FMT = DateTimeFormat.forPattern(BASE_FMT + "'Z'")
            .withZoneUTC();
    public static final DateTimeFormatter UTC_SUB_SECOND_FMT = DateTimeFormat.forPattern(BASE_FMT + ".SSS'Z'")
            .withZoneUTC();
    public static final DateTimeFormatter LOCAL_STANDARD_FMT = DateTimeFormat.forPattern(BASE_FMT + "Z")
            .withZoneUTC();
    public static final DateTimeFormatter LOCAL_SUB_SECOND_FMT = DateTimeFormat.forPattern(BASE_FMT + ".SSSZ")
            .withZoneUTC();

    /**
     * Contains various formats.  All formats should be of international standards when comes to the ordering of the
     * days and month.
     */
    private static final DateTimeFormatter DEFAULT_FORMATTER;
    /**
     * Contains alternative formats that will succeed after failures from the DEFAULT_FORMATTER.
     * i.e. 4/24/2014 will throw an exception on the default formatter because it will assume international date standards
     * However, the date will parse in the ALT_FORMATTER because it contains the US format of MM/dd/yyyy.
     */
    private static final DateTimeFormatter ALT_FORMATTER;

    static {
        DateTimeParser[] parsers = new DateTimeParser[] {
                DateTimeFormat.forPattern("EEE MMM dd HH:mm:ss Z yyyy").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("EEE, dd MMM yyyy HH:mm:ss Z").getParser(),
                DateTimeFormat.forPattern("dd MMMM yyyy HH:mm:ss").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("yyyyMMdd").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("dd-MM-yyyy").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("yyyy-MM-dd").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("yyyy/MM/dd").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("dd MMM yyyy").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("dd MMMM yyyy").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("yyyyMMddHHmm").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("yyyyMMdd HHmm").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("dd-MM-yyyy HH:mm").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("yyyy-MM-dd HH:mm").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("yyyy/MM/dd HH:mm").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("dd MMM yyyy HH:mm").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("dd MMMM yyyy HH:mm").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("yyyyMMddHHmmss").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("yyyyMMdd HHmmss").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("dd-MM-yyyy HH:mm:ss").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("yyyy/MM/dd HH:mm:ss").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("dd MMM yyyy HH:mm:ss").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("HH:mm:ss yyyy/MM/dd").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("HH:mm:ss MM/dd/yyyy").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("HH:mm:ss yyyy-MM-dd").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("HH:mm:ss MM-dd-yyyy").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("dd/MM/yyyy HH:mm").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("dd/MM/yyyy").withZoneUTC().getParser(), UTC_STANDARD_FMT.getParser(),
                UTC_SUB_SECOND_FMT.getParser(), LOCAL_STANDARD_FMT.getParser(), LOCAL_SUB_SECOND_FMT.getParser() };
        DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
        builder.append(null, parsers);
        DEFAULT_FORMATTER = builder.toFormatter().withZoneUTC();

        DateTimeParser[] altParsers = new DateTimeParser[] {
                DateTimeFormat.forPattern("MM-dd-yyyy HH:mm:ss").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("MM/dd/yyyy HH:mm:ss").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("MM/dd/yyyy HH:mm").withZoneUTC().getParser(),
                DateTimeFormat.forPattern("MM/dd/yyyy").withZoneUTC().getParser(), };
        builder = new DateTimeFormatterBuilder();
        builder.append(null, altParsers);
        ALT_FORMATTER = builder.toFormatter().withZoneUTC();
    }

    private RFC3339Utils() {
    }

    public static DateTime parseUTC(String toParse) {
        if (MILLIS.matcher(toParse).matches()) {
            return new DateTime(Long.valueOf(toParse), DateTimeZone.UTC);
        }
        if (UTC_STANDARD.matcher(toParse).matches()) {
            return parseUTC(UTC_STANDARD_FMT, toParse);
        }
        Matcher utc = UTC_SUB_SECOND.matcher(toParse);
        if (utc.matches()) {
            return parseUTC(getSubSecondFormat(utc.group(1), "'Z'"), toParse);
        }
        if (LOCAL_STANDARD.matcher(toParse).matches()) {
            return parseUTC(LOCAL_STANDARD_FMT, toParse);
        }
        Matcher local = LOCAL_SUB_SECOND.matcher(toParse);
        if (local.matches()) {
            return parseUTC(getSubSecondFormat(local.group(1), "Z"), toParse);
        }
        throw new IllegalArgumentException(
                String.format("Failed to parse date %s. Ensure format is RFC3339 Compliant", toParse));
    }

    public static String format(DateTime toFormat) {
        return UTC_SUB_SECOND_FMT.print(toFormat.getMillis());
    }

    public static String format(DateTime toFormat, TimeZone tz) {
        return LOCAL_SUB_SECOND_FMT.withZone(DateTimeZone.forTimeZone(tz)).print(toFormat.getMillis());
    }

    /**
     * Parses arbitrarily formatted Strings representing dates or dates and times to a {@link org.joda.time.DateTime}
     * objects.  It first attempts parse with international standards, assuming the dates are either dd MM yyyy or
     * yyyy MM dd.  If that fails it will try American formats where the month precedes the days of the month.
     * @param dateString abitrarily formatted date or date and time string
     * @return {@link org.joda.time.DateTime} representation of the dateString
     */
    public static DateTime parseToUTC(String dateString) {
        if (MILLIS.matcher(dateString).find()) {
            return new DateTime(Long.parseLong(dateString));
        }
        try {
            return DEFAULT_FORMATTER.parseDateTime(dateString);
        } catch (Exception e) {
            return ALT_FORMATTER.parseDateTime(dateString);
        }
    }

    /**
     * Formats an arbitrarily formatted into RFC3339 Specifications.
     * @param dateString date string to be formatted
     * @return RFC3339 compliant date string
     */
    public static String format(String dateString) {
        return format(parseToUTC(dateString));
    }

    private static DateTime parseUTC(DateTimeFormatter formatter, String toParse) {
        return formatter.parseDateTime(toParse);
    }

    private static DateTimeFormatter getSubSecondFormat(String sub, String suffix) {
        DateTimeFormatter result;
        //Since RFC3339 allows for any number of sub-second notations, we need to flexibly support more or less than 3
        //digits; however, if it is exactly 3, just use the standards.
        if (sub.length() == 3) {
            result = suffix.equals("Z") ? LOCAL_SUB_SECOND_FMT : UTC_SUB_SECOND_FMT;
        } else {
            StringBuilder pattern = new StringBuilder();
            pattern.append(BASE_FMT);
            pattern.append(".");
            for (int i = 0; i < sub.length(); i++) {
                pattern.append("S");
            }
            pattern.append(suffix);
            result = DateTimeFormat.forPattern(pattern.toString()).withZoneUTC();
        }
        return result;
    }
}