Convertir DateTime en date julienne en C# (ToOADate Safe?)
j'ai besoin de convertir à partir d'un standard Grégorien date Julien nombre de jours.
Je n'ai rien vu documenté dans C# pour le faire directement, mais j'ai trouvé de nombreux messages (en Googlant) suggérant l'utilisation de ToOADate.
la documentation sur ToOADate ne suggère pas cela comme méthode de conversion valide pour les dates juliennes.
est-ce que quelqu'un peut clarifier si cette fonction effectuera la conversion exactement, ou peut-être une méthode plus appropriée pour convertir DateTime Julienne chaîne formatée.
me fournit le nombre attendu lorsqu'il est validé par rapport à Page Julienne de Wikipédia
public static long ConvertToJulian(DateTime Date)
{
int Month = Date.Month;
int Day = Date.Day;
int Year = Date.Year;
if (Month < 3)
{
Month = Month + 12;
Year = Year - 1;
}
long JulianDay = Day + (153 * Month - 457) / 5 + 365 * Year + (Year / 4) - (Year / 100) + (Year / 400) + 1721119;
return JulianDay;
}
cependant, ceci est sans une compréhension des nombres magiques étant utiliser.
Merci
Références:
5 réponses
OADate est similaire à Julian Dates, mais utilise un point de départ différent (décembre 30, 1899 vs. Janvier 1, 4713 BC), et un nouveau point de 'jour' différent. Julian Dates considèrent midi pour être le début d'un nouveau jour, Odates utilisent la définition moderne, minuit.
La Date Julienne de minuit, le 30 décembre, 1899 est 2415018.5. Cette méthode devrait vous donner les bonnes valeurs:
public static double ToJulianDate(this DateTime date)
{
return date.ToOADate() + 2415018.5;
}
Comme pour l'algorithme:
if (Month < 3) ...
: faire De la magie les chiffres marchent à notre droite, ils mettent février à la " fin " de l'année.(153 * Month - 457) / 5
: Wow, c'est une grave numéros de magie.- Normalement, le nombre de jours dans chaque mois est 31 28 31 30 31 30 31 31 30 31 30 31, mais après que l'ajustement de l'instruction if, il devient 31 30 31 30 31 31 30 31 30 31 31 28. Ou, soustraire 30 et vous vous retrouvez avec 1 0 1 0 1 1 0 1 0 1 1 -2. Ils créent ce motif de 1s et 0s en faisant cette division dans l'espace entier.
- Ré-écrit à la virgule flottante, il serait
(int)(30.6 * Month - 91.4)
. 30.6 est le nombre moyen de jours par mois, à l'exclusion de février (30.63 répéter, pour être exact). 91,4 est presque le nombre de jours dans 3 mois moyens autres que Février. (30,6 * 3 est 91,8). - alors, retirons les 30, et concentrons-nous sur ces 0,6 jours. Si on le multiplie par le nombre de mois, et qu'on se tronque à un entier, on aura un motif de 0s et 1s.
- 0.6 * 0 = 0.0 -> 0
- 0.6 * 1 = 0.6 -> 0 (différence de 0)--19-->
- 0.6 * 2 = 1.2 -> 1 (différence de 1)--19-->
- 0.6 * 3 = 1.8 -> 1 (différence de 0)--19-->
- 0.6 * 4 = 2.4 -> 2 (différence de 1)--19-->
- 0.6 * 5 = 3.0 -> 3 (différence de 1)--19-->
- 0.6 * 6 = 3.6 -> 3 (différence de 0)--19-->
- 0.6 * 7 = 4.2 -> 4 (différence de 1)--19-->
- 0.6 * 8 = 4.8 -> 4 (différence de 0)--19-->
- voir ce schéma de des différences à droite? C'est la même tendance dans la liste ci-dessus, le nombre de jours dans chaque mois moins 30. La soustraction de 91,8 compenserait le nombre de jours dans les trois premiers mois, qui ont été déplacés à la " fin " de l'année, et en l'ajustant de 0,4 déplace les différences successives de 1 (0,6 * 4 et 0,6 * 5 dans le tableau ci-dessus) pour s'aligner avec les mois adjacents qui sont de 31 jours.
- depuis Février est maintenant à la "fin" de l'année, nous n'avons pas besoin de traiter avec son longueur. Il pourrait être de 45 jours (46 sur une année bissextile), et la seule chose à changer est la constante pour le nombre de jours dans une année, 365.
- notez que cela dépend de la tendance des jours de 30 et 31 mois. Si nous avions deux mois de suite qui étaient de 30 jours, ce ne serait pas possible.
365 * Year
: jours par an(Year / 4) - (Year / 100) + (Year / 400)
: Plus un jour de saut tous les 4 ans, moins un jour sur 100, plus un jour sur 400.+ 1721119
: C'est la date julienne du 2 mars, 1 avant JC. Puisque nous avons déplacé le "début" du calendrier de janvier à Mars, nous l'utilisons comme offset, plutôt que le 1er janvier. Puisqu'il n'y a pas d'année zéro, 1 BC obtient la valeur entière 0. Quant à savoir pourquoi le 2 mars au lieu du 1er Mars, je suppose que c'est parce que tout ce calcul de mois était encore un peu hors à la fin. Si l'auteur avait utilisé- 462
au lieu de- 457
(- 92.4
au lieu de- 91.4
en virgule flottante maths), alors le décalage aurait été au 1er mars.
alors que la méthode
public static double ToJulianDate(this DateTime date) { return date.ToOADate() + 2415018.5; }
fonctionne pour les dates modernes, il a des lacunes importantes.
la date julienne est définie pour les dates négatives-I. e, BCE (avant l'ère commune) date et est commun dans les calculs astronomiques. Vous ne pouvez pas construire un objet DateTime avec l'année inférieure à 0, et donc la Date julienne ne peut pas être calculée pour les dates BCE en utilisant la méthode ci-dessus.
La suite routines, adapté des "algorithmes astronomiques" de Jean Meeus, retourne des résultats corrects pour toutes les dates à partir de midi le 1er janvier, -4712, heure zéro sur le calendrier julien. Ils jettent également un argument Outofrangeexception si une date invalide est passée.
public class JulianDate
{
public static bool isJulianDate(int year, int month, int day)
{
// All dates prior to 1582 are in the Julian calendar
if (year < 1582)
return true;
// All dates after 1582 are in the Gregorian calendar
else if (year > 1582)
return false;
else
{
// If 1582, check before October 4 (Julian) or after October 15 (Gregorian)
if (month < 10)
return true;
else if (month > 10)
return false;
else
{
if (day < 5)
return true;
else if (day > 14)
return false;
else
// Any date in the range 10/5/1582 to 10/14/1582 is invalid
throw new ArgumentOutOfRangeException(
"This date is not valid as it does not exist in either the Julian or the Gregorian calendars.");
}
}
}
static private double DateToJD(int year, int month, int day, int hour, int minute, int second, int millisecond)
{
// Determine correct calendar based on date
bool JulianCalendar = isJulianDate(year, month, day);
int M = month > 2 ? month : month + 12;
int Y = month > 2 ? year : year - 1;
double D = day + hour/24.0 + minute/1440.0 + (second + millisecond / 1000.0)/86400.0;
int B = JulianCalendar ? 0 : 2 - Y/100 + Y/100/4;
return (int) (365.25*(Y + 4716)) + (int) (30.6001*(M + 1)) + D + B - 1524.5;
}
static public double JD(int year, int month, int day, int hour, int minute, int second, int millisecond)
{
return DateToJD(year, month, day, hour, minute, second, millisecond);
}
static public double JD(DateTime date)
{
return DateToJD(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, date.Millisecond);
}
}
L'explication par David Lacet est sur place, mais le calcul du nombre total de jours de l'année pour le mois précédant le mois donné est anti-intuitif. Si vous préférez un tableau d'entiers de rendre l'algorithme plus clair alors cela va faire:
/*
* convert magic numbers created by:
* (153*month - 457)/5)
* into an explicit array of integers
*/
int[] CumulativeDays = new int[]
{
-92 // Month = 0 (Should not be accessed by algorithm)
, -61 // Month = 1 (Should not be accessed by algorithm)
, -31 // Month = 2 (Should not be accessed by algorithm)
, 0 // Month = 3 (March)
, 31 // Month = 4 (April)
, 61 // Month = 5 (May)
, 92 // Month = 6 (June)
, 122 // Month = 7 (July)
, 153 // Month = 8 (August)
, 184 // Month = 9 (September)
, 214 // Month = 10 (October)
, 245 // Month = 11 (November)
, 275 // Month = 12 (December)
, 306 // Month = 13 (January, next year)
, 337 // Month = 14 (February, next year)
};
et les trois premières lignes du calcul deviennent alors:
int julianDay = day
+ CumulativeDays[month]
+ 365*year
+ (year/4)
(153*month - 457)/5)
bien que produit la même séquence exacte même entiers que le tableau ci-dessus pour les valeurs dans la gamme: 3 à 14; inclus et ne nécessite pas de stockage. Le manque d'exigences de stockage n'est qu'une vertu dans le calcul du nombre cumulatif de jours de telle manière et de manière confuse.
Si quelqu'un a besoin de convertir Julian date DateTime , voir ci-dessous :
public static DateTime FromJulianDate(double julianDate)
{
return DateTime.FromOADate(julianDate - 2415018.5);
}
mon code pour la date julienne modifiée utilise le même algorithme mais un nombre magique différent à la fin pour que la valeur résultante corresponde à la Date julienne modifiée affichée sur Wikipédia. J'utilise ce même algorithme depuis au moins 10 ans comme clé pour les séries chronologiques quotidiennes (à L'origine en Java).
public static int IntegerDate(DateTime date)
{
int Month = date.Month;
int Day = date.Day;
int Year = date.Year;
if (Month < 3)
{
Month = Month + 12;
Year = Year - 1;
}
//modified Julian Date
return Day + (153 * Month - 457) / 5 + 365 * Year + (Year / 4) - (Year / 100) + (Year / 400) - 678882;
}
le calcul inverse a plus de nombres magiques pour votre amusement:
public static DateTime FromDateInteger(int mjd)
{
long a = mjd + 2468570;
long b = (long)((4 * a) / 146097);
a = a - ((long)((146097 * b + 3) / 4));
long c = (long)((4000 * (a + 1) / 1461001));
a = a - (long)((1461 * c) / 4) + 31;
long d = (long)((80 * a) / 2447);
int Day = (int)(a - (long)((2447 * d) / 80));
a = (long)(d / 11);
int Month = (int)(d + 2 - 12 * a);
int Year = (int)(100 * (b - 49) + c + a);
return new DateTime(Year, Month, Day);
}