TS: Meine dumme Frage zum Jahresende: Anzahl der Kalenderwochen im Kalenderjahr.

Hello,

tut mir leid, wenn ich hier als Erster die dumme Frage zum Jahresende absende.
(Weitere sind durchaus möglich, auch von Anderen :-P )

Kann mir bitte nochmal jemand erklären, wie das mit den Kalenderwochen war? Wie berechnet und bestimmt sich das mit der Anzahl der KW im Jahr?

2018 ist der letzte Tag im Jahr ja ein Montag und gehört eigentlich zur 53. Kalenderwoche 2018. 2018 hat aber, soweit ich das bisher sehen konnte, nur 52 KW, und der Montag, 31.12.2018 gehört schon zur ersten KW 2019.

Wie ist da die Regel?

Liebe Grüße
Tom S.

--
Es gibt nichts Gutes, außer man tut es!
Das Leben selbst ist der Sinn.
  1. Hallo TS,

    https://de.wikipedia.org/wiki/Woche#Kalenderwoche

    Bis demnächst
    Matthias

    --
    Rosen sind rot.
    1. Hello,

      https://de.wikipedia.org/wiki/Woche#Kalenderwoche

      jetzt weiß ich alles :-O

      Glaub ja nicht, dass ich das heute noch verstehe in meinem betagten Alter und dem vorangeschrittenen Alkoholgenuss.

      Ich muss die Sch.... programmieren für eine MySQL-Benutzer-Funktion für den gesamten europäischen Raum. Ist Europa (auch die Anrainer der EU) sich zumindest in diesem Punkte einig?

      Liebe Grüße
      Tom S.

      --
      Es gibt nichts Gutes, außer man tut es!
      Das Leben selbst ist der Sinn.
      1. Tach!

        Ich muss die Sch.... programmieren für eine MySQL-Benutzer-Funktion

        SELECT WEEKOFYEAR(date) - Fertig!

        für den gesamten europäischen Raum. Ist Europa (auch die Anrainer der EU) sich zumindest in diesem Punkte einig?

        Die Frage klärt das englische Pendant zur bereits verlinkten Wikiseite.

        dedlfix.

      2. Ist Europa (auch die Anrainer der EU) sich zumindest in diesem Punkte einig?

        Damit deine Motivation nicht überschlägt, ist es sicher dass die Definition "was ist eine Woche" überhaupt gleich ist? Also mit welchem Tag beginnt sie.

        Ich würde am besten sowohl den ersten Tag der Woche, als auch die Berechnung der Kalenderwochen variabel d.h. konfigurierbar halten. Lieber gleich dran denken als später alles ändern müssen.

        1. Hello,

          Ich würde am besten sowohl den ersten Tag der Woche, als auch die Berechnung der Kalenderwochen variabel d.h. konfigurierbar halten. Lieber gleich dran denken als später alles ändern müssen.

          Danke. Guter Hinweis!

          Das könnte tatsächlich sein, dass die von Dedlfix verlinkte Funktion das nicht leistet. Muss ich tatsächlich zuerst mal überprüfen ;-O

          Liebe Grüße
          Tom S.

          --
          Es gibt nichts Gutes, außer man tut es!
          Das Leben selbst ist der Sinn.
          1. Tach!

            Das könnte tatsächlich sein, dass die von Dedlfix verlinkte Funktion das nicht leistet. Muss ich tatsächlich zuerst mal überprüfen ;-O

            Die leistet genau das, was wir hier haben. Es gibt aber auch eine andere Funktion mit ähnlichem Namen, mit der kann man die Ergebnisse für die anderen Kombinationen erfragen.

            dedlfix.

  2. Kann mir bitte nochmal jemand erklären, wie das mit den Kalenderwochen war? Wie berechnet und bestimmt sich das mit der Anzahl der KW im Jahr?

    Hängt vom verwendeten System ab; z.B. ist die erste Kalenderwoche, die die den 4. Januar enthält (Woche beginnt Montags) oder am 1. Januar startet die erste Kalenderwoche (alle anderen Wochen starten Sonntags, 1. und letzte 33./34. Woche sind potentiell kürzer als die anderen) oder Woche beginnt Sonntags und die erste Woche ist die, die den 1. Januar enthält oder die erste Woche im neuen Jahr, die vollständig ist. Alternativ könnte man auch das sinnvollste System verwenden, in dem jedes Jahr an einem Sweetmorn beginnt, die erste Woche am ersten Holyday des Jahres Mungtag, der ein Setting Orange ist, endet, und das Jahr mit dem 73. Setting Orange bzw. 73. The Aftermath endet.

  3. Hi Tom,

    nun, den Weg zur Regel nach ehem. DIN1355 kennst Du ja jetzt. Damit ist die Berechnung der KW kein Hexenei, aber ich stelle gerade fest, daß in meiner Lib auch noch eine MEthode fehlt welche die Tage für eine bestimmte KW/Jahr ermittelt -- Damit hast Du meinen Sonntag gerettet 😉

    MfG

    1. problematische Seite

      Hi Tom,

      nun, den Weg zur Regel nach ehem. DIN1355 kennst Du ja jetzt. Damit ist die Berechnung der KW kein Hexenei, aber ich stelle gerade fest, daß in meiner Lib auch noch eine MEthode fehlt welche die Tage für eine bestimmte KW/Jahr ermittelt -- Damit hast Du meinen Sonntag gerettet 😉

      Ooops, fertig 😉

      Schönen 1. Advent Euch allen!

      1. problematische Seite

        Hallo pl,

        Ooops, fertig 😉

        Deine Wochen haben nur 6 Tage.

        Bis demnächst
        Matthias

        --
        Rosen sind rot.
        1. problematische Seite

          Hallo Matthias,

          Ooops, fertig 😉

          Deine Wochen haben nur 6 Tage.

          Ach Du Scheise 😉

           my $fst = $self->_firstkwjd($year) + ($kw - 1) * 7;
           ($fst, $fst+1, $fst+2, $fst+3, $fst+4, $fst+5, $fst+6);
          

          Da hat $fst+4 gefehlt .. naja, ich sehe das nicht mehr so richtig. Herzlichen Dank!!

          Weiterhin schönen Advent 😉

          1. problematische Seite

            ($fst, $fst+1, $fst+2, $fst+3, $fst+4, $fst+5, $fst+6);
            

            Da hat $fst+4 gefehlt

            Ich habe mal vor fernen Tagen gehört, dass man auch deshalb sowas in for-Schleifen verbaut...

            1. problematische Seite

              ($fst, $fst+1, $fst+2, $fst+3, $fst+4, $fst+5, $fst+6);
              

              Da hat $fst+4 gefehlt

              Ich habe mal vor fernen Tagen gehört, dass man auch deshalb sowas in for-Schleifen verbaut...

              Ja 😉 Genau das war ja mein Fehler 😉

              Der Algorithmus zur Berechnung der Kalenderwoche nach DIN1355 ist übrigens ganz einfach. Man braucht den $jd (Julianischer Tag) und den $wd (numerischer Wochentag) zum 1.1. eines Jahres.

              Die Rechnung ist dann wie folgt:

                  # map(wd)
                  my %swd = (
                      0 => 1,
                      1 => 0,
                      2 => -1,
                      3 => -2,
                      4 => -3,
                      5 => 3,
                      6 => 2,
                      7 => 1,
                  );
                  return ($jd + $swd{$wd});
              

              Der Return ist also der Julianische Tag für den Montag der 1. Kalenderwoche eines Jahres die auch mal in das vorherige Jahr fallen kann. Mit diesem Tag kann man alles Weitere berechnen. So einfach sieht also DIN1355 (ISO/R 2015-1971) (deutsch) in Perl aus 😉

              MfG

              1. problematische Seite

                    # map(wd)
                    my %swd = (
                        0 => 1,
                        1 => 0,
                        2 => -1,
                        3 => -2,
                        4 => -3,
                        5 => 3,
                        6 => 2,
                        7 => 1,
                    );
                    return ($jd + $swd{$wd});
                

                So einfach sieht also DIN1355 (ISO/R 2015-1971) (deutsch) in Perl aus 😉

                Naja. Wenn es um "einfach" geht, dann würde ich

                my @swd = ( 1, 0, -1, -2, -3, 3, 2, 1 );
                return  $jd + $swd[$wd]
                

                vorziehen.

                Andere nennen noch kürzeres "einfach":

                return  $jd + ( 1, 0, -1, -2, -3, 3, 2, 1 )[$wd];
                
                1. problematische Seite

                      # map(wd)
                      my %swd = (
                          0 => 1,
                          1 => 0,
                          2 => -1,
                          3 => -2,
                          4 => -3,
                          5 => 3,
                          6 => 2,
                          7 => 1,
                      );
                      return ($jd + $swd{$wd});
                  

                  So einfach sieht also DIN1355 (ISO/R 2015-1971) (deutsch) in Perl aus 😉

                  Naja. Wenn es um "einfach" geht, dann würde ich

                  my @swd = ( 1, 0, -1, -2, -3, 3, 2, 1 );
                  return  $jd + $swd[$wd]
                  

                  vorziehen.

                  Ja sicher kann man hier auch den unsichtbaren Index eines Arrays verwenden, wenn der Schlüssel eben numerisch ist. I.d.R. jedoch bevorzuge ich eine namentliche Adressierung weil das besser lesbar ist. Und äquivalent zu:

                  # Fall Wochentag 1.1.
                  my %cwd = (
                    Montag     => 0,
                    Dienstag   => -1,
                    Mittwoch   => -2,
                    Donnerstag => -3,
                    Freitag    => 3,
                    Samstag    => 2,
                    Sonntag    => 1
                  );
                  

                  Damit man das auch nach Jahren nachvollziehen kann 😉

                  Dieser quasi Switch implementiert aber nur eine der in DIN1355 gefassten Bedingungen, den Beginn der Wochenzählung. Die zweite Bedingung, nämlich wieviele KWs ein Jahr hat, ist nicht minder wichtig. MfG

                  PS: Wenn man sieht was manchmal an entsetzlich wenig Code nach tagelanger Überlegung rauskommt, kann man echte Zweifel am Stundensatz bekommen 😉

                  1. problematische Seite

                    Die zweite Bedingung, nämlich wieviele KWs ein Jahr hat,

                    Wenn der 1.1 und der 31.12. auf einen Donnerstag fallen hat das Jahr 53 Wochen, sonst stets 52.

                    Für den Rest gilt: Perlianer sollten einfach nicht mit Datums- und Zeitangaben rechnen. Irgendwie wurde Perl nicht dafür gemacht. So entstand auch das Jahr-2000-Problem.

                    #!/usr/bin/perl -W
                    
                    use POSIX;
                    
                    for ( $i=2000; $i<2100; $i++ ) {
                            print ( $i, " : ",  getWeeksOfYear($i), "\n" );
                    }
                    exit;
                    
                    
                    sub getWeekdayFromYMD {
                        return strftime( '%w', 1, 1, 1, $_[2], $_[1]-1, $_[0]-1900 );
                    }
                    
                    
                    sub getWeeksOfYear {
                        if  (
                                ( 4 == getWeekdayFromYMD( $_[0], 1 , 1  ) )
                                and
                                ( 4 == getWeekdayFromYMD( $_[0], 12, 31 ) ) 
                        ) {
                            return 53;
                        } else {
                            return 52;
                        }
                    }
                    
                    1. problematische Seite

                      Dein Code wirft für das Jahr 2004 => 52 Wochen. 2004 hat aber 53 Wochen weil es an einem Donnerstag beginnt. DIN1355 sagt Beginn oder Ende am Donnerstag (nicht und).. Dein Code rechnet also nicht richtig. Mach aus dem und ein oder dann stimmts 😉

                      Wo siehst Du ein Jahr-2000-Problem?

                      Bis dann 😉

                      1. problematische Seite

                        Mach aus dem und ein oder dann stimmts 😉

                        Ihr Habt beide recht. Ich hab bei Wikipedia zu schnell gelesen. Da steht "oder".

                        #!/usr/bin/perl -W
                        
                        use POSIX;
                        
                        for ( $i=2000; $i<2100; $i++ ) {
                                print ( $i, " : ",  getWeeksOfYear($i), "\n" );
                        }
                        exit;
                        
                        
                        sub getWeekdayFromYMD {
                            return strftime( '%w', 1, 1, 1, $_[2], $_[1]-1, $_[0]-1900 );
                        }
                        
                        
                        sub getWeeksOfYear {
                            if  (
                                    ( 4 == getWeekdayFromYMD( $_[0], 1 , 1  ) )
                                    or
                                    ( 4 == getWeekdayFromYMD( $_[0], 12, 31 ) ) 
                            ) {
                                return 53;
                            } else {
                                return 52;
                            }
                        }
                        

                        So. jetzt stimmts.

                        Wo siehst Du ein Jahr-2000-Problem?

                        Wenn dauernd etwas wie $_[0]-1900 notwendig wird, dann hat die Sprache einen Knacks. JS ist also auch betroffen. Sowas verleitete vor anno 2k geradezu dazu, mit zweistelligen Jahreszahlen zu rechnen.

                        1. problematische Seite

                          Unnunne 😉

                          Wenn dauernd etwas wie $_[0]-1900 notwendig wird, dann hat die Sprache einen Knacks. JS ist also auch betroffen. Sowas verleitete vor anno 2k geradezu dazu, mit zweistelligen Jahreszahlen zu rechnen.

                          würdch mal sagen, das ist aus Kompatibilitätsgründen so hängengeblieben. PL und OS wollen ja auch irgendwie zusammenarbeiten. Ansonsten sind 2stellige Jahresangaben durchaus gängig und sogar negative. Dem Jahr -1 folgt das Jahr 1 und das passt sowohl rechentechnisch als auch zur Tatsache daß es das Jahr 0 nicht gibt.

                          Ich meine damit, daß man Kalenderberechungen von zeitrelevanten Builtin-Funktionen (gmtime, localtime, POSIX::strftime) vollständig trennen sollte. Wannst willst schicke ich Dir meine Module Scaliger.pm (Gregorianischer und Julianischer Kalender) und Kalenderwoche.pm sag Bescheid. MfG

                    2. problematische Seite

                      Hallo Regina Schaukrug,

                      Wenn der 1.1 und der 31.12. auf einen Donnerstag fallen hat das Jahr 53 Wochen, sonst stets 52.

                      In jedem Gemeinjahr fallen Neujahr und Silvester auf denselben Wochentag. 😝

                      Bis demnächst
                      Matthias

                      --
                      Rosen sind rot.
                    3. problematische Seite

                      mit POSIX

                      package KW;
                      
                      use POSIX;
                      use strict;
                      use warnings;
                      
                      # Wochentag numerisch {num}, 
                      # abgekürzt{abb}, voll{full}
                      sub wd{
                          my $self = shift;
                          my $day   = shift;
                          my $month = shift;
                          my $year  = shift;
                          my %hunt = split /\s+/, strftime( 
                              'num %w abb %a full %A', 0, 0, 0, 
                              $day, $month-1, $year-1900 
                          );
                          return \%hunt;
                      }
                      
                      # Wieviele Wochen hat ein Jahr
                      sub weeks_in_year{
                          my $self = shift;
                          my $year = shift;
                          return 
                          $self->wd(1,1,$year)->{num} == 4 || $self->wd(31,12,$year)->{num} == 4 ?
                          53 : 52;        
                      }
                      
                      # Datum für Montag in erster KW eines Jahres
                      sub first{
                          my $self = shift;
                          my $year = shift;
                          
                          my %cwd = (
                              0 => sprintf("02.01.%d", $year),
                              1 => sprintf("01.01.%d", $year),
                              2 => sprintf("31.12.%d", $year-1),
                              3 => sprintf("30.12.%d", $year-1),
                              4 => sprintf("29.12.%d", $year-1),
                              5 => sprintf("04.01.%d", $year),
                              6 => sprintf("03.01.%d", $year)
                          );
                          $cwd{$self->wd(1,1,$year)->{num}};
                      }
                      
                      1;
                      
                      print KW->first(2027);
                      
                      
                      

                      Viel Spaß damit

                      1. problematische Seite

                        Hallo pl,

                        Danke für das Codebeispiel.

                        Aber, hat perl kein switch/case Konstrukt? Das wäre in der first Funktion bestimmt besser als 6 unnötige sprintf Aufrufe.

                        Rolf

                        --
                        sumpsi - posui - clusi
                        1. problematische Seite

                          Hi Rolf mein Lieber,

                          Danke für das Codebeispiel.

                          Aber, hat perl kein switch/case Konstrukt? Das wäre in der first Funktion bestimmt besser als 6 unnötige sprintf Aufrufe.

                          sprintf wird nur einmal aufgerufen: Mit dem Wochentag gehts in das assoz. Array und damit nur ein sprintf ins return. Schreib da mal noch ein return davor

                          return $cwd{$self->wd(1,1,$year)->{num}};

                          Ansonsten ist der klassische Switch auch in Perl möglich. Ich kenne jedoch keinen der damit arbeitet 😉

                          Schöne Grüße.

                          1. problematische Seite

                            Hallo pl,

                            sprintf wird nur einmal aufgerufen…

                            Das würde eins von 2 Dingen voraussetzen:

                            Entweder wird Perl von einem gut optimierenden Compiler übersetzt, der dem Code ansieht, dass nur ein Wert des Array verwendet wird und den Code unter der Haube in ein switch umbaut.

                            Oder Perl speichert keine Werte im (assoziativen) Array, sondern Lambdas, so dass der eigentliche Wert eines Array-Elements erst beim Zugriff bestimmt wird. Dafür sieht die Syntax aber eigentlich zu konventionell aus.

                            Tatsächlich? Ist Perl so ein raffinierter Hund?

                            Nach meinem Verständnis einer altehrwürdigen, vom Optimierungen unverdorbenen Scriptsprache wie PHP oder Perl wird zunächst mal das Array aufgebaut und dafür sieben Mal sprintf aufgerufen. Es entsteht ein assoziatives Array mit sieben int => string Einträgen (wobei auch ein einfaches Array genügt hätte, aber das scheint ja bei vielen Perlians idiomatisch zu sein), und davon wird dann einer verwendet.

                            Rolf

                            --
                            sumpsi - posui - clusi
                            1. problematische Seite

                              Stimmt, Du hast recht. Du kannst es entweder ohne sprintf machen:

                                  my %cwd = (
                                      0 => "02.01.$year",
                                      1 => "01.01.$year",
                                      2 => "31.12.@{[$year-1]}",
                                      3 => "30.12.@{[$year-1]}",
                                      4 => "29.12.@{[$year-1]}",
                                      5 => "04.01.$year",
                                      6 => "03.01.$year"
                                  );
                              

                              oder Du legst eine Referenz auf sprintf:

                                  my %cwd = (
                                      2 => sub{sprintf "31.12.%d", $year - 1},
                                  );
                              
                                  return $cwd{$self->wd(1,1,$year)->{num}}->();
                              

                              MfG

                              1. problematische Seite

                                Ergänzung:

                                return $cwd{$self->wd(1,1,$year)->{num}}->($year);
                                
                                

                                Das Jahr muss noch übergeben werden.

                        2. problematische Seite

                          PS: Die Inline Berechnung geht so:

                          2 => "31.12.@{[$year-1]}",

                          unter Verzicht auf das sprintf. MfG

                        3. problematische Seite

                          Hi Rolf,

                          Danke für das Codebeispiel.

                          Aber, hat perl kein switch/case Konstrukt? Das wäre in der first Funktion bestimmt besser als 6 unnötige sprintf Aufrufe.

                          Wenn man mit fortlaufenden Tagen $jd (Julian Day) rechnen kann, ist es gar nicht notwendig, den Wochentag $wd über einen Switch zu jagen. Da genügt eine einfache Kontrollstruktur was den Wochentag $wd betrifft und der Rest ist Berechnung. Die Tabelle noch einmal, links der numerische Wochentag (1 bis 7) und rechts die Korrektur für den fortlaufenden Tag jd':

                          |wd|jd' |1|0 |2|-1 |3|-2 |4|-3 |5|3 |6|2 |7|1

                          In eine Kontrollstruktur gefasst ergibt sich für die Berechnung:

                          if( 1 <= $wd && $wd <= 4 ){
                             $jd = $jd - $wd - 1;
                          }
                          else{
                             $jd = $jd + 8 - $wd;
                          }
                          

                          Mir ist das heute an einer anderen Stelle wieder untergekommen, daß man beim Rechnen mit dem Datum auf Kontrollstrukturen weitgehend verzichten kann sofern es möglich ist mit fortlaufenden Tagen zu rechnen. Wenn man z.B. wissen will, ob auf KW 52 die KW 53 folgt oder KW 1 des nächsten Jahres, wird einfach ein JD-Tag der KW 52 hergenommen, 7 Tage aufaddiert und daraus die resultierende Woche berechnet.

                          Schönen Advent-Sonntag 😉

            2. problematische Seite

              ($fst, $fst+1, $fst+2, $fst+3, $fst+4, $fst+5, $fst+6);
              

              Da hat $fst+4 gefehlt

              Ich habe mal vor fernen Tagen gehört, dass man auch deshalb sowas in for-Schleifen verbaut...

              map wäre hier zweckmäßiger:

              @days = map{ $fst + $_ }( 0..6 );

              und noch ein Tipp lieber Kollege, mach Übergaben in Funktionen namentlich, am besten so:

              sub foo{
                my $self = shift;
                my $day  = shift;
                my $year = shift || $self->{year};
              }
              
              # oder als hash
              sub foo{
                my $self = shift;
                my %date = (
                  year  => 1984,
                  day   => 1,
                  month => ''
                @_);
              }
              

              so bleibt die Übersicht und Du kannst ggf. auch Default Werte setzen, s.o. MfG