Round down a DateTime based on a given Period using Joda-Time - java

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" ?

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

SimpleDateFormat tacks on hours for no reason?

What is the proper way to turn an integer of seconds into a formatted string of hh:mm:ss in Java?
For instance:
int Seconds = 650
String Time = 00:10:50
Right now I'm using this:
String Time = new SimpleDateFormat("hh:mm:ss").format(new Date((Seconds*1000)));
But this seems to tack on hours for no reason, and I'm guessing it's because I'm misusing Date or SimpleDateFormat, but I'm too inexperienced to know what's wrong. Or is there just a built in system for this that I don't know about.
EDIT: I should point out that I know I could use simple division to peel out the hours, then the remaining minutes, then the remaining seconds, and patch all three of those pieces into a string, but I was wondering if Java has a baked-in way to do this.
You can use TimeUnit class defined in java.util.concurrent package.
for eg you want to calculate hours:
long hours=TimeUnit.SECONDS.toHours(seconds);
similar methods are available for calulating days, hours, minutes.
but mind you this will give you direct conversion to hours, so you will end up having more than 24 hours. For a proper implementation you need to first calculate the days, the do the necessary maths and the give the remaining value for calculating hours. Lastly write a string as per your required format.
There is actually a (good) reason "to tack on hours".
Date(long date) constructor's JavaDoc:
Allocates a Date object and initializes it to represent the specified number of milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT.
So, if your JVM is not running in the GMT timezone you're off accordingly.
It's by design and logical, too:
new Date() is expected to be your current local time
new Date(0) is expected to be January 1, 1970, 00:00:00 GMT + local offset = local time
new Date(650*1000) is expected to be January 1, 1970, 00:10:50 GMT + local offset = local time
Try it the following way,
int seconds = 650;
long millis = seconds * 1000;
String format = String.format("%d:%d:%d",
TimeUnit.MILLISECONDS.toHours(millis),
TimeUnit.MILLISECONDS.toMinutes(millis),
TimeUnit.MILLISECONDS.toSeconds(millis)
- TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis))
);
System.out.println(format);
your code is correct .but problem is this gives you the time relative to start position .think if you run following code
String Time = new SimpleDateFormat("EEEE,MMMM d,yyyy hh:mm,a").format(new Date((0)));
System.out.println(Time);
output>>Wednesday,December 31,1969 04:00,PM //this 04 will be reason to your undesired output later
i think that is the minimum time for positive seconds .think when you give milliseconds 0 java gives you day as Wednesday and 4 hours ,so starting hours of java time is not 0.
so when you run
following code
String Time = new SimpleDateFormat("hh:mm:ss").format(new Date((Seconds*1000)));
output>> 04:10:50 //you expect 00:10:50
because `starting time+seconds`
04:00:00 + 00:10:50
but starting minutes and seconds are 0 so you only have problem about hours
if you can subtract starting hours then you will get desired output.
joda time library has interval so you can use it .take a look at this question

Best way to convert months into Milliseconds

I'm trying to convert a no of months into milliseconds
For example:
6 months = X milliseconds
There's no fixed answer to that, because it depends on which months those are - and indeed which year it is. Also potentially which time zone you're in, if you want to take account of that. (I'm assuming you mean the Gregorian calendar, by the way - different calendar systems have different month lengths.)
You could get some sort of "reasonable approximation" by assuming 365.25 days in a year, and saying that 6 months is half of that, then find out that many days in milliseconds. But it would only be an approximation.
For "how many milliseconds does it take to get from date/time X to 6 months later" you'd use an API (even Calendar would work for this particular case, although I'd recommend Joda Time or java.time in Java 8):
Set your start date/time, in the appropriate calendar and time zone
Fetch the "milliseconds since the Unix epoch" (which is easy enough to retrieve in any API) and remember it
Add 6 months
Fetch the "milliseconds since the Unix epoch" again, and subtract the earlier value from it
If you know exactly from when to when those 6 months reach, you can use a variety of ways to calculate the duration, using java.util.Calendar, JodaTime, or the JDK1.8 time API.
But if you don't have particular dates in mind, you can take an average duration for your month.
No API in the world can change that fact.
For example, the JDK1.8 time API uses this for the duration of a month in seconds: (from java.time.temporal.ChronoUnit)
MONTHS("Months", Duration.ofSeconds(31556952L / 12)),
31,556,952 is the number of a seconds in a year, based on a year that lasts 365.2425 days.
You can use the same number directly and get the same result as with the time API:
long months = 6;
long seconds = months * 31556952L / 12;
long milliseconds = seconds * 1000;
Result:
15,778,476,000
Calendar today = Calendar.getInstance();
Calendar sixMonthsAhead = Calendar.getInstance();
sixMonthsAhead.add(Calendar.MONTH, 6);
long differenceInMilis = sixMonthsAhead.getTimeInMillis() - today.getTimeInMillis();
You could also use...
sixMonthsAhead.add(Calendar.DATE, 180);
// or 183 days because 365 / 2 is approximately 183.
instead of...
sixMonthsAhead.add(Calendar.MONTH, 6);
for a more accurate result. But like Jon has mentioned, it will always vary depending on what day of the year it is.
The answer by Jon Skeet is correct.
Joda-Time
Assuming you could specify a pair of beginning and ending points on a time line, here is some example code using the Joda-Time 2.3 library.
This code grabs the current moment, adjusts to first of the month, and adjusts to first moment of that day. Then it adds 6 months. Joda-Time is smart about adding the months, taking into account leap year and various lengths of months. This span of 6 months is then represented as an Interval instance. From that we calculate the number of milliseconds. Note that count of milliseconds needs to be a long (64-bit) rather than an int (32-bit) we Java programmers more commonly use. Lastly, for fun, we see what this span of time looks like when formatted in the ISO 8601 standard’s "Duration" format.
DateTimeZone dateTimeZone = DateTimeZone.forID( "Europe/Paris" ); // Better to specify a time zone than rely on JVM’s default.
DateTime start = new DateTime( dateTimeZone ).withDayOfMonth( 1 ).withTimeAtStartOfDay();
DateTime stop = start.plusMonths( 6 );
Interval interval = new Interval( start, stop );
long milliseconds = interval.toDurationMillis(); // A long, not an int.
Period period = interval.toPeriod(); // For fun… ISO 8601 standard's Duration format.
Dump to console…
System.out.println("start: " + start );
System.out.println("stop: " + stop );
System.out.println("interval: " + interval );
System.out.println("milliseconds: " + milliseconds );
System.out.println("period: " + period );
When run…
start: 2014-04-01T00:00:00.000+02:00
stop: 2014-10-01T00:00:00.000+02:00
interval: 2014-04-01T00:00:00.000+02:00/2014-10-01T00:00:00.000+02:00
milliseconds: 15811200000
period: P6M

Does Jodatime have a method to get the months and days since some older date?

I would like to know exactly how many months and days(possibly years) some older date is from today. Is there a method to do that?
I know how to get the difference of the months, I know how to get the difference in days. But I am unable to get the months and the days.
Ex:
old = '2013-03-04'
now = '2013-04-17'
so the result im looking for is something like 1 month(s) and 13* day(s)
*maybe its 12 im not every sure.
This can be done by using Period in JodaTime.
For example,
LocalDate old = new LocalDate(2013, 3, 4);
LocalDate now = new LocalDate(2013, 4, 17);
Period p = new Period(old, now, PeriodType.yearMonthDay());
To get the months, use p.getMonths(), to get the days p.getDays().
The result of the example is 1 month, 13 days.
Yes, see the documentation of intervals:
Intervals
An interval in Joda-Time represents an interval of time from one
instant to another instant. Both instants are fully specified instants
in the datetime continuum, complete with time zone.
Intervals are implemented as half-open, which is to say that the start
instant is inclusive but the end instant is exclusive. The end is
always greater than or equal to the start. Both end-points are
restricted to having the same chronology and the same time zone.
Two implementations are provided, Interval and MutableInterval, both
are specializations of ReadableInterval.

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