2012-03-01 12 views
18

Mi zona horaria local es (GMT + 10: 00) Canberra, Melbourne, SydneyDateTime - el comportamiento de ahorro de luz extraña

sáb 31-Mar-2012 15:59 UTC = dom 01-Apr-2012 02:59 + 11:00
sáb 31-Mar-2012 16:00 UTC = dom 01-Apr-2012 02:00 10: 00

el horario de verano termina a las 3 AM primer domingo de abril y el viento reloj de nuevo 1 hora .

Dado el siguiente código ....

DateTime dt1 = DateTime.Parse("31-Mar-2012 15:59", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal); 

DateTime dt2 = DateTime.Parse("31-Mar-2012 15:59", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal).AddMinutes(1); 
DateTime dt3 = DateTime.Parse("31-Mar-2012 16:00", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal); 

Console.WriteLine("{0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1); 
Console.WriteLine("{0:yyyy-MMM-dd HH:mm:ss.ffff K} ({1}) = {2:yyyy-MMM-dd HH:mm:ss.ffff K} ({3})", dt2, dt2.Kind, dt3, dt3.Kind); 
Console.WriteLine("{0} : {1} : {2}", dt1.ToUniversalTime().Hour, dt2.ToUniversalTime().Hour, dt3.ToUniversalTime().Hour); 

me sale el siguiente resultado

2012-Apr-01 02: 59: 00.0000 11: 00
2012-Apr-01 03 : 00: 00,0000 10: 00 (local) = 2012-Apr-01 02: 00: 00,0000 10: 00 (local)
15: 17: 16

Adición de 1 minuto a la fecha y hora original hace lo local hora 3 AM pero también establece el desplazamiento a +10 horas. Agregar 1 minuto a la fecha UTC y el análisis correctamente establece la hora local a las 2 AM con un desplazamiento de +10 UTC.

Repitiendo con

DateTime dt1 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc); 

DateTime dt2 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc).AddMinutes(1); 
DateTime dt3 = new DateTime(2012, 03, 31, 16, 0, 0, DateTimeKind.Utc); 

o

DateTime dt1 = DateTime.Parse("31-Mar-2012 15:59", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); 

DateTime dt2 = DateTime.Parse("31-Mar-2012 15:59", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal).AddMinutes(1); 
DateTime dt3 = DateTime.Parse("31-Mar-2012 16:00", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); 

da

2012-Mar-31 15: 59: 00,0000 Z
2012-Mar-31 16: 00: 00,0000 Z (Utc) = 2012-Mar-31 16: 00: 00.0000 Z (Utc)
15: 16: 16

como se esperaba

Repitiendo de nuevo con

DateTime dt1 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc).ToLocalTime(); 

DateTime dt2 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc).ToLocalTime().AddMinutes(1); 
DateTime dt3 = new DateTime(2012, 03, 31, 16, 0, 0, DateTimeKind.Utc).ToLocalTime(); 

da el original

2012-Apr-01 02: 59: 00.0000 11: 00
2012-Abr-01 03:00: 00.0000 +10: 00 (Local) = 2012-abr-01 02: 00: 00.0000 +10: 00 (Local)
15: 17: 16

¿Alguien puede explicar esto?

Indecentemente si uso el TimeZoneInfo convertir de UTC a AUS Eastern Standard Time Me da la hora correcta, pero pierdo la información de desplazamiento en la instancia fecha y hora como el DateTime.Kind == DateTimeKind.Unspecified

= = Escenario adicional para resaltar

Esto es solo un simple incremento del intervalo de tiempo, comenzando con una fecha UTC NO ambigua, 1 minuto antes de que termine el horario de verano.

DateTime dt1 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc); 
DateTime dt2 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc).ToLocalTime(); 

Console.WriteLine("Original in UTC  : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1); 
Console.WriteLine("Original in Local : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1.ToLocalTime()); 
Console.WriteLine("+ 1 Minute in Local : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1.AddMinutes(1).ToLocalTime()); 
Console.WriteLine("+ 1 Minute in UTC : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1.AddMinutes(1)); 
Console.WriteLine("====================================================="); 
Console.WriteLine("Original in UTC  : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt2.ToUniversalTime()); 
Console.WriteLine("Original in Local : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt2); 
Console.WriteLine("+ 1 Minute in Local : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt2.AddMinutes(1)); 
Console.WriteLine("+ 1 Minute in UTC : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt2.AddMinutes(1).ToUniversalTime()); 

da

original en UTC: 2012-Mar-31 15:59:00.0000 Z
original en local: 2012-Apr-01 02: 59: 00.0000 11: 00
+ 1 minuto en un sitio: 2012-Apr-01 02 : 00: 00.0000 10: 00
+ 1 minuto en UTC: 2012-Mar-31 16 : 00: 00.0000 Z

============================= ========================

original en UTC: 2012-Mar-31 15: 59: 00.0000 Z
original en local: 2012 -Apr-01 02: 59: 00.0000 +11: 00
+ 1 minuto i n Local: 2012-Apr-01 03 : 00: 00.0000 10: 00
+ 1 minuto en UTC: 2012-Mar-31 17 : 00: 00.0000 Z

+0

DateTime NO "mantiene" el desplazamiento local. Simplemente muestro el desplazamiento que estaría en efecto en ese momento. Como dt2 siempre está en hora local, la vista que tiene de la hora actual ES "verdadera". Debería utilizar DateTimeOffset si desea llevar el desplazamiento "según aplicación" http://msdn.microsoft.com/en-us/library/system.datetimeoffset.aspx – IDisposable

+0

... si eso fuera cierto, entonces hubiera esperado que el tercer resultado de dt2 en el último escenario fuera 03:00:00 +11: 00, pero sabe que el horario de verano ha finalizado. Cambió correctamente a +10: 00, pero no quitó la hora. DateTimeOffset muestra la hora como 03:00:00 +11: 00, que no es válida para mi zona horaria local. –

+0

No, sabe que USTED DIJO que esto era 3:00 a partir del 1/4/2012, por lo que el desplazamiento EN ESE momento es +10: 00 – IDisposable

Respuesta

29

Creo que el problema está en los términos de cuando se realizan las conversiones.

Está analizando asumiendo el tiempo universal, pero luego convirtiendo implícitamente a un tipo "local" - con un valor de 2:59:59. Cuando solicite un valor "local" para agregar un minuto, solo agregará un minuto al valor local, sin tener en cuenta la zona horaria. Cuando imprima el desplazamiento, el sistema intenta calcular el desplazamiento a la hora local de las 3 a.m., que es +10.

con tanta eficacia que tienes:

  • paso Parse 1: Cadena de tratar como universales (15:59 GMT)
  • Analizar el paso 2: convertir resultado en local (02:59 local)
  • adición: en la hora local, no se aplicará ningún valores de zona horaria (03:00 local)
  • Formato paso 1: offset se solicita, por lo que trabajar en lo que los mapas de la hora local a (17:00 GMT)
  • paso
  • Formato 2: compensación de cómputo como diferencia entre local y univ ERSAL (+10)

Sí, es todo un poco doloroso - DateTime is painful in general, que es la razón principal por la que estoy escribiendo Noda Time, donde hay tipos separados para "fecha/hora en una zona de" vs "fecha local/hora "(o" fecha local "o" hora local "), y es obvio cuál está usando en un punto determinado.

No estoy seguro de lo que está tratando de lograr aquí. Si puede ser más específico, puedo mostrarle lo que haría en Noda Time, aunque puede haber algunas ambigüedades inherentes (conversiones de una fecha local/veces a fecha/hora "zonificada" puede tener 0, 1 o 2 resultados).

EDIT: Si el objetivo es simplemente para recordar la zona horaria, así como el instante, en Noda El tiempo que querría ZonedDateTime, así:

using System; 
using NodaTime; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var zone = DateTimeZone.ForId("Australia/Melbourne"); 
     ZonedDateTime start = Instant.FromUtc(2012, 3, 31, 15, 59, 0) 
            .InZone(zone); 
     ZonedDateTime end = start + Duration.FromMinutes(1); 

     Console.WriteLine("{0} ({1})", start.LocalDateTime, start.Offset); 
     Console.WriteLine("{0} ({1})", end.LocalDateTime, end.Offset); 
    } 
} 

Ver las notas en calendar arithmetic de algo más de información sobre esto.

+0

Vi la publicación en su blog y estaba escribiendo algunas pruebas de unidad alrededor del horario de verano para verificar el manejo del horario de verano de Noda Time, pero me quedé atascado cuando vi estos resultados solo de DateTime struct –

+0

Marcando esta respuesta como correcta, ya que las diversas discusiones han resaltado que la aritmética DateTime no -UTC datetime está fundamentalmente roto en .NET. La única forma de combatir esto es ajustar o reemplazar DateTime –

0

Mi manera de tratar esto es tratar DateTime como Flotantes, requieren un manejo especial para cuando los manipula frente a cuando se los muestra al usuario.Yo uso una pequeña biblioteca que escribí para envolverlos:

https://github.com/b9chris/TimeZoneInfoLib.Net

Y siempre los trato como GMT + TimeZoneInfo. De esta forma puede hacer todas las operaciones matemáticas típicas que normalmente haría, operando únicamente de UTC a UTC, y solo tratar con DateTimes locales en el último paso de mostrarlos al usuario en un formato agradable. Otra ventaja de esta estructura es que puede mostrar con mayor precisión una zona horaria limpia al usuario en un formato al que están acostumbrados, en lugar de rascarse en la clase TimeZoneInfo cada vez.

Cuestiones relacionadas