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


# Worktime

The working time of one calendar: daily hours, working weekdays and per-date exceptions. It's the `worktime` field of [`CalendarData`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/calendar-data.md>). Build it with a struct literal, or start from `Worktime::default()`, which is the built-in schedule.

## Declaration

```rust
#[derive(Clone, Debug, Default)]
pub struct Worktime {
    pub hours: Option<Vec<TimeRange>>,
    pub days: Option<[bool; 7]>,
    pub dates: BTreeMap<chrono::NaiveDate, DayRule>,
}
```

## Fields

| Field | Type | Default | Description |
|---|---|---|---|
| `hours` | `Option<Vec<TimeRange>>` | `None` = 08:00-12:00, 13:00-17:00 | Working hours of every working weekday. All working weekdays share the same hours. |
| `days` | `Option<[bool; 7]>` | `None` = Monday-Friday | Working weekdays. Index `0` is Sunday, index `6` is Saturday. `true` marks a working day. |
| `dates` | `BTreeMap<chrono::NaiveDate, DayRule>` | empty | Date exceptions. An entry replaces the weekday rule for that date. |

### hours

```rust
pub hours: Option<Vec<TimeRange>>
```

Each [`TimeRange`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/time-range.md>) is one working interval within the day. The order of the ranges doesn't matter. An empty list means no working hours on any weekday, so only `dates` entries can add working time.

### days

```rust
pub days: Option<[bool; 7]>
```

A weekday marked `false` has no working time unless a `dates` entry overrides it.

### dates

```rust
pub dates: BTreeMap<chrono::NaiveDate, DayRule>
```

Per-date overrides, keyed by calendar date. The [`DayRule`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/day-rule.md>) for a date wins over `days` and `hours`.

## Validation

A project with an invalid `Worktime` is rejected when it's loaded, with an [`InputError`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/errors/input-error.md>) of kind `Validation`:

| Problem | Error `path` |
|---|---|
| A range in `hours` is outside 00:00-24:00 | `calendars[{i}].worktime.hours` |

Ranges in `dates` entries are checked as described on [`DayRule`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/day-rule.md>).

## Example

The example uses `chrono` for the date keys: `cargo add chrono`.

```rust
use std::collections::BTreeMap;

use chrono::NaiveDate;
use plen_core::{CalendarData, DayRule, TimeRange, Worktime};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut dates = BTreeMap::new();
    // Public holiday.
    dates.insert(NaiveDate::from_ymd_opt(2026, 12, 25).ok_or("bad date")?, DayRule::Off);
    // Short working Saturday, 09:00-13:00.
    dates.insert(
        NaiveDate::from_ymd_opt(2026, 12, 19).ok_or("bad date")?,
        DayRule::Hours(vec![TimeRange { start: 9 * 60, end: 13 * 60 }]),
    );

    let calendar = CalendarData {
        id: "office".into(),
        worktime: Some(Worktime {
            // 09:00-18:00, minutes from midnight.
            hours: Some(vec![TimeRange { start: 9 * 60, end: 18 * 60 }]),
            // Sunday..Saturday: Monday-Friday.
            days: Some([false, true, true, true, true, true, false]),
            dates,
        }),
    };

    println!("{calendar:?}");
    Ok(())
}
```

## Related

- [`CalendarData`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/calendar-data.md>) - the calendar that holds this worktime
- [`TimeRange`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/time-range.md>) - one interval of working hours
- [`DayRule`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/data/day-rule.md>) - the working time of one date exception
- [Working time and calendars](<https://docs.dhtmlx.com/planning-engine/rust/scheduling/working-time-calendars.md>) - weekly hours, weekdays and holidays in practice
