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).
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.
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.
I am using Calendar on a Samsung Note. If I get a new instance of Calendar with Calendar.getInstance() and then call getTimeInMills() without doing anything else I get 1403732346277, which apparently is some value in the very far future.
I need to get a unix style timestamp. Is there some other preferred way to do this? Or some reason why the Calendar is returning this value (i.e. a standard adjustment I can make)?
Unix time represents the number of seconds from the epoch. As the name implies, getTimeInMillis() will return the number of milliseconds from the epoch. You need to divide your milliseconds by 1000 to get unix time.
long unixTime = Calendar.getInstance().getTimeInMillis() / 1000;
getTimeInMillis() returns you the time difference from Jan 1, 1970 with the calendar time in milliseconds.
Here is the calculation:
1403732346277 ms = 1403732346.277 seconds
1403732346.277 s = 389925.6517... hours
389925.6517 h = 16246.90 days
16246.90 days = 44.512 years (simple calculation: I divided by 365 just to get an approx idea. There are leap years in between.)
If you find the difference of current date from Jan 1, 1970, it is 44 years and ~6 months. So it seems to be giving you right time in milliseconds.
Or some reason why the Calendar is returning this value (i.e. a standard adjustment I can make)?
The java.util.Calendar API stores dates and times as the number of milliseconds that have elapsed from epoch (January 1, 1970 midnight UTC).
The number you got from Calendar.getInstance() - 1403732346277 - is what you'd expect. It's the number of milliseconds from epoch up to today at the exact time you called Calendar.getInstance().
If you want to extract more human-readable date/time information from that Calendar object, you can do something like:
Calendar cal = Calendar.getInstance();
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH);
int day = cal.get(Calendar.DAY_OF_MONTH);
int hour = cal.get(Calendar.HOUR_OF_DAY);
int minutes = cal.get(Calendar.MINUTE);
I need to get a unix style timestamp. Is there some other preferred way to do this?
As this post points out, you can get UNIX epoch by:
long unixTime = System.currentTimeMillis() / 1000L;
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 the following code to try and get the current day in seconds since epoch, by removing any seconds after 00:00:00 on any given day:
public void method(Date date) {
...
long dayDate = date.getTime() - (date.getTime() % 86400L);
...
}
For some reason, dayDate is simply being set to date.getTime(), and the mathematical operators are doing nothing here.
How would I go about fixing this?
I'll recommend that you use Joda Time library. It has most of the functions related to date, time and calendar that you'll ever need.
Like Ignacio already pointed out, date.getTime() returns the number of milliseconds since January 1st, 1970, so your line should have been:
long dayDate = date.getTime() - (date.getTime() % 86400000L);
Iif you are planning on creating a new Date with dayDate, make sure that it has the right timezone, e.g. it should be in the UTC/GMT timezone. Otherwise, strange things like this could happen:
Date epoch = new Date(0);
System.err.println(epoch);
which gives on my machine Thu Jan 01 01:00:00 CET 1970 because my dates are by default created in the CET (+1) timezone. So if you would use your code and you would create a new Date instance by using the long you calculated, you would end up with a date not at 0:00 on that day, but at 1:00.
However, without immediately resorting to Joda Time (which may not be an option in your project), you can use a Calendar to get the number of seconds:
// Create the calendar and set the date.
Calendar calendar = new GregorianCalendar();
calendar.setTime(date);
// Set the hours, minutes, etc. to 0.
calendar.set(Calendar.HOUR, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
calendar.set(Calendar.AM_PM, Calendar.AM);
long dayDate = calendar.getTime();