org.opentripplanner.util.time.DurationUtils Maven / Gradle / Ivy
package org.opentripplanner.util.time;
import static java.util.Locale.ROOT;
import java.time.Duration;
import java.time.LocalTime;
import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.opentripplanner.model.calendar.ServiceDate;
/**
* This class extend the Java {@link LocalTime} and with the ability
* to span multiple days and be negative.
*
* The class is used to track time relative to a {@link ServiceDate}.
*
* The RelativeTime can also be used relative to some other time, in which case it is similar
* to the JAva {@link Duration}.
*
* The primary usage of this class is to convert "number of seconds" into a string
* in the given context. Here is some examples:
*
* Service date time:
* 23:59:59 // One second to midnight
* 2:00+1d // 26 hours (1d2h) past midnight of service date
* 12:05-3d // 5 past noon on service-date minus 3 days
* -11:00+1d // 1 day and 11 hours before midnight service-date
* 2d4h12s // 2 days 4 hours and 12 seconds (relative to time in context)
* -3m // 3 minutes before (relative to time in context)
*
*
*/
public class DurationUtils {
private DurationUtils() { }
/**
* Convert a duration in seconds to a readable string. This a follows
* the ISO-8601 notation for most parts, but make it a bit more readable:
* {@code P2DT2H12M40S => 2d2h12m40s}. For negative durations
* {@code -P2dT3s => -2d3s, not -2d-3s or -(2d3s)} is used.
*/
public static String durationToStr(int timeSeconds) {
StringBuilder buf = new StringBuilder();
if(timeSeconds < 0) { buf.append("-"); }
int time = Math.abs(timeSeconds);
int sec = time % 60;
time = time / 60;
int min = time % 60;
time = time / 60;
int hour = time % 24;
int day = time / 24;
if(day != 0) { buf.append(day).append('d'); }
if(hour != 0) { buf.append(hour).append('h'); }
if(min != 0) { buf.append(min).append('m'); }
if(sec != 0) { buf.append(sec).append('s'); }
return buf.length() == 0 ? "0s" : buf.toString();
}
public static String durationToStr(Duration duration) {
return durationToStr((int)duration.toSeconds());
}
public static String durationToStr(int timeSeconds, int notSetValue) {
return timeSeconds == notSetValue ? "" : durationToStr(timeSeconds);
}
/**
* Parse a sting on format {@code nHnMn.nS} or {@link Duration#parse(CharSequence)}.
* The prefix "PT" is added if not present before handed of the
* {@link Duration#parse(CharSequence)} method.
*
* @return the duration in seconds
*/
public static int durationInSeconds(String duration) {
Duration d = duration(duration);
return (int)d.toSeconds();
}
/**
* Same as {@link #durationInSeconds(String)}, but returns the {@link Duration}, not the
* {@code int} seconds.
*/
public static Duration duration(String duration) {
var d = duration.toUpperCase();
if(!(d.startsWith("P") || d.startsWith("-P"))) {
int pos = d.indexOf('D') + 1;
if (pos > 0) {
var days = d.substring(0, pos);
d = pos == d.length() ? "P" + days : "P" + days + "T" + d.substring(pos) ;
}
else {
d = "PT" + d;
}
}
try {
return Duration.parse(d);
}
catch (DateTimeParseException e) {
throw new DateTimeParseException(
e.getMessage() + ": " + e.getParsedString(),
e.getParsedString(),
e.getErrorIndex()
);
}
}
/**
* Parse a list of durations using white-space, comma or semicolon as a separator.
*
* Example: {@code "2h 3m;5s"} will result in a list with 3 durations.
*/
public static List durations(String durations) {
if(durations == null || durations.isBlank()) {
return List.of();
}
return Arrays.stream(durations.split("[,;\\s]+"))
.map(DurationUtils::duration)
.collect(Collectors.toList());
}
public static String msToSecondsStr(long timeMs) {
if(timeMs == 0) { return "0 seconds"; }
if(timeMs == 1000) { return "1 second"; }
if(timeMs < 100) { return msToSecondsStr("%.3f", timeMs); }
if(timeMs < 995) { return msToSecondsStr( "%.2f", timeMs); }
if(timeMs < 9950) { return msToSecondsStr( "%.1f", timeMs); }
else { return msToSecondsStr( "%.0f", timeMs); }
}
private static String msToSecondsStr(String formatSeconds, double timeMs) {
return String.format (ROOT, formatSeconds, timeMs/1000.0) + " seconds";
}
}