Strange behavior of Date/Calendar - java

I just want to create a method that sets the Date to time "23:59:59.999" of the same day. So I made this:
public static Date date235959(Date date) {
Calendar c = Calendar.getInstance();
c.setTime(date);
c.set(Calendar.HOUR, 23);
c.set(Calendar.MINUTE, 59);
c.set(Calendar.SECOND, 59);
c.set(Calendar.MILLISECOND, 999);
//c.set(Calendar.AM_PM, Calendar.PM);
return c.getTime();
}
When I run:
Date d = new Date();
d=date235959(d);
System.out.println(d.toString());
d=date235959(d);
System.out.println(d.toString());
I expected
Tue Sep 17 23:59:59 BRT 2013
Tue Sep 17 23:59:59 BRT 2013
But the output was
Tue Sep 17 23:59:59 BRT 2013
Wed Sep 18 11:59:59 BRT 2013
Why is that happened and how can I fix it?

The Calendar.HOUR is used for the 12-hour clock, use Calendar.HOUR_OF_DAY instead or put correct value for 12-hour clock: c.set(Calendar.HOUR, 11);

Use JodaTime instead of Java Calendar. It is the way more user friendly.
All Joda Time date/time objects are built on top of a long timestamp, so it is cheap to create those objects from a long.
In Joda Time ver 2.1 creating a date/time object from date/time components (year, month, day, hour, min, sec) is ~3.5 time slower than
the same operation for JDK GregorianCalendar.
Date components addition/subtraction is 3.5 times slower in Joda Time rather than in GregorianCalendar. On the contrary, time
components operations are about the same 3.5 times faster than
GregorianCalendar implementation.
Date/time parsing is working at about the same speed as in JDK SimpleDateFormat. The advantage of Joda parsing is that creating a
parser – DateTimeFormatter object is extremely cheap, unlike an
expensive SimpleDateFormat, so you don’t have to cache parsers
anymore.
http://java-performance.info/joda-time-performance/

Related

Java Calendar Date still adding Time on UTC Date 00:00 [duplicate]

This question already has answers here:
Convert Java Date to UTC String
(7 answers)
Closed 5 years ago.
I set my Calendar instance to a UTC date at 00:00 however once i return the result its 1 hour ahead
Calendar cal = Calendar.getInstance(TIMEZONE_UTC, Locale.ENGLISH);
cal.set(2017, 12 - 1, 15);
cal.set(Calendar.AM_PM, Calendar.AM);
cal.set(Calendar.HOUR, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
System.out.println(cal.getTime());
// Fri Dec 15 01:00:00 CET 2017 but should be 00:00:00
I suppose there is the winter/summer offset but I didn't found any description in the Gregorian or Calendar Element to handle this issue
I am getting the impression that you are really after just the date of December 15, 2017, and just wanted to make sure that there is no unexpected hour-of-day? If so, LocalDate from java.time, the modern Java date and time API, is made for you:
LocalDate ld = LocalDate.of(2017, Month.DECEMBER, 15);
System.out.println(ld);
This prints
2017-12-15
No hours, minutes or seconds to be concerned about.
If you do need a date-time at 00:00 UTC (the time I used to call midnight until Basil Bourque’s comment), there are several ways to obtain it. One of them is:
OffsetDateTime utcDateTime = LocalDateTime.of(2017, Month.DECEMBER, 15, 0, 0)
.atOffset(ZoneOffset.UTC);
System.out.println(utcDateTime);
This prints
2017-12-15T00:00Z
The Z in the end means UTC or offset zero. You see that the time of day is 00:00 as you wanted.
The Calendar class that you used in the question is long outdated, so I recommend you don’t use it anymore. java.time, the modern Java date and time API also known as JSR-310, is so much nicer to work with.
What went wrong in your code?
While a Calendar does contain a time zone (you initialized it to TIMEZONE_UTC), a Date (another outdated class) doesn’t. So when you convert to Date using cal.getTime() you lose the information that you wanted the time to be in UTC. Next (and this confuses many), when you print the date, you implicitly call Date.toString(), and this method grabs your JVM’s time zone setting and produces a string with the time in this time zone. So apparently you are (like I am) in a time zone that is at UTC+01:00 in December. The following two date-times denote the same point on the timeline:
Fri Dec 15 01:00:00 CET 2017
Fri Dec 15 00:00:00 UTC 2017
The reason why you see local time printed is you're displaying it via an instance of Calendar using Date.toString() which uses the local timezone(implicitly use the system timezone).

Behaviour of GregorianCalendar.set() with daylight saving times

While troubleshooting a glitch, something strange in this method behavior appears.
Context
Some countries use to save some daylight by shifting time. For example, in the timezone "Europe/Paris", each year, time shifts 1 hour forwards end March and 1 hour backwards end October, both between 2 AM and 3 AM. This causes, for example, the date October, the 30th 2016 at 02:15 AM to exist twice.
Fortunately, both dates does not have the same timestamp (ms) amount, neither readable representation :
Before time shift : Sun Oct 30 02:15:00 CEST 2016 (+0200 from UTC)
After time shift : Sun Oct 30 02:15:00 CET 2016 (+0100 from UTC)
Issue
After instantiating a GregorianCalendar object at the Paris timezone (using SimpleDateFormat), we get our 02:15 AM before backward shift as expected.
But if we want to set minutes to this object using .set(), the +0200 offset information gets corrupted to +0100 ("same time", but after time shift)
Is there any means of doing it this way, as the method .add() actually preserves the offset information?
Example
// Instantiation
GregorianCalendar gc1 = new GregorianCalendar(TimeZone.getTimeZone("Europe/Paris"));
gc1.setTime(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss Z").parse("2016-10-30 02:15:00 +0200"));
GregorianCalendar gc2 = (GregorianCalendar) gc1.clone();
System.out.println(gc1.getTime()); // Output : Sun Oct 30 02:15:00 CEST 2016 ; OK
System.out.println(gc2.getTime()); // Output : Sun Oct 30 02:15:00 CEST 2016 ; OK
// Set/add minutes
gc1.set(Calendar.MINUTE, 10);
gc2.add(Calendar.MINUTE, 10);
System.out.println(gc1.getTime()); // Output : Sun Oct 30 02:10:00 CET 2016 ; Unexpected
System.out.println(gc2.getTime()); // Output : Sun Oct 30 02:25:00 CEST 2016 ; OK
An ugly alternative is to subtract 1 hour from gc1 after setting the value:
gc1.set(Calendar.MINUTE, 10);
gc1.add(Calendar.HOUR, -1);
The result will be Sun Oct 30 02:10:00 CEST 2016.
Unfortunately that seems to be the best solution available for Calendar API. This behaviour of set method was reported as a bug, and the recommendation in JDK bug tracker was to use add to "fix" it:
During the "fall-back" period, Calendar doesn't support disambiguation and the given local time is interpreted as standard time.
To avoid the unexpected DST to standard time change, call add() to reset the value.
I'm using Java 8, so it seems that this bug was never fixed.
Java new Date/Time API
The old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs.
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).
The code below works for both.
The only differences are:
the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.
conversion from/to the old Calendar API
To convert a GregorianCalendar to the new API, you can do:
// Paris timezone
ZoneId zone = ZoneId.of("Europe/Paris");
// convert GregorianCalendar to ZonedDateTime
ZonedDateTime z = Instant.ofEpochMilli(gc1.getTimeInMillis()).atZone(zone);
In Java 8, you can also do:
ZonedDateTime z = gc1.toInstant().atZone(zone);
To get the date in the same format produced by java.util.Date you can use a DateTimeFormatter:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss z yyyy", Locale.ENGLISH);
System.out.println(fmt.format(z));
The output is:
Sun Oct 30 02:15:00 CEST 2016
I used a java.util.Locale to force the locale to English, so the month name and day of week are formatted correclty. If you don't specify a locale, it'll use the system's default, and it's not guaranteed to always be English (and the default can also be changed without notice, even at runtime, so it's better to always make it explicit which one you're using).
With this API you can add or set the minutes easily:
// change the minutes to 10
ZonedDateTime z2 = z.withMinute(10);
System.out.println(fmt.format(z2)); // Sun Oct 30 02:10:00 CEST 2016
// add 10 minutes
ZonedDateTime z3 = z.plusMinutes(10);
System.out.println(fmt.format(z3)); // Sun Oct 30 02:25:00 CEST 2016
The output will be:
Sun Oct 30 02:10:00 CEST 2016
Sun Oct 30 02:25:00 CEST 2016
Note that in the new API, classes are immutable, so with and plus methods return a new instance.
To convert a ZonedDateTime back to GregorianCalendar, you can do:
gc1.setTimeInMillis(z.toInstant().toEpochMilli());
In java 8, you can also do:
gc1 = GregorianCalendar.from(z);
To parse the input 2016-10-30 02:15:00 +0200, you must use another formatter:
DateTimeFormatter parser = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss XX")
.withZone(ZoneId.of("Europe/Paris"));
ZonedDateTime z = ZonedDateTime.parse("2016-10-30 02:15:00 +0200", parser);
I had to set the timezone using withZone method, because just the offset +0200 is not enough to determine it (more than one timezone can use this same offset and the API can't decide which one to use)

Java Calendar: 1am switchover to standard time (from DST)

I have some code that uses Calendar.set() to return the beginning of the hour for a given date value. I encountered the following issue on Sunday Nov 4th, 2012 (Eastern Timezone - EDT to EST switchover):
public void testStartOfHourDST1() {
Calendar cal = Calendar.getInstance();
long time = 1352005200000L; // Nov 4, 2012, 1AM EDT
cal.setTimeInMillis(time);
cal.set(Calendar.MILLISECOND, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MINUTE, 0);
System.out.println(new Date(time));
System.out.println(new Date(cal.getTimeInMillis()));
System.out.println(System.getProperty("java.version"));
assertEquals(cal.getTimeInMillis(), time); // fails
return;
}
Ouput:
Sun Nov 04 01:00:00 EDT 2012
Sun Nov 04 01:00:00 EST 2012
1.6.0_35
Perhaps this is not the correct way to be using calendar, but running the same test for the next hour (or previous hour) works fine. Is this a JVM issue?
Thanks
It's not really an issue, in the sense that it is deterministic and doing what it was programmed to do. It's an issue if you would prefer that it pick the earlier of the two 1ams!
After changing fields on the Calendar the only information it has is "1am in US/Eastern". Well, your timezone had two 1ams that day, which one is it supposed to pick? The authors of OpenJDK made a decision that when presented with this ambiguity, they would always interpret it as the later one, in standard time. This comment is from java.util.GregorianCalendar OpenJDK 6:
// 2. The transition out of DST. Here, a designated time of 1:00 am - 1:59 am
// can be in standard or DST. Both are valid representations (the rep
// jumps from 1:59:59 DST to 1:00:00 Std).
// Again, we assume standard time.
If you print out the actual values of the numbers you will see cal.getTimeInMillis() has actually been changed by an hour from the value of time.
Getting the correct time zone is very important. For example, as I'm sure you are aware the system current time in milliseconds is measured from midnight on the 1st of January 1970. This is well documented. The Date constructor JavaDoc says:
public Date(long date)
Allocates a Date object and initializes it to represent the specified
number of milliseconds since the standard base time known as "the
epoch", namely January 1, 1970, 00:00:00 GMT.
which is fine, but makes the output of this program a little hard to understand at first:
import java.util.Date;
import java.util.TimeZone;
import java.text.SimpleDateFormat;
public class Demo {
public static void main(String[] args) {
TimeZone tz = TimeZone.getTimeZone("Europe/London");
Date d = new Date(-3600000);
SimpleDateFormat df = new SimpleDateFormat("MMMM dd, yyyy HH:mm:ss.SSS z");
df.setTimeZone(tz);
System.out.println(String.format("%8dms -> %s",
Long.valueOf(d.getTime()) ,df.format(d)));
d.setTime(0);
System.out.println(String.format("%8dms -> %s",
Long.valueOf(d.getTime()) ,df.format(d)));
}
}
The output is:
-3600000ms -> January 01, 1970 00:00:00.000 GMT
0ms -> January 01, 1970 01:00:00.000 GMT
As you can see, the epoch is apparently displaced by an hour!
Except it is not. If you use the "GMT" time zone explicitly, all is well. The "Europe/London" time zone is simply tricky like that.
When working with Calendars, understand the time zone you are using or be caught out.

How to set time to a date object in java

I created a Date object in Java. When I do so, it shows something like: date=Tue Aug 09 00:00:00 IST 2011. As a result, it appears that my Excel file is lesser by one day (27 feb becomes 26 feb and so on) I think it must be because of time. How can I set it to something like 5:30 pm?
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY,17);
cal.set(Calendar.MINUTE,30);
cal.set(Calendar.SECOND,0);
cal.set(Calendar.MILLISECOND,0);
Date d = cal.getTime();
Also See
Joda time
Calendar doc
Can you show code which you use for setting date object? Anyway< you can use this code for intialisation of date:
new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse("2011-01-01 00:00:00")
I should like to contribute the modern answer. This involves using java.time, the modern Java date and time API, and not the old Date nor Calendar except where there’s no way to avoid it.
Your issue is very likely really a timezone issue. When it is Tue Aug 09 00:00:00 IST 2011, in time zones west of IST midnight has not yet been reached. It is still Aug 8. If for example your API for putting the date into Excel expects UTC, the date will be the day before the one you intended. I believe the real and good solution is to produce a date-time of 00:00 UTC (or whatever time zone or offset is expected and used at the other end).
LocalDate yourDate = LocalDate.of(2018, Month.FEBRUARY, 27);
ZonedDateTime utcDateDime = yourDate.atStartOfDay(ZoneOffset.UTC);
System.out.println(utcDateDime);
This prints
2018-02-27T00:00Z
Z means UTC (think of it as offset zero from UTC or Zulu time zone). Better still, of course, if you could pass the LocalDate from the first code line to Excel. It doesn’t include time-of-day, so there is no confusion possible. On the other hand, if you need an old-fashioned Date object for that, convert just before handing the Date on:
Date oldfashionedDate = Date.from(utcDateDime.toInstant());
System.out.println(oldfashionedDate);
On my computer this prints
Tue Feb 27 01:00:00 CET 2018
Don’t be fooled, it is correct. My time zone (Central European Time) is at offset +01:00 from UTC in February (standard time), so 01:00:00 here is equal to 00:00:00 UTC. It’s just Date.toString() grabbing the JVMs time zone and using it for producing the string.
How can I set it to something like 5:30 pm?
To answer your direct question directly, if you have a ZonedDateTime, OffsetDateTime or LocalDateTime, in all of these cases the following will accomplish what you asked for:
yourDateTime = yourDateTime.with(LocalTime.of(17, 30));
If yourDateTime was a LocalDateTime of 2018-02-27T00:00, it will now be 2018-02-27T17:30. Similarly for the other types, only they include offset and time zone too as appropriate.
If you only had a date, as in the first snippet above, you can also add time-of-day information to it:
LocalDate yourDate = LocalDate.of(2018, Month.FEBRUARY, 27);
LocalDateTime dateTime = yourDate.atTime(LocalTime.of(17, 30));
For most purposes you should prefer to add the time-of-day in a specific time zone, though, for example
ZonedDateTime dateTime = yourDate.atTime(LocalTime.of(17, 30))
.atZone(ZoneId.of("Asia/Kolkata"));
This yields 2018-02-27T17:30+05:30[Asia/Kolkata].
Date and Calendar vs java.time
The Date class that you use as well as Calendar and SimpleDateFormat used in the other answers are long outdated, and SimpleDateFormat in particular has proven troublesome. In all cases the modern Java date and time API is so much nicer to work with. Which is why I wanted to provide this answer to an old question that is still being visited.
Link: Oracle Tutorial Date Time, explaining how to use java.time.
If you don't have access to java 8 and the API java.time, here is my simple function to copy the time of one date to another date using the old java.util.Calendar (inspire by Jigar Joshi) :
/**
* Copy only the time of one date to the date of another date.
*/
public static Date copyTimeToDate(Date date, Date time) {
Calendar t = Calendar.getInstance();
t.setTime(time);
Calendar c = Calendar.getInstance();
c.setTime(date);
c.set(Calendar.HOUR_OF_DAY, t.get(Calendar.HOUR_OF_DAY));
c.set(Calendar.MINUTE, t.get(Calendar.MINUTE));
c.set(Calendar.SECOND, t.get(Calendar.SECOND));
c.set(Calendar.MILLISECOND, t.get(Calendar.MILLISECOND));
return c.getTime();
}
Calendar calendar = new Calendar.Builder()
.setDate(2022, Calendar.JUNE, 1)
.setTimeOfDay(0, 0, 0)
.build();
System.out.println(calendar.getTimeInMillis());

Java Calendar set time to 12am of previous day in UTC

Can someone explain why or if this code is wrong?
// main
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
System.out.println(calendar.getTime());
calendar.add(Calendar.DATE, -1);
System.out.println(calendar.getTime());
calendar.set(Calendar.HOUR_OF_DAY, 0);
System.out.println(calendar.getTime());
It produces:
Fri Jan 28 15:27:35 EST 2011
Thu Jan 27 15:27:35 EST 2011
Wed Jan 26 19:27:35 EST 2011
Am I missing something obvious? I expect to see something like Thu Jan 27 00:27:35 EST 2011
Thanks.
You're printing it out in the default time zone, not UTC. Although your calendar knows the time zone you're interested in, the java.util.Date returned by getTime() doesn't... and Date.toString() uses the system time zone.
Given that you specified in the subject that you wanted 12am in UTC, why would you expect to see Thu Jan 27 00:27:35 EST 2011? EST isn't the same as UTC.
EDIT: As always, I'd just like to point out that Joda Time is generally a much nicer API to use for date/time arithmetic in Java. You're currently getting the right answer, but I'd still recommend moving to Joda :)
EST is UTC - 5 hours, so 19:27 EST corresponds to 00:27 UTC. It seems logical to me.
Use a date format with a UTC locale to display your calendars, instead of using your default locale.
For UTC calculations (only) you might find using long is simpler.
long time = System.currentTimeMillis();
// yesterday at 12:00:00.000 am.
long yesterday = (time / 86400000 - 1) * 86400000;

Categories

Resources