I'm grabbing some data from a database that has a stored date value, and I'm letting the user pick date ranges they would like to view data for. All my code for getting these date ranges works except for the method to get the date range covering all time, which would be a start value of the earliest possible data Java handles, to the end value of the max possible date.
Is there something wrong with my code, because I can't see a problem:
public static DateRange getAllTime() {
/**
* Get earliest possible
*/
Calendar c = Calendar.getInstance();
c.set(
c.getActualMinimum(Calendar.YEAR),
c.getActualMinimum(Calendar.MONTH),
c.getActualMinimum(Calendar.DAY_OF_MONTH),
c.getActualMinimum(Calendar.HOUR),
c.getActualMinimum(Calendar.MINUTE),
c.getActualMinimum(Calendar.SECOND)
);
c.set(Calendar.MILLISECOND, c.getActualMinimum(Calendar.MILLISECOND));
Date start = c.getTime();
/**
* Get latest possible date
*/
c.set(
c.getActualMaximum(Calendar.YEAR),
c.getActualMaximum(Calendar.MONTH),
c.getActualMaximum(Calendar.DAY_OF_MONTH),
c.getActualMaximum(Calendar.HOUR),
c.getActualMaximum(Calendar.MINUTE),
c.getActualMaximum(Calendar.SECOND)
);
c.set(Calendar.MILLISECOND, c.getActualMaximum(Calendar.MILLISECOND));
Date end = c.getTime();
DateRange range = new DateRange();
range.Start = start;
range.End = end;
return range;
}
Why not use
new Date(Long.MIN_VALUE) (in YEAR 292269055 BC)
new Date(Long.MAX_VALUE) (in YEAR 292278994 AD)?
Since froginvasion challenged the answer, I thought I'd double check
long day=1000*60*60*24;
System.out.println(new Date(Long.MAX_VALUE-day));
System.out.println(new Date(Long.MAX_VALUE));
System.out.println(new Date(0));
System.out.println(new Date(-day));
System.out.println(new Date(Long.MIN_VALUE));
System.out.println(new Date(Long.MIN_VALUE+day));
gave me
Sat Aug 16 07:12:55 GMT 292278994
Sun Aug 17 07:12:55 GMT 292278994
Thu Jan 01 00:00:00 GMT 1970
Wed Dec 31 00:00:00 GMT 1969
Sun Dec 02 16:47:04 GMT 292269055
Mon Dec 03 16:47:04 GMT 292269055
I think it is right. I assume the AD/BC are just being suppressed. The suggestion to use new Date(0) as the minimum is clearly wrong because new Date(-day) is clearly smaller.
Why make life so complicated? If you don't have a start date, don't query for a start date. If you don't have an end date, don't query for an end date. And if you have neither, don't query for dates at all.
That code works me, maybe you're not expecting the values it returns?
Start: Sat Jan 01 00:00:00 PST 1
End: Wed Apr 17 21:34:08 PST 292269054
(It would be easier to help if you included the stack trace)
I suspect may get an overflow by setting the year and then setting maximum values for all the other fields separately. That would make your end time somewhere around your start time and cause all records to be rejected. You might try just printing out the calendar times to see what's happening.
As seanizer points out, you're really making this more complicated than it should be - the correct way to deal with this is to leave the date clause off entirely in the query. That may be difficult sometimes because the sql statement isn't generated dynamically. But note that even if you can't modify the sql at run time, the condition (in Oracle)
start_date >= nvl(?, start_date)
will always be satisfied if the supplied value is null and start_date is populated.
Min and max supported by your database?
You may actually need the minimum and maximum value supported by your database rather than the minimum and maximum supported by Java. Some databases have datetime types that only support, say years 0001 through 9999. I haven’t tried nor studied the documentation, but I might suspect that either your JDBC driver or your query may fail if you pass dates outside the interval supported by the database.
For example the MariaDB documentation says:
MariaDB stores values that use the DATETIME data type in a format
that supports values between 1000-01-01 00:00:00.000000 and
9999-12-31 23:59:59.999999.
It’s not very nice to have Java code that depends on a specific brand of database engine (DBMS), though. If you nevertheless need to, just hardcode the values:
public static final LocalDateTime MIN_DATABASE_DATETIME
= LocalDateTime.of(1000, Month.JANUARY, 1, 0, 0);
public static final LocalDateTime MAX_DATABASE_DATETIME
= LocalDateTime.of(9999, Month.DECEMBER, 31, 23, 59, 59, 999_999_000);
I am using and recommending java.time, the modern Java date and time API.
Min and max supported by Java?
The minimum and maximum values supported by Java are built in as constants. java.time supports a wider range than the old-fashioned Date class did. Let’s see what the minimum and maximum are:
System.out.println("Min Java LocalDateTime: " + LocalDateTime.MIN);
System.out.println("Max Java LocalDateTime: " + LocalDateTime.MAX);
Output:
Min Java LocalDateTime: -999999999-01-01T00:00
Max Java LocalDateTime: +999999999-12-31T23:59:59.999999999
What went wrong in your code?
Is there something wrong with my code, because I can't see a problem:
You are correct, while your code gives a minimum value that you may be able to use as minimum, the max value that it gives is about as far off as can be. When I ran it just now, I got:
Sat Jan 01 00:00:00 CST 1 - Wed Apr 17 21:34:08 CST 292269054
Calendar and Date did support dates before the common era (“before Christ”), so giving year 1 as minimum is not correct. This happens because you have not changed the era of your Calendar, so it remains in CE (“AD”). The YEAR field number denotes year of era, so the minimum value is 1.
The maximum value I got looks like it lies 9940 years before the maximum possible Date but probably still way above any date actually present in your data. This is not so! The value you got is in year 292269054 before the common era (“before Christ“) and just 4 and a half months after the minimum value of a Date, pretty much the opposite of what you had tried to find. This is probably due to undetected arithmetic overflow in the computation. I suppose that Calendar never meant to guard against overflow and underflow.
getActualMinimum() and getActualMaximum() give you the min and max values possible given the actual value of the Calendar. So c.getActualMinimum(Calendar.MONTH) would give you 28 in February in a non-leap year and 31 in July. This is not what you wanted. Using Calendar.HOUR is a further bug since HOUR denotes the hour within AM or PM from 0 through 11.
The actual min and max values of Date and Calendar do not fall on midnight nor at New Year, but your code will try to find values at or very close to midnight in the night of New Year, which is the flaw by your technique. Trying to find December 31 in the year where the maximum value fell in August probably accounts for the overflow I mentioned. The 4 and half months you overflowed by roughly agrees with the 4 and a half months above the minimum value where we ended up.
Links
DATETIME in the MariaDB knowledge base
Oracle tutorial: Date Time explaining how to use java.time.
Related
Consider the following program
Date date1 = new Date(-124304227239000L);
Date date2 = new Date(0);
System.out.println(date1 + " vs. " + date2);
The result (at least with Java 8 on my computer, and with Java 11 on a different computer):
Sun Jan 01 16:59:21 CET 1970 vs. Thu Jan 01 01:00:00 CET 1970
This seems strange because following the documentation (https://docs.oracle.com/javase/8/docs/api/java/util/Date.html#Date-long-) , negative values as parameter for Date indicate dates before 1970. Instead, I get a Sunday instead of Thursday, but still 1970.
Can anybody explain this to me?
The value you've provided is around 1969/1970 BC, depending on whether you do a Gregorian/Julian cutover or not. Date.toString(), aside from all its other problems, doesn't bother to mention the era.
If you use Instant with the same value, it's clearer:
Instant instant = Instant.ofEpochMilli(-124304227239000L);
System.out.println(instant);
Output:
-1970-12-15T15:59:21Z
I'd draw the following conclusions from this:
When using values in the far past, there are lots of considerations to bear in mind, including textual representation and calendar system
Avoid java.util.Date as far as you can
Simple question why the result is like the following for this code:
Calendar cal2 = Calendar.getInstance();
cal2.set(Calendar.HOUR, 12);
cal2.set(Calendar.AM_PM, Calendar.PM);
System.out.println(cal2.getTime().toString()); // Wed Jan 13 00:11:08 EET 2021
cal2.set(Calendar.AM_PM, Calendar.PM);
System.out.println(cal2.getTime().toString()); // Wed Jan 13 12:11:08 EET 2021
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR, 12);
cal.set(Calendar.AM_PM, Calendar.AM);
System.out.println(cal.getTime().toString()); // Tue Jan 12 12:11:08 EET 2021
cal.set(Calendar.AM_PM, Calendar.AM);
System.out.println(cal.getTime().toString()); // Tue Jan 12 00:11:08 EET 2021
The first looks like it is 12 at midnight not afternoon.
The third one looks like it is 12 afternoon, not midnight
Why setting calendar AM or PM multiple times change the result?
How to set the time correctly?
Calendar is very confusing
You are so far from the first being confused about how the Calendar class works. Fortunately the class is also long outdated. You should not use it.
Why setting calendar AM or PM multiple times change the result? …
To answer your question as asked, Andi80 is correct in the other answer and the comments to it: HOUR goes from 0 through 11. The documentation says about HOUR:
Field number for get and set indicating the hour of the morning or
afternoon. HOUR is used for the 12-hour clock (0 - 11). Noon and
midnight are represented by 0, not by 12. E.g., at 10:04:15.250 PM the
HOUR is 10.
When you first set hour to 12 and AM/PM to PM, one should have expected an exception because the hour value is out of range. But no, a Calendar object with default settings doesn’t give you that. Instead it sets the time to 0 AM the following day; Jan 13 when you ran the code on Jan 12. By Calendar logic hour 12 is the hour that comes after hour 11.
When you set PM again, Calendar takes off from the time you had already got, which is in AM, and changes it into PM, so you get 12:11:08, still on Jan 13, the following day.
Why does it calculate the time twice? Not once and not three times when you do three calls to set()? It’s another confusing trait of Calendar. It calculates the time when you call getTime() (and some designated other methods). At that point it picks up all the changes from the calls to set() up to that point and combines them to the best of its abilities, discarding some if there are conflicts, using rules that no person in their right mind will want to understand.
The case for AM is similar, so I leave the details to the reader.
java.time
… How to set the time correctly?
I recommend that you use java.time, the modern Java date and time API, for your time work. If you just want 12 noon or 12 midnight, they are built in as constants:
LocalTime t12Noon = LocalTime.NOON;
System.out.println(t12Noon);
LocalTime t12Midnight = LocalTime.MIDNIGHT;
System.out.println(t12Midnight);
Output is:
12:00
00:00
A LocalTime is a time of day without a date.
If you have already got a time and only want to adjust the hour and AM/PM, use with():
LocalTime t12Noon = LocalTime.now(ZoneId.systemDefault())
.with(ChronoField.CLOCK_HOUR_OF_AMPM, 12)
.with(ChronoField.AMPM_OF_DAY, 1); // 1 = PM
System.out.println(t12Noon);
LocalTime t12Midnight = LocalTime.now(ZoneId.systemDefault())
.with(ChronoField.CLOCK_HOUR_OF_AMPM, 12)
.with(ChronoField.AMPM_OF_DAY, 0); // 0 = AM
System.out.println(t12Midnight);
12:47:00.665155
00:47:00.669248
If you need the date too, use ZonedDateTime or another appropriate class. All of the date-time classes of java.time that include time of day have the same with method, so the code will be the same.
If you indispensably need a Calendar object for a legacy API that you cannot afford to upgrade to java.time just now, use a ZonedDateTIme from java.time for your time math. Then use GregorianCalendar.from(ZoendDateTIme) for the conversion to a Calendar object.
Links
Documentation of Calendar.HOUR
Oracle tutorial: Date Time explaining how to use java.time.
Calendar.HOUR takes inputs in range 0-11. It will wrap around the 12 to a 0.
Use Calendar.HOUR_OF_DAY instead to use values from 0-23.
I had the below issue During daylight change CST-CDT reset.
Am getting the Input from Was8.5 server 2018-03-11-05.00 (UTC-5) as expected, but when it comes to WAS7 server, the below method returns Sun Mar 10 00.00.00 CST 2018 instead of Sun Mar 11 00.00.00 CDT 2018
/*
* Converts XMLGregorianCalendar to java.util.Date
*/
public static Date toDate(XMLGregorianCalendar calendar){
if(calendar == null) {
return null;
}
return calendar.toGregorianCalendar().getTime();
}
I know the server date/timezone reset didn’t take place properly, but in case if I want to get right Time when CST to CDT change or vise versa. How can I rewrite the code to convert XMLGregorianCalendar to java.util.Date in Java?
Something like,
If incoming request was CST(UTC-6), the toDate(XMLGregorianCalendar calendar) returns CDT (UTC-5). then I want toDate() should return CST (UTC-6).
the same way,
If incoming request was CDT(UTC-5), the toDate(XMLGregorianCalendar calendar) returns CST(UTC-6). then i want toDate() should return CDT(UTC-5).
java.util.Date doesn't have a timezone. It just have a long value that represents the number of milliseconds since unix epoch.
What you see (Sun Mar 10 00.00.00 CST 2018) is the result of toString() method, and it uses the JVM default timezone to convert the long value to a date and time in that timezone. See this article for more details:
https://codeblog.jonskeet.uk/2017/04/23/all-about-java-util-date/
Anyway, one way to really know what's happening is to check this long value:
long millis = calendar.toGregorianCalendar().getTimeInMillis();
And then you can print this value in UTC:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss XXX");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(sdf.format(new Date(millis)));
Or, if you use Java 8:
System.out.println(Instant.ofEpochMilli(millis));
This will tell you the UTC instant that the Date corresponds to, so you can debug your code a little better than relying on Date::toString() method, which is confusing and misleading.
Regarding your main issue, I've tried to reproduce (I'm using Java 8 because it's easier to manipulate than using Date). First I created a date/time corresponding to 2018-03-11 in UTC-05:00, and I assumed the time to be midnight:
// March 11th 2018, midnight, UTC-05:00
OffsetDateTime odt = OffsetDateTime.parse("2018-03-11T00:00-05:00");
Then I converted this to America/Chicago timezone, which is a zone that uses CST/CDT:
// get the same instant in Central Time
ZonedDateTime zdt = odt.atZoneSameInstant(ZoneId.of("America/Chicago"));
Then I printed this:
// print the date/time with timezone abbreviation
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm xxx z", Locale.US);
System.out.println(zdt.format(fmt)); // 2018-03-10 23:00 -06:00 CST
Note that the result is 2018-03-10 23:00 -06:00 CST: March 10th in UTC-06:00.
That's because in 2018, Daylight Saving Time starts only at 2 AM of March 11th. At midnight, DST has not started yet, so the offset is still UTC-06:00.
Anyway, your conversion code is correct, because Date just represents a point in time (a count of elapsed time since epoch) and doesn't have timezone attached to it. Perhaps the problem lies somewhere, and checking the millis value might help you to understand what's going on (my guess is that XmlGregorianCalendar sets the time to midnight when it's not present, which would explain the result of Sun Mar 10 00.00.00 CST 2018).
If that helps, the exact UTC instant where DST transition occurs (March 11th 2018 at 2 AM in UTC-06:00) corresponds to the millis value 1520755200000. If your dates in March 2018 have a value lower than that, it means they're before DST starts, and they'll be in CST.
My first suggestion is that you don’t need what you are asking for. As I see it, you’ve got a date and a UTC offset, and I don’t really see that the offset adds any useful information. Just take the date. I believe what has happened was that a point in time after the transition to summer time on March 11 was stripped of the time-of-day, but the UTC offset was kept for whatever reason or maybe for no reason at all. When giving the time at start of day (00:00), the offset disagrees with your time zone of America/Chicago (or Central Time Zone, but the ID in region/city format is unambiguous and recommended).
And don’t use java.util.Date for your date. That class is long outdated. Today we have so much better in java.time, the modern Java date and time API. Furthermore its LocalDate class is better suited for a date without time-of-day because this is exactly what it is, while a Date is really a point a in time, that is, a whole different story. Depending on taste conversion from XMLGregorianCalendar can happen in two ways.
The direct way
return LocalDate.of(calendar.getYear(), calendar.getMonth(), calendar.getDay());
With your XMLGregorianCalendar of 2018-03-11-05:00 the result is a LocalDate of 2018-03-11.
The indirect way via GregorianCalendar and ZonedDateTime:
return calendar.toGregorianCalendar().toZonedDateTime().toLocalDate();
The result is the same. The advantage of the latter is you don’t need to concern yourself with the individual fields of year, month and day-of-month. Among other things this means you don’t risk putting them in the wrong order.
If you do insist on keeping the time zone or UTC offset, at least take the offset. Sun Mar 11 00.00.00 CDT 2018 doesn’t make sense because March 11 at 00:00 hours DST was not yet in effect (it began at 02:00). Such a non-existing time will just confuse everyone. Convert your calendar object to OffsetDateTime:
return calendar.toGregorianCalendar().toZonedDateTime().toOffsetDateTime();
Result: 2018-03-11T00:00-05:00. This point in time exists.:-)
Since your calendar comes from a foreign system, you will probably want to validate it since any field may be undefined and return DatatypeConstants.FIELD_UNDEFINED. When using LocalDate.of(), you may decide that its argument validation is enough since it will object to DatatypeConstants.FIELD_UNDEFINED being passed as an argument. toGregorianCalendar() on the other hand will tacitly use default values, so when using it I would consider validation indispensable.
What went wrong in your code?
I ran your code, and similarly to iolus (see the other answer) I got Sat Mar 10 23:00:00 CST 2018. This the correct point in time. As iolus also explained, this is Date.toString rendering the point in time this way. The Date object itself doesn’t have a time zone or UTC offset in it. So I should say that your code was correct. It was just you getting confused by the toString method. Many have been before you, and the good solution is to avoid the Date class completely. Also I would think that your observations have nothing to do with any difference between WAS 7 and WAS 8.5.
This is regarding Java Calendar and the effects we encountered after today day light saving change in Toronto.
Below is the code
Date date = new Date(); //Sun Mar 11 00:00:00 EST 2018
Integer time = 349;
Calendar scheduleDateCal = Calendar.getInstance();
scheduleDateCal.setTime(date);
scheduleDateCal.set(Calendar.MINUTE, 0);
scheduleDateCal.set(Calendar.HOUR_OF_DAY, 0);
String strSchAdminTime = String.valueOf(time);
Integer schAdminMinute = time;
if (strSchAdminTime.length() >= 2) {
schAdminMinute = Integer.valueOf(strSchAdminTime.substring(strSchAdminTime.length()-2));
}
if(time>60){
Integer schAdminHour = Integer.valueOf(strSchAdminTime.substring(0,strSchAdminTime.length()-2));
scheduleDateCal.add(Calendar.HOUR_OF_DAY, schAdminHour);
}else{
scheduleDateCal.add(Calendar.HOUR_OF_DAY, 0);
}
scheduleDateCal.add(Calendar.MINUTE, schAdminMinute);
System.out.println(scheduleDateCal.getTime());
I know this code hasn't done with the best practises however I need to maintain it for the current release. In here it uses integer to represent the time portion and later there is a logic to extract the hours and minutes from it.
When I followed the logic, the hour portion is 3. Then there is a logic to add this time to Calendar object with value 'Sun Mar 11 00:00:00 EST 2018' with below statement
scheduleDateCal.add(Calendar.HOUR_OF_DAY, schAdminHour);
Theoretically after this calculation, the calendar object should have value "Sun Mar 11 03:00:00 EDT 2018". However it returns "Sun Mar 11 04:00:00 EDT 2018" I know starting from today the time will go one hour ahead with the daylight saving. Can any one please help me to understand this
Appreciate the help.
At 00:00:00 today (Sunday March 11, 2018) summer time (DST) was not yet in effect, so that time was correctly rendered as Sun Mar 11 00:00:00 EST 2018 (EST for Eastern Standard Time). Date.toString chooses between EST and EDT based on the time contained in the Date object (not based on the time the toString method is called). When you add 3 hours to that time, you cross the time at 2 when the clock was turned forward to 3. So 3 hours after your start time the time is 04:00:00 EDT (EDT for Eastern Daylight Time).
PS Modern code
PS In case you or someone else is interested, here is the modern — both simpler and shorter — version of your code. To set the time to 03:49:
int time = 349;
ZoneId zone = ZoneId.of("America/Toronto");
ZonedDateTime scheduledDateTime = LocalDate.now(zone)
.atTime(time / 100, time % 100)
.atZone(zone);
System.out.println(scheduledDateTime);
Today this printed
2018-03-11T03:49-04:00[America/Toronto]
Still better, of course, if you can get completely rid of representing 03:49 as the integer value 349. To use your JVM’s time zone setting you may set zone to ZoneId.systemDefault(). This is fragile because the setting may be changed at any time by other parts of your program or other programs running in the same JVM.
To set the time to 3 hours 49 minutes after midnight (which with DST transition isn’t the same thing, as you have seen):
ZonedDateTime scheduledDateTime = LocalDate.now(zone)
.atStartOfDay(zone)
.plusHours(time / 100)
.plusMinutes(time % 100);
This time I got
2018-03-11T04:49-04:00[America/Toronto]
EST is -5, EDT is -4, so you get 1 hour when you are calling add().
You can use scheduleDateCal.set(Calendar.HOUR_OF_DAY, schAdminHour) and scheduleDateCal.set(Calendar.MINUTE, schAdminMinute), if you need result in different timezone.
As part of some logic, it is necessary in my program to turn a long Java timestamp (including year, month, etc.) to a 'short' Java time. This should correspond to exactly the same hours, minutes and seconds of the original time, but within 1 day of Jan 1 1970 (i.e. a value between 0 (00:00:00) and 86400000 (23:59:59)). An example is the conversion in the question.
In order the perform this, I thought the below code would work:
public int convertToTime(long fullTimeStamp) {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(date);
c.set(Calendar.DATE, 1);
c.set(Calendar.MONTH, 0);
c.set(Calendar.YEAR, 1970);
return (int) c.getTimeInMillis();
}
The issue I am having is to do with timezones. In the UK we are currently in BST. After setting all the values with the function, the time remains the same numbers (e.g. 8.00am) but changes the timezone to GMT! 8.00am GMT is of course not the same as 8.00am BST, and is instead equal to 9.00am BST.
Adding some console output to the function demonstrates this issue:
public int convertToTime(long fullTimeStamp) {
System.out.println(new Date(fullTimeStamp)); // correct
Calendar c = Calendar.getInstance();
c.setTimeInMillis(fullTimeStamp);
System.out.println(c.getTime()); // correct
c.set(Calendar.DATE, 1);
c.set(Calendar.MONTH, 0);
c.set(Calendar.YEAR, 1970);
System.out.println(c.getTime()); // incorrect!
return (int) c.getTimeInMillis();
}
Program output:
Wed Jun 19 12:15:00 BST 2013 // ok
Wed Jun 19 12:15:00 BST 2013 // this makes sense
Thu Jan 01 12:15:00 GMT 1970 // Calendar, stahp!
The desired behaviour is for the last part to read:
Thu Jan 01 11:15:00 GMT 1970
or
Thu Jan 01 12:15:00 BST 1970
Is this expected behaviour of the calendar? My understanding was that it keeps all the 'digits' the same that aren't modified, so if the value of HOUR_OF_DAY is 8, it should stay at 8, even if the timezone is modified.
I have tried setting the timezone on the calendar (before any values are set) to BST and GMT and exactly the same behaviour occurs. I also cannot manually add or remove milliseconds to delete all years after 1970 as I will have to handle leap years.
Aside from 'use Joda time (or some other time package)' does anyone have any other suggestions to perform this operation? I kind of need to get a quick fix in before experimenting with other packages if possible.
Thanks!
I think you're running foul of a little-known fact about the UK time zone: at the Unix epoch, we were actually in UTC+1. Java is getting the time of day right (within the UK time zone), but the name wrong - it shouldn't be specifying GMT, but BST. This isn't BST as in British Summer Time; it's BST as in British Standard Time. Yes, it's that mad.
From the relevant wikipedia article:
An inquiry during the winter of 1959–60, in which 180 national organisations were consulted, revealed a slight preference for a change to all-year GMT+1, but the length of summer time was extended as a trial rather than the domestic use of Greenwich Mean Time abolished.[8] A further inquiry during 1966–67 led the government of Harold Wilson to introduce the British Standard Time experiment, with Britain remaining on GMT+1 throughout the year. This took place between 27 October 1968 and 31 October 1971, when there was a reversion to the previous arrangement.
It's worth bearing in mind that your original problem statement is somewhat ambiguous: you're taking in a long, which is just the millis since the Unix epoch - but then you're trying to interpret it in terms of the hour of day, which immediately begs the question of which time zone you need to interpret it in. Have you made that decision? If so, you should document it very carefully, and make sure your code complies with it.
Ultimately, my recommendations are:
If you can possibly use Joda Time, do so. It will save you hours and hours of heartache.
If you're trying to calendar calculations like this, consider changing the time zone of the calendar to UTC before doing anything else; it will save you some heartache
Avoid using Date.toString() where possible - you could use a DateFormatter with the time zone set to UTC, and then you would see the expected results
As user2340612's answer states, to get just the "millisecond of UTC day" you can use simple arithmetic - but not quite with the values given. I would use:
long timeOfDay = millisecondsSinceUnixEpoch % TimeUnit.DAYS.toMillis(1);
... but this only works if you're interested in the UTC time of day for the given instant. (It will also give a negative result for negative input, but you may not care about that.)
If you need a timestamp between 0 and 86399999 (which is 23:59:59.999) you can get the current timestamp and calculate the remainder of the division between it and 86400000:
desired_time = cur_time % 86400000
But you'll miss the summer time, if present.