001/* 002 * Licensed under the Apache License, Version 2.0 (the "License"); 003 * you may not use this file except in compliance with the License. 004 * You may obtain a copy of the License at 005 * 006 * http://www.apache.org/licenses/LICENSE-2.0 007 * 008 * Unless required by applicable law or agreed to in writing, software 009 * distributed under the License is distributed on an "AS IS" BASIS, 010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 011 * See the License for the specific language governing permissions and 012 * limitations under the License. 013 */ 014package org.gbif.common.parsers.date; 015 016import org.gbif.utils.PreconditionUtils; 017 018import java.time.format.DateTimeFormatter; 019import java.time.format.DateTimeParseException; 020import java.time.temporal.TemporalAccessor; 021import java.time.temporal.TemporalQuery; 022import java.util.Objects; 023 024import javax.annotation.Nullable; 025import javax.validation.constraints.NotNull; 026 027/** 028 * Adds some flexibility around {@link DateTimeFormatter} with the {@link DateTimeSeparatorNormalizer} and 029 * simple optimization the support of DateComponentOrdering. 030 * <p> 031 * This class is thread-safe once an instance is created. 032 */ 033public class DateTimeParser { 034 035 private final DateTimeFormatter formatter; 036 private final DateTimeSeparatorNormalizer normalizer; 037 private final DateComponentOrdering ordering; 038 039 private final TemporalQuery<?>[] types; 040 private final int minLength; 041 042 /** 043 * Package protected constructor. 044 * Use {@link DateTimeParserBuilder} 045 */ 046 DateTimeParser(@NotNull DateTimeFormatter formatter, @Nullable DateTimeSeparatorNormalizer normalizer, 047 @NotNull DateComponentOrdering ordering, TemporalQuery<?>[] type, int minLength) { 048 049 Objects.requireNonNull(formatter, "DateTimeFormatter can not be null"); 050 Objects.requireNonNull(ordering, "DateComponentOrdering can not be null"); 051 Objects.requireNonNull(type, "TemporalQuery can not be null"); 052 PreconditionUtils.checkArgument(minLength > 0, "minLength must be greater than 0"); 053 054 this.formatter = formatter; 055 this.ordering = ordering; 056 this.normalizer = normalizer; 057 this.minLength = minLength; 058 this.types = type; 059 } 060 061 public DateComponentOrdering getOrdering() { 062 return ordering; 063 } 064 065 /** 066 * Parses the provided String as a TemporalAccessor if possible, otherwise returns null. 067 * <p> 068 * This function fully support partial dates and will return the best possible date resolution based 069 * on the {@link DateComponentOrdering} provided. 070 * <p> 071 * This function will not throw DateTimeParseException but returns null in case the input 072 * can not be parsed. 073 * 074 * @return TemporalAccessor or null in case the input can not be parsed. 075 */ 076 public TemporalAccessor parse(String input) { 077 078 // return fast if minimum length is not meet 079 if (input.length() < minLength) { 080 return null; 081 } 082 083 if (normalizer != null) { 084 input = normalizer.normalize(input); 085 } 086 087 try { 088 if (types.length > 1) { 089 return formatter.parseBest(input, types); 090 } 091 return (TemporalAccessor) formatter.parse(input, types[0]); 092 } catch (DateTimeParseException dpe) { 093 } 094 return null; 095 } 096 097}