I am trying to use the Duration class instead of long.
It has superior literal syntax. I like its flexibility, though it looks weird.
"PT10S" means 10 seconds, what is the problem to accept "10 seconds"?!
Okay never mind.
I am just curious why PT prefix has been chosen (not "DU" e.g.) and why any prefix is better here rather than nothing?
As can be found on the page Jesper linked to (ISO-8601 - Data elements and interchange formats – Information interchange – Representation of dates and times)
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.
T is the time designator that precedes the time components of the representation.
So P means 'Period' and because there are no date-components it only has a 'Time'.
You could interpret this as 'Period of Time'
The 'why' this was chosen, you have to ask the ISO members that wrote the standard, but my guess is that it is easier to parse. (short and unambigious)
The details for the time component are:
H is the hour designator that follows the value for the number of hours.
M is the minute designator that follows the value for the number of minutes.
S is the second designator that follows the value for the number of seconds.
The value of PT20S then parses to:
Period
Time
20
Seconds
So, a duration of 20 seconds.
More examples can be found in the javadoc: https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html#parse-java.lang.CharSequence-
Java has taken a subset of the ISO 8601 standard format for a duration. So the “why” is why the standard was written the way it is, and it’s a guessing game. My go is:
P for period was chosen so that you can distinguish a duration from a date and/or time. Especially since a period may also be written in the same format as a local date-time, for example P0003-06-04T12:30:05 for 3 years 6 months 4 days 12 hours 30 minutes 5 seconds, the P can be necessary to distinguish. The P also gives a little but quick and convenient bit of validation in case you happen to pass a completely different string in a place where a duration was expected. And yes, PT10S looks weird, but once you get accustomed to it, you recognize it immediately as a duration, which can be practical.
T for time between the date part and the time part was chosen for two reasons:
For consistency with date-time strings that have T in the same place, for example 2018-07-04T15:00 for July 4, 2018 at 15:00 hours.
To disambiguate the otherwise ambiguous M for either months or minutes: P3M unambiguously means 3 months while PT3M means 3 minutes.
Actually if go on Duration API developed in Java since 1.8, they have gone with standard ISO 8601:
with java doc as below :
/**
* Applies an ISO 8601 Duration to a {#link ZonedDateTime}.
*
* <p>Since the JDK defined different types for the different parts of a Duration
* specification, this utility method is needed when a full Duration is to be applied to a
* {#link ZonedDateTime}. See {#link Period} and {#link Duration}.
*
* <p>All date-based parts of a Duration specification (Year, Month, Day or Week) are parsed
* using {#link Period#parse(CharSequence)} and added to the time. The remaining parts (Hour,
* Minute, Second) are parsed using {#link Duration#parse(CharSequence)} and added to the time.
*
* #param time A zoned date time to apply the offset to
* #param offset The offset in ISO 8601 Duration format
* #return A zoned date time with the offset applied
*/
public static ZonedDateTime addOffset(ZonedDateTime time, String offset) { }
Obtains a Duration from a text string of pattern: PnDTnHnMn.nS, where
nD = number of days,
nH = number of hours,
nM = number of minutes,
n.nS = number of seconds, the decimal point may be either a dot or a comma.
T = must be used before the part consisting of nH, nM, n.nS
Example of implementation with java as
import java.time.Duration;
public class ParseExample {
public static void main(String... args) {
parse("PT20S");//T must be at the beginning to time part
parse("P2D");//2 day
parse("-P2D");//minus 2 days
parse("P-2DT-20S");//S for seconds
parse("PT20H");//H for hours
parse("PT220H");
parse("PT20M");//M for minutes
parse("PT20.3S");//second can be in fraction like 20.3
parse("P4DT12H20M20.3S");
parse("P-4DT-12H-20M-20.3S");
parse("-P4DT12H20M20.3S");
}
private static void parse(String pattern) {
Duration d = Duration.parse(pattern);
System.out.println("Pattern: %s => %s%n", pattern, d);
}
}
Related
Part of my current project is to convert mm:ss to seconds...but the user has the option to enter x:xx or xx:xx.
For example, if someone wanted to enter one minute and thirty seconds into the program, they have the option to write it as either "01:30" or "1:30". And the output of both would be 90 seconds.
This is my current code.
System.out.print("Time (mm:ss): ")
String time = scan.nextLine();
int min = Integer.parseInt(time.substring(0, time.indexOf(':'))) * 60;
int sec = Integer.parseInt(time.substring(3, time.length()));
int duration = (min + sec);
System.out.println("Seconds: " + duration)
It works whenever I enter xx:xx, but fails when I enter x:xx.
I am not sure how to only read the characters after ":" . If I start the substring at ":" (I have it at 3 now), it can't convert to int because it reads the ":".
I have looked all over Google and my textbook, but have not found anything. I assume I am just using the wrong technique. The code needs to stay within the parameters of basic beginner String methods. Thank you!
This answer probably does not stay within the parameters of basic beginner String methods as requested. I think it will be useful for other readers of your question who don’t have the same limitation.
java.time.Duration
The Duration class is the class to use for an amount of time like 1 minute 30 seconds. Unfortunately, the Duration class can only parse strings in ISO 8601 format (link below), but the string conversion isn’t hard with a regular expression. And fortunately in ISO 8601 format leading zeroes don’t matter. The Duration class is part of java.time, the modern Java date and time API.
ISO 8601 format for a duration is like PT01M30S. Think of it as a period of time of 01 minute 30 seconds. If the format feels unusual at first, it is straightforward. So let’s convert to it. The following method accepts your user’s format, converts and returns a Duration.
public static Duration parseDuration(String durStr) {
String isoString = durStr.replaceFirst("^(\\d{1,2}):(\\d{2})$", "PT$1M$2S");
return Duration.parse(isoString);
}
Duration has a toSeconds method for converting to seconds. So let’s try the whole thing out:
System.out.println(parseDuration("01:30").toSeconds());
System.out.println(parseDuration("1:30").toSeconds());
Output is the expected:
90
90
Consider whether you need to convert to seconds at all, though. Keeping the Duration objects as they are will probably make your code more self-explanatory.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
int sec = Integer.parseInt(time.substring(time.indexOf(':') + 1), time.length()));
Thanks guys!
Here I have simplified your code.
String time = "1:30";
String[] timeUnit = time.split(":");
int totalSeconds = 60 * Integer.parseInt(timeUnit[0]) + Integer.parseInt(timeUnit[1]);
System.out.println("Seconds "+totalSeconds);
Given a period such as 3 days, or 5 weeks (a period with only one field type), I want to round a given DateTime to the nearest unit of that period (i.e, ignore the 5 in '5 days'). Examples:
Example 1:
Period: 3 days.
DateTime: Wednesday 4:26 AM UTC (2013-05-15T04:26:00Z)
Rounded DateTime: Wednesday Midnight UTC (2013-05-15T00:00:00Z)
Example 2:
Period: 5 weeks.
DateTime: Wednesday 4:26 AM UTC (2013-05-15T04:26:00Z)
Rounded DateTime: Monday Midnight UTC (2013-05-13T00:00:00Z)
My initial idea was to use Period's DurationFieldType getFieldTypes() method, and for every matching field in a DateTime (below the largest field), set them to zero. However, I don't know how to get the DateTimeFieldTypes from a DateTime and how to compare them to a DurationFieldType.
I would prefer not to do a huge if else approach.
Example bellow is a solution in case you can express period in days (can be modified to weeks, months etc.). Using DateTime Joda Java Library.
Unfortunately with rounding you require I see possible issue. You need to have a starting point in time since when you calculate the periods. In example bellow we calculate periods since 1970/01/01 00:00:00 UTC. Or are you actually asking to get period of 3 days from first day of a month (year) etc? It would make more sense.
Questions you need to ask your self: What will happen on leap days?
Java Method
DateTime roundDays(DateTime dt, int windowDays) {
Duration p = Duration.standardDays(windowDays);
long t = dt.getMillis() / p.getMillis() * p.getMillis();
// Keep TimeZone and round floor to a day
return new DateTime(t, dt.getZone()).dayOfMonth().roundFloorCopy();
}
Example use:
DateTime dt = new DateTime(1385578964580L, DateTimeZone.UTC);
System.out.println(roundDays(dt, 3));
System.out.println(roundDays(dt.plusDays(2), 3));
System.out.println(roundDays(dt.plusDays(4), 3));
System.out.println(roundDays(dt.plusDays(6), 3));
// Prints data rounded to every 3 days
// 2013-11-26T00:00:00.000Z
// 2013-11-29T00:00:00.000Z
// 2013-11-29T00:00:00.000Z
// 2013-12-02T00:00:00.000Z
Too long for comment:
It's not clear what that "rounding" means. To start with, you should deal with LocalDateTimes, not with DateTimes (they are very different things, see my answer here ).
It seems to me you want to set to zero all fields with resolution lower than that of your "period" unit, and then set the next field to a multiple of the given value... is that so? Then, I don't understand your second example (where are the 5 weeks?), and anyway, that would be badly specified: what to do with a period of "40 months" ?
Imagine if you wanted to model a non-fraction time range, that could be any of the following:
"1 hour" (all/any 1 hour period)
"1 hour, starting 1pm") (all/any 1 hour periods that start at 1pm)
"1 hour, starting 1pm, on Wednesdays" (all/any 1 hour periods that start at 1pm on wednesdays)
"1 hour, starting 1pm, on 3rd Wednesday in November"
"1 week, starting the first week in November"
You get the idea. An additional goal is to easily and efficiently compute overlap and subsets of these ranges. e.g. "1 hour, starting 1pm, on Wednesdays" overlaps with "1 hour, starting 1pm"
Additional info: This is for time periods in a baselining system. I want to have multiple granularities of time periods for baseline segments. Like the baseline for any 1 hour period at 1pm or the baseline for the 1 hour period starting at 1pm on the 3rd Wednesday of November.
An additional consideration is these baseline periods will be stored in a no-sql store, and it would be nice to efficiently fine periods in the smallest granularity that exist in the store. (does specific day-week-hour period exist? no, how about week-hour?, no? how about just a day-hour period) - if that makes sense. Maybe a tree-like hierarchy of some sort.
edit: the storing and querying part is probably the most important requirement. There will be billions of time periods stored, and they need to be looked up as fast as possible, (finding the finest granularity that exists). I will gladly sacrifice completeness for speed of looking up.
edit: thinking about it more, and how it will have to be stored in the datastore, a tree-like structure might be good for efficient look-ups. I can just walk down the tree to get the finest granularity that exists.
1hr
/
1hr#1pm
/
1hr#1pm#wednesday
/
1hr#1pm#wednesday#November
This is what I've come up with, but I feel it is pretty weak. I'm going to keep fiddling with it and updating here, but I'm curious to see if someone has a smarter way to model this.
public class DateRange {
Integer fTimeSpan;
TimeUnit fTimeUnit;
Integer fStartHour;
Integer fStartDay;
Integer fStartWeek;
Integer fStartMonth;
boolean intersects(DateRange other) { ... }
}
enum TimeUnit {
HOURS,
DAYS,
WEEKS,
MONTHS;
}
edit: A tree-based structure (like in my edit above) would be a lot simpler. No unused fields for large-grained spans. The granularity would be in the tree, rather than in the datastructure...
public class RangeTreeNode {
TimeUnit fTimeUnit;
int fStartTime;
int fSpanTime;
List<RangeTreeNode> fChildren;
}
Abstractions
I think what you are describing can be modelled with Joda Time's Interval class. It supports the notions of Instants, Periods, and Durations:
An interval represents an interval of time from one
millisecond instant to another instant. Both instants are fully
specified instants in the datetime continuum, complete with time zone.
An instant represents an exact point on the time-line, but limited to the precision of milliseconds.
A period represents a period of time defined in terms
of fields, for example, 3 years 5 months 2 days and 7 hours. This
differs from a duration in that it is inexact in terms of
milliseconds. A period can only be resolved to an exact number of
milliseconds by specifying the instant (including chronology and time
zone) it is relative to.
A duration represents a duration of time measured in
milliseconds. The duration is often obtained from an interval.
Furthermore, it's interface supports overlap, abuts, gap and other Interval relationship methods defined in AbstractInterval.
You may also want to consider Partial for your approach, which is explained in general terms here. This will help you since:
A partial does not fully specify a single point in the datetime
continuum, but instead may match multiple points (partial + missing fields + time zone = instant)
Examples
Some examples that relate back to your original question:
import static org.joda.time.DateTimeConstants.NOVEMBER;
import static org.joda.time.DateTimeConstants.WEDNESDAY;
import static org.joda.time.DateTimeFieldType.dayOfMonth;
import static org.joda.time.DateTimeFieldType.dayOfWeek;
import static org.joda.time.DateTimeFieldType.hourOfDay;
import static org.joda.time.DateTimeFieldType.monthOfYear;
import static org.joda.time.Duration.standardDays;
import static org.joda.time.Duration.standardHours;
import org.joda.time.Duration;
import org.joda.time.Partial;
public class Periods {
public static void main(String[] args) {
// "1 hour" (all/any 1 hour period)
Duration d1 = standardHours(1);
Partial p1 = new Partial();
// "1 hour, starting 1pm" (all/any 1 hour periods that start at 1pm)
Duration d2 = standardHours(1);
Partial p2 = new Partial().withField(hourOfDay(), 13);
// "1 hour, starting 1pm, on Wednesdays" (all/any 1 hour periods that start at 1pm on Eednesdays)
Duration d3 = standardHours(1);
Partial p4 = new Partial().withField(hourOfDay(), 13).withField(hourOfDay(), 1).withField(dayOfWeek(), WEDNESDAY);
// "1 hour, starting 1pm, on Wednesday in November"
Duration d4 = standardHours(1);
Partial p3 = new Partial().withField(hourOfDay(), 13).withField(hourOfDay(), 1).withField(dayOfWeek(), WEDNESDAY).withField(monthOfYear(), NOVEMBER);
// "1 week, starting the first week in November"
Duration d5 = standardDays(7);
Partial p5 = new Partial().withField(dayOfMonth(), 1).withField(monthOfYear(), NOVEMBER);
}
}
I'm going to design an application, in which I need to get the exact time difference between two dates. Ex:
Date1:31/05/2011 12:54:00
Date2:31/05/2011 13:54:00
I tried using getTime() but I didn't get exact result.
The expected output for the above inputs is 3600000 (60 * 60 * 1000) millisec but I'm getting 46800000 (13 * 60 * 60 * 1000).
When I went through different java forums people are suggesting to use JodaTime.
Still I'm unable to get the exact result.
The timezone on I'm working is London(GMT).
Init two dateTime and use Period :
DateTime dt1 = new DateTime(2013,9,11,9,58,56);
DateTime dt2 = new DateTime(2013,9,11,9,58,59);
Period p = new Period(dt1, dt2, PeriodType.millis());
To get difference in milliseconds :
System.out.println(p.getValue(0));
public static long getDiff(Calender cal1, Calender cal2)
{
return Math.abs(cal1.getTimeInMillis() - cal2.getTimeInMillis());
}
Check out secondsBetween( )
Creates a Seconds representing the number of whole seconds between the
two specified partial datetimes.
The two partials must contain the same fields, for example you can
specify two LocalTime objects.
Parameters:
start - the start partial date, must not be null
end - the end partial date, must not be null
Returns:
the period in seconds
JodaTime is using machine time inside. So to find miliseconds, you can use a constant storing LocalDateTime referring to Jan 1, 1970(Because of UNIX Time).
Unix time, or POSIX time, is a system for describing points in time,
defined as the number of seconds elapsed since midnight proleptic
Coordinated Universal Time (UTC) of January 1, 1970, not counting leap
seconds. Then calculate the difference between your DateTime.
I tried like this;
public static void main(String[] args) {
final LocalDateTime JAN_1_1970 = new LocalDateTime(1970, 1, 1, 0, 0);
DateTime local = new DateTime().withZone(DateTimeZone.forID("Europe/Amsterdam"));
DateTime utc = new DateTime(DateTimeZone.UTC);
System.out.println("Europe/Amsterdam milis :" + new Duration(JAN_1_1970.toDateTime(DateTimeZone.forID("Europe/Amsterdam")), local).getMillis());
System.out.println("UTC milis :" + new Duration(JAN_1_1970.toDateTime(DateTimeZone.UTC), utc).getMillis());
}
And the result is;
Europe/Amsterdam milis :1429695646528
UTC milis :1429692046534
And #leonbloy write here a good comment.
Your local and utc represent the same instants of time, (only with
different timezones attached). Hence, getMillis() (which gives the
"physical" time interval elapsed from the "instant" corresponding to
the unix epoch), must return the same value.
Joda is a perfect library but if you need the difference between 2 dates in milliseconds you just should calculate difference between getTime(). If you get wrong results you have some problems with timezones or so. Typically it works.
In Joda-Time 2, what is the difference between the three kinds of time spans:
Period
Interval
Duration
Why do we need three classes?
Which one performs better?
Why is dividing a Period or Duration or Interval instance not implemented? E.g. p = p.divideBy(2);
3 classes are needed because they represent different concepts so it is a matter of picking the appropriate one for the job rather than of relative performance. From the documentation with comments added by me in italics:
An interval in Joda-Time represents an interval of time from one millisecond instant to another instant. Both instants are fully specified instants in the datetime continuum, complete with time zone. Specific times are defined e.g. this might be the interval between 20:00:00GMT yesterday and 09:00:00GMT this morning.
A duration in Joda-Time represents a duration of time measured in milliseconds. The duration is often obtained from an interval. i.e. we can subtract start from end of an interval to derive a duration
A period in Joda-Time represents a period of time defined in terms of fields, for example, 3 years 5 months 2 days and 7 hours. This differs from a duration in that it is inexact in terms of milliseconds. A period can only be resolved to an exact number of milliseconds by specifying the instant (including chronology and time zone) it is relative to. e.g. consider the period of 1 year, if we add this to January 1st we will always arrive at the next January 1st but the duration will depend on whether the intervening year is a leap year or not. Similarly if we add 1 month to the 1st of a month then we will arrive at the 1st of the next month but the duration (in milliseconds) will vary based on the month in question
For question 3, A specific method to divide a duration is not really needed because we can always get the number of milliseconds from the duration as a long (using getMillis()), divide it and construct a new duration (using new Duration(long duration)).
Dividing a period doesn't really have a real meaning based on the definition of a period above. e.g. what is half a month? (its length would depend on which month).
To add to mikej's answer:
A Joda-Time duration is a "physical" time interval; eg:
12000 milliseconds <-- this is a duration
A Joda-Time interval is actually a pair of instants (start instant - end instant). An instant is, again, a "physical" concept, a point in the timeline. Eg (just a possible notation):
(2010/3/3 19:00:00.000 UTC ; 2010/3/3 20:00:00.000 UTC) <-- this is an interval
An interval, then, can be converted to a duration, but not the reverse.
Consider these two intervals:
I1=(2010/3/3 19:00:00.000 UTC ; 2010/3/3 20:00:00.000 UTC)
I2=(2010/3/3 21:00:00.000 UTC ; 2010/3/3 22:00:00.000 UTC)
As intervals, I1 and I2 are different, because the end-points are different; but if I convert them to durations, I get the same thing: 3600000 milliseconds.
(Math analogy: the intervals [10,12] and [95,97] are different intervals, but they have the same length: "interval length" maps to duration).
Finally, a period is a lapse of "civil time", expressed as a number of months, days, hours, etc. It does not -by itself- represent a "physical" interval, hence it can't be directly converted to a duration (months have variable lengths...).
This answers question 3: you can only divide by two a physical time (a duration).