This commit is contained in:
Looly 2024-06-27 18:36:37 +08:00
parent e56e10b75c
commit 85fac17eac
5 changed files with 129 additions and 23 deletions

View File

@ -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);

View File

@ -24,33 +24,41 @@ import java.util.regex.Pattern;
*/
public class GlobalRegexDateParser {
private static final String yearRegex = "(?<year>\\d{2,4})";
private static final String monthRegex = "(?<month>\\w{3,9})";
private static final String dayRegex = "(?<day>\\d{1,2})(?:th)?";
// 周的正则匹配Mon, Tue, Wed, Thu, Fri, Sat, Sun Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
// 日期中一般出现在头部可选
private static final String weekRegex = "(?<week>[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(?<hour>\\d{1,2})" +
"\\W+(?<hour>\\d{1,2})" +
":(?<minute>\\d{1,2})" +
"(:(?<second>\\d{1,2}))?" +
"(?:[.,](?<ns>\\d{1,9}))?(?<zero>z)?" +
"(\\s?(?<m>am|pm))?" +
"(\\s?(?<m>[ap]m))?" +
")?";
// +08:00 +0800 +08
// 时区类似 +08:00 +0800 +08可选
private static final String zoneOffsetRegex = "(\\s?(?<zoneOffset>[-+]\\d{1,2}:?(?:\\d{2})?))?";
// CST UTC (CST)
// 时区名称类似 CST UTC (CST)可选
private static final String zoneNameRegex = "(\\s[(]?(?<zoneName>[a-z ]+)[)]?)?";
private static final RegexDateParser PARSER;
static {
final String dateRegexMonthFirst = "(?<month>\\w+{3,9})\\W+(?<day>\\d{1,2})(?:th)?\\W+(?<year>\\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:515: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
// 日开头
);
}

View File

@ -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) {

View File

@ -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());
}
}

View File

@ -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 = "(?<week>[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