15 minutes
(School) ~ Informatik Test Vorbereitung
Kalender-algorithmen
Zeller
Um den Kalender nach Wochen und Monaten optisch zu gliedern, ist es erforderlich, dass der Wochentag für ein gegebenes Datum bestimmt werden kann. Die Gesetzmäßigkeiten des immerwährenden Kalenders wurde von Christoph Zeller 1885 zu einer mathematischen Formel zusammengefass. Diese Formael erlaubt es, für jedes Datum den Wochentag zu bestimmen,
w = (r+[2,61 + M - 0,2] + J + [J/4] + [H/4] - 2 * H) % 7
H ==> Jahrhundert (e.g.: 2000 --> 19 || 2010 --> 20)
J ==> Jahreszahl innerhalb des Jahrhunderts (e.g.: 2010 --> 10 || 1999 --> 99)
M ==> Monat (e.g.: März == 1 && Februar == 12)
T ==> Tag
W ==> Wochentag (e.g.: Sonntag --> 0 && Samstag --> 6s)
[x] ==> Gaußsche Klammerfunktion (e.g.: [2,8] --> 2 || [-2,8] --> -3)
Beispiel: Auf welchen Wochentag fällt der 1.Januar 1998?
H = 19, J = 97 (altrömische Zeitrechung), M = 11 (altrömische Zeitrechung), T = 1
fx(T, M, J, H)
= 116 % 7
= 4
Ergebnis: Der 1. Januar 1998 ist ein Donnerstag.
using System;
class Zeller{
public static void Main(){
Console.WriteLine(GetZeller(27, 09, 2016));
Console.WriteLine(GetZeller(01, 01, 1998));
}
#region Calender calculation
public static string GetZell(int h)
{
switch (h)
{
case 0:
return "Saturday";
case 1:
return "Sunday";
case 2:
return "Monday";
case 3:
return "Tuesday";
case 4:
return "Wednesday";
case 5:
return "Thursday";
case 6:
return "Friday";
default:
return "Error: Maybe wrong Input Format?";
}
}
public static string GetZeller(byte dd, byte mm, int yyyy)
{
int h = 0;
double q = dd, m = mm, y = yyyy, J = 0, K = 0;
switch ((int)m)
{
case 1:
m = 13; y--; break;
case 2:
m = 14; y--; break;
}
J = Math.Floor(y / 100);
K = y - J * 100;
h = (int)((q + Math.Floor(((m + 1) * 26) / 10) + K + Math.Floor(K / 4) + Math.Floor(J / 4) - 2 * J) % 7);
return GetZell(h < 0 ? h + 7 : h);
}
#endregion
}
Robertson
Seit Zeller ist von verschiedenen Mathematikern und Astronomen eine ganze Reihe von weiteren Kalenderformeln aufgestellt worden. Sie benötigen alle bestimmte Monatskennzahlen, die auch noch davon abhängig sind, obein Schaltjahr vorliegt oder nicht. Einen einfachen Kalender-Algorithmus liefert die Formel von J.D. Robertson aus Collected Algorithms (CA) CACM 689 - R1. Er korrigierte damit die Fehler im Algorithmus 398 von R.A. Stone.
robertson
Zusicherung: Gültiges Datum T.M.J des Gregorianischen Kalenders
A = M + 10
B = [(M - 14) / 12] + J
C = A - 12 [A / 13]
D = [(13C - 1) / 5]
E = [5 * (B % 100) / 4]
W = (D + T + 77 + E + [B / 400] - 2 * [B / 100]) % 7
Nachbedingung: Wochentag W in normaler Zählung
using System;
class Robertson{
public static void Main(){
Console.WriteLine(GetRobin(27, 09, 2016));
Console.WriteLine(GetRobin(01, 01, 1998));
}
#region Calender calculation
public static object GetRobin(byte day, byte month, int year){
switch(GetRobinson(day, month, year)){
case 0:
return "Sunday";
case 1:
return "Monday";
case 2:
return "Tuesday";
case 3:
return "Wednesday";
case 4:
return "Thursday";
case 5:
return "Friday";
case 6:
return "Saturday";
default:
return "Could not convert given number to a String.";
}
}
private static int GetRobinson(byte day, byte month, int year){
int lmon;
int mmon;
lmon = month + 10;
mmon = (month - 14) / 12 + year;
return ((((13 * (lmon - (lmon / 13) * 12) - 1) / 5 + day + 77
+ 5 * (mmon - (mmon / 100) * 100) / 4
+ mmon / 400 - (mmon / 100) * 2)
- 1) % 7 + 1
);
}
#endregion
}
Julian Calendar
In der Astronomie und Raumfahrt verwendet man einen fortlaufenden Kalender, das sog. Julianische Datum. Damit lassen sich alle Ereignisse wie Sonnen bzw. Mond Finsternisse, Perihel - Durchgänge von Kometen, Sternbedeckungen usw. berechnen. Das Julianische Datum zählt die Tage seit 1.1.4713 v.Chr., dem fiktiven Beginn der ägyptischen Kalenderrechnung (sog. Scalinger-Ära) Für ein Datum des Gregorianischen Kalenders kann da Julianische Datum mit Hilfe des Verfahrens von H.F. Fliegel und T.C. van Fladern (A Machine Algorithm for Processing Calendar Dates, ACM 11, 657, 1968) berechnet werden.
juldat (ConvertToJulian)
Zusicherung: Gültiges Datum T.M.J des Gregorianischen Kalenders
K = [(M - 14) / 12]
L = J + K + 4800
JD = T - 32075 + 1461 * [L/4] + 367 * [(M - 2 - 12 * K) / 12] - 3 * [(L + 100) / 400]
Nachbedingung: Julianisches Datum JD
Der Julianische Kalender wurde von Julius Caesar eingeführt, und war in manchen teilen der welt noch weit bis ins 20. Jahrhundert gültig, im kirlichen Bereich teilweise noch bis heute. Er wird heute in der Wissenschaft rückwirkend auch für die Jahre vor dem wirken Caesars verwendet. Er wurde seit dem 16. Jahrhundert sukzessiv durch den Gregorianischen Kalender abgelöst.
Beim Julianischen Kalender ist zu beachten, dass es das Jahr 0 nicht gegeben hat; d.h. auf jahr 1
2nd Algorithm:
juldat2 (ConvertToJulian2)
Zusicherung: Gültiges Datum T.M.J
GREG = 588829
Wenn J < 0
Dann J = J + 1
Wenn M > 2
Dann M = M + 1
Sonst J = J -1,
M = M + 13
JD = [1461 * J / 4] + [153 * M / 5] + T + 1720995
D = T + 31 * (M + 12 * J)
Wenn D >= GREG
Dann A = [J/100],
JD = JD + 2 - A + [A/4]
Nachbedingung: Julianisches Datum JD
Mit Hilfe des Julianischen Datums lassen sich bequem auch die Tagesdifferenz zwischen zwei Daten und auch der Wochentage bestimmen.
using System;
class JulCal{
public static void Main(){
Console.WriteLine(ConvertToJulian(27, 09, 2016));
Console.WriteLine(ConvertToJulian(01, 01, 1998));
Console.WriteLine(ConvertToJulian2(27, 09, 2016));
Console.WriteLine(ConvertToJulian2(01, 01, 1998));
}
#region Julianischer Calender
public static long ConvertToJulian(int day, int month, int year){
var K = ((month-14)/12);
var L = year+K+4800;
var JD = day - 32075 + 1461 * (L / 4) + 367 * ((month-2-12*K) / 12) -3 * ((L+100)/400);
return JD;
}
public static long ConvertToJulian2(int Day, int Month, int Year)
{
if (Month < 3)
{
Month = Month + 12;
Year = Year - 1;
}
long JD = Day + (153 * Month - 457) / 5 + 365 * Year + (Year / 4) - (Year / 100) + (Year / 400) + 1721119;
return JD;
}
#endregion
}
Gregorian Calendar
Folgender Algorithmus gregdat bestimmt zu einem Julianischen Datum JD das dazugehörige Gregorianische Datum T.M.J und liefert damit die Umkehrung des Algorithmus juldat. Er stammt ebenfalls von FLiegel/van Fladern.
gregdat (ConvertToGregorian)
Zusicherung: JD ist Julianisches Datum
L = JD + 68569
N = [(4 * L) / 146097]
L = L - [(146097 * N + 3) / 4]
J = [4000(L + 1) / 1461001]
L = L - 1461 * [J / 4] + 31
M = [80L / 2447]
T = L - [2447M / 80]
L = [M / 11]
M = M + 2 - 12L
J = 100(N - 49) + L + J
Nachbedingung: Gregorianisches Datum T.M.J
Für beliebige Daten gibt es eine Umkehrung zum Algotithmus juldat2:
gregdat2 (ConvertToGregorian2)
Zusicherung: JD ist Julianisches Datum
GREG = 2299161
JD = JD + 1
Wenn JD < GREG
Dann A = JD
Sonst ALPHA = [(JD-1867216.25) / 36524.25],
A = JD + 1 + ALPHA - [ALPHA / 4]
B = A + 1524
C = [(B - 122.1) / 365.25]
D = [1461 * C / 4]
E = [(B - D) / 30.6001]
T = (B - D) - [30.6001 * E] - 1
Wenn E < 13.5
Dann M = E - 1
Sonst M = E - 13
Wenn M > 2.5
Dann J = C - 4716
Sonst J = C - 4715
Nachbedingung: Gregorianisches Datum T.M.J
Die in der astronomischen Literatur angegebenen Algorithmen liefern meist einen um 1 höheren Wert für das Julianische Datum, da bei den Astronomen das Julianische Datum bereits um Mittag wechselt. Die hier gegebenen Versionen entsprechen der Zählweise aus dem ACM
using System;
class Greg<T> {
T Day;
T Month;
T Year;
public Greg(T d, T m, T y){
Day = d;
Month = m;
Year = y;
}
public override string ToString(){
return Day+"."+Month+"."+Year;
}
}
class GregCal{
public static void Main(){
Console.WriteLine(ConvertToGregorian(2423));
Console.WriteLine(ConvertToGregorian(234234));
Console.WriteLine(ConvertToGregorian2(53464574));
Console.WriteLine(ConvertToGregorian2(5566456));
}
#region Julianischer Calender
public static Greg<long> ConvertToGregorian(long JD){
var L = JD+68569;
var N = ((4*L)/146097);
L = L - ((146097 * N + 3) / 4);
var J = (4000*(L+1)/1461001);
L = L - 1461 * (J / 4) +31;
var M = (80*L/2447);
var T = L - (2447M/80);
L = (M / 11);
M = M + 2 - 12 * L;
J = 100*(N-49)+L+J;
return new Greg<long>((long)T, (long)M, (long)J);
}
public static Greg<long> ConvertToGregorian2(long JD)
{
var GREG = 2299161;
JD = JD+1;
long A = 0;
if (JD < GREG) {
A = JD;
} else {
var ALPHA = ((JD-1867216.25f) / 36524.25f);
A = (long)Math.Round(JD+1+ALPHA - (ALPHA / 4));
}
var B = A + 1524;
var C = ((B-122.1f) / 365.25f);
var D = (1461*C/4);
var E = ((B-D)/30.6001f);
var T = (B-D) - (30.6001f * E) -1;
long M = 0;
long J = 0;
if (E < 13.5f) {
M = (long)Math.Round(E - 1);
} else {
M = (long)Math.Round(E - 13);
}
if (M > 2.5){
J = (long)Math.Round(C-4716);
} else {
J = (long)Math.Round(C - 4715);
}
return new Greg<long>((long)T, (long)M, (long)J);
}
#endregion
}
Gauss
Das zeug mit den 2 Tabellen (M, N)
using System;
class Date{
public int Day {get;set;}
public int Month {get;set;}
public int Year {get;set;}
public Date(int d, int m, int y){
Day = d;
Month = m;
Year = y;
}
public override string ToString(){
return String.Format("Ostern is am: {0}.{1}.{2}", Day, Month, Year);
}
}
class Gauss{
public static void Main(){
Console.WriteLine(GetGauss(2016));
Console.WriteLine(GetGauss(1998));
}
private static int M(int yyyy){
if (yyyy >= 1582 && yyyy <= 1699){
return 22;
} else if (yyyy >= 1700 && yyyy <= 1799){
return 23;
} else if (yyyy >= 1800 && yyyy <= 1899){
return 23;
} else if (yyyy >= 1900 && yyyy <= 2099){
return 24;
} else if (yyyy >= 2100 && yyyy <= 2199){
return 24;
} else if (yyyy >= 2200 && yyyy <= 2299){
return 25;
} else if (yyyy >= 2300 && yyyy <= 2399){
return 26;
} else if (yyyy >= 2400 && yyyy <= 2499){
return 25;
} else {
return -1;
}
}
private static int N(int yyyy){
if (yyyy >= 1582 && yyyy <= 1699){
return 2;
} else if (yyyy >= 1700 && yyyy <= 1799){
return 3;
} else if (yyyy >= 1800 && yyyy <= 1899){
return 4;
} else if (yyyy >= 1900 && yyyy <= 2099){
return 5;
} else if (yyyy >= 2100 && yyyy <= 2199){
return 6;
} else if (yyyy >= 2200 && yyyy <= 2299){
return 0;
} else if (yyyy >= 2300 && yyyy <= 2399){
return 1;
} else if (yyyy >= 2400 && yyyy <= 2499){
return 2;
} else {
return -1;
}
}
public static Date GetGauss(int yyyy){
int A = yyyy % 19;
int B = yyyy % 4;
int C = yyyy % 7;
int D = (19*A + M(yyyy)) % 30;
int E = (2*B + 4*C + 6*D + N(yyyy)) % 7;
if (22+D+E > 31){
if (D+E-9 == 26) {
return new Date(19, 4, yyyy);
} else if ((D+E-9) == 25 && D == 28 && A > 10) {
return new Date(18, 4, yyyy);
} else {
return new Date((D+E-9), 4, yyyy);
}
} else {
return new Date((22+D+E), 3, yyyy);
}
}
}
DayOfYear
Für viele Zwecke benötigt man die Nummer eines Tages im laufenden Jahr. Dies macht der Algorithmus tagesnr (GetDayOfYear), er stammt ebenfalls von Robertson.
Zusicherung: Gültiges Datum T.M.J des Gregorianischen Kalenders
A = J % 4
B = J % 100
C = J % 400
D = [(M + 10) / 13]
E = T + [3055 * (M + 2) / 100] - 2 * D - 91
N = E + (1 - [(A + 3) / 4] + [(B + 99) / 100] - [(C + 399) / 400]) * D
Nachbedngung: N ist Tagesnummer vom T.M.J
using System;
class DOY{
public static void Main(){
Console.WriteLine(GetDayOfYear(12, 4, 2016));
Console.WriteLine(GetDayOfYear(5, 7, 1998));
}
public static int GetDayOfYear(int Day, int Month, int Year){
int A = Year % 4;
int B = Year % 100;
int C = Year % 400;
int D = (int)((Month+10)/13);
int E = Day + (int)(3055*(Month+2)/100) - 2*D - 91;
int N = E+(1-(int)((A+3)/4)+(int)((B+99)/100)-(int)((C+399)/400))*D;
return N;
}
}
DayOfYear to date
Zur umkehrung von tagesnr (GetDayOfYear) kann der Algorithmus tagimjahr (DOYtoDate.GetDate) von A. Stone (Tableless data Conversion, CA CACM 398) verwendet werden.
Tag_im_Jahr
Zusicherung: Tagesnummer N im Jahr J
Wenn J % 4 = 0
Dann A = 1
Sonst A = 0
Wenn N > 59
Dann H = 1
Sonst H = 0
Wenn H + A > 0
Dann B = 2 - A
Sonst B = 0
C = N + B + 91
M = [100C / 3055]
T = C - [3055M / 100]
M = M - 2
Nachbedingung: Tag ist T.M.J mit Tagesnummer N
using System;
class DOYtoDate{
public static void Main(){
Console.WriteLine(GetDate(271, 2016));
Console.WriteLine(GetDate(1, 1998));
}
public static int GetDate(int DayOfYear, int Year){
int A = Year % 4 == 0 ? 1 : 0;
int H = DayOfYear > 59 ? 1 : 0;
int B = H+A > 0 ? 2-A : 0;
int C = DayOfYear+B+91;
int M = (int)(100*C/3055);
int T = C - (int)(3055*M/100);
int M = M -2;
return new Date(T, M, Year);
}
}
Beirne
Schränkt man den Ostertermin auf die Jahre 1900-2099 ein, so kann der Gaußsche Algorithmus wesentlich vereinfacht werden. Der 1965 von T.H.O’Beirne Algorithmus lautet O’Beirne
Zusicherung: Jahr J 1900..2099 des Gregorianischen Kalenders
N = J-1900
A = N mod 19
B = [(7A+1)/19]
M = (11A+4-B) mod 29
Q = [N/4]
W = (N+Q+31-M) mod 7
P = 25-M-W
Ostersonntag ist der P. April oder der (P+31). März
using System;
class Beirne{
public static void Main(){
Console.WriteLine(GetBeirne(2016));
Console.WriteLine(GetBeirne(1998));
}
public static Date GetBeirne(int yyyy){
int N = yyyy - 1900;
int A = N % 19;
int B = (int)((7*A+1)/19);
int M = (11*A+4-B) % 29;
int Q = (int)(N/4);
int W = (N+Q+31-M) % 7;
int P = 25 - M - W;
if (P > 31 || P < 0){
return new Date(P+31, 2, yyyy);
} else {
return new Date(P, 4, yyyy);
}
}
}
Stored Procedures
Speicher Kunde
CREATE PROCEDURE sp_SpeicherKunde
@KundeId = -1 OUTPUT,@Name vc(50),@Vorname vc(50),@Straße vc(100),@TelefonNummer vc(50),@Email vc(100),@Plz vc(5),@Ort vc(50),@Bezeichnung vc(5)
AS
DECLARE @PersonId int, @AnredeId int, @OrtId int;
BEGIN TRANSACTION
BEGIN TRY
END
ELSE
SELECT @AnredeId = AnredeId FROM tblAnrede WHERE Bezeichnung = @Bezeichnung
IF (@AnredeId = 0)
BEGIN
SELECT @AnredeId = ISNULL(MAX(OrtId)+1, 1) FROM tblAnrede
INSERT INTO tblAnrede (AnredeId, Bezeichnung) VALUES (@AnredeId, @Bezeichnung)
END
SELECT @OrtId = Count(OrtId) FROM tblOrt WHERE Plz = @Plz AND Ort = @Ort
IF (@OrtId = 0)
BEGIN
SELECT @OrtId = ISNULL(MAX(OrtId)+1, 1) FROM tblOrt
INSERT INTO tblOrt (OrtId, Plz, Ort) VALUES (@OrtId, @Plz, @Ort)
END
ELSE
SELECT @OrtId = OrtId FROM tblOrt WHERE Plz = @Plz AND Ort = @Ort
SELECT @PersonId = ISNULL(MAX(PersonId)+1, 1) FROM tblPerson
INSERT INTO tblPerson (PersonId, AnredeId, [Name], Vorname, OrtId, Straße, TelefonNummer, Email) VALUES (@PersonId, @AnredeId, @Name, @Vorname, @OrtId, @Straße, @Telefonnummer, @Email)
SELECT @KundeId = ISNULL(MAX(KundeId)+1, 1) FROM tblKunde
INSERT INTO tblKunde (KundeId, PersonId) VALUES (@KundeId, @PersonId)
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrorNumber, ERROR_SEVERITY() AS ErrorServerity, ERROR_STATE() AS ErrorState, ERROR_PROCEDURE() AS ErrorProcedure, ERROR_LINE() AS ErrorLine, ERROR_MESSAGE() AS ErrorMessage;
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
END CATCH
IF @@TRANCOUNT > 0
COMMIT TRANSACTION;
END
Lese Mitarbeiter
sp_LeseMitarbeiter
CREATE Procedure sp_LeseAlleMitarbeiter
@MitarbeiterId int
AS
BEGIN
SET NOCOUNT ON;
SELECT tbl_Anrede.Bezeichnung as Anrede, tbl_Person.Name ,tbl_Person.Vorname, tbl_Ort.Plz, tbl_Ort.Ort, tbl_Person.Straße, tbl_Person.Telefon, tbl_Person.Mail
From tbl_Anrede INNER JOIN tbl_Person ON tbl_Anrede.AnredeId = tbl_Person.AnredeId INNER JOIN tbl_Mitarbeiter ON tbl_Person.PersonId = tbl_Mitarbeiter.PersonId INNER JOIN tbl_Ort ON tbl_Person.OrtId = tbl_Ort.OrtId
WHERE tbl_Mitarbeiter.MitarbeiterId = @MitarbeiterId
END
Lese Alle Mitarbeiter
sp_LeseAlleMitarbeiter
CREATE Procedure sp_LeseAlleMitarbeiter
AS
BEGIN
SET NOCOUNT ON;
SELECT tbl_Anrede.Bezeichnung as Anrede, tbl_Person.Name ,tbl_Person.Vorname, tbl_Ort.Plz, tbl_Ort.Ort, tbl_Person.Straße, tbl_Person.Telefon, tbl_Person.Mail
From tbl_Anrede INNER JOIN tbl_Person ON tbl_Anrede.AnredeId = tbl_Person.AnredeId INNER JOIN tbl_Mitarbeiter ON tbl_Person.PersonId = tbl_Mitarbeiter.PersonId INNER JOIN tbl_Ort ON tbl_Person.OrtId = tbl_Ort.OrtId
END
Speicher Mitarbeiter
sp_SpeicherMitarbeiter
CREATE PROCEDURE sp_SpeicherMitarbeiter
@MitarbeiterId = -1 OUTPUT,@Name vc(50),@Vorname vc(50),@Straße vc(100),@TelefonNummer vc(50),@Email vc(100),@Plz vc(5),@Ort vc(50),@Bezeichnung vc(5)
AS
DECLARE @PersonId int, @AnredeId int, @OrtId int;
BEGIN TRANSACTION
BEGIN TRY
END
ELSE
SELECT @AnredeId = AnredeId FROM tblAnrede WHERE Bezeichnung = @Bezeichnung
IF (@AnredeId = 0)
BEGIN
SELECT @AnredeId = ISNULL(MAX(OrtId)+1, 1) FROM tblAnrede
INSERT INTO tblAnrede (AnredeId, Bezeichnung) VALUES (@AnredeId, @Bezeichnung)
END
SELECT @OrtId = Count(OrtId) FROM tblOrt WHERE Plz = @Plz AND Ort = @Ort
IF (@OrtId = 0)
BEGIN
SELECT @OrtId = ISNULL(MAX(OrtId)+1, 1) FROM tblOrt
INSERT INTO tblOrt (OrtId, Plz, Ort) VALUES (@OrtId, @Plz, @Ort)
END
ELSE
SELECT @OrtId = OrtId FROM tblOrt WHERE Plz = @Plz AND Ort = @Ort
SELECT @PersonId = ISNULL(MAX(PersonId)+1, 1) FROM tblPerson
INSERT INTO tblPerson (PersonId, AnredeId, [Name], Vorname, OrtId, Straße, TelefonNummer, Email) VALUES (@PersonId, @AnredeId, @Name, @Vorname, @OrtId, @Straße, @Telefonnummer, @Email)
SELECT @MitarbeiterId = ISNULL(MAX(MitarbeiterId)+1, 1) FROM tblMitarbeiter
INSERT INTO tblMitarbeiter (MitarbeiterId, PersonId) VALUES (@MitarbeiterId, @PersonId)
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrorNumber, ERROR_SEVERITY() AS ErrorServerity, ERROR_STATE() AS ErrorState, ERROR_PROCEDURE() AS ErrorProcedure, ERROR_LINE() AS ErrorLine, ERROR_MESSAGE() AS ErrorMessage;
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
END CATCH
IF @@TRANCOUNT > 0
COMMIT TRANSACTION;
END
Using a Stored Procedure in C#
SQLConnection conn = new SQLConnection(host);
SQLCommand cmd = new SQLCommand("sp_speicherKunde", conn);
cmd.CommandTyppe = CommandType.StoredProcedure;
int KundeId;
cmd.Parameters.Add(new SQLParameter("@KundeId", SqlDbType.Int));
cmd.Parameter["@KundeId"].Direction = ParameterDirection.Output;
cmd.Parameter.AddWithValue("@Name", "Musil");
...
cmd.ExecuteNonQuery();
KundenId = (int)cmd.Parameters["@KundenId"].Value;