¿Cómo puedo convertir una marca de tiempo Unix a DateTime y viceversa?

755

Existe este código de ejemplo, pero luego comienza a hablar sobre problemas de milisegundos / nanosegundos.

La misma pregunta es en MSDN, segundos desde la época de Unix en 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);
  }
}
Mark Ingram
fuente
119
El próximo .NET 4.6 (que se lanzará más adelante en este año) presenta soporte para esto. Ver DateTimeOffset.FromUnixTimeSecondsy DateTimeOffset.ToUnixTimeSecondsmétodos. También hay métodos para milisegundos de tiempo unix.
Jeppe Stig Nielsen

Respuestas:

1018

Esto es lo que necesitas:

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 marca de tiempo está en milisegundos, no en 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;
}
ScottCher
fuente
55
El tiempo en Windows es manejado por HAL y solo es casi exacto dentro de 1ms a 15ms. Hay más información disponible en Windows Internals en la página 112, si alguien está interesado.
Jim Schubert
16
Esta respuesta corre el riesgo de truncar los segundos ... Un doble es un número flotante. El argumento debe ser int / long / etc.
Cocinar
44
Estos métodos deben aceptar un largo o int, no un doble. Además, para las marcas de tiempo Java, no hay necesidad de dividir entre 1000 y redondear. Just dodtDateTime.AddMilliseconds(javaTimeStamp).ToLocalTime();
Justin Johnson
11
¿Te perdiste el "viceversa"? ¿Cómo convertimos una fecha y hora en una marca de tiempo?
Jonny
38
Para .NET Framework 4.6 y superior hay ahora static DateTimeOffset.FromUnixMillisecondsy DateTimeOffset.ToUnixMilliseconds.
rookie1024
422

La última versión de .NET (v4.6) ha agregado soporte incorporado para las conversiones de tiempo Unix. Eso incluye el tiempo hacia y desde Unix representado por segundos o milisegundos.

  • Tiempo de Unix en segundos a UTC DateTimeOffset:

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

long unixTimeStampInSeconds = dateTimeOffset.ToUnixTimeSeconds();
  • Tiempo de Unix en milisegundos a UTC DateTimeOffset:

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

long unixTimeStampInMilliseconds = dateTimeOffset.ToUnixTimeMilliseconds();

Nota: Estos métodos se convierten hacia y desde un UTC DateTimeOffset. Para obtener una DateTimerepresentación simplemente use las propiedades DateTimeOffset.UtcDateTimeo DateTimeOffset.LocalDateTime:

DateTime dateTime = dateTimeOffset.UtcDateTime;
i3arnon
fuente
Esto no convierte la hora en hora local. Obtiene UTC si usa DateTimeOffset.FromUnixTimeSeconds ().
Berend de Boer
44
@BerenddeBoer Puedes usar ToLocalTimesi quieres.
i3arnon
1
Para obtener la hora actual puede usarlong unixMilliseconds = DateTimeOffset.Now.ToUnixTimeMilliseconds();
Dan Diplo
219

Fecha y hora a la marca de 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;
}
Dmitry Fedorkov
fuente
47

De Wikipedia :

UTC no cambia con un cambio de estaciones, pero la hora local o la hora civil pueden cambiar si una jurisdicción de zona horaria observa el horario de verano (horario de verano). Por ejemplo, la hora local en la costa este de los Estados Unidos es cinco horas menos que UTC durante el invierno, pero cuatro horas menos mientras se observa el horario de verano.

Entonces este es mi código:

TimeSpan span = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0,DateTimeKind.Utc));
double unixTime = span.TotalSeconds;
gl051
fuente
2
pero esto devuelve un doble, supongo que uno tiene que lanzar demasiado.
Knocte
30

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

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

AddSeconds y AddMilliseconds también cortan los microsegundos en el doble.

Estas versiones tienen alta precisión:

Unix -> DateTime

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;
}
Felix Keil
fuente
2
Esta es la respuesta correcta. Los demás obtienen la zona horaria incorrecta en la conversión de la marca de tiempo.
IamIC
para DateTime-> Java, solo [code] return (long) unixTimeStampInTicks / TimeSpan.TicksPerMilliSecond; [/ code]
Max
Entonces en tu UnixTimestampToDateTime, el suministrado unixTimetodavía está en segundos, ¿verdad?
Ngoc Pham
@NgocPham sí, lo es
Felix Keil
14

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);
    }
}
orad
fuente
Esto es bueno, pero sugeriría un pequeño cambio: el uso del tipo "largo" debería cambiarse a "Int32" o "int". "Largo" implica que hay una precisión significativa, pero no la hay. Toda la matemática solo es precisa a 1 segundo, por lo que Int32 sería más sugerente de lo que esperarías de una marca de tiempo Unix
JamesHoux,
3
Creo que se debe a que DateTime.Tickses Int64 (largo), por lo que están evitando un reparto extra sin control.
Orad
10

Para complementar la respuesta de ScottCher, recientemente me encontré en el molesto escenario de tener marcas de tiempo UNIX de segundos y milisegundos mezcladas arbitrariamente en un conjunto de datos de entrada. El siguiente código parece manejar esto 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);
}
Chris Thoman
fuente
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)!
Sam Grondahl
1
Tenga cuidado: esto no funcionará para las marcas de tiempo de Unix para fechas anteriores al 11 de enero de 1978 si están representadas en milisegundos. Un sello de fecha de Unix de 253324800 (segundos) da la fecha correcta de 11.01.1978, mientras que la representación en milisegundos 253324800000 da una fecha de 18.07.9997. Esto puede haber funcionado para su conjunto de datos, pero no es una solución general.
Øyvind
8

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

Ahora puede convertir más fácilmente los valores de fecha y hora hacia o desde los tipos de .NET Framework y la hora de 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 estructura DateTimeOffset :

static DateTimeOffset FromUnixTimeSeconds(long seconds)
static DateTimeOffset FromUnixTimeMilliseconds(long milliseconds)
long DateTimeOffset.ToUnixTimeSeconds()
long DateTimeOffset.ToUnixTimeMilliseconds()
Fred
fuente
Esto no te da la hora local, obtienes UTC.
Berend de Boer
@BerenddeBoer Ese es un valor predeterminado razonable. Puede aplicar un desplazamiento personalizado después de eso como lo desee.
Deilan
1
@BerenddeBoer Eso no comprende qué es el tiempo de Unix. El tiempo de Unix es segundos desde la medianoche, 1 de enero de 1970, UTC. No importa dónde se encuentre, la cantidad de segundos transcurridos desde esa época no cambia. La conversión a pantallas de hora local legibles por humanos está separada de esta representación universal, como debería ser.
Tanktalus
5

Encontré la respuesta correcta simplemente comparando la conversión a 1/1/1970 sin el ajuste de hora 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;
n8CodeGuru
fuente
4
var dt = DateTime.Now; 
var unixTime = ((DateTimeOffset)dt).ToUnixTimeSeconds();

// 1510396991

var dt = DateTimeOffset.FromUnixTimeSeconds(1510396991);

// [11.11.2017 10:43:11 +00: 00]

mesut
fuente
4

Desde .net 4.6, puede hacer esto:

var dateTime = DateTimeOffset.FromUnixTimeSeconds(unixDateTime).DateTime;
Yang Zhang
fuente
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 unixEpochuna estática global, por lo que solo necesita aparecer una vez en su proyecto, y uno puede usarAddSeconds si el tiempo UNIX es en segundos.

Para ir al otro lado:

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

Truncar a Int64 y / o usar TotalSecondssegún sea necesario.

Hot Licks
fuente
3

Escrito una extensión más simple que funciona para nosotros. Si alguien lo busca ...

public static class DateTimeExtensions
{
    public static DateTime FromUnixTimeStampToDateTime(this string unixTimeStamp)
    {

        return DateTimeOffset.FromUnixTimeSeconds(long.Parse(unixTimeStamp)).UtcDateTime;
    }
}
Riyaz Hameed
fuente
3
System.DateTimeOffset.Now.ToUnixTimeSeconds()
AMieres
fuente
2

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

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

Luk
fuente
3
Unix ha pasado una época pasada, que es 1/1/70.
ScottCher
1

Necesitaba convertir una estructura timeval (segundos, microsegundos) que contenga UNIX timea DateTimesin perder precisión y no he encontrado una respuesta aquí, así que pensé que podría agregar 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);
}
i3arnon
fuente
1

Puedes usar DateTimeOffset .

Por ejemplo. Tengo un objeto DateTime

var dateTime=new DateTime();

Si quiero convertirlo en marcas de tiempo de Unix, puedo lograrlo de la siguiente manera

var unixTimeSeconds= new DateTimeOffset(dateTime).ToUnixTimeSeconds()

Para obtener más información, visite este enlace: Método DateTimeOffset.ToUnixTimeSeconds

Ramil Aliyev
fuente
0
public static class UnixTime
    {
        private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0);

        public static DateTime UnixTimeToDateTime(double unixTimeStamp)
        {
            return Epoch.AddSeconds(unixTimeStamp).ToUniversalTime();
        }
    }

puedes llamar a UnixTime.UnixTimeToDateTime (doble fecha y hora))

madan
fuente
-2

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);
    }
}
superlogico
fuente
44
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, ¿cuál debería ser el punto al escribirlas nuevamente? ¿Quiso decir que su respuesta era una solución para versiones anteriores a 4.6?
Jeppe Stig Nielsen