public class Calendar
{
public const int MillisecondsPerSecond = 1000;
public const int SecondsPerMinute = 60;
public const int MinutesPerHour = 60;
public const int HoursPerDay = 24;
public const int DaysPerWeek = 7;
public const int MonthPerYear = 12;
public const int DaysPerNormalYear = 365;
public const int DaysPerLeapYear = 366;
#region Zeitspannenkonvertierung
public static int SecondsToMilliseconds(int seconds)
{
return MillisecondsPerSecond * seconds;
}
public static int MinutesToSeconds(int minutes)
{
return SecondsPerMinute * minutes;
}
public static int HoursToMinutes(int hours)
{
return MinutesPerHour * hours;
}
public static int DaysToHours(int days)
{
return HoursPerDay * days;
}
public static int MinutesToMilliseconds(int minutes)
{
return SecondsToMilliseconds(MinutesToSeconds(minutes));
}
public static int HoursToMilliseconds(int hours)
{
return MinutesToMilliseconds(HoursToMinutes(hours));
}
public static int DaysToMilliseconds(int days)
{
return HoursToMilliseconds(DaysToHours(days));
}
public static int HoursToSeconds(int hours)
{
return MinutesToSeconds(HoursToMinutes(hours));
}
public static int DaysToSeconds(int days)
{
return HoursToSeconds(DaysToHours(days));
}
public static int DaysToMinutes(int days)
{
return HoursToMinutes(DaysToHours(days));
}
public static int MillisecondsToSeconds(int milliSeconds)
{
return milliSeconds / MillisecondsPerSecond;
}
public static int SecondsToMinutes(int seconds)
{
return seconds / SecondsPerMinute;
}
public static int MinutesToHours(int minutes)
{
return minutes / MinutesPerHour;
}
public static int HoursToDays(int hours)
{
return hours / HoursPerDay;
}
public static int MillisecondsToMinutes(int milliSeconds)
{
return SecondsToMinutes(MillisecondsToSeconds(milliSeconds));
}
public static int MillisecondsToHours(int milliSeconds)
{
return MinutesToHours(MillisecondsToMinutes(milliSeconds));
}
public static int MillisecondsToDays(int milliSeconds)
{
return HoursToDays(MillisecondsToHours(milliSeconds));
}
public static int SecondsToHours(int seconds)
{
return MinutesToHours(SecondsToMinutes(seconds));
}
public static int SecondsToDays(int seconds)
{
return HoursToDays(SecondsToHours(seconds));
}
public static int MinutesToDays(int minutes)
{
return HoursToDays(MinutesToHours(minutes));
}
#endregion
/// <summary>
/// Berechnet den Tagesbruchteil einer Uhrzeit.
/// </summary>
/// <param name="hours">Die Stundenzahl [0...23].</param>
/// <param name="minutes">Die Minutenzahl [0...59].</param>
/// <param name="seconds">Die Sekundenzahl mit Dezimalen [0...60).</param>
/// <returns>Der Tagesbruchteil.</returns>
/// <exception cref="ArgumentException">Wenn ein Uhrzeit-Argument ungültig ist.</exception>
public static double GetDayFraction(int hours, int minutes, double seconds)
{
const string allowedPrefix = "Allowed interval: [0...";
if (hours < 0 || hours >= HoursPerDay)
{
throw new ArgumentException(allowedPrefix + (HoursPerDay - 1) + "]", "hours");
}
else if (minutes < 0 || minutes >= MinutesPerHour)
{
throw new ArgumentException(allowedPrefix + (MinutesPerHour - 1) + "]", "minutes");
}
else if (seconds < 0 || seconds >= SecondsPerMinute)
{
throw new ArgumentException(allowedPrefix + SecondsPerMinute + ")", "seconds");
}
else
{
int totalMinutes = HoursToMinutes(hours) + minutes;
double totalSecounds = MinutesToSeconds(totalMinutes) + seconds;
double result = totalSecounds / DaysToSeconds(1);
return result;
}
}
/// <summary>
/// Errechnet, ob ein Jahr ein Schaltjahr ist.
/// Im Gegensatz zur <see cref="DateTime.IsLeapYear" />-Methode
/// wird auch die Julianische Schaltjahresregel berücksichtigt.
/// </summary>
/// <param name="year">
/// Das Jahr.
/// Für die vorchristlichen Jahre wird die astronomische,
/// nicht die historische Zählweise vorausgesetzt.
/// Das vor dem Jahr 1 n. Chr. liegende Jahr wird daher als Jahr 0 gezählt (astronomisch),
/// nicht als Jahr 1 v. Chr. (historisch),
/// das vor diesem liegende Jahr wird als Jahr -1 und nicht als Jahr 2 v. Chr. gezählt, usw.
/// </param>
/// <returns>True bei einem Schaltjahr. Sonst false.</returns>
public static bool IsLeapYear(int year)
{
bool result;
if (year <= 1582)
{
//Julian calendar
result = (year % 4 == 0);
}
else
{
//Gregorian calendar
result = ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
}
return result;
}
/// <summary>
/// Ermittelt, ob ein Datum ein gültiges Datum ist.
/// </summary>
/// <param name="years">Die Kalenderjahre (astronomische Zählweise).</param>
/// <param name="months">Die Kalendermonate.</param>
/// <param name="days">Die Kalendertage.</param>
/// <returns>True bei einem gültigen Datum. Sonst false.</returns>
public static bool IsValidDate(int years, int months, int days)
{
bool result;
if (
years == 1582 && months == 10 && (days > 4 && days < 15)
)
{
result = false;
}
else
{
if (days <= 0)
{
result = false;
}
else
{
switch (months)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
{
int maxDays = 31;
result = (days <= maxDays);
}
break;
case 4:
case 6:
case 9:
case 11:
{
int maxDays = 30;
result = (days <= maxDays);
}
break;
case 2:
{
bool isLeapYear = IsLeapYear(years);
int maxDays = (
isLeapYear ?
29 :
28
);
result = (days <= maxDays);
}
break;
default:
result = false;
break;
}
}
}
return result;
}
/// <summary>
/// Ermittelt das Kalenderdatum aus einem Julianischen Tag.
/// </summary>
/// <param name="julianDate">Der Julianische Tag.</param>
/// <param name="years">Die Kalenderjahre (astronomische Zählweise).</param>
/// <param name="months">Die Kalendermonate.</param>
/// <param name="days">Die Kalendertage mit Dezimalen.</param>
/// <exception cref="ArgumentException">Wenn der Julianische Tag ungültig ist.</exception>
public static void JulianDayToYMD(double julianDay, out int years, out int months, out double days)
{
if (julianDay >= 0.0)
{
double N = julianDay + 0.5;
int Z = (int)N;
double F = N - Z;
int A;
{
if (Z >= 2299161)
{
int g = (int)((Z - 1867216.25) / 36524.25);
A = Z + 1 + g - (int)(g / 4);
}
else
{
A = Z;
}
}
int B = A + 1524;
int C = (int)((B - 122.1) / 365.25);
int K = (int)(365.25 * C);
int E = (int)((B - K) / 30.6001);
days = B - K - (int)(30.6001 * E) + F;
if (E <= 13)
{
months = E - 1;
years = C - 4716;
}
else
{
months = E - 13;
years = C - 4715;
}
}
else
{
//not a valid Julian Day
throw new ArgumentException("Not a valid Julian Day.");
}
}
#region GetJulianDay
/// <summary>
/// Ermittelt den Julianischen Tag aus einer <see cref="DateTime" />-Instanz.
/// Das in der <see cref="DateTime" />-Instanz enthaltende Kalenderdatum muss im Intervall
/// [-4712-01-01.5 ... +1582-10-05.0)
/// oder
/// [+1582-10-15.0 ... +INF)
/// liegen.
/// </summary>
/// <param name="dateTime">Das Kalenderdatum als <see cref="DateTime" />-Instanz.</param>
/// <returns>Der Julianische Tag.</returns>
/// <exception cref="ArgumentException">Wenn das in der <see cref="DateTime" />-Instanz enthaltende Kalenderdatum nicht in einem gültigen Intervall liegt.</exception>
public static double GetJulianDay(DateTime dateTime)
{
double result = GetJulianDay(
dateTime.Year,
dateTime.Month,
dateTime.Day + dateTime.TimeOfDay.TotalDays
);
return result;
}
/// <summary>
/// Ermittelt den Julianischen Tag aus einem Kalenderdatum und einer -uhrzeit.
/// Das Kalenderdatum muss im Intervall
/// [-4712-01-01.5 ... +1582-10-05.0)
/// oder
/// [+1582-10-15.0 ... +INF)
/// liegen.
/// </summary>
/// <param name="years">Die Kalenderjahre (astronomische Zählweise).</param>
/// <param name="months">Die Kalendermonate.</param>
/// <param name="days">Die Kalendertage.</param>
/// <param name="hours">Die Stundenzahl [0...23].</param>
/// <param name="minutes">Die Minutenzahl [0...59].</param>
/// <param name="seconds">Die Sekundenzahl mit Dezimalen [0...60).</param>
/// <returns>Der Julianische Tag.</returns>
/// <exception cref="ArgumentException">
/// Wenn das Kalenderdatum ungültig ist oder
/// wenn ein Uhrzeit-Argument ungültig ist oder
/// wenn das Kalenderdatum nicht in einem gültigen Intervall liegt.
/// </exception>
public static double GetJulianDay(int years, int months, int days, int hours, int minutes, double seconds)
{
double dayFraction = GetDayFraction(hours, minutes, seconds);
double result = GetJulianDay(years, months, days + dayFraction);
return result;
}
/// <summary>
/// Ermittelt den Julianischen Tag aus einem Kalenderdatum.
/// Das Kalenderdatum muss im Intervall
/// [-4712-01-01.5 ... +1582-10-05.0)
/// oder
/// [+1582-10-15.0 ... +INF)
/// liegen.
/// </summary>
/// <param name="years">Die Kalenderjahre (astronomische Zählweise).</param>
/// <param name="months">Die Kalendermonate.</param>
/// <param name="days">Die Kalendertage.</param>
/// <returns>Der Julianische Tag.</returns>
/// <exception cref="ArgumentException">
/// Wenn das Kalenderdatum ungültig ist oder
/// wenn das Kalenderdatum nicht in einem gültigen Intervall liegt.
/// </exception>
public static double GetJulianDay(int years, int months, int days)
{
double result = GetJulianDay(years, months, (double)days);
return result;
}
/// <summary>
/// Ermittelt den Julianischen Tag aus einem Kalenderdatum.
/// Das Kalenderdatum muss im Intervall
/// [-4712-01-01.5 ... +1582-10-05.0)
/// oder
/// [+1582-10-15.0 ... +INF)
/// liegen.
/// Quelle: Wepner, W.: Mathematisches Hilfsbuch für Studierende und Freunde der Astronomie, Treugesell-Verlag Dr. Vehrenberg KG, Düsseldorf, 2. berichtigte und erweiterte Auflage 1982
/// </summary>
/// <param name="years">Die Kalenderjahre (astronomische Zählweise).</param>
/// <param name="months">Die Kalendermonate.</param>
/// <param name="days">Die Kalendertage mit Dezimalen.</param>
/// <returns>Der Julianische Tag.</returns>
/// <exception cref="ArgumentException">
/// Wenn das Kalenderdatum ungültig ist oder
/// wenn das Kalenderdatum nicht in einem gültigen Intervall liegt.
/// </exception>
public static double GetJulianDay(int years, int months, double days)
{
bool isValidDate = IsValidDate(years, months, (int)days);
if (!isValidDate)
{
//not a valid calendar date
string errorText = "Not a valid calendar date.";
throw new ArgumentException(errorText);
}
else if (
years < -4712 ||
years == -4712 && months == 1 && days < 1.5
)
{
//for Julian Day not allowed
string errorText =
"Allowed intervals: " +
"[-4712-01-01.5 ... +1582-10-05.0)" +
" and " +
"[+1582-10-15.0 ... +INF)."
;
throw new ArgumentException(errorText);
}
else
{
//allowed
int y;
int m;
if (months <= 2)
{
y = years + 4712 - 1;
m = months + 1 + 12;
}
else
{
y = years + 4712;
m = months + 1;
}
double B;
{
//check if Gregorian Calendar
int KG = 10000 * 1582 + 100 * 10 + 15;
int K = 10000 * years + 100 * months + (int)days;
if (K < KG)
{
//Julian Calendar
B = -63.5;
}
else
{
//Gregorian Calendar
int A = (int)((y + 88) / 100.0);
B = -63.5 + 38 - A + (int)(A / 4);
}
}
double result =
(int)(365.25 * y) -
(y < 0 ? 1 : 0) + //Correction for int cast in -4712-01 or -4712-02
(int)(30.6001 * m) +
B +
days
;
return result;
}
}
#endregion
}