2010-09-16 15 views
7

Estoy convirtiendo un DateTime en OADate. Esperaba obtener exactamente el mismo DateTime al convertir el OADate, pero ahora solo tiene una resolución de milisegundos, y por lo tanto es diferente..NET DateTime, ¿diferente resolución al convertir ay desde OADate?

var a = DateTime.UtcNow; 
double oadate = a.ToOADate(); 
var b = DateTime.FromOADate(oadate); 
int compare = DateTime.Compare(a, b); 

//Compare is not 0; the date times are not the same 

Las garrapatas de un: 634202170964319073

Las garrapatas de b: 634202170964310000

El OADate doble: 40437.290467951389

¿Cuál es la razón de esto? La resolución de DateTime es claramente lo suficientemente buena.

Respuesta

3

El método estático llamado por ToOADate divide claramente las garrapatas de 10000 y luego almacena el resultado en un largo, eliminando así cualquier información milésima de segundo sub

¿Alguien sabe dónde encontrar las especificaciones del formato OADate?

private static double TicksToOADate(long value) 
    { 
     if (value == 0L) 
     { 
      return 0.0; 
     } 
     if (value < 0xc92a69c000L) 
     { 
      value += 0x85103c0cb83c000L; 
     } 
     if (value < 0x6efdddaec64000L) 
     { 
      throw new OverflowException(Environment.GetResourceString("Arg_OleAutDateInvalid")); 
     } 
     long num = (value - 0x85103c0cb83c000L)/0x2710L; 
     if (num < 0L) 
     { 
      long num2 = num % 0x5265c00L; 
      if (num2 != 0L) 
      { 
       num -= (0x5265c00L + num2) * 2L; 
      } 
     } 
     return (((double)num)/86400000.0); 
    } 
+1

No son exactamente las especificaciones, pero vale la pena leer esto: http://blogs.msdn.com/b/ericlippert/archive/2003/09/16/eric-s-complete-guide-to-vt-date.aspx –

+0

Gracias, parece que hay mucha historia detrás de este formato de fecha ... –

1

Probablemente tiene algo que ver con la precisión del doble, no con el DateTime.

+0

El doble convertido es 40437.290467951389, parece bastante preciso, pero puede que estés en lo cierto. – Ezombort

+0

Sí, el doble sería lo suficientemente preciso, pero debido a la forma en que se almacena el doble, no hay una representación exacta de la fecha y hora como un doble. – Carvellis

+0

@Ezombort Parece preciso solo por el factor de conversión entre segundos y días. Si multiplicas el 'doble' que mencionas, por' 24.0 * 60.0 * 60.0', verás solo tres decimales. Un "doble" "arbitrario" de esta magnitud mostrará cinco decimales (o siete decimales si se muestra con '.ToString (" R ")'). Entonces la precisión total de un 'doble' no se utiliza. Ver mi nueva respuesta. –

10

Creo que esta es una excelente pregunta. (Acabo descubierto.)

A menos que usted está operando con fechas muy cercanas al 1900, un DateTime tendrá un mayor precisión que una fecha de OA. Pero por alguna razón oscura, los autores de la estructura DateTime solo aman para truncar al milisegundo entero más cercano cuando convierten entre DateTime y algo más. Huelga decir que hacer esto arroja mucha precisión sin una buena razón.

Aquí es una solución alternativa:

static readonly DateTime oaEpoch = new DateTime(1899, 12, 30); 

public static DateTime FromOADatePrecise(double d) 
{ 
    if (!(d >= 0)) 
    throw new ArgumentOutOfRangeException(); // NaN or negative d not supported 

    return oaEpoch + TimeSpan.FromTicks(Convert.ToInt64(d * TimeSpan.TicksPerDay)): 
} 

public static double ToOADatePrecise(this DateTime dt) 
{ 
    if (dt < oaEpoch) 
    throw new ArgumentOutOfRangeException(); 

    return Convert.ToDouble((dt - oaEpoch).Ticks)/TimeSpan.TicksPerDay; 
} 

Ahora, vamos a considerar (de su pregunta) la DateTime dada por:

var ourDT = new DateTime(634202170964319073); 
// .ToSting("O") gives 2010-09-16T06:58:16.4319073 

La precisión de cualquier DateTime es de 0,1 mu s.

cerca de la fecha y la hora que estamos considerando, la precisión de una fecha de la OA es:

Math.Pow(2.0, -37.0) días, o alrededor de 0.6286 microsiemens

Llegamos a la conclusión de que en esta región una DateTime es más preciso que una fecha OA por (un poco) un factor seis.

Convirtamos ourDT a double usando mi método de extensión por encima de

double ourOADate = ourDT.ToOADatePrecise(); 
// .ToString("G") gives 40437.2904679619 
// .ToString("R") gives 40437.290467961888 

Ahora, si convierte ourOADate de nuevo a un DateTime utilizando el método estático FromOADatePrecise anterior, se obtiene

2010-09-16T06:58:16.4319072 (escrito con "O" formato)

Comparando con el original, vemos que la pérdida de precisión es en este caso 0.1 μs. Esperamos que la pérdida de precisión esté dentro de ± 0.4 μs ya que este intervalo tiene una longitud de 0.8 μs que es comparable a los 0.6286 μs mencionados anteriormente.

Si nos vamos a la inversa, comenzando con un double que representa una fecha de OA no demasiado cerca del año 1900, y primera uso FromOADatePrecise y continuaciónToOADatePrecise, entonces volvamos a un double, y porque la precisión del intermedio DateTime es superior a la de una fecha OA, esperamos un viaje de ida y vuelta perfecto en este caso. Si, por otro lado, utiliza los métodos BCL FromOADate y ToOADate en el mismo orden, es extremadamente improbable obtener un buen viaje de ida y vuelta (a menos que el double que comenzamos tenga una forma muy especial).

+1

Y solía ser peor. Hace años encontré un error en el que a veces las fechas se redondeaban al error de milisegundo más cercano, y podría inducir un error de hasta dos días. Afortunadamente, creo que finalmente se solucionó. –