根据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”
答案 0 :(得分:2)
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
。
因此,关于时区存在问题。什么是“今天”和什么是“明天”取决于您的时区。
让我提出一个真正的解决方案,而不是进一步弄脏我的大脑:停止使用这些可怕的日期时间类。
这些旧的日期时间类(Date
,Calendar
,SimpleDateFormat
)在多年前被现代的 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
对象。我们向新的格式化程序对象添加了一个时区,先前的格式化程序在其中保存了null
,as documented。并且请注意 java.time 如何使用immutable objects,在这种情况下,将根据原始值而不是更改(“更改”)原始对象来实例化一个新对象。因此,我们在第一个DateTimeFormatter
对象的基础上,添加了指定的覆盖区域。
让我们看看能得到什么。
System.out.println( outputOdtDateOnlyInKiribati );
2018-01-02
惊奇!回到您的问题中的问题。基里巴斯的部分地区比世界协调时间早14小时。因此,在UTC中午时,同时是Pacific/Kiritimati
区域中的14小时后,因此“明天”是第二个。
java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和SimpleDateFormat
。
目前位于Joda-Time的maintenance 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