001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.modeshape.common.util; 017 018import java.time.Instant; 019import java.time.LocalDate; 020import java.time.LocalDateTime; 021import java.time.LocalTime; 022import java.time.ZoneId; 023import java.time.ZonedDateTime; 024import java.time.format.DateTimeFormatter; 025import java.time.format.DateTimeFormatterBuilder; 026import java.time.format.DateTimeParseException; 027import java.time.format.SignStyle; 028import java.time.temporal.ChronoField; 029import java.time.temporal.TemporalAccessor; 030 031/** 032 * Utility for interacting with various date and time objects. 033 * 034 * @author Horia Chiorean (hchiorea@redhat.com) 035 */ 036public final class DateTimeUtil { 037 038 /** 039 * UTC zone id 040 */ 041 public static final ZoneId UTC = ZoneId.of("UTC"); 042 043 /** 044 * ISO 8601 formatter which attempts to be as close to the previous behavior (JODA) as possible. JDK 8 has some 045 * significant differences especially when it comes to milliseconds, which it doesn't support out-of-the-box. 046 * 047 * However, because of this bug in JDK 8: 048 * 049 * - https://bugs.openjdk.java.net/browse/JDK-8031085 this expression 050 * - http://bugs.java.com/view_bug.do?bug_id=8032491 051 * 052 * is WAY MORE COMPLICATED than it should be (in reality is should use the .SSS pattern) 053 */ 054 private static DateTimeFormatter JODA_ISO8601_FORMATTER = new DateTimeFormatterBuilder() 055 .parseLenient() 056 .appendPattern("uuuu-MM-dd['T'HH:mm:ss][.") 057 .appendValue(ChronoField.MILLI_OF_SECOND, 3, 3, SignStyle.NEVER).optionalEnd() 058 .appendPattern("[XXXXX]") 059 .toFormatter(); 060 061 private DateTimeUtil() { 062 } 063 064 /** 065 * Creates a {@link ZonedDateTime} instance based on the given pattern in ISO 8601 format, compatible with the Joda date-time 066 * library. 067 * <p> 068 * Note that there is no direct correspondence between the JODA-style dates and the new JDK 8 date, especially 069 * when it comes to handling milliseconds. 070 * </p> 071 * 072 * @param iso8601 a {@link String} representing a date and/or time pattern, may not be null 073 * @return a {@link ZonedDateTime} instance, never {@code null} 074 * 075 * @throws java.time.format.DateTimeParseException if the given pattern cannot be parsed 076 */ 077 public static ZonedDateTime jodaParse( String iso8601 ) throws DateTimeParseException { 078 CheckArg.isNotNull(iso8601, "iso8601"); 079 TemporalAccessor parse = JODA_ISO8601_FORMATTER.parse(iso8601); 080 LocalDate localDate = LocalDate.from(parse); 081 LocalTime localTime = parse.isSupported(ChronoField.HOUR_OF_DAY) ? LocalTime.from(parse) : LocalTime.MIDNIGHT; 082 ZoneId zoneId = parse.isSupported(ChronoField.OFFSET_SECONDS) ? ZoneId.from(parse) : UTC; 083 return ZonedDateTime.of(localDate, localTime, zoneId); 084 } 085 086 /** 087 * Returns the ISO8601 string of a given date-time instance with timezone information, trying to be as closed as possible 088 * to what the JODA date-time library would return. 089 * 090 * @param dateTime a {@link ZonedDateTime} instance, may not be null 091 * @return a {@link String} representation of the date instance according to the ISO8601 standard 092 */ 093 public static String jodaFormat( ZonedDateTime dateTime ) { 094 CheckArg.isNotNull(dateTime, "dateTime"); 095 return dateTime.format(JODA_ISO8601_FORMATTER); 096 } 097 098 /** 099 * Creates a new UTC {@link LocalDateTime} instance based on the given millis value 100 * 101 * @param millis a positive amount of millis 102 * @return a {@link LocalDateTime} instance. 103 */ 104 public static LocalDateTime localDateTimeUTC( long millis ) { 105 CheckArg.isPositive(millis, "millis"); 106 return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), UTC); 107 } 108}