结论先行
在Java中使用 YYYY-MM-dd
格式化日期时,若日期所在的周跨年,年份可能会被错误计算为下一年(如2021年12月26日显示为2022年)。而使用 yyyy-MM-dd
会始终返回正确的年份。问题的根源是 YYYY
和 yyyy
对年份的定义不同:YYYY
是「基于周的年份」,而 yyyy
是「自然年份」。
文章持续更新,可以微信搜一搜「 半个脑袋儿 」第一时间阅读
问题复现
尝试用以下代码格式化 2021-12-26
:
java
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateFormatDemo {
public static void main(String[] args) {
SimpleDateFormat sdf1 = new SimpleDateFormat("YYYY-MM-dd");
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd");
Date date = new Date(121, 11, 26); // 2021年12月26日
System.out.println(sdf1.format(date)); // 输出:2022-12-26
System.out.println(sdf2.format(date)); // 输出:2021-12-26
}
}
结果差异明显,YYYY
的年份跳到了2022年。
原因解析
1. YYYY
是「基于周的年份」
- 定义 :
YYYY
遵循 ISO 8601 标准,表示「日期所在周所属的年份」。 - 周的计算规则 :
- 默认一周从 周日开始 ,到 周六结束 (可通过
Locale
调整,如某些地区从周一开始)。 - 若某周跨年(即该周包含新年的第一天),则这一周会被归属到新年所在的年份。
- 默认一周从 周日开始 ,到 周六结束 (可通过
以 2021年12月26日 为例:
- 该周为 2021年12月26日(周日)至2022年1月1日(周六)。
- 由于这一周的大部分天数(4天)属于2022年,因此
YYYY
会将其归属到2022年。
2. yyyy
是「自然年份」
- 定义 :
yyyy
直接表示日期所在的自然年份,与周无关。 - 无论日期是否跨周或跨月,始终返回实际年份。
如何避免踩坑?
-
优先使用
yyyy
除非明确需要基于周的年份计算,否则格式化日期时应使用
yyyy
。 -
理解业务场景
- 财务系统、周报统计等可能需要
YYYY
(如按周生成报表)。 - 普通日期场景(如订单时间、日志记录)应使用
yyyy
。
- 财务系统、周报统计等可能需要
-
注意
Locale
的影响- 不同地区的周起始日不同(如美国默认周日开始,欧洲多从周一开始)。
- 可通过
SimpleDateFormat.setLocale()
或DateTimeFormatter.withLocale()
指定区域。
-
Java 8+ 的替代方案
使用
DateTimeFormatter
时,yyyy
对应Y
和u
的行为更清晰:javaLocalDate date = LocalDate.of(2021, 12, 26); DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("YYYY-MM-dd"); DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd"); System.out.println(date.format(formatter1)); // 2022-12-26 System.out.println(date.format(formatter2)); // 2021-12-26
总结
YYYY
与yyyy
的区别:基于周的年份 vs 自然年份。- 根本原因 :跨年周的归属规则导致
YYYY
的年份偏移。 - 最佳实践 :若无特殊需求,始终使用
yyyy-MM-dd
格式化日期。
一个小小的格式字符差异,可能导致跨年时的严重逻辑错误。切记:日期格式化无小事!