This commit is contained in:
Looly 2024-07-02 23:23:25 +08:00
parent 5c60b626cd
commit aa8d66b81a
23 changed files with 584 additions and 163 deletions

View File

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

View File

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

View File

@ -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}类型此方法根据是否设置了时区偏移量使用不同的转换策略
* <ul>
* <li>如果时区偏移量未设置则将时间转换为 Calendar 对象后获取其 DateTime 表现形式</li>
* <li>如果时区偏移量已设置则直接转换为 OffsetDateTime 对象进一步转换为 Instant 对象最后转换为 DateTime 对象返回</li>
* </ul>
*
*
* @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);
}

View File

@ -767,6 +767,39 @@ public class DateUtil extends CalendarUtil {
return date(CalendarUtil.parseByPatterns(str, parsePatterns));
}
/**
* 将日期字符串转换为{@link DateTime}对象格式<br>
* <ol>
* <li>yyyy-MM-dd HH:mm:ss</li>
* <li>yyyy/MM/dd HH:mm:ss</li>
* <li>yyyy.MM.dd HH:mm:ss</li>
* <li>yyyy年MM月dd日 HH时mm分ss秒</li>
* <li>yyyy-MM-dd</li>
* <li>yyyy/MM/dd</li>
* <li>yyyy.MM.dd</li>
* <li>HH:mm:ss</li>
* <li>HH时mm分ss秒</li>
* <li>yyyy-MM-dd HH:mm</li>
* <li>yyyy-MM-dd HH:mm:ss.SSS</li>
* <li>yyyy-MM-dd HH:mm:ss.SSSSSS</li>
* <li>yyyyMMddHHmmss</li>
* <li>yyyyMMddHHmmssSSS</li>
* <li>yyyyMMdd</li>
* <li>EEE, dd MMM yyyy HH:mm:ss z</li>
* <li>EEE MMM dd HH:mm:ss zzz yyyy</li>
* <li>yyyy-MM-dd'T'HH:mm:ss'Z'</li>
* <li>yyyy-MM-dd'T'HH:mm:ss.SSS'Z'</li>
* <li>yyyy-MM-dd'T'HH:mm:ssZ</li>
* <li>yyyy-MM-dd'T'HH:mm:ss.SSSZ</li>
* </ol>
*
* @param dateCharSequence 日期字符串
* @return 日期
*/
public static DateTime parseDateTime(final CharSequence dateCharSequence) {
return date(parse(dateCharSequence));
}
/**
* 将日期字符串转换为{@link DateTime}对象格式<br>
* <ol>
@ -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

View File

@ -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];
}
}

View File

@ -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 = "(?<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 weekRegexWithSuff = "(?<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 timeRegexWithPre = "(" +
"\\W+(at\\s)?(?<hour>\\d{1,2})" +
":(?<minute>\\d{1,2})" +
"(:(?<second>\\d{1,2}))?" +
"(?:[.,](?<ns>\\d{1,9}))?(?<zero>z)?" +
"(\\s?(?<m>[ap]m))?" +
")?";
// 时区类似 +08:00 +0800 +08可选
private static final String zoneOffsetRegexWithPre = "(\\s?(?<zoneOffset>[-+]\\d{1,2}:?(?:\\d{2})?))?";
// 时区名称类似 CST UTC (CST)可选
private static final String zoneNameRegexWithPre = "(\\s[(]?(?<zoneName>[a-z ]+)[)]?)?";
private static final String zoneNameIgnoreRegexWithPre = "(\\s[(]?(?<zoneNameIgnore>[a-z ]+)[)]?)?";
private static final RegexDateParser PARSER;
static {
final String yearRegex = "(?<year>\\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 = "(?<month>[jfmaasond][aepucoe][nbrylgptvc]\\w{0,6})";
final String dayRegex = "(?<day>\\d{1,2})(?:th)?";
// 周的正则匹配Mon, Tue, Wed, Thu, Fri, Sat, Sun
// Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
// 周一般出现在日期头部可选
final String weekRegexWithSuff = "(?<week>[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)?(?<hour>\\d{1,2})" +
"\\W(?<minute>\\d{1,2})" +
"(\\W(?<second>\\d{1,2}))?秒?" +
"(?:[.,](?<ns>\\d{1,9}))?(?<zero>z)?" +
"(\\s?(?<m>[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?(?<zone>"
// 匹配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:5105:57:51 +08:00
yearRegex + "\\W" + dateRegexMonthFirst + timeRegexWithPre + zoneRegex + maskRegex,
//年月日时类似2009-02-08或2014年04月08日时间部分可选类似5:57:5105:57:51 +08:00
yearRegex + "\\W(?<month>\\d{1,2})(\\W(?<day>\\d{1,2}))?日?" + timeRegexWithPre + zoneRegex + maskRegex,
//周月日年时类似May 8, 2009时间部分可选类似5:57:515:57:51 +08:00
weekRegexWithSuff + dateRegexMonthFirst + "\\W+" + yearRegex + timeRegexWithPre + zoneRegex,
//周月日年时类似May 8, 2009时间部分可选类似5:57:5105: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位年
"(?<dayOrMonth>\\d{1,2}\\W\\d{1,2})\\W(?<year>\\d{4})" + timeRegexWithPre + zoneRegex + maskRegex,
//纯数字日期时间
// yyyy
// yyyyMM
// yyyyMMdd
// yyyyMMddhhmmss
// unixtime(10)
// millisecond(13)
// microsecond(16)
// nanosecond(19)
"^(?<number>\\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);
}
/**

View File

@ -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<Pattern> 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'
*

View File

@ -41,6 +41,21 @@ import java.util.function.Predicate;
public class WordTree extends HashMap<Character, WordTree> {
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;
}
/**
* 单词字符末尾标识用于标识单词末尾字符
*/
@ -315,7 +330,7 @@ public class WordTree extends HashMap<Character, WordTree> {
}
// 本次循环结尾加入遗留匹配的单词
if(null != currentFoundWord){
if (null != currentFoundWord) {
foundWords.add(currentFoundWord);
if (limit > 0 && foundWords.size() >= limit) {
//超过匹配限制个数直接返回
@ -356,8 +371,8 @@ public class WordTree extends HashMap<Character, WordTree> {
* @param entry WordTree每个entry节点
* @return 递归扁平化后的结果
*/
private Iterable<String> innerFlatten(Entry<Character, WordTree> entry) {
List<String> list = EasyStream.of(entry.getValue().entrySet()).flat(this::innerFlatten).map(v -> entry.getKey() + v).toList();
private Iterable<String> innerFlatten(final Entry<Character, WordTree> entry) {
final List<String> list = EasyStream.of(entry.getValue().entrySet()).flat(this::innerFlatten).map(v -> entry.getKey() + v).toList();
if (list.isEmpty()) {
return EasyStream.of(entry.getKey().toString());
}

View File

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

View File

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

View File

@ -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);
// 新版外国人永久居留身份证

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<DateTime> 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<Integer> 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<DateTime> 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<DateTime> 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<DateTime> dateTimes1 = DateUtil.rangeNotContains(startRange, endRange);