001/*
002 *  Copyright (c) 2022-2023, 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    public static String datePatternWithoutDividing = "yyyyMMdd";
036    public static String datePattern = "yyyy-MM-dd";
037    public static final String dateMinutePattern = "yyyy-MM-dd HH:mm";
038    public static final String dateMinutePattern2 = "yyyy-MM-dd'T'HH:mm";
039    public static String datetimePattern = "yyyy-MM-dd HH:mm:ss";
040    public static final String dateMillisecondPattern = "yyyy-MM-dd HH:mm:ss SSS";
041    public static final String dateCSTPattern = "EEE MMM dd HH:mm:ss zzz yyyy";
042
043    private static final ThreadLocal<HashMap<String, SimpleDateFormat>> TL = ThreadLocal.withInitial(() -> new HashMap<>());
044
045    private static final Map<String, DateTimeFormatter> dateTimeFormatters = new ConcurrentHashMap<>();
046
047    public static DateTimeFormatter getDateTimeFormatter(String pattern) {
048        DateTimeFormatter ret = dateTimeFormatters.get(pattern);
049        if (ret == null) {
050            ret = DateTimeFormatter.ofPattern(pattern);
051            dateTimeFormatters.put(pattern, ret);
052        }
053        return ret;
054    }
055
056    public static SimpleDateFormat getSimpleDateFormat(String pattern) {
057        SimpleDateFormat ret = TL.get().get(pattern);
058        if (ret == null) {
059            if (dateCSTPattern.equals(pattern)) {
060                ret = new SimpleDateFormat(dateCSTPattern, Locale.US);
061            } else {
062                ret = new SimpleDateFormat(pattern);
063            }
064            TL.get().put(pattern, ret);
065        }
066        return ret;
067    }
068
069
070    public static Date parseDate(Object value) {
071        if (value instanceof Number) {
072            return new Date(((Number) value).longValue());
073        }
074        if (value instanceof Timestamp) {
075            return new Date(((Timestamp) value).getTime());
076        }
077        if (value instanceof LocalDate) {
078            return DateUtil.toDate((LocalDate) value);
079        }
080        if (value instanceof LocalDateTime) {
081            return DateUtil.toDate((LocalDateTime) value);
082        }
083        if (value instanceof LocalTime) {
084            return DateUtil.toDate((LocalTime) value);
085        }
086        String s = value.toString();
087        if (StringUtil.isNumeric(s)) {
088            return new Date(Long.parseLong(s));
089        }
090        return DateUtil.parseDate(s);
091    }
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
137    public static LocalDateTime parseLocalDateTime(String dateString) {
138        if (StringUtil.isBlank(dateString)) {
139            return null;
140        }
141        dateString = dateString.trim();
142        DateTimeFormatter dateTimeFormatter = getDateTimeFormatter(getPattern(dateString));
143        try {
144            return LocalDateTime.parse(dateString, dateTimeFormatter);
145        } catch (Exception ex) {
146            //2022-10-23 00:00:00.0
147            int lastIndexOf = dateString.lastIndexOf(".");
148            if (lastIndexOf == 19) {
149                return parseLocalDateTime(dateString.substring(0, lastIndexOf));
150            }
151
152            //2022-10-23 00:00:00,0
153            lastIndexOf = dateString.lastIndexOf(",");
154            if (lastIndexOf == 19) {
155                return parseLocalDateTime(dateString.substring(0, lastIndexOf));
156            }
157
158            //2022-10-23 00:00:00 000123
159            lastIndexOf = dateString.lastIndexOf(" ");
160            if (lastIndexOf == 19) {
161                return parseLocalDateTime(dateString.substring(0, lastIndexOf));
162            }
163
164            if (dateString.contains(".") || dateString.contains("/")) {
165                dateString = dateString.replace(".", "-").replace("/", "-");
166                dateTimeFormatter = getDateTimeFormatter(getPattern(dateString));
167                return LocalDateTime.parse(dateString, dateTimeFormatter);
168            } else {
169                throw ex;
170            }
171        }
172    }
173
174
175    private static String getPattern(String dateString) {
176        int length = dateString.length();
177        if (length == datetimePattern.length()) {
178            return datetimePattern;
179        } else if (length == datePattern.length()) {
180            return datePattern;
181        } else if (length == dateMinutePattern.length()) {
182            if (dateString.contains("T")) {
183                return dateMinutePattern2;
184            }
185            return dateMinutePattern;
186        } else if (length == dateMillisecondPattern.length()) {
187            return dateMillisecondPattern;
188        } else if (length == datePatternWithoutDividing.length()) {
189            return datePatternWithoutDividing;
190        } else if (length == dateCSTPattern.length()) {
191            return dateCSTPattern;
192        } else {
193            throw new IllegalArgumentException("The date format is not supported for the date string: " + dateString);
194        }
195    }
196
197
198    public static LocalDateTime toLocalDateTime(Date date) {
199        if (date == null) {
200            return null;
201        }
202        // java.sql.Date 不支持 toInstant(),需要先转换成 java.util.Date
203        if (date instanceof java.sql.Date) {
204            date = new Date(date.getTime());
205        }
206
207        Instant instant = date.toInstant();
208        ZoneId zone = ZoneId.systemDefault();
209        return LocalDateTime.ofInstant(instant, zone);
210    }
211
212
213    public static LocalDate toLocalDate(Date date) {
214        if (date == null) {
215            return null;
216        }
217        // java.sql.Date 不支持 toInstant(),需要先转换成 java.util.Date
218        if (date instanceof java.sql.Date) {
219            date = new Date(date.getTime());
220        }
221
222        Instant instant = date.toInstant();
223        ZoneId zone = ZoneId.systemDefault();
224        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
225        return localDateTime.toLocalDate();
226    }
227
228
229    public static LocalTime toLocalTime(Date date) {
230        if (date == null) {
231            return null;
232        }
233        // java.sql.Date 不支持 toInstant(),需要先转换成 java.util.Date
234        if (date instanceof java.sql.Date) {
235            date = new Date(date.getTime());
236        }
237
238        Instant instant = date.toInstant();
239        ZoneId zone = ZoneId.systemDefault();
240        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
241        return localDateTime.toLocalTime();
242    }
243
244
245    public static Date toDate(LocalDateTime localDateTime) {
246        if (localDateTime == null) {
247            return null;
248        }
249        ZoneId zone = ZoneId.systemDefault();
250        Instant instant = localDateTime.atZone(zone).toInstant();
251        return Date.from(instant);
252    }
253
254
255    public static Date toDate(LocalDate localDate) {
256        if (localDate == null) {
257            return null;
258        }
259        ZoneId zone = ZoneId.systemDefault();
260        Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
261        return Date.from(instant);
262    }
263
264
265    public static Date toDate(LocalTime localTime) {
266        if (localTime == null) {
267            return null;
268        }
269        LocalDate localDate = LocalDate.now();
270        LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
271        ZoneId zone = ZoneId.systemDefault();
272        Instant instant = localDateTime.atZone(zone).toInstant();
273        return Date.from(instant);
274    }
275
276
277    public static String toDateTimeString(Date date) {
278        return toString(date, datetimePattern);
279    }
280
281
282    public static String toString(Date date, String pattern) {
283        return date == null ? null : getSimpleDateFormat(pattern).format(date);
284    }
285
286
287}