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
<PackageReference Include="BrainEnterprise.Core.DateUtils" Version="7.0.0" />
<PackageVersion Include="BrainEnterprise.Core.DateUtils" Version="7.0.0" />
<PackageReference Include="BrainEnterprise.Core.DateUtils" />
paket add BrainEnterprise.Core.DateUtils --version 7.0.0
#r "nuget: BrainEnterprise.Core.DateUtils, 7.0.0"
#:package BrainEnterprise.Core.DateUtils@7.0.0
#addin nuget:?package=BrainEnterprise.Core.DateUtils&version=7.0.0
#tool nuget:?package=BrainEnterprise.Core.DateUtils&version=7.0.0
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
BETWEENconEndOfMonthesclude 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 arrotonda23:59:59.9999999alla mezzanotte del giorno successivo, includendo righe che dovrebbero restare fuori.EndOfDayè sicuro condatetime2e 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
DateTimelocale: la libreria non gestisce fusi orari néDateTimeOffset. - Nessuna dipendenza esterna oltre alla BCL (
System.Data.SqlTypesperToSqlDateTimeMinValue).
Breaking changes
7.0.0
StartDayOfWeekhaMondaycome valore predefinito, non piùSunday. Cambiano i risultati diStartOfWeek(data)edEndOfWeek(data)— gli overload senza giorno esplicito — e di conseguenzaIntervalHelper.ThisWeek(),PreviousWeek()eNextWeek(). Chi dipendeva dal vecchio comportamento deve impostareDateHelper.StartDayOfWeek = DayOfWeek.Sundayall'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
StartOfDayedEndOfDaysuDateHelpere 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 | Versions 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. |
-
.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.
Minor problems