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 9ca129387..db7c823db 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 @@ -53,6 +53,8 @@ public final class DateBuilder { private int ns; // Unix时间戳(秒) private long unixsecond; + // 时间戳(毫秒) + private long millisecond; // 时区偏移量是否已设置 private boolean zoneOffsetSetted; // 时区偏移量(分钟) @@ -247,6 +249,26 @@ public final class DateBuilder { return this; } + /** + * 获取时间戳(毫秒)。 + * + * @return 当前对象的时间戳(以毫秒为单位)。 + */ + public long getMillisecond() { + return millisecond; + } + + /** + * 设置时间戳(毫秒)。 + * + * @param millisecond 要设置的时间戳(以毫秒为单位)。 + * @return this + */ + public DateBuilder setMillisecond(final long millisecond) { + this.millisecond = millisecond; + return this; + } + /** * 检查时区偏移量是否已设置。 * @@ -363,6 +385,7 @@ public final class DateBuilder { this.second = 0; this.ns = 0; this.unixsecond = 0; + this.millisecond = 0; this.am = false; this.pm = false; this.zoneOffsetSetted = false; @@ -403,12 +426,6 @@ public final class DateBuilder { this.prepare(); final Calendar calendar = Calendar.getInstance(); // 获取一个Calendar实例 - // 如果有unix时间戳,则据此设置时间 - if (unixsecond != 0) { - calendar.setTimeInMillis(unixsecond * 1000 + ns / 1_000_000); // 设置时间戳对应的毫秒数 - return calendar; - } - // 设置时区 if (zone != null) { calendar.setTimeZone(zone); // 使用指定的时区 @@ -421,6 +438,18 @@ public final class DateBuilder { calendar.setTimeZone(TimeZone.getTimeZone(ids[0])); // 设置第一个找到的时区 } + // 如果毫秒数不为0,则直接使用毫秒数设置时间 + if(millisecond != 0){ + calendar.setTimeInMillis(millisecond); + return calendar; + } + + // 如果有unix时间戳,则据此设置时间 + if (unixsecond != 0) { + calendar.setTimeInMillis(unixsecond * 1000 + ns / 1_000_000); // 设置时间戳对应的毫秒数 + return calendar; + } + // 设置日期和时间字段 calendar.set(Calendar.YEAR, year); calendar.set(Calendar.MONTH, month - 1); // Calendar的月份从0开始 @@ -443,6 +472,11 @@ public final class DateBuilder { LocalDateTime toLocalDateTime() { this.prepare(); + if(millisecond > 0){ + final Instant instant = Instant.ofEpochMilli(millisecond); + return LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), DEFAULT_OFFSET); + } + // 如果unixsecond大于0,使用unix时间戳创建LocalDateTime if (unixsecond > 0) { return LocalDateTime.ofEpochSecond(unixsecond, ns, DEFAULT_OFFSET); @@ -480,6 +514,10 @@ public final class DateBuilder { OffsetDateTime toOffsetDateTime() { this.prepare(); // 准备工作,可能涉及一些初始化或数据处理 + if(millisecond > 0){ + return OffsetDateTime.ofInstant(Instant.ofEpochMilli(millisecond), ZoneUtil.ZONE_ID_UTC); + } + if (unixsecond > 0) { // 如果设置了 unix 时间戳,则使用它和纳秒创建 UTC 时间 return OffsetDateTime.ofInstant(Instant.ofEpochSecond(unixsecond, ns), ZoneUtil.ZONE_ID_UTC); 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 de4dcced1..a0ccdd1e3 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 @@ -24,33 +24,41 @@ 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 weekRegex = "(?[mwfts][oeruha][ndieut](\\w{3,6})?\\W+)?"; // hh:mm:ss.SSSSZ hh:mm:ss.SSSS hh:mm:ss hh:mm private static final String timeRegex = "(" + - "\\s(?\\d{1,2})" + + "\\W+(?\\d{1,2})" + ":(?\\d{1,2})" + "(:(?\\d{1,2}))?" + "(?:[.,](?\\d{1,9}))?(?z)?" + - "(\\s?(?am|pm))?" + + "(\\s?(?[ap]m))?" + ")?"; - // +08:00 +0800 +08 + // 时区,类似: +08:00 +0800 +08,可选 private static final String zoneOffsetRegex = "(\\s?(?[-+]\\d{1,2}:?(?:\\d{2})?))?"; - // CST UTC (CST) + // 时区名称,类似: CST UTC (CST),可选 private static final String zoneNameRegex = "(\\s[(]?(?[a-z ]+)[)]?)?"; private static final RegexDateParser PARSER; static { - final String dateRegexMonthFirst = "(?\\w+{3,9})\\W+(?\\d{1,2})(?:th)?\\W+(?\\d{2,4})"; + // 月开头,类似:May 8 + final String dateRegexMonthFirst = monthRegex + "\\W+" + dayRegex; PARSER = RegexDateParser.of( // 年开头 - //月开头,类似:May 8, 2009 5:57:51 - dateRegexMonthFirst + timeRegex + zoneOffsetRegex - - // 周开头 + //月在前,类似:May 8, 2009,时间部分可选,类似:5:57:51,5:57:51 +08:00 + weekRegex + dateRegexMonthFirst + "\\W+" + yearRegex + timeRegex + zoneOffsetRegex, + // 年在最后,类似:Mon Jan 2 15:04:05 MST 2006 + weekRegex + dateRegexMonthFirst + timeRegex + zoneNameRegex + zoneOffsetRegex + "\\W+" + yearRegex // 日开头 + ); } 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 9abee1378..5ab71aa9f 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 @@ -16,6 +16,7 @@ import org.dromara.hutool.core.collection.ListUtil; import org.dromara.hutool.core.date.*; 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 java.io.Serializable; @@ -95,11 +96,13 @@ public class RegexDateParser implements DateParser, Serializable { @Override public Date parse(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()) { - return parse(matcher); + parse(matcher, dateBuilder); + return dateBuilder.toDate(); } } @@ -110,17 +113,16 @@ public class RegexDateParser implements DateParser, Serializable { * 解析日期 * * @param matcher 正则匹配器 - * @return 日期 * @throws DateException 日期解析异常 */ - private static Date parse(final Matcher matcher) throws DateException { + private static void parse(final Matcher matcher, final DateBuilder dateBuilder) throws DateException { // 毫秒时间戳 final String millisecond = ReUtil.group(matcher, "millisecond"); if (StrUtil.isNotEmpty(millisecond)) { - return DateUtil.date(parseLong(millisecond)); + dateBuilder.setMillisecond(parseLong(millisecond)); + return; } - final DateBuilder dateBuilder = DateBuilder.of(); // year Opt.ofNullable(ReUtil.group(matcher, "year")).ifPresent((year) -> dateBuilder.setYear(parseYear(year))); // month @@ -139,7 +141,7 @@ public class RegexDateParser implements DateParser, Serializable { Opt.ofNullable(ReUtil.group(matcher, "ns")).ifPresent((ns) -> dateBuilder.setNs(parseNano(ns))); // am or pm Opt.ofNullable(ReUtil.group(matcher, "m")).ifPresent((m) -> { - if ('p' == m.charAt(0)) { + if (CharUtil.equals('p', m.charAt(0), true)) { dateBuilder.setPm(true); } else { dateBuilder.setAm(true); @@ -158,12 +160,15 @@ public class RegexDateParser implements DateParser, Serializable { dateBuilder.setZoneOffset(parseZoneOffset(zoneOffset)); }); + // zone name + Opt.ofNullable(ReUtil.group(matcher, "zoneName")).ifPresent((zoneOffset) -> { + // 暂时不支持解析 + }); + // unix时间戳 Opt.ofNullable(ReUtil.group(matcher, "unixsecond")).ifPresent((unixsecond) -> { dateBuilder.setUnixsecond(parseLong(unixsecond)); }); - - return dateBuilder.toDate(); } private static int parseYear(final String year) { 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 new file mode 100644 index 000000000..926440b11 --- /dev/null +++ b/hutool-core/src/test/java/org/dromara/hutool/core/date/format/parser/GlobalRegexDateParserTest.java @@ -0,0 +1,45 @@ +package org.dromara.hutool.core.date.format.parser; + +import org.dromara.hutool.core.date.DateUtil; +import org.junit.jupiter.api.Test; + +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class GlobalRegexDateParserTest { + + @Test + void parseMonthFirstTest() { + assertParse("2009-05-08 05:57:51", "May 8, 2009 5:57:51"); + assertParse("2009-05-08 17:57:51", "May 8, 2009 5:57:51 PM"); + assertParse("2009-05-08 17:57:51", "May 8, 2009 5:57:51 pm"); + assertParse("2009-05-08 17:57:51", "May 8, 2009 5:57:51pm"); + assertParse("2009-05-08 05:57:51", "May 8, 2009 5:57:51 +08:00"); + assertParse("2009-05-08 05:57:51", "May 8, 2009 5:57:51 +0800"); + assertParse("2009-05-08 05:57:51", "May 8, 2009 5:57:51 +08"); + assertParse("2009-05-08 00:00:00", "May 8, 2009"); + assertParse("2009-05-08 00:00:00", "May 8th, 2009"); + assertParse("2009-05-08 00:00:00", "May 8th, 09"); + assertParse("2009-05-08 00:00:00", "may. 8th, 09"); + } + + @Test + void parseWeekFirstTest() { + assertParse("2006-01-02 15:04:05", "Mon Jan 2, 2006 15:04:05"); + assertParse("2006-01-02 15:04:05", "Mon Jan 2 2006 15:04:05"); + } + + @Test + void parseWeekFirstYearLastTest() { + 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-03 06:04:05", "Mon Jan 02 15:04:05 -0700 2006"); + //assertParse("2006-01-03 06:04:05", "Monday, 02-Jan-06 15:04:05 MST"); + } + + 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/date/format/parser/RegexDateParserTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/date/format/parser/RegexDateParserTest.java index 580d61ef9..b523b61a0 100644 --- a/hutool-core/src/test/java/org/dromara/hutool/core/date/format/parser/RegexDateParserTest.java +++ b/hutool-core/src/test/java/org/dromara/hutool/core/date/format/parser/RegexDateParserTest.java @@ -1,6 +1,7 @@ package org.dromara.hutool.core.date.format.parser; import org.dromara.hutool.core.date.DateUtil; +import org.dromara.hutool.core.date.Week; import org.dromara.hutool.core.regex.ReUtil; import org.junit.jupiter.api.Test; @@ -59,6 +60,15 @@ public class RegexDateParserTest { assertMatch(zoneNameRegex, " (GMT Daylight Time)"); } + @Test + void weekMatchTest() { + final String weekRegex = "(?[mwfts][oeruha][ndieut](\\w{3,6})?)"; + for (final Week week : Week.values()) { + assertMatch(weekRegex, week.name()); + assertMatch(weekRegex, week.name().substring(0, 3)); + } + } + @Test void parsePureTest() { // yyyyMMdd