Un tipo para fecha solo en C #: ¿por qué no hay un tipo de fecha?

107

En nuestro proyecto C # tenemos la necesidad de representar una fecha sin hora. Sé de la existencia de DateTime, sin embargo, también incorpora una hora del día. Quiero hacer explícito que ciertas variables y argumentos de método se basan en fechas . Por lo tanto, no puedo usar la DateTime.Datepropiedad.

¿Cuáles son los enfoques estándar para este problema? ¿Seguro que no soy el primero en encontrarme con esto? ¿Por qué no hay ninguna Dateclase en C #?

¿Alguien tiene una buena implementación usando una estructura y tal vez algunos métodos de extensión en DateTime y tal vez implementando algunos operadores como == y <,>?

Carlo V. Dango
fuente
1
Si bien entiendo querer una semántica clara y explícita, ¿qué problemas específicos DateTimecrea?
Jeff Sternal
15
1 Necesito recordar eliminar las horas al comienzo del método. 2 no comunica bien que funcione únicamente en fechas. Esto es importante, por ejemplo, al almacenar y cargar desde Db, donde un tipo estrecho será suficiente. La programación es comunión para las personas, no para las computadoras
Carlo V. Dango
5
Solo quería decir que la falta de una clase de fecha ES un gran problema y que usar DateTime no sirve para nada. Tan pronto como guarde sus "fechas" como fecha-hora, será rehén de los problemas de horario de verano. Tirar la parte del tiempo puede enviar todas sus fechas atrás un día cuando los relojes cambian (!). Y los usuarios en diferentes zonas horarias verán diferentes fechas cuando intenten convertir las fechas y horas. Las fechas y horas están bien para representar momentos precisos en el tiempo (jiffies desde algún punto o lo que sea) pero son muy inadecuadas para representar una fecha abstracta.
TheMathemagician
2
Una pregunta similar posterior stackoverflow.com/questions/7167710/… , y Jon Skeet dice que debería haber una fecha.
goodeye
8
Un tipo de datos de solo fecha es DateTime como un tipo de datos entero es un decimal. Aquellos que argumentan que no necesitamos una fecha porque simplemente pueden desechar la parte de tiempo es similar a decir que no necesitamos enteros, ya que podemos desechar la parte decimal. Nuestro mundo tiene un concepto de fecha que no incluye una hora. El 5 de marzo no es el 5 de marzo a las 00:00:00.
Vago

Respuestas:

55

Permítame agregar una actualización a esta pregunta clásica:

  • La biblioteca Noda Time de Jon Skeet ahora está bastante madura y tiene un tipo de solo fecha llamado LocalDate. (Local en este caso solo significa local para alguien , no necesariamente local para la computadora donde se ejecuta el código).

  • Un tipo de solo fecha llamado Datees una adición propuesta a .NET Core, a través del proyecto corefxlab . Lo encontrará en el System.Timepaquete, junto con un TimeOfDaytipo y varios métodos de extensión para los tipos existentes.

He estudiado este problema de manera significativa, por lo que también compartiré varias razones de la necesidad de estos tipos:

  1. Existe una discrepancia lógica entre un valor de solo fecha y un valor de fecha a la medianoche.

    • No todos los días locales tienen una medianoche en todas las zonas horarias. Ejemplo: la transición del horario de verano de primavera hacia adelante de Brasil mueve el reloj de 11:59:59 a 01:00:00.

    • Una fecha y hora siempre se refiere a una hora específica dentro del día, mientras que una fecha solo puede referirse al comienzo del día, al final del día o al rango completo del día.

  2. Adjuntar una hora a una fecha puede hacer que la fecha cambie a medida que el valor se pasa de un entorno a otro, si las zonas horarias no se observan con mucha atención. Esto ocurre comúnmente en JavaScript (cuyo Dateobjeto es realmente una fecha + hora), pero también puede suceder fácilmente en .NET, o en la serialización cuando los datos se pasan entre JavaScript y .NET.

  3. Serializar un DateTimecon XML o JSON (y otros) siempre incluirá la hora, incluso si no es importante. Esto es muy confuso, especialmente considerando cosas como fechas de nacimiento y aniversarios, donde la hora es irrelevante.

  4. Arquitectónicamente, DateTimees un objeto de valor DDD , pero viola el Principio Único Responsable de varias maneras:

    • Está diseñado como un tipo de fecha + hora, pero a menudo se usa solo como fecha (ignorando la hora) o solo como hora del día (ignorando la fecha). ( TimeSpantambién se usa a menudo para la hora del día, pero ese es otro tema).

    • El DateTimeKindvalor adjunto a la .Kindpropiedad divide el tipo único en tres. El Unspecifiedtipo es realmente la intención original de la estructura y debe usarse de esa manera. El Utctipo alinea el valor específicamente con UTC y el Localtipo alinea el valor con la zona horaria local del entorno.

      El problema de tener una bandera separada para el tipo es que cada vez que consume un DateTime, se supone que debe verificar .Kindpara decidir qué comportamiento tomar. Todos los métodos marco hacen esto, pero otros a menudo lo olvidan. Esta es realmente una violación de SRP, ya que el tipo ahora tiene dos razones diferentes para cambiar (el valor y el tipo).

    • Los dos conducen a usos de API que se compilan, pero a menudo no tienen sentido o tienen casos extremos extraños causados ​​por efectos secundarios. Considerar:

      // nonsensical, caused by mixing types
      DateTime dt = DateTime.Today - TimeSpan.FromHours(3);  // when on today??
      
      // strange edge cases, caused by impact of Kind
      var london = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
      var paris = TimeZoneInfo.FindSystemTimeZoneById("Romance Standard Time");
      var dt = new DateTime(2016, 3, 27, 2, 0, 0);  // unspecified kind
      var delta = paris.GetUtcOffset(dt) - london.GetUtcOffset(dt);  // side effect!
      Console.WriteLine(delta.TotalHours); // 0, when should be 1 !!!

En resumen, si bien a DateTime se puede usar solo para una fecha, solo debe hacerlo cuando cada lugar que lo usa tenga mucho cuidado de ignorar la hora, y también tenga mucho cuidado de no intentar convertir hacia y desde UTC u otro zonas horarias.

Matt Johnson-Pinta
fuente
2
Si tan solo System.Time.Dateterminara en el marco .NET: /
Robert Jørgensgaard Engdahl
1
Puede usar esto hoy, simplemente suscríbase al feed myget de corefx y podrá acceder System.Timecomo cualquier otro paquete. Todavía no es "oficial".
Matt Johnson-Pint
16

Sospecho que no hay una Dateclase dedicada pura porque ya tienes la DateTimeque puede manejarla. El tener Dateconduciría a la duplicación y confusión.

Si desea el enfoque estándar, mire la DateTime.Datepropiedad que proporciona solo la parte de la fecha de a DateTimecon el valor de hora establecido en 12:00:00 medianoche (00:00:00).

Robert MacLean
fuente
60
Una gran ventaja de una clase Date dedicada es que no sufre las complejidades de las zonas horarias y el horario de verano.
Dimitri C.
3
@DimitriC. No estoy de acuerdo: puede usar DateTime con UTC y no sufre los problemas explicados, además con un DateTime, incluso si solo desea fechas, aún puede hacer cálculos que involucran tiempo (es decir, dame la fecha si resto 20 x 2 horas a partir de hoy).
Robert MacLean
@Robert MacLean: Gracias por subrayar la conveniencia de usar UTC DateTimes. Hice algunas pruebas y parece que DateTimeKind.Unspecified actúa como UTC con respecto a las restas. Entonces, de hecho, si tiene cuidado con el "tipo" de DateTimes con el que está trabajando, todo saldrá bien.
Dimitri C.
10
Tener que pensar en UTC y cualquier cosa que ver con la zona horaria es solo una pérdida de energía porque puede evitarse fácilmente con una clase de fecha separada. Y no veo ninguna confusión entre Date y DateTime.
maulik13
6
Acepte que C # realmente debería tener una clase Date. La conversión de zona horaria no solo es una fuente constante de errores submarinos, sino que es simplemente doloroso cuando se trata de cosas que se basan en un día hábil en lugar de en un tiempo.
Julian Birch
12

Envié un correo electrónico a [email protected] y esa es su respuesta

Marcos, este no es un buen lugar para hacer preguntas como estas. Tratar http://stackoverflow.com. La respuesta corta es que necesita un modelo para representar un punto en el tiempo, y DateTime lo hace, es el escenario más útil en la práctica . El hecho de que los humanos usen dos conceptos (fecha y hora) para marcar puntos en el tiempo es arbitrario y no es útil separarlos.

Solo desacople donde esté justificado, no haga las cosas por el simple hecho de hacer las cosas a ciegas. Piénselo de esta manera: ¿qué problema tiene que se resuelve dividiendo DateTime en Date y Time? ¿Y qué problemas tendrás que no tienes ahora? Sugerencia: si observa los usos de DateTime en el marco .NET: http://referencesource.microsoft.com/#mscorlib/system/datetime.cs#df6b1eba7461813b#references Verá que la mayoría se devuelve desde un método. Si no tuviéramos un concepto único como DateTime, tendrías que usar parámetros o Tuples para devolver un par de Date y Time.

HTH, Kirill Osenkov

En mi correo electrónico me pregunté si era porque DateTime usa TimeZoneInfo para obtener la hora de la máquina, en propiedad Now. Entonces yo diría que es porque "las reglas comerciales" están "demasiado acopladas", me lo confirmaron.

MVCDS
fuente
Esta publicación realmente brinda información sobre los pensamientos detrás de la decisión de diseño de no tener una clase de fecha incorporada. ¿Cuál fue la pregunta que les enviaste? No quiero dar a entender que estoy de acuerdo con esta decisión por las mismas razones que @TheMathemagician enumeró anteriormente.
Robert Jørgensgaard Engdahl
@ RobertJørgensgaardEngdahl lamentablemente ya no tengo acceso a esa cuenta de correo electrónico. Pero creo que les he preguntado por qué habían acoplado Time y Date juntos en la estructura DateTime. Y pensé que estaba de acuerdo con TheMathemagician, creo que MS adoptó este enfoque de diseño porque, como empresa internacional, paga sus necesidades, y ahora es demasiado tarde para cambiarlo, mientras que dividir los conceptos no lo hace.
MVCDS
2
¡Tal vez MS podría ir por todo el camino e implementar una SpaceTimeclase! Oye, según Einstein, el espacio y el tiempo están estrechamente relacionados, por lo que tampoco deberíamos necesitar diferenciarlos, ¿verdad? (!!!!!!!!!!!) Soy un poco nuevo en C #, pero tengo que decir, que es un campo de minas procedentes de VB.NET dónde está, simplemente, date, Today(), now, etc. Sin DateTimeprefijo basura, sin curioseaba. (¡Y estos puntos y comas y esta distinción entre mayúsculas y minúsculas es fastidioso! ¡Solo
dispárame
2
Y su propio SQL Server tiene Datetipo y el resultado debe ser de tipo Date, si se Dateesperaba el tipo de resultado como cadena sin tiempo. Por ejemplo, Delphi también tiene Date como DateTime, pero typeinfo es diferente para Date y DateTime.
user2091150
1
Kirill Osenkov responde a la pregunta de "¿Por qué no tener clases de fecha y hora separadas frente a la clase de fecha y hora ?". La Q real era "¿Por qué no tener también Clases de fecha y hora separadas?". Entiendo que la fecha y la hora deben combinarse en una clase para los muchos casos de uso concedidos del concepto de fecha y hora . Sin embargo, probablemente haya al menos tantos, si no más, casos de uso válidos de solo un concepto de fecha . Y, por supuesto, también hay muchos casos de uso válidos de un concepto de tiempo .
Tom
4

Si necesita realizar comparaciones de fechas, utilice

yourdatetime.Date;

Si está mostrando en la pantalla, use

yourdatetime.ToShortDateString();
MattP
fuente
La parte .Date es lo que estaba buscando.
Brendan Vogt
3

Permítanme especular: ¿Tal vez se deba a que hasta SQL Server 2008 no había un tipo de datos de fecha en SQL, por lo que sería difícil, así que almacenarlo en el servidor SQL? ¿Y es, después de todo, un producto de Microsoft?

Pleun
fuente
Una fecha y hora de db es diferente a una fecha y hora de C #. Un db datetime no tiene una zona horaria, por lo que en realidad no se refieren a un instante en particular. Pero C # sabe que el instante es y almacena los ticks desde la época UTC.
artsrc
2
la discusión es sobre la FECHA dedicada, no tanto sobre la parte de la fecha y hora, así que no entiendo el punto que está tratando de hacer.
Pleun
Esto no proporciona una respuesta a la pregunta. Para criticar o solicitar una aclaración de un autor, deje un comentario debajo de su publicación.
Barranka
@Barranka: la pregunta contiene "¿Por qué no hay una clase Date en C #?"
STLDev
2

Quién sabe por qué es así. Hay muchas malas decisiones de diseño en el marco .NET. Sin embargo, creo que esto es bastante menor. Siempre puede ignorar la parte de la hora, por lo que incluso si algún código decide que una DateTime se refiera a algo más que a la fecha, el código que le importa solo debe mirar la parte de la fecha. Alternativamente, puede crear un nuevo tipo que represente solo una fecha y usar funciones en DateTime para hacer el trabajo pesado (cálculos).

siride
fuente
1
Realmente no creo que haya sido una mala decisión, ya sea que quieras usar solo una fecha o no. No te votaré negativamente, pero esa es mi opinión.
JonH
No creo que lo haya redactado bien. En realidad, no tengo mucho problema con él, per se, aunque pude ver cómo tener dos o tres tipos sería más apropiado desde un punto de vista de abstracción / elegancia. Mi punto realmente fue que hay muchas cosas en el marco .NET que pueden dejarlo rascándose la cabeza y no vale la pena enfadarse demasiado, especialmente considerando que este "problema" es bastante menor en comparación con algunas decisiones de diseño atroces (genérico limitaciones).
siride
+1 porque es cierto ... ¿era este el único problema (o el más grande) de .NET :-) :-) ¿Cuántas versiones de SQL Server necesitaban para agregar un tipo DATE y TIME? Y allí fueron MUCHO más útiles (al menos por razones de integridad)
xanatos
También debo agregar que creo que "todo comienza en -100 puntos" es una buena manera de hacer un marco deficiente y esta puede ser una de las cosas que quedaron atrapadas en esa basura.
siride
2
Este problema me acaba de morder porque 1 parte del código no usó la propiedad .Date y, por lo tanto, no se comparó correctamente. Definitivamente creo que es necesario un tipo de fecha que no se almacene en ningún momento, para evitar este tipo de error
JoelFan
2

¿Por qué? Solo podemos especular y no ayuda mucho a resolver problemas de ingeniería. Una buena suposición es que DateTimecontiene toda la funcionalidad que tendría dicha estructura.

Si realmente te importa, simplemente envuelve DateTimetu propia estructura inmutable que solo expone la fecha (o mira la DateTime.Datepropiedad).

jason
fuente
2

Además de la respuesta de Robert, también tienes el DateTime.ToShortDateStringmétodo. Además, si realmente desea un objeto Date, siempre puede usar el patrón Adapter y envolver el objeto DateTime exponiendo solo lo que desea (es decir, mes, día, año).

Mate
fuente
2

Siempre existe la DateTime.Datepropiedad que corta la parte de tiempo del DateTime. Tal vez pueda encapsular o envolver DateTime en su propio tipo de fecha.

Y para la pregunta de por qué, bueno, supongo que tendrá que preguntarle a Anders Heljsberg.

Mikael Östberg
fuente
1

Porque para saber la fecha, debes conocer la hora del sistema (en tics), que incluye la hora, entonces, ¿por qué tirar esa información?

DateTimetiene una Datepropiedad si no le importa en absoluto el tiempo.

Macizo
fuente
1

Sí, también System.DateTime está sellado. He visto a algunas personas jugar con esto creando una clase personalizada solo para obtener el valor de cadena del tiempo como se menciona en publicaciones anteriores, cosas como:

class CustomDate
{
    public DateTime Date { get; set; }
    public bool IsTimeOnly { get; private set; }

    public CustomDate(bool isTimeOnly)
    {
        this.IsTimeOnly = isTimeOnly;
    }

    public string GetValue()
    {
        if (IsTimeOnly)
        {
            return Date.ToShortTimeString();
        }

        else
        {
            return Date.ToString();
        }
    }
}

Esto tal vez sea innecesario, ya que podría extraer fácilmente GetShortTimeString de un tipo DateTime antiguo sin una nueva clase

Ta01
fuente
0

Si usa las propiedades Fecha o Hoy para obtener solo la parte de la fecha del objeto DateTime.

DateTime today = DateTime.Today;
DateTime yesterday = DateTime.Now.AddDays(-1).Date;

Entonces obtendrá el componente de fecha solo con el componente de tiempo configurado en medianoche.

eph_tagh
fuente
1
esto definitivamente no es lo que quería
Carlo V. Dango
@Carlo V. Dango: No estoy de acuerdo. Creo que es exactamente lo que querías.
siride
1
@Carlo V. Dango: ¿Qué es lo que busca específicamente hacer que estas propiedades no le permitan lograr?
eph_tagh
5
Es bastante fácil: una huella de memoria de fecha probablemente sería solo la mitad de la huella de memoria de una fecha y hora (32 en lugar de 64 bits). Estarías seguro de que tu estúpido compañero de trabajo no agregó Horas (1) a tu fecha cambiándola pero "manteniendo la misma" desde el punto de vista de "solo cita". Si (por un error) el DateTime se establece en DateTimeKind.Local y la hora se normaliza a UTC, la Fecha probablemente cambiará (me pasó a mí mediante el uso de XmlSerialization y un viaje de ida y vuelta a JSON mal hecho) ... ¿Es suficiente?
xanatos