001package com.identityworksllc.iiq.common.plugin.vo; 002 003import com.fasterxml.jackson.annotation.JsonAutoDetect; 004import com.fasterxml.jackson.annotation.JsonPropertyDescription; 005 006import java.text.SimpleDateFormat; 007import java.time.*; 008import java.time.format.DateTimeFormatter; 009import java.time.format.TextStyle; 010import java.util.Date; 011import java.util.Locale; 012import java.util.Objects; 013import java.util.StringJoiner; 014import java.util.TimeZone; 015 016/** 017 * A VO class to wrap a date in a known format, allowing clients to 018 * consume it however they wish. 019 */ 020@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) 021public class ExpandedDate { 022 @JsonPropertyDescription("The date, rendered as an ISO8601 UTC date or date-time string") 023 private final String date; 024 025 @JsonPropertyDescription("The server time zone ID, as returned by Java's ZoneId class") 026 private final String serverTimeZoneId; 027 @JsonPropertyDescription("The server's 'short' time zone name") 028 private final String serverTimeZoneName; 029 @JsonPropertyDescription("The timestamp in Unix epoch milliseconds") 030 private final long timestamp; 031 032 /** 033 * Constructs a new ExpandedDate from the given LocalDate. In this case, the formatted 034 * date will be in the ISO8601 local date standard of yyyy-MM-dd. The timestamp will 035 * be set to local midnight in the system time zone. 036 * 037 * @param in The date to convert 038 */ 039 public ExpandedDate(LocalDate in) { 040 Objects.requireNonNull(in, "Cannot expand a null Date"); 041 042 DateTimeFormatter dateFormat = DateTimeFormatter.ISO_LOCAL_DATE; 043 044 this.timestamp = in.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); 045 this.date = in.format(dateFormat); 046 this.serverTimeZoneId = ZoneId.systemDefault().getId(); 047 this.serverTimeZoneName = ZoneId.systemDefault().getDisplayName(TextStyle.SHORT, Locale.US); 048 } 049 050 /** 051 * Constructs a new ExpandedDate by converting the given ZonedDateTime to an epoch instant. 052 * @param date The date to convert 053 */ 054 public ExpandedDate(ZonedDateTime date) { 055 this(Date.from(date.toInstant())); 056 } 057 058 /** 059 * Constructs a new ExpandedDate by converting the given LocalDateTime to an epoch instant 060 * in the system time zone. 061 * 062 * @param date The local date time 063 */ 064 public ExpandedDate(LocalDateTime date) { 065 this(Date.from(date.atZone(ZoneId.systemDefault()).toInstant())); 066 } 067 068 /** 069 * Constructs a new ExpandedDate by treating the Instant as a Date with millisecond 070 * precision. This is technically a loss of precision, as Instant values are more 071 * precise than Date values. 072 * 073 * @param instant The instant to convert to an ExpandedDate 074 */ 075 public ExpandedDate(Instant instant) { 076 this(Date.from(instant)); 077 } 078 079 /** 080 * Constructs a new ExpandedDate from the given input date 081 * @param in The input date, not null 082 */ 083 public ExpandedDate(Date in) { 084 Objects.requireNonNull(in, "Cannot expand a null Date"); 085 086 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); 087 dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 088 089 this.timestamp = in.getTime(); 090 this.date = dateFormat.format(in); 091 this.serverTimeZoneId = ZoneId.systemDefault().getId(); 092 this.serverTimeZoneName = ZoneId.systemDefault().getDisplayName(TextStyle.SHORT, Locale.US); 093 } 094 095 /** 096 * @return the input date as an ISO8601 timestamp 097 */ 098 public String getDate() { 099 return date; 100 } 101 102 /** 103 * @return the server time zone ID according to {@link ZoneId#getId()} 104 */ 105 public String getServerTimeZoneId() { 106 return serverTimeZoneId; 107 } 108 109 /** 110 * @return the server time zone ID according to {@link ZoneId#getDisplayName(TextStyle, Locale)}}, with a text style of {@link TextStyle#SHORT} and a locale of {@link Locale#US}. 111 */ 112 public String getServerTimeZoneName() { 113 return serverTimeZoneName; 114 } 115 116 /** 117 * @return the timestamp as a millisecond Unix epoch offset 118 */ 119 public long getTimestamp() { 120 return timestamp; 121 } 122 123 @Override 124 public String toString() { 125 StringJoiner joiner = new StringJoiner(", ", ExpandedDate.class.getSimpleName() + "[", "]"); 126 if ((date) != null) { 127 joiner.add("date='" + date + "'"); 128 } 129 if ((serverTimeZoneId) != null) { 130 joiner.add("serverTimeZoneId='" + serverTimeZoneId + "'"); 131 } 132 if ((serverTimeZoneName) != null) { 133 joiner.add("serverTimeZoneName='" + serverTimeZoneName + "'"); 134 } 135 joiner.add("timestamp=" + timestamp); 136 return joiner.toString(); 137 } 138}