How to convert a Duration to Period in Joda? - java

Can someone explain why the following tests fails?
#Test
public void testDuration() {
Duration duration = Duration.standardDays(1);
assertTrue(duration.getStandardMinutes() == 1440); //OK
assertTrue(duration.toPeriod().getMinutes() == 1440); //NOK. result = 0
assertTrue(new Period(duration.getMillis()).getMinutes() == 1400); //NOK. result = 0
}

A Period in JodaTime represents a "set of duration field values". Therefore, the methods getMinutes(), getHours(), ... are returning the value of those fields rather than computing the minutes, hours, ...
Furthermore, the conversion from a Duration sets the fields according to a PeriodType and a chronology.
From the API (http://joda-time.sourceforge.net/apidocs/org/joda/time/ReadableDuration.html#toPeriod%28%29):
Period toPeriod()
Converts this duration to a Period instance using the standard period type and the ISO chronology.
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.
If the duration is small, less than one day, then this method will perform as you might expect and split the fields evenly. If the duration is larger than one day then all the remaining duration will be stored in the largest available field, hours in this case.
This means, as a Duration's day has exactly 24 hours, all information is stored in the hours field and minutes stays at 0.
See this question for a good explanation of the differences between Interval, Duration and Period.

See here
A period of 1 day is not equal to a period of 24 hours, nor 1 hour equal to 60 minutes. This is because periods represent an abstracted definition of a time period (eg. a day may not actually be 24 hours, it might be 23 or 25 at daylight savings boundary).

Related

java.time: the simplest way to get a difference in hours between two LocalTime objects

I want to get a difference in hours between a current time in a specific timezone and UTC time. I tried this:
LocalTime time = LocalTime.now();
System.out.println(time); //21:05:42:764
LocalTime utcTime = LocalTime.now(ZoneId.of("UTC"));
System.out.println(utcTime); //18:05:42:769
System.out.println(Duration.between(utcTime, time).getSeconds()/3600); //2
System.out.println(Duration.between(time, utcTime).getSeconds()/3600); //-3
Why is the difference between the last two results and are there better ways to do it? I need to get the number 3.
Why is the difference between the last two results
The reason that you're getting different results for the two computed durations is a combination of the fact that there is some tiny amount of time elapsed between the two recordings and the fact that the duration start time is included in the range but the duration end time is not.
Consider these times instead: 6:00:00:001 vs 8:00:00:000. Here it is very obvious that we're only exactly one millisecond off of two hours, but when we think about seconds we're either going to get 7199 or -7200. When we then do integer math (i.e. divide by 3600), we're going to get 1 or -2.
If it weren't for the one extra millisecond on the first timestamp, the absolute value of the two would be identical.
Duration is the wrong class. There is zero duration between "now" in one time zone and "now" in another. For a fun but memorable way to think about this, see here.
You appear to be seeking to know the current offset from UTC for a given time zone. You can use the ZonedDateTime class for that:
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Kolkata"));
ZoneOffset offset = zdt.getOffset();
int offsetMinutes = offset.getTotalSeconds() / 60;
double offsetHours = ((double) offsetMinutes) / 60;
System.out.println(offsetHours); // 5.5
You could also just use ZonedDateTime.now() on the first line, if you want to use the computer's current time zone.
With regard to LocalTime - that is just the time portion (hours, minutes, seconds, and smaller). Since there is no date associated, you can't necessarily determine which time zone offset it belongs to. There is more than one date that "today" going on at any given moment. Time zone offsets range from UTC-12 to UTC+14, so there are indeed values where the same time of day is happening on two different dates somewhere on the planet.
As an example, 08:00:00 in Hawaii (Pacific/Honolulu) on 2019-01-01 is also 08:00:00 in Kiribati (Pacific/Kiritimati), but on 2019-01-02 - the following date! (Reference here.) Thus, if you had a LocalTime object with 08:00:00 and it was 08:00:00 in one of those two zones, you'd not be able to tell which one it was, or what the corresponding UTC offset should be.
Also, keep in mind that time zone offsets are not limited to whole hours. There are present-day time zones with half-hour and 45-minute offset. Historically, there have been others.
Lastly, keep in mind that an offset is not necessarily enough to identify a time zone. Many time zones share offsets at some points in time, but differ in others. See "Time Zone != Offset" in the timezone tag wiki.
Oh, and about your results getting 2 in one direction and -3 in the other - this is a rounding error due to your integer division. If you print out the seconds value, you'll notice they are one second apart (10799, vs -10800). Dig closer and you'll find that "now" included fractional seconds that were truncated with the getSeconds call. (You called .now() twice, so they were at slightly different times.)

Joda time, Period to total millis

I'm trying to get the total amount of Milliseconds (not the millis field) from the Period object instance. I've tried multiple conversions, as I couldn't find any method easily giving it.
Has anyone ever needed that and managed to retrieve it ?
(I need this for my patch, to figure out a negative period; negative millis = negative period.)
You can't get the millis directly from a Period, since fields like months and years are variable in terms of milliseconds.
In order to make this work, you need to supply a "baseline" instant from which Period can calculate that actual millisecond duration.
For example, the Period.toDurationFrom and Period.toDurationTo methods take such a baseline instant, and calculate a Duration object, which you can then obtain the millis.
The Javadoc for toDurationFrom says:
Gets the total millisecond duration of this period relative to a start instant.
This method adds the period to the specified instant in order to calculate the duration.
An instant must be supplied as the duration of a period varies. For example, a period of 1 month could vary between the equivalent of 28 and 31 days in milliseconds due to different length months. Similarly, a day can vary at Daylight Savings cutover, typically between 23 and 25 hours.
So you need to pick an appropriate baseline instant for your application.
If you want to get the millis from a specific time it can be done by using the plus() or minus() methods of the DateTime class.
e.g. getting the millis from now
DateTime start = new DateTime(); //NOW
DateTime end = start.plus(<your period>);
long millis = end.getMillis() - start.getMillis();
Using Joda time 2.3, it is:
toStandardDuration().getMillis()

Joda time, Period from millis

I'm trying to set my Period object using the "millis" constructor and have all the relevant fields be updated accordingly (years, months, weeks, days, hours, minutes, seconds)
That is, using the following code:
mPeriod = new Period(millis, PeriodType.standard());
doesn't fill all the relevant fields accordingly.
only weeks and minutes (for input millis of 1325965615539)
can someone please help me figure this thing out ?
You can normalize it using Period#normalizedStandard();
However, there's no way Period can "fill in all the relevant fields", because it can't make assumptions about the number of days in months or years. The best it can do is to turn it into weeks, days, and time fields.
The Javadoc touches on this, but not in great detail:
If the period contains years or months, then the months will be
normalized to be between 0 and 11. 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.
(Emphasis mine)
If you need it to normalize into years and months, you need to construct the Period with values in those fields.
Alternatively, you could use a Duration:
Construct a Duration with milliseconds
Use Duration#toPeriodFrom(ReadableInstant) to create a Period starting at a given instant in time (e.g. new DateTime()). According to the docs, this should work:
This conversion will determine the fields of a period accurately. The
results are based on the instant millis, the chronology of the
instant, the standard period type and the length of this duration.
Here's what normalize() should do to your millisecond input:
import org.joda.time.Period;
class Normalize {
public static void main(String[] args) {
Period period = new Period(1325965615539L);
System.out.println(period);
System.out.println(period.normalizedStandard());
}
}
// outputs
PT368323H46M55.539S
P2192W2DT19H46M55.539S
Notice the second line has been normalized, but only up to weeks.

How to compare Timestamp to current time less a certain amount of minutes in android

i have a sql.Timestamp object and i need to compare it to the current time minus 45 minutes, how would i go about doing this on Android?
i know i should use the compareTo Method as Timestamp extends java.util.Date , what i dont know how to do is create the date object 45 mins less than current time.
This might be an overkill, but you could create two Calendar instances, one with your timestamp and one with current time, then subtract 45 minutes from the latter and compare them.
Calendar then = Calendar.getInstance();
then.setTime(timestamp);
Calendar now = Calendar.getInstance();
now.add(Calendar.MINUTE, -45);
int diff = now.compareTo(then);
// ...
Timestamp (and Date) objects can be constructed by passing a value of type long as a constructor, with said value representing the number of milliseconds since January 1, 1970.
A millisecond is 1/1000th of a second, so 45 minutes would be 45 * 60 * 1000 milliseconds. Simply create two Timestamp instances - one using the long value that represents the time you want to compare (if you don't already have a Timestamp instance) and one using the long value that represents the time 45 minutes ago (System.currentTimeMillis() - (45 * 60 * 1000)), then compare those.

Joda-Time: what's the difference between Period, Interval and Duration?

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).

Categories