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 and as TaskFinish::finish inside Metrics. It isn't built by hand.

Declaration#

#[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 Finish date in this stage.
delta f64 Working time from the baseline finish to date, in SchedulingConfig::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.

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(())
}
  • Finish - holds the project finish as a Shift
  • TaskFinish - holds a focus task's finish as a Shift
  • Amount - the counterpart for counts and overload
  • Evaluating Alternatives - how finish shifts are read when comparing candidates