简体   繁体   English

SetDate()为Calendar对象以更改时间,将日期更改回1970

[英]SetDate() to Calendar object to change time, changes the date back to 1970

So, I start getting the current time in a Calendar object named startTime 因此,我开始在名为startTime的Calendar对象中获取当前时间。

Calendar startTime = Calendar.getInstance();
Log.w(TAG, "CALENDAR BEFORE: " + startTime.getTime());

This works great, as my logs say that the time is the current one (ie Tue Nov 07 12:50:10 CST 2017). 这很有效,因为我的日志显示时间是当前时间(即TST Nov 07 12:50:10 CST 2017)。 However I need to get a time from a Preference option, which returns me something like this: "15:00". 但是,我需要从“首选项”选项中抽出时间,该选项会向我返回类似“ 15:00”的信息。 This is being stored in a String alarmPref 这被存储在String alarmPref中

String alarmPref = preferences.getString(keyAlarm, "12:00");
Log.w(TAG, "AlarmReceiver: Alarm going to be set at: " + alarmPref);

Here is where it gets tricky, I change this String to a Date using a SimpleDateFormat with only hours and minutes (adding date stuff crashes). 这是棘手的地方,我使用仅数小时和数分钟的SimpleDateFormat将此字符串更改为Date(添加日期崩溃)。

SimpleDateFormat format = new SimpleDateFormat("HH:mm", Locale.getDefault());

And when I try to add this time to the Calendar object I created first (startTime) it changes the time to "Thu Jan 01 15:00:00 CST 1970", where I have the correct time but not date. 当我尝试将此时间添加到我首先创建的Calendar对象中(startTime)时,它将时间更改为“ Thu Jan 01 15:00:00 CST 1970”,此处我具有正确的时间,但没有日期。

Date newDate = format.parse(alarmPref);
startTime.setTime(newDate);
Log.w(TAG, "CALENDAR AFTER SET: " + startTime.getTime());

I've been trying to fix this but haven't found the solution, nor similar problems. 我一直在尝试解决此问题,但没有找到解决方案,也没有类似的问题。

If your goal is to alter a date-time by setting a specific time-of-day… 如果您的目标是通过设置特定的时间来更改日期时间...

tl;dr tl; dr

ZonedDateTime.of( 
    LocalDate.now( ZoneId.of( "America/Chicago" ) ) ,  // Get the current date for a particular place on Earth.
    LocalTime.parse( "15:00" ) ,                       // Specify the time-of-day.
    ZoneId.of( "America/Chicago" )                     // Assign the intended zone to the resulting `ZonedDateTime` object.
)

Issue 问题

You are parsing a String representing a time-of-day as a date-time value. 您正在解析一个表示日期时间值的字符串。 That parsing method assumes you want the first day since the epoch reference date of 1970-01-01T00:00:00Z. 该分析方法假定您要自纪元参考日期 1970-01-01T00:00:00Z开始的第一天。 So you got 3 PM on January 1st of 1970. That is all correct documented behavior. 因此您在1970年1月1日下午3点。这是所有正确记录的行为。 But not what you intended. 但不是您想要的。

Solution

Avoid legacy classes 避免遗留类

Avoid the troublesome old date-time classes bundled with the earliest versions of Java such as Calendar . 避免与最早的Java版本(例如Calendar捆绑在一起的麻烦的旧日期时间类。 These were supplanted years ago in Java 8 and Java 9 by the java.time classes. 这些在几年前被Java.time类替换为Java 8和Java 9。 For earlier Android, see the last bullets below. 对于较早的Android,请参阅下面的最新项目符号。

Time zone 时区

Specify your intended time zone. 指定您想要的时区。 If omitted, the JVM's current default time zone is implicitly applied. 如果省略,则隐式应用JVM的当前默认时区。 That default may change at any moment during runtime, and so is unreliable. 该默认值可能会在运行时随时更改,因此不可靠。

Specify a proper time zone name in the format of continent/region , such as America/Montreal , Africa/Casablanca , or Pacific/Auckland . continent/region的格式指定正确的时区名称 ,例如America/MontrealAfrica/CasablancaPacific/Auckland Never use the 3-4 letter abbreviation such as CST or EST or IST as they are not true time zones, not standardized, and not even unique(!). 切勿使用3-4个字母的缩写,例如CSTESTIST因为它们不是真实的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "America/Chicago" ) ;

A time zone is crucial in determining a date and time-of-day. 时区对于确定日期和时间至关重要。 For any given moment, the date varies around the globe by zone. 在任何给定时刻,日期都会在全球范围内变化。 For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec . 例如, 法国巴黎午夜过后几分钟是新的一天,而在魁北克蒙特利尔仍然是“昨天”。

ZonedDateTime zdtNow = ZonedDateTime.now( z ) ;

Time-of-day 一天中的时间

For a time-of-day only value, without any date and without an time zone, use the LocalTime class. 对于一天中唯一的值,没有任何日期,没有时区,请使用LocalTime类。

LocalTime lt = LocalTime.parse( "15:00" ) ;

Date-only + Time-of-Day 仅日期+时间

You want the same date but with another time-of-day. 您想要相同的日期,但又需要另一个时间。 So extract the date-only portion, combine with the desired time-of-day while applying the intended time zone to get a new ZonedDateTime object. 因此,提取仅日期部分,并与所需的日期时间结合,同时应用预期的时区以获得新的ZonedDateTime对象。

LocalDate ld = zdtNow.toLocalDate() ;
ZonedDateTime zdtTodayAtThreePm = ZonedDateTime.of( ld , lt , z ) ;

Tip: Generally best to do your logging, serializing, and data exchange in UTC rather than a particular time zone. 提示:通常最好在UTC而不是特定时区中进行日志记录,序列化和数据交换。 For that use the Instant class. 为此,请使用Instant类。 You can extract a Instant from a ZonedDateTime . 您可以从ZonedDateTime提取Instant ZonedDateTime

Instant instant = zdtTodayAtThreePm.toInstant() ;  // Always in UTC.

About java.time 关于java.time

The java.time framework is built into Java 8 and later. java.time框架内置于Java 8及更高版本中。 These classes supplant the troublesome old legacy date-time classes such as java.util.Date , Calendar , & SimpleDateFormat . 这些类取代了麻烦的旧的旧式日期时间类,例如java.util.DateCalendarSimpleDateFormat

The Joda-Time project, now in maintenance mode , advises migration to the java.time classes. 现在处于维护模式Joda-Time项目建议迁移到java.time类。

To learn more, see the Oracle Tutorial . 要了解更多信息,请参见Oracle教程 And search Stack Overflow for many examples and explanations. 并在Stack Overflow中搜索许多示例和说明。 Specification is JSR 310 . 规格为JSR 310

You may exchange java.time objects directly with your database. 您可以直接与数据库交换java.time对象。 Use a JDBC driver compliant with JDBC 4.2 or later. 使用与JDBC 4.2或更高版本兼容的JDBC驱动程序 No need for strings, no need for java.sql.* classes. 不需要字符串,不需要java.sql.*类。

Where to obtain the java.time classes? 在哪里获取java.time类?

I managed to get a fixed by doing this: 通过这样做,我设法解决了问题:

String alarmPref = preferences.getString(keyAlarm, "12:00");
Log.w(TAG, "AlarmReceiver: Alarm going to be set at: " + alarmPref);
SimpleDateFormat format = new SimpleDateFormat("HH:mm", Locale.getDefault());
Date newDate = format.parse(alarmPref);
//startTime.setTime(newDate);
startTime.set(Calendar.HOUR_OF_DAY, newDate.getHours());
startTime.set(Calendar.MINUTE, newDate..getHours());
startTime.set(Calendar.SECOND, 0);
Log.w(TAG, "CALENDAR AFTER SET: " + startTime.getTime());

Where I only set the HOURS and MINUTES (and seconds to 0), and the date remains the same. 在这里,我仅将HOURS和MINUTES设置为0(将秒设置为0),并且日期保持不变。 However the methods .getHours() and .getHours() are deprecated, is there a more fancy way to do this? 但是,不赞成使用方法.getHours()和.getHours(),是否有更理想的方法呢?

I don't see any problems here. 我在这里看不到任何问题。 You are asking the SimpleDateFormat to parse a Date, by only providing hours and minutes. 您要求SimpleDateFormat通过仅提供小时和分钟来解析日期。 It defaults the other fields which are missing from your input to the beginning of time, which is not what you expect. 它会将输入中缺少的其他字段默认为时间的开头,这不是您期望的。 Apparently the developer who implemented SimpleDateFormat thought that this behavior is reasonable. 显然,实现SimpleDateFormat的开发人员认为此行为是合理的。

The workaround in which you set only the HOUR_OF_DAY and MINUTE looks good. 仅设置HOUR_OF_DAY和MINUTE的解决方法看起来不错。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM