Je dois convertir une date grégorienne standard en un numéro de jour julien julien .
Je n'ai vu rien de documenté en C # pour le faire directement, mais j'ai trouvé de nombreux messages (pendant la recherche sur Google) suggérant l'utilisation de ToOADate .
La documentation sur ToOADate ne suggère pas cela comme une méthode de conversion valide pour les dates juliennes.
Quelqu'un peut-il préciser si cette fonction effectuera la conversion avec précision, ou peut-être une méthode plus appropriée pour convertir DateTime en une chaîne au format Julian.
Ce me fournit le nombre attendu lorsqu'il est validé par rapport à page Julian Day de Wikipedia
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, cela ne comprend pas les nombres magiques utilisés.
Merci
Références:
OADate est similaire aux dates juliennes, mais utilise un point de départ différent (30 décembre 1899 par rapport au 1er janvier 4713 avant JC) et un point de "nouveau jour" différent. Julian Dates considère que midi est le début d'une nouvelle journée, OADates utilise la définition moderne de minuit.
La date julienne de minuit, le 30 décembre 1899 est 2415018,5. Cette méthode devrait vous donner les valeurs appropriées:
public static double ToJulianDate(this DateTime date)
{
return date.ToOADate() + 2415018.5;
}
Quant à l'algorithme:
if (Month < 3) ...
: Pour que les nombres magiques fonctionnent à notre droite, ils mettent février à la "fin" de l'année.(153 * Month - 457) / 5
: Wow, ce sont des chiffres magiques sérieux. (int)(30.6 * Month - 91.4)
. 30,6 est le nombre moyen de jours par mois, hors février (30,63 répétition, pour être exact). 91,4 est presque le nombre de jours en 3 mois non février moyens. (30,6 * 3 est 91,8).365 * Year
: Jours par an(Year / 4) - (Year / 100) + (Year / 400)
: Plus un jour bissextile tous les 4 ans, moins un tous les 100, plus un tous les 400.+ 1721119
: Il s'agit de la date julienne du 2 mars 1 BC. Puisque nous avons déplacé le "début" du calendrier de janvier à mars, nous l'utilisons comme décalage, 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 le calcul du mois entier était encore un peu décalé à la fin. Si l'auteur d'origine avait utilisé - 462
Au lieu de - 457
(- 92.4
Au lieu de - 91.4
En mathématiques à virgule flottante), alors l'offset 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 présente des lacunes importantes.
La date julienne est définie pour les dates négatives - c'est-à-dire les dates BCE (avant l'ère commune) et est courante 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 à l'aide de la méthode ci-dessus.
La réforme du calendrier grégorien de 1582 a mis un trou de 11 jours dans le calendrier entre le 4 et le 15 octobre. Ces dates ne sont définies ni dans le calendrier julien ni dans le calendrier grégorien, mais DateTime les accepte comme arguments. En outre, l'utilisation de la méthode ci-dessus ne renvoie la valeur correcte pour aucune date julienne. Les expériences d'utilisation de System.Globalization.JulianCalendar.ToDateTime () ou de passage de l'ère JulianCalendar dans le constructeur DateTime produisent toujours des résultats incorrects pour toutes les dates antérieures au 5 octobre 1582.
Les routines suivantes, adaptées des "algorithmes astronomiques" de Jean Meeus, renvoient des résultats corrects pour toutes les dates à partir de midi le 1er janvier -4712, heure zéro sur le calendrier julien. Ils lèvent également une ArgumentOutOfRangeException si une date non valide 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 de David Yaw est exacte, mais le calcul du nombre cumulé de jours de l'année pour les mois précédant le mois donné est anti-intuitif. Si vous préférez un tableau d'entiers pour rendre l'algorithme plus clair, cela fera:
/*
* 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)
L'expression
(153*month - 457)/5)
produit cependant la même séquence exacte, les mêmes nombres entiers que le tableau ci-dessus pour les valeurs dans la plage: 3 à 14; inclus et le fait sans aucune exigence de stockage. L'absence d'exigences de stockage n'est que vertu dans le calcul du nombre cumulé de jours de manière telle et obscurcie.
Si quelqu'un a besoin de convertir de date julienne en 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 afin que la valeur résultante corresponde à la date julienne modifiée indiquée sur Wikipedia . 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);
}