I need to find the no. of months between 2 dates, both months inclusive. I am using ChronoUnit.Months.between, but am getting unexpected results.
Code works for : Jan 31 - July 31
Fails for: Jan 31 - June 30
CODE THAT FAILS:
import java.time.LocalDate;
import static java.time.temporal.ChronoUnit.MONTHS;
public class HelloWorld{
public static void main(String []args){
LocalDate startDate=LocalDate.of(2018,01,31);
LocalDate endDate=LocalDate.of(2018,6,30);
//Both dates inclusive, hence adding 1
long noOfMonths = (MONTHS.between(startDate,endDate)) + 1L;
System.out.println(" ********* No. of months between the two dates : " + noOfMonths);
}
}
Expected: 6 (Jan, Feb, Mar, Apr, May, Jun)
Actual: 5
Code that works:
import java.time.LocalDate;
import static java.time.temporal.ChronoUnit.MONTHS;
public class HelloWorld{
public static void main(String []args){
LocalDate startDate=LocalDate.of(2018,01,31);
LocalDate endDate=LocalDate.of(2018,7,31);
//Both dates inclusive, hence adding 1
long noOfMonths = (MONTHS.between(startDate,endDate)) + 1L;
System.out.println(" ********* No. of months between the two dates : " + noOfMonths);
}
}
Expected: 7 (Jan, Feb, Mar, Apr, May, Jun,July)
Actual: 7
Do you see the same behaviour? Is there any alternate way of doing this?
Thanks
Half-Open
The other two Answers by davidxxx and by Ole V.V. are both correct and informative.
I want to add about an alternative approach commonly used in date-time work for defining a span of time: Half-Open. In Half-Open approach, the beginning is inclusive while the ending is exclusive. So the months of the first half of the year (Jan, Feb, Mar, Apr, and Jun) are tracked as starting on January 1 and running up to, but not including, July 1.
Sometimes we intuitively use this approach in our daily life. For example, if a classroom of children breaks for lunch from 12:00 noon to 1 PM, the students are expected to be in their seats before the bell strikes at 1 PM. Lunch break runs up to, but does not include, 1 PM.
I believe you will find consistent use of the Half-Open approach makes your code easier to read, debug, and maintain.
The modern java.time classes use this approach in defining a span of time.
LocalDateRange
The Answer by Ole V.V. wisely suggests you look at YearMonth class to help your work.
Another helpful class is from the ThreeTen-Extra project: org.threeten.extra.LocalDateRange. You can represent your entire date range in a single object.
You can instantiate with a start date and a Period of six months.
LocalDateRange ldr = LocalDateRange.of(
LocalDate.of( 2018 , Month.JANUARY , 1 ) ,
Period.ofMonths( 6 )
) ;
Or specify start and stop dates.
LocalDateRange ldr = LocalDateRange.of(
LocalDate.of( 2018 , Month.JANUARY , 1 ) ,
LocalDate.of( 2018 , Month.JULY , 1 ) // Half-open.
) ;
While I don't recommend it, if you insist on using the Fully-Closed approach where beginning and ending are both inclusive, the LocalDateRange does offer LocalDateRange.ofClosed().
LocalDateRange ldr = LocalDateRange.ofClosed( // `ofClosed` vs `of`.
LocalDate.of( 2018 , Month.JANUARY , 1 ) ,
YearMonth( 2018 , Month.JUNE ).atEndOfMonth() // Fully-closed.
) ;
You can get a count of months from a LocalDateRange via the Period class.
LocalDateRange ldr = LocalDateRange.of(
LocalDate.of( 2018 , Month.JANUARY , 1 ) ,
Period.ofMonths( 6 )
) ;
Period p = ldr.toPeriod() ;
long totalMonths = p.toTotalMonths() ; // 6
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
davidxxx in the other answer has already explained why your code gave an unexpected result. Allow me to add that if you’re interested in months rather than dates, the YearMonth class is the good and correct one for you to use:
YearMonth startMonth = YearMonth.of(2018, Month.JANUARY);
YearMonth endMonth = YearMonth.of(2018, Month.JUNE);
// Both months inclusive, hence adding 1
long noOfMonths = ChronoUnit.MONTHS.between(startMonth, endMonth) + 1;
System.out.println(" ********* No. of months between the two months (inclusive): " + noOfMonths);
Output:
********* No. of months between the two months (inclusive): 6
This obviously also eliminates the problems coming from months having unequal lengths.
If you already got LocalDate objects, it’s probably worth converting:
LocalDate startDate = LocalDate.of(2018, Month.JANUARY, 31);
YearMonth startMonth = YearMonth.from(startDate);
As an aside, do not use two-digit 01 for a month. For January it works, but neither 08 nor 09 will work for August or September, so it’s a bad habit to acquire. Java (and many other languages) understand numbers beginning with 0 as octal numbers.
In terms of calendar, your question is understandable : the two comparisons represent 1 complete month.
But java.time.temporal.ChronoUnit.between doesn't reason in this way but in terms of complete units.
And the result is expected according to its javadoc :
The calculation returns a whole number, representing the number of
complete units between the two temporals. For example, the amount in
hours between the times 11:30 and 13:29 will only be one hour as it is
one minute short of two hours.
The LocalDate.until javadoc that is used under the hood by ChronoUnit.between() is more explicit for your case and also gives an interesting example about between() used with MONTHS as TemporalUnit:
The calculation returns a whole number, representing the number of
complete units between the two dates. For example, the amount in
months between 2012-06-15 and 2012-08-14 will only be one month as it
is one day short of two months.
In your working example :
LocalDate startDate=LocalDate.of(2018,01,31);
LocalDate endDate=LocalDate.of(2018,7,31);
long noOfMonths = MONTHS.between(startDate,endDate);
You have 31 days of month / 31 days of month, on 6 months => 6 months.
In your failing example :
LocalDate startDate=LocalDate.of(2018,01,31);
LocalDate endDate=LocalDate.of(2018,6,30);
long noOfMonths = MONTHS.between(startDate,endDate);
You have 30 days of month / 31 days of month, on 5 months
=> 4 months + 1 month short of 1 day <=> 4 months (rounded).
If you would write something like :
LocalDate startDate=LocalDate.of(2018,01,30);
LocalDate endDate=LocalDate.of(2018,6,30);
long noOfMonths = MONTHS.between(startDate,endDate);
You would have 30 days of month / 30 days of month, on 5 months => 5 months .
About your question :
Is there any alternate way of doing this?
The most simple : set the day of month to 1 or to any same number in both dates.
If it is not suitable for your requirement, you could check if the two dates are the last day of their current month, in this case, set it to the same month value.
You could write something like :
int lastMonthStart = startDate.getMonth()
.length(startDate.isLeapYear());
int lastMonthEnd = endDate.getMonth()
.length(endDate.isLeapYear());
if (startDate.getDayOfMonth() == lastMonthStart && endDate.getDayOfMonth() == lastMonthEnd) {
startDate = startDate.withDayOfMonth(1);
endDate = endDate.withDayOfMonth(1);
}
Other solution:
int getInclusiveMonthsDiff(LocalDate startDate, LocalDate endDate) {
return (endDate.monthValue + endDate.year * 12) - (startDate.monthValue + startDate.year * 12) + 1
}
Related
I am trying to determine the interval between two dates that I create using DateComponents. If I make the first date 1 year prior to the second, I get 365 days, 0 hours, 0 minutes and 0 seconds. If I make the dates further apart (400 years here), suddenly my date is off by 11 minutes 56 seconds. Here is the code:
import Foundation
var mycal = Calendar(identifier: .iso8601)
var datum = DateComponents(year:1600, month:1, day:1, hour:12, minute:0,
second:0)
let j2000 = DateComponents(year:2000, month:1, day:1, hour:12, minute:0,
second:0)
let datum_date = mycal.date(from: datum)
let j2000_date = mycal.date(from: j2000)
let interval = mycal.dateComponents([.day, .hour, .minute, .second], from:j2000_date!, to:datum_date!)
print("Datum: \(datum_date!)") //1600-01-01 19:48:04 +0000
print("j2000: \(j2000_date!)") //2000-01-01 20:00:00 +0000
Note the next-to-last line: Comments show what the print produces. I've tried it with the Gregorian calendar too, same problem. I'm not sure exactly how far back the inconsistency occurs, I've gone back far enough to produce and it sometimes seems to "stick" as I change the code moving closer in time again. Strangely, the "interval" appears to show the correct amount of days(here -146097), but the date shown is incorrect and I will likely need that in my calculations. Anyone have any ideas?
The difference could be related to leep year adjustments but that would give a difference of 11 minutes and 14 seconds (there'd still be 40 seconds unaccounted for, 26 of which could be leep seconds).
see: https://www.infoplease.com/leap-year-101-next-when-list-days-calendar-years-calculation-last-rules
In Theory, if you compute a multi-year time difference with a precision of minutes and seconds, you should get variations of 5 hours 48 minutes and 46 seconds every 3 out of four years and get within 11 minutes and 14 seconds on the fourth year. I don't know how macOS (Unix) deals with that there there is probably a bunch of considerations that they need to take into account (especially beyond 400 year where that 11 minutes 14 seconds gets adjusted).
If that level of precision is required by your use case, I would suggest reading up on the minute details of time calculations. Given that dates are stored internally as a number of seconds, going back to a precise day and time over these long periods must require some special math acrobatics.
See Apple's documentation here: https://developer.apple.com/reference/foundation/nscalendar
I run discrete event simulations which cover more than a year. All events need to be tracked on a second time scale.
Now, I want to schedule the events according to a timetable, calculate overtime, ... and compare it to the real labor time in the end.
I thought whether there is an elegant way to implement schedules in R such that the following tasks/questions can be answered within one line of code:
Schedule work according to a timetable.
Given a timetable, how many labor hours do we expect per day/week/month/... Calculate the overtime per day/week/month/...
Create simple plots to visualize working time, overtime, ... and compare it to the planned work load.
On an abstract level, assume all of the timetables are given more or less in the following way:
Name Days_of_year Available Time
Name1 Jan-07 – Oct-18 Mon, Tues, Wed, Thurs, Fri, Sat, Sun 8:45 - 18:00
Name2 Jan-01 – Dec-31 Mon, Tues, Wed, Thurs, Fri 20:00 - 7:00
I am not looking for obvious answers like "create an arry and somehow you get everything out of that" but an implementation that:
Makes schedules independent of time resolution (days, hours, minutes, seconds).
Schedules/timetables are not stored with redundant information.
Any result (e.g. total year labor, ...) should be easily transferred to any given unit.
Easy filter possibilities by month, wday, ...
Ideally, calendar functionalities (holidays, ...) can be easily used.
Perhaps as part of lubridate?
Use case view ("schedule functions" indicated by s_):
Time: s_hm(„7:45“), s_md(„Jan-23“) or s_md(„01-23“). Please note that those "times" do not make sence as dates or date-times, but in the framework of schedules they perfectly make sence!
Interval: s_interval. See first thoughts below.
Duration: s_interval/dhours(1) => Duration in hours
Functions:
Max(), Min() on any s_interval
%s_in%
Given a date, when does somebody work:
date(s) %s_in% s_interval
=> return TRUE/FALSE (array)
Given a schedule, when is somebody available in an interval:
s_interval %s_in% interval
=> return class interval - a subset of interval!
Given an interval, was the labor according to schedule? Calculate the idle-, over- and working time:
interval %s_in% s_interval
=> return class s_interval
Given two schedules, consider them as one unit. How is idle-, over- and working time distributed:
(s_interval1 %+% s_interval2)
=> return class s_interval
Some thoughts:
schedule in case of planning labor time:
s_interval $tzone UTC
$daysofyear 286 days (Better alternative: format month-day e.g. Jan-07 – Oct-18)
$wday Mon, Tues, ... Sun (According to the start time of a shift/lecture...)
$time 12 h (Better alternative: format time span* e.g. 8:00 – 18:00. NOT period which results in hours)
$overtime 0 h (A priori, a schedule does not plan overtime)
$idle 0 h (A priori, a schedule only plans labor time)
schedule when real time-data is available:
s_interval$tzone UTC
$daysofyear 286 days (Better alternative: format month-day e.g. Jan-07 – Oct-18)
$wday c(Mon, Tues, Wed, ...) (According to the start time of a shift/lecture...)
$time c(12 h, 1 h, 0.5 h, ...) (Better alternative: format time span* e.g. 8:00 – 18:00. NOT period which results in hours)
$overtime c(2 h, 0 h, -1 h, ...) total time = time + overtime. Array of lubridate intervals*?
$idle c(4 h, 8 h, 0h, ...) pure working_time = time – idle + overtime. Array of lubridate intervals*?
* Use intervals not periods such that interval/dhours(1) possible.
If the tzone is the same we could calculate e.g.
s_interval1 %+% s_interval2 =
s_interval$tzone UTC
$daysofyear 286 days, 123 days
$wday c(Mon, Tues, Wed, ...), c(Sat, Sun)
$time c(12 h, 1 h, 0.5 h, ...), c(-1 h, 2.5 h)
$overtime c(2 h, 0 h, -1 h, ...), c(0 h, 5 h)
$idle c(4 h, 8 h, 0h, ...), c(4 h, 8 h)
There are related posts about this topic visualization of schedules with some interesting answers concerning timeline and gantt packages and how to filter dates.
However, they are not very conclusive.
As I am quite new to R, I don't know, how to start such a task the best way and understanding the structure of a package like lubridate is quite advanced...
I developed a package which gives the desired functionality (even with plots). At the moment, the package is private, but let me know if you are interested.
If I have everyday datetime - how to find out, the event has already occurred or not, by subtraction with datetime.now()
Let we had everyday meeting at 15:35. Today John came earlier - at 12:45, but Alex was late for 2 h. and 15 min. (came at 17:40).
meet_dt = datetime(year=2015, month=8, day=19, hour=15, minute=35)
john_dt = datetime(year=2015, month=8, day=19, hour=12, minute=45)
alex_dt = datetime(year=2015, month=8, day=19, hour=17, minute=40)
print(meat_dt - john_dt) # came before > 2:50:00
print(meat_dt - alex_dt) # came after > -1 day, 21:55:00
If I take away from the big date less - then everything is fine, but conversely I recive -1 day, 21:55:00 why not -2:15:00, what a minus day?
Because timedeltas are normalized
All of the parts of the timedelta other than the days field are always nonnegative, as described in the documentation.
Incidentally, if you want to see what happened first, don't do this subtraction. Just compare directly with <:
if then < datetime.datetime.now():
# then is in the past
After a long day of research,
Is anybody knows how to convert a 19 digits time stamp from the metadata of .zvi file (produce by axiovision, Zeiss) to a real time format ? (The output probably includes milliseconds)
An example time-stamp is: 4675873294709522577
Thanks !
Arnon
Matlab solution:
The main issue is not the x2mdate conversion (which simply adds the number of days between the year zero, when Matlab starts counting, and the year 1900, when Excel/zvi starts counting), but the same class issue as described above. This conversion to double can be done with typecast in Matlab:
myZVI = 4675946358764751269;
timestampDouble = typecast(int64(myZVI),'double');
myTime = datestr(timestampDouble + 693960, 'dd-mmm-yyyy HH:MM:SS.FFF');
693960 is the number of days between year zero and 1900; if you don't need an absolute date but just the difference between two timestamps, you don't even need this; for instance the interval between two of my video frames can be calculated like this:
myZVI2 = 4675946358764826427;
timestampDouble2 = typecast(int64(myZVI2),'double');
myTimeDifference = datestr(timestampDouble2 - timestampDouble,'SS.FFF');
hope this helps:-)
This is a Microsoft OLE Automation Date. But you've read it as a 64-bit long integer instead of the 64-bit double that it should be.
You didn't specify a language, so I will pick C#:
long l = 4675873294709522577L;
byte[] b = BitConverter.GetBytes(l);
double d = BitConverter.ToDouble(b, 0);
Debug.WriteLine(d); // 41039.901598693
DateTime dt = DateTime.FromOADate(d);
Debug.WriteLine(dt); // 5/10/2012 9:38:18 PM
More information in this thread.
An OLE Automation Date is basically the number of whole 24-hour days since 1/1/1900 without any particular time zone reference.
I am going to find difference between two times but I am not getting what I want!!!
I have 2 timeEdit components in a form
here is my code:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TTime time1=StrToTime(t1->Text);
TTime time2=StrToTime(t2->Text);
//t1->Text=time2-StrToTime("3:00");
ShowMessage((time2-time1).TimeString());
}
If I set t1 = 02:00
and set t2 = 01:00
it shows 1:00
but I expect 23:00
that is 01:00 - 02:00 should be 23:00
where I am wrong ?
You are not taking into account how TTime is encoded. TDateTime is a Double, where the integral portion contains the number of days since Dec 30 1899, and the fractional portion contains a percentage of a 24-hour day (this information is stated in the C++Builder documentation). TTime is just the fractional portion of a TDateTime with its integral portion ignored. Because of this encoding, performing such seemingly simple mathematical operations on date/time values does not usually produce the kind of result you are expecting.
02:00 (2 AM) is represented as 0.083333333333, and 01:00 (1 AM) is represented as 0.041666666667. You are subtracting 2 AM from 1 AM, expecting it to subtract 2 hours to produce 11 PM (which is represented as 0.958333333333333). Subtracting 0.083333333333 from 0.041666666667 produces -0.041666666667. Ignoring the integral portion (the date), the fractional portion is a positive value from 0.0 (midnight), so -0.041666666667 is equivilent to 0.041666666667, which is 1 AM.
In order for your subtraction to work correctly, the starting time needs a positive integral portion (date) attached to it so the result contains the correct fractional portion, eg:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TTime t = (1.0 + StrToTime("01:00")) - StrToTime("02:00");
// (1.0 + 0.041666666667) - 0.083333333333
// = 1.041666666667 - 0.083333333333
// = 0.95833333333
ShowMessage(t.TimeString());
}