001/* 002 * The contents of this file are subject to the license and copyright 003 * detailed in the LICENSE and NOTICE files at the root of the source 004 * tree. 005 */ 006package org.fcrepo.search.impl; 007 008import java.time.Instant; 009import java.time.ZoneOffset; 010import java.time.format.DateTimeFormatter; 011import java.time.format.DateTimeFormatterBuilder; 012import java.time.temporal.ChronoField; 013import java.util.ArrayList; 014import java.util.List; 015 016import static java.time.format.DateTimeFormatter.ISO_DATE_TIME; 017import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME; 018import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME; 019 020/** 021 * A utility class for parsing a variety of different date/time formats into an Instant. 022 * 023 * @author dbernstein 024 */ 025public class InstantParser { 026 027 private static final List<DateTimeFormatter> VALID_DATE_FORMATS = new ArrayList<>(); 028 029 static { 030 VALID_DATE_FORMATS.add(ISO_DATE_TIME); 031 VALID_DATE_FORMATS.add(ISO_OFFSET_DATE_TIME); 032 VALID_DATE_FORMATS.add(RFC_1123_DATE_TIME); 033 final var zoneId = ZoneOffset.UTC; 034 VALID_DATE_FORMATS.add(new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd") 035 .parseDefaulting(ChronoField.NANO_OF_DAY, 0).toFormatter().withZone(zoneId)); 036 VALID_DATE_FORMATS.add(new DateTimeFormatterBuilder().appendPattern("yyyyMMdd") 037 .parseDefaulting(ChronoField.NANO_OF_DAY, 0).toFormatter().withZone(zoneId)); 038 VALID_DATE_FORMATS.add(new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss") 039 .parseDefaulting(ChronoField.NANO_OF_DAY, 0).toFormatter().withZone(zoneId)); 040 VALID_DATE_FORMATS.add(new DateTimeFormatterBuilder().appendPattern("yyyyMMdd HH:mm:ss") 041 .parseDefaulting(ChronoField.NANO_OF_DAY, 0).toFormatter().withZone(zoneId)); 042 } 043 044 private InstantParser() { } 045 046 /** 047 * Parse a datestring into an instant. If timezone or time information is missing, UTC is assumed. 048 * 049 * @param dateString The date string 050 * @return an instant 051 */ 052 public static Instant parse(final String dateString) { 053 for (final DateTimeFormatter formatter : VALID_DATE_FORMATS) { 054 try { 055 final var temporalAccessor = formatter.parse(dateString); 056 return Instant.from(temporalAccessor); 057 } catch (final Exception e) { 058 //ignore failures - if no date string is parsable, an error is thrown below 059 } 060 } 061 throw new IllegalArgumentException("Invalid date format: \"" + dateString + "\""); 062 } 063 064}