Autor(en): Bernhard Koehler (
koehlerbv), Formeln:
Jürgen Schomann und
Samuel MenigatJoachim (
jr)
Stand: 05.09.2009
Version: 1.02
Notes-Versionen: 4.x, 5.x, 6.x, 7.x, 8.x
Atnotes-Thread:
Thread (Einer von vielen)
VorbemerkungDas Thema "Kalenderwoche" erscheint uns in der Regel einfach zu lösen: Kalender aufschlagen und nachschauen ... Softwaretechnisch ist dies nicht ganz so einfach, allein schon, weil in unterschiedlichen Regionen der Erde auch unterschiedliche Regeln für die Berechnung der Wochennummer gelten. Im europäischen Sprachraum hat sich jedoch die Norm "ISO 8601" hinsichtlich der Berechnung der Kalenderwochennummer durchgesetzt.
Hierzu ein Auszug aus der deutschen Wikipedia (Stand 07.01.2005):
Kalenderwoche
Das Jahr wird in Kalenderwochen eingeteilt, die durchnummeriert sind. Ein Jahr hat 52 oder 53 Kalenderwochen.
Bei den Wochennummerierungen gibt es verschiedene Variationen:
Die erste Woche des Jahres ist
* jene, in die der 1. Januar fällt
* die erste vollständige Woche des Jahres
* die erste Woche, in die mindestens 4 Tage des neuen fallen. (ISO 8601)
Diese 3 Variationen beziehen sich alle auf die klassische Woche. Abweichungen gibt es z. B. in Großbritannien, wo es ein Steuerjahr gibt, das immer am 6. April beginnt.
Die internationale Norm ISO 8601 (1973) legt als Wochenanfang den Montag fest. Die erste Woche des Jahres muss mindestens vier Tage enthalten. Dies ist gleichbedeutend mit der Woche, die den 4. Januar enthält, oder der Woche, die den ersten Donnerstag des Jahres enthält. In Deutschland ist seit 1976 festgelegt, dass die Woche mit dem Montag beginnt: DIN 1355 (1974), DIN EN 28601 (1993).
Ein Vorteil dieser Zählweise ist, dass die letzte Kalenderwoche des Vorjahres nicht gleichzeitig die erste Kalenderwoche des Folgejahres ist.
Beispiel:
* Kalenderwoche "KW 52", 2003: 2003-W52 (Montag, 2003-12-22 bis Sonntag, 2003-12-28)
* Kalenderwoche "KW 1", 2004: 2004-W01 (Montag, 2003-12-29 bis Sonntag, 2004-01-04)
In weiten Teilen der Welt (z.B. Nordamerika) gilt die Konvention, den Sonntag als ersten Wochentag zu rechnen. Dies stimmt mit der Tradition der Judentums und Christentums überein, die den Samstag (Sabbat) als siebten Wochentag rechnen. Ebenso ist es in Japan (siehe Woche (Japan)). In den islamischen Ländern beginnt die Woche bereits mit dem Samstag.
Umsetzung der ISO 8601 in LotusScriptIn LotusScript gibt es leider keine eingebaute Funktion, mit der sich nach ISO 8601 die Kalenderwoche errechnen lässt. Die Format-Funktion verspricht zwar eine Berechnung der KW - leider aber abseits des von uns benötigten Standards. Siehe hierzu auch
Thread "Kalenderwoche mit Format berechnen"Der folgende Code basiert auf einem Algorithmus, der sich - ganz ähnlich codiert - im Netz (und in fertigen Applikationen) vielfach finden lässt. Christian Meis hat ihn 1999 bei Keysolutions veröffentlicht. Ich habe keine Ahnung, ob er der Ur-Autor ist. Einiges spricht aber dafür, denn der dort veröffentlichte Code funktioniert nur auf Systemen mit deutscher Datumsnotation.
Gerade wegen der Datumsnotation habe ich das Teil überarbeitet und für beliebige Datums-/Zeiteinstellungen lauffähig gemacht sowie ein paar programming standards eingebaut.
WICHTIG: Die Routine "ErrorHandler" (sowieso ein Muss für jede Anwendung) ist hier nur ein Dummy, der den jeweiligen Spezifika / Vorlieben etc. entsprechend selbst erstellt werden muss.
Sub ErrorHandler (szModule As String)
'Just a place holder ...
End Sub
Function CalculateISOWeekday (vDateGiven As Variant) As Integer
' Purpose: Calculates the weekday based on the ISO rules (0 = Monday, 6 = Sunday)
' Arguments:
' vDateGiven - the date we have to calculate the ISO weekday for
' Returns: The ISO weekday (0 = Monday, 6 = Sunday). If an error occurs, the return value will be -1
' Created by: Bernhard Koehler on 13.12.2004 Modified by:
' Changes:
Dim iTempOffset As Integer
On Error Goto ErrorRoutine
'The default (error-) result:
CalculateISOWeekday = -1
iTempOffset = Weekday (vDateGiven)
If iTempOffset = 1 Then 'Sunday in non-ISO version
iTempOffset = iTempOffset + 7
End If
CalculateISOWeekday = iTempOffset - 2
Exit Function
ErrorRoutine:
CalculateISOWeekday = -1
Call ErrorHandler ("CalculateISOWeekday")
Exit Function
End Function
Function CalculateWeekNo (vDateGiven As Variant) As Integer
' Purpose: Calculates the week number based on ISO 8601:1988
' Arguments:
' vDateGiven - the date we have to calculate the week number for
' Returns: The calendar week number. If an error occurs, the return value will be -1
' Notes: This routine is an enhanced version of the algorithm posted by Christian Meis posted in 1999 on www.keysolutions.com
' Created by: Bernhard Koehler on 13.12.2004 Modified by:
' Changes:
Dim iDateGivenOffset As Integer
Dim iYearGiven As Integer
Dim vJanuary4 As Variant
Dim iJanuary4Offset As Integer
Dim vFirstMonday As Variant
Dim iJanuary1Offset As Integer
Dim iDecember31Offset As Integer
Dim iWeekNo As Integer
On Error Goto ErrorRoutine
'The default result of the function:
CalculateWeekNo = -1
'Check if vDateGiven is a valid Date/Time value:
If Not (Isdate (vDateGiven)) Then
Exit Function
End If
iYearGiven = Year (vDateGiven)
'Get the offset of the given day to the ISO weekday value:
iDateGivenOffset = CalculateISOWeekday (vDateGiven)
'Get the offset for the first and the last day of the given year:
iJanuary1Offset = CalculateISOWeekday (Datenumber (iYearGiven, 1, 1))
iDecember31Offset = CalculateISOWeekday (Datenumber (iYearGiven, 12, 31))
'The ISO rule:
'Monday is the first day of the week. Week #1 is the week that contains the 4th of January (ISO 8601).
'The week at the end/beginning of the year belongs to the next/previous year, if there are 3 days or less of that week in the year in question.
'If the input date is after the 28th of December and the year ends with a monday, tuesday or wednesday, then the week belongs to the following year
'if the entered date is not a saturday or sunday
If Month (vDateGiven) = 1 And Day (vDateGiven) < 4 And iJanuary1Offset > 3 And iDateGivenOffset > 1 Then
iYearGiven = iYearGiven - 1
End If
' If the input date is after the 28th of December and the year ends with
' a monday, tuesday or wednesday, then the week belongs to the following year
' if the entered date is not a saturday or sunday
If Month (vDateGiven) = 12 And Day (vDateGiven) > 28 And iDecember31Offset < 3 And iDateGivenOffset < 5 Then
iYearGiven = iYearGiven + 1
End If
'The 4th of January defines week #1
vJanuary4 = Datenumber (iYearGiven, 1, 4)
'Offset to the monday of week #1
vFirstMonday = vJanuary4 - CalculateISOWeekday (vJanuary4)
'The time range between the monday of week #1 and the monday of the week in question is divided by 7, plus 1 for the first week:
iWeekNo = (vDateGiven - iDateGivenOffset - vFirstMonday) \ 7 + 1
'Return the result:
CalculateWeekNo = iWeekNo
Exit Function
ErrorRoutine :
Call ErrorHandler ("CalculateWeekNo")
CalculateWeekNo = -1
Exit Function
End Function
In Spaltenformeln von Ansichten oder in anderem @functions-bezogenen Kontext nützt natürlich LotusScript herzlich wenig, daher hier auch Umsetzung für die Formelsprache. Ausgangspunkt ist der Wert von "Datum":
REM "Berechnung der Kalenderwoche des Tages >>Datum<<";
Date := [<Datum>];
WeekThursday := @Adjust(Date; 0; 0; @If (@Weekday(Date) = 1; -3; -@Weekday (Date) + 5); 0; 0; 0);
WeekYear := @Year (WeekThursday);
January4Date := @Date (WeekYear; 1; 4);
FirstThursday := @Adjust (January4Date; 0; 0; @If (@Weekday (January4Date) = 1; -3; -@Weekday (January4Date) + 5); 0; 0; 0);
Week := ((WeekThursday - FirstThursday) / 86400) / 7 + 1;
@Prompt([Ok];"";@Text (Week) + ". KW, " + @Text (WeekYear))
Obige Formel wurde von
Jürgen Schomann zur Verfügung gestellt - meine eigene Routine war einige Zeilen länger.
Vielen Dank dafür.
Ebensolcher Dank geht an
Samuel Menigat - er lieferte die Idee zu diesem
Umkehrschluss: Einen Wochentag in einer bestimmten Kalenderwoche des Jahres bestimmenDer von Samuel beigetragene Algorithmus lässt sich besonders einfach in seiner ursprünglichen Fassung nachvollziehen. Wir gehen hierbei davon aus, dass durch die Variablen "Year" und "WeekNo" diese Werte vorgegeben sind:
_Monday := @If(
@Weekday(@Date(year; 1; 1))=1; @Adjust(@Date(year; 1; 1); 0; 0; 1+ ((WeekNo-1)*7); 0; 0; 0);
@Weekday(@Date(year; 1; 1))=2; @Adjust(@Date(year; 1; 1); 0; 0; ((WeekNo-1)*7); 0; 0; 0);
@Weekday(@Date(year; 1; 1))=3; @Adjust(@Date(year; 1; 1); 0; 0; ((WeekNo-1)*7) -1; 0; 0; 0);
@Weekday(@Date(year; 1; 1))=4; @Adjust(@Date(year; 1; 1); 0; 0; ((WeekNo-1)*7)-2; 0; 0; 0);
@Weekday(@Date(year; 1; 1))=5; @Adjust(@Date(year; 1; 1); 0; 0; ((WeekNo-1)*7)-3; 0; 0; 0);
@Weekday(@Date(year; 1; 1))=6; @Adjust(@Date(year; 1; 1); 0; 0; ((WeekNo-1)*7)+3; 0; 0; 0);
@Weekday(@Date(year; 1; 1))=7; @Adjust(@Date(year; 1; 1); 0; 0; ((WeekNo-1)*7)+2; 0; 0; 0);
"");
@Prompt ([OK]; "Ergebnis"; @Text (_Monday))
Die kürzere und performantere Variante habe ich wie folgt erstellt:
_FirstDayOfYear := @Date (Year; 1; 1);
WeekNoMonday := @Adjust (_FirstDayOfYear; 0; 0; @Subset (@Subset (1 : -0 : -1 : -2 : -3 : 3 : 2; @Weekday (_FirstDayOfYear)); -1) + ((WeekNo - 1) * 7); 0; 0; 0);
@Prompt ([Ok]; "Ergebnis"; @Text (WeekNoMonday))