How to get 5 years before now - java

I am new to java 8 and I am trying to get five years before now, here is my code:
Instant fiveYearsBefore = Instant.now().plus(-5,
ChronoUnit.YEARS);
But I get the following error:
java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Years
Can anyone help me how to do that?

ZonedDateTime.now().minusYears(5).toInstant()
That will use your default time zone to compute the time. If you want another one, specify it in now(). For example:
ZonedDateTime.now(ZoneOffset.UTC).minusYears(5).toInstant()

According to the Javadoc, Instant will only accept temporal units from nanos to days Instant.plus(long amountToAdd, TemporalUnit unit);
You can use LocalDateTime. You use it the same way, but it will support operation on the YEARS level.

Instant does not support addition or subtraction of YEARS.
You can use this LocalDate if you only need date without time:
LocalDate date = LocalDate.now();
date = date.plus(-5, ChronoUnit.YEARS);
Otherwise you can user LocalDateTime.

I bumped into the same exception, but with ChronoUnit.MONTHS. It is a little bit misleading, because on compile time does not throw an error or warning or something.
Anyway, I read the documentation, too:
and, yes, all the other ChronoUnit types are not supported unfortunately.
Happily, LocalDateTime can substract months and years, too.
LocalDateTime.now().minusYears(yearsBack)
LocalDateTime.now().minusMonths(monthsBack);

If you are so inclined is does not require the date time conversion.
Instant.now().minus(Period.ofYears(5).getDays(),ChronoUnit.DAYS);

Related

Why was getMonth deprecated on java.sql.Date and java.util.Date

I should preface this with I use Apache Spark, which uses java.sql.Date, in case anyone suggests I should use dates from java.time. The example below is in Scala.
The API that I use (which is deprecated) to get the month for a date is as follows:
val date: java.sql.Date = ???
val month = date.getMonth()
However if I look at how it appears I should do this based on the deprecation, the above code would be re-written as follows:
val date: java.sql.Date = ???
val cal = Calendar.getInstance()
cal.setTime(date)
cal.get(Calendar.MONTH)
The simplicity and readability of the code is significantly different, and the date being a side effect on the calendar is not terribly nice from a functional programming point of view. Can someone explain why they think this change was made?
Prior to JDK 1.1, the class Date had two additional functions. It
allowed the interpretation of dates as year, month, day, hour, minute,
and second values. It also allowed the formatting and parsing of date
strings. Unfortunately, the API for these functions was not amenable
to internationalization. As of JDK 1.1, the Calendar class should be
used to convert between dates and time fields and the DateFormat class
should be used to format and parse date strings. The corresponding
methods in Date are deprecated.
The JavaDoc explains. Internationalization.
"in case anyone suggests I should use dates from java.time"
There is nothing to stop you from converting to java.time classes as soon as possible, performing whatever calculations/modifications you need and, if you need to re-insert, converting back to java.sql.Date again.
val date: java.sql.Date = ???
val month = date.toLocalDate().getMonthValue()
You said it yourself, and I still think: You should use java.time, the modern Java date and time API. When you get an old-fashioned java.sql.Date from a legacy API not yet upgraded to java.time, convert it to a modern LocalDate and enjoy the natural code writing with java.time.
Why were getMonth() and the other getXxx methods deprecated?
While Michael has already answered the question with respect to java.util.Date, I have something to add when it comes to java.sql.Date. For this class the situation is quite a bit worse than what Michael reported.
What is left undeprecated (apprecated?) of java.util.Date after the deprecations is that a Date is a point in time. java.sql.Date on the other hand was never meant to be a point in time. One way to illustrate this fact is that its toInstant method — which should convert it to an Instant, a point in time — unconditionally throws an UnsupportedOperationException. A java.sql.Date was meant to be a calendar date to be used with an SQL database and its date datatype, which in most cases is also a date, defined by year, month and day of month. Since a Date is no longer year, month and day of month, they have virtually deprecated everything that a java.sql.Date was supposed to be. And they didn’t give us a replacement until with JDBC 4.2 we can exchange LocalDate objects with SQL databases.
The observations that lead to deprecation have got very practical consequences. Let’s try this (in Java because it is what I can write):
void foo(java.sql.Date sqlDate) {
System.out.println(sqlDate);
TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("Pacific/Samoa")));
System.out.println(sqlDate.getMonth());
}
In one call the method printed:
2020-11-02
9
So we had the 2nd day of the 11th month, and month prints as 9? There are two things going on:
Confusingly the month number that getMonth() returns is 0-based, so 9 means October.
The Date is internally represented as a count of milliseconds since the epoch to the start of the day in the default time zone of the JVM. 2020-11-02 00:00:00 in my original time zone (set to Pacific/Kiritimati for this demonstration) is the same point in time as 2020-10-31 23:00:00 in Samoa. Therefore we get October.
You don’t have to change the time zone yourself for this to happen. Situations where it can happen include:
The default time zone of the JVM can be changed from any part of your program and from any other program running in the same JVM.
The date may be serialized in a program running in one JVM and deserialized in a different JVM with a different time zone setting.
BTW the first snippet I presented at the top often won’t help against unexpected results in these situations. If things go off track before you convert from java.sql.Date to LocalDate, the conversion too will give you the wrong date. If you can make it, convert to LocalDate before anyone messes with the JVM time zone setting and be on the safe side.

Using instances of GregorianCalendar to determine time passed?

I am working on a project in my CIS 163 class that is effectively a campsite reservation system. The bulk of the code was provided and I just have to add certain functionalities to it. Currently I need to be able to determine how much time has passed between 2 different GregorianCalendar instances (one being the current date, the other being a predetermined "check out") represented by days. I haven't been able to figure out quite how to do this, and was hoping someone here might be able to help me out.
The GregorianCalendar is old and you shouldn't really use it anymore. It was cumbersome and was replaced by the "new" java.time module since Java 8.
Still, if you need to compare using GC instances, you could easily calculate time using milliseconds between dates, like this:
GregorianCalendar date1 = new GregorianCalendar();
GregorianCalendar date2 = new GregorianCalendar();
// Adding 15 days after the first date
date2.add(GregorianCalendar.DAY_OF_MONTH, 15);
long duration = (date2.getTimeInMillis() - date1.getTimeInMillis() )
/ ( 1000 * 60 * 60 * 24) ;
System.out.println(duration);
If you want to use the new Time API, the following code would work.
LocalDate date1 = LocalDate.now();
LocalDate date2 = date1.plusDays(15);
Period period = Period.between(date1, date2);
int diff = period.getDays();
System.out.println(diff);
If you need to convert between the types (e.g. you're working with legacy code), you can do it like this:
LocalDate date3 = gcDate1.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate date4 = gcDate2.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
Also I'm pretty sure this question must've been asked over and over again, so make sure you search properly before asking.
Since you have been forced to use the old and poorly designed GregorianCalendar class, the first thing you should do is convert each of the two GregorianCalendar objects to a modern type. Since Java 8 GregorianCalendar has a method that converts it to ZonedDateTime. Check the documentation, I include a link below.
Now that you’ve got two ZonedDateTime objects, there are different paths depending on your exact requirements. Often one will use Duration.between() for finding the duration, the amount of time between them in hours, minutes, seconds and fraction of second. If you know that you will always need just one of those time units, you may instead use for example ChronoUnit.HOURS.between() or ChronoUnit.MILLISECONDS.between(). If you need to count days, use ChronoUnit.DAYS.between().
If instead you need the time in months and days, you should instead use Period.between().
Links
Documentation:
GregorianCalendar (long outdated, don’t use unless forced to)
Duration
ChronoUnit
Period
Oracle tutorial: Date Time explaining how to use java.time, the modern Java date and time API to which ZonedDateTIme, Duration, ChronoUnit and Period belong.

get Date without time in java [duplicate]

This question already has answers here:
LocalDate to java.util.Date and vice versa simplest conversion? [duplicate]
(7 answers)
How do I get a Date without time in Java?
(23 answers)
Closed 3 years ago.
I want to get a Date without time, but always failed.
below is my codes:
long curLong = System.currentTimeMillis();
curLong = curLong - curLong % TimeUnit.DAYS.toMillis(1);
Date date = new Date(curLong);
System.out.println("date = " + date);
the output:
date = Mon Oct 28 08:00:00 CST 2019
anyone knows why? Thank you
It is not recommended to use java.util.Date anymore. It was called Date but doesn't necessarily hold only the date information but information about the time additionally.
Use this:
LocalDate today = LocalDate.now();
and print it as
System.out.println(today.format(DateTimeFormatter.ISO_DATE);
using the ISO date format. You can define your own formatting pattern using a
DateTimeFormatter.ofPattern("dd.MM.yyyy");
for example.
You can use java.time.LocalDate.now() to get just the date.
Anyway, your case doesn't work as you expect because you are doing nothing to remove the time from the date: you are just "repressing" it, that's why it's zero. If you want to continue this way you could always substring it (substring the Date.toString() of course I meant).
Hope I helped.
java.util.Date's javadoc states:
The class Date represents a specific instant in time, with millisecond precision.
Thats why you have date with time
If you want a date you can use : java.time.LocalDate.now() (Java 8+)
First of all, stop using the old java.util.Date. The new Java 8 date and time API has much better classes for all date and time operations.
The LocalDate class does exactly what you want.
The current date can be obtained by LocalDate.now().
It also has a lot of facilities to add and subtract days, months etc. and it takes into consideration all the calendar special cases for you.

Java Years between 2 Instants

In Joda, we can calculate years between 2 Datetime using
Years.between(dateTime1, dateTime2);
Is there any easy way to find years between 2 instants using the java.time API instead without much logic?
ChronoUnit.YEARS.between(instant1, instant2)
fails:
Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Years
at java.time.Instant.until(Instant.java:1157)
at java.time.temporal.ChronoUnit.between(ChronoUnit.java:272)
...
The number of years between two instants is considered undefined (apparently - I was surprised by this), but you can easily convert instants into ZonedDateTime and get a useful result:
Instant now = Instant.now();
Instant ago = Instant.ofEpochSecond(1234567890L);
System.out.println(ChronoUnit.YEARS.between(
ago.atZone(ZoneId.systemDefault()),
now.atZone(ZoneId.systemDefault())));
Prints:
8
I suspect the reason you can't directly compare instants is because the location of the year boundary depends on the time zone. That means that ZoneId.systemDefault() may not be what you want! ZoneOffset.UTC would be a reasonable choice, otherwise if there's a time zone that's more meaningful in your context (e.g. the time zone of the user who will see the result) you'd want to use that.

Converting duration to years in Java8 Date API?

I have a date in the far past.
I found out what the duration is between this date and now.
Now I would like to know - how much is this in years?
I came up withthis solution using Java8 API.
This is a monstrous solution, since I have to convert the duration to Days manually first, because there will be an UnsupportedTemporalTypeException otherwise - LocalDate.plus(SECONDS) is not supported for whatever reason.
Even if the compiler allows this call.
Is there a less verbous possibility to convert Duration to years?
LocalDate dateOne = LocalDate.of(1415, Month.JULY, 6);
Duration durationSinceGuss1 = Duration.between(LocalDateTime.of(dateOne, LocalTime.MIDNIGHT),LocalDateTime.now());
long yearsSinceGuss = ChronoUnit.YEARS.between(LocalDate.now(),
LocalDate.now().plus(
TimeUnit.SECONDS.toDays(
durationSinceGuss1.getSeconds()),
ChronoUnit.DAYS) );
/*
* ERROR -
* LocalDate.now().plus(durationSinceGuss1) causes an Exception.
* Seconds are not Supported for LocalDate.plus()!!!
* WHY OR WHY CAN'T JAVA DO WHAT COMPILER ALLOWS ME TO DO?
*/
//long yearsSinceGuss = ChronoUnit.YEARS.between(LocalDate.now(), LocalDate.now().plus(durationSinceGuss) );
/*
* ERROR -
* Still an exception!
* Even on explicitly converting duration to seconds.
* Everything like above. Seconds are just not allowed. Have to convert them manually first e.g. to Days?!
* WHY OR WHY CAN'T YOU CONVERT SECONDS TO DAYS OR SOMETHING AUTOMATICALLY, JAVA?
*/
//long yearsSinceGuss = ChronoUnit.YEARS.between(LocalDate.now(), LocalDate.now().plus(durationSinceGuss.getSeconds(), ChronoUnit.SECONDS) );
Have you tried using LocalDateTime or DateTime instead of LocalDate? By design, the latter does not support hours/minutes/seconds/etc, hence the UnsupportedTemporalTypeException when you try to add seconds to it.
For example, this works:
LocalDateTime dateOne = LocalDateTime.of(1415, Month.JULY, 6, 0, 0);
Duration durationSinceGuss1 = Duration.between(dateOne, LocalDateTime.now());
long yearsSinceGuss = ChronoUnit.YEARS.between(LocalDateTime.now(), LocalDateTime.now().plus(durationSinceGuss1) );
System.out.println(yearsSinceGuss); // prints 600
Although the accepted answer of #Matt Ball tries to be clever in usage of the Java-8-API, I would throw in following objection:
Your requirement is not exact because there is no way to exactly convert seconds to years.
Reasons are:
Most important: Months have different lengths in days (from 28 to 31).
Years have sometimes leap days (29th of February) which have impact on calculating year deltas, too.
Gregorian cut-over: You start with a year in 1415 which is far before first gregorian calendar reform which cancelled full ten days, in England even 11 days and in Russia more. And years in old Julian calendar have different leap year rules.
Historic dates are not defined down to second precision. Can you for example describe the instant/moment of the battle of Hastings? We don't even know the exact hour, just the day. Assuming midnight at start of day is already a rough and probably wrong assumption.
Timezone effects which have impact on the length of day (23h, 24h, 25h or even different other lengths).
Leap seconds (exotic)
And maybe the most important objection to your code:
I cannot imagine that the supplier of the date with year 1415 has got the intention to interprete such a date as gregorian date.
I understand the wish for conversion from seconds to years but it can only be an approximation whatever you choose as solution. So if you have years like 1415 I would just suggest following very simple approximation:
Duration d = ...;
int approximateYears = (int) (d.toDays() / 365.2425);
For me, it is sufficient in historic context as long as we really want to use a second-based duration for such an use-case. It seems you cannot change the input you get from external sources (otherwise it would be a good idea to contact the duration supplier and ask if the count of days can be supplied instead). Anyway, you have to ask yourself what kind of year definition you want to apply.
Side notes:
Your complaint "WHY OR WHY CAN'T JAVA DO WHAT COMPILER ALLOWS ME TO DO?" does not match the character of new java.time-API.
You expect the API to be type-safe, but java.time (JSR-310) is not designed as type-safe and heavily relies on runtime-exceptions. The compiler will not help you with this API. Instead you have to consult the documentation in case of doubt if any given time unit is applicable on any given temporal type. You can find such an answer in the documentation of any concrete implementation of Temporal.isSupported(TemporalUnit). Anyway, the wish for compile-safety is understandable (and I have myself done my best to implement my own time library Time4J as type-safe) but the design of JSR-310 is already set in stone.
There is also a subtile pitfall if you apply a java.time.Duration on either LocalDateTime or Instant because the results are not exactly comparable (seconds of first type are defined on local timeline while seconds of Instant are defined on global timeline). So even if there is no runtime exception like in the accepted answer of #Matt Ball, we have to carefully consider if the result of such a calculation is reasonable and trustworthy.
Use Period to get the number of years between two LocalDate objects:
LocalDate before = LocalDate.of(1415, Month.JULY, 6);
LocalDate now = LocalDate.now();
Period period = Period.between(before, now);
int yearsPassed = period.getYears();
System.out.println(yearsPassed);

Categories

Resources