de.huxhorn.sulky.stax.DateTimeFormatter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of de.huxhorn.sulky.stax Show documentation
Show all versions of de.huxhorn.sulky.stax Show documentation
This file is part of the sulky modules. It contains helper methods to simplify usage of StAX.
The newest version!
/*
* sulky-modules - several general-purpose modules.
* Copyright (C) 2007-2021 Joern Huxhorn
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
/*
* Copyright 2007-2021 Joern Huxhorn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 de.huxhorn.sulky.stax;
import java.text.ParseException;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.util.Date;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DateTimeFormatter
{
private static final String TIMEZONE_DATE_FORMAT_PATTERN = ".*([+-]\\d{2})(\\d{2})$";
private static final int TIMEZONE_DATE_FORMAT_LENGTH = 5;
private static final java.time.format.DateTimeFormatter ISO_DATE_TIME_PARSER =
new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral('T')
.appendValue(ChronoField.HOUR_OF_DAY, 2)
.appendLiteral(':')
.appendValue(ChronoField.MINUTE_OF_HOUR, 2)
.appendLiteral(':')
.appendValue(ChronoField.SECOND_OF_MINUTE, 2)
.optionalStart()
.appendFraction(ChronoField.MILLI_OF_SECOND, 3, 3, true)
.optionalEnd()
.appendOffset("+HH:MM", "Z")
.toFormatter(Locale.US)
.withZone(ZoneOffset.UTC);
private static final java.time.format.DateTimeFormatter ISO_DATE_TIME_FORMATTER_WITH_MILLIS =
new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral('T')
.appendValue(ChronoField.HOUR_OF_DAY, 2)
.appendLiteral(':')
.appendValue(ChronoField.MINUTE_OF_HOUR, 2)
.appendLiteral(':')
.appendValue(ChronoField.SECOND_OF_MINUTE, 2)
.appendFraction(ChronoField.MILLI_OF_SECOND, 3, 3, true)
.appendOffset("+HH:MM", "+00:00")
.toFormatter(Locale.US)
.withZone(ZoneOffset.UTC);
private static final java.time.format.DateTimeFormatter ISO_DATE_TIME_FORMATTER_WITHOUT_MILLIS =
new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(java.time.format.DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral('T')
.appendValue(ChronoField.HOUR_OF_DAY, 2)
.appendLiteral(':')
.appendValue(ChronoField.MINUTE_OF_HOUR, 2)
.appendLiteral(':')
.appendValue(ChronoField.SECOND_OF_MINUTE, 2)
.appendOffset("+HH:MM", "+00:00")
.toFormatter(Locale.US)
.withZone(ZoneOffset.UTC);
private final Pattern javaTimezonePattern = Pattern.compile(TIMEZONE_DATE_FORMAT_PATTERN);
private static final boolean IS_JAVA_8;
static {
boolean java8 = true;
String javaVersion = System.getProperty("java.version");
if(javaVersion != null)
{
java8 = javaVersion.startsWith("1.");
}
IS_JAVA_8 = java8;
}
/**
* This method parses a given string containing a dateTime in ISO8601 notation into a date.
*
* It can handle an optional millisecond fraction as well as timezone with either explicit '+/-HH:MM' or 'Z' UTC designator.
*
* @param dateTime a string containing a dateTime in ISO8601 notation.
* @return the parsed date
* @throws ParseException If the dateTime string is invalid.
*/
public Date parse(String dateTime)
throws ParseException
{
Matcher matcher = javaTimezonePattern.matcher(dateTime);
if(matcher.matches())
{
// correct +/-hhmm to +/-hh:mm
String hh = matcher.group(1);
String mm = matcher.group(2);
dateTime = dateTime.substring(0, dateTime.length() - TIMEZONE_DATE_FORMAT_LENGTH) + hh + ":" + mm;
}
TemporalAccessor temporal = ISO_DATE_TIME_PARSER.parse(dateTime);
long seconds = temporal.getLong(ChronoField.INSTANT_SECONDS);
if(IS_JAVA_8)
{
// http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8183913
seconds = seconds - temporal.getLong(ChronoField.OFFSET_SECONDS);
}
long millis = seconds * 1000 + temporal.getLong(ChronoField.MILLI_OF_SECOND);
return new Date(millis);
}
/**
* Returns a simplified ISO8601 datetime string in UTC.
*
* It will always contain a three-number millisecond field regardless if it is "needed"
* (i.e. MILLI_OF_SECOND != 0) or not. The timezone of the date is always UTC but isn't using
* the UTC designator 'Z'. Instead, it's using an explicit '+00:00'.
* That way a date formatted by this method will always have the same number of characters while creating output
* that less intelligent date-parsing frameworks (incapable of the 'Z' notation) are still able to process.
*
* @param date the date to be formatted.
* @return a simplified ISO8601 datetime string in UTC.
*/
public String format(Date date)
{
return this.format(date, true);
}
/**
* Returns a simplified ISO8601 datetime string in UTC.
*
* It will always contain a three-number millisecond field regardless if it is "needed"
* (i.e. MILLI_OF_SECOND != 0) or not if withMillis is true. The timezone of the date is always UTC but isn't using
* the UTC designator 'Z'. Instead, it's using an explicit '+00:00'.
* That way a date formatted by this method will always have the same number of characters while creating output
* that less intelligent date-parsing frameworks (incapable of the 'Z' notation) are still able to process.
*
* @param date the date to be formatted.
* @param withMillis whether or not milliseconds should be printed.
* @return a simplified ISO8601 datetime string in UTC.
*/
public String format(Date date, boolean withMillis)
{
Instant instant = Instant.ofEpochMilli(date.getTime());
ZonedDateTime zoned = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);
if(withMillis)
{
return ISO_DATE_TIME_FORMATTER_WITH_MILLIS.format(zoned);
}
return ISO_DATE_TIME_FORMATTER_WITHOUT_MILLIS.format(zoned);
}
}