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 016 017import java.time.Duration; 018import java.time.temporal.ChronoField; 019import java.time.temporal.TemporalAccessor; 020import java.util.Objects; 021 022import org.apache.commons.lang3.builder.HashCodeBuilder; 023import org.apache.commons.lang3.builder.ToStringBuilder; 024import org.apache.commons.lang3.builder.ToStringStyle; 025 026/** 027 * Still experimental, the class should probably require to have a LocalDate otherwise the output 028 * of getResolution can not be comparable with {@link AtomizedLocalDate} 029 * 030 * AtomizedLocalDateTime is a simple immutable class to hold local date and time data from a 031 * {@link TemporalAccessor}. 032 * 033 * 034 * Thread-Safe, immutable class. 035 */ 036public class AtomizedLocalDateTime { 037 038 private final AtomizedLocalDate localDate; 039 private final Integer hour; 040 private final Integer minute; 041 private final Integer second; 042 private final Integer millisecond; 043 044 private final int resolution; 045 046 047 private AtomizedLocalDateTime(AtomizedLocalDate localDate, Integer hour, Integer minute, 048 Integer second, Integer millisecond) { 049 int res = 0; 050 this.localDate = localDate; 051 this.hour = hour; 052 this.minute = minute; 053 this.second = second; 054 this.millisecond = millisecond; 055 056 // compute resolution 057 if(localDate != null){ 058 res += localDate.getResolution(); 059 } 060 061 if(hour != null){ 062 res ++; 063 } 064 if(minute != null){ 065 res++; 066 } 067 if(second != null){ 068 res++; 069 } 070 if(millisecond != null){ 071 res++; 072 } 073 this.resolution = res; 074 } 075 076 public Integer getYear() { 077 if(localDate == null){ 078 return null; 079 } 080 return localDate.getYear(); 081 } 082 083 public Integer getMonth() { 084 if(localDate == null){ 085 return null; 086 } 087 return localDate.getMonth(); 088 } 089 090 public Integer getDay() { 091 if(localDate == null){ 092 return null; 093 } 094 return localDate.getDay(); 095 } 096 097 public Integer getHour() { 098 return hour; 099 } 100 101 public Integer getMinute() { 102 return minute; 103 } 104 105 public Integer getSecond() { 106 return second; 107 } 108 109 public Integer getMillisecond() { 110 return millisecond; 111 } 112 113 /** 114 * Get the resolution of the {@link AtomizedLocalDateTime}. 115 * Resolution represents the number of parts this date/time contains. 116 * Be aware that it is not possible to express partial time which means that the lowest 117 * resolution possible if 4 (Time with no date) 118 * @return 119 */ 120 public int getResolution(){ 121 return resolution; 122 } 123 124 /** 125 * Build a new instance of {@link AtomizedLocalDateTime} based on a {@link TemporalAccessor}. 126 * This is done by extracting the {@link ChronoField}. 127 * Please note this expected behavior: if a least 1 time component is available on the TemporalAccessor, the other 128 * time components will return 0 instead of null if they are not provided. 129 * 130 * AtomizedLocalDateTime.fromTemporalAccessor(LocalTime.of(HOUR, MINUTE) will return 0 as second and millisecond and 131 * the resolution will be 4. 132 */ 133 public static AtomizedLocalDateTime fromTemporalAccessor(TemporalAccessor temporalAccessor) { 134 if (temporalAccessor == null) { 135 return null; 136 } 137 138 //TODO if localDate == null, we should probably return null. If we want to handle time only 139 //we should create AtomizedLocalTime 140 141 AtomizedLocalDate localDate = AtomizedLocalDate.fromTemporalAccessor(temporalAccessor); 142 143 144 Integer h = null, m = null, s = null, ms = null; 145 if (temporalAccessor.isSupported(ChronoField.HOUR_OF_DAY)) { 146 h = temporalAccessor.get(ChronoField.HOUR_OF_DAY); 147 } 148 149 if (temporalAccessor.isSupported(ChronoField.MINUTE_OF_HOUR)) { 150 m = temporalAccessor.get(ChronoField.MINUTE_OF_HOUR); 151 } 152 153 if (temporalAccessor.isSupported(ChronoField.SECOND_OF_MINUTE)) { 154 s = temporalAccessor.get(ChronoField.SECOND_OF_MINUTE); 155 } 156 157 if (temporalAccessor.isSupported(ChronoField.MILLI_OF_SECOND)) { 158 ms = temporalAccessor.get(ChronoField.MILLI_OF_SECOND); 159 } else if (temporalAccessor.isSupported(ChronoField.NANO_OF_SECOND)) { 160 ms = Long.valueOf(Duration.ofNanos(temporalAccessor.get(ChronoField.NANO_OF_SECOND)).toMillis()).intValue(); 161 } 162 return new AtomizedLocalDateTime(localDate, h, m, s, ms); 163 } 164 165 @Override 166 public int hashCode() { 167 return new HashCodeBuilder().append(localDate).append(hour).append(minute). 168 append(second).append(millisecond).append(resolution).toHashCode(); 169 } 170 171 @Override 172 public boolean equals(Object o) { 173 if (this == o) { 174 return true; 175 } 176 if (!(o instanceof AtomizedLocalDateTime)) { 177 return false; 178 } 179 180 AtomizedLocalDateTime that = (AtomizedLocalDateTime) o; 181 return resolution == that.resolution 182 && Objects.equals(localDate, that.localDate) 183 && Objects.equals(hour, that.hour) 184 && Objects.equals(minute, that.minute) 185 && Objects.equals(second, that.second) 186 && Objects.equals(millisecond, that.millisecond); 187 } 188 189 @Override 190 public String toString() { 191 return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) 192 .append("localDate", localDate) 193 .append("hour", hour) 194 .append("minute", minute) 195 .append("second", second) 196 .append("millisecond", millisecond) 197 .append("resolution", resolution) 198 .toString(); 199 } 200 201}