Java Period.between shows strange output - java

I am making an application just for learning purpose. And I tried to calculate the difference between two dates
LocalDate today = LocalDate.now();
LocalDate localHoudbaarheid = houdbaarheidsDatum;
Period period = Period.between(today, localHoudbaarheid);
The calculation works, but the output is strange.
Output of the code:
Dagen tot over de datum: P9D
The only thing that should be shown is the 9 without the P and D. This is where I call the name period in my code to print it out:
"Dagen tot over de datum: " + period;

There is nothing whatsoever strange about the output, it is exactly what the documentation, i.e. the javadoc of Period.toString() says it is:
Outputs this period as a String, such as P6Y3M1D.
The output will be in the ISO-8601 period format. A zero period will be represented as zero days, 'P0D'.
As documentation says, the output is following the ISO-8601 standard for durations, as also explained on Wikipedia:
Durations define the amount of intervening time in a time interval and are represented by the format P[n]Y[n]M[n]DT[n]H[n]M[n]S ...
P is the duration designator (for period) placed at the start of the duration representation.
Y is the year designator that follows the value for the number of years.
M is the month designator that follows the value for the number of months.
W is the week designator that follows the value for the number of weeks.
D is the day designator that follows the value for the number of days.

P9D means a Period of 9 Days.
import java.time.LocalDate;
import java.time.Period;
public class Main {
public static void main(String[] args) {
LocalDate today = LocalDate.now();
LocalDate after9Days = today.plusDays(9);
Period period = Period.between(today, after9Days);
System.out.println(period);
System.out.printf("Years: %d, Months: %d, Days: %d", period.getYears(), period.getMonths(), period.getDays());
}
}
Output:
P9D
Years: 0, Months: 0, Days: 9
Learn more about it at https://en.wikipedia.org/wiki/ISO_8601#Durations

The toString() method of Period outputs something like:
P(nbYears)Y(nbMonths)M(nbDays)D
P9D means a period of 9 days. P4Y3M9D means a period of 4 years, 3 months, and 9 days.
The Period class has utility methods to get this information: getDays(), getMonths(), and getYears().
If you just want to obtain the difference in the unit you choose, prefer using the until() method on the Temporal interface.
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
public class Main {
public static void main(String[] args) {
LocalDate today = LocalDate.now();
LocalDate after3Months9Days = today.plusMonths(3).plusDays(9);
System.out.println(today.until(after3Months9Days, ChronoUnit.DAYS));
}
}
This outputs:
101

Related

Not getting valid no of months from two dates

Need to identify number of months between two dates using java 8 but not getting proper number of months. Please find below program
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy", Locale.ENGLISH);
YearMonth startDate = YearMonth.parse("12/31/2020", formatter);
YearMonth endDate = YearMonth.parse("12/01/2021", formatter);
System.out.println(startDate.plusYears(1) + " :: " + endDate + " :: " + startDate.plusYears(1).isBefore(endDate));
long monthsBetween = ChronoUnit.MONTHS.between(startDate, endDate);
System.out.println(monthsBetween);
}
Output ::
2021-12 :: 2021-12 :: false
12
So from above program we are getting 12 months between this 2 dates "12/31/2020" and "12/01/2021"
But actually no of months between above dates is 13 so how we are getting 12 instead of 13?
May be I am wrong but can someone please explain
Note : date format is MM/dd/yyyy
The reason why you are getting 12 months because 'To' month is excluded from the counting.
Here is the between method's implementation,
//-----------------------------------------------------------------------
#Override
public long between(Temporal temporal1Inclusive, Temporal temporal2Exclusive) {
return temporal1Inclusive.until(temporal2Exclusive, this);
}
The answer by pratap is fully correct. From the documentation of between():
The calculation returns a whole number, representing the number of
complete units between the two temporals. For example, the amount in
hours between the times 11:30 and 13:29 will only be one hour as it is
one minute short of two hours.
Another way to say it is that the end month is not inclusive in the calculation. Which in turn means that the solution is to add one month to the end month:
long monthsBetween = ChronoUnit.MONTHS.between(startDate, endDate.plusMonths(1));
Now output is what you wanted:
13

Formatting LocalTime Minutes/Seconds into Integer Java

I know there are similar questions but they don't answer my problem. I want to format the current time into integer, but only the minutes or seconds.
So for example
LocalTime d = LocalTime.now(ZoneId.of("GMT"));
gives me the current GMT and with "withNano(0)" I can cut off the nanoseconds. Since I have it in the format 12:15:45 now I want to be able to save 15 (Minutes) into my Integer or 45 (Seconds). How can I convert it?
You can call specific methods on the LocalTime such as getMinute() and getSecond(), or you can use a DateTimeFormatter with a pattern that you’re looking for, like:
d.format(DateTimeFormatter.of(“mm:ss”);
If you’re doing that, you don’t need to zero out the nano first.
You can use built in methods for getting minutes and seconds, they both return integers:
d.getMinute();
d.getSecond();
Simply use the getter methods of LocalTime.
import java.time.LocalTime;
import java.time.ZoneOffset;
public class Main {
public static void main(String[] args) {
LocalTime now = LocalTime.now(ZoneOffset.UTC);
int hour = now.getHour();
int minute = now.getMinute();
int second = now.getSecond();
System.out.printf("%d hour, %d minute, %d second", hour, minute, second);
}
}
Output:
21 hour, 37 minute, 47 second
Also, I suggest you use ZoneOffset.UTC for UTC instead of the 3-letter name for timezone. You can also use ZoneId.of("Etc/UTC"). The general naming convention for timezone is Region/City e.g. Europe/London.

Java 8 calculate months between two dates

NOTE THIS IS NOT A DUPLICATE OF EITHER OF THE FOLLOWING
Calculating the difference between two Java date instances
calculate months between two dates in java [duplicate]
I have two dates:
Start date: "2016-08-31"
End date: "2016-11-30"
Its 91 days duration between the above two dates, I expected my code to return 3 months duration, but the below methods only returned 2 months. Does anyone have a better suggestion? Or do you guys think this is a bug in Java 8? 91 days the duration only return 2 months.
Thank you very much for the help.
Method 1:
Period diff = Period.between(LocalDate.parse("2016-08-31"),
LocalDate.parse("2016-11-30"));
Method 2:
long daysBetween = ChronoUnit.MONTHS.between(LocalDate.parse("2016-08-31"),
LocalDate.parse("2016-11-30"));
Method 3:
I tried to use Joda library instead of Java 8 APIs, it works. it loos will return 3, It looks like Java duration months calculation also used days value. But in my case, i cannot use the Joda at my project. So still looking for other solutions.
LocalDate dateBefore= LocalDate.parse("2016-08-31");
LocalDate dateAfter = LocalDate.parse("2016-11-30");
int months = Months.monthsBetween(dateBefore, dateAfter).getMonths();
System.out.println(months);
Since you don't care about the days in your case. You only want the number of month between two dates, use the documentation of the period to adapt the dates, it used the days as explain by Jacob. Simply set the days of both instance to the same value (the first day of the month)
Period diff = Period.between(
LocalDate.parse("2016-08-31").withDayOfMonth(1),
LocalDate.parse("2016-11-30").withDayOfMonth(1));
System.out.println(diff); //P3M
Same with the other solution :
long monthsBetween = ChronoUnit.MONTHS.between(
LocalDate.parse("2016-08-31").withDayOfMonth(1),
LocalDate.parse("2016-11-30").withDayOfMonth(1));
System.out.println(monthsBetween); //3
Edit from #Olivier Grégoire comment:
Instead of using a LocalDate and set the day to the first of the month, we can use YearMonth that doesn't use the unit of days.
long monthsBetween = ChronoUnit.MONTHS.between(
YearMonth.from(LocalDate.parse("2016-08-31")),
YearMonth.from(LocalDate.parse("2016-11-30"))
)
System.out.println(monthsBetween); //3
Since Java8:
ChronoUnit.MONTHS.between(startDate, endDate);
//Backward compatible with older Java
public static int monthsBetween(Date d1, Date d2){
if(d2==null || d1==null){
return -1;//Error
}
Calendar m_calendar=Calendar.getInstance();
m_calendar.setTime(d1);
int nMonth1=12*m_calendar.get(Calendar.YEAR)+m_calendar.get(Calendar.MONTH);
m_calendar.setTime(d2);
int nMonth2=12*m_calendar.get(Calendar.YEAR)+m_calendar.get(Calendar.MONTH);
return java.lang.Math.abs(nMonth2-nMonth1);
}
The documentation of Period#between states the following:
The start date is included, but the end date is not.
Furthermore:
A month is considered if the end day-of-month is greater than or equal to the start day-of-month.
Your end day-of-month 30 is not greater than or equal to your start day-of-month 31, so a third month is not considered.
Note the parameter names:
public static Period between​(LocalDate startDateInclusive, LocalDate endDateExclusive)
To return 3 months, you can increment the endDateExclusive by a single day.
In case you want stick to java.time.Period API
As per java.time.Period documentation
Period between(LocalDate startDateInclusive, LocalDate endDateExclusive)
where
#param startDateInclusive the start date, inclusive, not null
#param endDateExclusive the end date, exclusive, not null
So it is better to adjust your implementation to make your end date inclusive and get your desired result
Period diff = Period.between(LocalDate.parse("2016-08-31"),
LocalDate.parse("2016-11-30").plusDays(1));
System.out.println("Months : " + diff.getMonths());
//Output -> Months : 3
You have to be careful, never use LocalDateTime to calculate months between two dates the result is weird and incorrect, always use LocalDate !
here's is some code to prove the above:
package stack.time;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
public class TestMonthsDateTime {
public static void main(String[] args) {
/**------------------Date Time----------------------------*/
LocalDateTime t1 = LocalDateTime.now();
LocalDateTime t2 = LocalDateTime.now().minusMonths(3);
long dateTimeDiff = ChronoUnit.MONTHS.between(t2, t1);
System.out.println("diff dateTime : " + dateTimeDiff); // diff dateTime : 2
/**-------------------------Date----------------------------*/
LocalDate t3 = LocalDate.now();
LocalDate t4 = LocalDate.now().minusMonths(3);
long dateDiff = ChronoUnit.MONTHS.between(t4, t3);
System.out.println("diff date : " + dateDiff); // diff date : 3
}
}
My 2%
This example checks to see if the second date is the end of that month. If it is the end of that month and if the first date of month is greater than the second month date it will know it will need to add 1
LocalDate date1 = LocalDate.parse("2016-08-31");
LocalDate date2 = LocalDate.parse("2016-11-30");
long monthsBetween = ChronoUnit.MONTHS.between(
date1,
date2);
if (date1.isBefore(date2)
&& date2.getDayOfMonth() == date2.lengthOfMonth()
&& date1.getDayOfMonth() > date2.getDayOfMonth()) {
monthsBetween += 1;
}
After the short investigation, still not totally fix my question, But I used a dirty solution to avoid return the incorrect duration. At least, we can get the reasonable duration months.
private static long durationMonths(LocalDate dateBefore, LocalDate dateAfter) {
System.out.println(dateBefore+" "+dateAfter);
if (dateBefore.getDayOfMonth() > 28) {
dateBefore = dateBefore.minusDays(5);
} else if (dateAfter.getDayOfMonth() > 28) {
dateAfter = dateAfter.minusDays(5);
}
return ChronoUnit.MONTHS.between(dateBefore, dateAfter);
}
The Java API response is mathematically accurate according to the calendar. But you need a similar mechanism, such as rounding decimals, to get the number of months between dates that matches the human perception of the approximate number of months between two dates.
Period period = Period.between(LocalDate.parse("2016-08-31"), LocalDate.parse("2016-11-30"));
long months = period.toTotalMonths();
if (period.getDays() >= 15) {
months++;
}

Inconsistent `w` symbol formatting from joda time to java.time

My team is looking to switch from Joda time to java.time, but we're seeing different behavior in formatting using the same pattern. The issue arises when we're using the week-of-week-year w symbol:
final String dateString = "2016-01-04 00:00:00";
final String inputPattern = "yyyy-MM-dd HH:mm:ss";
// parse the input string using Joda
final org.joda.time.format.DateTimeFormatter jodaInputFormatter = org.joda.time.format.DateTimeFormat.forPattern(inputPattern);
final org.joda.time.DateTime jodaDateTime = jodaInputFormatter.parseDateTime(dateString);
// parse the input string using two different java.time classes
final java.time.format.DateTimeFormatter javaTimeInputFormatter = java.time.format.DateTimeFormatter.ofPattern(inputPattern).withZone(java.time.ZoneOffset.UTC);
final java.time.LocalDateTime localDateTime = java.time.LocalDateTime.parse(dateString, javaTimeInputFormatter);
final java.time.ZonedDateTime zonedDateTime = java.time.ZonedDateTime.parse(dateString, javaTimeInputFormatter);
final String outputPattern = "'week' w - dd/MM/yyyy HH:mm:ss";
final org.joda.time.format.DateTimeFormatter jodaOutputFormatter = org.joda.time.format.DateTimeFormat.forPattern(outputPattern);
final java.time.format.DateTimeFormatter javaTimeOutputFormatter = java.time.format.DateTimeFormatter.ofPattern(outputPattern);
// output: week 1 - 04/01/2016 00:00:00
System.out.println("With joda: " + jodaOutputFormatter.print(jodaDateTime));
// output: week 2 - 04/01/2016 00:00:00
System.out.println("With LocalDateTime: " + javaTimeOutputFormatter.format(localDateTime));
// output: week 2 - 04/01/2016 00:00:00
System.out.println("With ZonedDateTime: " + javaTimeOutputFormatter.format(zonedDateTime));
For some reason, the output from the w symbol is off-by-one across the two implementations.
What is causing this inconsistency? Is the w symbol inconsistently implemented across Joda time and java.time?
Well, it is a little bit speculative, but since you told me that your system timezone is EST (-05:00) I assume that you are sitting in US (New York?). And US does not apply ISO-8601-week rules. Weeks start on Sunday, and the first week of the year does not need to contain at least 4 days (even one day is enough to be counted as first week of year).
So let's look at your example date of 4th of January. It is a Monday. The first US-week is from 2016-01-01 until 2016-01-02 (2 days - enough for US). And the second US-week starts on Sunday the 3rd of January, so the fourth of January is in the second week, too.
And now the critical point: java.time (JSR-310) uses a localized week of week-based-year for the pattern symbol w, see also its backport which should have the same code. Code excerpt:
} else if (cur == 'w') {
if (count > 2) {
throw new IllegalArgumentException("Too many pattern letters: " + cur);
}
appendInternal(new WeekFieldsPrinterParser('w', count));
...
static final class WeekFieldsPrinterParser implements DateTimePrinterParser {
private final char letter;
private final int count;
public WeekFieldsPrinterParser(char letter, int count) {
this.letter = letter;
this.count = count;
}
#Override
public boolean print(DateTimePrintContext context, StringBuilder buf) {
WeekFields weekFields = WeekFields.of(context.getLocale());
DateTimePrinterParser pp = evaluate(weekFields);
return pp.print(context, buf);
}
The use of WeekFields.of(context.getLocale()) for the pattern symbol "w" is evident.
In contrast, Joda-Time only uses ISO-8601-week-definition which let weeks start on Monday and count that week as first week of year which contains at least four days in current calendar year. So the Monday 4th of January is the start of the first week-of-year because the three days before are not enough for ISO-8601 to be counted as week. Those preceding days are instead considered as last week of previous year.
Consequently, Joda-Time displays week 1 for 4th of January while java.time uses the US-week 2.
Solution of your problem is to specify the locale such that the formatter will use ISO-weeks so you get the same result as in Joda-Time. For example, you could choose Locale.UK which also uses English but other week rules. Don't rely on your default locale. This can fool you.
Edit: As Richard points out, I'm wrong—Java SE actually does say that the first week of a week-based year is the first Monday-based week containing at least four days, just like Joda-Time.
From the documentation of Java SE's IsoFields.WEEK_OF_WEEK_BASED_YEAR:
The week-of-week-based-year has values from 1 to 52, or 53 if the week-based-year has 53 weeks.
No mention is made of excluding any weeks, so it makes sense to assume that all weeks are counted.
From the Joda-Time Fields overview:
Weeks run from 1 to 52-53 in a week based year. The first day of the week is defined as Monday and given the value 1. The first week of a year is defined as the first week that has at least four days in the year.
January 1 and 2 comprise the first partial week of 2016, and since that's fewer than four days, Joda-Time does not count it as a week at all. January 4 is in the first week which contains four or more days.

Specifying time duration in java

I'm developing an application where the user sends a time duration in this format "YY : MM : DD : HH : mm : ss" ex . 1yr 2months 3days 4hrs 5mins 6sec will be specified as
1:2:3:4:5:6
From this value , i have to convert the duration into seconds. I want to know whether there is any inbuilt java class to specify time duration in this format?
The ISO 8601 standard defines a string representation for a span of time as PnYnMnDTnHnMnS where P marks the beginning and T separates the date portion from the time portion.
For example, "P3Y6M4DT12H30M5S" represents a duration of "three years, six months, four days, twelve hours, thirty minutes, and five seconds".
An hour and a half would be PT1H30M.
The Joda-Time library has the Period class that both parses and generates that format by default.
Search StackOverflow for more.
Java-8 introduced java.time.Duration and java.time.Period as part of JSR-310 implementation but there has been a long wait for a type combining period and duration as specified at ISO_8601#Durations.
However, javax.xml.datatype.Duration has been there since Java-6 and we can use this to achieve what you want to. The assumption is that your text is always in the format, y:M:d:H:m:s e.g. 1:2:3:4:5:6.
Demo:
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
public class Main {
public static void main(String[] args) throws DatatypeConfigurationException {
String[] durationParts = "1:2:3:4:5:6".split(":");
char[] symbols = "YMDHMS".toCharArray();
StringBuilder sb = new StringBuilder("P");
for (int i = 0; i < durationParts.length; i++) {
sb.append(durationParts[i]).append(symbols[i]);
if (i == 2) {
sb.append("T");
}
}
String strIso8601Duration = sb.toString();
System.out.println(strIso8601Duration);
Duration duration = DatatypeFactory.newInstance().newDuration(strIso8601Duration);
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(TimeZone.getTimeZone("GMT"));
long seconds = TimeUnit.MILLISECONDS.toSeconds(duration.getTimeInMillis(calendar));
System.out.println(seconds);
}
}
Output:
P1Y2M3DT4H5M6S
36907506

Categories

Resources