Split Task Dates Incorrect on .mpp Export/Import

Hi everyone ! :slight_smile:

When I export a .mpp file using Aspose.Tasks (C#, .NET 8), the split task dates appear incorrect when I import the file back into my application or another tool. However, if I open the exported .mpp file in Microsoft Project and save it (without making any changes), the dates are then correct on re-import.

  • All dates (task start/end, split segments, calendar exceptions) are set with DateTimeKind.Utc before export.
  • The project calendar is assigned to all tasks and to the project.(working hours are 8am to 5pm)
  • The issue only affects split tasks; regular tasks and milestones are imported correctly.

For example, a split task (start : 2023-04-05 00:00:00, end : 2023-04-20 00:00:00) exported with following dates split :

  • Start : 2023-04-05 17:00:00, Finish : 2023-04-17 08:00:00

will be imported with these segments :

  • Start : 2023-04-05 00:00:00, Finish : 2023-04-04 17:00:00
  • Start : 2023-04-14 08:00:00, Finish : 2023-04-18 17:00:00

when it should have been :

  • Start : 2023-04-05 00:00:00, Finish : 2023-04-05 17:00:00
  • Start : 2023-04-17 08:00:00, Finish : 2023-04-19 17:00:00

As stated, when I open the mpp file and save it, then I have the correct segments.

Here are my (hopefully relevant) code snippets :

Export segments :

var currentTask = parentTask.Children.Add(task.Name);
currentTask.Set(Tsk.Calendar, _projectCalendar);
currentTask.Set(Tsk.Start, task.StartDate);
currentTask.Set(Tsk.IsEstimated, false);
currentTask.Set(Tsk.IsManual, false);
currentTask.Set(Tsk.Type, TaskType.FixedDuration);
currentTask.Set(Tsk.Duration, _project.GetDuration((long)task.Duration, TimeUnitType.Day));
var splitResourceAssignment = _project.ResourceAssignments.Add(currentTask, null);
splitResourceAssignment.TimephasedDataFromTaskDuration(_projectCalendar);
for (var i = 1; i < task.Segments.Count; i++)
{
var splitStartDate = task.Segments[i - 1].EndDate.Date;
var splitEndDate = task.Segments[i].StartDate.Date;
splitResourceAssignment.SplitTask(splitStartDate, splitEndDate, _projectCalendar);
}

Import :

List GetSegments(List<Aspose.Tasks.SplitPart> segmentsList, DateTime projectStartDate)
{
var segments = new List();
segmentsList = segmentsList.OrderBy(_ => _.Start).ToList();
for (var i = 0; i < segmentsList.Count; i++)
{
var segment = segmentsList[i];
var startDate = segment.Start;
var endDate = segment.Finish;
segments.Add(new MppImportSegment()
{
Duration = GetWorkingDaysNumberForDateRange(startDate, endDate),
Finish = endDate,
Index = i,
Lag = GetWorkingDaysNumberForDateRange(projectStartDate, startDate) - 1,
Start = startDate
});
}
return segments;
}

2 files attached in the zip : MSExports.zip (69,6 Ko)

  • Planning_Proj 01_ Plan=True, Bill=True, Acc=True_S3 : OK file (opened and saved with MS Project after export)
  • Planning_Proj 01_ Plan=True, Bill=True, Acc=True_S3 (1) : KO file (not opened and saved with MS Project after export)

Any idea is welcomed :slight_smile:
Have a nice day

Hi,
I’m still struggling with this issue.
Not sure why it’s been placed in Tasks Plugin, considering I’m not going through pdf change but exporting and then importing straight from the mpp file.
Any idea, help or workaround is highly appreciated :sweat_smile:

@Opus_Team ,
sorry for the delayed answer.

We cannot determine which values are set to a task’s properties given this code snippet.

Could you attach a compilable code example so we can reproduce the issue?

Hi @vasiliy.sinitsyn ,

Thanks for your answer :slight_smile: Not sure I can attach code as-is (not quickly) but all values are from Aspose.Tasks.Tsk class (Tsk.Start, Tsk.IsEstimated, …) and TaskType and TimeUnitType are Aspose enumerations. task.StartDate is a DateTime UTC.

With the dates of the example, it could translate to :

currentTask.Set(Tsk.Start, “2023-04-05T00:00:00”);
currentTask.Set(Tsk.IsEstimated, false);
currentTask.Set(Tsk.IsManual, false);
currentTask.Set(Tsk.Type, TaskType.FixedDuration);
currentTask.Set(Tsk.Duration, 4, TimeUnitType.Day));

Hope that helps :slight_smile:
If not, I’ll see if I can simplify our functions to have a “single function” I can attach here.

Have a nice day,

@Opus_Team ,
I’ve tried to assembly a code example from the pieces of code in this topic and it seems working correctly:

public void Test()
{
Project _project = new Project();
var parentTask = _project.RootTask.Children.Add("ParentTask");
var currentTask = parentTask.Children.Add("Task");
currentTask.Set(Tsk.Calendar, _project.Calendar);
currentTask.Set(Tsk.Start, new DateTime(2023, 4, 5, 8, 0, 0));
currentTask.Set(Tsk.IsEstimated, false);
currentTask.Set(Tsk.IsManual, false);
currentTask.Set(Tsk.Type, TaskType.FixedDuration);
currentTask.Set(Tsk.Duration, _project.GetDuration(4, TimeUnitType.Day));
var splitResourceAssignment = _project.ResourceAssignments.Add(currentTask, null);

Console.WriteLine("Before:");
Console.WriteLine(DumpTimephasedData(splitResourceAssignment.TimephasedData));
splitResourceAssignment.SplitTask(new DateTime(2023, 4, 5, 17, 0, 0), new DateTime(2023, 4, 17, 8, 0, 0), _project.Calendar);

Console.WriteLine("After:");
Console.WriteLine(DumpTimephasedData(splitResourceAssignment.TimephasedData));

Console.WriteLine("Split parts:");
foreach (var s in splitResourceAssignment.SplitParts)
{
    Console.WriteLine("{0} - {1}", s.Start, s.Finish);
}
{
                
}
}

private static string DumpTimephasedData(IList<TimephasedData> tds)
{
StringBuilder sb = new StringBuilder();
for (int index = 0; index < tds.Count; index++)
{
    var td = tds[index];

    sb.AppendFormat("[{0}] ", td.TimephasedDataType);
    sb.AppendFormat(CultureInfo.InvariantCulture, "{0:dd.MM.yy HH:mm}", td.Start);
    sb.Append(" - ");
    sb.AppendFormat(CultureInfo.InvariantCulture, "{0:dd.MM.yy HH:mm}", td.Finish);
    sb.Append(", ");

    sb.AppendFormat(CultureInfo.InvariantCulture, "{0:F2}", Math.Round(td.ValueToDuration.TotalHours, 2, MidpointRounding.AwayFromZero));

    if (index < tds.Count - 1)
    {
        sb.AppendLine();
    }
}

return sb.ToString();
}

We still need a compilable code example that allows us to investigate the issue.

Hi @vasiliy.sinitsyn ,

Thanks for your answer, sorry for the delayed response,

I have an issue with the code you provided : Splitparts is not recognized for ResourceAssignment (Assembly Aspose.Tasks, Version=25.11.0.0) so it does not compile. Am I missing something ?
image.png (50,3 Ko)

When providing the dates, they are all set to midnight, not 8am / 5pm. Thoses hours are set by Aspose when creating the file. I’ll try to change the hours before setting the tasks in MSProject to see if it improves things.

However my issue is not quite with the export in itself, it’s more with the import after exporting (but as stated, I’ll try to see if changing hours would fix it).

When I export a .mpp file using Aspose.Tasks (C#, .NET 8), the split task dates appear incorrect when I import the file back into my application or another tool. However, if I open the exported .mpp file in Microsoft Project and save it (without making any changes), the dates are then correct on re-import.

I’ll loop back as soon as I can.

Hi @vasiliy.sinitsyn ,

Tried to force all dates to MSProject hours (so every “start” is at 8 am and not midnight and every “end” is at 5pm and not midnight).

It worked ! :smiley:

I’ll run some more tests on my side to ensure it’s OK in every aspect but I think the SplitTask function may have issues calculating the segments when splitting at weird dates (which would explain why saving it in MSProject fixed it : it forced the segments to have normal hours).

Many thanks for your help, and may you have great holidays :slight_smile:

1 Like

@Opus_Team ,
yes, you’re right. It’s better to align dates to the working calendar in the way you’ve described.
Otherwise Aspose.Tasks and/or MS Project will try to fix the dates which can lead to unexpected recalculations.