Dates · holidays · everyday utilities

The calendar your code
shouldn’t get wrong.

Bank holidays that move, leap days, month ends, durations people can read. Transformations computes Easter, English bank holidays and US observances from rules, does date arithmetic that clamps instead of throwing, and formats the result for humans.

✓ Real library output ✓ Checked against the official list ✓ .NET 8, 9, and 10
HolidayHelper · 2026 real output
2026 England & Wales bank holidays
Thu 01 JanNew Year's Day
Fri 03 AprGood Friday
Mon 06 AprEaster Monday
Mon 04 MayEarly May bank holiday
Mon 25 MaySpring bank holiday
Mon 31 AugSummer bank holiday
Fri 25 DecChristmas Day
Mon 28 DecBoxing Day (substitute day)
21years of holidays in the calendar below, 2015 to 2035
9England and Wales years checked date-by-date against the official list
2regions: England and Wales bank holidays, and US observances
6duration formats, from 02:30 to “2 hours 30 minutes”
Holiday calendar

Holidays computed from rules.

Easter comes from the computus. Bank holidays follow the substitute-day rules: when Christmas or New Year falls on a weekend, the day moves. Every date below was produced by running HolidayHelper in C#.

Holiday Easter Sunday Weekends shown in grey
Working days

How many days of actual work?

GetEnglishWorkingDaysCount counts the days from the start date up to, but not including, the end date, skipping weekends and England and Wales bank holidays.

Working-day counter

Computed in your browser from the same holiday data as the calendar, using the library’s rule: start included, end excluded. The three presets match what the C# library returned.

int days = HolidayHelper.GetEnglishWorkingDaysCount( new DateTime(2026, 12, 21), new DateTime(2027, 1, 5)); // 8

Know the coverage. The holiday rules are for England and Wales: Scotland and Northern Ireland have different days. One-off holidays are listed by year (jubilees, weddings, VE Day 2020, the 2022 funeral and the 2023 coronation), so a future royal event needs a library update. The US set is the observances the library implements, not the full federal list.

Date arithmetic

Maths that clamps instead of crashing.

AddMonthsSafely and friends stay inside the range of DateTime and land on the last valid day, so month-end and leap-day cases do not throw. Every result below is real output.

ExpressionResult
new DateTime(2024,1,31).AddMonthsSafely(1)2024-02-29
new DateTime(2024,2,29).AddYearsSafely(1)2025-02-28
DateTime.MaxValue.AddMonthsSafely(1)9999-12-31
DateTime.MinValue.AddDaysSafely(-1)0001-01-01
new DateTime(1990,6,15).CalculateAge(new DateTime(2026,6,14))35
new DateTime(1990,6,15).CalculateAge(new DateTime(2026,6,15))36
new DateTime(2024,2,10).LastOfMonth()2024-02-29
new DateTime(2026,10,1).FirstOfMonth(DayOfWeek.Monday)2026-10-05
new DateTime(2026,10,1).LastOfMonth(DayOfWeek.Friday)2026-10-30
new DateTime(2026,1,31).DateDiff(DateInterval.Month, new DateTime(2026,2,28))0
new DateTime(2026,1,15).DateDiff(DateInterval.Month, new DateTime(2026,2,14))0
new DateTime(2026,1,15).DateDiff(DateInterval.Month, new DateTime(2026,2,15))1

Reading the DateDiff rows. DateDiff with DateInterval.Month counts complete months, the way a person would: 15 January to 14 February is zero, 15 January to 15 February is one.

Durations and sizes

Numbers people can read.

Turn a TimeSpan into the format that suits the screen, and a byte count into a size a person recognises. Three durations, six formats, real output.

FormatTimeSpan 02:30:15TimeSpan 2.03:05:09TimeSpan 00:00:45.2500000
VerboseTotalTime2 hours2 days45 seconds
ShorthandTotalTime02h2days45s
VerboseTime2 hours 30 minutes 15 seconds 0ms2 days 3 hours 5 minutes 9 seconds 0ms45 seconds 250ms
ShorthandTime02h:30m:15s:000ms2days 03h:05m:09s:000ms45s:250ms
hhmm02:3051:0500:00
hhmmss02:30:1551:05:0900:00:45

File sizes

ToSizeString() on a long of bytes.

BytesToSizeString()
00.00 B
1,0231023.00 B
1,5361.50 KB
1,048,5761.00 MB
1,073,741,8241.00 GB
1,099,511,627,7761.00 TB

Short elapsed time

ToShortElapsedString() keeps the two largest units.

TimeSpanToShortElapsedString()
00:00:00.2500000250ms
00:01:151m 15s
02:03:042h 3m
3.02:01:003d 2h
And the rest

Everyday utilities, one call each.

Small helpers for the chores that sit between features: files, streams, collections and enums.

Batch file operations

Copy, move or delete many files at once. Choose whether errors are thrown one by one, gathered into a single exception, or ignored.

FileInfoHelper.CopyTo
Rename without ceremony

Rename a file, change only its extension, or rename it without touching the extension.

FileInfoHelper.ChangeExtension
Streams

Copy to memory, read all bytes or text, and seek to the start or end, without reaching for a reader and a using block.

StreamHelper.ReadToEnd
Large files

Process a huge file line by line, or copy with a progress callback, without loading it all into memory.

StreamExtensions.ForEachLine
Collections

Add only the values a collection does not already have, check for null or empty, clone lists and search ignoring case.

CollectionHelper.AddUnique
Enums

Read [Description] text, list an enum as value and description pairs, and parse strings or numbers with a default.

EnumHelper.GetDescription
Transformations

Install once.
Stop doing date maths by hand.

NuGet — the everyday toolkit
$ dotnet add package Transformations
View on NuGet.org .NET 8 · .NET 9 · .NET 10 · MIT licensed