Autor Beitrag
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2008
Erhaltene Danke: 368

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Sa 13.01.18 14:08 
Hallo Forum

Problemstellung:
Ich habe mir Methode erstellt, die abfragen soll, ob ein gegebenes Datum ein Feiertag ist oder nicht. Sie funktioniert an sich, jedoch braucht die Abfrage auf dem alten WinXP Rechner gut 3 Sekunden bis sie ein Ergebnis liefert, und das stockt den weiteren Ablauf ungemein.
Bestimmt liegt es an den vielen new DateTime()-"Items", die der Liste zugefügt werden. :gruebel: Leider weiss ich nicht, wie es umgehen könnte, denn nur über den DateTime-Konstruktor lassen sich Tag, Monat und Jahr übergeben und entsprechend umwandeln.
Eigenschaften wie DateTime.Date usw. lassen sich nur lesen, nicht schreiben. :motz:

Weiss jemand dieses Problem zu umgehen bzw. den Code etwas effizienter umzugestalten?

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
// Feiertage für WIEN
private bool IsHoliday(DateTime date) 

    if (date > DateTime.MinValue) 
    { 
        DateTime easter = EasterGregDate(date.Year); 
        sbyte[] easterDiff = { -21395060 }; 
        // Ostersonntag - 2   = Karfreitag 
        // Ostersonntag + 1   = Ostermontag 
        // Ostersonntag + 39  = Christi Himmelfahrt 
        // Ostersonntag + 50  = Pfingstmontag 
        // Ostersonntag + 60  = Fronleichnam 
        List<DateTime> dates = new List<DateTime>(); 
         
        dates.Add(new DateTime(date.Year, 11));   // Neujahr 
        dates.Add(new DateTime(date.Year, 16));   // Heilige Drei Könige 
        dates.Add(new DateTime(date.Year, 51));   // Staatsfeiertag 
        dates.Add(new DateTime(date.Year, 815));  // Maria Himmelfahrt 
        dates.Add(new DateTime(date.Year, 1026)); // Nationalfeiertag 
        dates.Add(new DateTime(date.Year, 111));  // Allerheiligen 
        dates.Add(new DateTime(date.Year, 128));  // Maria Empfängnis 
        dates.Add(new DateTime(date.Year, 1225)); // Weihnachten 
        dates.Add(new DateTime(date.Year, 1226)); // Stefanitag 
         
        for (int i = 0; i < easterDiff.Count(); i++) 
            dates.Add(easter.AddDays(easterDiff[i])); 
             
        return dates.Contains(date); 
    } 
    return false


// Formel stammt vom Buch:
// Practical Astronmy with your Calculator or Spreedsheet (Fourth Edition)
// Peter Duffett-Smith and Jonathan Zwart
private DateTime EasterGregDate(int year)
{
    if (year > 1582)
    {
        int a = year % 19;
        int b = (int)(year / 100);
        int c = year % 100;
        int d = (int)(b / 4);
        int e = b % 4;
        int f = (int)((b + 8) / 25);
        int g = (int)((b - f + 1) / 3);
        int h = (19 * a + b - d - g + 15) % 30;
        int i = (int)(c / 4);
        int j = c % 4;
        int k = (32 + 2 * (e + i) - h - j) % 7;
        int l = (int)((a + 11 * h + 22 * k) / 451);
        byte month = (byte)((h + k - 7 * l + 114) / 31);
        byte day = (byte)(((h + k - 7 * l + 114) % 31) + 1);

        return new DateTime(year, month, day);
    }
    return DateTime.MinValue;
}

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Chefentwickler
Beiträge: 20215
Erhaltene Danke: 2034

Win 10
C# (VS 2017)
BeitragVerfasst: Sa 13.01.18 14:30 
Hallo,

hast Du die Zeiten, was wie lange braucht, mal gemessen? Das Erzeugen von ganzen 15 Instanzen des DateTime-Structs sollte auf jeden Fall keine drei Sekunden brauchen. Wie sollte irgendein .NET Programm jemals benutzbar sein, wenn solche Basisoperationen so lange brauchen?

Du brauchst aber auch keine Liste von DateTime-Instanzen. Wenn das gegebene Datum einem der Feiertage entspricht, muss ich die anderen ja nicht mehr prüfen. Irgendwie so kann man es machen:
ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
        private static readonly Tuple<intint>[] staticHolidays = new Tuple<intint>[] {
            new Tuple<intint>(1,1),
            new Tuple<intint>(1,6),
            new Tuple<intint>(5,1),
            new Tuple<intint>(8,15),
            new Tuple<intint>(10,26),
            new Tuple<intint>(11,1),
            new Tuple<intint>(12,8),
            new Tuple<intint>(12,25),
            new Tuple<intint>(12,26),
        };

        // Feiertage für WIEN
        private static bool IsHoliday(DateTime date)
        {
            if (date > DateTime.MinValue)
            {
                foreach (var monthDay in staticHolidays)
                    if (date.Month == monthDay.Item1 && date.Day == monthDay.Item2)
                        return true;
                
                DateTime easter = EasterGregDate(date.Year);
                sbyte[] easterDiff = { -21395060 };
                // Ostersonntag - 2   = Karfreitag 
                // Ostersonntag + 1   = Ostermontag 
                // Ostersonntag + 39  = Christi Himmelfahrt 
                // Ostersonntag + 50  = Pfingstmontag 
                // Ostersonntag + 60  = Fronleichnam 

                for (int i = 0; i < easterDiff.Length; i++)
                    if (date == easter.AddDays(easterDiff[i]))
                        return true;
            }
            return false;
        }


Grüße
Christian


//edit: Es macht natürlich keinen Sinn, die Elemente im Array easterDiff per LINQ (Count()) zu zählen, wenn man einfach die Length-Eigenschaft des Arrays benutzen kann ;)

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".

Für diesen Beitrag haben gedankt: Frühlingsrolle
Frühlingsrolle Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2008
Erhaltene Danke: 368

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Sa 13.01.18 14:43 
Gemessen hab ich es nicht. Ich hab die Methode allein über einen Button mehrmals hintereinander aufgerufen, sowie die Anwendung mehrmals geöffnet, und dennoch hat es jedes Mal lange gebraucht.
Auf dem Win7 Rechner ging das flott. Wie effizient .NET Anwendungen sind, da fragst du den Falschen. :D
Ich probier dein Beispiel gleich danach aus, mal sehen.

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Chefentwickler
Beiträge: 20215
Erhaltene Danke: 2034

Win 10
C# (VS 2017)
BeitragVerfasst: Sa 13.01.18 15:20 
Ach ja, ich würde bei der Methode EasterGregDate übrigens bei einem Jahr <= 1582 auch eine ArgumentOutOfRange-Exception werfen anstatt DateTime.Min zurückzugeben. Irgendwelche "magischen Werte" als Fehlerwerte zu verwenden, fällt einem irgendwann mal auf die Füße. Alternativ kann man noch über einen nullable DateTime als Rückgabewert nachdenken und für die ungültigen Jahre dann null zurückgeben. Favorisieren würde ich aber die Exception, weil man ja tatsächlich den Bereich gültiger Argumente (für diese Methode) verlässt.

Ein paar der Casts auf int in der Methode kannst Du Dir übrigens sparen, weil die Division zweier Integer immer einen Integer zurückgibt.

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".

Für diesen Beitrag haben gedankt: Frühlingsrolle
Frühlingsrolle Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2008
Erhaltene Danke: 368

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Sa 13.01.18 15:35 
Das Beispiel hat sich bewährt. Auf beiden Rechnern läuft es nun flott.

Meine erste Methode sah auch eine throw new Exception vor. Am Ende hab ich es abgeändert, um die Abfrage für den Fehlerfall leichter handzuhaben. Und an "nullable" hab ich garnicht gedacht.
Bestimmt kann man einige int-Casts weglassen. Weil ich das Buch zu genau genommen habe, hat sich das so ergeben. Sie sollen stellvertrend für die Trunc() / Truncate() Funktion sein, was die Divisionen angeht.
Kann man alles noch schön optimieren.
Danke für die Unterstützung, Christian S.!


Das Thema hat sich erledigt !!!

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4263
Erhaltene Danke: 851


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Sa 13.01.18 15:43 
Aus Neugier da ich deinen Test am Anfang der Methode sehe, wie bekommt man einen DateTime kleiner MinValue hin?
Und nach der Neugierfrage jetzt noch eine gemeine Bemerkung ;) Du berücksichtigst die Zeitzone und den Zeitpunkt des DateTime nicht. Dir mag das vordergründig egal sein aber spätestens das DateTime.Equals das beim Aufruf von List.Contains verwendet wird ist das nicht egal und in deine Methode lässt du jeden DateTime rein. Beispielsweise ein Zeitpunkt am erste Januar in UTC muss in deiner Zeitzone noch lange nicht (oder bezogen auf Mitteleuropa eher nicht mehr) Neujahr sein. Zeitzonen/Kalendersysteme sind doch immer ein Quell steter Freude. Was würden wir den Spaß vermissen wenn es das nicht gebe :roll:

Zitat:
Eigenschaften wie DateTime.Date usw. lassen sich nur lesen, nicht schreiben. :motz:


Wenn du mal das Thema mutable structs nachliest und was das für Konsequenzen hätte wirst du ganz schnell zu dem Schluss kommen das das eine gute Sache ist das das nicht geht ;)
Obwohl wenn es ginge hätten wir hier wieder reichlich zu tun um tolle fragen zu beantworten wie
"Warum ändert sich das Datum in der Liste nicht wenn ich 'dates[0].date = DateTime.Now' aufrufe?"

Für diesen Beitrag haben gedankt: Frühlingsrolle
Frühlingsrolle Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2008
Erhaltene Danke: 368

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Sa 13.01.18 16:10 
Die Überlegung war, entweder vergleiche ich den .MinValue Wert auf ungleich oder auf größer als. Zielführend waren beide Operatoren.
Für die Zeitzone habe ich keinen Bedarf gesehen, denn ich arbeite hier nu mit einem Datum und nicht mit einer genauen Uhrzeit. Die Feiertage sind auch nur lokal (für Wien) gültig und würden für andere Ortschaften mit anderen Zeitzonen sowieso keinen Sinn ergeben.
Wenn es mich freut, erstell ich auch eine Assembly, die europaweit oder sogar weltweit Feiertage aufbereitet. Dafür und für einiges mehr bin ich im Moment zu unmotiviert. ;)

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4263
Erhaltene Danke: 851


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Sa 13.01.18 16:30 
Zitat:
Für die Zeitzone habe ich keinen Bedarf gesehen, denn ich arbeite hier nu mit einem Datum und nicht mit einer genauen Uhrzeit. Die Feiertage sind auch nur lokal (für Wien) gültig und würden für andere Ortschaften mit anderen Zeitzonen sowieso keinen Sinn ergeben.


Dann wäre das eine sinnvolle Eingangsprüfung für den DateTime anstatt auf MinDate zu prüfen.
Also das der DateTime.Kind auf Local (für UTC funktioniert der Code nur bedingt) steht und je nach Code ob der DateTime einen Zeitanteil enthält. Dein ursprünglicher Code hätte dann auch ein Problem. Bei einer Lösung wie bei Christian wäre das unproblematisch.
Frühlingsrolle Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2008
Erhaltene Danke: 368

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: So 14.01.18 03:22 
Ich versteh schon, was du meinst. Die Uhrzeit würde sich aus Sicht eines anderen Standortes nicht decken und .Contains() würde immer false liefern, auch wenn das Datum stimmt.
Christian's Version gefällt mir auch besser, und daher übernehm ich es auch so:

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
// Practical Astronmy with your Calculator or Spreedsheet (Fourth Edition) 
// Peter Duffett-Smith and Jonathan Zwart 
private static DateTime EasterGregDate(int year) 

    if (year < 1583
        throw new ArgumentOutOfRangeException("The easter year must be at least 1583 AC."); 
     
    int a = year % 19
    int b = year / 100
    int c = year % 100
    int d = b / 4
    int e = b % 4
    int f = (b + 8) / 25
    int g = (b - f + 1) / 3
    int h = (19 * a + b - d - g + 15) % 30
    int i = c / 4
    int j = c % 4
    int k = (32 + 2 * (e + i) - h - j) % 7
    int l = (a + 11 * h + 22 * k) / 451
    int month = (h + k - 7 * l + 114) / 31
    int day = ((h + k - 7 * l + 114) % 31) + 1
     
    return new DateTime(year, month, day); 

 
private static readonly Tuple<intint>[] staticHolidays = new Tuple<intint>[] { 
    new Tuple<intint>(11),    // Neujahr 
    new Tuple<intint>(16),    // Heilige Drei Könige 
    new Tuple<intint>(51),    // Staatsfeiertag 
    new Tuple<intint>(815),   // Maria Himmelfahrt 
    new Tuple<intint>(1026),  // Nationalfeiertag 
    new Tuple<intint>(111),   // Allerheiligen 
    new Tuple<intint>(128),   // Maria Empfängnis 
    new Tuple<intint>(1225),  // Weihnachten 
    new Tuple<intint>(1226),  // Stefanitag 
}; 
 
// Feiertage für WIEN 
private static bool IsHoliday(DateTime date) 

    DateTime easter = EasterGregDate(date.Year); 
    sbyte[] easterDiff = { -21395060 }; 
    // Ostersonntag - 2   = Karfreitag 
    // Ostersonntag + 1   = Ostermontag 
    // Ostersonntag + 39  = Christi Himmelfahrt 
    // Ostersonntag + 50  = Pfingstmontag 
    // Ostersonntag + 60  = Fronleichnam 
         
    for (int i = 0; i < easterDiff.Length; i++) 
        if (date == easter.AddDays(easterDiff[i])) 
            return true

    foreach (var monthDay in staticHolidays) 
        if (date.Month == monthDay.Item1 && date.Day == monthDay.Item2) 
            return true;
             
    return false
}

Was das Tuple() angeht, frage ich mich, warum ein zusätzliches readonly hier vorliegt? Ist ein Tuple() nicht von sich aus nur lesbar/readonly?

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)


Zuletzt bearbeitet von Frühlingsrolle am So 14.01.18 17:59, insgesamt 1-mal bearbeitet
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4263
Erhaltene Danke: 851


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: So 14.01.18 13:35 
Readonly bezieht sich auf die Variable und nicht den Inhalt der Variablen an der es steht. Durch das ReadOnly kannst du also dem staticHolidays keine neues Tuple Array im Code zuweisen. Die Tuples in dem vorhandenen Array kannst du aber weiterhin beliebig austauschen. Und wenn Tuple änderbar wären (was sie nicht sind) könnte man die natürlich auch ändern egal ob da readonly steht. Wenn man das auch will müsste man z.b. eine ReadOnlyCollection verwenden anstatt einem Array.

Für diesen Beitrag haben gedankt: Frühlingsrolle