Autor Thema: [LotusScript, Formelsprache]: Kalenderwoche: Berechnung mit Formeln und LS  (Gelesen 30989 mal)

Offline koehlerbv

  • Moderator
  • Gold Platin u.s.w. member:)
  • *****
  • Beiträge: 20.460
  • Geschlecht: Männlich
Autor(en):             Bernhard Koehler (koehlerbv), Formeln: Jürgen Schomann und Samuel Menigat

Joachim (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)



Vorbemerkung

Das 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):

Zitat
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 LotusScript

In 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.

Code
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":

Code
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 bestimmen

Der 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:
Code
_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:
Code
_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))
« Letzte Änderung: 27.02.10 - 16:33:33 von koehlerbv »

 

Impressum Atnotes.de  -  Powered by Syslords Solutions  -  Datenschutz