I have an app where i need to check if travel is in duration between 4 hours and 30 hours, I store it as a strings "04:00" and "30:00", then i try to parse it using LocalTime.parse(), "04:00" is parsed successfully but "30:00" gets an error for invalid format, what could be best way to parse duration hours from a string ?
First of all, you're storing it somehow wrong. I suggest to store it in way Duration.parse can handle it, in standard ISO 8601 format.
Examples:
"PT20.345S" -- parses as "20.345 seconds"
"PT15M" -- parses as "15 minutes" (where a minute is 60 seconds)
"PT10H" -- parses as "10 hours" (where an hour is 3600 seconds)
"P2D" -- parses as "2 days" (where a day is 24 hours or 86400 seconds)
"P2DT3H4M" -- parses as "2 days, 3 hours and 4 minutes"
"P-6H3M" -- parses as "-6 hours and +3 minutes"
"-P6H3M" -- parses as "-6 hours and -3 minutes"
"-P-6H+3M" -- parses as "+6 hours and -3 minutes"
So then you can just do:
Duration dur30H = Duration.parse("PT30H"); // 30h
Duration dur4H = Duration.parse("PT4H"); // 4h
Duration travelTime = Duration.parse("P1D"); // 1D
boolean result = travelTime.compareTo(dur30H) <= 0 && travelTime.compareTo(dur4H) >= 0; // true
Related
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
I am storing two DateTimes (Joda) in an object and then I get a Period from the object by new Period(dateTime1, dateTime2).
I then want to add all the periods from different objects together.
I am both adding all the periods together in a variable and summing up some periods in smaller periods stored in a HashMap<long, Period>.
The result and issue is this.
The first period gets "2 hours and 30 minutes" with a PeriodFormat.getDefault().print(p) (the values are the same if i concatenate getHours and getMinutes).
The second value "5 hours and 52 minutes". So far so good.
But when I do it with the 3rd and 4th, the minutes stop converting to hours.
"5 hours and 103 minutes"
"8 hours and 132 minutes"
It should be 10h and 12m, but as you can see. That's not what I am getting. What is the issue? How can Period just forget to do the conversion? I don't have any problems with the selected sums, yet.
code: (with variable names changed)
mainSum= new Period();
tasksSum= new HashMap<Long, Period>();
for(Entry entry: entries){
long main_id= entry.getMain_id();
long task_id = entry.getTask_id();
Period entryPeriod = entry.getPeriod();
if(main_id == mainStuff.getId()){
mainSum = entryPeriod.plus(mainSum);
Timber.d("mainSum: " + PeriodFormat.getDefault().print(mainSum));
Timber.d("sum of workplace: " + mainSum.getHours() + " : " + mainSum.getMinutes());
Period taskPeriod = tasksPeriodSums.remove(task_id);
if(taskPeriod == null){
tasksPeriodSums.put(task_id, entryPeriod);
} else {
tasksPeriodSums.put(task_id, taskPeriod.plus(entryPeriod));
}
}
}
Please help, thank you :)
This is documented behaviour, check out the Javadoc for the plus(Period) function:
/**
* Returns a new period with the specified period added.
* <p>
* Each field of the period is added separately. Thus a period of
* 2 hours 30 minutes plus 3 hours 40 minutes will produce a result
* of 5 hours 70 minutes - see {#link #normalizedStandard()}.
* <p>
...
Drilling down into the Javadoc of the normalizedStandard(..) function itself, we see what's the tradeoff:
/**
* Normalizes this period using standard rules, assuming a 12 month year,
* 7 day week, 24 hour day, 60 minute hour and 60 second minute,
*
...
* However to achieve this it makes the assumption that all years are
* 12 months, all weeks are 7 days, all days are 24 hours,
* all hours are 60 minutes and all minutes are 60 seconds. This is not
* true when daylight savings time is considered, and may also not be true
* for some chronologies.
...
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.
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.
I have fetched google maps ETAs (that is, durations) for some routes in the format of x hour(s) y min(s), and also if x > 24 then this format changes into u day(s) v hour(s).
Now I want to compare these values to some other ETAs so all I need to do is convert these format value into a minutes-only value.
Such as I have a value as 4 hours 34 mins, I want to change it into minutes using Java or such as 1 hour 1 min to minutes, there are records where hour indicated as 1 hour and 3 hours and same for mins and days.
Duration lessThanADay = Duration.ofHours(4).plusMinutes(34);
long minutes = lessThanADay.toMinutes();
This yields 274 minutes. The case for more than 24 hours is similar:
Duration moreThanADay = Duration.ofDays(1).plusHours(3);
This time toMinutes() returns 1620.
You can apply and mix plusHours() and plusMinutes()freely depending on which input numbers you’ve got.
EDIT: Your input strings are a bit more complicated. The Duration class can parse strings in ISO 8601 format, it goes like PT3H23M for a period of time of 3 hours 23 minutes. It may feel a little odd at first. However, we can fix your strings into this format:
private static Duration toDuration(String durationString) {
durationString = durationString.replaceAll(" days?", "D");
durationString = durationString.replaceAll(" hours?", "H");
durationString = durationString.replaceAll(" mins?", "M");
durationString = durationString.replace(" ", "");
if (durationString.contains("D")) {
durationString = durationString.replaceFirst("\\d+D", "P$0T");
} else {
durationString = "PT" + durationString;
}
return Duration.parse(durationString);
}
Let’s try this method on your example strings from the comment:
System.out.println(toDuration("3 hours 23 mins"));
System.out.println(toDuration("2 hours 56 mins"));
System.out.println(toDuration("1 hour 1 min"));
System.out.println(toDuration("1 day 18 hours"));
This prints:
PT3H23M
PT2H56M
PT1H1M
PT42H
So all of your strings have been recognized and parsed.
For the comparison, you don’t need to convert to minutes since Duration objects have a natural ordering and can be compared using compareTo, for example:
if (lessThanADay.compareTo(moreThanADay) < 0) {
System.out.println("Less");
}
(This prints Less.) You may find it more natural to compare the long minutes values using < and >, though.
You could split it on space...
String s[]="3 hours 23 mins".split(" ")
Then I'd just normalize everything to minutes
int minutes=0;
for(int i=0;i<=2;i+=2) {
if(s[i+1].startsWith("min"))
minutes+=s[i]
if(s[i+1].startsWith("hour"))
minutes+=s[i]*60
if(s[i+1].startsWith("day"))
minutes+=s[i]*60*24
}
If you were to put the lookup values into a map you could cut 2/3 from that loop, I'd do it that way in Groovy where the additional syntax would be minimal but in java it's a 50/50 if the added awkwardness loweres the redundancy enough to be worth it.