diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1aff6a9a81..292f40901b 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,7 @@
* 【core 】 修复Tailer指定初始读取行数的计算错误问题(issue#IA77ML@Gitee)
* 【http 】 修复getFileNameFromDisposition获取头错误问题(issue#3632@Github)
* 【core 】 修复\n#出现在双引号中解析错误问题(issue#IA8WE0@Gitee)
+* 【core 】 修复FastDatePrinter处理YY错误问题(issue#3641@Github)
-------------------------------------------------------------------------------------------------------------
# 5.8.28(2024-05-29)
diff --git a/hutool-core/src/main/java/cn/hutool/core/date/format/FastDatePrinter.java b/hutool-core/src/main/java/cn/hutool/core/date/format/FastDatePrinter.java
index fb5c2704bd..9be99cdbef 100644
--- a/hutool-core/src/main/java/cn/hutool/core/date/format/FastDatePrinter.java
+++ b/hutool-core/src/main/java/cn/hutool/core/date/format/FastDatePrinter.java
@@ -23,19 +23,24 @@
public class FastDatePrinter extends AbstractDateBasic implements DatePrinter {
private static final long serialVersionUID = -6305750172255764887L;
- /** 规则列表. */
+ /**
+ * 规则列表.
+ */
private transient Rule[] rules;
- /** 估算最大长度. */
+ /**
+ * 估算最大长度.
+ */
private transient int mMaxLengthEstimate;
// Constructor
// -----------------------------------------------------------------------
+
/**
* 构造,内部使用
*
- * @param pattern 使用{@link java.text.SimpleDateFormat} 相同的日期格式
+ * @param pattern 使用{@link java.text.SimpleDateFormat} 相同的日期格式
* @param timeZone 非空时区{@link TimeZone}
- * @param locale 非空{@link Locale} 日期地理位置
+ * @param locale 非空{@link Locale} 日期地理位置
*/
public FastDatePrinter(String pattern, TimeZone timeZone, Locale locale) {
super(pattern, timeZone, locale);
@@ -50,7 +55,7 @@ private void init() {
rules = rulesList.toArray(new Rule[0]);
int len = 0;
- for (int i = rules.length; --i >= 0;) {
+ for (int i = rules.length; --i >= 0; ) {
len += rules[i].estimateLength();
}
@@ -59,6 +64,7 @@ private void init() {
// Parse the pattern
// -----------------------------------------------------------------------
+
/**
*
* Returns a list of Rules given a pattern.
@@ -207,7 +213,7 @@ protected List parsePattern() {
* Performs the parsing of tokens.
*
*
- * @param pattern the pattern
+ * @param pattern the pattern
* @param indexRef index references
* @return parsed token
*/
@@ -267,7 +273,7 @@ protected String parseToken(String pattern, int[] indexRef) {
* Gets an appropriate rule for the padding required.
*
*
- * @param field the field to get a rule for
+ * @param field the field to get a rule for
* @param padding the padding required
* @return a new rule with the correct padding
*/
@@ -364,8 +370,8 @@ private String applyRulesToString(Calendar c) {
*
*
* @param calendar the calendar to format
- * @param buf the buffer to format into
- * @param the Appendable class type, usually StringBuilder or StringBuffer.
+ * @param buf the buffer to format into
+ * @param the Appendable class type, usually StringBuilder or StringBuffer.
* @return the specified string buffer
*/
private B applyRules(Calendar calendar, B buf) {
@@ -380,7 +386,7 @@ private B applyRules(Calendar calendar, B buf) {
}
/**
- *估算生成的日期字符串长度
+ * 估算生成的日期字符串长度
* 实际生成的字符串长度小于或等于此值
*
* @return 日期字符串长度
@@ -391,11 +397,12 @@ public int getMaxLengthEstimate() {
// Serializing
// -----------------------------------------------------------------------
+
/**
* Create the object after serialization. This implementation reinitializes the transient properties.
*
* @param in ObjectInputStream from which the object is being deserialized.
- * @throws IOException if there is an IO issue.
+ * @throws IOException if there is an IO issue.
* @throws ClassNotFoundException if a class cannot be found.
*/
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
@@ -407,7 +414,7 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE
* Appends two digits to the given buffer.
*
* @param buffer the buffer to append to.
- * @param value the value to append digits from.
+ * @param value the value to append digits from.
*/
private static void appendDigits(Appendable buffer, int value) throws IOException {
buffer.append((char) (value / 10 + '0'));
@@ -420,7 +427,7 @@ private static void appendDigits(Appendable buffer, int value) throws IOExceptio
* Appends all digits to the given buffer.
*
* @param buffer the buffer to append to.
- * @param value the value to append digits from.
+ * @param value the value to append digits from.
*/
private static void appendFullDigits(Appendable buffer, int value, int minFieldWidth) throws IOException {
// specialized paths for 1 to 4 digits -> avoid the memory allocation from the temporary work array
@@ -490,6 +497,7 @@ private static void appendFullDigits(Appendable buffer, int value, int minFieldW
// Rules
// -----------------------------------------------------------------------
+
/**
* 规则
*/
@@ -504,7 +512,7 @@ private interface Rule {
/**
* Appends the value of the specified calendar to the output buffer based on the rule implementation.
*
- * @param buf the output buffer
+ * @param buf the output buffer
* @param calendar calendar to be appended
* @throws IOException if an I/O error occurs
*/
@@ -521,7 +529,7 @@ private interface NumberRule extends Rule {
* Appends the specified value to the output buffer based on the rule implementation.
*
* @param buffer the output buffer
- * @param value the value to be appended
+ * @param value the value to be appended
* @throws IOException if an I/O error occurs
*/
void appendTo(Appendable buffer, int value) throws IOException;
@@ -601,7 +609,7 @@ private static class TextField implements Rule {
/**
* Constructs an instance of {@code TextField} with the specified field and values.
*
- * @param field the field
+ * @param field the field
* @param values the field values
*/
TextField(int field, String[] values) {
@@ -615,7 +623,7 @@ private static class TextField implements Rule {
@Override
public int estimateLength() {
int max = 0;
- for (int i = mValues.length; --i >= 0;) {
+ for (int i = mValues.length; --i >= 0; ) {
final int len = mValues[i].length();
if (len > max) {
max = len;
@@ -691,7 +699,6 @@ private static class UnpaddedMonthField implements NumberRule {
/**
* Constructs an instance of {@code UnpaddedMonthField}.
- *
*/
UnpaddedMonthField() {
}
@@ -738,7 +745,7 @@ private static class PaddedNumberField implements NumberRule {
* Constructs an instance of {@code PaddedNumberField}.
*
* @param field the field
- * @param size size of the output field
+ * @param size size of the output field
*/
PaddedNumberField(int field, int size) {
if (size < 3) {
@@ -1038,7 +1045,12 @@ public int estimateLength() {
@Override
public void appendTo(Appendable buffer, Calendar calendar) throws IOException {
- mRule.appendTo(buffer, calendar.getWeekYear());
+ int weekYear = calendar.getWeekYear();
+ if (mRule instanceof TwoDigitYearField) {
+ // issue#3641
+ weekYear %= 100;
+ }
+ mRule.appendTo(buffer, weekYear);
}
@Override
@@ -1056,10 +1068,10 @@ public void appendTo(Appendable buffer, int value) throws IOException {
* Gets the time zone display name, using a cache for performance.
*
*
- * @param tz the zone to query
+ * @param tz the zone to query
* @param daylight true if daylight savings
- * @param style the style to use {@code TimeZone.LONG} or {@code TimeZone.SHORT}
- * @param locale the locale to use
+ * @param style the style to use {@code TimeZone.LONG} or {@code TimeZone.SHORT}
+ * @param locale the locale to use
* @return the textual name of the time zone
*/
static String getTimeZoneDisplay(TimeZone tz, boolean daylight, int style, Locale locale) {
@@ -1091,8 +1103,8 @@ private static class TimeZoneNameRule implements Rule {
* Constructs an instance of {@code TimeZoneNameRule} with the specified properties.
*
* @param timeZone the time zone
- * @param locale the locale
- * @param style the style
+ * @param locale the locale
+ * @param style the style
*/
TimeZoneNameRule(TimeZone timeZone, Locale locale, int style) {
mLocale = locale;
@@ -1269,6 +1281,7 @@ public void appendTo(final Appendable buffer, final Calendar calendar) throws IO
}
// ----------------------------------------------------------------------
+
/**
*
* Inner class that acts as a compound key for time zone names.
@@ -1284,8 +1297,8 @@ private static class TimeZoneDisplayKey {
*
* @param timeZone the time zone
* @param daylight adjust the style for daylight saving time if {@code true}
- * @param style the timezone style
- * @param locale the timezone locale
+ * @param style the timezone style
+ * @param locale the timezone locale
*/
TimeZoneDisplayKey(final TimeZone timeZone, final boolean daylight, final int style, final Locale locale) {
mTimeZone = timeZone;
diff --git a/hutool-core/src/test/java/cn/hutool/core/date/FastDateFormatTest.java b/hutool-core/src/test/java/cn/hutool/core/date/FastDateFormatTest.java
new file mode 100644
index 0000000000..46d67cbcf1
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/date/FastDateFormatTest.java
@@ -0,0 +1,58 @@
+package cn.hutool.core.date;
+
+import cn.hutool.core.date.format.FastDateFormat;
+import org.junit.Test;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+import static org.junit.Assert.assertEquals;
+
+public class FastDateFormatTest {
+ private static final TimeZone timezone = TimeZone.getTimeZone("Etc/Utc");
+
+ private static FastDateFormat getHutoolInstance(String pattern) {
+ return FastDateFormat.getInstance(pattern, timezone);
+ }
+
+ @Test
+ public void yearTest() {
+ Date date = DateUtil.date(0L);
+
+ assertEquals(
+ "1970-01-01 00:00:00",
+ getHutoolInstance("yyyy-MM-dd HH:mm:ss").format(date)
+ );
+
+ assertEquals(
+ "1970-01-01 00:00:00",
+ getHutoolInstance("YYYY-MM-dd HH:mm:ss").format(date)
+ );
+
+ assertEquals(
+ "1970",
+ getHutoolInstance("YYYY").format(date)
+ );
+
+ assertEquals(
+ "70",
+ getHutoolInstance("yy").format(date)
+ );
+ }
+
+ @Test
+ public void weekYearTest() {
+ Date date = DateUtil.date(0L);
+
+ assertEquals(
+ "70",
+ new SimpleDateFormat("YY").format(date)
+ );
+
+ assertEquals(
+ "70",
+ getHutoolInstance("YY").format(date)
+ );
+ }
+}