Android的Calendar.Day_Of_Month是否基于零?

时间:2018-06-21 21:54:36

标签: java android calendar

根据android文档,Day_Of_Month从1开始(即'1'表示每月的第一天)(请参见https://developer.android.com/reference/java/util/Calendar#DAY_OF_MONTH)。

但是,当我在两个android设备上运行以下代码时,结果为“ 2018-01-02”。我想念什么吗?

Calendar cal = new GregorianCalendar();
TimeZone tzone = TimeZone.getTimeZone("GMT");
cal.setTimeZone(tzone);

cal.set(Calendar.HOUR, 12);
cal.set(2018, 0, 1);

DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
String date = df.format(cal.getTime());

我不仅认为文档有误的原因是,我有一些证据表明我的客户设备之一将日期报告为“ 2018-01-01”

2 个答案:

答案 0 :(得分:2)

tl; dr

  

Android的Calendar.Day_Of_Month是否从零开始?

不。

  

我得到“ 2018-01-02”……

     

…报告日期为“ 2018-01-01”

第一期与第二期的问题与时区有关。

使用 java.time 代替那些非常麻烦的旧类。

OffsetDateTime.of(
    LocalDate.of( 2018 , Month.JANUARY , 1 ) , 
    LocalTime.NOON ,
    ZoneOffset.UTC
)
  

.toString():2018-01-01T12:00Z

.format(
    DateTimeFormatter.ISO_LOCAL_DATE
)
  

2018-01-01

或覆盖区域/偏移量。

.format(
    DateTimeFormatter.ISO_LOCAL_DATE
                     .withZone( ZoneId.of( "Pacific/Kiritimati" ) )  // Using zone 14 hours ahead of UTC. So noon UTC is “tomorrow” in Kiribati.
)
  

2018-01-02

时区

如果在America/Los_Angeles时区的16:00运行代码,则会得到2018-01-01。但是,如果我将您的代码更改为将小时设置为23而不是12,则结果为2018-01-02

因此,关于时区存在问题。什么是“今天”和什么是“明天”取决于您的时区。

让我提出一个真正的解决方案,而不是进一步弄脏我的大脑:停止使用这些可怕的日期时间类

java.time

这些旧的日期时间类(DateCalendarSimpleDateFormat)在多年前被现代的 java.time 类所取代。

显然,您希望在第一年中午。就是这样。请注意理智的编号:一月至十二月的月份是1到12(与传统类不同),倒数第一月的日期是1-31(与传统类一样)。

获取日期。

LocalDate ld = LocalDate.of( 2018 , 1 , 1 ) ; // January 1, 2018.

或者使用更具可读性的Month枚举。

LocalDate ld = LocalDate.of( 2018 , Month.JANUARY , 1 ) ; // January 1, 2018.

生成一个以标准ISO 8601格式表示该值的字符串。

  

ld.toString():2018-01-01

获取一天中的时间。 LocalTime类为此具有一个常量。

LocalTime lt = LocalTime.NOON ; 

指定自UTC的偏移量为零,即UTC本身。 ZoneOffset类为此具有一个常量。

ZoneOffset offset = ZoneOffset.UTC ;

将一个时刻表示为OffsetDateTime对象。

OffsetDateTime odt = OffsetDateTime.of( ld , lt , offset ) ;

生成一个以标准ISO 8601格式表示该值的字符串。

  

odt.toString():2018-01-01T12:00Z

如果只需要日期部分,请提取LocalDate

LocalDate ld = odt.toLocalDate() ;

或者通过定义DateTimeFormatter仅使用日期部分打印字符串。

DateTimeFormatter f = DateTimeFormatter.ISO_LOCAL_DATE ; 
String outputOdtDateOnly = odt.format( f ) ;
  

2018-01-01

默认情况下,DateTimeFormatter对象使用新字符串表示的对象的偏移量或区域。您可以选择覆盖该偏移量/区域。让我们尝试一下。

ZoneId z = ZoneId.of( "Pacific/Kiritimati" );  // Most eastern (earliest) time zone is in Kiribati. https://en.wikipedia.org/wiki/Kiribati
DateTimeFormatter fKiritimati = f.withZone( z );
String outputOdtDateOnlyInKiribati = odt.format( fKiritimati );

请注意,我们更改了格式化程序对象,而不是数据对象,而不是OffsetDateTime对象。我们向新的格式化程序对象添加了一个时区,先前的格式化程序在其中保存了nullas documented。并且请注意 java.time 如何使用immutable objects,在这种情况下,将根据原始值而不是更改(“更改”)原始对象来实例化一个新对象。因此,我们在第一个DateTimeFormatter对象的基础上,添加了指定的覆盖区域。

让我们看看能得到什么。

System.out.println( outputOdtDateOnlyInKiribati );
  

2018-01-02

惊奇!回到您的问题中的问题。基里巴斯的部分地区比世界协调时间早14小时。因此,在UTC中午时,同时是Pacific/Kiritimati区域中的14小时后,因此“明天”是第二个。


关于 java.time

java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

目前位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

在哪里获取java.time类?

答案 1 :(得分:2)

实际上,您正在做的三件事会影响您的结果。

(1)您正在使用Calendar.HOUR,它是12小时制!

Java文档:

  

小时:
获取和设置的字段编号,指示早晨的时间   或下午。 HOUR用于12小时制(0-11)。中午和   午夜用0而不是12表示。例如,在晚上10:04:15.250   小时是10。

您要使用的是Calendar.HOUR_OF_DAY,它是24小时制!

Java文档:

  

HOUR_OF_DAY:
get和set的字段号指示   天。 HOUR_OF_DAY用于24小时制。例如,在10:04:15.250   HOUR_OF_DAY下午22点。

(2)您将时间设置为12。这在使用Calendar.HOUR时会引起一些问题。

(3)您将TimeZone“ GMT”与Calendar.HOUR设置结合使用会导致日期根据用户所在的时区而增加。

示例:

我在CST时区,并且运行了以下代码:

Calendar cal = new GregorianCalendar();
cal.set(2018, 0, 1);
int h = 0;

cal.set(Calendar.HOUR_OF_DAY, h);
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
String date1 = df.format(cal.getTime());
Log.e(TAG, "date1 = " + date1)

cal.set(Calendar.HOUR, h);
TimeZone tzone = TimeZone.getTimeZone("GMT");
cal.setTimeZone(tzone);
String date2 = df.format(cal.getTime());
Log.e(TAG, "date2 GMT = " + date2)

并得到h = 0的这些结果:

date1 = 2018-01-01 00:54
date2 GMT = 2017-12-31 18:54

设置h = 12:

date1 = 2018-01-01 12:54
date2 GMT = 2018-01-01 18:54

七个小时后,我会得到:

date1 = 2018-01-01 19:54
date2 GMT = 2018-01-02 01:54