Knara.UtcStrict 1.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Knara.UtcStrict --version 1.1.0
                    
NuGet\Install-Package Knara.UtcStrict -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Knara.UtcStrict" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Knara.UtcStrict" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Knara.UtcStrict" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Knara.UtcStrict --version 1.1.0
                    
#r "nuget: Knara.UtcStrict, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Knara.UtcStrict@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Knara.UtcStrict&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Knara.UtcStrict&version=1.1.0
                    
Install as a Cake Tool

Knara.UtcStrict

Build and Test NuGet NuGet License: MIT

Opinionated library that enforces UTC time handling in .NET applications.

The Problem

Mixed environments with legacy systems create DateTime chaos:

  • Database stores some timestamps as UTC, others as local time
  • APIs return DateTime vs DateTimeOffset inconsistently
  • Frontend sends local or UTC time depending on implementation
  • Converting already-UTC DateTime to UTC again produces wrong results
  • DateTime.ToUniversalTime() makes assumptions about timezone that are often wrong

The Solution

UtcStrict is an opinionated library that forces developers to work exclusively in UTC, eliminating bugs caused by mixing DateTime, DateTimeOffset, DateTime.Now, and DateTime.UtcNow throughout the codebase.

The library normalizes all datetime values to UTC and provides convenient business logic types (UtcSchedule and UtcTimeWindow) that handle common scheduling scenarios without boilerplate code or timezone-related bugs.

Installation

dotnet add package Knara.UtcStrict

Requirements

  • .NET Standard 2.0 or later
  • No external dependencies

Core Types

UtcDateTime

Enforces UTC timezone on all DateTime values:

// All these become UTC automatically
UtcDateTime utcFromLocal = DateTime.Now;           // Converts local → UTC  
UtcDateTime utcFromOffset = DateTimeOffset.Now;    // Extracts UTC portion
UtcDateTime utcFromDb = dbDateTime;                // Assumes server timezone

// Safe comparisons - everything is UTC
if (utcFromDb < utcFromLocal) { /* ... */ }

// Explicit timezone conversion when you know the source
var easternTime = new DateTime(2024, 6, 15, 14, 30, 0);
var utc = new UtcDateTime(easternTime, "Eastern Standard Time");

UtcSchedule

Enable/disable features on a schedule:

// Create new schedule (must be future)
var schedule = UtcSchedule.CreateSchedule(
    enableOn: new UtcDateTime(DateTime.UtcNow.AddHours(1)),
    disableOn: new UtcDateTime(DateTime.UtcNow.AddDays(7))
);

// Load existing schedule from database (any dates allowed)
var existing = new UtcSchedule(enabledDate, disabledDate);

// Check if feature should be active
var (isActive, reason) = schedule.IsActiveAt(UtcDateTime.UtcNow);
if (isActive) 
{
    // Feature is enabled
}

// Unscheduled = always inactive
var unscheduled = UtcSchedule.Unscheduled;
Console.WriteLine(unscheduled.HasSchedule()); // False

UtcTimeWindow

Control service availability by time of day and timezone:

// Business hours in Eastern timezone
var businessHours = new UtcTimeWindow(
    startOn: new TimeSpan(9, 0, 0),    // 9 AM
    stopOn: new TimeSpan(17, 0, 0),    // 5 PM
    timeZone: "Eastern Standard Time",
    daysActive: new[] { DayOfWeek.Monday, DayOfWeek.Tuesday, 
                       DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday }
);

// Check availability (UTC time gets converted to Eastern automatically)
var (isOpen, reason) = businessHours.IsActiveAt(UtcDateTime.UtcNow);

// Overnight window (10 PM to 6 AM)
var maintenance = new UtcTimeWindow(
    startOn: new TimeSpan(22, 0, 0),   // 10 PM
    stopOn: new TimeSpan(6, 0, 0)      // 6 AM next day
);

// Always available
var alwaysOpen = UtcTimeWindow.AlwaysOpen;
Console.WriteLine(alwaysOpen.HasWindow()); // False (no restrictions)

Usage Scenarios

API Controllers:

public class EventController : ControllerBase
{
    public IActionResult CreateEvent(CreateEventRequest request)
    {
        // Frontend might send local time - normalize to UTC
        var eventTime = new UtcDateTime(request.EventDate);
        
        var evt = new Event 
        { 
            StartTime = eventTime.DateTime, // Always UTC in database
            Schedule = request.EnableSchedule 
                ? UtcSchedule.CreateSchedule(eventTime, eventTime.DateTime.AddHours(4))
                : UtcSchedule.Unscheduled
        };
        
        return Ok(evt);
    }
}

Database Queries:

public class EventRepository
{
    public List<Event> GetActiveEvents()
    {
        var now = UtcDateTime.UtcNow;
        
        return context.Events
            .Where(e => e.StartTime <= now.DateTime && e.EndTime >= now.DateTime)
            .ToList();
    }
    
    public Event LoadEvent(int id)
    {
        var evt = context.Events.Find(id);
        
        // Convert database DateTime to UtcDateTime for business logic
        evt.NormalizedStartTime = new UtcDateTime(evt.StartTime);
        return evt;
    }
}

Business Logic:

public class FeatureService
{
    public bool IsFeatureEnabled(string featureId, UtcDateTime atTime)
    {
        var feature = GetFeature(featureId);
        
        // Check schedule
        var (scheduleActive, _) = feature.Schedule.IsActiveAt(atTime);
        if (!scheduleActive) return false;
        
        // Check time window
        var (windowActive, _) = feature.TimeWindow.IsActiveAt(atTime);
        return windowActive;
    }
}

Key Benefits

  • Prevents double-conversion bugs - detects already-UTC values
  • Handles mixed input sources - DateTime, DateTimeOffset, database values
  • Timezone-aware business logic - schedules and time windows work across timezones
  • Implicit conversions - works with existing DateTime code
  • Clear business intent - HasSchedule() and HasWindow() methods

License

MIT License

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Knara.UtcStrict:

Package Downloads
Propel.FeatureFlags

Core feature flag models and evaluation engine for .NET applications

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.1 162 10/23/2025
1.1.0 394 9/27/2025
1.0.1 118 9/26/2025
1.0.0 117 9/26/2025