Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 121 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,76 +1,145 @@
# SlaCalculator
# SlaCore

SlaCalculator is a .NET library for computing Service Level Agreement (SLA) deadlines
and evaluating SLA status (on-track, at-risk, breached). It supports both wall-clock and
business-hours-aware calculations, configurable business days, hours and holidays.
[SlaCore on NuGet](https://www.nuget.org/packages/SlaCore)

Prerequisites
- .NET 8 SDK
SlaCore is a lightweight .NET library for calculating SLA deadlines and evaluating SLA status. It supports wall-clock and business-hours SLAs with features like:

Quick start
- Predefined priority levels (Critical, High, Medium, Low)
- Custom SLA durations
- Configurable business hours and business days
- Holiday exclusions
- Warning thresholds and SLA status evaluation
- Custom business rules

1. Build the solution
The library targets `netstandard2.0` and can be used from modern .NET applications.

```bash
dotnet build
```
## Installation

2. Run the example runner project
Install from NuGet:

```bash
dotnet run --project src/SlaCore.Runner
```
dotnet add package SlaCore
```

Using the library

Add a project reference to the `SlaCore` library (from your application project):
Or reference the package in your project file:

```bash
dotnet add reference src/SlaCore/SlaCore.csproj
```
<PackageReference Include="SlaCore" Version="1.0.0" />
```

Sample code
## Quick start

```csharp
using System;
Create a predefined policy and calculate a deadline:

```
using SlaCore;
using SlaCore.Models;

class Program
{
static void Main()
{
var started = DateTimeOffset.UtcNow;
var startedAt = DateTimeOffset.UtcNow;
var policy = SlaPolicy.High();
var deadline = SlaCalculator.CalculateSlaDeadline(startedAt, policy);
Console.WriteLine($"SLA deadline: {deadline}");
```

`SlaPolicy.High()` defaults to a 4-hour allowed duration.

Predefined policies (defaults):

| Policy | Default duration |
|---------:|-----------------:|
| Critical | 1 hour |
| High | 4 hours |
| Medium | 8 hours |
| Low | 24 hours |

## Evaluating SLA status

```
var result = SlaCalculator.Evaluate(startedAt, policy);
Console.WriteLine($"Status: {result.Status}");
Console.WriteLine($"Elapsed: {result.ElapsedTime}");
Console.WriteLine($"Deadline: {result.Deadline}");
```

Status values:

- `SlaStatus.OnTrack`
- `SlaStatus.AtRisk`
- `SlaStatus.Breached`

By default, `AtRisk` is reached when `WarningThreshold` (0.80) of the allowed duration has elapsed.

## Business rules (wall-clock vs business time)

// Use a predefined policy
var policy = SlaPolicy.High(); // 4 hours
Set `UseBusinessHoursOnly = true` to apply business-days, business-hours, and holiday rules. When false, SLAs use continuous wall-clock time.

// Compute deadline (wall-clock)
var deadline = SlaCalculator.CalculateSlaDeadline(started, policy);
Console.WriteLine($"Deadline: {deadline}");
Examples and detailed explanations are available in the `docs/` folder:

// Evaluate status now
var result = SlaCalculator.Evaluate(started, policy);
Console.WriteLine(result);
- `docs/business-rules.md`
- `docs/examples.md`
- `docs/policies.md`

// Business-hours example
var businessPolicy = SlaPolicy.High(businessHoursOnly: true, configure: p =>
## Real-life scenario: Helpdesk ticket SLA

Scenario:

- A helpdesk creates a ticket at `2026-09-02T16:00` (local time).
- The customer has a `High` SLA (4 hours) and the team operates Monday–Friday, 09:00–17:00.
- The next business day is Tuesday at 09:00 (because Monday is a holiday).

Calculation (business hours):

- Day 1: Thursday 16:00 → 17:00 = 1 hour
- Day 2: Friday is a holiday (0 hours)
- Weekend: 0 hours
- Monday: holiday (0 hours)
- Tuesday 09:00 → 12:00 = 3 hours
- Deadline: Tuesday 12:00

Code to configure the policy:

```
var policy = SlaPolicy.High(
businessHoursOnly: true,
configure: p =>
{
p.BusinessHoursStart = TimeSpan.FromHours(9);
p.BusinessHoursEnd = TimeSpan.FromHours(17);
p.BusinessDays = new List<DayOfWeek>
{
p.BusinessHoursStart = TimeSpan.FromHours(9);
p.BusinessHoursEnd = TimeSpan.FromHours(17);
p.Holidays = new System.Collections.Generic.List<DateTime> { new DateTime(2024, 12, 25) };
});

var businessDeadline = SlaCalculator.CalculateSlaDeadline(started, businessPolicy);
Console.WriteLine($"Business-hours deadline: {businessDeadline}");
}
}
DayOfWeek.Monday,
DayOfWeek.Tuesday,
DayOfWeek.Wednesday,
DayOfWeek.Thursday,
DayOfWeek.Friday
};
p.Holidays = new List<DateTime> { new DateTime(2026, 9, 6), new DateTime(2026, 9, 7) };
});
```

## API overview

Key types:

- `SlaPolicy` — defines allowed duration and business rules (`AllowedDuration`, `UseBusinessHoursOnly`, `BusinessHoursStart`, `BusinessHoursEnd`, `BusinessDays`, `Holidays`, `WarningThreshold`).
- `SlaCalculator.CalculateSlaDeadline(startedAt, policy)` — computes the deadline.
- `SlaCalculator.Evaluate(startedAt, policy)` — returns `SlaResult` with `Status`, `Deadline`, `ElapsedTime`.

## Use cases

- Helpdesk and support ticket SLAs
- Incident management
- Customer response-time monitoring
- Internal service requests and compliance tracking

## Development

Build the solution:

```
dotnet build
```

API highlights
- `SlaPolicy` — create policies with `SlaPolicy.Critical()`, `High()`, `Medium()`, `Low()` and customize
- `SlaCalculator.CalculateSlaDeadline(startedAt, policy)` — compute deadline
- `SlaCalculator.Evaluate(startedAt, policy)` — get `SlaResult` with status, elapsed and remaining time
## License

License
This repository is provided as-is.
MIT — see `LICENSE.txt`.
163 changes: 163 additions & 0 deletions docs/business-rules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# Business rules

SlaCalculator allows an SLA to follow the actual working rules of an organization.

Business rules are only applied when:

`UseBusinessHoursOnly = true`

Otherwise, the SLA uses wall-clock time.

## Business hours

Business hours define the portion of each business day that can contribute to the SLA.

For example:

```
BusinessHoursStart = TimeSpan.FromHours(9);
BusinessHoursEnd = TimeSpan.FromHours(17);
```

This gives 8 applicable hours per business day.

A task started at 16:00 with a 4-hour SLA would have:

- Day 1: 16:00 → 17:00 1 hour
- Day 2: 09:00 → 12:00 3 hours

The deadline is therefore 12:00 on the following business day.

## Business days

`BusinessDays` determines which days are eligible to contribute time.

For example, a Monday–Thursday schedule:

```
BusinessDays = new List<DayOfWeek>
{
DayOfWeek.Monday,
DayOfWeek.Tuesday,
DayOfWeek.Wednesday,
DayOfWeek.Thursday
};
```

Friday is not considered a business day even though it would normally be part of a Monday–Friday schedule.

## Holidays

Holidays are additional exclusions.

For example:

```
Holidays = new List<DateTime>
{
new DateTime(2026, 9, 7)
};
```

If September 7 is a Monday, it will not contribute business time even if Monday is included in `BusinessDays`.

## Business Days and Holidays Together

The two settings are independent. For example:

```
BusinessDays = new List<DayOfWeek>
{
DayOfWeek.Monday,
DayOfWeek.Tuesday,
DayOfWeek.Wednesday,
DayOfWeek.Thursday
};

Holidays = new List<DateTime>
{
new DateTime(2026, 9, 7)
};
```

This means:

- Monday is normally a business day, but September 7 is a holiday.
- Tuesday–Thursday are business days.
- Friday is not a business day.
- Saturday and Sunday are not business days.

### Example: SLA spanning a weekend and holiday

Consider:

- Business hours: 09:00–17:00
- Business days: Monday–Friday
- Friday: holiday
- Monday: holiday
- Saturday/Sunday: weekend
- SLA starts Thursday at 09:00
- SLA duration: 15 hours

The calculation is:

- Thursday 09:00 → 17:00 8 hours
- Friday holiday
- Saturday weekend
- Sunday weekend
- Monday holiday
- Tuesday 09:00 → 16:00 7 hours

Total: 15 hours → deadline Tuesday at 16:00.

## Custom working schedules

The library does not require a Monday–Friday schedule. For example, a business operating Tuesday–Saturday can configure:

```
BusinessDays = new List<DayOfWeek>
{
DayOfWeek.Tuesday,
DayOfWeek.Wednesday,
DayOfWeek.Thursday,
DayOfWeek.Friday,
DayOfWeek.Saturday
};
```

Similarly, a support team operating 24 hours but only on weekdays could use:

```
BusinessHoursStart = TimeSpan.Zero;
BusinessHoursEnd = TimeSpan.FromHours(24);

BusinessDays = new List<DayOfWeek>
{
DayOfWeek.Monday,
DayOfWeek.Tuesday,
DayOfWeek.Wednesday,
DayOfWeek.Thursday,
DayOfWeek.Friday
};
```

## Wall-clock vs business time

There are two fundamentally different SLA models.

Wall-clock (`UseBusinessHoursOnly = false`):

The SLA continuously counts elapsed time.

- Monday 16:00 + 4 hours = Monday 20:00

Business time (`UseBusinessHoursOnly = true`):

Only configured business time counts.

- Monday 16:00 → 17:00 = 1 hour
- Tuesday 09:00 → 12:00 = 3 hours

Deadline = Tuesday 12:00

This distinction allows the same calculator to support both 24/7 and business-hours-based service agreements.
Loading
Loading