jodaTime PeriodFormatter not formatting correctly - java

I have this PeriodFormatter:
PeriodFormatterBuilder()
.appendDays()
.appendSuffix(
" day",
" days")
)
.appendSeparator(", ")
.printZeroRarelyLast()
.appendHours()
.appendSuffix(
" hours",
" hours"
)
.appendSeparator(" ")
.appendMinutes()
.appendSuffix(" minute")
.toFormatter()
.let {
Period(Seconds.seconds(seconds.toInt())).toString(it)
}
I want to give seconds as input and get x DAYS, x HOURS, x MINUTES back....
I get an empty String back when doing this. If i add "appendSeconds()" to the formatter creation I get the same amount of seconds back as a return value as I sent in..
I need the conversion, not just the amount, and I'm not interested in number of seconds, but how many minutes, hours and days it accounts to.. Anyone who can help?

You need to use Period.normalizedStandard() to persuade your period to convert your seconds into minutes, hours and days.
Period(Seconds.seconds(seconds.toInt())).normalizedStandard().toString(it)
(Not sure whether the empty round brackets () are needed in Kotlin. They are in Java, where I tested.)

Related

Duration of minutes not working correctly in Java/Android

I am trying to display a rest time value in hours and minutes to my UI on an application I am finishing up.. I made use of another StackOverflow forum to figure this out and so far it is working okay, the time is displaying, but I think the maths is wrong somewhere..
I have two TextViews, one which displays active time in minutes, and another which displays rest time in hours and minutes (as the rest time will normally be a significantly longer period). The rest time is essentially 24hrs minus the active time.
So far, I have converted my stored active time string to an Int, subtracted that from the minute value for 24 hours (1440 minutes), then used the Duration method to convert that value to hours and minutes (hh:MM) which worked fine, the UI showed the correct subtracted value in hh:MM.
My problem is when I tried to split this into parts using a string, where my string is (restHours + " h" + restMinutes + " minutes") I get the hours and minutes value for the entire restTimeInt value (for example: 23h 1430minutes).
How can I change this so it displays the correct minutes value (for example: 23hr 40min)?
int activeTimeValue = Integer.parseInt(activityTime); // ex: 10 mins
int day = 1440;
int restTimeInt = day - activeTimeValue; // 1430 mins
Duration d = Duration.ofMinutes(restTimeInt);
int restHours = (int) d.toHours();
int restMinutes = (int) d.toMinutes();
restTimeTV.setText(restHours + " hr" + restMinutes + " mins");
Duration.minus() and Duration.toMinutesPart()
Now you’re at it, why not go all in on Duration and let it handle all math for you? With a little help it can even parse your string of minutes.
String activityTimeStr = "10"; // Minutes
Duration activityTime = Duration.parse("PT" + activityTimeStr + "M");
Duration restTime = Duration.ofDays(1).minus(activityTime);
System.out.format("Rest time: %s%n", restTime);
Output is:
Rest time: PT23H50M
So 23 hours 50 minutes, as expected. If you need those numbers, 23 and 50, for example for formatting for the user, as deHaar said, use the toMinutesPart method of Duration:
int restHours = Math.toIntExact(restTime.toHours());
int restMinutes = restTime.toMinutesPart();
System.out.format("Rest time: %d hours %d minutes%n", restHours, restMinutes);
Rest time: 23 hours 50 minutes

How to handle LocalDate Period.between results?

I use LocalDate (ThreeTenABP) to calculate the period between to given dates. When I use 01/01/2018 as the first and 12/31/2018 as the second date, Period.between gives the following result:
00 years
11 months
30 days
IMHO this is wrong, since a full year has 12 months.
I tried to add one day. Then I get:
00 years
11 months
31 days
I would need a reliable method to get the real amount of full months within a given period.
If you look at the Javadoc for Period.between(), it tells you that the end date is exclusive (meaning: not counted).
The start date is included, but the end date is not. The period is calculated by removing complete months, then calculating the remaining number of days, adjusting to ensure that both have the same sign.
I wrote this code, and it appears to function as I would expect...
LocalDate d1 = LocalDate.of(2018, 1, 1);
LocalDate d2 = LocalDate.of(2018, 12, 31);
Period p1 = Period.between(d1, d2);
System.out.println(p1.getYears() + " years, " + p1.getMonths() + " months, " + p1.getDays() + " days");
// Prints: 0 years, 11 months, 30 days
Adding one day, making it 2019-01-01, gives me one year:
LocalDate d3 = LocalDate.of(2019, 1, 1);
Period p2 = Period.between(d1, d3);
System.out.println(p2.getYears() + " years, " + p2.getMonths() + " months, " + p2.getDays() + " days");
// Prints: 1 years, 0 months, 0 days
Edit:
If you just add one day to the calculated Period object, you are really just adding a day, not recalculating the period as the between method would. Here's the code from Period which does plusDays():
public Period plusDays(long daysToAdd) {
if (daysToAdd == 0) {
return this;
}
return create(years, months, Math.toIntExact(Math.addExact(days, daysToAdd)));
}
If you follow the create call, it really just adds one day to the days counter, it doesn't recalculate anything. To properly add a day to a period, recalculate it with different endpoints as I have above.
The reliable method is:
LocalDate begin = LocalDate.of(2018, Month.JANUARY, 1);
LocalDate end = LocalDate.of(2018, Month.DECEMBER, 31);
Period inclusive = Period.between(begin, end.plusDays(1));
System.out.println(inclusive);
This prints
P1Y
Voilà, a period of one year. Adding 1 day to the end date makes the end date inclusive (since now it’s the day after the end date that is exclusive).

joda time PeriodFormatter does not print years

I have a formatter as below:
private static PeriodFormatter formatter = new PeriodFormatterBuilder()
.printZeroNever()
.appendYears().appendSuffix(" years ")
.appendMonths().appendSuffix(" months ")
.appendWeeks().appendSuffix(" weeks ")
.appendDays().appendSuffix(" days ")
.appendHours().appendSuffix(" hours ")
.appendMinutes().appendSuffix(" minutes ")
.appendSeconds().appendSuffix(" seconds")
.toFormatter();
and use it as below:
DateTime dt = DateTime.parse("2010-06-30T01:20");
Duration duration = new Duration(dt.toInstant().getMillis(), System.currentTimeMillis());
Period period = duration.toPeriod().normalizedStandard(PeriodType.yearMonthDayTime());
formatter.print(period);
the output is:
2274 days 13 hours 59 minutes 39 seconds
So where is the years?
The underlying problem here is your use of Duration to start with, IMO. A Duration is just a number of milliseconds... it's somewhat troublesome to consider the number of years in that, as a year is either 365 or 366 days (and even that depends on the calendar system). That's why the toPeriod method you're calling explicitly says:
Only precise fields in the period type will be used. Thus, only the hour, minute, second and millisecond fields on the period will be used. The year, month, week and day fields will not be populated.
Then you're calling normalizedStandard(PeriodType) which includes:
The days field and below will be normalized as necessary, however this will not overflow into the months field. Thus a period of 1 year 15 months will normalize to 2 years 3 months. But a period of 1 month 40 days will remain as 1 month 40 days.
Rather than create the period from a Duration, create it directly from the DateTime and "now", e.g.
DateTime dt = DateTime.parse("2010-06-30T01:20");
DateTime now = DateTime.now(); // Ideally use a clock abstraction for testability
Period period = new Period(dt, now, PeriodType.yearMonthDayTime());

Effectively getting time unit with Joda

I am looking for a neat solution to get the time units in Java 7 ( or using Joda date time)
Like, to 65 minutes, it should say 1 hour 5 minutes
To 30 minutes, it should just say 30 minutes
Thanks.
You can use joda time's normalizedStandard to print your output too.
Per the doc,
Normalizes this period using standard rules, assuming a 12 month year,
7 day week, 24 hour day, 60 minute hour and 60 second minute.
An example for 65 minutes would be:
System.out.println(PeriodFormat.getDefault().print(Period.hours(0).plusMinutes(65).plusSeconds(0).normalizedStandard()));
Output:
1 hour and 5 minutes
Short answer, use org.joda.time.Period.
For example, a general purpose solution might be to have a method that takes the number of milliseconds and returns a String of the form:
X hours, X minutes, X seconds, X milliseconds
public class DateTimeUtils {
public static String toNicePeriodValue(Period period) {
return period.getHours() + "hours " +
period.getMinutes() + "minutes " +
period.getSeconds() + "seconds " +
period.getMillis() + "milliseconds";
}
}
An easy way to create a Period object is like this:
public String nicePeriodValueFromMillis(long timeInMillis) {
Period period = new Period(timeInMillis);
String ret = DateTimeUtils.toNicePeriodValue(period);
return ret;
}
And invoke it like this:
long timeInMillis = /* obtain somehow */
String nicePeriodValue = nicePeriodValue(timeInMillis);
System.out.println("Nice Period Value: " + nicePeriodValue);
This is not, of course, a complete solution, but it should get you started.
If your input is always minutes use the modulus operator % 60 to find remaining minutes and / 60 to find hours.

Approximate Time Period with Joda-Time

I am using Joda library to get time period passed since a given timestamp:
public static String getTimePassedSince(Date initialTimestamp){
DateTime initDT = new DateTime(initialTimestamp.getTime());
DateTime now = new DateTime();
Period p = new Period(initDT, now);
PeriodFormatter formatter = new PeriodFormatterBuilder()
.appendYears().appendSuffix(" year, ", " years, ")
.appendMonths().appendSuffix(" month, ", " months, ")
.appendDays().appendSuffix(" day, ", " days, ")
.appendHours().appendSuffix(" hour, ", " hours, ")
.appendMinutes().appendSuffix(" minute, ", " minutes, ")
.appendSeconds().appendSuffix(" second, ", " seconds")
.printZeroNever()
.toFormatter();
return formatter.print(p);
}
The function returns exact time period strings for given timestamps. For example:
3 minutes, 23 seconds
1 hour, 30 minutes, 57 seconds
1 day, 23 hours, 21 minutes, 19 seconds
Is there any way that I can get approximate time instead of exact? For example, if one minute and 30 seconds have passed since the initialTimestamp, it only returns 1.5 minutes. Similarly, if an hour and 35 minutes have passed, it returns about 1.5 hours instead of 1 hour, 35 minutes, xy seconds.
I know the string returned can be parsed and manipulated but I am looking for something more sophisticated.
Take a look on PrettyTime.
I think you'll need to create your own formatter for this, which looks at the period and determines what granularity you want to format it in, say for 63 seconds "1 minute", or for 3 hours 48 minutes: "3 hours". Sounds to me like you want to report only the one largest unit of time, and ignore the more granular ones. You'll need to define the rounding behavior and how to render times in days: "44 days ago" or "one month ago" or "1 month and 2 weeks ago".
I am not aware of any generic utility which does this, but I haven't looked for one either.

Categories