Diverse Kalenderfunktionen in C#

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