2017-06-20 40 views
2

当我运行下面的代码:Calendar.get(Calendar.DAY_OF_MONTH)返回错误的一天

int year = 2017; 
int month = 7; 
int dayOfMonth = 10; 

Calendar dateOfBirth = new GregorianCalendar(year, month, dayOfMonth); 
dateOfBirth.getTime(); // See http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4827490 
dateOfBirth.setTimeZone(TimeZone.getTimeZone("UTC")); 

System.out.println("Original Day Of Month:\t\t" + dayOfMonth); 
System.out.println("Calendar:\t\t\t" + dateOfBirth); 
System.out.println("Calendar substring:\t\t" + dateOfBirth.toString().substring(dateOfBirth.toString().indexOf("DAY_OF_MONTH"), dateOfBirth.toString().indexOf("DAY_OF_MONTH") + 15)); 
System.out.println("Formatted date:\t\t\t" + new SimpleDateFormat("dd").format(dateOfBirth.getTime())); 
System.out.println("get(Calendar.DAY_OF_MONTH):\t" + dateOfBirth.get(Calendar.DAY_OF_MONTH)); 

我得到这样的输出:

Original Day Of Month:  10 
Calendar:     java.util.GregorianCalendar[time=1502312400000,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=?,YEAR=2017,MONTH=7,WEEK_OF_YEAR=?,WEEK_OF_MONTH=?,DAY_OF_MONTH=10,DAY_OF_YEAR=?,DAY_OF_WEEK=?,DAY_OF_WEEK_IN_MONTH=?,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=?,ZONE_OFFSET=?,DST_OFFSET=?] 
Calendar substring:   DAY_OF_MONTH=10 
Formatted date:    10 
get(Calendar.DAY_OF_MONTH): 9 

正如你所看到的,通过dateOfBirth.get(Calendar.DAY_OF_MONTH)返回的值是不正确的。

我使用的UTC是因为它是出生日期,我不希望它依赖于特定的时区。为了正确地做到这一点,我需要致电dateOfBirth.getTime() - 请参阅http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4827490。 如果我拿出那个电话,这个工作正常,但后来我遇到了其他的错误。

任何想法我可以做些什么来准确地得到正确的一个月的一天?

我正在使用Java v1.7.0_79,并且,万一它很重要,我目前处于GMT + 3。

+0

我想第0天只是代表本月的第一天,因为您希望第10天输出第9天。如果您将日期输出格式化为dd-MM-yyyy,那么您将获得日历的javadoc所说的内容,因此10-08-2017 最后,请记住,在结构中,您必须添加1以回到人类约定,其中数字从1开始,而不是0 – Webster

+1

@Webster月中的某天不从索引0开始。它从1开始。 –

+2

我知道您正在使用Java 7,并且可能暂时不打算升级到Java 8。但是,如果您的程序正在处理日期和/或时区,请认真考虑获取[ThreeTen Backport](http://www.threeten.org/threetenbp/)并开始使用现代课程。通过'LocalDate'你知道日期是什么。一般来说,现代课程更适合工作,并且少有像这样的惊喜。如果最终你升级了你的Java版本,你将会做好充分的准备,因为这些类是在Java 8及更高版本中内置的。 –

回答

3

高兴的是,the answer by Robby Cornelissen解决您的问题。我想解释发生了什么,以及为什么这个答案有帮助。

当您使用三ARG GregorianCalendar构造,它“构造具有给定日期GregorianCalendar与默认语言环境的时区设置。”引用的文档;斜体是我的。在你的情况下,这是格林威治标准时间+3(现在;重要的是,在8月,它仍然是格林威治标准时间东西)。

文档不太清楚的是,一天中的小时数设置为0,因此您确实得到了2017年8月10日0:00时区域偏移+3:00的时间点。你真的想要GregorianCalendar不用担心时间,但是没有这样的事情。这是我推荐LocalDate而不是我在评论中提到的原因之一。

当您更改时区时,时间点保持不变,但由于您在自己的时区GMT之前,因此您现在有2017年8月9日21:00 UTC。所以9是现在正确的月份价值。

我认为最大的惊喜是toString方法仍然产生DAY_OF_MONTH=10。当GregorianCalendar计算出哪些字段对我来说很晦涩,但显然还记得此时的旧值。

要格式化,请使用new SimpleDateFormat("dd")。此格式化程序使用GMT + 3的默认时区和日历对象的时间点,正确生成10.如果您希望它从日历中生成月份,则可能已设置其时区到UTC也是如此。

dateOfBirth.get(Calendar.DAY_OF_MONTH)产生9的正确值。

有趣的是,如果这一呼吁后,我重复一下您行:

System.out.println("Calendar substring:\t\t" 
      + dateOfBirth.toString().substring(dateOfBirth.toString().indexOf("DAY_OF_MONTH"), 
        dateOfBirth.toString().indexOf("DAY_OF_MONTH") + 15)); 

这一次,它产生:

Calendar substring:  DAY_OF_MONTH=9, 

所以那场已经被计算。

在Robby Cornelissen的代码中,日历对象与UTC时区一起诞生,所以当您设置日期时,它确实设置为 - 2017年7月10日这一天,但是0:00 UTC。这对应于您所在时区的3:00,但日期也是7月10日,因此其余代码将生成您预期的10。然而,格林威治西部时区的用户现在会遇到问题,因为他们的SimpleDateFormat将使用他们的时区,这是在格林尼治标准时间后面,因此他们将看到格式化日期09

为了完整起见,这里的ThreeTen(反向移植)版本:

LocalDate date = LocalDate.of(2017, Month.AUGUST, 10); 
    System.out.println("Day of month:\t" + date.getDayOfMonth()); 

这将打印

Day of month: 10 

一个LocalDate得到了不是天和任何时区的任何时候,留下了很多混乱的空间较小。与Calendar相反,它也以人类的方式计算月数,所以7月是7月,8月是8月。

2

设置时区后刚刚成立的日期值:

int year = 2017; 
int month = Calendar.JULY; 
int dayOfMonth = 10; 

Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC")); 
calendar.set(year, month, dayOfMonth); 

System.out.println("DAY_OF_MONTH: " + calendar.get(Calendar.DAY_OF_MONTH)); 

此输出正确月份的一天:

DAY_OF_MONTH: 10 
+0

谢谢!这样做! – rweiser

+0

Scratch - 如果我在GMT-中,那么我最终会遇到问题,就像Ole V.V.指出。会给ThreeTen Backport一枪。 – rweiser