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