[Canonical page](<https://docs.dhtmlx.com/planning-engine/rust/api/core/evaluate/shift/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# Shift

A finish date in an evaluated stage and its working-time distance from the same finish in the baseline. It appears as [`Finish::project`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/evaluate/finish.md>) and as [`TaskFinish::finish`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/evaluate/task-finish.md>) inside [`Metrics`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/evaluate/metrics.md>). It isn't built by hand.

## Declaration

```rust
#[derive(Clone, Debug, PartialEq)]
pub struct Shift {
    pub date: DateTime,
    pub delta: f64,
}
```

With the `serde` feature (`cargo add plen-core --features serde`), `Shift` also implements `Serialize` and `Deserialize`, with field names in camelCase.

## Fields

| Field | Type | Description |
|---|---|---|
| `date` | [`DateTime`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/date-time.md>) | Finish date in this stage. |
| `delta` | `f64` | Working time from the baseline finish to `date`, in [`SchedulingConfig::duration_unit`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/scheduling-config.md#duration_unit>). Positive when the finish is later than the baseline, negative when earlier, `0` in the baseline itself. |

### delta

- For a task finish, working time is counted on the task's calendar.
- For the project finish, working time is counted on the global calendar. The project `delta` is `0` when the baseline has no tasks.

## Example

Evaluate a candidate that makes task 2 longer, then print the project finish and the finish of task 2.

```rust
use plen_core::{
    Candidate, DateTime, EvaluateOptions, Patch, Project, ProjectData, ProjectOptions,
    SchedulingChanges, SolveOptions, TaskData, TaskUpdate,
};

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

    let task = |id: i32| TaskData {
        start_date: Some(monday),
        duration: Some(2.0),
        ..TaskData::new(id)
    };
    let data = ProjectData {
        tasks: vec![task(1), task(2)],
        ..Default::default()
    };

    let mut project = Project::new(ProjectOptions::default());
    project.solve(Some(data), &SolveOptions::default())?;

    let mut changes = SchedulingChanges::default();
    changes.tasks.update.push(TaskUpdate {
        duration: Patch::Value(5.0),
        ..TaskUpdate::new(2)
    });
    let candidates = [Candidate { id: "longer-2".into(), changes }];
    let options = EvaluateOptions {
        focus: vec![2.into()],
        ..Default::default()
    };

    let result = project.evaluate(&candidates, &options)?;
    for c in &result.candidates {
        let Some(scheduled) = &c.scheduled else { continue };
        if let Some(project_finish) = &scheduled.finish.project {
            println!("project: {} ({:+})", project_finish.date, project_finish.delta);
        }
        for t in &scheduled.finish.tasks {
            if let Some(shift) = &t.finish {
                println!("task {}: {} ({:+})", t.id, shift.date, shift.delta);
            }
        }
    }
    Ok(())
}
```

## Related

- [`Finish`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/evaluate/finish.md>) - holds the project finish as a `Shift`
- [`TaskFinish`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/evaluate/task-finish.md>) - holds a focus task's finish as a `Shift`
- [`Amount`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/evaluate/amount.md>) - the counterpart for counts and overload
- [Evaluating Alternatives](<https://docs.dhtmlx.com/planning-engine/rust/resources-and-assignment/evaluating-alternatives.md#metrics-and-cost>) - how finish shifts are read when comparing candidates
