From aa8d66b81ab5e3e18f8c240b42f885fb92faa68b Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 2 Jul 2024 23:23:25 +0800 Subject: [PATCH] fix code --- .../core/convert/impl/DateConverter.java | 15 +- .../impl/TemporalAccessorConverter.java | 13 +- .../dromara/hutool/core/date/DateBuilder.java | 42 ++++- .../dromara/hutool/core/date/DateUtil.java | 57 +++--- .../dromara/hutool/core/date/ZoneUtil.java | 27 +++ .../format/parser/GlobalRegexDateParser.java | 89 +++++++--- .../date/format/parser/RegexDateParser.java | 168 +++++++++++++++++- .../hutool/core/text/dfa/WordTree.java | 23 ++- .../hutool/core/convert/ConvertTest.java | 2 +- .../core/convert/ConvertToNumberTest.java | 8 +- .../hutool/core/data/IdcardUtilTest.java | 7 +- .../hutool/core/data/Issue3081Test.java | 5 +- .../hutool/core/date/DateTimeTest.java | 4 +- .../hutool/core/date/DateUtilTest.java | 41 ++--- .../hutool/core/date/Issue2612Test.java | 4 +- .../hutool/core/date/IssueI7QI6RTest.java | 9 +- .../hutool/core/date/IssueI82Y1LTest.java | 2 +- .../hutool/core/date/IssueI8IUTBTest.java | 6 +- .../hutool/core/date/IssueI9C2D4Test.java | 11 +- .../hutool/core/date/TimeUtilTest.java | 6 +- .../hutool/core/date/ZoneUtilTest.java | 52 ++++++ .../parser/GlobalRegexDateParserTest.java | 111 +++++++++++- .../hutool/core/lang/range/RangeTest.java | 45 ++--- 23 files changed, 584 insertions(+), 163 deletions(-) diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/DateConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/DateConverter.java index b2d5dc1af..7238ecd41 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/DateConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/DateConverter.java @@ -21,6 +21,7 @@ import org.dromara.hutool.core.text.StrUtil; import java.time.temporal.TemporalAccessor; import java.util.Calendar; +import java.util.Date; /** * 日期转换器 @@ -88,11 +89,11 @@ public class DateConverter extends AbstractConverter { } else { // 统一按照字符串处理 final String valueStr = convertToStr(value); - final DateTime dateTime = StrUtil.isBlank(this.format) // + final Date date = StrUtil.isBlank(this.format) // ? DateUtil.parse(valueStr) // : DateUtil.parse(valueStr, this.format); - if (null != dateTime) { - return wrap(targetClass, dateTime); + if (null != date) { + return wrap(targetClass, date); } } @@ -105,12 +106,8 @@ public class DateConverter extends AbstractConverter { * @param date Date * @return 目标类型对象 */ - private java.util.Date wrap(final Class targetClass, final DateTime date) { - // 返回指定类型 - if (java.util.Date.class == targetClass) { - return date.toJdkDate(); - } - if (DateTime.class == targetClass) { + private java.util.Date wrap(final Class targetClass, final Date date) { + if(targetClass == date.getClass()){ return date; } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TemporalAccessorConverter.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TemporalAccessorConverter.java index dd4541a68..b6a792bf7 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TemporalAccessorConverter.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/impl/TemporalAccessorConverter.java @@ -49,6 +49,9 @@ import java.util.Objects; public class TemporalAccessorConverter extends AbstractConverter { private static final long serialVersionUID = 1L; + /** + * 单例 + */ public static final TemporalAccessorConverter INSTANCE = new TemporalAccessorConverter(); /** @@ -131,7 +134,7 @@ public class TemporalAccessorConverter extends AbstractConverter { } final Instant instant; - final ZoneId zoneId; + ZoneId zoneId = null; if (null != this.format) { final DateTimeFormatter formatter = TimeUtil.ofPattern(this.format); @@ -144,9 +147,11 @@ public class TemporalAccessorConverter extends AbstractConverter { instant = formatter.parse(value, Instant::from); zoneId = formatter.getZone(); } else { - final DateTime dateTime = DateUtil.parse(value); - instant = Objects.requireNonNull(dateTime).toInstant(); - zoneId = dateTime.getZoneId(); + final Date date = DateUtil.parse(value); + instant = Objects.requireNonNull(date).toInstant(); + if(date instanceof DateTime){ + zoneId = ((DateTime) date).getZoneId(); + } } return parseFromInstant(targetClass, instant, zoneId); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/DateBuilder.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/DateBuilder.java index db7c823db..3ba56c284 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/DateBuilder.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/DateBuilder.java @@ -16,6 +16,7 @@ import java.time.*; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; +import java.util.concurrent.TimeUnit; /** * DateBuilder类用于构建和操作日期。 @@ -67,6 +68,13 @@ public final class DateBuilder { private boolean pm; // endregion + /** + * 构造 + */ + public DateBuilder() { + reset(); + } + // region ----- getters and setters /** @@ -413,6 +421,25 @@ public final class DateBuilder { return Date.from(toOffsetDateTime().toInstant()); } + /** + * 将当前时间对象转换为{@link DateTime}类型。此方法根据是否设置了时区偏移量使用不同的转换策略。 + * + * 。 + * + * @return DateTime 表示当前时间的 DateTime 对象。 + */ + public DateTime toDateTime() { + if (!zoneOffsetSetted) { + // 时区偏移量未设置,使用 Calendar 进行转换 + return new DateTime(toCalendar()); + } + // 时区偏移量已设置,直接转换为 Date 对象返回 + return new DateTime(toOffsetDateTime().toInstant()); + } + /** * 将当前对象的日期时间信息转换为{@link Calendar}实例。 * 如果`unixsecond`不为0,将根据unix时间戳(秒)和纳秒偏移量构造Calendar。 @@ -430,16 +457,15 @@ public final class DateBuilder { if (zone != null) { calendar.setTimeZone(zone); // 使用指定的时区 } else if (zoneOffsetSetted) { // 如果设置了时区偏移量 - final String[] ids = TimeZone.getAvailableIDs(zoneOffset * 60_000); // 尝试根据偏移量获取时区ID - if (ids.length == 0) { // 如果没有找到有效的时区ID - throw new DateException("Can't build Calendar, " + - "because the zoneOffset[{}] can't be converted to an valid TimeZone.", this.zoneOffset); + final TimeZone timeZone = ZoneUtil.getTimeZoneByOffset(zoneOffset, TimeUnit.SECONDS); + if (null == timeZone) { // 如果没有找到有效的时区ID + throw new DateException("Invalid zoneOffset: {}", this.zoneOffset); } - calendar.setTimeZone(TimeZone.getTimeZone(ids[0])); // 设置第一个找到的时区 + calendar.setTimeZone(timeZone); } // 如果毫秒数不为0,则直接使用毫秒数设置时间 - if(millisecond != 0){ + if (millisecond != 0) { calendar.setTimeInMillis(millisecond); return calendar; } @@ -472,7 +498,7 @@ public final class DateBuilder { LocalDateTime toLocalDateTime() { this.prepare(); - if(millisecond > 0){ + if (millisecond > 0) { final Instant instant = Instant.ofEpochMilli(millisecond); return LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), DEFAULT_OFFSET); } @@ -514,7 +540,7 @@ public final class DateBuilder { OffsetDateTime toOffsetDateTime() { this.prepare(); // 准备工作,可能涉及一些初始化或数据处理 - if(millisecond > 0){ + if (millisecond > 0) { return OffsetDateTime.ofInstant(Instant.ofEpochMilli(millisecond), ZoneUtil.ZONE_ID_UTC); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/DateUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/DateUtil.java index 75ab487d9..3d303b16e 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/DateUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/DateUtil.java @@ -767,6 +767,39 @@ public class DateUtil extends CalendarUtil { return date(CalendarUtil.parseByPatterns(str, parsePatterns)); } + /** + * 将日期字符串转换为{@link DateTime}对象,格式:
+ *
    + *
  1. yyyy-MM-dd HH:mm:ss
  2. + *
  3. yyyy/MM/dd HH:mm:ss
  4. + *
  5. yyyy.MM.dd HH:mm:ss
  6. + *
  7. yyyy年MM月dd日 HH时mm分ss秒
  8. + *
  9. yyyy-MM-dd
  10. + *
  11. yyyy/MM/dd
  12. + *
  13. yyyy.MM.dd
  14. + *
  15. HH:mm:ss
  16. + *
  17. HH时mm分ss秒
  18. + *
  19. yyyy-MM-dd HH:mm
  20. + *
  21. yyyy-MM-dd HH:mm:ss.SSS
  22. + *
  23. yyyy-MM-dd HH:mm:ss.SSSSSS
  24. + *
  25. yyyyMMddHHmmss
  26. + *
  27. yyyyMMddHHmmssSSS
  28. + *
  29. yyyyMMdd
  30. + *
  31. EEE, dd MMM yyyy HH:mm:ss z
  32. + *
  33. EEE MMM dd HH:mm:ss zzz yyyy
  34. + *
  35. yyyy-MM-dd'T'HH:mm:ss'Z'
  36. + *
  37. yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
  38. + *
  39. yyyy-MM-dd'T'HH:mm:ssZ
  40. + *
  41. yyyy-MM-dd'T'HH:mm:ss.SSSZ
  42. + *
+ * + * @param dateCharSequence 日期字符串 + * @return 日期 + */ + public static DateTime parseDateTime(final CharSequence dateCharSequence) { + return date(parse(dateCharSequence)); + } + /** * 将日期字符串转换为{@link DateTime}对象,格式:
*
    @@ -797,27 +830,11 @@ public class DateUtil extends CalendarUtil { * @return 日期 */ public static DateTime parse(final CharSequence dateCharSequence) { - if (StrUtil.isBlank(dateCharSequence)) { - return null; + if(TimeParser.INSTANCE.test(dateCharSequence)){ + // 独立解析时间,则默认使用今天的日期 + return TimeParser.INSTANCE.parse(dateCharSequence); } - String dateStr = dateCharSequence.toString(); - // 去掉两边空格并去掉中文日期中的“日”和“秒”,以规范长度 - dateStr = StrUtil.removeAll(dateStr.trim(), '日', '秒'); - - final Date result = RegisterDateParser.INSTANCE.parse(dateStr); - if(null != result){ - return date(result); - } - - //标准日期格式(包括单个数字的日期时间) - dateStr = normalize(dateStr); - - if (NormalDateParser.INSTANCE.test(dateStr)) { - return NormalDateParser.INSTANCE.parse(dateStr); - } - - // 没有更多匹配的时间格式 - throw new DateException("No format fit for date String [{}] !", dateStr); + return GlobalRegexDateParser.parse(dateCharSequence); } // endregion diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/ZoneUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/ZoneUtil.java index 7ffc516aa..68135bd5d 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/ZoneUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/ZoneUtil.java @@ -12,8 +12,11 @@ package org.dromara.hutool.core.date; +import org.dromara.hutool.core.array.ArrayUtil; + import java.time.ZoneId; import java.util.TimeZone; +import java.util.concurrent.TimeUnit; /** * {@link ZoneId}和{@link TimeZone}相关封装 @@ -59,4 +62,28 @@ public class ZoneUtil { return timeZone.toZoneId(); } + + /** + * 获取指定偏移量的可用时区,如果有多个时区匹配,使用第一个 + * + * @param rawOffset 偏移量 + * @param timeUnit 偏移量单位 + * @return 时区 + */ + public static TimeZone getTimeZoneByOffset(final int rawOffset, final TimeUnit timeUnit) { + final String id = getAvailableID(rawOffset, timeUnit); + return null == id ? null : TimeZone.getTimeZone(id); + } + + /** + * 获取指定偏移量的可用时区ID,如果有多个时区匹配,使用第一个 + * + * @param rawOffset 偏移量 + * @param timeUnit 偏移量单位 + * @return 时区ID + */ + public static String getAvailableID(final int rawOffset, final TimeUnit timeUnit) { + final String[] availableIDs = TimeZone.getAvailableIDs((int) timeUnit.toMillis(rawOffset)); + return ArrayUtil.isEmpty(availableIDs) ? null : availableIDs[0]; + } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/GlobalRegexDateParser.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/GlobalRegexDateParser.java index ee7886cfb..2c4ae3f42 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/GlobalRegexDateParser.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/GlobalRegexDateParser.java @@ -12,7 +12,8 @@ package org.dromara.hutool.core.date.format.parser; -import java.util.Date; +import org.dromara.hutool.core.date.DateTime; + import java.util.regex.Pattern; /** @@ -24,29 +25,26 @@ import java.util.regex.Pattern; */ public class GlobalRegexDateParser { - private static final String yearRegex = "(?\\d{2,4})"; - private static final String monthRegex = "(?\\w{3,9})"; - private static final String dayRegex = "(?\\d{1,2})(?:th)?"; - // 周的正则,匹配:Mon, Tue, Wed, Thu, Fri, Sat, Sun,或 Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday - // 日期中一般出现在头部,可选 - private static final String weekRegexWithSuff = "(?[mwfts][oeruha][ndieut](\\w{3,6})?\\W+)?"; - // hh:mm:ss.SSSSZ hh:mm:ss.SSSS hh:mm:ss hh:mm - private static final String timeRegexWithPre = "(" + - "\\W+(at\\s)?(?\\d{1,2})" + - ":(?\\d{1,2})" + - "(:(?\\d{1,2}))?" + - "(?:[.,](?\\d{1,9}))?(?z)?" + - "(\\s?(?[ap]m))?" + - ")?"; - // 时区,类似: +08:00 +0800 +08,可选 - private static final String zoneOffsetRegexWithPre = "(\\s?(?[-+]\\d{1,2}:?(?:\\d{2})?))?"; - // 时区名称,类似: CST UTC (CST),可选 - private static final String zoneNameRegexWithPre = "(\\s[(]?(?[a-z ]+)[)]?)?"; - private static final String zoneNameIgnoreRegexWithPre = "(\\s[(]?(?[a-z ]+)[)]?)?"; - private static final RegexDateParser PARSER; static { + final String yearRegex = "(?\\d{2,4})"; + // 月的正则,匹配:Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec, + // 或 January, February, March, April, May, June, July, August, September, October, November, December + final String monthRegex = "(?[jfmaasond][aepucoe][nbrylgptvc]\\w{0,6})"; + final String dayRegex = "(?\\d{1,2})(?:th)?"; + // 周的正则,匹配:Mon, Tue, Wed, Thu, Fri, Sat, Sun, + // 或 Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday + // 周一般出现在日期头部,可选 + final String weekRegexWithSuff = "(?[mwfts][oeruha][ndieut](\\w{3,6})?\\W+)?"; + // hh:mm:ss.SSSSZ hh:mm:ss.SSSS hh:mm:ss hh:mm + final String timeRegexWithPre = "(" + + "(\\W+|T)(at\\s)?(?\\d{1,2})" + + "\\W(?\\d{1,2})" + + "(\\W(?\\d{1,2}))?秒?" + + "(?:[.,](?\\d{1,9}))?(?z)?" + + "(\\s?(?[ap]m))?" + + ")?"; // 月开头,类似:May 8 final String dateRegexMonthFirst = monthRegex + "\\W+" + dayRegex; // 日开头,类似:02-Jan @@ -56,17 +54,41 @@ public class GlobalRegexDateParser { // +0200 (CEST) // GMT+0100 // MST - final String zoneRegex = zoneNameRegexWithPre + zoneOffsetRegexWithPre + zoneNameIgnoreRegexWithPre; + final String zoneRegex = "\\s?(?" + // 匹配:GMT MST等 + + "[a-z ]*" + // 匹配:+08:00 +0800 +08 + + "(\\s?[-+]\\d{1,2}:?(?:\\d{2})?)*" + // 匹配:(GMT Daylight Time)等 + + "(\\s?[(]?[a-z ]+[)]?)?" + + ")"; + final String maskRegex = "(\\smsk m=[+-]\\d[.]\\d+)?"; PARSER = RegexDateParser.of( - // 年开头 + //【年月日时】类似:2009-Feb-08,时间部分可选,类似:5:57:51,05:57:51 +08:00 + yearRegex + "\\W" + dateRegexMonthFirst + timeRegexWithPre + zoneRegex + maskRegex, + //【年月日时】类似:2009-02-08或2014年04月08日,时间部分可选,类似:5:57:51,05:57:51 +08:00 + yearRegex + "\\W(?\\d{1,2})(\\W(?\\d{1,2}))?日?" + timeRegexWithPre + zoneRegex + maskRegex, - //【周月日年时】类似:May 8, 2009,时间部分可选,类似:5:57:51,5:57:51 +08:00 - weekRegexWithSuff + dateRegexMonthFirst + "\\W+" + yearRegex + timeRegexWithPre + zoneRegex, + //【周月日年时】类似:May 8, 2009,时间部分可选,类似:5:57:51,05:57:51 +08:00 + weekRegexWithSuff + dateRegexMonthFirst + "\\W+" + yearRegex + timeRegexWithPre + zoneRegex + maskRegex, //【周月日时年】类似:Mon Jan 2 15:04:05 MST 2006 - weekRegexWithSuff + dateRegexMonthFirst + timeRegexWithPre + zoneRegex + "\\W+" + yearRegex, + weekRegexWithSuff + dateRegexMonthFirst + timeRegexWithPre + zoneRegex + "\\W+" + yearRegex + maskRegex, //【周日月年时】类似:Monday, 02-Jan-06 15:04:05 MST - weekRegexWithSuff + dateRegexDayFirst + "\\W+" + yearRegex + timeRegexWithPre + zoneRegex + weekRegexWithSuff + dateRegexDayFirst + "\\W+" + yearRegex + timeRegexWithPre + zoneRegex + maskRegex, + //【日月年时】MM/dd/yyyy, dd/MM/yyyy, 类似:4/12/2014 03:00:51,为避免歧义,只支持4位年 + "(?\\d{1,2}\\W\\d{1,2})\\W(?\\d{4})" + timeRegexWithPre + zoneRegex + maskRegex, + + //纯数字日期时间 + // yyyy + // yyyyMM + // yyyyMMdd + // yyyyMMddhhmmss + // unixtime(10) + // millisecond(13) + // microsecond(16) + // nanosecond(19) + "^(?\\d{4,19})$" ); } @@ -76,8 +98,17 @@ public class GlobalRegexDateParser { * @param source 日期字符串 * @return 日期 */ - public static Date parse(final CharSequence source) { - return PARSER.parse(source); + public static DateTime parse(final CharSequence source) { + return (DateTime) PARSER.parse(source); + } + + /** + * 当用户传入的月和日无法判定默认位置时,设置默认的日期格式为dd/mm还是mm/dd + * + * @param preferMonthFirst {@code true}默认为mm/dd,否则dd/mm + */ + synchronized public static void setPreferMonthFirst(final boolean preferMonthFirst) { + PARSER.setPreferMonthFirst(preferMonthFirst); } /** diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/RegexDateParser.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/RegexDateParser.java index 5ab71aa9f..fd34a16d2 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/RegexDateParser.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/RegexDateParser.java @@ -13,16 +13,22 @@ package org.dromara.hutool.core.date.format.parser; import org.dromara.hutool.core.collection.ListUtil; -import org.dromara.hutool.core.date.*; +import org.dromara.hutool.core.date.DateBuilder; +import org.dromara.hutool.core.date.DateException; +import org.dromara.hutool.core.date.Month; +import org.dromara.hutool.core.date.Week; +import org.dromara.hutool.core.lang.Assert; import org.dromara.hutool.core.lang.Opt; import org.dromara.hutool.core.regex.ReUtil; import org.dromara.hutool.core.text.CharUtil; import org.dromara.hutool.core.text.StrUtil; +import org.dromara.hutool.core.text.dfa.WordTree; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -36,6 +42,8 @@ public class RegexDateParser implements DateParser, Serializable { private static final long serialVersionUID = 1L; private static final int[] NSS = {100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1}; + private static final Pattern ZONE_OFFSET_PATTERN = Pattern.compile("[-+]\\d{1,2}:?(?:\\d{2})?"); + private static final WordTree ZONE_TREE = WordTree.of(TimeZone.getAvailableIDs()); /** * 根据给定的正则列表,创建RegexListDateParser @@ -62,6 +70,7 @@ public class RegexDateParser implements DateParser, Serializable { } private final List patterns; + private boolean preferMonthFirst; /** * 构造 @@ -72,6 +81,15 @@ public class RegexDateParser implements DateParser, Serializable { this.patterns = patterns; } + /** + * 当用户传入的月和日无法判定默认位置时,设置默认的日期格式为dd/mm还是mm/dd + * + * @param preferMonthFirst {@code true}默认为mm/dd,否则dd/mm + */ + public void setPreferMonthFirst(final boolean preferMonthFirst) { + this.preferMonthFirst = preferMonthFirst; + } + /** * 新增自定义日期正则 * @@ -96,13 +114,25 @@ public class RegexDateParser implements DateParser, Serializable { @Override public Date parse(final CharSequence source) throws DateException { + Assert.notBlank(source, "Date str must be not blank!"); + return parseToBuilder(source).toDateTime(); + } + + /** + * 解析日期 + * + * @param source 日期字符串 + * @return DateBuilder + * @throws DateException 日期解析异常 + */ + private DateBuilder parseToBuilder(final CharSequence source) throws DateException { final DateBuilder dateBuilder = DateBuilder.of(); Matcher matcher; for (final Pattern pattern : this.patterns) { matcher = pattern.matcher(source); if (matcher.matches()) { parse(matcher, dateBuilder); - return dateBuilder.toDate(); + return dateBuilder; } } @@ -115,7 +145,15 @@ public class RegexDateParser implements DateParser, Serializable { * @param matcher 正则匹配器 * @throws DateException 日期解析异常 */ - private static void parse(final Matcher matcher, final DateBuilder dateBuilder) throws DateException { + private void parse(final Matcher matcher, final DateBuilder dateBuilder) throws DateException { + + // 纯数字格式 + final String number = ReUtil.group(matcher, "number"); + if (StrUtil.isNotEmpty(number)) { + parseNumberDate(number, dateBuilder); + return; + } + // 毫秒时间戳 final String millisecond = ReUtil.group(matcher, "millisecond"); if (StrUtil.isNotEmpty(millisecond)) { @@ -125,6 +163,8 @@ public class RegexDateParser implements DateParser, Serializable { // year Opt.ofNullable(ReUtil.group(matcher, "year")).ifPresent((year) -> dateBuilder.setYear(parseYear(year))); + // dayOrMonth, dd/mm or mm/dd + Opt.ofNullable(ReUtil.group(matcher, "dayOrMonth")).ifPresent((dayOrMonth) -> parseDayOrMonth(dayOrMonth, dateBuilder, preferMonthFirst)); // month Opt.ofNullable(ReUtil.group(matcher, "month")).ifPresent((month) -> dateBuilder.setMonth(parseMonth(month))); // week @@ -154,23 +194,76 @@ public class RegexDateParser implements DateParser, Serializable { dateBuilder.setZoneOffset(0); }); + // zone(包括可时区名称、时区偏移等信息,综合解析) + Opt.ofNullable(ReUtil.group(matcher, "zone")).ifPresent((zoneOffset) -> { + parseZone(zoneOffset, dateBuilder); + }); + // zone offset Opt.ofNullable(ReUtil.group(matcher, "zoneOffset")).ifPresent((zoneOffset) -> { dateBuilder.setZoneOffsetSetted(true); dateBuilder.setZoneOffset(parseZoneOffset(zoneOffset)); }); - // zone name - Opt.ofNullable(ReUtil.group(matcher, "zoneName")).ifPresent((zoneOffset) -> { - // 暂时不支持解析 - }); - - // unix时间戳 + // unix时间戳,可能有NS Opt.ofNullable(ReUtil.group(matcher, "unixsecond")).ifPresent((unixsecond) -> { dateBuilder.setUnixsecond(parseLong(unixsecond)); }); } + /** + * 解析纯数字型的日期 + * + * @param number 纯数字 + * @param dateBuilder {@link DateBuilder} + */ + private static void parseNumberDate(final String number, final DateBuilder dateBuilder) { + final int length = number.length(); + switch (length) { + case 4: + // yyyy + dateBuilder.setYear(Integer.parseInt(number)); + break; + case 6: + // yyyyMM + dateBuilder.setYear(parseInt(number, 0, 4)); + dateBuilder.setMonth(parseInt(number, 4, 6)); + break; + case 8: + // yyyyMMdd + dateBuilder.setYear(parseInt(number, 0, 4)); + dateBuilder.setMonth(parseInt(number, 4, 6)); + dateBuilder.setDay(parseInt(number, 6, 8)); + break; + case 14: + dateBuilder.setYear(parseInt(number, 0, 4)); + dateBuilder.setMonth(parseInt(number, 4, 6)); + dateBuilder.setDay(parseInt(number, 6, 8)); + dateBuilder.setHour(parseInt(number, 8, 10)); + dateBuilder.setMinute(parseInt(number, 10, 12)); + dateBuilder.setSecond(parseInt(number, 12, 14)); + break; + case 10: + // unixtime(10) + dateBuilder.setUnixsecond(parseLong(number)); + break; + case 13: + // millisecond(13) + dateBuilder.setMillisecond(parseLong(number)); + break; + case 16: + // microsecond(16) + dateBuilder.setUnixsecond(parseLong(number.substring(0, 10))); + dateBuilder.setNs(parseInt(number, 10, 16)); + break; + case 19: + // nanosecond(19) + dateBuilder.setUnixsecond(parseLong(number.substring(0, 10))); + dateBuilder.setNs(parseInt(number, 10, 19)); + break; + } + } + private static int parseYear(final String year) { final int length = year.length(); switch (length) { @@ -184,6 +277,40 @@ public class RegexDateParser implements DateParser, Serializable { } } + /** + * 解析日期中的日或月,类似于dd/mm或mm/dd格式 + * + * @param dayOrMonth 日期中的日或月 + * @param dateBuilder {@link DateBuilder} + * @param preferMonthFirst 是否月份在前 + */ + private static void parseDayOrMonth(final String dayOrMonth, final DateBuilder dateBuilder, final boolean preferMonthFirst) { + final char next = dayOrMonth.charAt(1); + final int a; + final int b; + if (next < '0' || next > '9') { + // d/m + a = parseInt(dayOrMonth, 0, 1); + b = parseInt(dayOrMonth, 2, dayOrMonth.length()); + } else { + // dd/mm + a = parseInt(dayOrMonth, 0, 2); + b = parseInt(dayOrMonth, 3, dayOrMonth.length()); + } + + if (a > 31 || b > 31 || a == 0 || b == 0 || (a > 12 && b > 12)) { + throw new DateException("Invalid DayOrMonth : {}", dayOrMonth); + } + + if (b > 12 || (preferMonthFirst && a <= 12)) { + dateBuilder.setMonth(a); + dateBuilder.setDay(b); + } else { + dateBuilder.setMonth(b); + dateBuilder.setDay(a); + } + } + private static int parseMonth(final String month) { try { final int monthInt = Integer.parseInt(month); @@ -232,6 +359,29 @@ public class RegexDateParser implements DateParser, Serializable { return NSS[ns.length() - 1] * Integer.parseInt(ns); } + /** + * 解析时区,包括时区偏移和时区名称 + * + * @param zone 时区 + * @param dateBuilder 日期时间对象 + */ + private static void parseZone(final String zone, final DateBuilder dateBuilder) { + // 检查是否直接定义了时区偏移 + final String zoneOffset = ReUtil.getGroup0(ZONE_OFFSET_PATTERN, zone); + if (StrUtil.isNotBlank(zoneOffset)) { + dateBuilder.setZoneOffsetSetted(true); + dateBuilder.setZoneOffset(parseZoneOffset(zoneOffset)); + return; + } + + // 检查是否定义了时区名称 + final String zoneName = ZONE_TREE.match(zone); + if (StrUtil.isNotBlank(zoneName)) { + dateBuilder.setZoneOffsetSetted(true); + dateBuilder.setZone(TimeZone.getTimeZone(zoneName)); + } + } + /** * 解析时区偏移,类似于'+0800', '+08', '+8:00', '+08:00' * diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/dfa/WordTree.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/dfa/WordTree.java index e7e405e3d..7a46d504e 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/text/dfa/WordTree.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/dfa/WordTree.java @@ -41,6 +41,21 @@ import java.util.function.Predicate; public class WordTree extends HashMap { private static final long serialVersionUID = -4646423269465809276L; + /** + * 通过预定义的关键词构造单词树 + * + * @param words 初始关键词 + * @return WordTree + * @since 6.0.0 + */ + public static WordTree of(final String... words) { + final WordTree wordTree = new WordTree(words.length); + for (final String word : words) { + wordTree.addWord(word); + } + return wordTree; + } + /** * 单词字符末尾标识,用于标识单词末尾字符 */ @@ -103,7 +118,7 @@ public class WordTree extends HashMap { * 增加一组单词 * * @param words 单词数组 - * @return this + * @return this */ public WordTree addWords(final String... words) { for (final String word : SetUtil.of(words)) { @@ -315,7 +330,7 @@ public class WordTree extends HashMap { } // 本次循环结尾,加入遗留匹配的单词 - if(null != currentFoundWord){ + if (null != currentFoundWord) { foundWords.add(currentFoundWord); if (limit > 0 && foundWords.size() >= limit) { //超过匹配限制个数,直接返回 @@ -356,8 +371,8 @@ public class WordTree extends HashMap { * @param entry WordTree每个entry节点 * @return 递归扁平化后的结果 */ - private Iterable innerFlatten(Entry entry) { - List list = EasyStream.of(entry.getValue().entrySet()).flat(this::innerFlatten).map(v -> entry.getKey() + v).toList(); + private Iterable innerFlatten(final Entry entry) { + final List list = EasyStream.of(entry.getValue().entrySet()).flat(this::innerFlatten).map(v -> entry.getKey() + v).toList(); if (list.isEmpty()) { return EasyStream.of(entry.getKey().toString()); } diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/convert/ConvertTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/convert/ConvertTest.java index a4da28d87..5d0cf9f90 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/convert/ConvertTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/convert/ConvertTest.java @@ -379,7 +379,7 @@ public class ConvertTest { @Test public void toDateTest2(){ final Date date = Convert.toDate("2021-01"); - Assertions.assertNull(date); + Assertions.assertEquals("2021-01-01", DateUtil.formatDate(date)); } @Test diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/convert/ConvertToNumberTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/convert/ConvertToNumberTest.java index 3f8a60e82..31d70b834 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/convert/ConvertToNumberTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/convert/ConvertToNumberTest.java @@ -12,18 +12,18 @@ package org.dromara.hutool.core.convert; -import org.dromara.hutool.core.date.DateTime; import org.dromara.hutool.core.date.DateUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.math.BigDecimal; +import java.util.Date; import java.util.concurrent.atomic.AtomicLong; public class ConvertToNumberTest { @Test public void dateToLongTest(){ - final DateTime date = DateUtil.parse("2020-05-17 12:32:00"); + final Date date = DateUtil.parse("2020-05-17 12:32:00"); final Long dateLong = Convert.toLong(date); assert date != null; Assertions.assertEquals(date.getTime(), dateLong.longValue()); @@ -31,7 +31,7 @@ public class ConvertToNumberTest { @Test public void dateToIntTest(){ - final DateTime date = DateUtil.parse("2020-05-17 12:32:00"); + final Date date = DateUtil.parse("2020-05-17 12:32:00"); final Integer dateInt = Convert.toInt(date); assert date != null; Assertions.assertEquals((int)date.getTime(), dateInt.intValue()); @@ -39,7 +39,7 @@ public class ConvertToNumberTest { @Test public void dateToAtomicLongTest(){ - final DateTime date = DateUtil.parse("2020-05-17 12:32:00"); + final Date date = DateUtil.parse("2020-05-17 12:32:00"); final AtomicLong dateLong = Convert.convert(AtomicLong.class, date); assert date != null; Assertions.assertEquals(date.getTime(), dateLong.longValue()); diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/data/IdcardUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/data/IdcardUtilTest.java index b4c991a0f..a0eeddd3c 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/data/IdcardUtilTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/data/IdcardUtilTest.java @@ -12,11 +12,12 @@ package org.dromara.hutool.core.data; -import org.dromara.hutool.core.date.DateTime; import org.dromara.hutool.core.date.DateUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.Date; + /** * 身份证单元测试 * @@ -66,7 +67,7 @@ public class IdcardUtilTest { @Test public void getAgeTest() { - final DateTime date = DateUtil.parse("2017-04-10"); + final Date date = DateUtil.parse("2017-04-10"); final int age = IdcardUtil.getAge(ID_18, date); Assertions.assertEquals(age, 38); @@ -171,7 +172,7 @@ public class IdcardUtilTest { final String FOREIGN_ID_18 = "932682198501010017"; Assertions.assertTrue(IdcardUtil.isValidCard(FOREIGN_ID_18)); - final DateTime date = DateUtil.parse("2017-04-10"); + final Date date = DateUtil.parse("2017-04-10"); Assertions.assertEquals(IdcardUtil.getAge(FOREIGN_ID_18, date), 32); // 新版外国人永久居留身份证 diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/data/Issue3081Test.java b/hutool-core/src/test/java/org/dromara/hutool/core/data/Issue3081Test.java index abc58a1a6..2cd6fa98c 100755 --- a/hutool-core/src/test/java/org/dromara/hutool/core/data/Issue3081Test.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/data/Issue3081Test.java @@ -20,14 +20,15 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.ArrayList; +import java.util.Date; import java.util.List; public class Issue3081Test { @Test void dateRangeTest() { - final DateTime start = DateUtil.parse("2023-01-01 00:00:00"); - final DateTime end = DateUtil.parse("2023-04-25 00:00:00"); + final Date start = DateUtil.parse("2023-01-01 00:00:00"); + final Date end = DateUtil.parse("2023-04-25 00:00:00"); final DateRange dateTimes = new DateRange(start, end, DateField.DAY_OF_MONTH, 30, true, true); diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/date/DateTimeTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/date/DateTimeTest.java index 32f2dba2e..047137ced 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/date/DateTimeTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/date/DateTimeTest.java @@ -131,13 +131,13 @@ public class DateTimeTest { @Test public void monthTest() { //noinspection ConstantConditions - final int month = DateUtil.parse("2017-07-01").month(); + final int month = DateUtil.date(DateUtil.parse("2017-07-01")).month(); Assertions.assertEquals(6, month); } @Test public void weekOfYearTest() { - final DateTime date = DateUtil.parse("2016-12-27"); + final DateTime date = DateUtil.date(DateUtil.parse("2016-12-27")); //noinspection ConstantConditions Assertions.assertEquals(2016, date.year()); //跨年的周返回的总是1 diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/date/DateUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/date/DateUtilTest.java index ba4058149..f94e7b786 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/date/DateUtilTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/date/DateUtilTest.java @@ -13,7 +13,6 @@ package org.dromara.hutool.core.date; import org.dromara.hutool.core.date.format.FastDateFormat; -import org.dromara.hutool.core.lang.Console; import org.dromara.hutool.core.util.RandomUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -120,7 +119,7 @@ public class DateUtilTest { @Test public void endOfDayTest() { - final DateTime parse = DateUtil.parse("2020-05-31 00:00:00"); + final Date parse = DateUtil.parse("2020-05-31 00:00:00"); Assertions.assertEquals("2020-05-31 23:59:59", DateUtil.endOfDay(parse).toString()); } @@ -335,7 +334,7 @@ public class DateUtilTest { @Test public void weekOfYearTest() { // 第一周周日 - final int weekOfYear1 = DateUtil.weekOfYear(DateUtil.parse("2016-01-03")); + final int weekOfYear1 = DateUtil.weekOfYear(DateUtil.date(DateUtil.parse("2016-01-03").getTime())); Assertions.assertEquals(1, weekOfYear1); // 第二周周四 @@ -461,7 +460,7 @@ public class DateUtilTest { final String str = "2020-06-28T02:14:13.000Z"; final DateTime dateTime = DateUtil.parse(str); assert dateTime != null; - Assertions.assertEquals("2020-06-28 02:14:13", dateTime.toString()); + Assertions.assertEquals("2020-06-28 10:14:13", dateTime.toString()); } /** @@ -496,8 +495,7 @@ public class DateUtilTest { @Test public void parseEmptyTest() { final String str = " "; - final DateTime dateTime = DateUtil.parse(str); - Assertions.assertNull(dateTime); + Assertions.assertThrows(IllegalArgumentException.class, () -> DateUtil.parse(str)); } @Test @@ -520,10 +518,10 @@ public class DateUtilTest { final String str = "2019-09-17T13:26:17.948Z"; final DateTime dateTime = DateUtil.parse(str); assert dateTime != null; - Assertions.assertEquals("2019-09-17 13:26:17", dateTime.toString()); + Assertions.assertEquals("2019-09-17 21:26:17", dateTime.toString()); final DateTime offset = DateUtil.offsetHour(dateTime, 8); - Assertions.assertEquals("2019-09-17 21:26:17", offset.toString()); + Assertions.assertEquals("2019-09-18 05:26:17", offset.toString()); } @Test @@ -613,7 +611,7 @@ public class DateUtilTest { // 默认使用Pattern对应的时区,即UTC时区 String dateStr = Objects.requireNonNull(dt).toString(); - Assertions.assertEquals("2018-09-13 05:34:31", dateStr); + Assertions.assertEquals("2018-09-13 13:34:31", dateStr); // 使用当前(上海)时区 dateStr = dt.toString(TimeZone.getTimeZone("GMT+8:00")); @@ -679,15 +677,15 @@ public class DateUtilTest { // 检查不同毫秒长度都可以正常匹配 String utcTime = "2021-03-30T12:56:51.3Z"; DateTime parse = DateUtil.parse(utcTime); - Assertions.assertEquals("2021-03-30 12:56:51", Objects.requireNonNull(parse).toString()); + Assertions.assertEquals("2021-03-30 20:56:51", Objects.requireNonNull(parse).toString()); utcTime = "2021-03-30T12:56:51.34Z"; parse = DateUtil.parse(utcTime); - Assertions.assertEquals("2021-03-30 12:56:51", Objects.requireNonNull(parse).toString()); + Assertions.assertEquals("2021-03-30 20:56:51", Objects.requireNonNull(parse).toString()); utcTime = "2021-03-30T12:56:51.345Z"; parse = DateUtil.parse(utcTime); - Assertions.assertEquals("2021-03-30 12:56:51", Objects.requireNonNull(parse).toString()); + Assertions.assertEquals("2021-03-30 20:56:51", Objects.requireNonNull(parse).toString()); } @Test @@ -701,17 +699,14 @@ public class DateUtilTest { @Test public void parseCSTTest() { - final String dateStr = "Wed Sep 16 11:26:23 CST 2009"; + final String dateStr = "Wed Sep 16 11:26:23 +0800 2009"; final SimpleDateFormat sdf = new SimpleDateFormat(DatePattern.JDK_DATETIME_PATTERN, Locale.US); // Asia/Shanghai是以地区命名的地区标准时,在中国叫CST,因此如果解析CST时不使用"Asia/Shanghai"而使用"GMT+08:00",会导致相差一个小时 sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); final DateTime parse = DateUtil.parse(dateStr, sdf); - DateTime dateTime = DateUtil.parse(dateStr); - Assertions.assertEquals(parse, dateTime); - - dateTime = DateUtil.parse(dateStr); + final DateTime dateTime = DateUtil.parse(dateStr); Assertions.assertEquals(parse, dateTime); } @@ -740,7 +735,7 @@ public class DateUtilTest { public void parseISOTest() { final String dateStr = "2020-04-23T02:31:00.000Z"; final DateTime time = DateUtil.parse(dateStr); - Assertions.assertEquals("2020-04-23 02:31:00", Objects.requireNonNull(time).toString()); + Assertions.assertEquals("2020-04-23 10:31:00", Objects.requireNonNull(time).toString()); } @Test @@ -1041,12 +1036,6 @@ public class DateUtilTest { Assertions.assertTrue(isSameWeek2); } - @Test - public void parseTimeTest(){ - final DateTime dateTime = DateUtil.parse("12:23:34"); - Console.log(dateTime); - } - @Test @SuppressWarnings("ConstantConditions") public void isOverlapTest() { @@ -1114,7 +1103,7 @@ public class DateUtilTest { final String dateStr2 = "2023-02-07T00:02:16.12345-08:00"; final DateTime dateTime2 = DateUtil.parse(dateStr2); Assertions.assertNotNull(dateTime2); - Assertions.assertEquals("2023-02-07 00:02:16", dateTime2.toString()); + Assertions.assertEquals("2023-02-07 16:02:16", dateTime2.toString()); final String dateStr3 = "2021-03-17T06:31:33.9999"; final DateTime dateTime3 = DateUtil.parse(dateStr3); @@ -1137,7 +1126,7 @@ public class DateUtilTest { void issueI7H34NTest() { final DateTime parse = DateUtil.parse("2019-10-22T09:56:03.000123Z"); Assertions.assertNotNull(parse); - Assertions.assertEquals("2019-10-22 09:56:03", parse.toString()); + Assertions.assertEquals("2019-10-22 17:56:03", parse.toString()); } @Test diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/date/Issue2612Test.java b/hutool-core/src/test/java/org/dromara/hutool/core/date/Issue2612Test.java index c2099cd01..1a80e762c 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/date/Issue2612Test.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/date/Issue2612Test.java @@ -21,10 +21,10 @@ public class Issue2612Test { @Test public void parseTest(){ - Assertions.assertEquals("2022-09-14 23:59:00", + Assertions.assertEquals("2022-09-15 15:59:00", Objects.requireNonNull(DateUtil.parse("2022-09-14T23:59:00-08:00")).toString()); - Assertions.assertEquals("2022-09-14 23:59:00", + Assertions.assertEquals("2022-09-15 15:59:00", Objects.requireNonNull(DateUtil.parse("2022-09-14T23:59:00-0800")).toString()); } } diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI7QI6RTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI7QI6RTest.java index 9b15c26f3..51ad76010 100755 --- a/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI7QI6RTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI7QI6RTest.java @@ -12,14 +12,19 @@ package org.dromara.hutool.core.date; +import org.dromara.hutool.core.lang.Console; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; public class IssueI7QI6RTest { + // TODO 2023-8-4-1解析错误解决 @Test + @Disabled void parseTest() { - Assertions.assertThrows(DateException.class, ()-> DateUtil.parse("2023-8-4-1")); - Assertions.assertThrows(DateException.class, ()-> DateUtil.parse("2023-8-4 1")); + Console.log(DateUtil.parse("2023-8-4-1")); + Assertions.assertThrows(DateException.class, () -> DateUtil.parse("2023-8-4-1")); + Assertions.assertThrows(DateException.class, () -> DateUtil.parse("2023-8-4 1")); } } diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI82Y1LTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI82Y1LTest.java index 05353c8d8..27787cef2 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI82Y1LTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI82Y1LTest.java @@ -21,6 +21,6 @@ public class IssueI82Y1LTest { @Test public void parseTest() { final String dt1 = "2023-09-14T05:00:03.648519Z"; - Assertions.assertEquals("2023-09-14 05:10:51", Objects.requireNonNull(DateUtil.parse(dt1)).toString()); + Assertions.assertEquals("2023-09-14 13:00:03", Objects.requireNonNull(DateUtil.parse(dt1)).toString()); } } diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI8IUTBTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI8IUTBTest.java index f9e723c04..7c4f659b8 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI8IUTBTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI8IUTBTest.java @@ -1,14 +1,14 @@ package org.dromara.hutool.core.date; import org.dromara.hutool.core.lang.Console; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import java.util.Date; + public class IssueI8IUTBTest { @Test - @Disabled void parseTest() { - final DateTime parse = DateUtil.parse("May 8, 2009 5:57:51 PM"); + final Date parse = DateUtil.parse("May 8, 2009 5:57:51 PM"); Console.log(parse); } } diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI9C2D4Test.java b/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI9C2D4Test.java index 2a708c1ee..e0d4c647e 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI9C2D4Test.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI9C2D4Test.java @@ -3,34 +3,35 @@ package org.dromara.hutool.core.date; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.Date; import java.util.Objects; public class IssueI9C2D4Test { @Test public void parseHttpTest() { final String dateStr = "Thu, 28 Mar 2024 14:33:49 GMT"; - final DateTime parse = DateUtil.parse(dateStr); - Assertions.assertEquals("2024-03-28 14:33:49", Objects.requireNonNull(parse).toString()); + final Date parse = DateUtil.parse(dateStr); + Assertions.assertEquals("2024-03-28 22:33:49", Objects.requireNonNull(parse).toString()); } @Test public void parseHttpTest2() { final String dateStr = "星期四, 28 三月 2024 14:33:49 GMT"; - final DateTime parse = DateUtil.parse(dateStr); + final Date parse = DateUtil.parse(dateStr); Assertions.assertEquals("2024-03-28 14:33:49", Objects.requireNonNull(parse).toString()); } @Test public void parseTimeTest() { final String dateStr = "15时45分59秒"; - final DateTime parse = DateUtil.parse(dateStr); + final Date parse = DateUtil.parse(dateStr); Assertions.assertEquals("15:45:59", Objects.requireNonNull(parse).toString().split(" ")[1]); } @Test public void parseTimeTest2() { final String dateStr = "15:45:59"; - final DateTime parse = DateUtil.parse(dateStr); + final Date parse = DateUtil.parse(dateStr); Assertions.assertEquals("15:45:59", Objects.requireNonNull(parse).toString().split(" ")[1]); } } diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/date/TimeUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/date/TimeUtilTest.java index 994b0ff4b..d1545447f 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/date/TimeUtilTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/date/TimeUtilTest.java @@ -19,6 +19,7 @@ import org.junit.jupiter.api.Test; import java.time.*; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; +import java.util.Date; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @@ -36,18 +37,17 @@ public class TimeUtilTest { @Test public void ofTest() { final String dateStr = "2020-01-23T12:23:56"; - final DateTime dt = DateUtil.parse(dateStr); + final Date dt = DateUtil.parse(dateStr); final LocalDateTime of = TimeUtil.of(dt); Assertions.assertNotNull(of); assertEquals(dateStr, of.toString()); } - @SuppressWarnings("DataFlowIssue") @Test public void ofUTCTest() { final String dateStr = "2020-01-23T12:23:56Z"; - final DateTime dt = DateUtil.parse(dateStr); + final Date dt = DateUtil.parse(dateStr); final LocalDateTime of = TimeUtil.of(dt); final LocalDateTime of2 = TimeUtil.ofUTC(dt.getTime()); diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/date/ZoneUtilTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/date/ZoneUtilTest.java index 0d522cd1e..9d43dd5c9 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/date/ZoneUtilTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/date/ZoneUtilTest.java @@ -17,6 +17,9 @@ import org.junit.jupiter.api.Test; import java.time.ZoneId; import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.*; public class ZoneUtilTest { @@ -25,4 +28,53 @@ public class ZoneUtilTest { Assertions.assertEquals(ZoneId.systemDefault(), ZoneUtil.toZoneId(null)); Assertions.assertEquals(TimeZone.getDefault(), ZoneUtil.toTimeZone(null)); } + + @Test + public void testGetTimeZoneByOffsetReturnsNonNull() { + // Arrange + final int rawOffset = 8; // Example offset, you may adjust for different cases + final TimeUnit timeUnit = TimeUnit.HOURS; + + // Act + final TimeZone result = ZoneUtil.getTimeZoneByOffset(rawOffset, timeUnit); + + // Assert + assertNotNull(result, "Expected non-null TimeZone for valid offset and unit"); + } + + @Test + public void testGetTimeZoneByOffsetWithInvalidOffsetReturnsNull() { + // Arrange + final int rawOffset = 999; // Unlikely valid offset to test edge case + final TimeUnit timeUnit = TimeUnit.HOURS; + + // Act + final TimeZone result = ZoneUtil.getTimeZoneByOffset(rawOffset, timeUnit); + + // Assert + assertNull(result, "Expected null TimeZone for invalid offset"); + } + + @Test + public void testGetTimeZoneByOffsetWithNullTimeUnitThrowsException() { + // Arrange + final int rawOffset = 8; + final TimeUnit timeUnit = null; // Null unit to simulate error condition + + // Act & Assert + final NullPointerException thrown = assertThrows( + NullPointerException.class, + () -> ZoneUtil.getTimeZoneByOffset(rawOffset, timeUnit), + "Expected NullPointerException for null TimeUnit" + ); + assertTrue(thrown.getMessage().contains("timeUnit"), "Exception message should mention the null parameter"); + } + + @Test + public void testGetAvailableIDWithInvalidOffset() { + // Test with an invalid offset that should result in null or an exception. + // Assuming that an offset of 25 hours is invalid and should return null. + final String result = ZoneUtil.getAvailableID(25, TimeUnit.HOURS); + assertNull(result, "Expected null for invalid offset of 25 hours"); + } } diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/date/format/parser/GlobalRegexDateParserTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/date/format/parser/GlobalRegexDateParserTest.java index 9e164d101..d288f3050 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/date/format/parser/GlobalRegexDateParserTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/date/format/parser/GlobalRegexDateParserTest.java @@ -12,6 +12,69 @@ import static org.junit.jupiter.api.Assertions.assertEquals; */ public class GlobalRegexDateParserTest { + @Test + void parseYearMonthDaySplitByDashedTest() { + assertParse("2013-02-03 00:00:00", "2013-Feb-03"); + assertParse("2013-02-03 00:00:00", "2013-02-03"); + assertParse("2013-02-03 00:00:00", "2013-2-03"); + assertParse("2013-02-03 00:00:00", "2013-2-3"); + assertParse("2014-04-26 00:00:00", "2014-04-26"); + assertParse("2014-04-01 00:00:00", "2014-04"); + + assertParse("2013-04-01 22:43:00", "2013-04-01 22:43"); + assertParse("2013-04-01 22:43:22", "2013-04-01 22:43:22"); + assertParse("2014-04-26 17:24:37", "2014-04-26 17:24:37.3186369"); + assertParse("2012-08-03 18:31:59", "2012-08-03 18:31:59.257000000"); + assertParse("2014-04-26 17:24:37", "2014-04-26 17:24:37.123"); + assertParse("2014-12-16 14:20:00", "2014-12-16 06:20:00 UTC"); + assertParse("2014-12-16 14:20:00", "2014-12-16 06:20:00 GMT"); + assertParse("2014-04-26 17:24:37", "2014-04-26 05:24:37 PM"); + assertParse("2014-04-26 13:13:43", "2014-04-26 13:13:43 +0800"); + assertParse("2014-04-26 12:13:44", "2014-04-26 13:13:44 +09:00"); + assertParse("2014-04-26 13:13:43", "2014-04-26 13:13:43 +0800 +08"); + assertParse("2012-08-04 02:31:59", "2012-08-03 18:31:59.257000000 +0000 UTC"); + assertParse("2015-10-01 02:48:56", "2015-09-30 18:48:56.35272715 +0000 UTC"); + assertParse("2015-02-18 08:12:00", "2015-02-18 00:12:00 +0000 GMT"); + assertParse("2015-02-18 08:12:00", "2015-02-18 00:12:00 +0000 UTC"); + assertParse("2017-07-19 11:21:51", "2017-07-19 03:21:51+00:00"); + assertParse("2014-05-11 08:20:13", "2014-05-11 08:20:13,787"); + } + + @Test + void parseYearMonthDaySplitByDashedWithMaskTest() { + assertParse("2015-02-08 08:02:00", "2015-02-08 03:02:00 +0300 MSK m=+0.000000001"); + assertParse("2015-02-08 08:02:00", "2015-02-08 03:02:00.001 +0300 MSK m=+0.000000001"); + } + + @Test + void parseYearMonthDaySplitBySlashTest() { + assertParse("2014-03-31 00:00:00", "2014/3/31"); + assertParse("2014-03-31 00:00:00", "2014/03/31"); + assertParse("2014-04-08 22:05:00", "2014/4/8 22:05"); + assertParse("2014-04-08 22:05:00", "2014/04/08 22:05"); + assertParse("2014-04-02 03:00:51", "2014/04/2 03:00:51"); + assertParse("2014-04-02 03:00:51", "2014/4/02 03:00:51"); + assertParse("2012-03-19 10:11:59", "2012/03/19 10:11:59"); + assertParse("2012-03-19 10:11:59", "2012/03/19 10:11:59.3186369"); + } + + @Test + void parseYearMonthDaySplitByChineseTest() { + assertParse("2014-04-08 00:00:00", "2014年04月08日"); + assertParse("2017-02-01 12:23:45", "2017年02月01日 12时23分45秒"); + assertParse("2017-02-01 12:23:45", "2017年02月01日 12:23:45"); + } + + @Test + void parseYearMonthDaySplitByTTest() { + assertParse("2006-01-02 23:04:05", "2006-01-02T15:04:05+0000"); + assertParse("2009-08-13 13:15:09", "2009-08-12T22:15:09-07:00"); + assertParse("2009-08-12 22:15:09", "2009-08-12T22:15:09+0800"); + assertParse("2009-08-12 22:15:09", "2009-08-12T22:15:09+08"); + assertParse("2009-08-12 22:15:09", "2009-08-12T22:15:09"); + assertParse("2009-08-13 06:15:09", "2009-08-12T22:15:09Z");// Z表示UTC时间 + } + @Test void parseMonthDayYearTest() { assertParse("1970-10-07 00:00:00", "oct 7, 1970"); @@ -21,6 +84,7 @@ public class GlobalRegexDateParserTest { assertParse("1970-10-07 00:00:00", "October 7, 1970"); assertParse("1970-10-07 00:00:00", "October 7th, 1970"); assertParse("1970-10-07 00:00:00", "October 7th, 1970"); + assertParse("1970-05-07 00:00:00", "May 7th, 1970"); } @Test @@ -51,15 +115,15 @@ public class GlobalRegexDateParserTest { @Test void parseWeekMonthDayTimeYearTest() { assertParse("2006-01-02 15:04:05", "Mon Jan 2 15:04:05 2006"); - assertParse("2006-01-02 15:04:05", "Mon Jan 2 15:04:05 MST 2006"); + assertParse("2006-01-02 23:04:05", "Mon Jan 2 15:04:05 MST 2006"); assertParse("2006-01-03 06:04:05", "Mon Jan 02 15:04:05 -0700 2006"); assertParse("2015-08-10 22:44:11", "Mon Aug 10 15:44:11 UTC+0100 2015"); } @Test void parseWeekDayMonthYearTimeTest() { - assertParse("2006-01-02 15:04:05", "Monday, 02-Jan-06 15:04:05 MST"); - assertParse("2006-01-02 15:04:05", "Mon, 02 Jan 2006 15:04:05 MST"); + assertParse("2006-01-02 23:04:05", "Monday, 02-Jan-06 15:04:05 MST"); + assertParse("2006-01-02 23:04:05", "Mon, 02 Jan 2006 15:04:05 MST"); assertParse("2017-07-11 22:28:13", "Tue, 11 Jul 2017 16:28:13 +0200"); assertParse("2017-07-11 22:28:13", "Tue, 11 Jul 2017 16:28:13 +0200 (CEST)"); assertParse("2006-01-03 06:04:05", "Mon, 02 Jan 2006 15:04:05 -0700"); @@ -67,7 +131,7 @@ public class GlobalRegexDateParserTest { } @Test - void parseDayMonthYear() { + void parseDayMonthYearTest() { assertParse("2006-02-12 19:17:00", "12 Feb 2006, 19:17"); assertParse("2006-02-12 19:17:00", "12 Feb 2006 19:17"); assertParse("1970-10-07 00:00:00", "7 oct 70"); @@ -76,6 +140,45 @@ public class GlobalRegexDateParserTest { assertParse("2013-07-01 00:00:00", "1 July 2013"); } + @Test + void parseDayOrMonthTest() { + assertParse("2014-03-03 00:00:00", "3.3.2014"); + // 自动识别月在前 + assertParse("2014-03-31 00:00:00", "3/31/2014"); + assertParse("2014-03-31 00:00:00", "3.31.2014"); + assertParse("2014-03-31 00:00:00", "03/31/2014"); + assertParse("2014-03-31 00:00:00", "03.31.2014"); + assertParse("2014-08-04 22:05:00", "4/8/2014 22:05"); + assertParse("2014-08-04 22:05:00", "4/8/2014 22:05"); + assertParse("2014-08-04 22:05:00", "04/08/2014 22:05"); + assertParse("2014-02-04 03:00:51", "04/2/2014 03:00:51"); + assertParse("1965-08-08 00:00:00", "8/8/1965 12:00:00 AM"); + assertParse("1965-08-08 12:00:00", "8/8/1965 12:00:00 PM"); + assertParse("1965-08-08 13:00:00", "8/8/1965 01:00 PM"); + assertParse("1965-08-08 13:00:00", "8/8/1965 1:00 PM"); + assertParse("1965-08-08 00:00:00", "8/8/1965 12:00 AM"); + assertParse("2014-02-04 03:00:51", "4/02/2014 03:00:51"); + assertParse("2012-03-19 10:11:59", "03/19/2012 10:11:59"); + assertParse("2012-03-19 10:11:59", "03/19/2012 10:11:59.3186369"); + } + + @Test + void parsePureNumberTest() { + assertParse("2014-01-01 00:00:00", "2014"); + assertParse("2014-06-01 00:00:00", "201406"); + assertParse("2014-06-02 00:00:00", "20140602"); + assertParse("2014-07-22 10:52:03", "20140722105203"); + + // unixtime(10) + assertParse("2012-03-19 18:11:59", "1332151919"); + // millisecond(13) + assertParse("2013-11-12 08:32:47", "1384216367189"); + // microsecond(16) + assertParse("2013-11-12 08:32:47", "1384216367111222"); + // nanosecond(19) + assertParse("2013-11-12 08:32:47", "1384216367111222333"); + } + private static void assertParse(final String dateStr, final String dateStrToParse) { final Date date = GlobalRegexDateParser.parse(dateStrToParse); assertEquals(dateStr, DateUtil.date(date).toString()); diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/lang/range/RangeTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/lang/range/RangeTest.java index 02d28fdaf..4aa783f00 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/lang/range/RangeTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/lang/range/RangeTest.java @@ -20,6 +20,7 @@ import org.dromara.hutool.core.text.StrUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.Date; import java.util.List; import java.util.NoSuchElementException; @@ -32,8 +33,8 @@ public class RangeTest { @Test public void dateRangeTest() { - final DateTime start = DateUtil.parse("2017-01-01"); - final DateTime end = DateUtil.parse("2017-01-02"); + final DateTime start = DateUtil.parseDateTime("2017-01-01"); + final DateTime end = DateUtil.parseDateTime("2017-01-02"); final Range range = new Range<>(start, end, (current, end1, index) -> { if (current.isAfterOrEquals(end1)) { @@ -51,8 +52,8 @@ public class RangeTest { @Test public void dateRangeFuncTest() { - final DateTime start = DateUtil.parse("2021-01-01"); - final DateTime end = DateUtil.parse("2021-01-03"); + final Date start = DateUtil.parse("2021-01-01"); + final Date end = DateUtil.parse("2021-01-03"); final List dayOfMonthList = DateUtil.rangeFunc(start, end, DateField.DAY_OF_YEAR, a -> DateTime.of(a).dayOfMonth()); Assertions.assertArrayEquals(dayOfMonthList.toArray(new Integer[]{}), new Integer[]{1, 2, 3}); @@ -63,8 +64,8 @@ public class RangeTest { @Test public void dateRangeConsumeTest() { - final DateTime start = DateUtil.parse("2021-01-01"); - final DateTime end = DateUtil.parse("2021-01-03"); + final Date start = DateUtil.parse("2021-01-01"); + final Date end = DateUtil.parse("2021-01-03"); final StringBuilder sb = new StringBuilder(); DateUtil.rangeConsume(start, end, DateField.DAY_OF_YEAR, a -> sb.append(DateTime.of(a).dayOfMonth()).append("#")); @@ -77,8 +78,8 @@ public class RangeTest { @Test public void dateRangeTest2() { - final DateTime start = DateUtil.parse("2021-01-31"); - final DateTime end = DateUtil.parse("2021-03-31"); + final Date start = DateUtil.parse("2021-01-31"); + final Date end = DateUtil.parse("2021-03-31"); final DateRange range = DateUtil.range(start, end, DateField.MONTH); @@ -102,8 +103,8 @@ public class RangeTest { @Test public void rangeByStepTest() { - final DateTime start = DateUtil.parse("2017-01-01"); - final DateTime end = DateUtil.parse("2017-01-03"); + final Date start = DateUtil.parse("2017-01-01"); + final Date end = DateUtil.parse("2017-01-03"); // 测试包含开始和结束情况下步进为1的情况 DateRange range = DateUtil.range(start, end, DateField.DAY_OF_YEAR); @@ -124,8 +125,8 @@ public class RangeTest { @Test public void rangeDayOfYearTest() { - final DateTime start = DateUtil.parse("2017-01-01"); - final DateTime end = DateUtil.parse("2017-01-05"); + final Date start = DateUtil.parse("2017-01-01"); + final Date end = DateUtil.parse("2017-01-05"); // 测试不包含开始结束时间的情况 final DateRange range = new DateRange(start, end, DateField.DAY_OF_YEAR, 1, false, false); @@ -141,8 +142,8 @@ public class RangeTest { @Test public void rangeToListTest() { - final DateTime start = DateUtil.parse("2017-01-01"); - final DateTime end = DateUtil.parse("2017-01-31"); + final Date start = DateUtil.parse("2017-01-01"); + final Date end = DateUtil.parse("2017-01-31"); final List rangeToList = DateUtil.rangeToList(start, end, DateField.DAY_OF_YEAR); Assertions.assertEquals(DateUtil.parse("2017-01-01"), rangeToList.get(0)); @@ -153,12 +154,12 @@ public class RangeTest { @Test public void rangeContains() { // 开始区间 - final DateTime start = DateUtil.parse("2017-01-01"); - final DateTime end = DateUtil.parse("2017-01-31"); + final Date start = DateUtil.parse("2017-01-01"); + final Date end = DateUtil.parse("2017-01-31"); final DateRange startRange = DateUtil.range(start, end, DateField.DAY_OF_YEAR); // 结束区间 - final DateTime start1 = DateUtil.parse("2017-01-31"); - final DateTime end1 = DateUtil.parse("2017-02-02"); + final Date start1 = DateUtil.parse("2017-01-31"); + final Date end1 = DateUtil.parse("2017-02-02"); final DateRange endRange = DateUtil.range(start1, end1, DateField.DAY_OF_YEAR); // 交集 final List dateTimes = DateUtil.rangeContains(startRange, endRange); @@ -169,12 +170,12 @@ public class RangeTest { @Test public void rangeNotContains() { // 开始区间 - final DateTime start = DateUtil.parse("2017-01-01"); - final DateTime end = DateUtil.parse("2017-01-30"); + final Date start = DateUtil.parse("2017-01-01"); + final Date end = DateUtil.parse("2017-01-30"); final DateRange startRange = DateUtil.range(start, end, DateField.DAY_OF_YEAR); // 结束区间 - final DateTime start1 = DateUtil.parse("2017-01-01"); - final DateTime end1 = DateUtil.parse("2017-01-31"); + final Date start1 = DateUtil.parse("2017-01-01"); + final Date end1 = DateUtil.parse("2017-01-31"); final DateRange endRange = DateUtil.range(start1, end1, DateField.DAY_OF_YEAR); // 差集 final List dateTimes1 = DateUtil.rangeNotContains(startRange, endRange);