-1

So, I start getting the current time in a Calendar object named startTime

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 (i.e. Tue 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". This is being stored in a 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 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.

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.

Juan Carlos Mendoza
  • 5,736
  • 7
  • 25
  • 50
Diego Rivera
  • 403
  • 1
  • 5
  • 19
  • Your Question is not clear. You want to alter a date-time by setting a specific time-of-day? And talking about irrelevant subjects such as "alarmPref" and logs is an unwelcome distraction. – Basil Bourque Nov 07 '17 at 21:31
  • 1
    Even on Android it’s worth considering getting [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP) so you can use [the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/) and skip the long outdated classes `Calendar` and `Date`. Certainly for non-trivial date-time operations like yours. – Ole V.V. Nov 08 '17 at 05:45

3 Answers3

3

If your goal is to alter a date-time by setting a specific time-of-day…

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. So you got 3 PM on January 1st of 1970. That is all correct documented behavior. 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. These were supplanted years ago in Java 8 and Java 9 by the java.time classes. For earlier Android, see the last bullets below.

Time zone

Specify your intended time zone. If omitted, the JVM’s current default time zone is implicitly applied. 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. 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(!).

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 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.

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. For that use the Instant class. You can extract a Instant from a ZonedDateTime.

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

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Good and thorough answer, thanks. How about `ZonedDateTime.now(ZoneId.of("America/Chicago")).with(LocalTime.parse("15:00"))`? What I like about is we need only mention the zone ID once. – Ole V.V. Nov 08 '17 at 05:59
  • 1
    @OleV.V. Yes, that works well too. I just like to show the pieces separately for clear demonstration. – Basil Bourque Nov 08 '17 at 08:27
0

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. However the methods .getHours() and .getHours() are deprecated, is there a more fancy way to do this?

Diego Rivera
  • 403
  • 1
  • 5
  • 19
  • 1
    A more fancy way? Yes. `ZonedDateTime.of( LocalDate.now( ZoneId.of( "America/Chicago" ) ) , LocalTime.parse( "15:00" ) , ZoneId.of( "America/Chicago" ) )` – Basil Bourque Nov 08 '17 at 01:15
  • Using the deprecated methods of `Date` (which is in turn entirely outdated). This is in no way the solution I would recommend. – Ole V.V. Nov 08 '17 at 05:38
  • If you insist on using the outdated classes, you should at least use a second `Calendar` object for the time-of-day and copy the hours and minutes (and seconds and milliseconds) from it. – Ole V.V. Nov 08 '17 at 06:05
0

I don't see any problems here. You are asking the SimpleDateFormat to parse a Date, by only providing hours and minutes. 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.

The workaround in which you set only the HOUR_OF_DAY and MINUTE looks good.

vbuhlev
  • 509
  • 2
  • 10