Currently I have a AIX SERVER and if I issue the command date on the terminal, I get the response as Mon Nov 4 00:28:40 EST 2013.
And I have the seconds value 1383561560 which is the number of seconds since Wednesday, 31 December 1969, 19:00:00 (UTC time).
Now I have a code which computes the date and time since 31 December 1969, 19:00:00 (UTC time) to EST time.
DateFormat df = new SimpleDateFormat("MM/dd/yyyy_HH:mm:ss a");
df.setTimeZone(TimeZone.getTimeZone("EST"));
long ms = (long)(1383560920) *1000;
Date d1 = new Date(ms);
String formattedDate = df.format(d1);
System.out.println("Now the date/time is "+formattedDate );
Now the date/time displayed is 11/04/2013_05:28:40 AM
Here why is there a 5 hour difference?
Java Date base time start from 1 January 1970, 00:00:00 but your time is 31 December 1969, 19:00:00. So the difference of calculated time will be 5 hour.
For details, read this docs.
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.
Parameters: date - the milliseconds since January 1, 1970, 00:00:00 GMT.
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.
In my Java project I used date in long and for example it is 12136219 and by creating Date object as below:
long time = 12136219;
Date date = new Date(time);
and it represent date as Thu Jan 01 04:22:16 CET 1970. How can I round date (in long representation) to minutes ?
For example I want achieve Thu Jan 01 04:22:00 CET 1970 if the seconds are <30 and Thu Jan 01 04:23:00 CET 1970 if the seconds are >=30 but I want round this long time = 12136219 representation. Any idea?
Don’t reinvent the wheel. Use java.time.Instant for representing an instant in time:
Instant i = Instant.ofEpochMilli(time);
i = i.plusSeconds(30).truncatedTo(ChronoUnit.MINUTES);
Instant doesn’t offer rounding, only truncation. However, adding 30 seconds and then truncating gives you what you want. If you need your milliseconds back, it’s easy:
time = i.toEpochMilli();
System.out.println(time);
With the number from your question this prints
12120000
(This is equal to an instant of 1970-01-01T03:22:00Z, or 1970-01-01T04:22+01:00[Europe/Paris] in CET, or the expected rounding down of your 04:22:16 CET.)
PS I am quite convinced that a library like Time4J will offer rounding so you don’t need the trick of adding and truncating. Unfortunately I don’t have the experience to give you the details.
Since time is
"milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT"
You could calculate the seconds like this:
secondsInMillis = time % (60 * 1000) //get remainder (modulo): seconds * milliseconds
if (secondsInMillis < 30000) {
time -= secondsInMillis; //round down
} else {
time += (60000 - secondsInMillis); // round up
}
When you create a Date from a long, the long represents the number of milliseconds since Jan 1, 1970. There are 60*1000 milliseconds in a minute. That should be enough information to fashion the rounding algorithm you need.
Reset seconds and milliseconds with Calendar.set
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
cal.add(Calendar.MINUTES, calendar.get(Calendar.SECOND) >= 30 ? 1 : 0)
currentDate = cal.getTimeInMillis();
Sets the given calendar field to the given value. The value is not interpreted by this method regardless of the leniency mode.
You should do it on the Date object. There is no easy way to calculate it in the time epoch, because of various difficulties including the length of a month (28, 29, 30 or 31).
DateTimeFormatter df = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss");
DateTime temp = df.withOffserParsed().parseDateTime("1970-01-02T00:00:00");
Date date = temp.toDate();
DateTime dateTime = new DateTime(date);
long epoch_start_date = dateTime.getMillis();
System.out.println(dateTime);
This prints out 1970-01-02T00:00:00.000+12:00
Is there any way to remove the Time Zone (+12:00 bit) from the dateTime object? It results in the resultant time being 2 Jan 1970 12:00pm rather than the desired 2 Jan 1970 12:00 am.
That's the local time offset from UTC/GMT.
Is there any way to remove the Time Zone (+12:00 bit) from the dateTime object?
Yes, there is a way, but it would result in the wrong time. Assuming you live in Fiji or Kiribati, the time given is the correct time. It's expressed with (+12:00) to indicate which time zone you're in.
It results in the resultant time being 1 Jan 1970 12:00pm rather than the desired 1 Jan 1970 12:00 am.
Not in actual fact, due to the reason I've given. The desired time is 2 Jan 1970, not 1 Jan. The GMT time is 1 Jan at 12:00pm at that moment, and your local time is 2 Jan at 12:00am.
Having given you proper warning, here's how you could work around the time zone:
DateTimeFormatter df = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss");
DateTime temp = df.parseDateTime("1970-01-02T00:00:00");
Date date = temp.toDate();
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
int offset = calendar.getTimeZone().getOffset(calendar.getTimeInMillis());
calendar.add(Calendar.MILLISECOND, offset);
calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
DateTime dateTime = new DateTime(calendar);
System.out.println(dateTime);
Just keep in mind, by doing this you may be throwing away any benefit you were getting from Joda expressing time in UTC.
Date now = new Date();
long timeInterval = now.getTime() - (15705 * 24 * 60 * 60 * 1000L);
long hours = timeInterval / (60 * 60 * 1000L);
LOG.debug(String.format("current date:%s, timeInterval:%d,hours:%d",now.toString(),timeInterval, hours));
The result that system print is(15705 means the number of days since 1970s):
12/12/31 22:06:47 DEBUG stat.TimeTest: current date:Mon Dec 31
22:06:47 CST 2012, timeInterval:50807153, hours:14
You can see the current hour is 21 hours, but the result displays as 14 hours.
Mon Dec 31 22:06:47 CST 2012 is Mon Dec 31 14:06:47 2012 in GMT time, which is the time zone used for the start of the epoch.
In other words, now.getTime() returns the number of milliseconds since January 1, 1970, 00:00:00 GMT and you use a different time zone.
now.getTime() would get you the value in UTC millis - thats GMT+0.
the log print you showed probably uses the system time zone, where it was 22:06:47 and probably wasnt anywhere near england :-)
also, please use the Calendar class for date arithmatic because it, unlike your code, would take into account things like leap years, leap seconds and timezone changes (wehich dont happen in UTC, might mihg thappen in any other zone)
I have searched for the answer to this, but cannot seem to find one. Can someone explain why the following code does not give me the 1 of each month, but jumps to the 31 for some months? It is for a report event that needs to determine the next month's date.
DateFormat formatter = new SimpleDateFormat("dd-MMM-yy");
Calendar cal = Calendar.getInstance( TimeZone
.getTimeZone( "America/Los_Angeles" ) );
DateFormat formatter = new SimpleDateFormat("dd-MMM-yy");
Date date;
try
{
date = (Date)formatter.parse("01-JUN-12");
cal.setTime(date);
//cal.set(2012, 05, 01);
long now = new Date().getTime();
int frequency = 1;
System.out.println("Current calendar time=" + cal.getTime().toString()) ;
while (cal.getTime().getTime() < now)
{
cal.add( Calendar.MONTH, frequency );
System.out.println("In loop - current calendar time=" + cal.getTime().toString()) ;
}
}
catch (ParseException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
The output is:
Current calendar time=Fri Jun 01 00:00:00 EDT 2012
In loop - current calendar time=Sun Jul 01 00:00:00 EDT 2012
In loop - current calendar time=Tue Jul 31 00:00:00 EDT 2012
In loop - current calendar time=Fri Aug 31 00:00:00 EDT 2012
In loop - current calendar time=Mon Oct 01 00:00:00 EDT 2012
In loop - current calendar time=Wed Oct 31 00:00:00 EDT 2012
Notice how it jumps to 31, then back to 1. If I use Calendar.set() instead, the output is correct:
Current calendar time=Fri Jun 01 15:14:26 EDT 2012
In loop - current calendar time=Sun Jul 01 15:14:26 EDT 2012
In loop - current calendar time=Wed Aug 01 15:14:26 EDT 2012
In loop - current calendar time=Sat Sep 01 15:14:26 EDT 2012
In loop - current calendar time=Mon Oct 01 15:14:26 EDT 2012
In loop - current calendar time=Thu Nov 01 15:14:26 EDT 2012
This seems like it is either 1) A bug with the Calendar API, or 2) a lack of understanding of how the Calendar API works. In either case, I just want the next month (same day), unless of course there is a problem with certain months and days. But the above scenario is puzzling me. This does not happen with any other day of the month, only with the 1st.
It's a timezone issue. Replace your existing setup lines with the following, so that the Calendar and the Formatter are both using the same timezone:
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
formatter.setTimeZone(tz);
Calendar cal = Calendar.getInstance(tz);
and it all works fine:
Current calendar time=Fri Jun 01 08:00:00 BST 2012
In loop - current calendar time=Sun Jul 01 08:00:00 BST 2012
In loop - current calendar time=Wed Aug 01 08:00:00 BST 2012
In loop - current calendar time=Sat Sep 01 08:00:00 BST 2012
In loop - current calendar time=Mon Oct 01 08:00:00 BST 2012
In loop - current calendar time=Thu Nov 01 07:00:00 GMT 2012
You may want to replace the toString() calls with formatter.format() calls so that the output is in the right timezone too (it might look OK to you if you are in EDT, but I'm in the UK timezone, as you can see).
Instead of just cal.add( Calendar.MONTH, frequency );, try this:
cal.add(Calendar.MONTH, frequency);
cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH));
You are printing the time in the system default time zone. Use formatter to display it.
Set the same time zone for the formatter:
DateFormat formatter = new SimpleDateFormat("dd-MMM-yy");
formatter.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
And use it to display the time:
while (cal.getTime().getTime() < now) {
cal.add(Calendar.MONTH, frequency);
System.out.println("In loop - current calendar time=" + formatter.format(cal.getTime()));
}
From Javadoc Calendar
add(f, delta) adds delta to field f. This is equivalent to calling set(f, get(f) + delta) with two adjustments:
Add rule 1. The value of field f after the call minus the value of
field f before the call is delta, modulo any overflow that has
occurred in field f. Overflow occurs when a field value exceeds its
range and, as a result, the next larger field is incremented or
decremented and the field value is adjusted back into its range.
Add rule 2. If a smaller field is expected to be invariant, but it is
impossible for it to be equal to its prior value because of changes
in its minimum or maximum after field f is changed or other
constraints, such as time zone offset changes, then its value is
adjusted to be as close as possible to its expected value. A smaller
field represents a smaller unit of time. HOUR is a smaller field than
DAY_OF_MONTH. No adjustment is made to smaller fields that are not
expected to be invariant. The calendar system determines what fields
are expected to be invariant.
In addition, unlike set(), add() forces an immediate recomputation of the calendar's milliseconds and all fields.
Example: Consider a GregorianCalendar originally set to August 31, 1999. Calling add(Calendar.MONTH, 13) sets the calendar to September 30, 2000. Add rule 1 sets the MONTH field to September, since adding 13 months to August gives September of the next year. Since DAY_OF_MONTH cannot be 31 in September in a GregorianCalendar, add rule 2 sets the DAY_OF_MONTH to 30, the closest possible value. Although it is a smaller field, DAY_OF_WEEK is not adjusted by rule 2, since it is expected to change when the month changes in a GregorianCalendar.
So in your case considering the time that you have set if calendar API add month to Jul then it is possible that it is getting overflown more than a month so it is being adjusted back Jul Midnight