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

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));

Related

What is a best way to find the number of days and months between two java.time.Instant objects?

NOTE: search Google before marking this question as duplicate. I did search and browse this question and all answers that I found were either for LocalDate, Joda or legacy Java Date.
It took me quite some time to investigate this so I've decided to share this as an answer.
I'd like a way to calculate the (approximate) number of months and days between two Java Instants (objects of java.time.Instant)?
First, what you are asking is not well-defined. For example between the instants 2020-03-01T06:00:00Z and 2020-03-31T05:00:00Z could be:
29 days 23 hours in Australia/Melbourne time zone;
30 days in Europe/Paris time zone;
1 month 1 day in America/Los_Angeles time zone.
Accurate result in a given time zone
ZoneId zone = ZoneId.of("America/Los_Angeles");
Instant start = Instant.parse("2020-03-01T06:00:00Z");
Instant end = Instant.parse("2020-03-31T05:00:00Z");
ZonedDateTime startZdt = start.atZone(zone);
LocalDate startDate = startZdt.toLocalDate();
ZonedDateTime endZdt = end.atZone(zone);
LocalDate endDate = endZdt.toLocalDate();
Period p = Period.between(startDate, endDate);
if (startZdt.plus(p).isAfter(endZdt)) {
// The time of day on the end date is earlier, so don’t count a full date
endDate = endDate.minusDays(1);
p = Period.between(startDate, endDate);
}
System.out.println(p);
Output:
P1M1D
Read as a period of 1 month 1 day.
Approximate result independent of time zone
Prefer to leave as much of the calculation to java.time as possible. This includes the estimate of the length of a month.
Duration diff = Duration.between(start, end);
Duration durationOfAMonth = ChronoUnit.MONTHS.getDuration();
long months = diff.dividedBy(durationOfAMonth);
diff = diff.minus(durationOfAMonth.multipliedBy(months));
long days = diff.toDays();
System.out.println("" + months + " months " + days + " days");
0 months 29 days
I've opted out to approximate solution (it assumes all months have 30.44 days). I've opted out to use something like this:
Duration duration = Duration.between(instant1, instant2).abs(); /* if want negative values remove .abs() */
long hours = duration.toHours();
double daysAndMonthsInDays = hours / 24.0;
int months = daysAndMonthsInDays / 30.44; //average number of days per month
int days = daysAndMonthsInDays - months * 30.44;
Please post another answer if there is a better solution using Duration class or something else. I've decided not to convert Instant to LocalDate and to perform the conversion on that level. That would not use an approximation of 30.44 days in a month, but rather the actual number.

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

Compare Timestamp with another Timestamp object

I'm newer to Java. I'm using two Timestamp objects dateFrom and dateTo. I want to check whether the dateFrom is 45 days earlier than dateTo. I used this code fragment to compare this
if(dateFrom.compareTo(dateTo) < 45)
{
// do the action;
}
I'm confusing with the 45 given in the code. Can I expect the correct result. will it meets my result.
compareTo() returns a value of -1, 0 or 1, depending on the result.
What you want to do is
long result = dateTo.getTime() - (1000 * 60 * 60 * 24 * 45) - dateFrom.getTime();
if(result >= 0) {
System.out.println("dateFrom is 45 days or more before dateTo");
else {
System.out.println("dateFrom is less than 45 days before dateTo");
}
This is rather ugly though. Is there a specific reason you're not using a Calendar?
You have to think about it a bit logically. First of all you need to get to a timestamp which is 45 days before the dateTo date. Time has various units (seconds, minutes, hours, days) so just checking < 45 is meaningless in this case. The compareTo() method is just there for ordering to know if a timestamp is before or after the other.
You could first create a Calendar for the timestamps, and add() dateFrom by 45 days. Then you can use the before() method to check if dateFrom is before dateTo.
Use Joda Time or Calendar class(add 45 days to dateFrom, compare the result with dateTo).
Do like this
Date dateFrom ="your from date";
Calendar cal = GregorianCalendar.getInstance();
cal .setTime(dateFrom);
cal.add(Calendar.DAY_OF_MONTH, 45);
Date expireDate = cal.getTime();
Date dateTo = new Date();
if(dateTo.after(expireDate)){
}
Since Timestamp in Java is number of milliseconds from UNIX Epoch - change 45 days to number of milliseconds (45d=45*24h=45*24*3600s=45*24*3600*1000ms) so if:
(time_B + 45*24*3600*1000) >= time_A
it means that time_B is 45 days (or more) 'further' in time that time_A
Of course you can use JodaTime and other libs too.

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

Java Convert unixtime to date , giving wrong results

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.

Categories