2010-09-16 10 views
7

Je convertis un DateTime en OADate. Je m'attendais à avoir exactement le même DateTime lors de la conversion de l'OADate, mais maintenant il n'a que la résolution d'une milliseconde, et est donc différent..NET DateTime, résolution différente lors de la conversion de et vers 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 

Tiques d'un: 634202170964319073

Tiques de b: 634202170964310000

Le OADate double: 40437,290467951389

Quelle est la raison? La résolution de DateTime est clairement assez bonne.

Répondre

3

La méthode statique appelée par ToOADate divise clairement les tiques de 10000, puis stocke le résultat dans une longue, éliminant ainsi tout sous milliseconde d'info

Est-ce que quelqu'un sait où trouver les spécifications du format 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

Pas exactement les spécifications, mais cela vaut la peine d'être lu: http://blogs.msdn.com/b/ericlippert/archive/2003/09/16/eric-s-complete-guide-to-vt-date.aspx –

+0

Merci, il semble y avoir beaucoup d'histoire derrière ce format de date ... –

1

Probablement a quelque chose à voir avec la précision du double, pas le DateTime.

+0

Le double converti est 40437.290467951389, semble assez précis, mais vous pourriez être correct. – Ezombort

+0

Oui, le double serait assez précis, mais en raison de la façon dont le double est stocké, il n'y a pas de représentation exacte du datetime en tant que double. – Carvellis

+0

@Ezombort Il semble précis uniquement en raison du facteur de conversion entre les secondes et les jours. Si vous multipliez le «double» que vous mentionnez, par «24,0 * 60,0 * 60,0», vous ne verrez que trois décimales. Un «double» «arbitraire» de cette magnitude montrera cinq décimales (ou sept décimales si vous affichez '.ToString (" R ")'). Donc, la précision d'un «double» n'est pas utilisée. Voir ma nouvelle réponse. –

10

Je pense que c'est une excellente question. (Je viens de le découvrir.)

À moins que vous opérez avec des dates assez proches de l'année 1900, un DateTime aura un plus précision qu'une date d'OA. Mais pour une raison obscure, les auteurs de DateTime struct juste aiment tronquer à la milliseconde entière près lorsqu'ils convertissent entre DateTime et quelque chose d'autre. Inutile de dire que faire cela jette beaucoup de précision sans raison valable.

est ici un travail autour:

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

Maintenant, nous allons examiner (de votre question) la DateTime donnée par:

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

La précision de toute DateTime est de 0,1 ms.

Près de la date et l'heure que nous envisageons, la précision d'une date d'OA est:

Math.Pow(2.0, -37.0) jours, ou vers 0.6286 ps

Nous concluons que dans cette région un DateTime est plus précis qu'une date OA par (juste au-dessus) un facteur six.

Convertissons ourDT-double en utilisant ma méthode d'extension au-dessus

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

Maintenant, si vous convertissez ourOADate revenir à un DateTime en utilisant la méthode FromOADatePrecise statique ci-dessus, vous obtenez

2010-09-16T06:58:16.4319072 (écrit avec "O" format)

En comparant avec l'original, on constate que la perte de précision est dans ce cas de 0,1 μs. Nous nous attendons à ce que la perte de précision soit de ± 0,4 μs puisque cet intervalle a une longueur de 0,8 μs, ce qui est comparable aux 0,6286 μs mentionnés précédemment.

Si nous dans l'autre sens, en commençant par un double représentant une date d'OA pas trop proche de l'année 1900, et premier utilisation FromOADatePrecise et puisToOADatePrecise, alors nous revenons à un double, et parce que la précision de l'intermédiaire DateTime est supérieure à celle d'une date OA, on s'attend à un aller-retour parfait dans ce cas. Si, d'un autre côté, vous utilisez les méthodes BCL FromOADate et ToOADate dans le même ordre, il est extrêmement improbable d'obtenir un bon aller-retour (sauf si le double que nous avons commencé a une forme très spéciale).

+1

Et c'était pire. Il y a quelques années, j'ai trouvé un bug où parfois les dates étaient arrondies à la milliseconde près, ce qui pouvait induire une erreur de deux jours. Heureusement, je pense que cela a finalement été réparé. –