2010-09-16 7 views
7

Sto convertendo un DateTime in OADate. Mi aspettavo di ottenere lo stesso identico DateTime durante la conversione di OADate, ma ora ha solo una risoluzione di millisecondo ed è quindi diverso.. DateTime .NET, risoluzione diversa durante la conversione da e verso 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 

zecche da un: 634202170964319073

zecche da B: 634202170964310000

L'OADate doppia: 40437,290467951389

Qual è la ragione di questo? La risoluzione di DateTime è chiaramente abbastanza buona.

risposta

3

Il metodo statico chiamato da ToOADate divide chiaramente le zecche di 10000 e quindi memorizza il risultato di una lunga, eliminando così qualsiasi sub informazioni millisecondo

Qualcuno sa dove trovare le specifiche 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

Non esattamente le specifiche, ma vale la pena leggere: http://blogs.msdn.com/b/ericlippert/archive/2003/09/16/eric-s-complete-guide-to-vt-date.aspx –

+0

Grazie, sembra esserci molta storia dietro questo formato data ... –

1

Probabilmente ha qualcosa a che fare con la precisione del doppio, non con il DateTime.

+0

Il doppio convertito è 40437.290467951389, sembra piuttosto preciso, ma potrebbe essere corretto. – Ezombort

+0

Sì, il doppio sarebbe abbastanza preciso, ma a causa del modo in cui viene memorizzato il doppio, non esiste una rappresentazione esatta del datetime come un doppio. – Carvellis

+0

@Ezombort Sembra preciso solo per il fattore di conversione tra secondi e giorni. Se moltiplichi il 'double' che dici, per' 24.0 * 60.0 * 60.0', vedrai solo tre decimali. Un "doppio" "arbitrario" di questa grandezza mostrerà cinque decimali (o sette decimali se si visualizza con '.ToString (" R ")'). Quindi la piena precisione di un 'doppio' non viene utilizzata. Vedi la mia nuova risposta. –

10

Penso che questa sia una domanda eccellente. (Ho appena scoperto.)

A meno che non si opera con date molto vicine l'anno 1900, un DateTime avrà un più alto precisione rispetto una data di OA. Ma per qualche motivo oscuro, gli autori della struttura DateTime solo amano per troncare al più vicino intero millisecondo quando convertono tra DateTime e qualcos'altro. Inutile dire che facendo questo si butta via molta precisione senza una buona ragione.

Ecco un work-around:

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; 
} 

Ora, prendiamo in considerazione (dalla tua domanda) il DateTime data da:

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

La precisione di qualsiasi DateTime è di 0,1 ms.

Vicino la data e l'ora stiamo considerando, la precisione di una data di OA è:

Math.Pow(2.0, -37.0) giorni, o circa 0.6286, microsiemens

Concludiamo che in questa regione un DateTime è più preciso di una data OA di (poco più di) un fattore sei.

Diamo convertire ourDT-double usando il mio metodo di estensione sopra

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

Ora, se si converte ourOADate di nuovo ad un DateTime utilizzando il metodo statico FromOADatePrecise sopra, si ottiene

2010-09-16T06:58:16.4319072 (scritto con Formato "O")

Confrontando con l'originale, vediamo che la perdita di precisione è in questo caso 0,1 μs. Ci aspettiamo che la perdita di precisione sia compresa tra ± 0,4 μs poiché questo intervallo ha una lunghezza di 0,8 μs che è paragonabile a 0,6286 μs menzionati in precedenza.

Se andiamo nella direzione opposta, a partire da un double che rappresenta una data di OA non troppo vicino al 1900, e prima uso FromOADatePrecise, e poiToOADatePrecise, poi torniamo a un double, e perché la precisione dell'intermedio DateTime è superiore a quella di una data OA, ci aspettiamo un round trip perfetto in questo caso. Se, d'altra parte, utilizzi i metodi BCL FromOADate e ToOADate nello stesso ordine, è estremamente improbabile ottenere un buon viaggio di andata e ritorno (a meno che lo double con cui abbiamo iniziato abbia un modulo molto speciale).

+1

E prima era peggio. Anni fa ho trovato un bug in cui a volte le date venivano arrotondate al millesimo di secondo sbagliato, e poteva indurre un errore di almeno due giorni. Fortunatamente penso che alla fine sia stato risolto. –

Problemi correlati