¿Cómo podemos desarrollar prácticas de codificación diseñadas para proteger contra errores de año bisiesto? [cerrado]

80

Microsoft acaba de anunciar que un error de software al calcular las fechas (durante un año bisiesto) provocó una interrupción importante en Windows Azure la semana pasada.

¿Fue realmente un simple error de juicio DateTime.Now.AddYears(1)en un año bisiesto?

¿Qué prácticas de codificación podrían haber evitado esto?

EDITAR Como señaló dcstraw DateTime.Now.AddYears(1)en un año bisiesto, de hecho devuelve la fecha correcta en .NET. Así que no es un error del marco, sino evidentemente un error en los cálculos de fecha.

Fijador
fuente
7
Como no hay una respuesta "única", y esta es más una pregunta de discusión, creo que debería migrarse a los programadores. Además: Siempre prueba unitariamente tu código. Siempre.
George Stocker
2
Los problemas de fecha y hora son un tema retorcido y complicado. Se podría decir algo como hacer todo en UTC, pero siempre habrá algún punto de traducción ... y cuando eso suceda, la cantidad de permutaciones que tendría que tener en cuenta para evitar todos los errores es asombrosa. La mayoría de nosotros nunca trabajará en un sistema que tenga que preocuparse.
Josh
9
Creo que esta pregunta es demasiado interesante para ser cerrada :-)
Steven
11
¿CUATRO votos para cerrar? Espero que este cierre feliz y de mal genio sea solo una fase en la evolución de SO. Todo es un poco más santo que tú.
Iain Holder
7
@IainMH tiene que estar de acuerdo contigo, especialmente porque casi todos los que lo cerraron nunca han hecho una pregunta con más de unos pocos votos.
Lloyd

Respuestas:

95

Enchufe desvergonzado:

Utilice una mejor API de fecha y hora

Las bibliotecas de fecha y hora .NET incorporadas son terriblemente difíciles de usar correctamente. Ellos no le permiten hacer todo lo que necesita, pero no se puede expresar con claridad a través del sistema de tipos. DateTimees un desastre , DateTimeOffsetpuede hacer que piense que en realidad está conservando la información de la zona horaria cuando no lo está, y TimeZoneInfono lo obliga a pensar en todo lo que debería considerar.

Ninguno de estos proporciona una forma agradable de decir "solo una hora del día" o "solo una fecha", ni tampoco hacen una distinción clara entre "hora local" y "hora en una zona horaria en particular". Y si desea utilizar un calendario que no sea el gregoriano, debe pasar por la Calendarclase todo el tiempo.

Por todo esto, estoy construyendo Noda Time , una biblioteca alternativa de fecha y hora construida en un puerto del "motor" de Joda Time , pero con una API nueva (y más sencilla) en la parte superior.

Algunos puntos en los que quizás quiera pensar, que son fáciles de pasar por alto si no los conoce:

  • Asignar una fecha / hora local a una en una zona horaria en particular no es tan simple como podría pensar. Una fecha / hora local específica puede ocurrir una, dos veces (ambigüedad) o cero veces (se omite) debido a las transiciones de horario de verano.
  • Las zonas horarias varían históricamente, más de lo que TimeZoneInfogeneralmente está dispuesto a revelar, francamente. (No admite una zona horaria cuya idea de "hora estándar" cambie con el tiempo o que se convierta en horario de verano permanente).
  • Incluso con la base de datos zoneinfo, los ID de zona horaria no son necesariamente estables. (CLDR aborda esto; algo que espero apoyar en Noda Time eventualmente).
  • Las representaciones textuales de fechas y horas son una pesadilla, no solo en términos de orden, sino también de separadores de fecha, separadores de tiempo y cosas raras como nombres genitivos de meses.
  • El comienzo del día no siempre es medianoche; en Brasil, por ejemplo, la transición del horario de verano de primavera mueve el reloj de pared de 11:59:59 p.m. a 1 a.m.
  • En algunos casos (bueno, uno que yo sepa), una zona horaria puede obligar a omitir un día entero: ¡el 30 de diciembre de 2011 no ocurrió en Samoa! Sospecho que la mayoría de los desarrolladores probablemente puedan ignorar este, pero ...
  • Si va a utilizar un calendario que no sea el gregoriano, tenga cuidado y asegúrese de saber realmente cómo espera que se comporte.

En cuanto a prácticas de desarrollo específicas:

  • Piense en lo que realmente está tratando de representar. Espero que el beneficio principal de Noda Time sea obligar a los desarrolladores a elegir entre varios tipos diferentes para representar sus datos. Hágalo bien y todo lo demás será más sencillo.
  • Prueba unitaria todo lo que se te ocurra. Eso dependerá exactamente de lo que haga su sistema, por supuesto, pero considere especialmente las diferentes zonas horarias, lo que sucede en las transiciones de horario de verano y, por supuesto, los años bisiestos.
  • Aconsejaría inyectar una "interfaz similar a un reloj", un servicio para decir la hora actual, en lugar de llamar explícitamente DateTime.Nowo DateTime.UtcNow; hace que sea más fácil (¡factible!) realizar pruebas unitarias
  • Si está realizando varias operaciones con "ahora", obtenga esa fecha / hora una vez y recuérdelo, en lugar de solicitar repetidamente "ahora"; de lo contrario, el valor podría cambiar de manera desafortunada entre las llamadas.
  • "Hacer todo en UTC" tampoco es siempre la respuesta, si quiero saber "¿cuándo exactamente ocurre 'dentro de dos semanas' en mi zona horaria local?" entonces necesito almacenar la fecha / hora local , así como la zona horaria.
Jon Skeet
fuente
2
@flq: Nuevamente, necesitaría definir exactamente lo que quiere decir con "seguro para años bisiestos". Dudo que haya sido un error del marco lo que causó el problema en Azure; supongo que fue un mal uso del marco.
Jon Skeet
10
Innumerables viñetas de anuncios sin una sola sobre el tema de los años bisiestos (es decir, el tema). Y luego resopló en Twitter. Creo que has tenido mejores momentos, Jon.
Will Dean
8
@WillDean: Tenga en cuenta también que la pregunta fue editada por George Stocker. El título original era "Programación defensiva contra errores de DateTime", en cuyo caso creo que estarás de acuerdo en que mi publicación es completamente relevante. (Solo me di cuenta de que el título había sido cambiado. Decía DateTime cuando comencé a responder ...)
Jon Skeet
2
Jon, lo siento, ¡no había visto el título anterior! Tal vez sea conveniente que sea alguien de cambiar un título para editar todas las respuestas demasiado ...
Will Dean
3
@WillDean: Sí ... Estoy tentado de revertir el cambio de título o hacer que termine con "errores de fecha y hora (por ejemplo, años bisiestos)"
Jon Skeet
25

Vale la pena señalar que el error probablemente no se debió a una línea como la que publicó:

DateTime.Now.AddYears(1)

Eso no crea una fecha inválida. Si tu corres:

(new DateTime(2012, 2, 29)).AddYears(1)

obtiene el 28 de febrero de 2013. No sé en qué está escrito el agente invitado de Azure, pero debe haber sido una llamada diferente que falló. Una mala forma de hacer esto en .NET habría sido:

new DateTime(today.Year + 1, today.Month, today.Day)

Eso lanza una excepción si today es un día bisiesto. Sin embargo, el blog de Microsoft sobre el problema de Azure decía que crearon una fecha no válida del 29 de febrero de 2013, lo que no estoy seguro de que sea posible hacerlo DateTimeen .NET.

No estoy diciendo eso DateTimey no soy DateTimeOffsetpropenso a errores, solo que no creo que hayan causado este problema en particular.

dcstraw
fuente
Supongo que se hizo en C ++
PhilPursglove
2
@PhilPursglove: ¿Qué te hace pensar eso? El sistema operativo Windows se escribió principalmente en C y C ++ y maneja los días bisiestos correctamente. Probablemente tuvo más que ver con una falla relacionada con la fecha en el proceso de creación del certificado de transferencia.
In silico
¿Podría estar analizando una fecha de una cadena?
Fixer
Y sí, tiene razón sobre los AddYears (1) como señaló. No estoy seguro de lo que sucedió internamente. Fue solo un intento de darle a la pregunta un poco de perspectiva, sin profundizar demasiado en la publicación del blog.
Fixer
Tiene sentido. Sinceramente, no sé si este fue el caso, pero es creíble. He visto códigos desagradables de grandes empresas, incluida MS. Pero, de nuevo, no podemos hacer mucho más que especular en este punto.
Alpha
2

¿Cómo podemos desarrollar prácticas de codificación diseñadas para proteger contra errores de año bisiesto? ¿Qué prácticas de codificación podrían haber evitado esto?

Las fechas específicas de prueba unitaria como John mencionó es una práctica de código que ayudará, sin embargo, nada supera lo que defino como una 'prueba de integración manual'

cambie el reloj en su servidor de desarrollo / banco de pruebas y observe lo que sucede cuando el tiempo pasa.

No se empantane en detalles específicos sobre si se trata de una 'práctica de codificación'. Obviamente, no puede hacer esto para todas las fechas del calendario; elija las fechas que le preocupan, ya sea el 29 de febrero a fin de mes fechas o fechas de cambio de horario de verano.

wal
fuente