mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
add RegexDateParser
This commit is contained in:
parent
9a34a155c6
commit
47872b8380
@ -717,7 +717,7 @@ public class CalendarUtil {
|
|||||||
* @throws DateException if none of the date patterns were suitable
|
* @throws DateException if none of the date patterns were suitable
|
||||||
* @since 5.3.11
|
* @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);
|
return parseByPatterns(str, null, parsePatterns);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -734,7 +734,7 @@ public class CalendarUtil {
|
|||||||
* @throws DateException if none of the date patterns were suitable
|
* @throws DateException if none of the date patterns were suitable
|
||||||
* @since 5.3.11
|
* @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);
|
return parseByPatterns(str, locale, true, parsePatterns);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -753,7 +753,7 @@ public class CalendarUtil {
|
|||||||
* @see java.util.Calendar#isLenient()
|
* @see java.util.Calendar#isLenient()
|
||||||
* @since 5.3.11
|
* @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) {
|
if (str == null || parsePatterns == null) {
|
||||||
throw new IllegalArgumentException("Date and Patterns must not be null");
|
throw new IllegalArgumentException("Date and Patterns must not be null");
|
||||||
}
|
}
|
||||||
|
@ -37,9 +37,9 @@ public final class DateBuilder {
|
|||||||
// region ----- fields
|
// region ----- fields
|
||||||
// 年份
|
// 年份
|
||||||
private int year;
|
private int year;
|
||||||
// 月份
|
// 月份,从1开始
|
||||||
private int month;
|
private int month;
|
||||||
// 周数
|
// 周数,ISO8601规范,1代表Monday,2代表Tuesday,以此类推。
|
||||||
private int week;
|
private int week;
|
||||||
// 日
|
// 日
|
||||||
private int day;
|
private int day;
|
||||||
@ -88,18 +88,18 @@ public final class DateBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取月份。
|
* 获取月份,从1开始。
|
||||||
*
|
*
|
||||||
* @return 返回设置的月份。
|
* @return 返回设置的月份,从1开始。
|
||||||
*/
|
*/
|
||||||
public int getMonth() {
|
public int getMonth() {
|
||||||
return month;
|
return month;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置月份。
|
* 设置月份,从1开始。
|
||||||
*
|
*
|
||||||
* @param month 要设置的月份。
|
* @param month 要设置的月份,从1开始。
|
||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
public DateBuilder setMonth(final int month) {
|
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。
|
* @return this。
|
||||||
*/
|
*/
|
||||||
public DateBuilder setWeek(final int week) {
|
public DateBuilder setWeek(final int week) {
|
||||||
|
@ -344,17 +344,17 @@ public class FastDateFormat extends Format implements PositionDateParser, DatePr
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------- Parsing
|
// ----------------------------------------------------------------------- Parsing
|
||||||
@Override
|
@Override
|
||||||
public Date parse(final String source) throws DateException {
|
public Date parse(final CharSequence source) throws DateException {
|
||||||
return parser.parse(source);
|
return parser.parse(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Date parse(final String source, final ParsePosition pos) {
|
public Date parse(final CharSequence source, final ParsePosition pos) {
|
||||||
return parser.parse(source, pos);
|
return parser.parse(source, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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);
|
return parser.parse(source, pos, calendar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,5 +31,5 @@ public interface DateParser extends DateBasic{
|
|||||||
* @return {@link Date}对象
|
* @return {@link Date}对象
|
||||||
* @throws DateException 转换异常,被转换的字符串格式错误。
|
* @throws DateException 转换异常,被转换的字符串格式错误。
|
||||||
*/
|
*/
|
||||||
Date parse(String source) throws DateException;
|
Date parse(CharSequence source) throws DateException;
|
||||||
}
|
}
|
||||||
|
@ -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.FastDatePrinter;
|
||||||
import org.dromara.hutool.core.date.format.SimpleDateBasic;
|
import org.dromara.hutool.core.date.format.SimpleDateBasic;
|
||||||
import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap;
|
import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap;
|
||||||
|
import org.dromara.hutool.core.text.StrUtil;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
@ -224,7 +225,7 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Date parse(final String source) throws DateException {
|
public Date parse(final CharSequence source) throws DateException {
|
||||||
final ParsePosition pp = new ParsePosition(0);
|
final ParsePosition pp = new ParsePosition(0);
|
||||||
final Date date = parse(source, pp);
|
final Date date = parse(source, pp);
|
||||||
if (date == null) {
|
if (date == null) {
|
||||||
@ -239,7 +240,7 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
// timing tests indicate getting new instance is 19% faster than cloning
|
||||||
final Calendar cal = Calendar.getInstance(timeZone, locale);
|
final Calendar cal = Calendar.getInstance(timeZone, locale);
|
||||||
cal.clear();
|
cal.clear();
|
||||||
@ -248,7 +249,7 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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<StrategyAndWidth> lt = patterns.listIterator();
|
final ListIterator<StrategyAndWidth> lt = patterns.listIterator();
|
||||||
while (lt.hasNext()) {
|
while (lt.hasNext()) {
|
||||||
final StrategyAndWidth strategyAndWidth = lt.next();
|
final StrategyAndWidth strategyAndWidth = lt.next();
|
||||||
@ -337,7 +338,7 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse
|
|||||||
return false;
|
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
|
@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) {
|
||||||
final Matcher matcher = pattern.matcher(source.substring(pos.getIndex()));
|
final Matcher matcher = pattern.matcher(source.subSequence(pos.getIndex(), source.length()));
|
||||||
if (!matcher.lookingAt()) {
|
if (!matcher.lookingAt()) {
|
||||||
pos.setErrorIndex(pos.getIndex());
|
pos.setErrorIndex(pos.getIndex());
|
||||||
return false;
|
return false;
|
||||||
@ -486,7 +487,7 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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) {
|
for (int idx = 0; idx < formatField.length(); ++idx) {
|
||||||
final int sIdx = idx + pos.getIndex();
|
final int sIdx = idx + pos.getIndex();
|
||||||
if (sIdx == source.length()) {
|
if (sIdx == source.length()) {
|
||||||
@ -558,7 +559,7 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 idx = pos.getIndex();
|
||||||
int last = source.length();
|
int last = source.length();
|
||||||
|
|
||||||
@ -590,7 +591,7 @@ public class FastDateParser extends SimpleDateBasic implements PositionDateParse
|
|||||||
return false;
|
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);
|
pos.setIndex(idx);
|
||||||
|
|
||||||
calendar.set(field, modify(parser, value));
|
calendar.set(field, modify(parser, value));
|
||||||
|
@ -48,7 +48,7 @@ public class ISO8601DateParser extends DefaultDateBasic implements PredicateDate
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DateTime parse(String source) throws DateException{
|
public DateTime parse(CharSequence source) throws DateException{
|
||||||
final int length = source.length();
|
final int length = source.length();
|
||||||
if (StrUtil.contains(source, 'Z')) {
|
if (StrUtil.contains(source, 'Z')) {
|
||||||
if (length == DatePattern.UTC_PATTERN.length() - 4) {
|
if (length == DatePattern.UTC_PATTERN.length() - 4) {
|
||||||
@ -65,7 +65,7 @@ public class ISO8601DateParser extends DefaultDateBasic implements PredicateDate
|
|||||||
}
|
}
|
||||||
} else if (StrUtil.contains(source, '+')) {
|
} else if (StrUtil.contains(source, '+')) {
|
||||||
// 去除类似2019-06-01T19:45:43 +08:00加号前的空格
|
// 去除类似2019-06-01T19:45:43 +08:00加号前的空格
|
||||||
source = source.replace(" +", "+");
|
source = StrUtil.replace(source, " +", "+");
|
||||||
final String zoneOffset = StrUtil.subAfter(source, '+', true);
|
final String zoneOffset = StrUtil.subAfter(source, '+', true);
|
||||||
if (StrUtil.isBlank(zoneOffset)) {
|
if (StrUtil.isBlank(zoneOffset)) {
|
||||||
throw new DateException("Invalid format: [{}]", source);
|
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
|
// Issue#2612,类似 2022-09-14T23:59:00-08:00 或者 2022-09-14T23:59:00-0800
|
||||||
|
|
||||||
// 去除类似2019-06-01T19:45:43 -08:00加号前的空格
|
// 去除类似2019-06-01T19:45:43 -08:00加号前的空格
|
||||||
source = source.replace(" -", "-");
|
source = StrUtil.replace(source, " -", "-");
|
||||||
if(':' != source.charAt(source.length() - 3)){
|
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)) {
|
if (StrUtil.contains(source, CharUtil.DOT)) {
|
||||||
@ -128,7 +128,7 @@ public class ISO8601DateParser extends DefaultDateBasic implements PredicateDate
|
|||||||
* @return 规范之后的毫秒部分
|
* @return 规范之后的毫秒部分
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("SameParameterValue")
|
@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)) {
|
if (StrUtil.isBlank(after)) {
|
||||||
final String millOrNaco = StrUtil.subPre(StrUtil.subAfter(dateStr, before, true), 3);
|
final String millOrNaco = StrUtil.subPre(StrUtil.subAfter(dateStr, before, true), 3);
|
||||||
return StrUtil.subBefore(dateStr, before, true) + before + millOrNaco;
|
return StrUtil.subBefore(dateStr, before, true) + before + millOrNaco;
|
||||||
|
@ -51,7 +51,7 @@ public class NormalDateParser extends DefaultDateBasic implements PredicateDateP
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DateTime parse(String source) throws DateException{
|
public DateTime parse(CharSequence source) throws DateException{
|
||||||
final int colonCount = StrUtil.count(source, CharUtil.COLON);
|
final int colonCount = StrUtil.count(source, CharUtil.COLON);
|
||||||
switch (colonCount) {
|
switch (colonCount) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -80,7 +80,7 @@ public class PatternsDateParser extends DefaultDateBasic implements DateParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DateTime parse(final String source) {
|
public DateTime parse(final CharSequence source) {
|
||||||
return new DateTime(CalendarUtil.parseByPatterns(source, this.locale, this.parsePatterns));
|
return new DateTime(CalendarUtil.parseByPatterns(source, this.locale, this.parsePatterns));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ public interface PositionDateParser extends DateParser {
|
|||||||
* @param pos {@link ParsePosition}
|
* @param pos {@link ParsePosition}
|
||||||
* @return {@link Date}
|
* @return {@link Date}
|
||||||
*/
|
*/
|
||||||
Date parse(String source, ParsePosition pos);
|
Date parse(CharSequence source, ParsePosition pos);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据给定格式更新{@link Calendar}
|
* 根据给定格式更新{@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)
|
* @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.
|
* @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);
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import org.dromara.hutool.core.date.DatePattern;
|
|||||||
import org.dromara.hutool.core.date.DateTime;
|
import org.dromara.hutool.core.date.DateTime;
|
||||||
import org.dromara.hutool.core.date.format.DefaultDateBasic;
|
import org.dromara.hutool.core.date.format.DefaultDateBasic;
|
||||||
import org.dromara.hutool.core.math.NumberUtil;
|
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
|
@Override
|
||||||
public DateTime parse(final String source) throws DateException {
|
public DateTime parse(final CharSequence source) throws DateException {
|
||||||
final int length = source.length();
|
final int length = source.length();
|
||||||
// 纯数字形式
|
// 纯数字形式
|
||||||
if (length == DatePattern.PURE_DATETIME_PATTERN.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);
|
return new DateTime(source, DatePattern.PURE_TIME_FORMAT);
|
||||||
} else if(length >= 11 && length <= 13){
|
} 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);
|
throw new DateException("No pure format fit for date String [{}] !", source);
|
||||||
|
@ -57,7 +57,7 @@ public class RFC2822DateParser extends DefaultDateBasic implements PredicateDate
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DateTime parse(final String source) {
|
public DateTime parse(final CharSequence source) {
|
||||||
// issue#I9C2D4
|
// issue#I9C2D4
|
||||||
if(StrUtil.contains(source, ',')){
|
if(StrUtil.contains(source, ',')){
|
||||||
if(StrUtil.contains(source, KEYWORDS_LOCALE_CHINA)){
|
if(StrUtil.contains(source, KEYWORDS_LOCALE_CHINA)){
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正则日期解析器<br>
|
||||||
|
* 通过定义一个命名分组的正则匹配日期格式,使用正则分组获取日期各部分的值,命名分组使用{@code (?<xxx>子表达式) }表示,如:<br>
|
||||||
|
* <pre>{@code
|
||||||
|
* ^(?<year>\d{4})(?<month>\d{2})$ 匹配 201909
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
}
|
@ -46,7 +46,7 @@ public class RegisterDateParser extends DefaultDateBasic implements DateParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Date parse(final String source) throws DateException {
|
public Date parse(final CharSequence source) throws DateException {
|
||||||
return parserList
|
return parserList
|
||||||
.stream()
|
.stream()
|
||||||
.filter(predicateDateParser -> predicateDateParser.test(source))
|
.filter(predicateDateParser -> predicateDateParser.test(source))
|
||||||
|
@ -44,7 +44,7 @@ public class TimeParser extends DefaultDateBasic implements PredicateDateParser
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DateTime parse(String source) {
|
public DateTime parse(CharSequence source) {
|
||||||
// issue#I9C2D4 处理时分秒
|
// issue#I9C2D4 处理时分秒
|
||||||
//15时45分59秒 修正为 15:45:59
|
//15时45分59秒 修正为 15:45:59
|
||||||
source = StrUtil.replaceChars(source, "时分秒", ":");
|
source = StrUtil.replaceChars(source, "时分秒", ":");
|
||||||
|
@ -166,7 +166,6 @@ public class NumberValidator {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
Integer.decode(s);
|
Integer.decode(s);
|
||||||
} catch (final NumberFormatException e) {
|
} catch (final NumberFormatException e) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -884,7 +884,7 @@ public class ReUtil {
|
|||||||
boolean result = matcher.find();
|
boolean result = matcher.find();
|
||||||
if (result) {
|
if (result) {
|
||||||
final Set<String> varNums = findAll(PatternPool.GROUP_VAR, replacementTemplate, 1,
|
final Set<String> varNums = findAll(PatternPool.GROUP_VAR, replacementTemplate, 1,
|
||||||
new TreeSet<>(StrLengthComparator.INSTANCE.reversed()));
|
new TreeSet<>(StrLengthComparator.INSTANCE.reversed()));
|
||||||
final StringBuffer sb = new StringBuffer();
|
final StringBuffer sb = new StringBuffer();
|
||||||
do {
|
do {
|
||||||
String replacement = replacementTemplate;
|
String replacement = replacementTemplate;
|
||||||
@ -985,4 +985,24 @@ public class ReUtil {
|
|||||||
}
|
}
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据提供的匹配器和组名尝试获取匹配的字符串。
|
||||||
|
* <p>
|
||||||
|
* 此方法旨在方便地从匹配器中提取指定名称的组匹配的字符串。如果指定的组不存在,
|
||||||
|
* 则通过捕获异常并返回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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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("^(?<year>\\d{4})(?<month>\\d{2})(?<day>\\d{2})$");
|
||||||
|
final Date parse = parser.parse("20220101");
|
||||||
|
Assertions.assertEquals("2022-01-01", DateUtil.date(parse).toDateStr());
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user