Open-source library for .NET

Deadlines that know your business hours.

“Respond within 16 business hours.” Simple to say, easy to get wrong: lunch breaks, weekends, public holidays, time zones, daylight saving, and the days you spent waiting on the customer. SlaClock gets it right, so your helpdesk, ticketing or approval system doesn't have to.

  • .NET 8 and .NET 10
  • No dependencies
  • MIT licence
  • 62 tests
var calendar = BusinessCalendar.Create("Asia/Phnom_Penh")
    .Weekdays("08:00-12:00", "13:00-17:00")
    .Closed(new DateOnly(2026, 11, 9), "Independence Day")
    .Build();

// Friday 15:30 + 16 business hours
var due = calendar.Add(createdAt, TimeSpan.FromHours(16));
// → Wednesday 11 Nov, 10:30

Why a library for this?

Every team that builds a ticketing or approval system writes this code, and the bugs only show up in production, on holidays, and twice a year when the clocks change.

Lunch and weekends

A ticket raised at 16:30 on Friday isn't late on Saturday morning. Only open hours count.

Holidays and half days

Public holidays, office closures over several days, and short days like New Year's Eve.

Time zones and daylight saving

Hours are wall-clock times in your office's zone. The day the clocks change really is an hour shorter or longer.

Waiting on the customer

Pause the clock while you wait for a reply, resume when they answer, and still know the deadline.

Try it

Change anything and watch the deadline move.

This is the real SlaClock library running in your browser on .NET WebAssembly, not a copy of it. The example is a 16-hour ticket created on a Friday afternoon, with a public holiday on Monday and a pause while waiting on the customer. Drag Check the status at to move through time.

Loading .NET in your browser…

Set up

Three steps.

  1. Install the package

    Add it to the project that calculates deadlines, usually your API or service layer.

    dotnet add package SlaClock
  2. Describe when you're open

    Weekly hours (split them for lunch), holidays, ranges of closed days, yearly holidays and half days, in your office's time zone. Build() checks it all and refuses overlapping or empty hours.

    var calendar = BusinessCalendar.Create("Asia/Phnom_Penh")
        .Weekdays("08:00-12:00", "13:00-17:00")
        .On(DayOfWeek.Saturday, "08:00-12:00")
        .Closed(new DateOnly(2026, 11, 9), "Independence Day")
        .Closed(new DateOnly(2027, 4, 14), new DateOnly(2027, 4, 16), "Khmer New Year")
        .ClosedEveryYear(1, 1, "International New Year")
        .Hours(new DateOnly(2026, 12, 31), "New Year's Eve", ["08:00-12:00"])
        .Build();
  3. Ask it questions

    Every answer comes back in your office's local time, whatever offset you pass in.

    When is it due?calendar.Add(from, TimeSpan.FromHours(16))
    How long did it take?calendar.Elapsed(from, to)
    Are we open now?calendar.IsOpen(now)
    When do we next open?calendar.NextOpen(now)
    Hours or holiday on a date?calendar.HoursOn(date), calendar.DayName(date)
    var created = new DateTimeOffset(2026, 11, 6, 15, 30, 0, TimeSpan.FromHours(7));
    
    calendar.Add(created, TimeSpan.FromHours(16));   // Wed 11 Nov 10:30 +07:00
    calendar.IsOpen(created.AddDays(3));             // false (Independence Day)
    calendar.Elapsed(created, created.AddDays(4));   // 07:30:00

Use it in an app

How it fits into a real helpdesk.

An ASP.NET Core app with a database, from startup to monthly reports. SlaClock never reads the clock or the database itself: you pass the times in, so it works with any storage and is easy to test.

Build the calendar once and share it. Calendars never change after Build(), so one instance is safe for the whole app.

// Program.cs
builder.Services.AddSingleton(
    BusinessCalendar.Create("Asia/Phnom_Penh")
        .Weekdays("08:00-12:00", "13:00-17:00")
        .ClosedEveryYear(1, 1, "International New Year")
        .Build());

Rules worth knowing

Small decisions, made once.

Open at 08:00, closed at 17:00

Ranges include their start and exclude their end, so 17:00 is closed and back-to-back ranges never double count.

Due by closing time

A deadline that lands exactly at closing is returned as 17:00 Friday, not as 08:00 Monday.

Shifts past midnight

Give each day its part, 22:00-24:00 and 00:00-06:00. They join into one open period.

Daylight saving

Hours in the skipped hour move forward by the gap. In the repeated hour, the first occurrence counts.

Breached means over

Exactly on target is still on time. IsAtRisk turns on at 80 % of the target by default.

Wrong setups fail

Overlapping hours, a calendar with no hours, unknown time zones and events out of order throw clear errors.

Questions

Does it need a database or a server?

No. It's plain calculation on values you pass in. Store calendars and timer events however you like.

Is it safe to share between requests?

Calendars are immutable and thread-safe; register one as a singleton. Timers are per ticket and not shared.

Which time zones work?

Any IANA id (Asia/Phnom_Penh, Europe/London) or Windows id that your operating system knows. Use BusinessCalendar.AlwaysOpen for 24/7 targets.

Can I load hours and holidays from settings?

Not built in yet: for now you read them from your settings and call the builder. Loading from configuration is planned for version 0.2.

Is it free?

Yes, MIT licensed. Use it in commercial software.