All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.gsonfire.util.RFC3339DateFormat Maven / Gradle / Ivy

package io.gsonfire.util;

import java.text.*;
import java.util.Date;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author julio
 */
public final class RFC3339DateFormat extends DateFormat {

    private static final Pattern TIMEZONE_PATTERN = Pattern.compile("(.*)([+-][0-9][0-9])\\:?([0-9][0-9])$");
    private static final Pattern MILLISECONDS_PATTERN = Pattern.compile("(.*)\\.([0-9]+)(.*)");
    private static final Pattern DATE_ONLY_PATTERN = Pattern.compile("^[0-9]{1,4}-[0-1][0-9]-[0-3][0-9]$");

    private final SimpleDateFormat rfc3339Parser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
    private final SimpleDateFormat rfc3339Formatter;
    private final boolean serializeTime;
    private final boolean threeDigitMillis;
    
    public RFC3339DateFormat(TimeZone serializationTimezone, boolean serializeTime, boolean threeDigitMillis) {
        if(serializeTime) {
            this.rfc3339Formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
        } else {
            this.rfc3339Formatter = new SimpleDateFormat("yyyy-MM-dd");
        }
        this.serializeTime = serializeTime;
        this.threeDigitMillis = threeDigitMillis;
        this.rfc3339Formatter.setTimeZone(serializationTimezone);
    }
    
    public RFC3339DateFormat(TimeZone serializationTimezone, boolean serializeTime) {
        this(serializationTimezone, serializeTime, false);
    }

    public RFC3339DateFormat(TimeZone serializationTimezone) {
        this(serializationTimezone, true, false);
    }
    
    public RFC3339DateFormat(boolean serializeTime, boolean threeDigitMillis) {
        this(TimeZone.getTimeZone("UTC"), serializeTime, threeDigitMillis);
    }

    public RFC3339DateFormat(boolean serializeTime) {
        this(serializeTime, false);
    }

    public RFC3339DateFormat() {
        this(true);
    }

    private String generateTimezone(long time, TimeZone serializationTimezone){
        if(serializationTimezone.getOffset(time) == 0){
            return "Z";
        }

        int offset = (int) (serializationTimezone.getOffset(time) / 1000L);
        int hours = offset / 3600;
        int minutes = Math.abs((offset - hours * 3600) / 60);
        String sign = hours >= 0 ? "+" : "-";

        return sign + String.format("%02d", Math.abs(hours)) + ":" + String.format("%02d", minutes);
    }

    @Override
    public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {

        StringBuffer formatted = new StringBuffer();

        formatted.append(rfc3339Formatter.format(date).toString());

        if(this.serializeTime) {
            //Add milliseconds
            long time = date.getTime();
            long millis = time % 1000L;
            
            String fraction = String.format("%03d", millis);
            
            if (!threeDigitMillis) {
                fraction = fraction.replaceAll("0*$", "");
            }
            
            if (!fraction.isEmpty()) {
                formatted.append("." + fraction);
            }

            //Timezone
            String timezoneStr = generateTimezone(time, this.rfc3339Formatter.getTimeZone());
            formatted.append(timezoneStr);
        }
        return formatted;
    }

    @Override
    public Date parse(String source, ParsePosition pos) {
        //Check if this is only a date
        if(DATE_ONLY_PATTERN.matcher(source).matches()) {
            source += "T00:00:00-0000";
        } else {
            if(source.charAt(10) == 't') {
                source = source.substring(0, 10) + "T" + source.substring(12);
            }
        }

        //Filter milliseconds
        int millis = 0;
        if(source.contains(".")){
            Matcher matcher = MILLISECONDS_PATTERN.matcher(source);
            String millisStr = matcher.replaceAll("$2");
            millis = (int) Math.round(Double.parseDouble("0." + millisStr) * 1000);
            source = matcher.replaceAll("$1") + matcher.replaceAll("$3");
        }

        //Filter ending in Z
        if(source.endsWith("Z") || source.endsWith("z")){
            source = source.substring(0, source.length() -1) + "-0000";
        } else {
            //Check if we have timezone information present
            Matcher matcher = TIMEZONE_PATTERN.matcher(source);
            if (matcher.matches()) {
                //Filter colon in timezone
                source = matcher.replaceAll("$1") + matcher.replaceAll("$2") + matcher.replaceAll("$3");
            } else {
                //It appears we don't have any timezone info or Z at the end of the date
                //We will assume it is RFC3339
                source += "-0000";
            }
        }

        try {
            Date res = rfc3339Parser.parse(source);
            if(millis > 0){
                res = new Date(res.getTime() + millis);
            }
            pos.setIndex(source.length());
            return res;
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy