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.
Related
I have two epoch timestamps, I am trying to find the number of days between the two timestamps.
This is what I have now:
dateFrom = inputEntry.getValue(inputFields(0).get).asInstanceOf[String].toLong
dateTo =inputEntry.getValue(inputFields(1).get).asInstanceOf[String].toLong
Example:
dateFrom dateTo result
1501583232 1501641000 1
1501583232 1501986600 5
I am starting with two epoch dates here
tl;dr
ChronoUnit.DAYS.between( … , … )
Details
This has been covered many times already on Stack Overflow. So briefly here…
For date-time values, use date-time objects. Use java.time classes only, avoid the troublesome legacy date-time classes ( Date, Calendar, etc).
Do you mean a difference of dates or a difference of 24-hour chunks of time?
I'll go with dates here.
First, translate what appears to be number of whole seconds since an epoch reference date of 1970-01-01T00:00:00Z into a point in the timeline in UTC.
Note the L on the end of numeric literal to indicate a long rather than int.
Instant instant = Instant.ofEpochSecond( 1_501_583_232L ) ;
Assign time zone for which you want to consider dates.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z );
Convert to a date-only.
LocalDate ld = zdt.toLocalDate() ;
Get difference.
long days = ChronoUnit.DAYS.between( ld , ld2 ) ;
To get the results you want, you must define how you're going to calculate the difference.
Taking your first example (difference between 1501583232 and 1501641000 should be 1 day):
The epochs 1501583232 and 1501641000 are the number of seconds since 1970-01-01T00:00Z, so they are equivalent to the following UTC dates:
1501583232: 2017-08-01T10:27:12Z
1501641000: 2017-08-02T02:30:00Z
Note that the difference between them is 16 hours, 2 minutes and 48 seconds (so, less than a day). If you get the difference in days, technically it will be zero.
But if you consider only the dates (2017-08-01 and 2017-08-02) and ignore the time (hour/minute/second), then the difference can be either zero or 1, depending on the timezone you are.
If you consider only the UTC dates (2017-08-01 and 2017-08-02), the difference is 1 day.
But if you take the same UTC dates above in America/Los_Angeles timezone, you'll get:
1501583232: 2017-08-01T03:27:12-07:00
1501641000: 2017-08-01T19:30-07:00
Now the difference is zero days, no matter if you consider only the date (both are 2017-08-01), or the date and time (the difference in hours will be 16, less than a day).
So, you must define how you're going to calculate the difference (consider only the date, or both date and time, and what timezone will be used).
In your case, it seems that you're considering only the date and ignoring the time, but it's not clear what timezone it's using. Anyway, you can use JDK's 8 new java.time API for that (for JDK <= 7 you can use the ThreeTen Backport - The code below works for both. The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same).
The code is basically the same of #BasilBourque's answer, because it's very straighforward with the new API (I just wanted to add the insights above).
First you create the Instant's from the epoch values:
Instant instant1 = Instant.ofEpochSecond(1501583232L);
Instant instant2 = Instant.ofEpochSecond(1501641000L);
If you want the difference considering the date and time, you can use:
ChronoUnit.DAYS.between(instant1, instant2);
The result will be zero.
If you want to consider only the dates in UTC (and ignore the time), just do:
// convert to UTC and get just the date (day/month/year)
LocalDate d1 = instant1.atZone(ZoneOffset.UTC).toLocalDate();
LocalDate d2 = instant2.atZone(ZoneOffset.UTC).toLocalDate();
long days = ChronoUnit.DAYS.between(d1, d2);
The result will be 1.
To convert to a different timezone (instead of UTC), use the ZoneId class:
// use a specific timezone
ZoneId zone = ZoneId.of("Asia/Kolkata");
// convert the Instant to a timezone and get only the date
LocalDate d1 = instant1.atZone(zone).toLocalDate();
LocalDate d2 = instant2.atZone(zone).toLocalDate();
long days = ChronoUnit.DAYS.between(d1, d2);
In this case, the difference is 1, but as I said above, different timezones can produce different results (can be either zero or 1 - for example, changing the code above to ZoneId.of("America/Los_Angeles"), the result is zero).
Note that the API uses IANA timezones names (always in the format Region/City, like Asia/Kolkata or Europe/Berlin).
Avoid using the 3-letter abbreviations (like CST or IST) because they are ambiguous and not standard.
You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds().
You can also use the system's default timezone with ZoneId.systemDefault(), but this can be changed without notice, even at runtime, so it's better to explicity use a specific one.
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);
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);
This question already has answers here:
How can I convert the result of System.nanoTime to a date in Java?
(3 answers)
Closed 7 years ago.
I am writing a code for implementing Stop Watch. I capture a moment with System.nanoTime(). But I would also like to convert and store that moment into a date field. When I try to use new Date(long msec), it's giving me some absurd date-time value. Can anyone help me how to get this done?
System.nanoTime is not the current time:
This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time.
This is why you're experiencing "some absurd date-time value".
Use System.currentTimeMillis if you want the date(s) you've captured as milliseconds (see: unix time):
the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
I have 2 Date variables, Date1 and Date2.
I want to check if Date 1 fall on the same date as Date2 (but they are allowed to have different times).
How do i do this?
It looks like a really easy thing to do, but i'm struggling.
EDIT: I want to avoid external libraries and stuff
EDIT:
My orgional idea was to remove the hour, min, sec but those features are marked as depreciated in Java. So what should I use????
Although given answers based on date component parts of a java.util.Date are sufficient in many parts, I would stress the point that a java.util.Date is NOT a date but a kind of UNIX-timestamp measured in milliseconds. What is the consequence of that?
Date-only comparisons of Date-timestamps will depend on the time zone of the context. For example in UTC time zone the date-only comparison is straight-forward and will finally just compare year, month and day component, see other answers (I don't need to repeat).
But consider for example the case of Western Samoa crossing the international dateline in 2011. You can have valid timestamps of type java.util.Date, but if you consider their date parts in Samoa you can even get an invalid date (2011-12-30 never existed in Samoa locally) so a comparison of just the date part can fail. Furthermore, depending on the time zone the date component can generally differ from local date in UTC zone by one day, ahead or behind, in worst case there are even two days difference.
So following extension of solution is slightly more precise:
SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd");
fmt.setTimeZone(...); // your time zone
return fmt.format(date1).equals(fmt.format(date2));
Similar extension also exists for the more programmatic approach to first convert the j.u.Date-timestamp into a java.util.GregorianCalendar, then setting the time zone and then compare the date components.
Why don't you simply compare the year, month and day? You can write your method for doing it something like:
private boolean isDateSame(Calendar c1, Calendar c2) {
return (c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR) &&
c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH) &&
c1.get(Calendar.DAY_OF_MONTH) == c2.get(Calendar.DAY_OF_MONTH));
}
Today = Span Of Time
While the other answers may be correct, I prefer the approach where we recognize that "today" is actually a span of time.
Because of anomalies such as Daylight Saving Time (DST), days vary in length, not always 24 hours long. Here in the United States, some days are 23 hours long, some 25.
Half-Open
Commonly in data-time work, we use the "Half-Open" strategy where the beginning of a span is inclusive and the ending is exclusive. So that means "today" spans from the first moment of today up to, but not including, the first moment of tomorrow.
Time Zones
Time zones are critical, as explained in the correct answer by Meno Hochschild. The first moment of a day depends on its time zone rules.
Joda-Time
The Joda-Time library has nice classes for handling spans of time: Period, Duration, and Interval.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
DateTime now = new DateTime( timeZone );
Interval today = new Interval( now.withTimeAtStartOfDay(), now.plusDays(1).withTimeAtStartOfDay() );
DateTime dateTimeInQuestion = new DateTime( date ); // Convert java.util.Date.
boolean happensToday = today.contains( dateTimeInQuestion );
Benefits
This approach using a span of time has multiple benefits:
Avoids Daylight Saving Time (DST) issues
Lets you compare date-time values from other time zones
Flexible, so you can use the same kind of code for other spans (multiple days, months, etc.)
Gets your mind shifted away from calendar dates (a layered abstraction) and onto date-times as points on a flowing timeline (the underlying truth).
Java 8 has a new java.time package built-in. These new classes are modeled after Joda-Time but are entirely re-architected. This same kind of code can be written using java.time.
When you use the toString() method what do you get? Is it only the year/month/day or time too? If it is then you could simply compare the strings of the two objects. (date1.toString().equals(date2.toString()));