当前位置: 首页 > 面试题库 >

为什么新的SimpleDateFormat对象包含错误年份的日历?

曾景龙
2023-03-14
问题内容

我遇到了一种奇怪的行为,这使我感到好奇,并且还没有令人满意的解释。

为简单起见,我将注意到的症状减少到以下代码:

import java.text.SimpleDateFormat;
import java.util.GregorianCalendar;

public class CalendarTest {
    public static void main(String[] args) {
        System.out.println(new SimpleDateFormat().getCalendar());
        System.out.println(new GregorianCalendar());
    }
}

运行此代码时,我得到的内容与以下输出非常相似:

java.util.GregorianCalendar [time = -1274641455755,areFieldsSet =

true,areAllFieldsSet = true,lenient = true,zone = sun.util.calendar.ZoneInfo
[id =“ America / Los_Angeles”,offset = -28800000,dstSavings = 3600000,
useDaylight = true,transitions = 185,lastRule = java.util.SimpleTimeZone [id
= America / Los_Angeles,offset = -28800000,dstSavings = 3600000,useDaylight
= true,startYear = 0,startMode = 3,startMonth = 2,startDay = 8
,startDayOfWeek = 1,startTime = 7200000,startTimeMode = 0,endMode =
3,endMonth = 10,endDay = 1,endDayOfWeek = 1,endTime = 7200000,endTimeMode =
0]],firstDayOfWeek = 1,minimalDaysInFirstWeek = 1,ERA = 1 ,YEAR = 1929,MONTH
= 7,WEEK_OF_YEAR = 32,WEEK_OF_MONTH = 2,DAY_OF_MONTH = 10,DAY_OF_YEAR =
222,DAY_OF_WEEK = 7,DAY_OF_WEEK_IN_MONTH = 2,AM_PM = 1,HOUR = 8,HOUR_OF_DAY
= 20,MINUTD = 55, = 44,MILLISECOND = 245,ZONE_OFFSET = -28800000,DST_OFFSET
= 0]
java.util.GregorianCalendar [time = 1249962944248,areFieldsSet =
true,areAllFieldsSet = true,lenient = true,zone = sun.util.calendar.ZoneInfo
[id =“ America / Los_Angeles”,offset = -28800000,dstSavings =
3600000,useDaylight = true,transitions = 185,lastRule =
java.util.SimpleTimeZone [id = America / Los_Angeles,offset =
-28800000,dstSavings = 3600000,useDaylight = true,startYear = 0,startMode =
3,startMonth = 2,startDay = 8, startDayOfWeek = 1,startTime =
7200000,startTimeMode = 0,endMode = 3,endMonth = 10,endDay = 1,endDayOfWeek
= 1,endTime = 7200000,endTimeMode = 0]],firstDayOfWeek =
1,minimalDaysInFirstWeek = 1,ERA = 1, YEAR = 2009,MONTH = 7,WEEK_OF_YEAR =
33,WEEK_OF_MONTH = 3,DAY_OF_MONTH = 10,DAY_OF_YEAR = 222,DAY_OF_WEEK =
2,DAY_OF_WEEK_IN_MONTH = 2,AM_PM = 1,HOUR = 8,HOUR_OF_DAY = 20,MINUTE =
55,SECOND 44,MILLISECOND = 248,ZONE_OFFSET = -28800000,DST_OFFSET = 3600000]

(如果我提供类似于"yyyy-MM-dd"SimpleDateFormat 的有效格式字符串,也会发生同样的事情。)

原谅可怕的无包装线,但这是比较两者的最简单方法。如果滚动到大约2/3的位置,您会看到日历的YEAR值分别为1929和2009。(还有一些其他差异,例如一年中的星期几,星期几和DST偏移量。)两者显然都是GregorianCalendar的实例,但是它们之所以令人困惑是令人困惑的。

据我所知,格式化程序在传递给它的Date对象格式化时会产生准确的结果。显然,正确的功能比正确的参考年份更重要,但是差异仍然令人感到困惑。我认为我不必为了获得当前年份而将日历设置在全新的日期html" target="_blank">格式化程序上…

我已经在具有Java 5(OS X 10.4,PowerPC)和Java 6(OS X
10.6,Intel)的Mac上进行了测试,结果相同。由于这是Java库API,因此我假设它在所有平台上的行为都相同。对这里发生的事情有任何见解吗?

(注意:此SO问题有些相关,但不相同。)

编辑:

以下所有答案均有助于解释此行为。事实证明,SimpleDateFormat的Javadocs
实际上在某种程度上对此进行了说明:

“要使用缩写的年份模式(“ y”或“
yy”)进行解析,SimpleDateFormat必须解释相对于某个世纪的缩写年份。它通过将日期调整为SimpleDateFormat实例之前的80年和之后的20年内来实现此目的。被建造。”

因此,他们没有花时间解析日期,而是默认将内部日历设置为80年。该部分本身没有记录,但是当您知道它时,所有部分都可以放在一起。


问题答案:

我不确定汤姆为什么会说“这与序列化有关”,但是他有正确的说法:

private void initializeDefaultCentury() {
    calendar.setTime( new Date() );
    calendar.add( Calendar.YEAR, -80 );
    parseAmbiguousDatesAsAfter(calendar.getTime());
}

这是SimpleDateFormat.java中的第813行,该过程很晚。到那时为止,年份是正确的(日期部分的其余部分也是正确的),然后将其减80。

啊哈!

对的调用parseAmbiguousDatesAsAfter()是与以下调用相同的私有函数set2DigitYearStart()

/* Define one-century window into which to disambiguate dates using
 * two-digit years.
 */
private void parseAmbiguousDatesAsAfter(Date startDate) {
    defaultCenturyStart = startDate;
    calendar.setTime(startDate);
    defaultCenturyStartYear = calendar.get(Calendar.YEAR);
}

/**
 * Sets the 100-year period 2-digit years will be interpreted as being in
 * to begin on the date the user specifies.
 *
 * @param startDate During parsing, two digit years will be placed in the range
 * <code>startDate</code> to <code>startDate + 100 years</code>.
 * @see #get2DigitYearStart
 * @since 1.2
 */
public void set2DigitYearStart(Date startDate) {
    parseAmbiguousDatesAsAfter(startDate);
}

现在我知道发生了什么。彼得在有关“苹果和橙子”的评论中是对的!SimpleDateFormat中的年份是“默认世纪”的第一年,该范围是两位数字的年份字符串(例如,“
1/12/14”)被解释为的范围。参见http://java.sun.com/j2se/1.4.2/docs/api/java/text/SimpleDateFormat.html#get2DigitYearStart%28%29:

因此,为了获得“效率”而不是清晰度,SimpleDateFormat中的年份用于存储“解析两位数年份的100年期间的开始”,而不是当前年份!

谢谢,这很有趣-终于让我安装了jdk源(我的/分区上只有4GB的总空间。)



 类似资料:
  • 问题内容: 我有字符串格式的日期,我想将其解析为使用日期。 我将其解析为: 但是奇怪的是,如果我传递的是“ 03-08- 201309 hjhkjhk ”或“ 03- 88 -2013”​​或 43 -88-201378,它不会抛出错误,而是对其进行解析。 为此,我必须编写正则表达式模式以检查日期的输入是否正确。但是为什么会这样呢? 代码: 问题答案: 您应该使用: 我不确定是否可以捕获 到 您想

  • 问题内容: 哪里是。 当我执行以上操作时,时间变成 我在做错什么?这是一个错误吗?我希望它是2013年的第一周,而不是最后一周。 问题答案: 看这里。 该weekyear,模式字母的,周是为了与使用,weekyear。从javadoc的 将模式更改为 然后您将解析为 根据您的情况,2013年的第一周始于2012年。

  • 我想有一个日期选择器,用户可以从日期选择器中只选择月份和年份,我还需要限制从以前的日期选择器中选择月份和年份的下一个日期选择器... 有人能帮我解脱吗?

  • 问题内容: 我可以从Google那里收集到的是: Hibernate使用代理对象来实现延迟加载。当我们请求从数据库中加载对象,并且获取的对象具有对另一个具体对象的引用时,Hibernate返回一个代理而不是具体的关联对象。 Hibernate使用字节码检测(由Javassist提供)创建代理对象。Hibernate在运行时使用代码生成库创建我们的实体类的子类,并将实际对象替换为新创建的代理。 那么

  • 我能从谷歌得到的信息是: > Hibernate使用代理对象实现延迟加载。当我们请求从数据库加载对象,并且获取的对象引用了另一个具体对象时,Hibernate会返回一个代理,而不是具体的关联对象。 Hibernate使用字节码检测(由javhelp提供)创建代理对象。Hibernate在运行时使用代码生成库创建我们实体类的子类,并用新创建的代理替换实际对象。 那么,代理对象到底包含什么? 它是否包

  • 问题内容: 当我运行以下代码片段时 我收到以下消息: 并有4个条目(与打印相对应的3的a为0(实际错误为18) 但是,如果我替换为,则会得到预期的结果: 消息是,我在…中只有一个条目 有什么办法可以避免??的奇怪行为? 问题答案: 不,您无法避免这种行为。它是TdsParser.ThrowExceptionAndWarning()编写方式的结果 特别是这条线 我的猜测是,无论出于何种原因,对于Ex