2011-08-30 17 views
6

estoy atascado con un problema en torno a la fecha y hora de análisis:de análisis DateTime con un conocido, pero no se da zona horaria

Estoy tratando de analizar una cadena de fecha y hora extraído de un sitio web alemán. Se da en el formato 'día.mes.año 24 horas: minutos', como:

01.01.2011 17:00 

Y es siempre en la zona horaria alemán. Pero aquí viene el problema:

  • '01 .01.2011 17:00' debe analizar como una estructura DateTime con '01 .01.2011 16:00' en UTC (en este caso, la zona horaria es CET, sin luz del día ahorro de tiempo)
  • mientras '01 .06.2011 17:00' debe analizar como una estructura DateTime con '01 .01.2011 15:00' en UTC (en este caso, la zona horaria es CEST, con el tiempo de ahorro de luz diurna)

No tengo ni idea de cómo lograr esto. Si configuro mi reloj local en la zona horaria alemana, y analizo con DateTime.ParseExact y la bandera DateTimeStyles.AssumeLocal y DateTimeStyles.AdjustToUniversal, se analiza correctamente. Sin embargo, quiero que cualquier cliente lo analice independientemente de su reloj local y zona horaria. Además, no quiero compensar el huso horario, porque depende de la fecha (verano: -2/invierno: -1).

Una vez que tenga la fecha y hora en UTC, sería fácil convertirla a cualquier zona horaria local.

+0

posible duplicado de [Creación de un DateTime en un huso horario específico en C# fx 3.5] (http://stackoverflow.com/questions/246498/creating-a-datetime-in-a-specific-time-zone-in -c-fx-3-5) –

+0

@Michael Haren: No, no es un duplicado. Eché un vistazo a esto antes de hacer esta pregunta y no me ayudó con mi problema específico. –

+0

quizás no sea un duplicado, pero pensé que sería útil –

Respuesta

1

Después de haber visto que la tarea no se puede archieved con la ayuda del marco/Silverlight WP7, escribí una pequeña ayuda que hace el trabajo:

public static class DateTimeHelper 
{ 
    /// <summary> 
    /// Tries to parse the given datetime string that is not annotated with a timezone 
    /// information but known to be in the CET/CEST zone and returns a DateTime struct 
    /// in UTC (so it can be converted to the devices local time). If it could not be 
    /// parsed, result contains the current date/time in UTC. 
    /// </summary> 
    public static bool TryParseCetCest(string s, string format, IFormatProvider provider, DateTimeStyles style, out DateTime result) 
    { 
     // Parse datetime, knowing it is in CET/CEST timezone. Parse as universal as we fix it afterwards 
     if (!DateTime.TryParseExact(s, format, provider, style, out result)) 
     { 
      result = DateTime.UtcNow; 
      return false; 
     } 
     result = DateTime.SpecifyKind(result, DateTimeKind.Utc); 

     // The boundaries of the daylight saving time period in CET and CEST (_not_ in UTC!) 
     // Both DateTime structs are of kind 'Utc', to be able to compare them with the parsing result 
     DateTime DstStart = LastSundayOf(result.Year, 3).AddHours(2); 
     DateTime DstEnd = LastSundayOf(result.Year, 10).AddHours(3); 

     // Are we inside the daylight saving time period? 
     if (DstStart.CompareTo(result) <= 0 && result.CompareTo(DstEnd) < 0) 
      result = result.AddHours(-2); // CEST = UTC+2h 
     else 
      result = result.AddHours(-1); // CET = UTC+1h 

     return true; 
    } 

    /// <summary> 
    /// Returns the last sunday of the given month and year in UTC 
    /// </summary> 
    private static DateTime LastSundayOf(int year, int month) 
    { 
     DateTime firstOfNextMonth = new DateTime(year, month + 1, 1, 0, 0, 0, DateTimeKind.Utc); 
     return firstOfNextMonth.AddDays(firstOfNextMonth.DayOfWeek == DayOfWeek.Sunday ? -7 : 
                (-1 * (int)firstOfNextMonth.DayOfWeek)); 
    } 
} 

el truco consistía en analizar que sin la bandera DateTimeStyles.AssumeUniversal (esto hace suponer TryParseExact la fecha es UTC y devolver la fecha convertida/ajustarse a local), respecifying como la hora UTC y luego, manualmente ajustándolo al actu al equivalente UTC.

Sigue las reglas de DST que se pueden encontrar here. Lo probé con los 4 casos límite justo antes/después del inicio/final del horario de verano. Eso demostró nuevamente la importancia de las pruebas: tuve que cambiar el operador < en DstStart.CompareTo(result) < 0 a <= para que produjera el resultado correcto.

Tenía la sensación de que estoy reinventando la rueda aquí (lo que odio hacer), pero no quería utilizar una biblioteca dedicada para este trabajo simple. Eché un vistazo a Noda Time, que es un gran proyecto, pero creo que no es necesario para esto.

Espero poder ahorrarle a alguien un poco de tiempo con este pequeño ayudante. Intencionalmente no es genérico para todas las zonas horarias (si lo necesita, use una lib como Noda Time en su lugar), pero para estos casos en los que solo tiene una zona horaria fija, como en mi caso.

+0

Me sorprende que no haya utilizado AssumeUniversal; hubiera esperado * que lo devuelva con un horario UTC; disculpas por engañarte en ese frente si no es el caso. DateTime me molesta :( –

+0

@Jon: Pensé exactamente lo mismo para ser sincero, y me pregunté por qué produjo resultados tan extraños. Hasta que me di cuenta de que hace lo correcto pero diferente de lo que cabría esperar: si lo dejas analizarlo '17 : 00 'supone 5pm UTC como se esperaba pero devuelve una estructura DateTime con 7pm (Kind = local) en mi caso (timezone local = german). Si llama '' SpecifyKind (.., Utc) '' en esto, regresa una estructura Utc 7pm ... –

+0

Y sí: DateTime es realmente molesto :) –

4

Parece que sabe en qué zona horaria debe analizarla. Suponiendo .NET 3.5 (y por lo tanto TimeZoneInfo) que debe lógicamente:

  • analizarlo como un tiempo "local" (no zona horaria específica)
  • convertir esa hora local a una hora UTC

Desafortunadamente DateTime makes that slightly tricky. EDITAR: I pensamiento que desea convertir analice utilizando DateTimeStyles.AssumeUniversal - pero que termina devolviendo localDateTime, molestamente. Básicamente, usted quiere terminar con unDateTime con el momento adecuado, para que pueda utilizar:

parsed = DateTime.SpecifyKind(parsed, DateTimeKind.Unspecified); 

entonces se puede obtener un valor UTC con:

DateTime utc = TimeZoneInfo.ConvertTimeToUtc(parsed, germanTimeZone); 

Nota que realmente desea una tiempo de fecha "no especificado" primero, para que pueda convertirlo a UTC en una zona horaria arbitraria. También debe recordar la posibilidad de que una hora local sea ambigua (se produzca dos veces) o imposible (no ocurra) debido a los cambios de horario de verano.

Y sí, esto va a ser mucho más fácil en Noda Time cuando esté terminado :)

+0

+1 Genial, el '' ConvertTimeToUtc'' era de alguna manera la pieza que faltaba. Sin embargo, tengo una última pregunta: ¿de dónde consigo el '' germanTimeZone''? –

+0

@Philip: Calcula la ID de zona horaria adecuada y utiliza 'TimeZoneInfo.FindSystemTimeZoneById'. Creo que quiere "hora estándar de W. Europe" (ignore el hecho de que se llama hora estándar; esa es solo la identificación, todavía manejará el horario de verano). –

+0

Probablemente debería haber mencionado que estoy construyendo una aplicación para Windows Phone 7, donde '' TimeZoneInfo'' solo tiene el método '' ConvertTime'' y las zonas horarias '' Utc'' y '' Local''. –

Cuestiones relacionadas