I have a very simple Java program as follows:
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
System.out.println(new Date(cal.getTimeInMillis()));
System.out.println(cal.get(Calendar.ZONE_OFFSET));
cal.set(Calendar.ZONE_OFFSET, 0);
System.out.println(new Date(cal.getTimeInMillis()));
}
My home timezone is GMT. For the purpose of my experiment I set the computer's timezone to EDT and observed the system clock has moved back 5 hours.
When I run the program, I get this output:
Sat Apr 25 10:09:23 EDT 2015
-18000000
Sat Apr 25 05:09:23 EDT 2015
The Sat Apr 25 10:09:23 EDT 2015 indicates the system time and timezone, as expected.
The -18000000 indicates the zone offset in ms, which is negative 5 hours as expected.
When I set the zone offset to 0, I would expect the time to read my real local time of 15:09 but instead it reads 05:09, in other words, it has taken off another 5 hours instead of adding them.
Why? I'm confused!
I think what you have done is set a calendar with the current time in the current timezone (-5 hours) (10:09) you then got the time in Milliseconds which returns the milliseconds time as if you are in GMT. Ie It adds 5 hours (15:09)
System.out.println(New Date(milliseconds)) interprets that time in the current timezone (-5 hours)(10:09)
You then change the zone offset to zero but keep the day and time numbers unchanged. (10:09) You then take time in Milliseconds again which again is as if it is GMT (it adds nothing) (10:09)
System.out.println(New Date(time in millis)) interprets that time in the current timezone (-5 hours) (05:09)
The key information here is that a Date is always GMT internally and the timezone is only applied when you format it or call toString() which is done by the println (...) method.
calendar.getTimeInMillis () always returns the number of milliseconds since the start of 01/01/1970 UTC (the computer epoch)
Java takes your computer's timezone which is EDT. When you set the Calendar.ZONE_OFFSET to '0' then it will give you the current EDT time. If you need to get your current time you need to set the Calendar.ZONE_OFFSET related to EDT which is 5 hours (+18000000ms).
[UPDATE]
Time zone offset set to '0' means declaring I am at GMT and system time is GMT.
But you need to keep in mind that still you are in your original location which is -18000000.
When you calculate you take the correct location which is -1800000 and then adjust 5 hours early time.
Let me explain what you did using an example.
Think you can go to any location of the world in no time.
You set your time at New York (EST) to 10:00.
Now you go to Greenwich (GMT) and you are saying that my time is 10:00. (setting the time zone offset to '0')
Then you come to New York (EST) and print your time thinking that 10:00 is the time at Greenwich (GMT) which is 05:00.
Related
Edit: It turns out the problem is not about Jackson, but about time adjustment in Thailand on 1 April 1920.
How does com.fasterxml.jackson.databind.ObjectMapper works? I thought it used Unix timestamp.
I tried converting a java.util.Date with mapper.writeValueAsString().
When I convert the string back to Date with mapper.readerFor(Date.class).readValue(), the result is correct.
However, when I trying removing the last 3 digits and and put the same string into some converter websites, the result is off for some minutes and seconds.
Please see the code below.
Date wayBack = new SimpleDateFormat("yyyy-MM-dd").parse("1900-01-31");
System.out.println(wayBack); // Wed Jan 31 00:00:00 ICT 1900
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(wayBack)); // -2206420924000
Date deserialised = mapper.readerFor(Date.class).readValue(mapper.writeValueAsString(wayBack));
System.out.println(deserialised); // Wed Jan 31 00:00:00 ICT 1900
Below is a screenshot from http://www.onlineconversion.com/unix_time.htm
Please note that 7-hour off is expected because of my timezone but I don't understand the 17:56 minutes different.
EDIT - Here is my attempt to provide a better answer than my first one.
Background
Before looking at the code in the question, some background notes:
The epoch value (in seconds) at midnight 31st Jan 1900 in Bangkok is -2206420924:
LocalDateTime localDateTime = LocalDateTime.parse("1900-01-31T00:00:00");
ZoneId z = ZoneId.of("Asia/Bangkok");
ZonedDateTime ict_1 = ZonedDateTime.of(localDateTime, z);
System.out.println("Epoch seconds: " + ict_1.toEpochSecond());
System.out.println("ICT datetime : " + ict_1);
The above prints this:
Epoch seconds: -2206420924
ICT datetime : 1900-01-31T00:00+06:42:04[Asia/Bangkok]
The epoch value (in seconds) for UTC midnight on the same date is -1570060800:
ZonedDateTime utcDateTime = ZonedDateTime.parse("1900-01-31T00:00:00Z");
System.out.println("Epoch seconds: " + utcDateTime.toEpochSecond());
System.out.println("UTC datetime : " + utcDateTime);
The above prints this:
Epoch seconds: -2206396800
UTC datetime : 1900-01-31T00:00Z
The time at midnight in Bangkok on 31st January 1900 was 24,124 seconds further into the past than the time at midnight in Greenwich, UK (the prime meridian - or UTC).
That is to say, on that date Bangkok was 6 hours, 42 minutes and 4 seconds ahead of UTC time (or GMT as I believe it was then called - as UTC had not been established at that time).
The Specific Code in the Question
First, I changed my default time zone to match the one used in the question:
System.setProperty("user.timezone", "Asia/Bangkok");
The below line from the question does the following:
(1) The SimpleDateFormat constructor, in which the date format string does not specify a locale, uses the default locale.
(2) Then the parse() method creates the Date object:
Date wayBack = new SimpleDateFormat("yyyy-MM-dd").parse("1900-01-31");
At this point we can check the date object:
System.out.println(wayBack);
System.out.println(wayBack.getTime());
This prints the following:
Wed Jan 31 00:00:00 ICT 1900
-2206420924000 // epoch milliseconds
This matches what we saw earlier, in the background section.
When you use an online tool such as the one mentioned in the question, you will see the above milliseconds value reported as the following GMT (UTC) datetime:
GMT: Tuesday, January 30, 1900 5:17:56 PM
For the above output I used this tool.
Again, this is what we expect - when it's midnight in Bangkok, it's still the afternoon of the day before in Greenwich, UK.
The remainder of the code (including the Jackson object mapper transformations) are all subject to this initial set-up of your Date object.
For the question: "How does com.fasterxml.jackson.databind.ObjectMapper works? I thought it used Unix timestamp." It shows the same behavior as the core Java date object. I believe your assumption is correct.
Regarding the Unusual Offset
Regarding the ICT offset of +06:42:04 shown above:
On April 1st 1920, an adjustment was made to the local ICT (Indochina Time), to align it with UTC time (with an offset of +7 hours, as you note). The local clocks were set forward by 17 minutes and 56 seconds, to round up the UTC (GMT) offset to 7 hours.
See this link for a specific reference to the 17 minutes & 56 seconds change.
This is why you will not see that unusual offset from April 1920 onwards.
Further Links
See this answer regarding the newer java.time classes which should be used instead of java.util.Date.
See this question and its answers for a related deep-dive into the topic of historic time zone adjustments.
The java.util.Date class is based on the number of seconds since 1 January 1970 00:00 GMT. So why does this code
System.out.println(new Date(0));
print Thu Jan 01 01:00:00 GMT 1970? My local time zone is GMT, so I expected it to print 00:00:00 GMT.
There is an interesting reason for this. Refer (BST Offset bug report)
.
It says, "and the experiment with British Standard Time from 1968 to 1972, by which the time was advanced by one hour from GMT throughout the year." And further: “The local time produced by Date.toString() is historically correct, except for the time zone abbreviation. It should be "BST" (British Standard Time for this case), but it's a known limitation of the current TimeZone implementation.”
This link might help. I'm quite a novice at the Date class, but I figured this could help somehow.
Unix Epoch Time is a system of time describing how much time has elapsed since January 1st, 1970.
Therefore, when you create a new java.util.Date object with 0 milliseconds elapsed, it will return January 1st, 1970.
What you are looking for is here.
In Joda-Time version 2.9.9 I want to remove time part of DateTime variable.
Only for time zone Asia/Tehran and some dates like (2036-03-21, 2037-03-21, ...) it returns 1:00:00 in time part of result.
I also checked the Joda-Time source code but I couldn't find any problem.
The code is:
DateTime dt = new DateTime(2036, 03, 21, 10, 0, DateTimeZone.forID(Asia/Tehran));
dt = dt.withTimeAtStartOfday();
Actual result:
2036-03-21T01:00:00.000+04:30
Expected result:
2036-03-21T00:00:00.000+04:30
Time is not zero. This only happens for zone Asia/Tehran.
My system config:
Java version: 1.7.0_72 - I have to use Java 7
Joda-Time: 2.9.9
I solved this problem by converting DateTime to LocalDate, but I want to know why this problem happens?
This is because Iran switches from Standard time to Daylight Savings time at 00:00 on the 21st or 22nd of March (whichever day contains the astronomical equinox).
In 2036 this happens on the 21st. In 2018 it happens to fall on the 22nd.
In short, the time jumps from 2036/03/20 24:00 to 2036/03/21 01:00. The hour from midnight to 1 AM does not exist on that specific day.
When writing code that deals with time, ALWAYS keep in mind that unexpected small offsets from expected results are almost surely due to administrative time changes. This is even more true for historical dates, where offsets could be any number of minutes and seconds, not just whole or half-hours.
Can we get from which timezone the Long Value is produced?
I have long values of Date. I want to know from which timezone it is generated.
For e.g
Long value: 1435640400000
Date: 30 June 2015 CDT
I want to develop program which input will be the Date in long value
that will return output as Timezone with the respective
long value for 30 June 2015 12:00 AM GMT/UTC
The unix time (as it is called) is not a date. You can calculate a date from it but it really is just the duration of seconds (or ms) since 01/01/1970 at 00:00 UTC.
This means it has no timezone attached to it. You need the "target" timezone to calculate the actual date from it, but simply having this number does not include any timezone information (which means you'll need to get it somewhere else in order to calculate dates).
Think of the unix timestamp more as a duration than a date. It's like saying "I'll meet you in 30 minutes". Those 30 minutes do not have a timezone attached to them. To you and the person you're talking to, that meeting might happen at different dates (e.g. 2:30pm vs. 3:30pm) because of timezones. But it will still happen at the same point in time relative to the moment you said it.
I hope this makes the difference somewhat clearer.
There was a way to do this directly via the getTimezoneOffset() function in the Date class but that has been deprecated.
It has been replaced by
(Calendar.get(Calendar.ZONE_OFFSET) + Calendar.get(Calendar.DST_OFFSET)) / (60 * 1000)
I will get a Date like Sat May 31 16:38:17 GMT 2014. In DB also column has the same value. But when I will search for date like Sat May 31 16:30:00 GMT 2014 it wont search. But If I give less than 5:30 hours like Sat May 31 11:00:00 GMT 2014 . It works fine.
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone( "GMT-0:00" ));
cal.setTimeInMillis(inputDate.getTime());
cal.set(Calendar.MILLISECOND, 0);
inputDate = cal.getTime();
The underlying implementation of Date and Calendar both use the same system: a long representing milliseconds since 1st of January 1970, 0:00:00 GMT.
See Here for Date (lines 136 & 168) and HERE for Calendar (line 778)
So this gets us to this:
cal.setTimeInMillis(inputDate.getTime());
// this gets the long from the Date and puts it into the Calendar
// nothing is changed, you only get added functionality
// even if you change the Timezone this only affects how it is displayed
cal.set(Calendar.MILLISECOND, 0);
// the long is modified by setting the that represents only milliseconds to zero
inputDate = cal.getTime();
//the long is stored in a Date and returned
You get back a minimally altered Date object. Without the second instruction there would be no change at all.
This explains why nothing is converted, but not why the select doesn't work.
From the info you gave it seems there is a mismatch in the timezones of trinity of the DB, the JVM and your system.
Something in the line of this: your system is set to IST and your JVM somehow interprets your system time as GMT. To check this you could run System.out.println(new Date()). If it writes out the timezone as GTM but the numbers are the same as your systems clock (IST) then that is your problem.
You could also check:
do the time-stamps of an insert statement match with what arrives in the database
do the time-stamps of a database entry match the one you receive after a select