This commit is contained in:
Looly 2021-09-28 00:07:13 +08:00
parent 75d84b4768
commit 9642fa8862
7 changed files with 71 additions and 45 deletions

View File

@ -2,6 +2,7 @@ package cn.hutool.core.date;
import cn.hutool.core.comparator.CompareUtil; import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.convert.NumberChineseFormatter; import cn.hutool.core.convert.NumberChineseFormatter;
import cn.hutool.core.date.format.DateParser;
import cn.hutool.core.date.format.FastDateParser; import cn.hutool.core.date.format.FastDateParser;
import cn.hutool.core.date.format.GlobalCustomFormat; import cn.hutool.core.date.format.GlobalCustomFormat;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
@ -695,4 +696,21 @@ public class CalendarUtil {
throw new DateException("Unable to parse the date: {}", str); throw new DateException("Unable to parse the date: {}", str);
} }
/**
* 使用指定{@link DateParser}解析字符串为{@link Calendar}
*
* @param str 日期字符串
* @param lenient 是否宽容模式
* @param parser {@link DateParser}
* @return 解析后的 {@link Calendar}解析失败返回{@code null}
* @since 5.7.14
*/
public static Calendar parse(CharSequence str, boolean lenient, DateParser parser) {
final Calendar calendar = Calendar.getInstance(parser.getTimeZone(), parser.getLocale());
calendar.clear();
calendar.setLenient(lenient);
return parser.parse(StrUtil.str(str), new ParsePosition(0), calendar) ? calendar : null;
}
} }

View File

@ -287,7 +287,7 @@ public class DateTime extends Date {
* @see DatePattern * @see DatePattern
*/ */
public DateTime(CharSequence dateStr, DateParser dateParser) { public DateTime(CharSequence dateStr, DateParser dateParser) {
this(parse(dateStr, dateParser), dateParser.getTimeZone()); this(parse(dateStr, dateParser));
} }
// -------------------------------------------------------------------- Constructor end // -------------------------------------------------------------------- Constructor end
@ -1019,14 +1019,15 @@ public class DateTime extends Date {
* @param parser {@link FastDateFormat} * @param parser {@link FastDateFormat}
* @return {@link Date} * @return {@link Date}
*/ */
private static Date parse(CharSequence dateStr, DateParser parser) { private static Calendar parse(CharSequence dateStr, DateParser parser) {
Assert.notNull(parser, "Parser or DateFromat must be not null !"); Assert.notNull(parser, "Parser or DateFromat must be not null !");
Assert.notBlank(dateStr, "Date String must be not blank !"); Assert.notBlank(dateStr, "Date String must be not blank !");
try {
return parser.parse(dateStr.toString()); final Calendar calendar = CalendarUtil.parse(dateStr, true, parser);
} catch (Exception e) { if (null == calendar) {
throw new DateException("Parse [{}] with format [{}] error!", dateStr, parser.getPattern(), e); throw new DateException("Parse [{}] with format [{}] error!", dateStr, parser.getPattern());
} }
return calendar;
} }
/** /**

View File

@ -33,8 +33,8 @@ public interface DateParser extends DateBasic{
Date parse(String source, ParsePosition pos); Date parse(String source, ParsePosition pos);
/** /**
* 根据给定格式转换日期字符串 * 根据给定格式更新{@link Calendar}
* Updates the Calendar with parsed fields. Upon success, the ParsePosition index is updated to indicate how much of the source text was consumed. * Upon success, the ParsePosition index is updated to indicate how much of the source text was consumed.
* Not all source text needs to be consumed. * Not all source text needs to be consumed.
* Upon parse failure, ParsePosition error index is updated to the offset of the source text which does not match the supplied format. * Upon parse failure, ParsePosition error index is updated to the offset of the source text which does not match the supplied format.
* *
@ -49,20 +49,24 @@ public interface DateParser extends DateBasic{
/** /**
* 将日期字符串解析并转换为 {@link Date} 对象<br> * 将日期字符串解析并转换为 {@link Date} 对象<br>
* *
* @param source A <code>String</code> whose beginning should be parsed. * @param source A {@code String} whose beginning should be parsed.
* @return a <code>java.util.Date</code> object * @return a {@code java.util.Date} object
* @throws ParseException if the beginning of the specified string cannot be parsed. * @throws ParseException if the beginning of the specified string cannot be parsed.
* @see java.text.DateFormat#parseObject(String) * @see java.text.DateFormat#parseObject(String)
*/ */
Object parseObject(String source) throws ParseException; default Object parseObject(String source) throws ParseException{
return parse(source);
}
/** /**
* 根据 {@link ParsePosition} 给定将日期字符串解析并转换为 {@link Date} 对象<br> * 根据 {@link ParsePosition} 给定将日期字符串解析并转换为 {@link Date} 对象<br>
* *
* @param source A <code>String</code> whose beginning should be parsed. * @param source A {@code String} whose beginning should be parsed.
* @param pos the parse position * @param pos the parse position
* @return a <code>java.util.Date</code> object * @return a {@code java.util.Date} object
* @see java.text.DateFormat#parseObject(String, ParsePosition) * @see java.text.DateFormat#parseObject(String, ParsePosition)
*/ */
Object parseObject(String source, ParsePosition pos); default Object parseObject(String source, ParsePosition pos){
return parse(source, pos);
}
} }

View File

@ -220,18 +220,14 @@ public class FastDateParser extends AbstractDateBasic implements DateParser {
} }
@Override @Override
public Object parseObject(final String source) throws ParseException { public Date parse(String source) throws ParseException {
return parse(source);
}
@Override
public Date parse(final String source) throws ParseException {
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) {
// Add a note re supported date range // Add a note re supported date range
if (locale.equals(JAPANESE_IMPERIAL)) { if (locale.equals(JAPANESE_IMPERIAL)) {
throw new ParseException("(The " + locale + " locale does not support dates before 1868 AD)\n" + "Unparseable date: \"" + source, pp.getErrorIndex()); throw new ParseException("(The " + locale + " locale does not support dates before 1868 AD)\n" +
"Unparseable date: \"" + source, pp.getErrorIndex());
} }
throw new ParseException("Unparseable date: " + source, pp.getErrorIndex()); throw new ParseException("Unparseable date: " + source, pp.getErrorIndex());
} }
@ -239,12 +235,7 @@ public class FastDateParser extends AbstractDateBasic implements DateParser {
} }
@Override @Override
public Object parseObject(final String source, final ParsePosition pos) { public Date parse(String source, ParsePosition pos) {
return parse(source, pos);
}
@Override
public Date parse(final String 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();
@ -253,12 +244,12 @@ public class FastDateParser extends AbstractDateBasic implements DateParser {
} }
@Override @Override
public boolean parse(final String source, final ParsePosition pos, final Calendar calendar) { public boolean parse(String source, ParsePosition pos, 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();
final int maxWidth = strategyAndWidth.getMaxWidth(lt); final int maxWidth = strategyAndWidth.getMaxWidth(lt);
if (!strategyAndWidth.strategy.parse(this, calendar, source, pos, maxWidth)) { if (false == strategyAndWidth.strategy.parse(this, calendar, source, pos, maxWidth)) {
return false; return false;
} }
} }
@ -500,9 +491,6 @@ public class FastDateParser extends AbstractDateBasic implements DateParser {
this.formatField = formatField; this.formatField = formatField;
} }
/**
* {@inheritDoc}
*/
@Override @Override
boolean isNumber() { boolean isNumber() {
return false; return false;
@ -575,9 +563,6 @@ public class FastDateParser extends AbstractDateBasic implements DateParser {
this.field = field; this.field = field;
} }
/**
* {@inheritDoc}
*/
@Override @Override
boolean isNumber() { boolean isNumber() {
return true; return true;
@ -637,9 +622,6 @@ public class FastDateParser extends AbstractDateBasic implements DateParser {
} }
private static final Strategy ABBREVIATED_YEAR_STRATEGY = new NumberStrategy(Calendar.YEAR) { private static final Strategy ABBREVIATED_YEAR_STRATEGY = new NumberStrategy(Calendar.YEAR) {
/**
* {@inheritDoc}
*/
@Override @Override
int modify(final FastDateParser parser, final int iValue) { int modify(final FastDateParser parser, final int iValue) {
return iValue < 100 ? parser.adjustYear(iValue) : iValue; return iValue < 100 ? parser.adjustYear(iValue) : iValue;
@ -726,9 +708,6 @@ public class FastDateParser extends AbstractDateBasic implements DateParser {
createPattern(sb); createPattern(sb);
} }
/**
* {@inheritDoc}
*/
@Override @Override
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) { void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
if (value.charAt(0) == '+' || value.charAt(0) == '-') { if (value.charAt(0) == '+' || value.charAt(0) == '-') {
@ -759,9 +738,6 @@ public class FastDateParser extends AbstractDateBasic implements DateParser {
createPattern(pattern); createPattern(pattern);
} }
/**
* {@inheritDoc}
*/
@Override @Override
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) { void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
if (Objects.equals(value, "Z")) { if (Objects.equals(value, "Z")) {

View File

@ -4,15 +4,26 @@ import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import java.util.Calendar; import java.util.Calendar;
import java.util.Objects;
public class CalendarUtilTest { public class CalendarUtilTest {
@Test @Test
public void formatChineseDate(){ public void formatChineseDate(){
Calendar calendar = DateUtil.parse("2018-02-24 12:13:14").toCalendar(); Calendar calendar = Objects.requireNonNull(DateUtil.parse("2018-02-24 12:13:14")).toCalendar();
final String chineseDate = CalendarUtil.formatChineseDate(calendar, false); final String chineseDate = CalendarUtil.formatChineseDate(calendar, false);
Assert.assertEquals("二〇一八年二月二十四日", chineseDate); Assert.assertEquals("二〇一八年二月二十四日", chineseDate);
final String chineseDateTime = CalendarUtil.formatChineseDate(calendar, true); final String chineseDateTime = CalendarUtil.formatChineseDate(calendar, true);
Assert.assertEquals("二〇一八年二月二十四日一十二时一十三分一十四秒", chineseDateTime); Assert.assertEquals("二〇一八年二月二十四日一十二时一十三分一十四秒", chineseDateTime);
} }
@Test(expected = IllegalArgumentException.class)
public void parseTest(){
final Calendar calendar = CalendarUtil.parse("2021-09-27 00:00:112323", false,
DatePattern.NORM_DATETIME_FORMAT);
// https://github.com/dromara/hutool/issues/1849
// 在使用严格模式时秒不正确抛出异常
DateUtil.date(calendar);
}
} }

View File

@ -1,5 +1,6 @@
package cn.hutool.core.date; package cn.hutool.core.date;
import cn.hutool.core.lang.Console;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -131,4 +132,11 @@ public class DateTimeTest {
//跨年的周返回的总是1 //跨年的周返回的总是1
Assert.assertEquals(1, date.weekOfYear()); Assert.assertEquals(1, date.weekOfYear());
} }
@Test
public void ofTest(){
String a = "2021-09-27 00:00:99";
final DateTime dateTime = new DateTime(a, DatePattern.NORM_DATETIME_FORMAT);
Console.log(dateTime);
}
} }

View File

@ -183,4 +183,12 @@ public class CsvReaderTest {
Assert.assertEquals("456", row.get(1)); Assert.assertEquals("456", row.get(1));
Assert.assertEquals("'789;0'abc", row.get(2)); Assert.assertEquals("'789;0'abc", row.get(2));
} }
@Test
public void readDisableCommentTest(){
final CsvReader reader = CsvUtil.getReader(CsvReadConfig.defaultConfig().disableComment());
final CsvData read = reader.read(ResourceUtil.getUtf8Reader("test.csv"));
final CsvRow row = read.getRow(0);
Assert.assertEquals("# 这是一行注释,读取时应忽略", row.get(0));
}
} }