/* * ModeShape (http://www.modeshape.org) * * 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 org.modeshape.common.util; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; import java.time.format.SignStyle; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; /** * Utility for interacting with various date and time objects. * * @author Horia Chiorean (hchiorea@redhat.com) */ public final class DateTimeUtil { /** * UTC zone id */ public static final ZoneId UTC = ZoneId.of("UTC"); /** * ISO 8601 formatter which attempts to be as close to the previous behavior (JODA) as possible. JDK 8 has some * significant differences especially when it comes to milliseconds, which it doesn't support out-of-the-box. * * However, because of this bug in JDK 8: * * - https://bugs.openjdk.java.net/browse/JDK-8031085 this expression * - http://bugs.java.com/view_bug.do?bug_id=8032491 * * is WAY MORE COMPLICATED than it should be (in reality is should use the .SSS pattern) */ private static DateTimeFormatter JODA_ISO8601_FORMATTER = new DateTimeFormatterBuilder() .parseLenient() .appendPattern("uuuu-MM-dd['T'HH:mm:ss][.") .appendValue(ChronoField.MILLI_OF_SECOND, 3, 3, SignStyle.NEVER).optionalEnd() .appendPattern("[XXXXX]") .toFormatter(); private DateTimeUtil() { } /** * Creates a {@link ZonedDateTime} instance based on the given pattern in ISO 8601 format, compatible with the Joda date-time * library. * <p> * Note that there is no direct correspondence between the JODA-style dates and the new JDK 8 date, especially * when it comes to handling milliseconds. * </p> * * @param iso8601 a {@link String} representing a date and/or time pattern, may not be null * @return a {@link ZonedDateTime} instance, never {@code null} * * @throws java.time.format.DateTimeParseException if the given pattern cannot be parsed */ public static ZonedDateTime jodaParse( String iso8601 ) throws DateTimeParseException { CheckArg.isNotNull(iso8601, "iso8601"); TemporalAccessor parse = JODA_ISO8601_FORMATTER.parse(iso8601); LocalDate localDate = LocalDate.from(parse); LocalTime localTime = parse.isSupported(ChronoField.HOUR_OF_DAY) ? LocalTime.from(parse) : LocalTime.MIDNIGHT; ZoneId zoneId = parse.isSupported(ChronoField.OFFSET_SECONDS) ? ZoneId.from(parse) : UTC; return ZonedDateTime.of(localDate, localTime, zoneId); } /** * Returns the ISO8601 string of a given date-time instance with timezone information, trying to be as closed as possible * to what the JODA date-time library would return. * * @param dateTime a {@link ZonedDateTime} instance, may not be null * @return a {@link String} representation of the date instance according to the ISO8601 standard */ public static String jodaFormat( ZonedDateTime dateTime ) { CheckArg.isNotNull(dateTime, "dateTime"); return dateTime.format(JODA_ISO8601_FORMATTER); } /** * Creates a new UTC {@link LocalDateTime} instance based on the given millis value * * @param millis a positive amount of millis * @return a {@link LocalDateTime} instance. */ public static LocalDateTime localDateTimeUTC( long millis ) { CheckArg.isPositive(millis, "millis"); return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), UTC); } }