2009-08-30 15 views
47

Estoy almacenando todas mis fechas en formato UTC en mi base de datos. Le pregunto al usuario por su zona horaria y quiero usar su zona horaria más lo que supongo que es la hora del servidor para averiguar el UTC para ellos.Tengo problemas para convertir mi DateTime a UTC

Una vez que tengo eso quiero hacer una búsqueda para ver qué rango hay en la base de datos usando su fecha UTC recién convertida.

pero siempre obtengo esta excepción.

System.ArgumentException was unhandled by user code 
Message="The conversion could not be completed because the 
supplied DateTime did not have the Kind property set correctly. 
For example, when the Kind property is DateTimeKind.Local, 
the source time zone must be TimeZoneInfo.Local. 
Parameter name: sourceTimeZone" 

No sé por qué me sale esto.

me trataron 2 maneras

TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById(id); 
// I also tried DateTime.UtcNow 
DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local); 
var utc = TimeZoneInfo.ConvertTimeToUtc(now , zone); 

Esto fracasó así que cansado

DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local); 
var utc = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(now, 
              ZoneId, TimeZoneInfo.Utc.Id); 

Esto también falló ambos con el mismo error. ¿Qué estoy haciendo mal?

¿Editaría esto?

DateTime localServerTime = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local); 
TimeZoneInfo info = TimeZoneInfo.FindSystemTimeZoneById(id); 

var usersTime = TimeZoneInfo.ConvertTime(localServerTime, info); 

var utc = TimeZoneInfo.ConvertTimeToUtc(usersTime, userInfo); 

Editar 2 @ Jon Skeet

Ya estaba pensando acerca de que yo no fuera siquiera necesidad de hacer todo esto. Las cosas del tiempo me confunden ahora, así que es por eso que la publicación puede no ser tan clara como debería ser. Nunca sé qué diablos está recibiendo DateTime.Now (Intenté cambiar mi zona horaria a otra zona horaria y seguía obteniendo mi hora local).

Esto es lo que quería que hiciera mi trabajo. El usuario llega al sitio agrega un poco de alerta y ahora se guarda como utc (antes de que fuera DateTime.Now y luego alguien sugirió almacenar todo UTC).

Así que antes de que un usuario entrara a mi sitio y dependiendo de dónde estaba mi servidor de alojamiento podría ser al día siguiente. Entonces, si se dijo que la alerta se mostraría el 30 de agosto (su hora), pero con la diferencia horaria del servidor, podrían aparecer el 29 de agosto y se mostraría la alerta.

Así que quise combatir eso. Entonces, ¿no estoy seguro de si solo debo almacenar su hora local y luego usar estas cosas de compensación? O simplemente almacene la hora UTC. Con solo almacenar el tiempo UTC podría ser incorrecto ya que el usuario aún estaría pensando en la hora local y no estoy seguro de cómo realmente funciona el UTC, pero podría terminar una diferencia de tiempo.

Edit3

var info = TimeZoneInfo.FindSystemTimeZoneById(id) 

DateTimeOffset usersTime = TimeZoneInfo.ConvertTime(DataBaseUTCDate, 
              TimeZoneInfo.Utc, info); 
+0

'DateTime.Now.Kind' ya es' Local'; no necesita llamar 'SpecifyKind' en él. – SLaks

+0

@Edit 2: Almacene las alertas en UTC y muéstrelas en la zona horaria proporcionada por el usuario como se muestra en mi respuesta. – dtb

+0

Entonces, ¿cómo funcionaría eso? Obtengo el DateTime.UtcNow y filtro las alertas del db? luego convierte los resultados encontrados a su hora local? – chobo2

Respuesta

30

La estructura DateTime sólo admite dos zonas horarias:.

  • El local de zona horaria la máquina está funcionando en
  • y UTC.

Eche un vistazo a la estructura DateTimeOffset.

var info = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time"); 

DateTimeOffset localServerTime = DateTimeOffset.Now; 

DateTimeOffset usersTime = TimeZoneInfo.ConvertTime(localServerTime, info); 

DateTimeOffset utc = localServerTime.ToUniversalTime(); 

Console.WriteLine("Local Time: {0}", localServerTime); 
Console.WriteLine("User's Time: {0}", usersTime); 
Console.WriteLine("UTC:   {0}", utc); 

Salida:

Local Time: 30.08.2009 20:48:17 +02:00 
User's Time: 31.08.2009 03:48:17 +09:00 
UTC:   30.08.2009 18:48:17 +00:00 
+0

Eso parece funcionar. Las cosas del tiempo confunden Tengo que leer sobre ese DAteTimeOffSet y por qué funciona, y eso de la conversión no. Pero me pregunto si no puedo solo DateTime.UtcNow? como cuando lo hago, convierte mi tiempo de servidor en una fecha actual, lo que es lo mismo si hice su fecha de apagado (usando el tiempo de África Occidental). ¿O serían diferentes? – chobo2

+1

Como dice Jon Skeet, si solo quiere la hora actual en UTC, simplemente use 'DateTime.UtcNow' o' DateTimeOffset.UtcNow'. – dtb

+0

Me pregunto por qué DateTime no proporciona una propiedad TimeZoneInfo, por lo que podríamos especificar una fecha y hora junto con la zona horaria? – brighty

85

Es necesario configurar el Kind a Unspecified, así:

DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Unspecified); 
var utc = TimeZoneInfo.ConvertTimeToUtc(now , zone); 

DateTimeKind.Local significa que el, y no cualquier otro zona horaria en la zona horaria local. Es por eso que estabas recibiendo el error.

+0

Ok, intentaré esto. ¿Funcionaría lo que acabo de editar? Parece estar apagado (West Africa TimeZone estaba desactivado en unos 10 minutos) cuando se pasa de una zona horaria a otra, pero el UTC era la hora correcta. – chobo2

+0

Hmm, ¿no funciona porque no conoce la zona horaria correcta? Entonces sigo pensando algo porque creo que estoy obteniendo los tiempos incorrectos. – chobo2

+0

Leí mal tu código; realmente funcionará – SLaks

6

Como dice dtb, debe usar DateTimeOffset si desea almacenar una fecha/hora con un huso horario específico.

Sin embargo, no es del todo claro por su publicación que realmente lo necesite. Solo da ejemplos usando DateTime.Now y dice que está adivinando que está usando la hora del servidor. ¿A qué hora realmente quieres? Si solo desea la hora actual en UTC, use DateTime.UtcNow o DateTimeOffset.UtcNow. No necesita saber la zona horaria para conocer la hora UTC actual, precisamente porque es universal.

Si obtiene una fecha/hora del usuario de alguna otra manera, por favor brinde más información, de esa manera podremos encontrar lo que necesita hacer. De lo contrario, solo estamos adivinando.

+0

ver mi publicación original Edit2 – chobo2

2

UTC es solo un huso horario que todos acordaron como zona horaria estándar. Específicamente, es una zona horaria que contiene Londres, Inglaterra. EDITAR: Tenga en cuenta que no es exactamente la misma zona horaria; por ejemplo, UTC no tiene horario de verano. (Gracias, Jon Skeet)

Lo único especial de UTC es que es mucho más fácil de usar en .Net que cualquier otra zona horaria (DateTime.UtcNow, DateTime.ToUniversalTime y otros miembros).

Por lo tanto, como otros han mencionado, la mejor cosa que puedes hacer es almacenar todas las fechas en UTC dentro de su base de datos, a continuación, convertir a la hora local del usuario (escribiendo TimeZoneInfo.ConvertTime(time, usersTimeZone) antes de mostrar.


Si quieres ser más elegante, puede geolocate direcciones IP de los usuarios de adivinar automáticamente sus zonas horarias

+0

Que serán versiones futuras lol. Tengo que aprender lo básico primero. – chobo2

+0

Entonces, ¿qué es lo que quieres saber ahora? – SLaks

+0

Así que esto no daría ningún momento diferente a continuación. Al igual que obtener primero todas las alertas por UTC, cambiarlas a la hora local una vez que se encuentran no provocará que se pierdan las alertas. Al igual que su voluntad, ¿no habrá algunos que solo de alguna manera se fuera por un par de minutos o algo así? – chobo2

5

de todos los demás respuesta parece demasiado complejo que tenía un requisito específico y esto funcionó bien para mí:..

void Main() 
{ 
    var startDate = DateTime.Today; 
    var StartDateUtc = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.SpecifyKind(startDate.Date, DateTimeKind.Unspecified), "Eastern Standard Time", "UTC"); 
    startDate.Dump(); 
    StartDateUtc.Dump(); 
} 

que da salida (de LINQPad) lo que esperaba:

12/20/2013 12:00:00 AM

12/20/2013 5:00:00 AM

Puntales a Slaks para la sugerencia de tipo no especificado. Eso es lo que me estaba perdiendo. Pero todo lo que se dice sobre el hecho de que solo hay dos clases de fechas (local y UTC) simplemente me confundió el problema.

FYI - la máquina en la que ejecuté esto estaba en la zona horaria central y el horario de verano no estaba en efecto.

Cuestiones relacionadas