2008-10-30 39 views
552

Hay este código de ejemplo, pero luego comienza a hablar de problemas de milisegundos/nanosegundos.¿Cómo puedo convertir una marca de tiempo de Unix a DateTime y viceversa?

La misma pregunta es en MSDN, Seconds since the Unix epoch in C#.

Esto es lo que tengo hasta ahora:

public Double CreatedEpoch 
{ 
    get 
    { 
    DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0).ToLocalTime(); 
    TimeSpan span = (this.Created.ToLocalTime() - epoch); 
    return span.TotalSeconds; 
    } 
    set 
    { 
    DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0).ToLocalTime(); 
    this.Created = epoch.AddSeconds(value); 
    } 
} 
+61

La próxima ** ** NET 4.6 (para ser liberar más adelante en este año) introduce soporte para esto.. Consulte ['DateTimeOffset.FromUnixTimeSeconds'] (https://msdn.microsoft.com/en-us/library/system.datetimeoffset.fromunixtimeseconds.aspx) y [' DateTimeOffset.ToUnixTimeSeconds'] (https://msdn.microsoft. com/en-us/library/system.datetimeoffset.tounixtimeseconds.aspx) métodos. Hay métodos para milisegundos de tiempo Unix también. –

Respuesta

773

Esto es lo que necesita:

public static DateTime UnixTimeStampToDateTime(double unixTimeStamp) 
{ 
    // Unix timestamp is seconds past epoch 
    System.DateTime dtDateTime = new DateTime(1970,1,1,0,0,0,0,System.DateTimeKind.Utc); 
    dtDateTime = dtDateTime.AddSeconds(unixTimeStamp).ToLocalTime(); 
    return dtDateTime; 
} 

O, para Java (que es diferente porque la fecha es en milisegundos, no segundos):

public static DateTime JavaTimeStampToDateTime(double javaTimeStamp) 
{ 
    // Java timestamp is milliseconds past epoch 
    System.DateTime dtDateTime = new DateTime(1970,1,1,0,0,0,0,System.DateTimeKind.Utc); 
    dtDateTime = dtDateTime.AddMilliseconds(javaTimeStamp).ToLocalTime(); 
    return dtDateTime; 
} 
+3

AddSeconds no parece comportarse muy bien para valores inferiores a 0,1 milisegundos (iirc) – Luk

+0

@Luk sí, hay una serie de métodos de DateTime/TimeSpan [inexplicablemente menos de milisegundos dañados] (http://stackoverflow.com/questions/4672359/why-does-timespan-fromsecondsdouble-round-to-miliseconds) ... –

+4

La hora en Windows es manejada por HAL y solo es cercana a la precisión dentro de 1 ms a 15 ms. Hay más información disponible en [Windows Internals] (http://shop.oreilly.com/product/0790145305930.do) en la página 112, si alguien está interesado. –

2

Un Unix garrapata es de 1 segundo (si no recuerdo mal), y una marca NET es 100   nanosegundos.

Si ha tenido problemas con nanosegundos, puede intentar usar AddTick (valor 10000000 *).

+3

Unix es segundos después de la época, que es 1/1/70. – ScottCher

4

he encontrado la respuesta correcta simplemente comparando la conversión a 1/1/1970 sin el ajuste horario local;

DateTime date = new DateTime(2011, 4, 1, 12, 0, 0, 0); 
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0); 
TimeSpan span = (date - epoch); 
double unixTime =span.TotalSeconds; 
199

DateTime a tiempo UNIX:

public static double DateTimeToUnixTimestamp(DateTime dateTime) 
{ 
    return (TimeZoneInfo.ConvertTimeToUtc(dateTime) - 
      new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc)).TotalSeconds; 
} 
+0

esta respuesta es incorrecta, no convierta primero la fecha y hora a UTC. – raidsan

44

"UTC no cambia con un cambio de las estaciones, pero la hora local o la hora civil puede cambiar si una jurisdicción zona horaria observa el horario de verano (horario de verano). Por ejemplo, UTC es 5 horas antes (es decir, más tarde en el día que) hora local en la costa este de los Estados Unidos durante el invierno, pero 4 horas más adelante, mientras que allí se observa el horario de verano ".

Así que este es mi código:

TimeSpan span = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0,DateTimeKind.Utc)); 
double unixTime = span.TotalSeconds; 
+3

Me pregunto por qué esto tiene un voto negativo? Parece ser similar a lo que Jon Skeet defiende en su respuesta aquí - http://stackoverflow.com/a/7983514/685760 –

+0

Este me funcionó, mientras que la versión de Dmitry fue un día libre. – tremby

+2

pero esto devuelve un doble, ¿supongo que uno necesita lanzar mucho? – knocte

8

Como complemento a la respuesta de ScottCher, poco encontré a mí mismo en el escenario molesto de tener dos segundos y milisegundos UNIX marcas de tiempo arbitrariamente mezclados entre sí en un conjunto de datos de entrada. El siguiente código parece manejar esto muy bien:

static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); 
static readonly double MaxUnixSeconds = (DateTime.MaxValue - UnixEpoch).TotalSeconds; 

public static DateTime UnixTimeStampToDateTime(double unixTimeStamp) 
{ 
    return unixTimeStamp > MaxUnixSeconds 
     ? UnixEpoch.AddMilliseconds(unixTimeStamp) 
     : UnixEpoch.AddSeconds(unixTimeStamp); 
} 
+1

Tenga cuidado cuando no use el argumento DateTimeKind, ya que el DateTime construido estará en la hora local de la computadora (¡gracias por el código, Chris)! –

+0

Esto es exactamente lo que necesitaba también, gracias, @ChrisThoman. –

1

que necesitaba para convertir un timeval struct (segundos, microsegundos) que contiene UNIX time a DateTime sin perder precisión y no he encontrado una respuesta aquí, así que pensé que sólo podría añadir la mía :

DateTime _epochTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); 
private DateTime UnixTimeToDateTime(Timeval unixTime) 
{ 
    return _epochTime.AddTicks(
     unixTime.Seconds * TimeSpan.TicksPerSecond + 
     unixTime.Microseconds * TimeSpan.TicksPerMillisecond/1000); 
} 
+0

está perdiendo precisión de microsegundos al hacer una división de enteros y no usar el módulo. – Harrison

+0

@Harrison 1. Definitivamente no estoy perdiendo precisión. 2. 'AddTicks' recibe un valor 'largo', no puede enviarle un módulo, incluso si tiene uno. – i3arnon

+1

Mi mal. No estaba mirando bien. Lo siento. – Harrison

14

¡Tenga cuidado, si necesita una precisión superior a milisegundos!

.NET (v4.6) métodos (por ejemplo, FromUnixTimeMilliseconds) no proporcionan esta precisión.

AddSeconds y AddMilliseconds también cortaron las microsegundos en el doble.

Estas versiones tienen alta precisión:

Unix -> Fecha y hora

public static DateTime UnixTimestampToDateTime(double unixTime) 
{ 
    DateTime unixStart = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc); 
    long unixTimeStampInTicks = (long) (unixTime * TimeSpan.TicksPerSecond); 
    return new DateTime(unixStart.Ticks + unixTimeStampInTicks, System.DateTimeKind.Utc); 
} 

DateTime -> Unix

public static double DateTimeToUnixTimestamp(DateTime dateTime) 
{ 
    DateTime unixStart = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc); 
    long unixTimeStampInTicks = (dateTime.ToUniversalTime() - unixStart).Ticks; 
    return (double) unixTimeStampInTicks/TimeSpan.TicksPerSecond; 
} 
+1

Creo que la fecha de regreso debe ser UTC -> DateTime (unixStart.Ticks + unixTimeStampInTicks, DateTimeKind.Utc); –

+0

@ZoltanTirinda ¡Bien! Hice una edición. ¡Gracias! –

+1

Esta es la respuesta correcta. Los otros obtienen la zona horaria incorrecta en la conversión de vuelta de timestamp. – IamIC

3
DateTime unixEpoch = DateTime.ParseExact("1970-01-01", "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture); 
DateTime convertedTime = unixEpoch.AddMilliseconds(unixTimeInMillisconds); 

Por supuesto, uno puede hacer unixEpoch es una static global, por lo que solo necesita aparecer una vez en su proyecto, y uno puede usar AddSeconds si el tiempo de UNIX está en segundos.

Para ir a la inversa:

double unixTimeInMilliseconds = timeToConvert.Subtract(unixEpoch).TotalMilliseconds; 

Truncar a Int64 y/o uso TotalSeconds según sea necesario.

219

El latest version of .NET (v4.6) ha agregado compatibilidad incorporada para las conversiones de tiempo de Unix. Eso incluye el tiempo hacia y desde Unix representado por segundos o milisegundos.

  • tiempo Unix en cuestión de segundos a DateTimeOffset:

DateTimeOffset dateTimeOffset = DateTimeOffset.FromUnixTimeSeconds(1000); 
  • DateTimeOffset a tiempo Unix en segundos:

long unixTimeStampInSeconds = dateTimeOffset.ToUnixTimeSeconds(); 
  • tiempo Unix en milisegundos para DateTimeOffset:

DateTimeOffset dateTimeOffset = DateTimeOffset.FromUnixTimeMilliseconds(1000000); 
  • DateTimeOffset a tiempo Unix en milisegundos:

long unixTimeStampInMilliseconds= dateTimeOffset.ToUnixTimeMilliseconds(); 

Nota: Estos métodos se convierten en y desde DateTimeOffset. Para obtener una representación DateTime simplemente utilizar el DateTimeOffset.DateTime propiedad:

DateTime dateTime = dateTimeOffset.UtcDateTime; 
+1

https://docs.microsoft.com/en-us/dotnet/api/system.datetimeoffset.fromunixtimeseconds?view=netframework-4.6.2 # System_DateTimeOffset_FromUnixTimeSeconds_System_Int64_ – yedevtxt

+0

Esto no convierte el tiempo a la hora local. Obtiene UTC si usa DateTimeOffset.FromUnixTimeSeconds(). –

+0

@BerenddeBoer Puede usar 'ToLocalTime' si lo desea. – i3arnon

12

Ver IdentityModel.EpochTimeExtensions

public static class EpochTimeExtensions 
{ 
    /// <summary> 
    /// Converts the given date value to epoch time. 
    /// </summary> 
    public static long ToEpochTime(this DateTime dateTime) 
    { 
     var date = dateTime.ToUniversalTime(); 
     var ticks = date.Ticks - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).Ticks; 
     var ts = ticks/TimeSpan.TicksPerSecond; 
     return ts; 
    } 

    /// <summary> 
    /// Converts the given date value to epoch time. 
    /// </summary> 
    public static long ToEpochTime(this DateTimeOffset dateTime) 
    { 
     var date = dateTime.ToUniversalTime(); 
     var ticks = date.Ticks - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero).Ticks; 
     var ts = ticks/TimeSpan.TicksPerSecond; 
     return ts; 
    } 

    /// <summary> 
    /// Converts the given epoch time to a <see cref="DateTime"/> with <see cref="DateTimeKind.Utc"/> kind. 
    /// </summary> 
    public static DateTime ToDateTimeFromEpoch(this long intDate) 
    { 
     var timeInTicks = intDate * TimeSpan.TicksPerSecond; 
     return new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddTicks(timeInTicks); 
    } 

    /// <summary> 
    /// Converts the given epoch time to a UTC <see cref="DateTimeOffset"/>. 
    /// </summary> 
    public static DateTimeOffset ToDateTimeOffsetFromEpoch(this long intDate) 
    { 
     var timeInTicks = intDate * TimeSpan.TicksPerSecond; 
     return new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero).AddTicks(timeInTicks); 
    } 
} 
-1

para .NET 4.6 y posterior:

public static class UnixDateTime 
{ 
    public static DateTimeOffset FromUnixTimeSeconds(long seconds) 
    { 
     if (seconds < -62135596800L || seconds > 253402300799L) 
      throw new ArgumentOutOfRangeException("seconds", seconds, ""); 

     return new DateTimeOffset(seconds * 10000000L + 621355968000000000L, TimeSpan.Zero); 
    } 

    public static DateTimeOffset FromUnixTimeMilliseconds(long milliseconds) 
    { 
     if (milliseconds < -62135596800000L || milliseconds > 253402300799999L) 
      throw new ArgumentOutOfRangeException("milliseconds", milliseconds, ""); 

     return new DateTimeOffset(milliseconds * 10000L + 621355968000000000L, TimeSpan.Zero); 
    } 

    public static long ToUnixTimeSeconds(this DateTimeOffset utcDateTime) 
    { 
     return utcDateTime.Ticks/10000000L - 62135596800L; 
    } 

    public static long ToUnixTimeMilliseconds(this DateTimeOffset utcDateTime) 
    { 
     return utcDateTime.Ticks/10000L - 62135596800000L; 
    } 

    [Test] 
    public void UnixSeconds() 
    { 
     DateTime utcNow = DateTime.UtcNow; 
     DateTimeOffset utcNowOffset = new DateTimeOffset(utcNow); 

     long unixTimestampInSeconds = utcNowOffset.ToUnixTimeSeconds(); 

     DateTimeOffset utcNowOffsetTest = UnixDateTime.FromUnixTimeSeconds(unixTimestampInSeconds); 

     Assert.AreEqual(utcNowOffset.Year, utcNowOffsetTest.Year); 
     Assert.AreEqual(utcNowOffset.Month, utcNowOffsetTest.Month); 
     Assert.AreEqual(utcNowOffset.Date, utcNowOffsetTest.Date); 
     Assert.AreEqual(utcNowOffset.Hour, utcNowOffsetTest.Hour); 
     Assert.AreEqual(utcNowOffset.Minute, utcNowOffsetTest.Minute); 
     Assert.AreEqual(utcNowOffset.Second, utcNowOffsetTest.Second); 
    } 

    [Test] 
    public void UnixMilliseconds() 
    { 
     DateTime utcNow = DateTime.UtcNow; 
     DateTimeOffset utcNowOffset = new DateTimeOffset(utcNow); 

     long unixTimestampInMilliseconds = utcNowOffset.ToUnixTimeMilliseconds(); 

     DateTimeOffset utcNowOffsetTest = UnixDateTime.FromUnixTimeMilliseconds(unixTimestampInMilliseconds); 

     Assert.AreEqual(utcNowOffset.Year, utcNowOffsetTest.Year); 
     Assert.AreEqual(utcNowOffset.Month, utcNowOffsetTest.Month); 
     Assert.AreEqual(utcNowOffset.Date, utcNowOffsetTest.Date); 
     Assert.AreEqual(utcNowOffset.Hour, utcNowOffsetTest.Hour); 
     Assert.AreEqual(utcNowOffset.Minute, utcNowOffsetTest.Minute); 
     Assert.AreEqual(utcNowOffset.Second, utcNowOffsetTest.Second); 
     Assert.AreEqual(utcNowOffset.Millisecond, utcNowOffsetTest.Millisecond); 
    } 
} 
+4

No entiendo. En .NET 4.6, el BCL ya tiene esos métodos (ver, por ejemplo, mi comentario a la pregunta anterior, o algunas de las otras respuestas nuevas (2015). Entonces, ¿qué sentido tiene escribirlas de nuevo? ¿Quiso decir que su respuesta fue una solución para las versiones _prior_ a 4.6? –

5

La conversión de tiempo Unix es nueva en .NET Framework 4.6.

Ahora puede convertir más fácilmente valores de fecha y hora ao desde tipos de .NET Framework y tiempo Unix. Esto puede ser necesario, por ejemplo, al convertir valores de tiempo entre un cliente JavaScript y un servidor .NET. Las siguientes API se han agregado a la DateTimeOffset structure:

static DateTimeOffset FromUnixTimeSeconds(long seconds) 
static DateTimeOffset FromUnixTimeMilliseconds(long milliseconds) 
long DateTimeOffset.ToUnixTimeSeconds() 
long DateTimeOffset.ToUnixTimeMilliseconds() 
+0

Esto no le da la hora local, usted obtiene UTC. –

+0

@BerenddeBoer Es un valor predeterminado razonable. Puede aplicar una compensación personalizada después de eso como desee. – Deilan

Cuestiones relacionadas