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