It seems that Java and JavaScript give different results for negative millisecond values.
JAVA:
System.out.println(new Date(-12220000000000L));
Wed Sep 26 12:33:20 MST 1582
JavaScript:
console.log(new Date(-12220000000000));
Date {Wed Oct 06 1582 12:33:20 GMT-0700 (LMT)}
There is a 10 day difference in their outputs. But for some values, the difference is less than 10 days. I used W3C TryIt editor to test JavaScript output. I don't know exactly where the deviation begins. Am I doing something wrong here?
The default date for the switch from Julian to Gregorian calendar in Java's GregorianCalendar class "is October 15, 1582 (Gregorian). Previous to this, dates will be in the Julian calendar."
Hence, the OP's Java date of Sep 26, 1582 is a Julian date. The difference between Julian and Gregorian dates in 1582 was 10 days.
If the JavaScript implementation in question doesn't respect the Julian to Gregorian switch but works with Gregorian dates regardless of the point in time we are at the situation the OP experienced.
Using nashorn, you can see when they deviate:
> function printDate(x) { System.out.println(new Date(x) + " - " + new java.util.Date(x)); }
> printDate(-12219292800000)
Fri Oct 15 1582 00:00:00 GMT+0000 (GMT) - Fri Oct 15 00:00:00 GMT 1582
> printDate(-12219292800001)
Thu Oct 14 1582 23:59:59 GMT+0000 (GMT) - Thu Oct 04 23:59:59 GMT 1582
This corresponds to a switch from the Julian to Gregorian calendar.
Java's date library takes this change into account, but JavaScript's doesn't.
Both java getTime() and javascript getTime() returns many milliseconds have passed since January 1, 1970, 00:00:00 GMT. Negative argument calculate the date to the past.
I found a bug in jdk . Reading this it seems in jdk 7 there is a lack of 10 days from Oct 5 1582 to Oct 15 1582. I think the 10 days difference from javascript getTime() comes from this bug.
Related
I have an application which I create dates that a user can select to an appointment. If a user start to work at 9, and an appointment takes 2 hours, I create dates at 9, 11, 13... until a limit, of course. And then I change the day and start again.
This is the code for doing this:
public List<Agenda> createListOfDates(Calendar initial, Calendar end,
int appointmentDuration, int lunchTimeDuration, int lunchTimeStart) {
List<Agenda> agendaList = new ArrayList<Agenda>();
Agenda agenda = new Agenda();
agenda.setWorkingHour(initial.getTime());
agendaList.add(agenda);
while (true) {
initial.add(Calendar.HOUR_OF_DAY, appointmentDuration);
// Logger.error("" + initial.getTime());
if (initial.getTime().after(end.getTime())) {
break;
} else if (initial.get(Calendar.HOUR_OF_DAY) == lunchTimeStart
&& initial.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY
) {
initial.add(Calendar.HOUR_OF_DAY, lunchTimeDuration);
agenda = new Agenda();
agenda.setWorkingHour(initial.getTime());
agendaList.add(agenda);
} else {
agenda = new Agenda();
agenda.setWorkingHour(initial.getTime());
agendaList.add(agenda);
}
}
for(Agenda agendaX : agendaList){
Logger.info("" + agendaX.getWorkingHour());
}
return agendaList;
}
I am working with the "America/Sao_Paulo" timezone to create these dates. I set the variables "initial" and "end" as "America/Sao_Paulo". My system timezone is "GMT", and that is ok, because I want to save these dates in GMT in the database. When I print the dates in last "for", magically it is already converted from "America/Sao_Paulo" to "GMT" and it is printing right. The strange thing is that from a certain date, it changes the time zone. Example of prints:
Sat Mar 30 12:00:00 GMT 2019
Sat Mar 30 14:00:00 GMT 2019
Sat Mar 30 16:00:00 GMT 2019
Sat Mar 30 18:00:00 GMT 2019
Mon Apr 01 13:00:00 BST 2019
Mon Apr 01 15:00:00 BST 2019
Mon Apr 01 18:00:00 BST 2019
Mon Apr 01 20:00:00 BST 2019
Mon Apr 01 22:00:00 BST 2019
While is in GMT, it is right, but I can't understand this BST. Can it be because it's too much in the future? It always starts on April.
Your system time isn’t GMT, it’s Europe/London (or something similar). In March London time coincides with GMT. Not in April. That’s why.
getWorkingHour() returns an instance of Date (another poorly designed and long outdated class, but let that be a different story for now). When you append it to the empty string, Date.toString is implicitly called and builds the string using your system time zone. During standard time it prints GMT as time zone abbreviation. Summer time (DST) begins in London on the last Sunday of March, in this case March 31. So in April Date.toString on your JVM uses British Summer Time and its abbreviation, BST for printing the time.
The good solution involves two changes:
Don’t rely on the JVM’s default time zone. It can be changed at any time from another part of your program or another program running in the same JVM, so is too fragile. Instead give explicit time zone to your date-time operations.
Skip the old date-time classes Calendar and Date and instead use java.time, the modern Java date and time API. It is so much nicer to work with and gives much clearer code, not least when it comes to conversions between time zones.
Instead of Calendar use ZonedDateTime. Depending on the capabilities of your JDBC driver, convert it to either Instant or OffsetDateTime in UTC for saving to the database.
To create a ZonedDateTime, one option is to use one of its of methods (there are several):
ZonedDateTime initial = ZonedDateTime.of(2019, 3, 10, 9, 0, 0, 0, ZoneId.of("America/Sao_Paulo"));
This creates a date-time of March 10, 2019 at 09:00 in São Paolo. To add 2 hours to it:
int appointmentDuration = 2;
ZonedDateTime current = initial.plusHours(appointmentDuration);
System.out.println(current);
Output:
2019-03-10T11:00-03:00[America/Sao_Paulo]
To convert to an Instant for your database:
Instant inst = current.toInstant();
System.out.println(inst);
Output:
2019-03-10T14:00:00Z
Instants are time zone neutral, just a point in time, but print in UTC. Some JDBC drivers accept them for UTC times. If yours doesn’t happen to, you will need to give it an OffsetDateTime instead. Convert like this:
OffsetDateTime odt = current.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC);
System.out.println(odt);
Output:
2019-03-10T14:00Z
Note that I give UTC explicitly rather than relying on the JVM default. So this is explicitly in UTC. You notice that the date and time agree with what was printed from the Instant.
I had a situation where the Java runtime returned sort of "inverted" millisecond values when reading dates from the database (in java.sql.Date). The millisecond value was approximately the same amount of days, but counted backwards from year 0.
The problem was solved by just restarting the Java runtime.
But: I found out that Java handles these "inverted" values almost correctly except of the week day.
When you run the following code:
System.out.println(new java.util.Date(253402214400000l));
System.out.println(new java.util.Date(-377648784000000l));
You will get the following output:
Fri Dec 31 01:00:00 CET 9999
Tue Dec 31 01:00:00 CET 9999
Another example:
System.out.println(new java.util.Date(-294192000000l));
System.out.println(new java.util.Date(-123967324800000l));
Result:
Mon Sep 05 01:00:00 CET 1960
Mon Sep 05 01:00:00 CET 1960
When using online converters, the result will be different for the particular second line. It will result in a negative date (the year is negative) close to the real, positive date:
Example1:
253402214400000 = Fri Dec 31 9999 01:00:00
-377648784000000 = Tue Oct 15 -9998 02:00:00
Example 2:
-294192000000 = Mon Sep 05 1960 02:00:00
-123967324800000 = Mon Aug 19 -1959 02:00:00
I have not found any information about this "topic".
So, what's the myth behind "inverted" dates? Why does Java handle them almost correctly? And what is the sense of a JDBC ResultSet returning "inverted" millisecond values when calling resultSet.getDate(1).getTime()?
When you are passing a negative number in the Date constructor then it is considered as number of milleseconds before 1/1/1970. The Javadoc says:
date - milliseconds since January 1, 1970, 00:00:00 GMT not to exceed
the milliseconds representation for the year 8099. A negative number
indicates the number of milliseconds before January 1, 1970,
You can see the result which you get when you try to provide the Long.MIN_VALUE and Long.MAX_VALUE in the Date constructor.
DateFormat df = new SimpleDateFormat("d MMM yyyy G, HH:mm:ss.S Z");
System.out.println(df.format(new Date(Long.MIN_VALUE)));
System.out.println(df.format(new Date(Long.MAX_VALUE)));
Ideone Demo
I found out that Java handles these "inverted" values almost correctly except of the week day.
In your first example, the two dates are not the same - one is BC and the other one AD (which explains why the day of the week is different):
Date d1 = new Date(253402214400000l);
Date d2 = new Date(-377648784000000l);
DateFormat fmt = new SimpleDateFormat("yyyy G");
System.out.println(fmt.format(d1)); //9999 AD
System.out.println(fmt.format(d2)); //9999 BC
So your observation is just a coincidence (however there may be a date formatter somewhere that has gone wild and negates the years or the years may actually be negative in your database).
The difference with online converters is probably due to how the year 0 is taken into account and/or variations in the calendar used for the calculations.
Please refer to the simple code below. I want to deduct one month from current month and expecting value of February 2015 in output.
Calendar today = Calendar.getInstance();
System.out.println(today.getTime().toString());
today.set(Calendar.MONTH, -1);
Date date = today.getTime();
System.out.println(date.toString());
The program output is as below. I don't see the month February as expected, instead is shows Dec 2014 as month.
Tue Mar 31 11:48:34 EDT 2015
Wed Dec 31 11:48:34 EST 2014
Thanks for your time and help.
You're setting the month number to be -1, i.e. "December of the year before". You want to add -1 month:
today.add(Calendar.MONTH, -1);
I'd strongly recommend using Joda Time or Java 8's java.time package if you possibly can though - both are much nicer APIs than Date/Calendar.
I currently have two Calendar instances that are set to specific dates and times, they are:
Fri 14th March 2015 - 09:00:00 GMT
Mon 17th March 2015 - 10:30:15 GMT
What i'd like is a way to get the difference in days, hours, minutes and seconds, in a Day:HH:MM:SS format. Note the dates can be in different timezones. So in this example, the answer should be:
3:1:30:15
Any idea how I can achieve this?
I've just came upon a very strange behaviour of Java's Date class when I try to create two dates consequently:
Date startDate = new Date(1282863600000L);
System.out.println(startDate);
Date endDate = new Date(1321919999000L);
System.out.println(endDate);
The output is respectively:
Fri Aug 27 00:00:00 BST 2010
Mon Nov 21 23:59:59 GMT 2011
Has anyone seen something like that? Both date are initialized in an identical manner but when printed the first is shown in BST and the latter in GMT?
I tried to find explanation about that but I didn't. Can someone help me?
Thanks in advance!
This is documented behaviour.
From Date.toString():
Converts this Date object to a String of the form:
dow mon dd hh:mm:ss zzz yyyy
zzz is the time zone (and may reflect daylight saving time). Standard time zone abbreviations include those recognized by the method parse. If time zone information is not available, then zzz is empty - that is, it consists of no characters at all.
You are using a locale that uses British Summer Time and creating a date where a day-light-saving rule applies. This would be the expected form of the date at that time to a local user.
For me the output of this code is
Fri Aug 27 01:00:00 CEST 2010
Tue Nov 22 00:59:59 CET 2011
The exact result depends on the default locale Java is using on your system.
The difference is that CEST is the central european summer time, while CET is the central european time (i.e. not summer time).
You seem to be running in a british locale (en_GB or similar), so your output shows the British Summer Time and the Greenwich Mean Time respectively.
The first date you specify falls into the respective summer times and the second doesn't. So Java chooses the appropriate time zone for each locale/time combination.
After a lovely session of trying different long values I got this:
Date startDate1 = new Date(1284245999999L);
Date startDate2 = new Date(1284246000000L);
System.out.println(startDate1);
System.out.println(startDate2);
Date endDate = new Date(1321919999000L);
System.out.println(endDate);
The output was:
Sun Sep 12 01:59:59 IDT 2010
Sun Sep 12 01:00:00 IST 2010 <-- Long value is greater, but due to DST changes, actual time is one hour earlier
Tue Nov 22 01:59:59 IST 2011
Note that incrementing the long by 1 from 1284245999999L to 1284246000000L takes us "back in time" because of the transition from standard time to daylight savings time.
That is how Java time calculation behaves - the number of milliseconds since 1/1/1970 does not change, but the time it represents is based on the timezone.