001/*
002 *  Copyright (c) 2022-2025, Mybatis-Flex (fuhai999@gmail.com).
003 *  <p>
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *  <p>
008 *  http://www.apache.org/licenses/LICENSE-2.0
009 *  <p>
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package com.mybatisflex.core.util;
017
018
019import java.sql.Timestamp;
020import java.text.ParseException;
021import java.text.SimpleDateFormat;
022import java.time.*;
023import java.time.format.DateTimeFormatter;
024import java.util.Date;
025import java.util.HashMap;
026import java.util.Locale;
027import java.util.Map;
028import java.util.concurrent.ConcurrentHashMap;
029
030
031public class DateUtil {
032
033    private DateUtil() {
034    }
035
036    public static String datePatternWithoutDividing = "yyyyMMdd";
037    public static String datePattern = "yyyy-MM-dd";
038    public static final String dateMinutePattern = "yyyy-MM-dd HH:mm";
039    public static final String dateMinutePattern2 = "yyyy-MM-dd'T'HH:mm";
040    public static String datetimePattern = "yyyy-MM-dd HH:mm:ss";
041    public static final String dateMillisecondPattern = "yyyy-MM-dd HH:mm:ss SSS";
042    public static final String dateCSTPattern = "EEE MMM dd HH:mm:ss zzz yyyy";
043
044    private static final ThreadLocal<HashMap<String, SimpleDateFormat>> TL = ThreadLocal.withInitial(HashMap::new);
045
046    private static final Map<String, DateTimeFormatter> dateTimeFormatters = new ConcurrentHashMap<>();
047
048    public static DateTimeFormatter getDateTimeFormatter(String pattern) {
049        DateTimeFormatter ret = dateTimeFormatters.get(pattern);
050        if (ret == null) {
051            ret = DateTimeFormatter.ofPattern(pattern);
052            dateTimeFormatters.put(pattern, ret);
053        }
054        return ret;
055    }
056
057    public static SimpleDateFormat getSimpleDateFormat(String pattern) {
058        SimpleDateFormat ret = TL.get().get(pattern);
059        if (ret == null) {
060            if (dateCSTPattern.equals(pattern)) {
061                ret = new SimpleDateFormat(dateCSTPattern, Locale.US);
062            } else {
063                ret = new SimpleDateFormat(pattern);
064            }
065            TL.get().put(pattern, ret);
066        }
067        return ret;
068    }
069
070
071    public static Date parseDate(Object value) {
072        if (value instanceof Number) {
073            return new Date(((Number) value).longValue());
074        }
075        if (value instanceof Timestamp) {
076            return new Date(((Timestamp) value).getTime());
077        }
078        if (value instanceof LocalDate) {
079            return DateUtil.toDate((LocalDate) value);
080        }
081        if (value instanceof LocalDateTime) {
082            return DateUtil.toDate((LocalDateTime) value);
083        }
084        if (value instanceof LocalTime) {
085            return DateUtil.toDate((LocalTime) value);
086        }
087        String s = value.toString();
088        if (StringUtil.isNumeric(s)) {
089            return new Date(Long.parseLong(s));
090        }
091        return DateUtil.parseDate(s);
092    }
093
094
095    public static Date parseDate(String dateString) {
096        if (StringUtil.isBlank(dateString)) {
097            return null;
098        }
099        dateString = dateString.trim();
100        try {
101            SimpleDateFormat sdf = getSimpleDateFormat(getPattern(dateString));
102            try {
103                return sdf.parse(dateString);
104            } catch (ParseException ex) {
105                //2022-10-23 00:00:00.0
106                int lastIndexOf = dateString.lastIndexOf(".");
107                if (lastIndexOf == 19) {
108                    return parseDate(dateString.substring(0, lastIndexOf));
109                }
110
111                //2022-10-23 00:00:00,0
112                lastIndexOf = dateString.lastIndexOf(",");
113                if (lastIndexOf == 19) {
114                    return parseDate(dateString.substring(0, lastIndexOf));
115                }
116
117                //2022-10-23 00:00:00 000123
118                lastIndexOf = dateString.lastIndexOf(" ");
119                if (lastIndexOf == 19) {
120                    return parseDate(dateString.substring(0, lastIndexOf));
121                }
122
123                if (dateString.contains(".") || dateString.contains("/")) {
124                    dateString = dateString.replace(".", "-").replace("/", "-");
125                    return sdf.parse(dateString);
126                } else {
127                    throw ex;
128                }
129            }
130        } catch (ParseException ex) {
131            throw new IllegalArgumentException("The date format is not supported for the date string: " + dateString);
132        }
133    }
134
135
136    public static LocalDateTime parseLocalDateTime(String dateString) {
137        if (StringUtil.isBlank(dateString)) {
138            return null;
139        }
140        dateString = dateString.trim();
141        DateTimeFormatter dateTimeFormatter = getDateTimeFormatter(getPattern(dateString));
142        try {
143            return LocalDateTime.parse(dateString, dateTimeFormatter);
144        } catch (Exception ex) {
145            //2022-10-23 00:00:00.0
146            int lastIndexOf = dateString.lastIndexOf(".");
147            if (lastIndexOf == 19) {
148                return parseLocalDateTime(dateString.substring(0, lastIndexOf));
149            }
150
151            //2022-10-23 00:00:00,0
152            lastIndexOf = dateString.lastIndexOf(",");
153            if (lastIndexOf == 19) {
154                return parseLocalDateTime(dateString.substring(0, lastIndexOf));
155            }
156
157            //2022-10-23 00:00:00 000123
158            lastIndexOf = dateString.lastIndexOf(" ");
159            if (lastIndexOf == 19) {
160                return parseLocalDateTime(dateString.substring(0, lastIndexOf));
161            }
162
163            if (dateString.contains(".") || dateString.contains("/")) {
164                dateString = dateString.replace(".", "-").replace("/", "-");
165                dateTimeFormatter = getDateTimeFormatter(getPattern(dateString));
166                return LocalDateTime.parse(dateString, dateTimeFormatter);
167            } else {
168                throw ex;
169            }
170        }
171    }
172
173
174    private static String getPattern(String dateString) {
175        int length = dateString.length();
176        if (length == datetimePattern.length()) {
177            return datetimePattern;
178        } else if (length == datePattern.length()) {
179            return datePattern;
180        } else if (length == dateMinutePattern.length()) {
181            if (dateString.contains("T")) {
182                return dateMinutePattern2;
183            }
184            return dateMinutePattern;
185        } else if (length == dateMillisecondPattern.length()) {
186            return dateMillisecondPattern;
187        } else if (length == datePatternWithoutDividing.length()) {
188            return datePatternWithoutDividing;
189        } else if (length == dateCSTPattern.length()) {
190            return dateCSTPattern;
191        } else {
192            throw new IllegalArgumentException("The date format is not supported for the date string: " + dateString);
193        }
194    }
195
196
197    public static LocalDateTime toLocalDateTime(Date date) {
198        if (date == null) {
199            return null;
200        }
201        // java.sql.Date 不支持 toInstant(),需要先转换成 java.util.Date
202        if (date instanceof java.sql.Date) {
203            date = new Date(date.getTime());
204        }
205
206        Instant instant = date.toInstant();
207        ZoneId zone = ZoneId.systemDefault();
208        return LocalDateTime.ofInstant(instant, zone);
209    }
210
211
212    public static LocalDate toLocalDate(Date date) {
213        if (date == null) {
214            return null;
215        }
216        // java.sql.Date 不支持 toInstant(),需要先转换成 java.util.Date
217        if (date instanceof java.sql.Date) {
218            date = new Date(date.getTime());
219        }
220
221        Instant instant = date.toInstant();
222        ZoneId zone = ZoneId.systemDefault();
223        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
224        return localDateTime.toLocalDate();
225    }
226
227
228    public static LocalTime toLocalTime(Date date) {
229        if (date == null) {
230            return null;
231        }
232        // java.sql.Date 不支持 toInstant(),需要先转换成 java.util.Date
233        if (date instanceof java.sql.Date) {
234            date = new Date(date.getTime());
235        }
236
237        Instant instant = date.toInstant();
238        ZoneId zone = ZoneId.systemDefault();
239        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
240        return localDateTime.toLocalTime();
241    }
242
243
244    public static Date toDate(LocalDateTime localDateTime) {
245        if (localDateTime == null) {
246            return null;
247        }
248        ZoneId zone = ZoneId.systemDefault();
249        Instant instant = localDateTime.atZone(zone).toInstant();
250        return Date.from(instant);
251    }
252
253
254    public static Date toDate(LocalDate localDate) {
255        if (localDate == null) {
256            return null;
257        }
258        ZoneId zone = ZoneId.systemDefault();
259        Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
260        return Date.from(instant);
261    }
262
263
264    public static Date toDate(LocalTime localTime) {
265        if (localTime == null) {
266            return null;
267        }
268        LocalDate localDate = LocalDate.now();
269        LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
270        ZoneId zone = ZoneId.systemDefault();
271        Instant instant = localDateTime.atZone(zone).toInstant();
272        return Date.from(instant);
273    }
274
275
276    public static String toDateTimeString(Date date) {
277        return toString(date, datetimePattern);
278    }
279
280
281    public static String toString(Date date, String pattern) {
282        return date == null ? null : getSimpleDateFormat(pattern).format(date);
283    }
284
285
286}