BrainEnterprise.Core.DateUtils 7.0.0

Prefix Reserved
dotnet add package BrainEnterprise.Core.DateUtils --version 7.0.0
                    
NuGet\Install-Package BrainEnterprise.Core.DateUtils -Version 7.0.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="BrainEnterprise.Core.DateUtils" Version="7.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="BrainEnterprise.Core.DateUtils" Version="7.0.0" />
                    
Directory.Packages.props
<PackageReference Include="BrainEnterprise.Core.DateUtils" />
                    
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 BrainEnterprise.Core.DateUtils --version 7.0.0
                    
#r "nuget: BrainEnterprise.Core.DateUtils, 7.0.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 BrainEnterprise.Core.DateUtils@7.0.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=BrainEnterprise.Core.DateUtils&version=7.0.0
                    
Install as a Cake Addin
#tool nuget:?package=BrainEnterprise.Core.DateUtils&version=7.0.0
                    
Install as a Cake Tool

BrainEnterprise.Core.DateUtils

Utility per il calcolo di inizio e fine periodo, intervalli di date, arrotondamenti di orari e confronti con la data odierna. Copre i calcoli che ricorrono nei filtri di report, nelle rendicontazioni e nella normalizzazione dei tempi registrati.

Versione corrente: 7.0.0. Le differenze rispetto alla 6.0.0 sono in fondo, nella sezione Breaking changes.

Target Framework

  • .NET Standard 2.0
  • .NET Framework 4.6.2
  • .NET 9

Installazione

dotnet add package BrainEnterprise.Core.DateUtils

Classi principali

Classe Namespace Contenuto
DateHelper BrainEnterprise.Core.DateUtils Classe partial divisa in quattro file: inizio/fine periodo, confronti con oggi, arrotondamenti, conversioni
IntervalHelper BrainEnterprise.Core.DateUtils Intervalli pronti: ThisWeek(), PreviousMonth(), NextYear(), …
DateTimeInterval BrainEnterprise.Core.DateUtils Coppia inizio/fine con la durata calcolata
MonthHelper BrainEnterprise.Core.DateUtils Nome del mese, esteso e abbreviato
DateTimeExtender BrainEnterprise.Core.DateUtils.Extenders Gli stessi calcoli come extension method su DateTime
ICalendar BrainEnterprise.Core.DateUtils.Classes Contratto di un calendario lavorativo

Inizio e fine periodo

using BrainEnterprise.Core.DateUtils;

var d = new DateTime(2026, 8, 23, 14, 37, 42);

DateHelper.StartOfMonth(d);      // 2026-08-01 00:00:00
DateHelper.EndOfMonth(d);        // 2026-08-31 00:00:00
DateHelper.StartOfYear(d);       // 2026-01-01 00:00:00
DateHelper.EndOfYear(d);         // 2026-12-31 00:00:00

Esistono anche gli overload che partono dai numeri invece che da una data, comodi quando anno e mese arrivano da un filtro:

DateHelper.StartOfMonth(2026, 8);   // 2026-08-01
DateHelper.EndOfMonth(2024, 2);     // 2024-02-29, gli anni bisestili sono gestiti
DateHelper.StartOfYear(2026);       // 2026-01-01
DateHelper.EndOfYear(2026);         // 2026-12-31

Il fine periodo è a mezzanotte, non alle 23:59:59. È voluto — sono metodi che restituiscono una data, non un istante — ma se filtri record che hanno anche l'orario, un BETWEEN con EndOfMonth esclude tutto l'ultimo giorno.

Ci sono due modi corretti di filtrare. Il primo, da preferire, usa un estremo superiore esclusivo sul giorno successivo:

// sbagliato: perde i movimenti del 31 agosto dopo la mezzanotte
var righe = tutte.Where(r => r.Data >= inizio && r.Data <= DateHelper.EndOfMonth(d));

// corretto: intervallo semiaperto
var limite = DateHelper.EndOfMonth(d).AddDays(1);
var righe = tutte.Where(r => r.Data >= inizio && r.Data < limite);

Il secondo usa EndOfDay, quando serve un estremo inclusivo sull'istante:

DateHelper.StartOfDay(d);   // 2026-08-23 00:00:00.0000000
DateHelper.EndOfDay(d);     // 2026-08-23 23:59:59.9999999

var fine = DateHelper.EndOfMonth(d).EndOfDay();   // 2026-08-31 23:59:59.9999999
var righe = tutte.Where(r => r.Data >= inizio && r.Data <= fine);

Con SQL Server usa il primo modo se la colonna è datetime. Quel tipo ha una risoluzione di 3,33 millisecondi e arrotonda 23:59:59.9999999 alla mezzanotte del giorno successivo, includendo righe che dovrebbero restare fuori. EndOfDay è sicuro con datetime2 e con i confronti in memoria.

Settimana: attenzione al giorno di inizio

Il primo giorno della settimana è configurabile. Dalla 7.0.0 il valore predefinito è Monday, secondo la convenzione europea; fino alla 6.0.0 era Sunday.

var domenica = new DateTime(2026, 8, 23);   // è una domenica

DateHelper.StartOfWeek(domenica);                      // 2026-08-17, lunedì
DateHelper.EndOfWeek(domenica);                        // 2026-08-23, domenica

// indicando esplicitamente un giorno diverso
DateHelper.StartOfWeek(domenica, DayOfWeek.Sunday);    // 2026-08-23, domenica
DateHelper.EndOfWeek(domenica, DayOfWeek.Sunday);      // 2026-08-29, sabato

StartDayOfWeek è una proprietà statica e globale: se ti serve un valore diverso dal predefinito, impostalo una volta sola all'avvio dell'applicazione, prima di qualunque calcolo.

DateHelper.StartDayOfWeek = DayOfWeek.Sunday;

Gli overload con DayOfWeek esplicito non toccano l'impostazione globale: usali quando un singolo calcolo deve seguire una convenzione diversa da quella dell'applicazione.

Intervalli

DateTimeInterval è la coppia inizio/fine, con la durata già calcolata:

var periodo = new DateTimeInterval(new DateTime(2026, 1, 1), new DateTime(2026, 1, 31));

periodo.StartInterval;   // 2026-01-01
periodo.EndInterval;     // 2026-01-31
periodo.Interval;        // TimeSpan di 30 giorni

Il costruttore senza argomenti inizializza l'intervallo sulla data odierna.

IntervalHelper restituisce gli intervalli di uso più frequente, senza doverli comporre a mano:

using BrainEnterprise.Core.DateUtils;

DateTimeInterval settimana = IntervalHelper.ThisWeek();
DateTimeInterval meseScorso = IntervalHelper.PreviousMonth();   // 2026-07-01 -> 2026-07-31
DateTimeInterval annoProssimo = IntervalHelper.NextYear();

// tipico uso in un filtro di report
var movimenti = repository.Query()
    .Where(m => m.Data >= meseScorso.StartInterval
             && m.Data < meseScorso.EndInterval.AddDays(1));

Sono disponibili per tutti e tre i livelli:

Periodo Metodi
Settimana Week(data), ThisWeek(), PreviousWeek(), NextWeek()
Mese Month(data), ThisMonth(), PreviousMonth(), NextMonth()
Anno Year(data), ThisYear(), PreviousYear(), NextYear()

Le varianti This/Previous/Next partono da DateTime.Today. Poiché il fine periodo è a mezzanotte, ThisYear().Interval vale 364 giorni e non 365: è la distanza fra il primo e l'ultimo giorno, non la durata dell'anno.

Arrotondamenti di orario

Servono a normalizzare tempi registrati a mano prima di sommarli o fatturarli.

using BrainEnterprise.Core.DateUtils;

var d = new DateTime(2026, 8, 23, 14, 37, 42);

DateHelper.RoundUpMinutes(d, 15);                       // 14:45:00
DateHelper.RoundDownMinutes(d, 15);                     // 14:30:00
DateHelper.RoundNearest(d, TimeSpan.FromMinutes(15));   // 14:45:00
DateHelper.RoundNearest(d, TimeSpan.FromHours(1));      // 15:00:00

// intervallo arbitrario, non solo minuti
DateHelper.RoundUp(d, TimeSpan.FromMinutes(30));        // 15:00:00

RoundDownMinutes azzera anche i secondi. RoundNearest va al superiore da metà intervallo in su, quindi non applica l'arrotondamento bancario.

Confronti con la data odierna

Predicati leggibili, da usare al posto dei confronti espliciti con DateTime.Today:

using BrainEnterprise.Core.DateUtils;

DateHelper.IsCurrentYear(scadenza);
DateHelper.IsNextYear(scadenza);
DateHelper.IsNextYearOrGreater(scadenza);
DateHelper.IsPreviousYear(scadenza);

DateHelper.IsCurrentMonth(scadenza);
DateHelper.IsNextMonth(scadenza);
DateHelper.IsNextMonthOrGreater(scadenza);
DateHelper.IsPreviousMonth(scadenza);
DateHelper.IsPreviousMonthOrLess(scadenza);

IsPreviousYearOrLess è stata corretta nella 7.0.0: fino alla 6.0.0 il confronto era >= invece di <=, quindi restituiva true per l'anno corrente e per quelli futuri e false per una data di due anni fa. Ora si comporta come dice il nome.

DateHelper.IsPreviousYearOrLess(new DateTime(2020, 1, 1));   // true
DateHelper.IsPreviousYearOrLess(DateTime.Today);             // false

Conversioni

using BrainEnterprise.Core.DateUtils;

// da stringa con separatore: anno, mese, giorno in quest'ordine
DateHelper.DecodeDateFrom_yyyyMMdd("2026-08-23");        // 2026-08-23
DateHelper.DecodeDateFrom_yyyyMMdd("2026/08/23", '/');   // 2026-08-23

Il separatore è obbligatorio: su una stringa compatta come "20260823" il metodo lancia IndexOutOfRangeException. Per quel formato usa DateTime.ParseExact(value, "yyyyMMdd", CultureInfo.InvariantCulture).

// porta una data sotto il minimo di SQL Server dentro il dominio ammesso
DateHelper.ToSqlDateTimeMinValue(DateTime.MinValue);   // 1753-01-01 00:00:00
DateHelper.ToSqlDateTimeMinValue(new DateTime(2026, 8, 23));  // invariata

È il modo per evitare l'errore di conversione quando una data non valorizzata — che in .NET vale 0001-01-01 — finisce in una colonna datetime di SQL Server.

Nomi dei mesi

using BrainEnterprise.Core.DateUtils;

MonthHelper.DecodeMonthName(3);        // "marzo"
MonthHelper.DecodeMonthNameShort(3);   // "mar"

I nomi seguono la cultura corrente del thread: in un servizio che gira con cultura invariante escono in inglese. Un numero fuori dall'intervallo 1-12 produce ArgumentOutOfRangeException.

Extension method

DateTimeExtender espone gli stessi calcoli direttamente su DateTime, per non spezzare le catene di chiamate:

using BrainEnterprise.Core.DateUtils.Extenders;

var d = new DateTime(2026, 8, 23, 14, 37, 42);

d.StartOfMonth();
d.EndOfYear();
d.StartOfWeek(DayOfWeek.Monday);
d.RoundUpMinutes(15);
d.RoundNearest(TimeSpan.FromHours(1));
d.IsCurrentYear();

// composizione: primo giorno della settimana del mese in corso
var inizio = DateTime.Today.StartOfMonth().StartOfWeek(DayOfWeek.Monday);

Sono deleghe dirette ai metodi di DateHelper, quindi valgono le stesse avvertenze — compreso il giorno di inizio settimana.

ICalendar

Contratto per un calendario lavorativo: identificativo, descrizione, flag di calendario predefinito e sette booleani, uno per giorno, che dicono se è lavorativo.

using BrainEnterprise.Core.DateUtils.Classes;

public class CalendarioStandard : ICalendar
{
    public int CalendarId { get; set; }
    public string Description { get; set; }
    public bool DefaultCalendar { get; set; }

    public bool MondayIsWorkday { get; set; } = true;
    public bool TuesdayIsWorkday { get; set; } = true;
    public bool WednesdayIsWorkday { get; set; } = true;
    public bool ThursdayIsWorkday { get; set; } = true;
    public bool FridayIsWorkday { get; set; } = true;
    public bool SaturdayIsWorkday { get; set; }
    public bool SundayIsWorkday { get; set; }
}

L'interfaccia è solo un contratto: la libreria non contiene né un'implementazione né i calcoli sui giorni lavorativi. Serve a far parlare la stessa lingua alle applicazioni che gestiscono un proprio calendario, tipicamente persistito a database insieme alle festività.

Note

  • Tutti i calcoli lavorano su DateTime locale: la libreria non gestisce fusi orari né DateTimeOffset.
  • Nessuna dipendenza esterna oltre alla BCL (System.Data.SqlTypes per ToSqlDateTimeMinValue).

Breaking changes

7.0.0

  • StartDayOfWeek ha Monday come valore predefinito, non più Sunday. Cambiano i risultati di StartOfWeek(data) ed EndOfWeek(data) — gli overload senza giorno esplicito — e di conseguenza IntervalHelper.ThisWeek(), PreviousWeek() e NextWeek(). Chi dipendeva dal vecchio comportamento deve impostare DateHelper.StartDayOfWeek = DayOfWeek.Sunday all'avvio.
  • IsPreviousYearOrLess è stata corretta: il confronto era >= invece di <=. Chi la usava riceveva il risultato opposto a quello atteso; se qualche codice a valle compensava l'errore, va rivisto.
  • Nuovi metodi StartOfDay ed EndOfDay su DateHelper e come extension method, per ottenere l'istante iniziale e finale di una giornata. Sono aggiunte, non rotture.

Come sempre cambia anche l'AssemblyVersion, quindi i consumer su .NET Framework devono aggiornare gli eventuali binding redirect.

Licenza

MIT - 2026 Brain Enterprise S.r.l.

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 is compatible.  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 is compatible.  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.
  • .NETFramework 4.6.2

    • No dependencies.
  • .NETStandard 2.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
7.0.0 91 8/23/2026
6.0.0 299 12/30/2024
5.0.0 206 12/28/2024
4.0.0 239 12/28/2024
3.0.0 203 12/27/2024
2.0.1 314 6/28/2023
2.0.0 549 11/5/2021
1.0.1 520 10/30/2021
1.0.0 585 10/30/2021

Minor problems