[.NET 6] DateOnly y TimeOnly

Estamos muy cerca del lanzamiento de .NET, el próximo 9 de Noviembre. Así que, es un momento ideal para comenzar una nueva seria de artículos donde hablar de novedades. En este artículo, vamos a hablar de DateOnly y TimeOnly.

Clock

Trabajando con fechas

Hasta ahora, para trabajar con fechas usábamos la clase DateTime.

var date = new DateTime(1985, 7, 23);
var onlyDate = DateTime.Now.Date;

Con bastantes constructores y métodos, podíamos crear fechas de diferentes formas pero siempre trabajando con el tipo DateTime que incluye el tiempo aunque no lo necesitásemos.

De igual forma, no tenemos una forma de representar una hora del día sin incluir información de la fecha. Tenemos a nuestra disposición la clase TimesSpan que pueden representar tiempo transcurrido (2 horas, 20 minutos y 5 segundos) pero para representar una hora del día necesitamos DateTime.

Nuevas posibilidades

Con la llegada de .NET 6 se introducen nuevas estructuras, DateOnly y TimeOnly que permiten representar fechas sin una hora del día y una hora del día sin fecha respectivamente.

// DateOnly is a new struct in .NET 6 that represents just a date.
DateOnly july23th = new DateOnly(1985, 7, 23);

// TimeOnly is a new type in .NET 6. Stores a time without a date.
TimeOnly tenThirtyPM = new TimeOnly(22, 30); // 22:30, or 10:30 PM

DateOnly

La nueva struct cuenta con los mismo métodos que ya existían en DateTime para modificar la fecha: AddDays, AddMonths y AddYears.

// Like with DateTime, we can add days, months, and years.
var dateOnlyFromDateTime = dateOnlyFromDateTime.AddYears(1).AddMonths(2).AddDays(15); // Apr 29th, 2001

DateOnly almacena los valores como un entero, donde el valor 0 corresponde al 0 de Enero, 0001. De esta forma, es posible convertir un entero a un DateOnly usando el método FromDayNumber.

DateOnly dateOnlyInteger = new(1985, 7, 23); // Jul 23th 1985
int dayNumber = dateOnlyInteger.DayNumber;
DateOnly resultFromDayNumber = DateOnly.FromDayNumber(dayNumber);

También es posible creat una instancia de DateOnly desde una cadena usando el método TryParse.

// We can parse like uusing DateTime with the TryParse method.
DateOnly.TryParse("01/21/2020", out DateOnly result);

TimeOnly

La struct TimeOnly almacena la información usando un long que representa el número de ticks desde medianoche.

TimeOnly elevenTen = new TimeOnly(11, 10);
long ticks = elevenTen.Ticks;
TimeOnly timeOnlyTicks = new TimeOnly(ticks);

Podemos crear una instancia TimeOnly desde un DateTime usando el método FromDateTime.

DateTime dateTime = new DateTime(2020, 12, 12, 8, 00, 00);
TimeOnly timeOnlyFromDateTime = TimeOnly.FromDateTime(dateTime);

Podemos realizar operaciones matemáticas entre instancias de TimeOnly que nos devolverá como resultado un TimeSpan.

var afternoon = new TimeOnly(17, 00); // 5:00 PM
var morning = new TimeOnly(8, 00); // 8:00 AN
TimeSpan difference = afternoon - morning; // 9 hours

En general, nuevas structs que en mi opinión se integrarán facilmente con el resto de posibilidades de .NET y sobretodo será útil en tareas donde haya operaciones de serialización o bases de datos.

¿Qué te parecen estas novedades?. Recuerda, puedes usar los comentarios de la entrada para dejar tu opinión.

Más Información

.NET Blog: Date, Time, and Time Zone Enhancements in .NET 6

[C# 10] File Scoped Namespaces

Seguimos repasando novedades de C# 10. En esta ocasión, vamos a centrarnos en File Scoped Namespaces.

Declaración normal de namespaces (hasta ahora)

Imagina que creamos una clase Person.cs con el siguiente contenido.

using System;

namespace FileScopedNamespaceSample
{
    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

La clase Person.cs se encuentra en el namespace FileScopedNamespaceSample. Cuando se observa el fragmento de código anterior, se puede ver que la declaración del espacio de nombres normal necesita un par de corchetes.

¿Y si pudiesemos simplificar esto?.

File Scoped Namespaces

A continuación, veremos un fragmento de código que muestra una declaración de espacio de nombres de ámbito de archivo (File Scoped Namespace) que es posible con C# 10.

using System;

namespace FileScopedNamespaceSample;
    
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Como puedes ver, después del espacio de nombres se agrega un punto y coma, y no corchetes como con una declaración de espacio de nombres clásica. Este espacio de nombres de ámbito de archivo significa que todos los tipos de este archivo, como clases e interfaces, están en el espacio de nombres FileScopedNamespaceSample.

Es posible igualmente colocar la declaración del espacio de nombres en la parte superior del archivo, encima de la directivas using.

namespace FileScopedNamespaceSample;
   
using System;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Pero no es posible colocar la declaración del namespace al final del archivo.

También es importante destacar que solo podemos declarar un espacio de nombres de ámbito de archivo por archivo. Por lo general, la mayoría de las clases C# tienen solo un espacio de nombres, por lo que no hay problema.

Pero, ¿qué sucede si deseas declarar más de un espacio de nombres en un solo archivo?.

Esto no es algo posible con espacios de nombres de ámbito de archivo. Si intentas agregar otro espacio de nombres con ámbito de archivo en el mismo archivo, obtendrás un error en el IDE.

Los espacios de nombres de ámbito de archivo son una nueva posibilidad pequeña, pero interesante y útiles en diferentes casos.

¿Qué te parece esta nueva funcionalidad?. Recuerda, puedes usar los comentarios de la entrada para dejar tu feedback.

Más información

[C# 10] Global Usings

Una de las novedades más sonadas quizás por ser un cambio del que estoy seguro tendrá desarrolladores a favor y otros en contra. En este artículo, vamos a hablar de la llegada de Global Usings.

Declarar usings hasta ahora

En la parte superior de todas las clases C# hay una colección de instrucciones using que especifican los espacios de nombres que la clase necesita para compilar:

using System;

namespace GlobalUsingsSample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Global Usings!");
        }
    }
}

Cuando en el mismo proyecto tenemos diferentes clases haciendo uso de las mismas APIs, aparecen duplicadas instrucciones using.

Es cierto que Visual Studio puede colapsar los usings, pero, ¿y si se pueden gestionar de otra forma?.

Global Usings

C # 10 permite hacer uso de la palabra global para poder identificar namespaces que deberían aplicarse a toda nuestra aplicación:

global using System;

// The previous using statements will be included in every class in this project.

Estas declaraciones pueden colocarse en un archivo en cualquier lugar del proyecto, y el compilador de C# sabrá que tiene que aplicar estos namespaces a nivel de aplicación.

De esta forma:

namespace GlobalUsingsSample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Global Usings!");
        }
    }
}

No hay namespaces a nivel de la clase!. Pero…¿y si añades un namespace a nivel de clase que ya se encuentra a nivel global?. Visual Studio avisará de este caso con un warning.

Más información