Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ProjectCalendar getWork() method doesn't work properly with ranged calendar exceptions #125

Closed
AndrewLGoldman opened this issue Oct 8, 2019 · 7 comments

Comments

@AndrewLGoldman
Copy link

The ProjectCalendar() getWork(Date, Date, TimeUnit) method in the C# version of MPXJ appears to be buggy when you have Calendar exceptions with Ranges

For example, if I have a task that's 80 hours that runs from 12/23 - 1/8, but I have calendar holiday exceptions of 12/23 - 12/25 for Christmas and 1/1 for new years day, and I do something simple like:

java.util.Date date1 = new java.util.Date(2019, 12, 23);
java.util.Date date2 = new java.util.Date(2020, 1, 8);
Debug.WriteLine(myProjectCalendar.GetWork(date1, date2, net.sf.mpxj.TimeUnit.DAYS));

It returns 11 days instead of 9 days.

What I believe is happening:

  • Between 12/23 and 1/8, there's 13 days

  • The code sees that there's two exceptions, one starting on 12/25, and one only on 1/1 and says 13 - 2 = 11

  • In reality, I would expect the code to say hey, this exception day starts on 12/25 and continues through 12/27 so that's 3 days, and there's another exception day on 1/1 which is a total of 4 days, 13 total business days - 4 exception days = 9 days of work

Example .mpp file attached where I'm seeing this bug today:
Sample Schedule.zip

Would be great if this could be patched!

@ndarlington
Copy link

ndarlington commented Oct 9, 2019

Hey @AndrewLGoldman

To account for 1/8 and get 13 days, I think you may have to set your finish date to 1/9 (midnight) or else the data between 2020-01-08T00:00:00 to 2020-01-08T23:59:59 might be excluded from the calculations.

Next, I'm not sure what else you're doing to sanitize your input but java.util.Date(year, month, day) takes these ranges:

year = year - 1900
month = 0 to 11
day = 1 to 31

Have you inspected in the debug/immediate window which year and timezone your data is coming out?

new java.util.Date(2019-1900, 9, 8)
{Tue Oct 08 00:00:00 CDT 2019}

Now check out the year and month from this:

new java.util.Date(2019, 12, 23);
{Fri Jan 23 00:00:00 CST 3920}

Lastly, in case you (or your users) are not already GMT based, and since most calendars in schedulers in my experience are 'timezone agnostic', you may want to ensure you have this (or similar) in every project you are using:

java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone("GMT"));

I just say that from experience of having odd results due to hemispheres and timezone shifts.

Lastly, please note in your schedule's calendar that the new year's exception is for this year (i.e. already passed, 10 months ago), and is not inside the range 12/23/2019 - 01/08/2020 either. The xmas exception is in the right place though.

@ndarlington
Copy link

I will say that the feedback from the ProjectCalendarException From and To dates is confusing; but until I noticed that neither I or my testers / clients have run into an issue with the calendars and exceptions we converted (from another system) and then performed getWork() calls on to get the durations:

image

@ndarlington
Copy link

Digging further, and the strange appearance of the dates in the exceptions came about in the jump from v7.7.1 to v7.8.0.

That has also been the turning point that causes 5 out of 16 of my test cases in this area to fail (although as of yet we have not run into any issues with it in deployment).

The changes in v7.8.0 that caused this and the odd date appearances in the debug view I guess were from #78 then. This behavior and test failure rate seems to carry on through to v7.9.3.

Of course, we can't use anything lower than v7.8.4 nowadays anyway due to the problems from #107 so this will need understanding and reconciling some how.

I'm not sure if this also contributes to the OPs concerns or not - I apologize if it is unrelated, it's just what I dug up as there were some similar concerns showing.

@ndarlington
Copy link

If you correct the January 1 exception in your project file to be 1/1/2020 instead of 1/1/2019 and then run this, do you get the correct result then (excluding any other ramblings and suggestions I may have made above)?

java.util.Date date1 = new java.util.Date(2019 - 1900, 11, 23);
java.util.Date date2 = new java.util.Date(2020 - 1900, 0, 8, 23, 59, 59);
Debug.WriteLine(myProjectCalendar.GetWork(date1, date2, net.sf.mpxj.TimeUnit.DAYS));

My way of working previously worked GREAT across the globe until v7.8.0 and then seems hosed 🤣

This new way creates some huge challenges for me, but should work for you provided that:

  1. You make sure to include all the available working time for the range of dates you're interested in, I.e. up to 23:59:59 or up to 00:00:00 for midnight the following day - noting that doing so would then create discrepancies between GetWork and GetDuration calls.
  2. You will still need to take care of the year and month in new java Date() instantiations like I have
  3. You get the New Years Day exception in the right year ;)

Sorry for getting a bit derailed before - I hope this can now get it working for you.

@AndrewLGoldman
Copy link
Author

AndrewLGoldman commented Oct 10, 2019

Unfortunately this is indeed still an issue and I believe (looking at https://github.com/joniles/mpxj/blob/master/src/main/java/net/sf/mpxj/ProjectCalendar.java) that it's likely tied to the getRanges() method referenced in the getWork() method that takes a single date rather than a date range and doesn't consider all the dates in between a start date and end date when an exception is a range and not a single date in time

In the meantime I've coded a workaround unique to my project here essentially making an overridden getWork() method to do what I need it to do, but still I believe this would be a positive enhancement/bug fix to have for this library!

@ndarlington
Copy link

/**
* Retrieves the working hours on the given date.
*
* @param date required date
* @param cal optional calendar instance
* @param day optional day instance
* @return working hours
*/
private ProjectCalendarDateRanges getRanges(Date date, Calendar cal, Day day)

getRanges(...) isn't for pulling data from multiple dates, but for getting the working hours within the ranges of the shifts defined on a given day (ref: L1879).

I.e. rather than whole days as returned by getDuration(), getWork() has to look at the range of hours available on each day and see where it might intersect the dates (and times) provided in the getWork() parameters.

It took me a few days but I've been able to update my own code (c#) to get everything working again in the post-#78 releases. For globalization purposes I've had to roll my own extension methods for translating between c# DateTime and java.util.Date and that's where the changes had caused me problems.

… but still I believe this would be a positive enhancement/bug fix to have for this library!

This may be so :) Not my place to say. Maybe it will help if you have a small code snippet that shows both the problem and your workaround.

@joniles
Copy link
Owner

joniles commented Jun 9, 2020

@AndrewLGoldman apologies for the delay in replying. I've constructed a test case to the MPXJ units tests based on the scenario outlined in your original note:

https://github.com/joniles/mpxj/blob/master/src/test/java/net/sf/mpxj/junit/calendar/MultiDayExceptionsTest.java

This appears to generate the expected duration both in the Java and .Net versions. Feel free to modify that example if it helps to demonstrate the problem you were encountering and I can take a closer look.

@joniles joniles closed this as completed Jun 9, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants