From 47872b83809837284ff694ce009ab5d399fad478 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 6 Jun 2024 21:42:12 +0800 Subject: [PATCH] add RegexDateParser --- .../hutool/core/date/CalendarUtil.java | 6 +- .../dromara/hutool/core/date/DateBuilder.java | 16 +- .../core/date/format/FastDateFormat.java | 6 +- .../core/date/format/parser/DateParser.java | 2 +- .../date/format/parser/FastDateParser.java | 19 +- .../date/format/parser/ISO8601DateParser.java | 10 +- .../date/format/parser/NormalDateParser.java | 2 +- .../format/parser/PatternsDateParser.java | 2 +- .../format/parser/PositionDateParser.java | 4 +- .../date/format/parser/PureDateParser.java | 5 +- .../date/format/parser/RFC2822DateParser.java | 2 +- .../date/format/parser/RegexDateParser.java | 252 ++++++++++++++++++ .../format/parser/RegisterDateParser.java | 2 +- .../core/date/format/parser/TimeParser.java | 2 +- .../hutool/core/math/NumberValidator.java | 1 - .../org/dromara/hutool/core/regex/ReUtil.java | 22 +- .../hutool/core/date/DateBuilderTest.java | 44 +++ .../hutool/core/date/IssueI8IUTBTest.java | 12 + .../format/parser/RegexDateParserTest.java | 16 ++ 19 files changed, 385 insertions(+), 40 deletions(-) create mode 100644 hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/RegexDateParser.java create mode 100644 hutool-core/src/test/java/org/dromara/hutool/core/date/DateBuilderTest.java create mode 100644 hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI8IUTBTest.java create mode 100644 hutool-core/src/test/java/org/dromara/hutool/core/date/format/parser/RegexDateParserTest.java diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/CalendarUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/CalendarUtil.java index 78127dee8..17c7c9d7d 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/CalendarUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/CalendarUtil.java @@ -717,7 +717,7 @@ public class CalendarUtil { * @throws DateException if none of the date patterns were suitable * @since 5.3.11 */ - public static Calendar parseByPatterns(final String str, final String... parsePatterns) throws DateException { + public static Calendar parseByPatterns(final CharSequence str, final String... parsePatterns) throws DateException { return parseByPatterns(str, null, parsePatterns); } @@ -734,7 +734,7 @@ public class CalendarUtil { * @throws DateException if none of the date patterns were suitable * @since 5.3.11 */ - public static Calendar parseByPatterns(final String str, final Locale locale, final String... parsePatterns) throws DateException { + public static Calendar parseByPatterns(final CharSequence str, final Locale locale, final String... parsePatterns) throws DateException { return parseByPatterns(str, locale, true, parsePatterns); } @@ -753,7 +753,7 @@ public class CalendarUtil { * @see java.util.Calendar#isLenient() * @since 5.3.11 */ - public static Calendar parseByPatterns(final String str, final Locale locale, final boolean lenient, final String... parsePatterns) throws DateException { + public static Calendar parseByPatterns(final CharSequence str, final Locale locale, final boolean lenient, final String... parsePatterns) throws DateException { if (str == null || parsePatterns == null) { throw new IllegalArgumentException("Date and Patterns must not be null"); } 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 9f8a4d3eb..9ca129387 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 @@ -37,9 +37,9 @@ public final class DateBuilder { // region ----- fields // 年份 private int year; - // 月份 + // 月份,从1开始 private int month; - // 周数 + // 周数,ISO8601规范,1代表Monday,2代表Tuesday,以此类推。 private int week; // 日 private int day; @@ -88,18 +88,18 @@ public final class DateBuilder { } /** - * 获取月份。 + * 获取月份,从1开始。 * - * @return 返回设置的月份。 + * @return 返回设置的月份,从1开始。 */ public int getMonth() { return month; } /** - * 设置月份。 + * 设置月份,从1开始。 * - * @param month 要设置的月份。 + * @param month 要设置的月份,从1开始。 * @return this */ public DateBuilder setMonth(final int month) { @@ -117,9 +117,9 @@ public final class DateBuilder { } /** - * 设置日期构建器的周数。 + * 设置日期构建器的周数,ISO8601规范,1代表Monday,2代表Tuesday,以此类推。 * - * @param week 指定的周数,通常用于构建具体的日期对象。 + * @param week 指定的周数,通常用于构建具体的日期对象,ISO8601规范,1代表Monday,2代表Tuesday,以此类推。 * @return this。 */ public DateBuilder setWeek(final int week) { diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/FastDateFormat.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/FastDateFormat.java index b0266012f..7b6636d3d 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/FastDateFormat.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/FastDateFormat.java @@ -344,17 +344,17 @@ public class FastDateFormat extends Format implements PositionDateParser, DatePr // ----------------------------------------------------------------------- Parsing @Override - public Date parse(final String source) throws DateException { + public Date parse(final CharSequence source) throws DateException { return parser.parse(source); } @Override - public Date parse(final String source, final ParsePosition pos) { + public Date parse(final CharSequence source, final ParsePosition pos) { return parser.parse(source, pos); } @Override - public boolean parse(final String source, final ParsePosition pos, final Calendar calendar) { + public boolean parse(final CharSequence source, final ParsePosition pos, final Calendar calendar) { return parser.parse(source, pos, calendar); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/DateParser.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/DateParser.java index f795c5a80..0edb1d2e5 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/DateParser.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/DateParser.java @@ -31,5 +31,5 @@ public interface DateParser extends DateBasic{ * @return {@link Date}对象 * @throws DateException 转换异常,被转换的字符串格式错误。 */ - Date parse(String source) throws DateException; + Date parse(CharSequence source) throws DateException; } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/FastDateParser.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/FastDateParser.java index 3780bf2a7..cea5c3f9d 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/FastDateParser.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/FastDateParser.java @@ -17,6 +17,7 @@ import org.dromara.hutool.core.date.format.FastDateFormat; import org.dromara.hutool.core.date.format.FastDatePrinter; import org.dromara.hutool.core.date.format.SimpleDateBasic; import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap; +import org.dromara.hutool.core.text.StrUtil; import java.io.IOException; import java.io.ObjectInputStream; @@ -224,7 +225,7 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse } @Override - public Date parse(final String source) throws DateException { + public Date parse(final CharSequence source) throws DateException { final ParsePosition pp = new ParsePosition(0); final Date date = parse(source, pp); if (date == null) { @@ -239,7 +240,7 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse } @Override - public Date parse(final String source, final ParsePosition pos) { + public Date parse(final CharSequence source, final ParsePosition pos) { // timing tests indicate getting new instance is 19% faster than cloning final Calendar cal = Calendar.getInstance(timeZone, locale); cal.clear(); @@ -248,7 +249,7 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse } @Override - public boolean parse(final String source, final ParsePosition pos, final Calendar calendar) { + public boolean parse(final CharSequence source, final ParsePosition pos, final Calendar calendar) { final ListIterator lt = patterns.listIterator(); while (lt.hasNext()) { final StrategyAndWidth strategyAndWidth = lt.next(); @@ -337,7 +338,7 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse return false; } - abstract boolean parse(FastDateParser parser, Calendar calendar, String source, ParsePosition pos, int maxWidth); + abstract boolean parse(FastDateParser parser, Calendar calendar, CharSequence source, ParsePosition pos, int maxWidth); } /** @@ -356,8 +357,8 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse } @Override - boolean parse(final FastDateParser parser, final Calendar calendar, final String source, final ParsePosition pos, final int maxWidth) { - final Matcher matcher = pattern.matcher(source.substring(pos.getIndex())); + boolean parse(final FastDateParser parser, final Calendar calendar, final CharSequence source, final ParsePosition pos, final int maxWidth) { + final Matcher matcher = pattern.matcher(source.subSequence(pos.getIndex(), source.length())); if (!matcher.lookingAt()) { pos.setErrorIndex(pos.getIndex()); return false; @@ -486,7 +487,7 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse } @Override - boolean parse(final FastDateParser parser, final Calendar calendar, final String source, final ParsePosition pos, final int maxWidth) { + boolean parse(final FastDateParser parser, final Calendar calendar, final CharSequence source, final ParsePosition pos, final int maxWidth) { for (int idx = 0; idx < formatField.length(); ++idx) { final int sIdx = idx + pos.getIndex(); if (sIdx == source.length()) { @@ -558,7 +559,7 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse } @Override - boolean parse(final FastDateParser parser, final Calendar calendar, final String source, final ParsePosition pos, final int maxWidth) { + boolean parse(final FastDateParser parser, final Calendar calendar, final CharSequence source, final ParsePosition pos, final int maxWidth) { int idx = pos.getIndex(); int last = source.length(); @@ -590,7 +591,7 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse return false; } - final int value = Integer.parseInt(source.substring(pos.getIndex(), idx)); + final int value = Integer.parseInt(StrUtil.sub(source, pos.getIndex(), idx)); pos.setIndex(idx); calendar.set(field, modify(parser, value)); diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/ISO8601DateParser.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/ISO8601DateParser.java index 54d5548e4..2686825e0 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/ISO8601DateParser.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/ISO8601DateParser.java @@ -48,7 +48,7 @@ public class ISO8601DateParser extends DefaultDateBasic implements PredicateDate } @Override - public DateTime parse(String source) throws DateException{ + public DateTime parse(CharSequence source) throws DateException{ final int length = source.length(); if (StrUtil.contains(source, 'Z')) { if (length == DatePattern.UTC_PATTERN.length() - 4) { @@ -65,7 +65,7 @@ public class ISO8601DateParser extends DefaultDateBasic implements PredicateDate } } else if (StrUtil.contains(source, '+')) { // 去除类似2019-06-01T19:45:43 +08:00加号前的空格 - source = source.replace(" +", "+"); + source = StrUtil.replace(source, " +", "+"); final String zoneOffset = StrUtil.subAfter(source, '+', true); if (StrUtil.isBlank(zoneOffset)) { throw new DateException("Invalid format: [{}]", source); @@ -88,9 +88,9 @@ public class ISO8601DateParser extends DefaultDateBasic implements PredicateDate // Issue#2612,类似 2022-09-14T23:59:00-08:00 或者 2022-09-14T23:59:00-0800 // 去除类似2019-06-01T19:45:43 -08:00加号前的空格 - source = source.replace(" -", "-"); + source = StrUtil.replace(source, " -", "-"); if(':' != source.charAt(source.length() - 3)){ - source = source.substring(0, source.length() - 2) + ":00"; + source = StrUtil.sub(source, 0, source.length() - 2) + ":00"; } if (StrUtil.contains(source, CharUtil.DOT)) { @@ -128,7 +128,7 @@ public class ISO8601DateParser extends DefaultDateBasic implements PredicateDate * @return 规范之后的毫秒部分 */ @SuppressWarnings("SameParameterValue") - private static String normalizeMillSeconds(final String dateStr, final CharSequence before, final CharSequence after) { + private static String normalizeMillSeconds(final CharSequence dateStr, final CharSequence before, final CharSequence after) { if (StrUtil.isBlank(after)) { final String millOrNaco = StrUtil.subPre(StrUtil.subAfter(dateStr, before, true), 3); return StrUtil.subBefore(dateStr, before, true) + before + millOrNaco; diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/NormalDateParser.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/NormalDateParser.java index 4af0aaf6c..ad63633a5 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/NormalDateParser.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/NormalDateParser.java @@ -51,7 +51,7 @@ public class NormalDateParser extends DefaultDateBasic implements PredicateDateP } @Override - public DateTime parse(String source) throws DateException{ + public DateTime parse(CharSequence source) throws DateException{ final int colonCount = StrUtil.count(source, CharUtil.COLON); switch (colonCount) { case 0: diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/PatternsDateParser.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/PatternsDateParser.java index 670878d10..6cbe513b3 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/PatternsDateParser.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/PatternsDateParser.java @@ -80,7 +80,7 @@ public class PatternsDateParser extends DefaultDateBasic implements DateParser { } @Override - public DateTime parse(final String source) { + public DateTime parse(final CharSequence source) { return new DateTime(CalendarUtil.parseByPatterns(source, this.locale, this.parsePatterns)); } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/PositionDateParser.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/PositionDateParser.java index b891a9b88..5203aec09 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/PositionDateParser.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/PositionDateParser.java @@ -32,7 +32,7 @@ public interface PositionDateParser extends DateParser { * @param pos {@link ParsePosition} * @return {@link Date} */ - Date parse(String source, ParsePosition pos); + Date parse(CharSequence source, ParsePosition pos); /** * 根据给定格式更新{@link Calendar} @@ -46,5 +46,5 @@ public interface PositionDateParser extends DateParser { * @return true, if source has been parsed (pos parsePosition is updated); otherwise false (and pos errorIndex is updated) * @throws IllegalArgumentException when Calendar has been set to be not lenient, and a parsed field is out of range. */ - boolean parse(String source, ParsePosition pos, Calendar calendar); + boolean parse(CharSequence source, ParsePosition pos, Calendar calendar); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/PureDateParser.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/PureDateParser.java index 6d6315a7e..007a15878 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/PureDateParser.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/PureDateParser.java @@ -17,6 +17,7 @@ import org.dromara.hutool.core.date.DatePattern; import org.dromara.hutool.core.date.DateTime; import org.dromara.hutool.core.date.format.DefaultDateBasic; import org.dromara.hutool.core.math.NumberUtil; +import org.dromara.hutool.core.text.StrUtil; /** * 纯数字的日期字符串解析,支持格式包括; @@ -45,7 +46,7 @@ public class PureDateParser extends DefaultDateBasic implements PredicateDatePar } @Override - public DateTime parse(final String source) throws DateException { + public DateTime parse(final CharSequence source) throws DateException { final int length = source.length(); // 纯数字形式 if (length == DatePattern.PURE_DATETIME_PATTERN.length()) { @@ -58,7 +59,7 @@ public class PureDateParser extends DefaultDateBasic implements PredicateDatePar return new DateTime(source, DatePattern.PURE_TIME_FORMAT); } else if(length >= 11 && length <= 13){ // 时间戳 - return new DateTime(NumberUtil.parseLong(source)); + return new DateTime(NumberUtil.parseLong(StrUtil.str(source))); } throw new DateException("No pure format fit for date String [{}] !", source); diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/RFC2822DateParser.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/RFC2822DateParser.java index 7d36b3ee7..d6e5f4724 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/RFC2822DateParser.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/RFC2822DateParser.java @@ -57,7 +57,7 @@ public class RFC2822DateParser extends DefaultDateBasic implements PredicateDate } @Override - public DateTime parse(final String source) { + public DateTime parse(final CharSequence source) { // issue#I9C2D4 if(StrUtil.contains(source, ',')){ if(StrUtil.contains(source, KEYWORDS_LOCALE_CHINA)){ 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 new file mode 100644 index 000000000..1cc994eef --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/RegexDateParser.java @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2024. looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * https://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.core.date.format.parser; + +import org.dromara.hutool.core.date.*; +import org.dromara.hutool.core.date.format.DefaultDateBasic; +import org.dromara.hutool.core.regex.ReUtil; +import org.dromara.hutool.core.text.StrUtil; + +import java.util.Date; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 正则日期解析器
+ * 通过定义一个命名分组的正则匹配日期格式,使用正则分组获取日期各部分的值,命名分组使用{@code (?子表达式) }表示,如:
+ *
{@code
+ *  ^(?\d{4})(?\d{2})$ 匹配 201909
+ * }
+ * + * @author Looly + * @since 6.0.0 + */ +public class RegexDateParser extends DefaultDateBasic implements PredicateDateParser { + private static final long serialVersionUID = 1L; + + private static final int[] NSS = {100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1}; + + /** + * 根据给定带名称的分组正则,创建RegexDateParser + * + * @param regex 正则表达式 + * @return RegexDateParser + */ + public static RegexDateParser of(final String regex) { + return new RegexDateParser(Pattern.compile(regex)); + } + + private final Pattern pattern; + + /** + * 构造 + * + * @param pattern 正则表达式 + */ + public RegexDateParser(final Pattern pattern) { + this.pattern = pattern; + } + + @Override + public boolean test(final CharSequence source) { + return ReUtil.isMatch(this.pattern, source); + } + + @Override + public Date parse(final CharSequence source) throws DateException { + final Matcher matcher = this.pattern.matcher(source); + if(!matcher.matches()){ + throw new DateException("Invalid date string: [{}]", source); + } + + final DateBuilder dateBuilder = DateBuilder.of(); + // year + final String year = ReUtil.group(matcher, "year"); + if (StrUtil.isNotEmpty(year)) { + dateBuilder.setYear(parseYear(year)); + } + + // month + final String month = ReUtil.group(matcher, "month"); + if (StrUtil.isNotEmpty(month)) { + dateBuilder.setMonth(parseMonth(month)); + } + + // week + final String week = ReUtil.group(matcher, "week"); + if (StrUtil.isNotEmpty(week)) { + dateBuilder.setWeek(parseWeek(week)); + } + + // day + final String day = ReUtil.group(matcher, "day"); + if (StrUtil.isNotEmpty(day)) { + dateBuilder.setDay(parseNumberLimit(day, 1, 31)); + } + + // hour + final String hour = ReUtil.group(matcher, "hour"); + if (StrUtil.isNotEmpty(hour)) { + dateBuilder.setHour(parseNumberLimit(hour, 0, 23)); + } + + // minute + final String minute = ReUtil.group(matcher, "minute"); + if (StrUtil.isNotEmpty(minute)) { + dateBuilder.setMinute(parseNumberLimit(minute, 0, 59)); + } + + // second + final String second = ReUtil.group(matcher, "second"); + if (StrUtil.isNotEmpty(second)) { + dateBuilder.setSecond(parseNumberLimit(second, 0, 59)); + } + + // ns + final String ns = ReUtil.group(matcher, "ns"); + if (StrUtil.isNotEmpty(ns)) { + dateBuilder.setNs(parseNano(ns)); + } + + // am or pm + final String m = ReUtil.group(matcher, "m"); + if (StrUtil.isNotEmpty(m)) { + if ('p' == m.charAt(0)) { + dateBuilder.setPm(true); + } else { + dateBuilder.setAm(true); + } + } + + // zero zone offset + final String zero = ReUtil.group(matcher, "zero"); + if (StrUtil.isNotEmpty(zero)) { + dateBuilder.setZoneOffsetSetted(true); + dateBuilder.setZoneOffset(0); + } + + // zone offset + final String zoneOffset = ReUtil.group(matcher, "zoneOffset"); + if (StrUtil.isNotEmpty(zoneOffset)) { + dateBuilder.setZoneOffsetSetted(true); + dateBuilder.setZoneOffset(parseZoneOffset(zoneOffset)); + } + + // unix时间戳 + final String unixsecond = ReUtil.group(matcher, "unixsecond"); + if (StrUtil.isNotEmpty(unixsecond)) { + dateBuilder.setUnixsecond(parseLong(unixsecond)); + } + + // 毫秒时间戳 + final String millisecond = ReUtil.group(matcher, "millisecond"); + if (StrUtil.isNotEmpty(millisecond)) { + return DateUtil.date(parseLong(millisecond)); + } + + return dateBuilder.toDate(); + } + + private static int parseYear(final String year) { + final int length = year.length(); + switch (length) { + case 4: + return Integer.parseInt(year); + case 2: + final int num = Integer.parseInt(year); + return (num > 50 ? 1900 : 2000) + num; + default: + throw new DateException("Invalid year: [{}]", year); + } + } + + private static int parseMonth(final String month) { + try { + final int monthInt = Integer.parseInt(month); + if (monthInt > 0 && monthInt < 13) { + return monthInt; + } + } catch (final NumberFormatException e) { + return Month.of(month).getValueBaseOne(); + } + + throw new DateException("Invalid month: [{}]", month); + } + + private static int parseWeek(final String week){ + return Week.of(week).getIso8601Value(); + } + + private static int parseNumberLimit(final String numberStr, final int minInclude, final int maxInclude) { + try { + final int monthInt = Integer.parseInt(numberStr); + if (monthInt >= minInclude && monthInt <= maxInclude) { + return monthInt; + } + } catch (final NumberFormatException ignored) { + } + throw new DateException("Invalid number: [{}]", numberStr); + } + + private static long parseLong(final String numberStr) { + try { + return Long.parseLong(numberStr); + } catch (final NumberFormatException ignored) { + } + throw new DateException("Invalid long: [{}]", numberStr); + } + + private static int parseInt(final String numberStr, final int from, final int to) { + try { + return Integer.parseInt(numberStr.substring(from, to)); + } catch (final NumberFormatException ignored) { + } + throw new DateException("Invalid int: [{}]", numberStr); + } + + private static int parseNano(final String ns) { + return NSS[ns.length() - 1] * Integer.parseInt(ns); + } + + /** + * 解析时区偏移,类似于'+0800', '+08', '+8:00', '+08:00' + * @param zoneOffset 时区偏移 + * @return 偏移量 + */ + private int parseZoneOffset(final String zoneOffset) { + int from = 0; + final int to = zoneOffset.length(); + final boolean neg = '-' == zoneOffset.charAt(from); + from++; + + // parse hour + final int hour; + if (from + 2 <= to && Character.isDigit(zoneOffset.charAt(from + 1))) { + hour = parseInt(zoneOffset, from, from + 2); + from += 2; + } else { + hour = parseInt(zoneOffset, from, from + 1); + from += 1; + } + // skip ':' optionally + if (from + 3 <= to && zoneOffset.charAt(from) == ':') { + from++; + } + // parse minute optionally + int minute = 0; + if (from + 2 <= to) { + minute = parseInt(zoneOffset, from, from + 2); + } + return (hour * 60 + minute) * (neg ? -1 : 1); + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/RegisterDateParser.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/RegisterDateParser.java index a22e042bf..b225516e2 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/RegisterDateParser.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/RegisterDateParser.java @@ -46,7 +46,7 @@ public class RegisterDateParser extends DefaultDateBasic implements DateParser { } @Override - public Date parse(final String source) throws DateException { + public Date parse(final CharSequence source) throws DateException { return parserList .stream() .filter(predicateDateParser -> predicateDateParser.test(source)) diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/TimeParser.java b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/TimeParser.java index cc7599a85..986517736 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/TimeParser.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/date/format/parser/TimeParser.java @@ -44,7 +44,7 @@ public class TimeParser extends DefaultDateBasic implements PredicateDateParser } @Override - public DateTime parse(String source) { + public DateTime parse(CharSequence source) { // issue#I9C2D4 处理时分秒 //15时45分59秒 修正为 15:45:59 source = StrUtil.replaceChars(source, "时分秒", ":"); diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberValidator.java b/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberValidator.java index d9f3cead0..8809e58c9 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberValidator.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberValidator.java @@ -166,7 +166,6 @@ public class NumberValidator { return false; } try { - //noinspection ResultOfMethodCallIgnored Integer.decode(s); } catch (final NumberFormatException e) { return false; diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/regex/ReUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/regex/ReUtil.java index 2bc4f9f9c..ecf2697a2 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/regex/ReUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/regex/ReUtil.java @@ -884,7 +884,7 @@ public class ReUtil { boolean result = matcher.find(); if (result) { final Set varNums = findAll(PatternPool.GROUP_VAR, replacementTemplate, 1, - new TreeSet<>(StrLengthComparator.INSTANCE.reversed())); + new TreeSet<>(StrLengthComparator.INSTANCE.reversed())); final StringBuffer sb = new StringBuffer(); do { String replacement = replacementTemplate; @@ -985,4 +985,24 @@ public class ReUtil { } return builder.toString(); } + + /** + * 根据提供的匹配器和组名尝试获取匹配的字符串。 + *

+ * 此方法旨在方便地从匹配器中提取指定名称的组匹配的字符串。如果指定的组不存在, + * 则通过捕获异常并返回null来优雅地处理错误。 + * + * @param matcher 匹配器对象,用于查找和匹配文本。 + * @param name 组的名称,用于指定要提取的匹配字符串的组。 + * @return 如果找到并成功提取了指定组的匹配字符串,则返回该字符串;如果组不存在,则返回null。 + */ + public static String group(final Matcher matcher, final String name) { + try { + // 尝试根据组名获取匹配的字符串。 + return matcher.group(name); + } catch (final IllegalArgumentException e) { + // 如果组名无效,捕获异常并返回null。 + return null; + } + } } diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/date/DateBuilderTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/date/DateBuilderTest.java new file mode 100644 index 000000000..480d96b74 --- /dev/null +++ b/hutool-core/src/test/java/org/dromara/hutool/core/date/DateBuilderTest.java @@ -0,0 +1,44 @@ +package org.dromara.hutool.core.date; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.util.Date; +import java.util.TimeZone; + +public class DateBuilderTest { + @Test + public void testNormal() { + final DateBuilder builder = new DateBuilder(); + builder.setYear(2019); + builder.setMonth(10); + builder.setDay(1); + final Date date = builder.toDate(); + + Assertions.assertEquals("2019-10-01", DateUtil.date(date).toDateStr()); + } + + @Test + public void testLocalDateTime() { + final DateBuilder builder = DateBuilder.of() + .setYear(2019) + .setMonth(10) + .setDay(1) + .setHour(10) + .setMinute(20) + .setSecond(30) + .setNs(900000000) + .setZone(TimeZone.getDefault()); + + final LocalDateTime dateTime = builder.toLocalDateTime(); + Assertions.assertEquals("2019-10-01T10:20:30.900", builder.toLocalDateTime().toString()); + } + + @Test + public void testTimestamp() { + final String timestamp = "946656000"; + final DateBuilder dateBuilder = DateBuilder.of().setUnixsecond(Long.parseLong(timestamp)); + Assertions.assertEquals("2000-01-01T00:00", dateBuilder.toLocalDateTime().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 new file mode 100644 index 000000000..9e3610baa --- /dev/null +++ b/hutool-core/src/test/java/org/dromara/hutool/core/date/IssueI8IUTBTest.java @@ -0,0 +1,12 @@ +package org.dromara.hutool.core.date; + +import org.dromara.hutool.core.lang.Console; +import org.junit.jupiter.api.Test; + +public class IssueI8IUTBTest { + @Test + void parseTest() { + final DateTime 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/format/parser/RegexDateParserTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/date/format/parser/RegexDateParserTest.java new file mode 100644 index 000000000..a0141ac4b --- /dev/null +++ b/hutool-core/src/test/java/org/dromara/hutool/core/date/format/parser/RegexDateParserTest.java @@ -0,0 +1,16 @@ +package org.dromara.hutool.core.date.format.parser; + +import org.dromara.hutool.core.date.DateUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Date; + +public class RegexDateParserTest { + @Test + void parsePureTest() { + final RegexDateParser parser = RegexDateParser.of("^(?\\d{4})(?\\d{2})(?\\d{2})$"); + final Date parse = parser.parse("20220101"); + Assertions.assertEquals("2022-01-01", DateUtil.date(parse).toDateStr()); + } +}