Duration.ofDays generates UnsupportedTemporalTypeException - java

I am trying to learn the new Date & Time API. My code is working except for the last line:
LocalDate current=LocalDate.now();
System.out.println(current);
LocalDate personaldate=LocalDate.of(2011,Month.AUGUST, 15);
System.out.println(personaldate);
LocalDate afterten=current.plus(Period.ofDays(10));
System.out.println(afterten);
// error occurs here
System.out.println(afterten.plus(Duration.ofDays(3)));
When I try and add a Duration in days, it generates an error. Can anyone help me understand why?
Error:
Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Seconds
at java.time.LocalDate.plus(LocalDate.java:1241)
at java.time.LocalDate.plus(LocalDate.java:137)
at java.time.Duration.addTo(Duration.java:1070)
at java.time.LocalDate.plus(LocalDate.java:1143)
at TestClass.main(TestClass.java:15)

Whilst the accepted answer is completely correct, when I arrived at this question, I was looking for a simple solution to my problem.
I found using Period would not allow me to count the number of days between my two LocalDate objects. (Tell me how many years, months and days between the two, yes, but not just then number of days.)
However, to get the result I was after was as simple as adding the LocalDate method "atStartOfDay" to each of my objects.
So my erronious code:
long daysUntilExpiry = Duration.between(LocalDate.now(), training.getExpiryDate()).toDays();
was simply adjusted to:
long daysUntilExpiry = Duration.between(LocalDate.now().atStartOfDay(), training.getExpiryDate().atStartOfDay()).toDays();
Doing this make the objects into LocalDateTime objects which can be used with Duration. Because both object have start of day as the "time" part, there is no difference.
Hope this helps someone else.

A Duration measures an amount of time using time-based values (seconds, nanoseconds). A Period uses date-based values (years, months, days).
here is the link
https://docs.oracle.com/javase/tutorial/datetime/iso/period.html
the same as in JodaTime

//(year,month,day)
LocalDate beginDate = LocalDate.of(1899,12,31);
LocalDate today = LocalDate.now();
ChronoUnit.DAYS.between(beginDate, today)

As hinted before, Duration is always seconds-based whereas Period honours the day as concept.
The code throws an exception when it tries to add seconds on a LocalDate - which is also day-based.
Changing your code like this shows the difference: use LocalDateTime when getting down to instants within days:
LocalDateTime current = LocalDateTime.now();
System.out.println(current);
LocalDateTime afterten = current.plus(Period.ofDays(10));
System.out.println(afterten);
// error occurred here - but with LocalDateTime is resolved!
System.out.println(afterten.plus(Duration.ofDays(3)));

Try to run following code in a Unit test and see for yourself that the accepted answer to your problem should be ChronoUnit.DAYS.between() as stated by Ravi.
LocalDate date1 = LocalDate.of(2020,6,2);
LocalDate date2 = LocalDate.of(2020,7,4);
System.out.printf("ChronoUnit.DAYS = %d%n", ChronoUnit.DAYS.between(date1, date2));
System.out.printf("Period.between = %d%n",Period.between(date1, date2).getDays());
Because the output will look as follows:
ChronoUnit.DAYS = 32
Period.between = 2
Period will incorrectly return only the days portion of the difference (ignoring higher order differences like months and years).
System.out.printf("Duration.between = %d%n",Duration.between(date1, date2).getSeconds());
This will throw an exception as LocalDate does not provide enough information for seconds calculations (undefined hour, minutes and seconds).
Therefore you would have to convert it to LocalDateTime for example by calling date1.atStartOfDay().
System.out.printf("Duration.between = %d%n",Duration.between(date1.atStartOfDay(), date2.atStartOfDay()).get(ChronoUnit.DAYS));
This call will simply throw java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Days because of how the get method on Duration class is implemented in Java:
#Override
public long get(TemporalUnit unit) {
if (unit == SECONDS) {
return seconds;
} else if (unit == NANOS) {
return nanos;
} else {
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
}

Related

java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Seconds

i like to get the duration between to datetime values in minutes.
public long datetimeDiffInMinutes(String dateStop, String dateStart) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDate firstDate = LocalDate.parse(dateStart, formatter);
LocalDate secondDate = LocalDate.parse(dateStop, formatter);
Duration d1 = Duration.between(firstDate, secondDate);
long min = d1.toMinutes();
return min;
}
There will be thrown an exception: java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Seconds
But i dont use "Seconds" in this function. This line throws the exception: Duration d1 = Duration.between(firstDate, secondDate);
The documentation for the method you're calling (Duration.between(Temporal, Temporal)) states:
The specified temporal objects must support the SECONDS unit. For full accuracy, either the NANOS unit or the NANO_OF_SECOND field should be supported.
But LocalDate.isSupported is documented with:
If the unit is a ChronoUnit then the query is implemented here. The supported units are: DAYS, WEEKS, MONTHS, YEARS, DECADES, CENTURIES, MILLENNIA, ERAS
All other ChronoUnit instances will return false.
So no, LocalDate doesn't support seconds, which is required for the method you're calling.
It may be worth considering that a Duration is intended to be an elapsed time - a fixed number of seconds etc. The elapsed time between two dates may depend on the time zone involved - because a day doesn't always have 24 hours when there are time zones involved.
If you're happy assuming a 24-hour day, you could use Duration.ofDays(DAYS.between(firstDate, secondDate)).
You specify your dates with time information. That makes LocalDate a suboptimal choice. LocalDateTime is a better option. That already lets you create a duration.
Results may be off because of DST. Adding the right time zone should solve that:
ZoneId zone = ZoneId.systemDefault(); // or an explicit one
ZonedDateTime firstDateTime = LocalDateTime.parse(dateStart, formatter).atZone(zone);
ZonedDateTime secondDateTime = LocalDateTime.parse(dateStop, formatter).atZone(zone);
Duration d1 = Duration.between(firstDateTime, secondDateTime);
long min = d1.toMinutes();
For differences between dates, Period is a better representation.
Because your format string contains time, however, it looks like you want to be parsing to LocalDateTime instead of LocalDate. This way, the minutes (and seconds) you care about are not discarded:
private static final DateTimeFormatter parser =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public long datetimeDiffInMinutes(String dateStop, String dateStart) {
LocalDateTime firstDate = parser.parse(dateStart, LocalDateTime::from);
LocalDateTime secondDate = parser.parse(dateStop, LocalDateTime::from);
return firstDate.until(secondDate, ChronoUnit.MINUTES);
}
Note that because you don't have information about the time zone and any daylight-saving transitions that may occur, the results might not match what people expect in every case. You should clarify the use case, and get more information about the zone if necessary.

Add 30 minutes to the current time

I have written the code below but if the current date-time is 2022-07-03 09:48:05.448 and I add 30 minutes, my response returns 2022-07-03 09:79:05.448.
But minutes can never be 79, it is supposed to move to the hours instead...
public static String getExpiryDate(int additionalMinutesToCurrentMinute) {
LocalDateTime now = LocalDateTime.now();
int year = now.getYear();
int month = now.getMonthValue();
int day = now.getDayOfMonth();
int hour = now.getHour();
int minute = now.getMinute() + additionalMinutesToCurrentMinute;
int second = now.getSecond();
int millis = now.get(ChronoField.MILLI_OF_SECOND); // Note: no direct getter available.
String expiryDateAndTime = String.format("%d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, minute, second, millis);
return expiryDateAndTime;
}
Explanation
The reason your code does not work as expected is because you are not involving javas Date/Time API at all in your "math".
Your adding the minutes with plain int-arithmetic
int minute = now.getMinute() + additionalMinutesToCurrentMinute;
and then you use plain string formatting
String.format("%d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, minute, second, millis);
Nothing in this chain is "clever" and knows about date/time specifics.
Solution
You have to involve the Date/Time API for your math, then it will be clever and correctly adjust the hours as well. Fortunately, there is a method in LocalDateTime already that does what you want:
LocalDateTime expirationTime = LocalDateTime.now().plusMinutes(30);
and that is pretty much all you need.
For the formatting part, either roll with the default representation:
return expirationTime.toString();
or use a DateTimeFormatter:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd HH:mm:ss.AAA");
and then
return expirationTime.format(formatter);
Notes
Instant
You are actually using the incorrect type for an expiration time. Using LocalDateTime will result in your application failing under certain situations. For example if your computer moves across countries, or your government decides to change its timezone. Or when DST hits (summer vs winter time) or leap seconds are added and more...
The correct type would be Instant, which represents a single moment on the timeline, without interpretation of clock-time or calendar-dates.
The API is the same, so you can just use it the same way.
That said, your method should also return Instant and not a String. Keep the clever date/time type as long as possible, dont go to something as low level and raw as a string.
public static Instant getExpiryDate(int additionalMinutes) {
return Instant.now()
.plus(additionalMinutes, ChronoUnit.MINUTES);
}
Design
Design-wise it would be better if the method would not even take int additionalMinutes but also the unit. Otherwise the call-site is hard to read for users:
getExpiryDate(30) // 30 what? minutes? seconds? days?
with the unit, it would be easier to read and harder to misunderstand
getExpiryDate(30, ChronoUnit.MINUTES)
At which point one could argue that the method is kinda obsolete now.
Instead of editing the amount of minutes manually, try using the plusMinutes method on your LocalDateTime like so:
LocalDateTime now = LocalDateTime.now();
LocalDateTime then = now.plusMinutes(30);
This way, the class should increase the hour for you once it passes 60 minutes.

Java date treatment - March 29

I usually deal with dates with ints according to the pattern yyyyMMdd.
e.g.: today is 20170113, or better 20,170,113.
I need to calculate the distance bethween dates in days, so I wrote this:
public static int calculateDistance(int data1, int data2){
try {
SimpleDateFormat normalDateFormat = new SimpleDateFormat("yyyyMMdd");
long beginTime = normalDateFormat.parse(String.valueOf(data1)).getTime();
long endTime = normalDateFormat.parse(String.valueOf(data2)).getTime();
long diff = endTime - beginTime;
return (int) TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
} catch (ParseException e) {
e.printStackTrace();
return 0;
}
}
And this test:
public void testCalculateDistance() {
System.out.println(Calculator.calculateDistance(20090325, 20090328));
System.out.println(Calculator.calculateDistance(20090326, 20090329));
System.out.println(Calculator.calculateDistance(20090327, 20090330));
System.out.println(Calculator.calculateDistance(20090328, 20090331));
System.out.println(Calculator.calculateDistance(20090329, 20090401));
System.out.println(Calculator.calculateDistance(20090330, 20090402));
System.out.println(Calculator.calculateDistance(20090331, 20090403));
System.out.println(Calculator.calculateDistance(20090401, 20090404));
}
The output should always be the same, since I'm making the same modifications to both begin and end. But I get 2 (so 1 day less) iff the interval (end excluded) contains March 29.I also found it happens whenever I have this day of any year within the interval I mean to measure.
Why does it happen?
How can I fix it?
EDIT: I read this and I know how to calculate the difference between dates. The point is that this doesn't seem to work with this kind of classes, so this question is not a duplicate because I needn't know how to find the difference, but I need to know which classes I should use, instead of the ones I used to use.
In this case it is probably preferable to use LocalDate and ChronoUnit from java.time package.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
LocalDate beginning = LocalDate.parse("20090325",formatter);
LocalDate ending = LocalDate.parse("20090328",formatter);
System.out.println(ChronoUnit.DAYS.between(beginning,ending));

Test a date within a day intervall range

I have a date and a number and want to check if this date and this number occurs in a list of other dates within:
+-20 date intervall with the same number
so for example 1, 1.1.2013 and 1,3.1.2013 should reuturn false.
I tried to implement the method something like that:
private List<EventDate> dayIntervall(List<EventDate> eventList) throws Exception {
List<EventDate> resultList = new ArrayList<EventDate>();
for (int i = 0; i < eventList.size(); i++) {
String string = eventList.get(i).getDate();
Date equalDate = new SimpleDateFormat("dd.MM.yyyy", Locale.GERMAN).parse(string);
for (int j = 0; j < eventList.size(); j++) {
String string1 = eventList.get(i).getDate();
Date otherDate = new SimpleDateFormat("dd.MM.yyyy", Locale.GERMAN).parse(string1);
if (check number of i with number of j && check Date) {
//do magic
}
}
}
return resultList;
}
The construction of the iteration method is not that hard. What is hard for me is the date intervall checking part. I tried it like that:
boolean isWithinRange(Date testDate, Date days) {
return !(testDate.before(days) || testDate.after(days));
}
However that does not work because days are not takes as days. Any suggestions on how to fix that?
I really appreciate your answer!
You question is difficult to follow. But given its title, perhaps this will help…
Span Of Time In Joda-Time
The Joda-Time library provides a trio of classes to represent a span of time: Interval, Period, and Duration.
Interval
An Interval object has specific endpoints that lie on the timeline of the Universe. A handy contains method tells if a DateTime object occurs within those endpoints. The beginning endpoint in inclusive while the last endpoint is exclusive.
Time Zones
Note that time zones are important, for handling Daylight Saving Time and other anomalies, and for handling start-of-day. Keep in mind that while a java.util.Date seems like it has a time zone but does not, a DateTime truly does know its own time zone.
Sample Code
Some code off the top of my head (untested)…
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Berlin" );
DateTime dateTime = new DateTime( yourDateGoesHere, timeZone );
Interval interval = new Interval( dateTime.minusDays( 20 ), dateTime.plusDays( 20 ) );
boolean didEventOccurDuringInterval = interval.contains( someOtherDateTime );
Whole Days
If you want whole days, call the withTimeAtStartOfDay method to get first moment of the day. In this case, you probably need to add 21 rather than 20 days for the ending point. As I said above, the end point is exclusive. So if you want whole days, you need the first moment after the time period you care about. You need the moment after the stroke of midnight. If this does not make sense, see my answers to other questions here and here.
Note that Joda-Time includes some "midnight"-related methods and classes. Those are no longer recommended by the Joda team. The "withTimeAtStartOfDay" method takes their place.
DateTime start = dateTime.minusDays( 20 ).withTimeAtStartOfDay();
DateTime stop = dateTime.plusDays( 21 ).withTimeAtStartOfDay(); // 21, not 20, for whole days.
Interval interval = new Interval( start, stop );
You should avoid java.util.Date if at all possible. Using the backport of ThreeTen (the long awaited replacement date/time API coming in JDK8), you can get the number of days between two dates like so:
int daysBetween(LocalDate start, LocalDate end) {
return Math.abs(start.periodUntil(end).getDays());
}
Does that help?
You can get the number of dates in between the 2 dates and compare with your days parameter. Using Joda-Time API it is relatively an easy task: How do I calculate the difference between two dates?.
Code:
SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy", Locale.GERMAN);
Date startDate = format.parse("1.1.2013");
Date endDate = format.parse("3.1.2013");
Days d = Days.daysBetween(new DateTime(startDate), new DateTime(endDate));
System.out.println(d.getDays());
Gives,
2
This is possible using Calendar class as well:
Calendar cal = Calendar.getInstance();
cal.setTime(startDate);
System.out.println(cal.fieldDifference(endDate, Calendar.DAY_OF_YEAR));
Gives,
2
This 2 can now be compared to your actual value (20).

Error while calculating java Date difference

Calculating the difference between two dates (java.util.Date) in terms of no. of days look like very simple and we can find different ways to do that. I used the following code to calculate the date difference:
public static long daysBetween(Calendar startDate, Calendar endDate) {
Calendar date = (Calendar) startDate.clone();
long daysBetween = 0;
while (date.before(endDate)) {
date.add(Calendar.DAY_OF_MONTH, 1);
daysBetween++;
}
return daysBetween;
}
In main(), I used the following two dates :
Calendar c1 = Calendar.getInstance();
c1.set(2011, 1, 1);
Calendar c2 = Calendar.getInstance();
c2.set(2011, 1, 31);
long difference = daysBetween(c1, c2); //
But the value of the variable difference is not consistent. It is sometimes 30 and sometimes 31. So, why that might have happened.
Is there any solution to use the method results a consistent output ?
You're setting the date part of the calendars, but not the time part.
Sometimes the clock will tick between the calls to getInstance() and sometimes it won't, hence the inconsistency.
Options:
Set the time as well as the date, e.g. to midnight
Use a better date/time library - Joda Time - which has a more suitable representation (LocalDate). An important moral here is that if you can find a type which represents the exact information you have, and nothing else, that's likely to be a good fit and cause fewer complications.
Using LocalDate, you wouldn't even have to do the loop as Joda Time has good support for computing the differences between two values anyway.
LocalDate date1 = new LocalDate(2011, 1, 1);
LocalDate date2 = new LocalDate(2011, 1, 31);
Days period = Days.daysBetween(days1, days2);
int days = period.getDays();
You are only setting the year, month and day. The hours, minutes, seconds and milli-seconds are the current time (and thus different every time you run it)
I suggest you use Joda Time's LocalDate instead as it appears to does exactly what you want.

Categories