Browse Source

fix FastDateParser

Looly 5 years ago
parent
commit
8ba988e028

+ 1 - 0
CHANGELOG.md

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

+ 1 - 0
hutool-core/src/main/java/cn/hutool/core/date/format/DateBasic.java

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

+ 3 - 1
hutool-core/src/main/java/cn/hutool/core/date/format/FastDateParser.java

@@ -740,7 +740,9 @@ class FastDateParser extends AbstractDateBasic implements DateParser {
 			} else {
 				final TzInfo tzInfo = tzNames.get(value.toLowerCase(locale));
 				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());
 			}
 		}
 	}

+ 11 - 69
hutool-core/src/main/java/cn/hutool/core/date/format/FormatCache.java

@@ -3,13 +3,13 @@ package cn.hutool.core.date.format;
 import java.text.DateFormat;
 import java.text.Format;
 import java.text.SimpleDateFormat;
-import java.util.Arrays;
 import java.util.Locale;
 import java.util.TimeZone;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
 import cn.hutool.core.lang.Assert;
+import cn.hutool.core.lang.Tuple;
 
 /**
  * 日期格式化器缓存<br>
@@ -24,16 +24,16 @@ abstract class FormatCache<F extends Format> {
 	 */
 	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);
 
 	/**
 	 * 使用默认的pattern、timezone和locale获得缓存中的实例
 	 * @return a date/time formatter
 	 */
 	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) {
 			locale = Locale.getDefault();
 		}
-		final MultipartKey key = new MultipartKey(pattern, timeZone, locale);
+		final Tuple key = new Tuple(pattern, timeZone, locale);
 		F format = cInstanceCache.get(key);
 		if (format == null) {
 			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
 	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
 	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
 	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);
 		if (pattern == null) {
 			try {
 				DateFormat formatter;
 				if (dateStyle == null) {
-					formatter = DateFormat.getTimeInstance(timeStyle.intValue(), locale);
+					formatter = DateFormat.getTimeInstance(timeStyle, locale);
 				} else if (timeStyle == null) {
-					formatter = DateFormat.getDateInstance(dateStyle.intValue(), locale);
+					formatter = DateFormat.getDateInstance(dateStyle, locale);
 				} else {
-					formatter = DateFormat.getDateTimeInstance(dateStyle.intValue(), timeStyle.intValue(), locale);
+					formatter = DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale);
 				}
 				pattern = ((SimpleDateFormat) formatter).toPattern();
 				final String previous = cDateTimeInstanceCache.putIfAbsent(key, pattern);
@@ -188,62 +188,4 @@ abstract class FormatCache<F extends Format> {
 		}
 		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;
-		}
-	}
-
 }

+ 27 - 5
hutool-core/src/main/java/cn/hutool/core/lang/Tuple.java

@@ -1,12 +1,12 @@
 package cn.hutool.core.lang;
 
+import cn.hutool.core.clone.CloneSupport;
+import cn.hutool.core.collection.ArrayIter;
+
 import java.io.Serializable;
 import java.util.Arrays;
 import java.util.Iterator;
 
-import cn.hutool.core.clone.CloneSupport;
-import cn.hutool.core.collection.ArrayIter;
-
 /**
  * 不可变数组类型,用于多值返回<br>
  * 多值可以支持每个元素值类型不同
@@ -17,7 +17,9 @@ import cn.hutool.core.collection.ArrayIter;
 public class Tuple extends CloneSupport<Tuple> implements Iterable<Object>, Serializable{
 	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(){
 		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
 	public int hashCode() {
+		if(this.cacheHash && 0 != this.hashCode){
+			return this.hashCode;
+		}
 		final int prime = 31;
 		int result = 1;
 		result = prime * result + Arrays.deepHashCode(members);
+		if(this.cacheHash){
+			this.hashCode = result;
+		}
 		return result;
 	}
 
@@ -74,8 +95,9 @@ public class Tuple extends CloneSupport<Tuple> implements Iterable<Object>, Seri
 		return Arrays.toString(members);
 	}
 
+	@SuppressWarnings("NullableProblems")
 	@Override
 	public Iterator<Object> iterator() {
-		return new ArrayIter<Object>(members);
+		return new ArrayIter<>(members);
 	}
 }

+ 23 - 5
hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java

@@ -2,6 +2,7 @@ package cn.hutool.core.date;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.date.BetweenFormater.Level;
+import cn.hutool.core.date.format.FastDateFormat;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -16,6 +17,7 @@ import java.util.Calendar;
 import java.util.Date;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.TimeZone;
@@ -505,23 +507,39 @@ public class DateUtilTest {
 		Assert.assertEquals("2018-09-13 13:34:39.999", dateStr);
 	}
 
-	@SuppressWarnings("ConstantConditions")
 	@Test
 	public void parseCSTTest(){
 		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);
-		Assert.assertEquals("2009-09-17 01:26:23", dateTime.toString());
+		Assert.assertEquals(parse, dateTime);
 
 		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
 	public void parseJDkTest() {
 		String dateStr = "Thu May 16 17:57:18 GMT+08:00 2019";
 		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