fix FastDateParser

This commit is contained in:
Looly 2020-03-08 09:55:31 +08:00
parent 81dbc75ba3
commit 8ba988e028
6 changed files with 66 additions and 80 deletions

View File

@ -6,6 +6,7 @@
## 5.2.1 ## 5.2.1
### 新特性 ### 新特性
* 【core 】 修改FastDateParser策略与JDK保持一致issue#I1AXIN@Gitee
### Bug修复 ### Bug修复
* 【setting】 修复Props.toBean方法null的问题 * 【setting】 修复Props.toBean方法null的问题
* 【core 】 修复DataUtil.parseLocalDateTime无时间部分报错问题issue#I1B18H@Gitee * 【core 】 修复DataUtil.parseLocalDateTime无时间部分报错问题issue#I1B18H@Gitee

View File

@ -10,6 +10,7 @@ import java.util.TimeZone;
* @since 2.16.2 * @since 2.16.2
*/ */
public interface DateBasic { public interface DateBasic {
/** /**
* 获得日期格式化或者转换的格式 * 获得日期格式化或者转换的格式
* *

View File

@ -740,7 +740,9 @@ class FastDateParser extends AbstractDateBasic implements DateParser {
} else { } else {
final TzInfo tzInfo = tzNames.get(value.toLowerCase(locale)); final TzInfo tzInfo = tzNames.get(value.toLowerCase(locale));
cal.set(Calendar.DST_OFFSET, tzInfo.dstOffset); cal.set(Calendar.DST_OFFSET, tzInfo.dstOffset);
cal.set(Calendar.ZONE_OFFSET, tzInfo.zone.getRawOffset()); //issue#I1AXIN@Gitee
// cal.set(Calendar.ZONE_OFFSET, tzInfo.zone.getRawOffset());
cal.set(Calendar.ZONE_OFFSET, parser.getTimeZone().getRawOffset());
} }
} }
} }

View File

@ -3,13 +3,13 @@ package cn.hutool.core.date.format;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.Format; import java.text.Format;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Tuple;
/** /**
* 日期格式化器缓存<br> * 日期格式化器缓存<br>
@ -24,16 +24,16 @@ abstract class FormatCache<F extends Format> {
*/ */
static final int NONE = -1; static final int NONE = -1;
private final ConcurrentMap<MultipartKey, F> cInstanceCache = new ConcurrentHashMap<>(7); private final ConcurrentMap<Tuple, F> cInstanceCache = new ConcurrentHashMap<>(7);
private static final ConcurrentMap<MultipartKey, String> cDateTimeInstanceCache = new ConcurrentHashMap<>(7); private static final ConcurrentMap<Tuple, String> cDateTimeInstanceCache = new ConcurrentHashMap<>(7);
/** /**
* 使用默认的patterntimezone和locale获得缓存中的实例 * 使用默认的patterntimezone和locale获得缓存中的实例
* @return a date/time formatter * @return a date/time formatter
*/ */
public F getInstance() { public F getInstance() {
return getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, TimeZone.getDefault(), Locale.getDefault()); return getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, null, null);
} }
/** /**
@ -53,7 +53,7 @@ abstract class FormatCache<F extends Format> {
if (locale == null) { if (locale == null) {
locale = Locale.getDefault(); locale = Locale.getDefault();
} }
final MultipartKey key = new MultipartKey(pattern, timeZone, locale); final Tuple key = new Tuple(pattern, timeZone, locale);
F format = cInstanceCache.get(key); F format = cInstanceCache.get(key);
if (format == null) { if (format == null) {
format = createInstance(pattern, timeZone, locale); format = createInstance(pattern, timeZone, locale);
@ -129,7 +129,7 @@ abstract class FormatCache<F extends Format> {
*/ */
// package protected, for access from FastDateFormat; do not make public or protected // package protected, for access from FastDateFormat; do not make public or protected
F getDateInstance(final int dateStyle, final TimeZone timeZone, final Locale locale) { F getDateInstance(final int dateStyle, final TimeZone timeZone, final Locale locale) {
return getDateTimeInstance(Integer.valueOf(dateStyle), null, timeZone, locale); return getDateTimeInstance(dateStyle, null, timeZone, locale);
} }
/** /**
@ -145,7 +145,7 @@ abstract class FormatCache<F extends Format> {
*/ */
// package protected, for access from FastDateFormat; do not make public or protected // package protected, for access from FastDateFormat; do not make public or protected
F getTimeInstance(final int timeStyle, final TimeZone timeZone, final Locale locale) { F getTimeInstance(final int timeStyle, final TimeZone timeZone, final Locale locale) {
return getDateTimeInstance(null, Integer.valueOf(timeStyle), timeZone, locale); return getDateTimeInstance(null, timeStyle, timeZone, locale);
} }
/** /**
@ -161,18 +161,18 @@ abstract class FormatCache<F extends Format> {
*/ */
// package protected, for access from test code; do not make public or protected // package protected, for access from test code; do not make public or protected
static String getPatternForStyle(final Integer dateStyle, final Integer timeStyle, final Locale locale) { static String getPatternForStyle(final Integer dateStyle, final Integer timeStyle, final Locale locale) {
final MultipartKey key = new MultipartKey(dateStyle, timeStyle, locale); final Tuple key = new Tuple(dateStyle, timeStyle, locale);
String pattern = cDateTimeInstanceCache.get(key); String pattern = cDateTimeInstanceCache.get(key);
if (pattern == null) { if (pattern == null) {
try { try {
DateFormat formatter; DateFormat formatter;
if (dateStyle == null) { if (dateStyle == null) {
formatter = DateFormat.getTimeInstance(timeStyle.intValue(), locale); formatter = DateFormat.getTimeInstance(timeStyle, locale);
} else if (timeStyle == null) { } else if (timeStyle == null) {
formatter = DateFormat.getDateInstance(dateStyle.intValue(), locale); formatter = DateFormat.getDateInstance(dateStyle, locale);
} else { } else {
formatter = DateFormat.getDateTimeInstance(dateStyle.intValue(), timeStyle.intValue(), locale); formatter = DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale);
} }
pattern = ((SimpleDateFormat) formatter).toPattern(); pattern = ((SimpleDateFormat) formatter).toPattern();
final String previous = cDateTimeInstanceCache.putIfAbsent(key, pattern); final String previous = cDateTimeInstanceCache.putIfAbsent(key, pattern);
@ -188,62 +188,4 @@ abstract class FormatCache<F extends Format> {
} }
return pattern; return pattern;
} }
// ----------------------------------------------------------------------
/**
* <p>
* Helper class to hold multi-part Map keys
* </p>
*/
private static class MultipartKey {
private final Object[] keys;
private int hashCode;
/**
* Constructs an instance of <code>MultipartKey</code> to hold the specified objects.
*
* @param keys the set of objects that make up the key. Each key may be null.
*/
public MultipartKey(final Object... keys) {
this.keys = keys;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final MultipartKey other = (MultipartKey) obj;
return false != Arrays.equals(keys, other.keys);
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
if (hashCode == 0) {
int rc = 0;
for (final Object key : keys) {
if (key != null) {
rc = rc * 7 + key.hashCode();
}
}
hashCode = rc;
}
return hashCode;
}
}
} }

View File

@ -1,12 +1,12 @@
package cn.hutool.core.lang; package cn.hutool.core.lang;
import cn.hutool.core.clone.CloneSupport;
import cn.hutool.core.collection.ArrayIter;
import java.io.Serializable; import java.io.Serializable;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import cn.hutool.core.clone.CloneSupport;
import cn.hutool.core.collection.ArrayIter;
/** /**
* 不可变数组类型用于多值返回<br> * 不可变数组类型用于多值返回<br>
* 多值可以支持每个元素值类型不同 * 多值可以支持每个元素值类型不同
@ -17,7 +17,9 @@ import cn.hutool.core.collection.ArrayIter;
public class Tuple extends CloneSupport<Tuple> implements Iterable<Object>, Serializable{ public class Tuple extends CloneSupport<Tuple> implements Iterable<Object>, Serializable{
private static final long serialVersionUID = -7689304393482182157L; private static final long serialVersionUID = -7689304393482182157L;
private Object[] members; private final Object[] members;
private int hashCode;
private boolean cacheHash;
/** /**
* 构造 * 构造
@ -45,12 +47,31 @@ public class Tuple extends CloneSupport<Tuple> implements Iterable<Object>, Seri
public Object[] getMembers(){ public Object[] getMembers(){
return this.members; return this.members;
} }
/**
* 缓存Hash值当为true时此对象的hash值只被计算一次常用于Tuple中的值不变时使用
* 注意当为true时member变更对象后hash值不会变更
*
* @param cacheHash 是否缓存hash值
* @return this
* @since 5.2.1
*/
public Tuple setCacheHash(boolean cacheHash){
this.cacheHash = cacheHash;
return this;
}
@Override @Override
public int hashCode() { public int hashCode() {
if(this.cacheHash && 0 != this.hashCode){
return this.hashCode;
}
final int prime = 31; final int prime = 31;
int result = 1; int result = 1;
result = prime * result + Arrays.deepHashCode(members); result = prime * result + Arrays.deepHashCode(members);
if(this.cacheHash){
this.hashCode = result;
}
return result; return result;
} }
@ -74,8 +95,9 @@ public class Tuple extends CloneSupport<Tuple> implements Iterable<Object>, Seri
return Arrays.toString(members); return Arrays.toString(members);
} }
@SuppressWarnings("NullableProblems")
@Override @Override
public Iterator<Object> iterator() { public Iterator<Object> iterator() {
return new ArrayIter<Object>(members); return new ArrayIter<>(members);
} }
} }

View File

@ -2,6 +2,7 @@ package cn.hutool.core.date;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.BetweenFormater.Level; import cn.hutool.core.date.BetweenFormater.Level;
import cn.hutool.core.date.format.FastDateFormat;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -16,6 +17,7 @@ import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Objects; import java.util.Objects;
import java.util.TimeZone; import java.util.TimeZone;
@ -505,23 +507,39 @@ public class DateUtilTest {
Assert.assertEquals("2018-09-13 13:34:39.999", dateStr); Assert.assertEquals("2018-09-13 13:34:39.999", dateStr);
} }
@SuppressWarnings("ConstantConditions")
@Test @Test
public void parseCSTTest(){ public void parseCSTTest(){
String dateStr = "Wed Sep 16 11:26:23 CST 2009"; String dateStr = "Wed Sep 16 11:26:23 CST 2009";
SimpleDateFormat sdf = new SimpleDateFormat(DatePattern.JDK_DATETIME_PATTERN, Locale.US);
final DateTime parse = DateUtil.parse(dateStr, sdf);
DateTime dateTime = DateUtil.parseCST(dateStr); DateTime dateTime = DateUtil.parseCST(dateStr);
Assert.assertEquals("2009-09-17 01:26:23", dateTime.toString()); Assert.assertEquals(parse, dateTime);
dateTime = DateUtil.parse(dateStr); dateTime = DateUtil.parse(dateStr);
Assert.assertEquals("2009-09-17 01:26:23", dateTime.toString()); Assert.assertEquals(parse, dateTime);
}
@Test
public void parseCSTTest2(){
String dateStr = "Wed Sep 16 11:26:23 CST 2009";
SimpleDateFormat sdf = new SimpleDateFormat(DatePattern.JDK_DATETIME_PATTERN, Locale.US);
sdf.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
final DateTime parse = DateUtil.parse(dateStr, sdf);
FastDateFormat fdf = FastDateFormat.getInstance(DatePattern.JDK_DATETIME_PATTERN, TimeZone.getTimeZone("America/Chicago"), Locale.US);
final DateTime parse2 = DateUtil.parse(dateStr, fdf);
Assert.assertEquals(parse, parse2);
} }
@SuppressWarnings("ConstantConditions")
@Test @Test
public void parseJDkTest() { public void parseJDkTest() {
String dateStr = "Thu May 16 17:57:18 GMT+08:00 2019"; String dateStr = "Thu May 16 17:57:18 GMT+08:00 2019";
DateTime time = DateUtil.parse(dateStr); DateTime time = DateUtil.parse(dateStr);
Assert.assertEquals("2019-05-16 17:57:18", time.toString()); Assert.assertEquals("2019-05-16 17:57:18", Objects.requireNonNull(time).toString());
} }
@Test @Test