Ideensammlung Urlaubsplaner

  • VB.NET
  • .NET 4.0

Es gibt 44 Antworten in diesem Thema. Der letzte Beitrag () ist von tragl.

    Ideensammlung Urlaubsplaner

    Hallo zusammen.

    Da ich nirgendwo wirklich fündig geworden bin, dachte ich ich starte mal einen Sammelthread für einen Urlaubsplaner.
    Ich hab' dazu ein kleines Projekt gebaut, mit 3 Forms die anzeigen, wie ich mir das in etwa vorstelle.

    Die 3 Forms sind "händisch" mit Daten gefüllt und sollen wirklich nur meine Idee aufzeigen:


    In unserem Unternehmen gibt es etliche Standorte und pro Standort nochmal etliche Mitarbeiter.
    Hier könnte man auch Umdenken in z.B. "Abteilung" -> "Mitarbeiter"

    Meine Ideen zur Ansicht:
    eine Ansicht Gruppierung - Mit Standort und Mitarbeitern angezeigt
    eine Ansicht Mitarbeiter - alle Mitarbeiter untereinander weg (könnte man vorher Filtern)
    eine Ansicht Standort - Summierung Urlaub/Krank für bestimmten Zeitraum auf Standortebene

    Der Datumsbereich soll gefiltert werden können, ansonsten beim Öffnen automatisch das aktuelle Datum + 1 Monat anzeigen.
    Ansonsten könnte man auch die z.B. 3 Varianten (siehe oben) in ein Dropdown packen, dann würde man nur eine Form mit ListView benötigen.

    Die Daten zu Mitarbeiter, Standort und Urlaub liegen in einem DataSet bestehend aus "Standort" "Mitarbeiter" "Urlaubsplan"


    Eingaben würde ich wie folgt vorsehen:
    Urlaub: 1 = ganzer Tag / 0,5 = halber Tag
    Krank: K = ganzer Tag / k = halber Tag | Alternativ auch 1 = ganzer Tag / 0,5 = halber Tag (lässt sich wohl einfacher summieren :D )

    Sichtbarkeit der Daten:
    Ich denke am sinnvollsten wäre hier wohl mit einer ListView zu arbeiten :?: Das DGV hat wohl begrenzte Anzahl an Spalten,
    vorstellbar wäre (Datum vertikal, Mitarbeiter horizontal) oder (Datum horizontal, Mitarbeiter vertikal)

    Alternativ auch ein TabControl mit den einzelnen Monaten als TabPages :?:

    Die Spalten (oder jenachdem Zeilen) mit Datum sollen dann anhand des Auswahlbereichs gefüllt werden und nur Daten an dem Datum anzeigen,
    an dem es auch Einträge im DataSet dazu gibt.

    Ich weiß nicht ob sowas überhaupt lösbar ist,
    würde mich aber freuen, wenn hier viele Ideen (zur Umsetzung und zum Design) dazu zustande kämen, ggf. hat auch jemand Lust
    und Zeit das Ganze mit zu entwickeln. ich hab' in vielen Foren gelesen, dass danach gesucht wird - aber eine richtige Lösung hab ich noch keine gefunden.

    Im angehangenen Projekt können u.a. auch Testdaten erfasst werden (es sind auch schon welche von mir angelegt). Beim Code bitte nicht meckern,
    ich bin noch recht frisch in der Programmierung und hab' das auf die Schnelle mal gemacht.
    Schaut einfach mal rein ;)

    Achso - hier noch 2 Bildchen, wie ich das derzeit über Excel gelöst hab:
    Dateien
    • ListViewTest.zip

      (801,09 kB, 26 mal heruntergeladen, zuletzt: )
    Originaler (noch) Nichtskönner :D

    tragl schrieb:

    Sichtbarkeit der Daten:
    Ich denke am sinnvollsten wäre hier wohl mit einer ListView zu arbeiten Das DGV hat wohl begrenzte Anzahl an Spalten,
    vorstellbar wäre (Datum vertikal, Mitarbeiter horizontal) oder (Datum horizontal, Mitarbeiter vertikal)
    Ich glaub nicht, dasss die Anzahl an ListView-Spalten unbegrenzt ist.
    Ich neige zu MA horizontal, weil die Anzahl der MA ist endlich, die der Tage unendlich - und das kann man horizontal schlecht darstellen.
    Üblicherweise stellt man ja horizontal maximal 31 Tage dar, was für die Übersicht in der Nähe von MonatsWechseln doof ist.
    Da würde ich Tage untereinander soviele anzeigen, wie Platz haben, mit einem beliebigen StartDatum.

    Datenmodellmässig ist ein Kalender eine schwierige Herausforderung.
    Streng genommen sind Pro Mitarbeiter nur die Übergänge von einem zum anderen Zustand abzuspeichern - alles andere wären redundante Daten, und gehören nicht abgespeichert.
    Weil ein MA kann sich zu einem Zeitpunkt nur in einem Zustand befinden: U, K, A. Wobei diese Zustände auch halbiert sein können - puh - allein das wird schon knifflig darzustellen.
    Aber interessant: Im Grunde müsste man die Zeitachse splitten: Also ein Monat hat nicht max 31 Tage, sondern hat max. 62 Halbtage!

    Jo, und so ein Datenmodell kannste garnet anzeigen in einer Kalender-Ansicht, sondern musst dir ein Kalender-Datenmodell basteln, und geeignete Abfragen an das Persistenz-Modell, die dessen Werte übersetzen in ein für deine Präsentation taugliches Datenmodell.
    Mit einer typisierten DataTable geht das nicht, weil da sind die Spalten fest vorgegeben - das kannste ja nicht neu generieren, wenn ein neuer MA eingestellt wird.

    Jaaa - solche Probleme kommen da auf dich zu...

    Vielleicht doch lieber die Monatsweise ansicht mit den Tagen konventionell horizontal - da könnte man eine typisierte Tabelle basteln mit 31 Tagen - die letzten werden dann eben ggfs ausgegraut.
    Sofern es möglich ist, den Text des Columnheaders mit dem Datumseintrag aus der Tabelle zu vergleichen sollte das eigentlich klappen.
    Mitarbeiter links nach unten, Datum oben nach rechts. Das Datum kann man sich via Button als Columns befüllen
    (natürlich fehlen da noch Schönheitsoperationen (z.B. wenn über Monatsende hinaus, dann weglassen) aber wäre so erstmal OK)

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. lvTest.View = View.Details
    3. lvTest.Scrollable = True
    4. lvTest.Items.Clear()
    5. lvTest.Columns.Clear()
    6. lvTest.Columns.Add("Mitarbeiter")
    7. lvTest.Columns.Add("Standort")
    8. Dim d As Date = dtpTest.Value
    9. For i As Integer = 1 To 31
    10. lvTest.Columns.Add(d.AddDays(i - 1).ToShortDateString, 70, HorizontalAlignment.Left)
    11. Next
    12. End Sub


    Bin grad dabei rauszufinden, wie ich Daten aus dem DataSet dareinfüllen kann - dann teste ich weiter.



    Edit: Befüllen von Mitarbeitern und Standort klappt schonmal,
    ich überlege Gerade die Eingabemethode auf Char zu ändern. Dann gibt's ein Feld in der Urlaub-Tabelle ("Art" oder so ähnlich).
    Also nur noch "MitarbeiterID" "Datum" "Art" -> Mitarbeitername und Standort wird aus dem FK_Mitarbeiter_Urlaubsplan geholt.
    Urlaub = U
    0,5 Urlaub = u
    Krank = K
    0,5 krank = k
    Sowas lässt sich sicher auch summieren :?:
    und für viiiieeel später könnte man je nach Eintrag (U,u,K,k) die Zellen noch farblich markieren :)



    Edit2: Weitere Designidee:
    Der EingabeDialog für Urlaub/Krank sollte noch erweitert werden, sodass ein Zeitraum angegeben werden kann -> Datum(s) daraus sollen dann automatisch als Eintrag erstellt werden.
    Originaler (noch) Nichtskönner :D

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „tragl“ ()

    So, das Befüllen klappt nun schonmal - angewendet auf die Variante mit Tabs als Monat.
    Beim Öffnen wird alles (für alle Mitarbeiter) geladen, könnte man noch so einstellen, dass das erst beim Anwählen eines Tabs passiert.


    Damit kann man schon arbeiten.
    Was meiner Meinung nach noch fehlt:

    Ansicht:
    - Farbige Markierung (unterschiedlich z.B. blau bei U,u / rot bei K,k) nur auf eine Zelle die Urlaub oder Krank enthält
    - Summierung pro Monat für die Einträge (U,u,K,k)

    Daten:

    - Erfassung von Zeiträumen ermöglichen (2 DateTimePicker) -> ein Zeitraum soll aufgeteilt werden und pro Datum einen Datensatz erstellen
    - Gültigkeitsprüfung auf Char (U,u,K,k)
    - Checkbox für Urlaubsgenehmigung? Könnte über Zugriffsrechte gesteuert werden...

    Ansonsten ist der gesamte Code bestimmt noch optimierfähig, hab' ich nur auf die Schnelle mal zusammengebastelt:
    (das aktuelle Projekt hab' ich angehangen)

    VB.NET-Quellcode

    1. Imports ListViewTest.DataSet1
    2. Public Class frmTabbedMonatsweise
    3. Private Sub tsJahrÄndern_Click(sender As Object, e As EventArgs) Handles tsJahrÄndern.Click
    4. Forms.ShowDataDialog(Of dlgJahrÄndern)()
    5. End Sub
    6. Private Sub frmTabbedMonatsweise_Load(sender As Object, e As EventArgs) Handles Me.Load
    7. 'Voreinstellung, damit "JAHR" nicht Nothing oder "" ist
    8. If My.Settings.TabbedJahr = "" Then My.Settings.TabbedJahr = Date.Now.ToString("yyyy")
    9. 'ListViewHeader
    10. ListViewHeader("1", lvJanuar)
    11. ListViewHeader("2", lvFebruar)
    12. ListViewHeader("3", lvMärz)
    13. ListViewHeader("4", lvApril)
    14. ListViewHeader("5", lvMai)
    15. ListViewHeader("6", lvJuni)
    16. ListViewHeader("7", lvJuli)
    17. ListViewHeader("8", lvAugust)
    18. ListViewHeader("9", lvSeptember)
    19. ListViewHeader("10", lvOktober)
    20. ListViewHeader("11", lvNovember)
    21. ListViewHeader("12", lvDezember)
    22. 'Daten
    23. ListViewData(lvJanuar)
    24. ListViewData(lvFebruar)
    25. ListViewData(lvMärz)
    26. ListViewData(lvApril)
    27. ListViewData(lvMai)
    28. ListViewData(lvJuni)
    29. ListViewData(lvJuli)
    30. ListViewData(lvAugust)
    31. ListViewData(lvSeptember)
    32. ListViewData(lvOktober)
    33. ListViewData(lvNovember)
    34. ListViewData(lvDezember)
    35. End Sub
    36. Private Sub ListViewHeader(Monat As String, lvMonat As Windows.Forms.ListView)
    37. Dim sDate As Date = Date.Parse($"01.{Monat}.{My.Settings.TabbedJahr}")
    38. Dim eDate As Date = Date.Parse($"{MonatsEnde(Monat).ToShortDateString}")
    39. With lvMonat
    40. .View = View.Details
    41. .GridLines = True
    42. .Scrollable = True
    43. .Items.Clear()
    44. .Columns.Clear()
    45. .Columns.Add("Mitarbeiter", 100)
    46. .Columns.Add("Standort", 100)
    47. End With
    48. Dim i As Date = sDate
    49. Do While i < eDate.AddDays(1)
    50. lvMonat.Columns.Add(i.ToShortDateString, 35)
    51. i = i.AddDays(1)
    52. Loop
    53. End Sub
    54. Private Sub ListViewData(lvMonat As Windows.Forms.ListView)
    55. For Each rw In Dts.Mitarbeiter
    56. Dim lvItem As ListViewItem = Nothing
    57. If Not rw.Item(Dts.Mitarbeiter.expFullnameColumn).ToString = "<keine Angabe>, <keine Angabe>" Then
    58. Dim maID = rw.Item(Dts.Mitarbeiter.IDColumn).ToString
    59. lvItem = lvMonat.Items.Add(rw.Item(Dts.Mitarbeiter.expFullnameColumn).ToString)
    60. lvItem.SubItems.Add(rw.Item(Dts.Mitarbeiter.StandortColumn).ToString)
    61. For i As Integer = 2 To lvMonat.Columns.Count - 1
    62. Dim filteredRows = Dts.Urlaubsplan.Select($"MitarbeiterID = {CInt(maID)}").Cast(Of UrlaubsplanRow)
    63. Dim colDate = Date.Parse(lvMonat.Columns(i).Text.ToString).ToShortDateString
    64. Dim dateExists As String = ""
    65. For Each rw2 In filteredRows
    66. Dim dataDate = rw2.Datum.ToShortDateString
    67. If dataDate = colDate Then
    68. dateExists = rw2.Art.ToString
    69. End If
    70. Next
    71. lvItem.SubItems.Add(dateExists)
    72. Next
    73. End If
    74. Next
    75. End Sub
    76. Private Function MonatsEnde(Monat As String) As Date
    77. Dim d As Date = Date.Parse($"01.{Monat}.{My.Settings.TabbedJahr}")
    78. Return d.AddMonths(1).AddDays(-1)
    79. End Function
    80. End Class
    Dateien
    • ListViewTest.zip

      (1,02 MB, 20 mal heruntergeladen, zuletzt: )
    Originaler (noch) Nichtskönner :D

    tragl schrieb:

    Der EingabeDialog für Urlaub/Krank sollte noch erweitert werden, sodass ein Zeitraum angegeben werden kann -> Datum(s) daraus sollen dann automatisch als Eintrag erstellt werden.
    Kannst auch nochmal meine Stechuhr angucken.
    Da kann man U/K/H direkt ins DGV eintragen - das geht auch bei mehreren so flott, dass ich da kein Bedarf sehe, was spezielles mit Multi-Selection zu coden (was duchaus auch möglich wäre).
    Und coloriert ists auch.

    ErfinderDesRades schrieb:

    Kannst auch nochmal meine Stechuhr angucken.
    Da kann man U/K/H direkt ins DGV eintragen - das geht auch bei mehreren so flott, dass ich da kein Bedarf sehe, was spezielles mit Multi-Selection zu coden (was duchaus auch möglich wäre).
    Und coloriert ists auch.

    Hey, das mag sein - aber dann müsstest du jedes Datum für jedes Jahr in der Table für jeden Mitarbeiter bereitstellen, was meiner Meinung nach unnötige Daten wären.
    Daher wäre mir die Methode mit "von Datum bis Datum" und daraus für jedes Datum einen Eintrag erstellen lieber (ginge ja dann wenn von und bis das gleiche Datum wär).
    Dann hast du nur die Datum's in der Tabelle, wo auch Urlaub oder Krank vor kommt - was am Ende wiederum performanter sein sollte.

    Da das ganze Teil für mein Nutzen auf alle Standorte reagieren soll kommen hier mal schnell über 100 Mitarbeiter zusammen.
    Originaler (noch) Nichtskönner :D
    So, ich bin ein ganzen Stück weiter gekommen
    dank der Hilfe von @ErfinderDesRades ;)

    - Urlaube/Krank können nun über einen Zeitraum angelegt werden (über 2 DateTimePicker)
    - In der Eingabe kann über eine Checkbox die Wochenendüberprüfung ein und ausgeschaltet werden
    - Filterfunktionen nach Mitarbeiter und/oder Standort
    - Es gibt ein TabControl mit TabPages pro Monat, darin werden die Urlaube/Krank dargestellt.

    weiter geplant:

    - Feiertage: Feiertagstabelle in Beziehung zu Bundesland, Bundesland in Beziehung zu Mitarbeiter setzen
    - Chars bei Urlaubsanlage erweitern: ? für vorgeplante Urlaube / ggf. noch einige Kürzel für Dienstpläne dann kann der Urlaubsplaner auch als Dienstplaner genutzt werden
    -- Die Chars könnten über eine Settingsform eingestellt werden
    - Anwesende Mitarbeiter anzeigen lassen
    - verschiebung der Checkbox Wochenende/Feiertag in die Settings-Form
    - verbesserte Handhabung bei der Anlage von Urlauben/Krank -> bessere Textboxen(DateTimePicker) um bequemeres Eingeben zu ermöglichen

    Urlaubsdaten:
    - Bei Anlage prüfung auf Feiertag -> über Checkbox in Einstellungen steuerbar
    - Checkbox für Urlaubsgenehmigung durch Vorgesetzen (über Rechtesteuerung) / alternativ Statusfeld zur Auswahl "genehmigt/nicht genehmigt"
    -- hierzu müsste mehrfachauswahl und Editierung eingebaut werden
    - Übersicht über DGV für verbrauchte Urlaubstage / selektierte Zeilen / gesamtzeilen

    Urlaubsplan (ListView):
    - Jahrauswahl oben drüber setzen -> lv's sollen sich nach Auswahl aktualisieren
    - Sortierung / Filterung (Mitarbeiter/Standort)
    - Felder mit Inhalt (Chars) farblich markieren (z.B. blau für Urlaub, rot für krank)
    - Komplette Spalten farblich markieren wenn Samstag, Sonntag
    - Feiertag pro Standort markieren (sind ja ggf. unterschiedlich)
    - Gruppierung nach Standort einbauen (ggf. klappbar machen) -> Gruppierung nach Auswahl erst sichtbar
    - Summierung der Chars pro Mitarbeiter / Standort
    Dateien
    Originaler (noch) Nichtskönner :D
    @ErfinderDesRades:
    Ich habe mich mal an deinem Code aus dem Stechuhr-Projekt bzgl. der Feiertage bedient und den ein wenig angepasst.
    (var's ins Deutsche umgeändert und fehlende Feiertage ergänzt) -> das Ganze soll später mal mit Bundesländern zusammen
    arbeiten :)

    VB.NET-Quellcode

    1. Imports System.Runtime.CompilerServices
    2. Public Module Feiertage
    3. Public Enum Bundesland
    4. 'Quelle Statistisches Bundesamt: https://www.destatis.de/DE/Themen/Laender-Regionen/Regionales/Gemeindeverzeichnis/Glossar/bundeslaender.html
    5. blSH = 1 '01 Schleswig-Holstein (SH)
    6. blHH = 2 '02 Hamburg (HH)
    7. blNI = 3 '03 Niedersachsen (NI)
    8. blHB = 4 '04 Bremen (HB)
    9. blNW = 5 '05 Nordrhein-Westfalen (NW)
    10. blHE = 6 '06 Hessen (HE)
    11. blRP = 7 '07 Rheinland-Pfalz (RP)
    12. blBW = 8 '08 Baden-Württemberg (BW)
    13. blBY = 9 '09 Bayern (BY)
    14. blSL = 10 '10 Saarland (SL)
    15. blBE = 11 '11 Berlin (BE)
    16. blBB = 12 '12 Brandenburg (BB)
    17. blMV = 13 '13 Mecklenburg-Vorpommern (MV)
    18. blSN = 14 '14 Sachsen (SN)
    19. blST = 15 '15 Sachsen-Anhalt (ST)
    20. blTH = 16 '16 Thüringen (TH)
    21. End Enum
    22. Public Enum Feiertag
    23. 'Quelle: https://www.deutschland-feiert.de/feiertage/ und https://www.schulferien.org/deutschland/feiertage/2021/
    24. ftKeinFeiertag = 0
    25. ftNeujahr = 1 + (1 << 16) 'FEST (01.01.) -> alle BL
    26. ftHeiligeDreiKönige = 6 + (1 << 16) 'FEST (06.01.) -> BW,BY,ST
    27. 'ftInternationalerFrauentag ' -> BE
    28. ftKarfreitag = 2 'BEWEGLICH -> alle BL
    29. ftOstersonntag 'BEWEGLICH -> BB
    30. ftOstermontag 'BEWEGLICH -> alle BL
    31. ftTagDerArbeit = 1 + (5 << 16) 'FEST (01.05.) -> alle BL
    32. ftChristiHimmelfahrt 'BEWEGLICH -> alle BL
    33. ftPfingstSonntag 'BEWEGLICH -> BB
    34. ftPfingstMontag 'BEWEGLICH -> alle BL
    35. ftFronleichnam 'BEWEGLICH -> BW,BY,HE,NW,RP,SL
    36. frMariäHimmelfahrt = 15 + (8 << 16) 'FEST (15.08.) -> BY,SL
    37. 'ftWeltkindertag ' -> TH
    38. ftTagDerDeutschenEinheit = 3 + (10 << 16) 'FEST (03.10.) -> alle BL
    39. ftReformationstag = 31 + (10 << 16) 'FEST (31.10.) -> BB,HB,HH,MV,NI,SN,ST,SH,TH
    40. ftAllerheiligen = 1 + (11 << 16) 'FEST (01.11.) -> BW,BY,NW,RP,SL
    41. ftBussUndBettag 'BEWEGLICH -> SN
    42. ftErsterWeihnachtsfeiertag = 25 + (12 << 16) 'FEST (25.12.) -> alle BL
    43. ftZweiterWeihnachtsfeiertag = 26 + (12 << 16) 'FEST (26.12.) -> alle BL
    44. End Enum
    45. Private _Feiertag As New HashSet(Of Integer)(DirectCast([Enum].GetValues(GetType(Feiertag)), Integer()))
    46. <Extension()>
    47. Public Function GetFeiertag(ByVal dtDate As Date) As Feiertag
    48. dtDate = dtDate.Date
    49. Dim n = dtDate.Day + (dtDate.Month << 16)
    50. If _Feiertag.Contains(n) Then Return DirectCast(n, Feiertag)
    51. Dim Ostersonntag = GetOstersonntag(dtDate.Year)
    52. Dim BussUndBettag = GetBussUndBettag(dtDate.Year)
    53. If dtDate = Ostersonntag.AddDays(-2) Then Return Feiertag.ftKarfreitag
    54. If dtDate = Ostersonntag.AddDays(1) Then Return Feiertag.ftOstermontag
    55. If dtDate = Ostersonntag Then Return Feiertag.ftOstersonntag
    56. If dtDate = Ostersonntag.AddDays(39) Then Return Feiertag.ftChristiHimmelfahrt
    57. If dtDate = Ostersonntag.AddDays(49) Then Return Feiertag.ftPfingstSonntag
    58. If dtDate = Ostersonntag.AddDays(50) Then Return Feiertag.ftPfingstMontag
    59. If dtDate = Ostersonntag.AddDays(60) Then Return Feiertag.ftFronleichnam
    60. If dtDate = BussUndBettag Then Return Feiertag.ftBussUndBettag
    61. Return Feiertag.ftKeinFeiertag
    62. End Function
    63. Private Function GetOstersonntag(ByVal year As Integer) As Date
    64. Dim a As Integer = year Mod 19
    65. Dim b As Integer = year \ 100
    66. Dim c As Integer = (8 * b + 13) \ 25 - 2
    67. Dim d As Integer = b - (year \ 400) - 2
    68. Dim e As Integer = (19 * (year Mod 19) + ((15 - c + d) Mod 30)) Mod 30
    69. If e = 28 Then
    70. If a > 10 Then
    71. e = 27
    72. End If
    73. ElseIf e = 29 Then
    74. e = 28
    75. End If
    76. Dim f As Integer = (d + 6 * e + 2 * (year Mod 4) + 4 * (year Mod 7) + 6) Mod 7
    77. Return New DateTime(year, 3, 22).AddDays(e + f)
    78. End Function
    79. Private Function GetBussUndBettag(ByVal year As Integer) As Date
    80. Dim datum = New DateTime(year, 12, 25)
    81. Dim day As Integer = CInt(datum.DayOfWeek)
    82. day = If((day = 0), -11, -day - 4)
    83. datum = datum.AddDays(-4 * 7).AddDays(day)
    84. Return datum
    85. End Function
    86. End Module


    Nun lass' ich im Urlaubsplaner in einer Schleife Testweise den Urlaub vom 01.01.2020 bis 31.12.2020 eintragen,
    dabei fange ich erstmal zum Testen die Feiertage in einer Textbox ab

    VB.NET-Quellcode

    1. Dim loopDatum = startDatum
    2. Do While loopDatum < endDatum.AddDays(1)
    3. Dim ft = GetFeiertag(loopDatum)
    4. If ft <> Feiertag.ftKeinFeiertag Then
    5. msgInformation($"{ft} | {loopDatum.ToShortDateString}")
    6. End If


    Soweit so gut. Nu tritt folgendes Problem auf:
    Bis einschl. 01. Mai läuft das sauber durch, dann meldet er

    Christi Himmelfahrt 02.05. (ist eigentlich am 21.05.)
    Pfingstsonntag 03.05. (ist eigentlich am 31.05.)
    Pfingstmontag 04.05. (ist eigentlich am 01.06.)
    Fronleichnam 05.05. (ist eigentlich am 11.06.)

    Danach richtig:

    Christi Himmelfahrt 21.05.
    Pfingstsonntag 31.05.
    Pfingstmontag 01.06.
    Fronleichnam 11.06.

    Danach falsch:
    BussUndBettag 02.11. / 18.11.

    Direkt danach richtig:
    BussUndBettag 18.11.

    es sieht so aus als würde es nach den festen Feiertagen (Tag der deutschen Einheit (01.05.) und Allerheiligen (01.11.) erstmal murks geben, bis
    er sich wieder fängt :?: :?: :?: . Hast du eine Idee, woran das liegen kann?
    Originaler (noch) Nichtskönner :D
    Mach mal die beweglichen Feiertage lieber über Spencers Osterformel. Die ist ohne Ausnahmen.
    Und: Augsburg hat nen Sonderfeiertag - falls relevant

    tragl schrieb:

    var's ins Deutsche umgeändert
    - die sind nicht ohne Grund englisch. Deutsch hat selten was im Code verloren, weil VB auf Englisch ist. Das gäbe dann ein leseunfreundliches Sprachmischmasch.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Danke für den BugReport.
    (Übrigens: Prefixe an Enums sind unnötig und hässlich. Vergleiche Bundesland.SH mit Bundesland.blSH)

    Ich hab folgende Ausgabe gemacht:

    VB.NET-Quellcode

    1. [...]
    2. Case sender Is btTest
    3. Dim startDatum = New Date(2020, 1, 1), endDatum = startDatum.AddYears(1)
    4. Dim loopDatum = startDatum
    5. Do While loopDatum < endDatum.AddDays(1)
    6. Dim ft = loopDatum.GetHoliday
    7. If ft <> Holidays.None Then
    8. Dbg($"{ft} | {loopDatum.ToShortDateString}")
    9. End If
    10. loopDatum += TimeSpan.FromDays(1)
    11. Loop

    Ergebnis:

    Quellcode

    1. 1 Neujahr | 01.01.2020
    2. 2 Karfreitag | 10.04.2020
    3. 3 Ostermontag | 13.04.2020
    4. 4 Maifeiertag | 01.05.2020
    5. 5 ChristiHimmelfahrt | 21.05.2020
    6. 6 Pfingstmontag | 01.06.2020
    7. 7 Fronleichnam | 11.06.2020
    8. 8 TagDerDeutschenEinheit | 03.10.2020
    9. 9 Heiligabend | 24.12.2020
    10. 0 ErsterWeihnachtsfeiertag | 25.12.2020
    11. 1 ZweiterWeihnachtsfeiertag | 26.12.2020
    12. 2 Silvester | 31.12.2020
    13. 3 Neujahr | 01.01.2021
    Sieht mir richtig aus - BussnBetTag hamwa nich.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

    VaporiZed schrieb:

    - die sind nicht ohne Grund englisch. Deutsch hat selten was im Code verloren, weil VB auf Englisch ist. Das gäbe dann ein leseunfreundliches Sprachmischmasch.

    macht auch wieder Sinn - fällt schwer da den korrekten Übergang zu finden oder generell alles was Code betrifft in Englisch?
    Den Augsburger Feiertag würde ich außenvor lassen. Das würde für die Programmlogik einiges an Mehraufwand bedeuten. So wie es jetzt ist, kann man den Mitarbeitern oder den Standorten Bundesländer zuteilen
    und die dann für das Auslesen der Feiertage benutzen. (so zumindest der Plan :) )
    Ansonsten laufen die beweglichen Feiertage über die Gauss-Formel: (GetOstersonntag)
    Ich hatte das mit dem Eintrag in MSDN abgeglichen, entspricht exakt dem was da steht - nur eben für VB.NET umgewandelt
    ohne die hässlichen Microsoft.VisualBasic-Dinger :)

    VB.NET-Quellcode

    1. Private _Feiertag As New HashSet(Of Integer)(DirectCast([Enum].GetValues(GetType(Feiertag)), Integer()))
    2. <Extension()>
    3. Public Function GetFeiertag(ByVal dtDate As Date) As Feiertag
    4. dtDate = dtDate.Date
    5. Dim n = dtDate.Day + (dtDate.Month << 16)
    6. If _Feiertag.Contains(n) Then Return DirectCast(n, Feiertag)
    7. Dim Ostersonntag = GetOstersonntag(dtDate.Year)
    8. Dim BussUndBettag = GetBussUndBettag(dtDate.Year)
    9. If dtDate = Ostersonntag.AddDays(-2) Then Return Feiertag.ftKarfreitag
    10. If dtDate = Ostersonntag.AddDays(1) Then Return Feiertag.ftOstermontag
    11. If dtDate = Ostersonntag Then Return Feiertag.ftOstersonntag
    12. If dtDate = Ostersonntag.AddDays(39) Then Return Feiertag.ftChristiHimmelfahrt
    13. If dtDate = Ostersonntag.AddDays(49) Then Return Feiertag.ftPfingstSonntag
    14. If dtDate = Ostersonntag.AddDays(50) Then Return Feiertag.ftPfingstMontag
    15. If dtDate = Ostersonntag.AddDays(60) Then Return Feiertag.ftFronleichnam
    16. If dtDate = BussUndBettag Then Return Feiertag.ftBussUndBettag
    17. Return Feiertag.ftKeinFeiertag
    18. End Function
    19. Private Function GetOstersonntag(ByVal year As Integer) As Date
    20. Dim a As Integer = year Mod 19
    21. Dim b As Integer = year \ 100
    22. Dim c As Integer = (8 * b + 13) \ 25 - 2
    23. Dim d As Integer = b - (year \ 400) - 2
    24. Dim e As Integer = (19 * (year Mod 19) + ((15 - c + d) Mod 30)) Mod 30
    25. If e = 28 Then
    26. If a > 10 Then
    27. e = 27
    28. End If
    29. ElseIf e = 29 Then
    30. e = 28
    31. End If
    32. Dim f As Integer = (d + 6 * e + 2 * (year Mod 4) + 4 * (year Mod 7) + 6) Mod 7
    33. Return New DateTime(year, 3, 22).AddDays(e + f)
    34. End Function


    ErfinderDesRades schrieb:

    Danke für den BugReport.
    (Übrigens: Prefixe an Enums sind unnötig und hässlich. Vergleiche <code class="inlineCode">Bundesland.SH</code> mit <code class="inlineCode">Bundesland.blSH</code>)

    Stimmt, macht Sinn :thumbup:

    Schaust du nach dem Bug? Oder kann das schon mit der Formel (Gauss zu Spencer) zusammenhängen?
    Originaler (noch) Nichtskönner :D
    Hab schon geschaut - ich glaub der Bug ist deiner.

    Ansonsten mitte Sprachen sehe ich locker. Das wichtigste ist, dass die Leser es verstehen - und zuallererst man selbst.
    Klar, generell Englisch bevorzugen.
    Aber viele Dinge sind schon inne Muttersprache schwierig genug begrifflich exakt zu fassen - da mussich mich nicht auch noch mit meinen Englisch-Unkenntnissen lächerlich machen.

    Also Strictly Denglisch!
    Dassis die Sprache der Zukunft!

    ErfinderDesRades schrieb:

    Hab schon geschaut - ich glaub der Bug ist deiner.

    Hmh seltsam - dabei hab' ich nicht wirklich was verändert...
    Ich schau' mir das morgen mal in Ruhe an. Lässt sich vermutlich schnell nachvollziehen, denn das veranstaltet das Programm auch wenn ich beim 01.05. anfange :)
    Originaler (noch) Nichtskönner :D

    ErfinderDesRades schrieb:

    Hab schon geschaut - ich glaub der Bug ist deiner.

    so, ich hab mir das grad mal mit Haltepunkt angeschaut. Er baut definitiv Mist (irgendwo)
    Datumsbereich: 01.05.2020 - 31.08.2020


    Der 01.05.2020 wird richtig erkannt:


    Der 02.05. verweist auf Christi Himmelfahrt, obwohl hinter ChristiHimmelfahrt in Pulbic Enum Feiertag nix steht
    er läuft den DirectCast durch und Returnt Feiertag.ChristiHimmelfahrt als 02.05.2020


    Hast du eine Idee? Ich nämlich nicht... ;(
    Originaler (noch) Nichtskönner :D

    tragl schrieb:

    obwohl hinter ChristiHimmelfahrt in Pulbic Enum Feiertag nix steht
    Das ist der Bug!
    Wenn hinter einem enum nix steht, wird einfach hochgezählt.
    Bei mir siehts so aus:

    VB.NET-Quellcode

    1. Public Enum Holidays
    2. None = 0
    3. Neujahr = 1 + (1 << 16)
    4. Maifeiertag = 1 + (5 << 16)
    5. TagDerDeutschenEinheit = 3 + (10 << 16)
    6. Heiligabend = 24 + (12 << 16)
    7. ErsterWeihnachtsfeiertag = 25 + (12 << 16)
    8. ZweiterWeihnachtsfeiertag = 26 + (12 << 16)
    9. Silvester = 31 + (12 << 16)
    10. Karfreitag = 2
    11. Ostermontag
    12. ChristiHimmelfahrt
    13. Pfingstmontag
    14. Fronleichnam
    15. End Enum
    Dadurch erhält ChristiHimmelfahrt den Wert 4 - und das wird im HashSet nie gefunden, wenn darin realexistierende Datumse gesucht werden.
    Also die beweglichen Feiertage haben sehr niedrige Werte, während die fixen Feiertage den Monat als monat << 16 eincodiert haben, und dadurch immer > 2^16 liegen.
    Ja - ich wollte dich nur mal mit einem Beispiel für schlecht wartbaren Code versorgen :rolleyes: .
    (Es gibt Leute, die hassen es, wenn Enum-Werte nicht explizit zugewiesen sind - wirklich!)

    ErfinderDesRades schrieb:

    Das ist der Bug!
    Wenn hinter einem enum nix steht, wird einfach hochgezählt.

    Da muss man erstmal drauf kommen. Ich dachte halt "komm, räumst das sauber und ordentlich auf und reihst die Feiertage mal in die richtige Reihenfolge... "
    war auch wieder falsch :D
    jetzt läuft das auch durch:

    VB.NET-Quellcode

    1. Public Module Feiertage
    2. Public Enum Bundesland
    3. 'Quelle Statistisches Bundesamt: https://www.destatis.de/DE/Themen/Laender-Regionen/Regionales/Gemeindeverzeichnis/Glossar/bundeslaender.html
    4. SH = 1 '01 Schleswig-Holstein (SH)
    5. HH = 2 '02 Hamburg (HH)
    6. NI = 3 '03 Niedersachsen (NI)
    7. HB = 4 '04 Bremen (HB)
    8. NW = 5 '05 Nordrhein-Westfalen (NW)
    9. HE = 6 '06 Hessen (HE)
    10. RP = 7 '07 Rheinland-Pfalz (RP)
    11. BW = 8 '08 Baden-Württemberg (BW)
    12. BY = 9 '09 Bayern (BY)
    13. SL = 10 '10 Saarland (SL)
    14. BE = 11 '11 Berlin (BE)
    15. BB = 12 '12 Brandenburg (BB)
    16. MV = 13 '13 Mecklenburg-Vorpommern (MV)
    17. SN = 14 '14 Sachsen (SN)
    18. ST = 15 '15 Sachsen-Anhalt (ST)
    19. TH = 16 '16 Thüringen (TH)
    20. End Enum
    21. Public Enum Feiertag
    22. 'Quelle: https://www.deutschland-feiert.de/feiertage/ und https://www.schulferien.org/deutschland/feiertage/2021/
    23. KeinFeiertag = 0
    24. Neujahr = 1 + (1 << 16) 'FEST (01.01.) -> alle BL
    25. HeiligeDreiKönige = 6 + (1 << 16) 'FEST (06.01.) -> BW,BY,ST
    26. 'InternationalerFrauentag ' -> BE
    27. TagDerArbeit = 1 + (5 << 16) 'FEST (01.05.) -> alle BL
    28. MariäHimmelfahrt = 15 + (8 << 16) 'FEST (15.08.) -> BY,SL
    29. 'Weltkindertag ' -> TH
    30. TagDerDeutschenEinheit = 3 + (10 << 16) 'FEST (03.10.) -> alle BL
    31. Reformationstag = 31 + (10 << 16) 'FEST (31.10.) -> BB,HB,HH,MV,NI,SN,ST,SH,TH
    32. Allerheiligen = 1 + (11 << 16) 'FEST (01.11.) -> BW,BY,NW,RP,SL
    33. ErsterWeihnachtsfeiertag = 25 + (12 << 16) 'FEST (25.12.) -> alle BL
    34. ZweiterWeihnachtsfeiertag = 26 + (12 << 16) 'FEST (26.12.) -> alle BL
    35. 'Bewegliche unter die festen wegen der Hochzählung
    36. Karfreitag = 2 'BEWEGLICH -> alle BL
    37. Ostersonntag 'BEWEGLICH -> BB
    38. Ostermontag 'BEWEGLICH -> alle BL
    39. ChristiHimmelfahrt 'BEWEGLICH -> alle BL
    40. PfingstSonntag 'BEWEGLICH -> BB
    41. PfingstMontag 'BEWEGLICH -> alle BL
    42. Fronleichnam 'BEWEGLICH -> BW,BY,HE,NW,RP,SL
    43. BussUndBettag 'BEWEGLICH -> SN
    44. End Enum
    45. Private _Feiertag As New HashSet(Of Integer)(DirectCast([Enum].GetValues(GetType(Feiertag)), Integer()))
    46. <Extension()>
    47. Public Function GetFeiertag(ByVal dtDate As Date) As Feiertag
    48. dtDate = dtDate.Date
    49. Dim n = dtDate.Day + (dtDate.Month << 16)
    50. If _Feiertag.Contains(n) Then Return DirectCast(n, Feiertag)
    51. Dim Ostersonntag = GetOstersonntag(dtDate.Year)
    52. Dim BussUndBettag = GetBussUndBettag(dtDate.Year)
    53. If dtDate = Ostersonntag.AddDays(-2) Then Return Feiertag.Karfreitag
    54. If dtDate = Ostersonntag.AddDays(1) Then Return Feiertag.Ostermontag
    55. If dtDate = Ostersonntag Then Return Feiertag.Ostersonntag
    56. If dtDate = Ostersonntag.AddDays(39) Then Return Feiertag.ChristiHimmelfahrt
    57. If dtDate = Ostersonntag.AddDays(49) Then Return Feiertag.PfingstSonntag
    58. If dtDate = Ostersonntag.AddDays(50) Then Return Feiertag.PfingstMontag
    59. If dtDate = Ostersonntag.AddDays(60) Then Return Feiertag.Fronleichnam
    60. If dtDate = BussUndBettag Then Return Feiertag.BussUndBettag
    61. Return Feiertag.KeinFeiertag
    62. End Function
    63. Private Function GetOstersonntag(ByVal year As Integer) As Date
    64. Dim a As Integer = year Mod 19
    65. Dim b As Integer = year \ 100
    66. Dim c As Integer = (8 * b + 13) \ 25 - 2
    67. Dim d As Integer = b - (year \ 400) - 2
    68. Dim e As Integer = (19 * (year Mod 19) + ((15 - c + d) Mod 30)) Mod 30
    69. If e = 28 Then
    70. If a > 10 Then
    71. e = 27
    72. End If
    73. ElseIf e = 29 Then
    74. e = 28
    75. End If
    76. Dim f As Integer = (d + 6 * e + 2 * (year Mod 4) + 4 * (year Mod 7) + 6) Mod 7
    77. Return New DateTime(year, 3, 22).AddDays(e + f)
    78. End Function
    79. Private Function GetBussUndBettag(ByVal year As Integer) As Date
    80. Dim datum = New DateTime(year, 12, 25)
    81. Dim day As Integer = CInt(datum.DayOfWeek)
    82. day = If((day = 0), -11, -day - 4)
    83. datum = datum.AddDays(-4 * 7).AddDays(day)
    84. Return datum
    85. End Function
    86. End Module


    ErfinderDesRades schrieb:

    Also die beweglichen Feiertage haben sehr niedrige Werte, während die fixen Feiertage den Monat als monat << 16 eincodiert haben, und dadurch immer > 2^16 liegen.
    Ja - ich wollte dich nur mal mit einem Beispiel für schlecht wartbaren Code versorgen .
    (Es gibt Leute, die hassen es, wenn Enum-Werte nicht explizit zugewiesen sind - wirklich!)

    Danke dafür :P aber macht ja auch mal Spaß Probleme zu analysieren. Mit dem <<16 hab' ich mich noch nicht befasst, ich hab mir aber
    gedacht, dass du dafür deine Gründe hattest :D

    Dann kann ich ja jetzt mal zusehen wie ich das mit den Bundesländern verknüpft bekomme
    Originaler (noch) Nichtskönner :D

    Quellcode

    1. 'Bewegliche Feiertage müssen in der Reihenfolge NACH den Festen stehen, da ansonsten nach
    2. 'Abfrage eines festen Feiertags die beweglichen nur hochgezählt werden und landen so auf dem
    3. 'falschen Datum

    so besser? ;)

    Da ich das Projekt später als OpenSource bereitstellen will, wäre es nicht sinnvoll hier mit vielen Kommentaren zu arbeiten damit ggf. auch Anfänger verstehen was da vor sich geht?
    Originaler (noch) Nichtskönner :D
    jo, für ein Tut mehr Kommentare auf jeden Fall.
    Aber vor allem müssen Kommentare auch stimmen. Es stimmt nicht, dass Bewegliche nach den Festen stehen müssen.
    Und kurz sein.

    Also heutzutage würde ich so kommentieren:

    VB.NET-Quellcode

    1. Public Enum Holidays
    2. 'Monate sind eincodiert an Bit-Positionen >= 16 ( '<<' Bitshift-Operator)
    3. Neujahr = 1 + (1 << 16)
    4. Maifeiertag = 1 + (5 << 16)
    5. TagDerDeutschenEinheit = 3 + (10 << 16)
    6. Heiligabend = 24 + (12 << 16)
    7. ErsterWeihnachtsfeiertag = 25 + (12 << 16)
    8. ZweiterWeihnachtsfeiertag = 26 + (12 << 16)
    9. Silvester = 31 + (12 << 16)
    10. 'Bewegliche Feiertage (und None) haben keinen Monat eincodiert
    11. 'Beachte, dass Enum-Werte sich fortlaufend erhöhen, wenn nicht explizit gesetzt
    12. None = 0
    13. Karfreitag
    14. Ostermontag
    15. ChristiHimmelfahrt
    16. Pfingstmontag
    17. Fronleichnam
    18. End Enum

    Aber Verständlichkeit ist sone Sache. Letztendlich kannst du besser entscheiden als ich, ob mein Code verständlich ist.
    Kommentare sind auch Code.

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „ErfinderDesRades“ ()

    @ErfinderDesRades:
    Ich hab ein neues DataSet für den "Planer" gebaut:


    soweit so gut, Beziehungen sind alle hinzugefügt - allerdings kann ich nur noch im DGV selbst Einträge editieren oder hinzufügen.
    Die Funktionen (bs.EditNew und bs.EditCurrent) landen alle in folgendem Fehler:


    Ich hab dir das Projekt mal angehangen, würdest du dir das mal anschauen, bevor ich einer der Helpers zerschieße?
    Dateien
    • Planer.zip

      (1,12 MB, 18 mal heruntergeladen, zuletzt: )
    Originaler (noch) Nichtskönner :D