001package net.filebot.web; 002 003import static net.filebot.Logging.*; 004 005import java.io.Serializable; 006import java.time.Instant; 007import java.time.LocalDate; 008import java.time.ZoneOffset; 009import java.time.ZonedDateTime; 010import java.time.chrono.ChronoLocalDate; 011import java.time.format.DateTimeFormatter; 012import java.time.temporal.ChronoUnit; 013import java.util.Locale; 014import java.util.Objects; 015import java.util.regex.Matcher; 016import java.util.regex.Pattern; 017 018public class SimpleDate implements Serializable, Comparable<Object> { 019 020 protected int year; 021 protected int month; 022 protected int day; 023 024 public SimpleDate() { 025 // used by deserializer 026 } 027 028 public SimpleDate(int year, int month, int day) { 029 this.year = year; 030 this.month = month; 031 this.day = day; 032 } 033 034 public SimpleDate(LocalDate date) { 035 this.year = date.getYear(); 036 this.month = date.getMonthValue(); 037 this.day = date.getDayOfMonth(); 038 } 039 040 public SimpleDate(int year) { 041 this(year, 1, 1); 042 } 043 044 public int getYear() { 045 return year; 046 } 047 048 public int getMonth() { 049 return month; 050 } 051 052 public int getDay() { 053 return day; 054 } 055 056 @Override 057 public boolean equals(Object obj) { 058 if (obj instanceof SimpleDate) { 059 SimpleDate other = (SimpleDate) obj; 060 return year == other.year && month == other.month && day == other.day; 061 } else if (obj instanceof CharSequence) { 062 return toString().equals(obj.toString()); 063 } 064 return super.equals(obj); 065 } 066 067 @Override 068 public int compareTo(Object other) { 069 if (other instanceof SimpleDate) { 070 return compareTo((SimpleDate) other); 071 } 072 if (other instanceof ChronoLocalDate) { 073 return compareTo((ChronoLocalDate) other); 074 } 075 if (other instanceof CharSequence) { 076 SimpleDate otherDate = parse(other.toString()); 077 if (otherDate != null) { 078 return compareTo(otherDate); 079 } 080 } 081 throw new IllegalArgumentException("Bad Date: " + other); 082 } 083 084 public int compareTo(SimpleDate other) { 085 return toLocalDate().compareTo(other.toLocalDate()); 086 } 087 088 public int compareTo(ChronoLocalDate other) { 089 return toLocalDate().compareTo(other); 090 } 091 092 public int compareTo(Instant other) { 093 return toInstant().compareTo(other); 094 } 095 096 public SimpleDate plus(int days) { 097 return new SimpleDate(toLocalDate().plusDays(days)); 098 } 099 100 public SimpleDate minus(int days) { 101 return new SimpleDate(toLocalDate().minusDays(days)); 102 } 103 104 public long minus(SimpleDate other) { 105 return getEpochDay() - other.getEpochDay(); 106 } 107 108 @Override 109 public int hashCode() { 110 return Objects.hash(year, month, day); 111 } 112 113 @Override 114 public SimpleDate clone() { 115 return new SimpleDate(year, month, day); 116 } 117 118 public String format(String pattern) { 119 return format(pattern, Locale.US); 120 } 121 122 public String format(String pattern, Locale locale) { 123 return DateTimeFormatter.ofPattern(pattern, locale).format(toLocalDate()); 124 } 125 126 public LocalDate toLocalDate() { 127 try { 128 return LocalDate.of(year, month, day); 129 } catch (Exception e) { 130 throw new IllegalStateException("Bad Date: " + this, e); 131 } 132 } 133 134 public Instant toInstant() { 135 return toLocalDate().atStartOfDay(ZoneOffset.UTC).toInstant(); 136 } 137 138 public long getEpochDay() { 139 return ChronoUnit.DAYS.between(Instant.EPOCH, toInstant()); 140 } 141 142 public long getTimeStamp() { 143 return toInstant().toEpochMilli(); 144 } 145 146 @Override 147 public String toString() { 148 return String.format(Locale.ROOT, "%04d-%02d-%02d", year, month, day); 149 } 150 151 public static SimpleDate now() { 152 return new SimpleDate(LocalDate.now()); 153 } 154 155 public static SimpleDate from(Instant instant) { 156 return instant == null ? null : new SimpleDate(instant.atOffset(ZoneOffset.UTC).toLocalDate()); 157 } 158 159 public static SimpleDate from(ZonedDateTime date) { 160 return date == null ? null : new SimpleDate(date.toLocalDate()); 161 } 162 163 public static SimpleDate of(int year, int month, int day) { 164 return new SimpleDate(LocalDate.of(year, month, day)); 165 } 166 167 public static SimpleDate parse(String date) { 168 if (date == null || date.isEmpty()) { 169 return null; 170 } 171 172 Matcher m = DATE_FORMAT.matcher(date); 173 if (m.matches()) { 174 int year = Integer.parseInt(m.group(1)); 175 int month = Integer.parseInt(m.group(2)); 176 int day = Integer.parseInt(m.group(3)); 177 178 try { 179 return new SimpleDate(LocalDate.of(year, month, day)); 180 } catch (Exception e) { 181 debug.finest(message("Bad Date", date)); 182 } 183 } 184 185 return null; 186 } 187 188 public static final Pattern DATE_FORMAT = Pattern.compile("(\\d{4})\\D(\\d{1,2})\\D(\\d{1,2})"); 189 190}