[Canonical page](<https://docs.dhtmlx.com/planning-engine/rust/how-to/load-all-task-dates/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# Load all task dates

Get the start, end and duration of every scheduled task from one solve, for example to fill your UI when a project first opens.

By default, `updates` in a [`ScheduleResult`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/schedule-result.md>) is sparse: it lists only the tasks that moved. Set [`SolveOptions::include_unchanged`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/solve-options.md>) and the same solve also reports the tasks that didn't move. You don't need a separate call.

## Steps

1. Build the solve options with `include_unchanged` set to `true`:

   ```rust
   use plen_core::SolveOptions;

   let options = SolveOptions {
       include_unchanged: true,
       ..Default::default()
   };
   ```

2. Pass the options to the solve that loads the data. Use [`Project::solve`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md>) with `Some(data)` for a long-lived project, or [`schedule`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/schedule.md>) for a one-shot job.

3. Read every entry of `result.updates`. Each entry has `start_date`, `end_date` and `duration` set.

The full example below schedules three tasks. `review` doesn't move, but it's still reported:

```rust
use plen_core::{DateTime, LinkData, LinkType, Project, ProjectData, SolveOptions, TaskData};

fn task(id: &str, start: DateTime, duration: f64) -> TaskData {
    let mut t = TaskData::new(id);
    t.start_date = Some(start);
    t.duration = Some(duration);
    t
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let jan1 = DateTime::from_ymd_hm(2026, 1, 1, 0, 0).ok_or("invalid date")?;
    let jan5 = DateTime::from_ymd_hm(2026, 1, 5, 0, 0).ok_or("invalid date")?;

    let mut data = ProjectData {
        tasks: vec![
            task("design", jan1, 2.0),
            task("build", jan1, 1.0),
            task("review", jan5, 1.0),
        ],
        links: vec![LinkData {
            id: "design-build".into(),
            source: "design".into(),
            target: "build".into(),
            link_type: LinkType::Fs,
            lag: 0.0,
            lag_unit: None,
        }],
        ..Default::default()
    };
    // 24/7 time keeps the example dates easy to check.
    data.config.use_calendars = false;

    let options = SolveOptions {
        include_unchanged: true,
        ..Default::default()
    };

    let mut project = Project::default();
    let result = project.solve(Some(data), &options)?;

    for m in &result.updates {
        if let (Some(start), Some(end), Some(duration)) = (m.start_date, m.end_date, m.duration) {
            println!("{} {}..{} {}", m.id, start, end, duration);
        }
    }
    Ok(())
}
```

## Get the full list from an already solved project

To reload every date later, call [`solve(None, ...)`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#solve>) with the same options. On a solved project with no pending edits, the call doesn't replan or change the accepted state, and `baseline_id` stays the same:

```rust
let all_dates = project.solve(None, &options)?;
```

Switch back to sparse results for the edits that follow. `SolveOptions::default()` returns only what changed:

```rust
let next = project.solve(None, &SolveOptions::default())?;
```

The result cache uses the whole [`SolveOptions`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/solve-options.md>) as its key. An `include_unchanged` call never gets a cached sparse result, and a sparse call never gets a cached full one.

## What is and isn't reported

- The result includes every scheduled task: leaf tasks, summaries and milestones. Check [`Mutation::kind`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/results/mutation.md>) to tell them apart.
- Tasks with `unscheduled: true` are never reported.
- `leveling_delay` and `pauses` never appear, because a solve doesn't change them.
- `baseline_id` follows real changes only. Asking for unchanged tasks doesn't create a new baseline.
- With [`subtree`](<https://docs.dhtmlx.com/planning-engine/rust/how-to/reschedule-a-branch.md>), the result holds every task in the subtree, including the subtree tasks' ancestors. Tasks outside the subtree appear only when a sparse subtree solve would report them too, because they changed or were edited.
- On a project with a dependency cycle, nothing is scheduled. The result still lists every scheduled task, in model order, with its dates and duration as stored. `subtree` is ignored, and no entry has a `reason`, even with `reasons: true`.
- [`Project::preview`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md>) and [`Project::level`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md>) always return sparse updates.

## Result

The example prints one line per task, including `review`, which didn't move (the lines may come in a different order):

```text
design 2026-01-01 00:00..2026-01-03 00:00 2
build 2026-01-03 00:00..2026-01-04 00:00 1
review 2026-01-05 00:00..2026-01-06 00:00 1
```

Without `include_unchanged`, the same solve reports only `build`.

See [Reading results](<https://docs.dhtmlx.com/planning-engine/rust/scheduling/reading-results.md>) for how to write the dates back into your model.
