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}