Java Convert unixtime to date , giving wrong results - java

Im trying to convert unixtime to date but the results im getting are wrong :
for example i have this unixtime : 1354312800 accurding to this site :
enter link description here
the result is :
Fri, 30 Nov 2012 22:00:00 GMT
but when i do :
long timestamp = 1354312800;
java.util.Date time=new java.util.Date((long)timestamp*1000);
int d = time.getDay();
int m = time.getMonth();
im getting :
d= 6 << this is wrong should be 30.
and m - 11

You have mistaken getDay()for getDate().
getDay Javadoc:
Returns the day of the week represented by this date. The returned value (0 = Sunday, 1 = Monday, 2 = Tuesday, 3 = Wednesday, 4 = Thursday, 5 = Friday, 6 = Saturday) represents the day of the week that contains or begins with the instant in time represented by this Date object, as interpreted in the local time zone.
So just use getDate() instead of getDay()
For more info, check the javadoc: http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Date.html
If your time still differs from the Unix time you tried, you are probably living not in the GMT Timezone, so you need to find out the date for the corresponding timezone:
SimpleDateFormat df = new SimpleDateFormat();
df.setTimeZone(TimeZone.getTimeZone("GMT"));
System.out.println(df.format(time));
This should give you the expected output, even though your local tiemzone differs from GMT

The method Date.getDay() gives the day of the week (0 = Sunday, ..., 6 = Saturday).
Change it to Date.getDate() and you will get 30 as the result.
Some side-notes:
The Date class is pretty much deprecated. Use Calendar instead, or even better, the Joda time library.
Your conversion is sort of funny. (long)timestamp*1000 converts timestamp to a long value (which is already a long value) and then automatically widens 1000 to a long value to carry out the multiplication.
I would skip the conversion, (long) , altogether, and if you want to explicitly say that the factors are long values, use 1000L instead, which is a long-literal.

Related

How to handle dates when the input dates are in the transition period from PDT to PST?

I am calling an API which takes two dates as input.The API checks if the difference between the two date is greater than 60 min, then it throws an exception.My input dates are startDate=11-06-2016T00:57:01 and endDate=11-06-2016T01:56:01.These two dates are saved in java.util.Date object.
Now the issue is though the two dates have a difference of 59 min which is less than 60 min, still the API throws exception.Looks like this isssue is due to DayLightSaving.On Nov 6,once 2 am is reached , DayLightSaving ends (PDT time zone ends), time is moved backward by 1 hr due to which time again become 1 am but in PST time zone now.This means on Nov 6 , there would be 1-2 am twice one in PDT and one in PST zone.
When this API is called on NOV 7, the time zone would be PST.So when the two dates are passed without the timezone specified, it takes the startDate in PDT zone and enddate in PST zone.Since PDT and PST itself have a difference of 1 hour, this would get added to the 59 min differnce and exception is being thrown.
How to handle this case when the input dates are in the transition period from PDT to PST?
sample code
SimpleDateFormat formatter1 = new SimpleDateFormat("MM-dd-yyyy hh:mm:ss");
String start="11-06-2016 00:57:01";
String end ="11-06-2016 01:56:01";
Date startdate = formatter1.parse(start);
Date enddate = formatter1.parse(end);
System.out.println("startDate is :" + startdate);
System.out.println("endDate is :" +enddate);
long dateRange = enddate.getTime() - startdate.getTime();
//if the difference between the two dates is > than 60 min i.e 3600000 ms, then throw exception.
System.out.println(dateRange);
if (dateRange > (60 * 60 * 1000)){
throw new Exception("Date time range cannot be greater than 60 minutes.(calculated using millisecond difference)");
}
Output
[Date Range is = 7140000
Exception in thread "main" java.lang.Exception: Date time range cannot be greater than 60 minutes.(calculated using millisecond difference).
at Datetest.main(Datetest.java:28)][1]
The above snippet throws exception when called in PST time zone.
Neither SimpleDateFormat nor the underlying Calendar specifies what happens when parsing a datetime string without timezone for a time in the overlapping hour between daylight savings time and standard time.
You have observed that it will return the later time, i.e. it seems to prefer standard over daylight savings time. But, the behavior is undefined, so...
The new java.time classes do however specify exactly what happens, and how to choose the other "hour" of the overlap.
In the new API, since your datetime string is without timezone, you'd likely first parse using LocalDateTime, then apply time zone to get a ZonedDateTime, e.g.
LocalDateTime ldtEnd = LocalDateTime.parse("2016-11-06T01:56:01");
ZonedDateTime zdtEnd = ldtEnd.atZone(ZoneId.of("America/Los_Angeles"));
// zdtEnd is now: 2016-11-06T01:56:01-07:00[America/Los_Angeles]
To see the overlap, you can try adding an hour:
ZonedDateTime zdtEnd2 = zdtEnd.plusHours(1);
// zdtEnd2 is now: 2016-11-06T01:56:01-08:00[America/Los_Angeles]
The behavior is well-defined, see javadoc of atZone():
In most cases, there is only one valid offset for a local date-time. In the case of an overlap, where clocks are set back, there are two valid offsets. This method uses the earlier offset typically corresponding to "summer".
In the case of a gap, where clocks jump forward, there is no valid offset. Instead, the local date-time is adjusted to be later by the length of the gap. For a typical one hour daylight savings change, the local date-time will be moved one hour later into the offset typically corresponding to "summer".
To obtain the later offset during an overlap, call ZonedDateTime.withLaterOffsetAtOverlap() on the result of this method. To throw an exception when there is a gap or overlap, use ZonedDateTime.ofStrict(LocalDateTime, ZoneOffset, ZoneId).
As you can see, it will always return the earlier time in an overlap, which is opposite of the observed behavior of SimpleDateFormat. If you want the later time in an overlap, call withLaterOffsetAtOverlap().
If you don't want to rely on documented default, you can always be explicit:
ZoneId PT = ZoneId.of("America/Los_Angeles");
LocalDateTime ldtStart = LocalDateTime.parse("2016-11-06T00:57:01");
ZonedDateTime zdtStartEarly = ldtStart.atZone(PT).withEarlierOffsetAtOverlap();
ZonedDateTime zdtStartLater = ldtStart.atZone(PT).withLaterOffsetAtOverlap();
System.out.println(zdtStartEarly); // 2016-11-06T00:57:01-07:00[America/Los_Angeles]
System.out.println(zdtStartLater); // 2016-11-06T00:57:01-07:00[America/Los_Angeles]
LocalDateTime ldtEnd = LocalDateTime.parse("2016-11-06T01:56:01");
ZonedDateTime zdtEndEarly = ldtEnd.atZone(PT).withEarlierOffsetAtOverlap();
ZonedDateTime zdtEndLater = ldtEnd.atZone(PT).withLaterOffsetAtOverlap();
System.out.println(zdtEndEarly); // 2016-11-06T01:56:01-07:00[America/Los_Angeles]
System.out.println(zdtEndLater); // 2016-11-06T01:56:01-08:00[America/Los_Angeles]
As you can see, for the 00:57 time, it makes no difference, because that time is not in the overlap hour.
What you can do here get the difference between the 2 dates using timezone offset. something like below
private int getDSTdifferenceDateAdjustment(Date startDate, Date endDate, TimeZone timeZone)
{
if (startDate == null || endDate == null) return 0;
int baseOffset = timeZone.getOffset(startDate.getTime());
int newOffSet = timeZone.getOffset(endDate.getTime());
return (newOffSet - baseOffset);
}
Have something like this in your method
int dstDifference = getDSTdifferenceDateAdjustment(startdate, enddate, TimeZone.getDefault());
// The dstDifference will get in the negative, so we are adding to the dateRange variable
dateRange += dstDifference;
Try this one and even check when the DST starts next year. Mostly this will work in all these cases

Is the TimeUnit class broken?

I noticed a strange behaviour of the TimeUnit class, so I created this minimal example to reproduce it.
long differenceInDays;
Calendar c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();
c1.setTimeInMillis(1466062306000l); // Thu Jun 16 2016 09:31:46 GMT+0200
c2.setTimeInMillis(1466028000000l); // Thu Jun 16 2016 00:00:00 GMT+0200
differenceInDays = TimeUnit.DAYS.convert(c2.getTimeInMillis() - c1.getTimeInMillis(), TimeUnit.MILLISECONDS);
System.out.println(differenceInDays); // obviously zero
c2.add(Calendar.DATE, +1);
differenceInDays = TimeUnit.DAYS.convert(c2.getTimeInMillis() - c1.getTimeInMillis(), TimeUnit.MILLISECONDS);
System.out.println(differenceInDays); // why zero and not one?
c2.add(Calendar.DATE, +1);
differenceInDays = TimeUnit.DAYS.convert(c2.getTimeInMillis() - c1.getTimeInMillis(), TimeUnit.MILLISECONDS);
System.out.println(differenceInDays); // suddenly a 1, but not a 2 like expected
It is obvious that the first time the difference is calculated it is 0, because not a whole day lies between the dates.
But the second time a whole day is added, so how can the difference be still 0?
Output:
001
I don't think this problem is daylight saving time or leap year related because I only do calculations within the same year, even month.
Here is a date to milliseconds calculator for you to check.
You can see better what's going on here with simple math:
c1 = 1466062306000
c2 = 1466028000000
d = 86400000 // one day
c2 - c1 = -34306000 // negative, but less than one day in magnitude
c2 - c1 + d = 52094000 // less than one day
c2 - c1 + d + d = 138494000 // more than one day, less than two days
The correct way to handle this, assuming you're using Java 8, is as follows:
// Decide what time zone you want to work in
ZoneId tz = ZoneId.of("Europe/Berlin");
// If you wanted the local time zone of the system,
// Use this instead:
// ZoneId tz = ZoneId.systemDefault();
// Get instants from the timestamps
Instant i1 = Instant.ofEpochMilli(1466062306000l);
Instant i2 = Instant.ofEpochMilli(1466028000000l);
// Get the calendar date in the specified time zone for each value
LocalDate d1 = i1.atZone(tz).toLocalDate();
LocalDate d2 = i2.atZone(tz).toLocalDate();
// Get the difference in days
long daysBetween = ChronoUnit.DAYS.between(d2, d1);
If your inputs are truly Calendar objects instead of timestamps, I'd suggest Calendar.toInstant() as described in the Legacy Date-Time Code guidance.
If you're using Java 7 or earlier, you will find similar capabilities from the Joda Time library.
if you really don't want to use any of these, and still do things the old (hard) way, then see this example.

How to remove Hour, Minute, and Second from Epoch timestamp

I need to write a function that accepts a java.util.Date and removes the hours, minutes, and milliseconds from it USING JUST MATH (no Date formatters, no Calendar objects, etc.):
private Date getJustDateFrom(Date d) {
//remove hours, minutes, and seconds, then return the date
}
The purpose of this method is to get the date from a millisecond value, without the time.
Here's what I have so far:
private Date getJustDateFrom(Date d) {
long milliseconds = d.getTime();
return new Date(milliseconds - (milliseconds%(1000*60*60)));
}
The problem is, this only removes minutes and seconds. I don't know how to remove hours.
If I do milliseconds - (milliseconds%(1000*60*60*23)), then it goes back to 23:00 hrs on the previous day.
EDIT:
Here's an alternative solution:
public static Date getJustDateFrom(Date d) {
Calendar c = Calendar.getInstance();
c.setTime(d);
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
return c.getTime();
}
Will this solution be affected by time zone differences between the client/server sides of my app?
There are 24 hours in a day. Use milliseconds%(1000*60*60*24).
Simply not possible by your definition.
A millisecond timestamp represents milliseconds elapsed from a fixed point in time (1970-01-01 00:00:00.000 UTC, if I remember correctly). This timestamp can not be converted into a date + time without specifying the timezone to convert to.
So you can only round the timestamp to full days in respect to a specific timezone, not in general. So any fiddling with Date.getTime() and not taking into account any timezone is guaranteed to work in only one time zone - the one you hardcoded for.
Do yourself a favor and use a Calendar.
You can make use of apache's commons lang DateUtils helper utility class.
For example, if you had the datetime of 28 Mar 2002
13:45:01.231, if you passed with Calendar.HOUR, it would return 28 Mar
2002 13:00:00.000. If this was passed with Calendar.MONTH, it would
return 1 Mar 2002 0:00:00.000.
Date newDate = DateUtils.truncate(new Date(1408338000000L), Calendar.DAY_OF_MONTH);
You can download commons lang jar at http://commons.apache.org/proper/commons-lang/
import java.sql.Date;
long dateInEpoch = 1_592_283_050_000L;
ZoneId defaultZoneId = ZoneId.systemDefault();
long currentDate = Date
.from(new Date(dateInEpoch)
.toLocalDate()
.atStartOfDay(defaultZoneId)
.toInstant())
.getTime();
input : 1592283050000
output: 1592245800000

Long To XMLGregorianCalendar and back to Long

I am trying to convert from millisecond time stamp to XMLGregorianCalendar and back, but I seem to be getting wrong results. Am I doing something wrong? It seems I am gaining days.
// Time stamp 01-Jan-0001 00:00:00.000
Long ts = -62135740800000L;
System.out.println(ts);
System.out.println(new Date(ts)); // Sat Jan 01 00:00:00 PST 1 .. Cool!
// to Gregorian Calendar
GregorianCalendar gc = new GregorianCalendar();
gc.setTimeInMillis(ts);
// to XML Gregorian Calendar
XMLGregorianCalendar xc = DatatypeFactory.newInstance().newXMLGregorianCalendar(gc);
// back to GC
GregorianCalendar gc2 = xc.toGregorianCalendar();
// to Timestamp
Long newTs = gc2.getTimeInMillis();
System.out.println(newTs); // -62135568000000 .. uh?
System.out.println(new Date(newTs)); // Mon Jan 03 00:00:00 PST 1 .. where did the extra days come from?
Interesting - it works fine for values down to (about) -10000000000000L (and positive values) but larger negative values become inconsistent.
If you print out gc, xc, and gc2, you can see where the problem arises (the conversion from XMLGregorianCalendar to GregorianCalendar
gc: java.util.GregorianCalendar[time=-62135740800000 ... DAY_OF_WEEK=7
xc: 0001-01-01T08:00:00.000Z
gc2: java.util.GregorianCalendar[time=? ... DAY_OF_WEEK=5
If you print out the fields of xc, you get 1,1,1.
System.out.println(xc.getYear());
System.out.println(xc.getMonth());
System.out.println(xc.getDay());
For gc2, you get 1,0,1 (which matches xc, because months are zero-based in GregorianCalendar)
System.out.println(gc2.get(gc2.YEAR));
System.out.println(gc2.get(gc2.MONTH));
System.out.println(gc2.get(gc2.DAY_OF_MONTH));
However, adding these 3 println calls changes the output from printing out gc2! - the time=? output from gc2 changes to time=-62135568000000 - so some calculation has been triggered by querying the GregorianCalendar object; the areFieldsSet property also changes from false to true.
The timezones of the two GregorianCalendars are different, but this does not account for the error, which persists even if you set explicit TimeZone and Locale.
I believe here is the problem. Per documentation, toGregorianCalendar() relies on the GregorianCalendar corresponding defaults for conversion when there is any field missing.
If you try:
Date date = new Date();
long ts = date.getTime(); //in place of your input
and run your code, you should find, both to and from conversion working fine.
If you want your toGregorianCalendar() with custom provide inputs as in your example, please use toGregorianCalendar(TimeZone,Locale,Defaults) and supply the updated defaults to be used in conversion.

How do I increment a java.sql.Timestamp by 14 days?

I have an app that takes a Timestamp as a boundary for the start date and end date of a sql selection, I want to populate a hashmap with weeks this year since the first monday of the year as the values and the week number as the keys. I'm finding it really hard to work with timestamps and I don't feel very good about adding 86,400,000 seconds to it to increment the day, as this doesn't account for the leap days, hours, seconds.
I plan on adding 13 days 23 hours, 59 minutes and 59 seconds to it so that I can lookup the start date in the map by the week as the key, then use the start date to get the end date.
So I'm looking to try to get something like this:
Week startDate endDate
1 2011-01-03 00:00:00 2011-01-16 23:59:59
2 2011-01-17 00:00:00 2011-01-30 23:59:59
With the first two columns in the Map and the last one being calculated after looking it up. How do I safely increment a java.sql.Timestamp?
java.sql.Timestamp ts = ...
Calendar cal = Calendar.getInstance();
cal.setTime(ts);
cal.add(Calendar.DAY_OF_WEEK, 14);
ts.setTime(cal.getTime().getTime()); // or
ts = new Timestamp(cal.getTime().getTime());
This will correctly cater for daylight-time transitions in your default Timezone. You can tell the Calendar class to use a different Timezone if need be.
It worth noting that 14 days is not always 14 * 24 * 3600 seconds. When you have daylight savings, this can be an hour shorter or longer. Historically it can be much more complex than that.
Instead I would suggest using JodaTime or the Calendar to perform the time zone dependant calculation.
Java 8
Timestamp old;
ZonedDateTime zonedDateTime = old.toInstant().atZone(ZoneId.of("UTC"));
Timestamp new = Timestamp.from(zonedDateTime.plus(14, ChronoUnit.DAYS).toInstant());
private Long dayToMiliseconds(int days){
Long result = Long.valueOf(days * 24 * 60 * 60 * 1000);
return result;
}
public Timestamp addDays(int days, Timestamp t1) throws Exception{
if(days < 0){
throw new Exception("Day in wrong format.");
}
Long miliseconds = dayToMiliseconds(days);
return new Timestamp(t1.getTime() + miliseconds);
}
Timestamp my14DaysAfter = Timestamp.valueOf(myTimestamp.toLocalDateTime().plusDays(14));

Categories