Mr. Mercado
On a further study of the function posted by you, I noticed a few issues, which I hope you do not mind me bringing to your notice.
The broad logic that the week number is the number of Thursdays is accepted. The function aims at counting the number of thursdays from the first thursday to the thursday nearest to the given date.
But there is a minor mistake in implementation of the logic. But the first thursday should be the one relating to the year of the nearest thursday but not to the year of the given date.
Subject to your permission, I proposed minor changes to give effect to this. Here is your function modifed without disturbing your core logic.
- Code: Select all Expand view RUN
Function WeekDate2( dDate ) // Mr Mercado's function slightly modified
Local nDay, dFirstThu, dThisThu, nWeek, n
nDay := If( ( nDay := DoW( dDate ) - 1 ) == 0, 7, nDay )
dThisThu := dDate - nDay + 4
dFirstThu := StoD( Str( Year( dThisThu ), 4 ) + "0101" )
n := If( ( n := DoW( dFirstThu ) - 1 ) == 0, 7, n )
dFirstThu := dFirstThu - n + 4
nWeek := ( dThisThu - dFirstThu ) / 7 + If( Year( dThisThu ) > Year( dFirstThu ), 0, 1 )
Return Str( Year( dThisThu ), 4 ) + '-W' + StrZero( nWeek, 2 ) + '-' + Str( nDay, 1 )
Now, this function returns correct result for all dates.
I may also be permitted to make another point.
- Code: Select all Expand view RUN
nWeek := ( dThisThu - dFirstThu ) / 7 + If( Year( dThisThu ) > Year( dFirstThu ), 0, 1 )
gives the same result as :
- Code: Select all Expand view RUN
Int( ( DoY( dThisThu ) - 1 ) / 7 ) + 1
Second approach is computationally simpler and avoids the need to find the first thursday. This is the approah I adopted in the function I proposed. I repost the function below:
- Code: Select all Expand view RUN
function ISO8061WD( dDate )
local nWeekDay, dThu
nWeekDay := DoW( dDate ) - 1 // Find weekday
if nWeekDay == 0 // Mon to Sun
nWeekDay := 7 // as 1 to 7
endif
dThu := dDate - nWeekDay + 4 // Nearest Thursday
return Str( Year( dThu ), 4 ) + '-W' + StrZero( Int( ( DoY( dThu ) - 1 ) / 7 ) + 1, 2 ) + '-' + Str( nWeekDay, 1 )
This function economises on the number of computations and usage of date functions.
Both the functions give correct results for all dates.
Here is the program I used to check the correctness of the results from 1900-01-01 to 3000-12-31
- Code: Select all Expand view RUN
#include 'fivewin.ch'
function Main()
local aErr := {}
local dFrom, dUpto, dDate
local aPrev, aThis
local nMonth, nDay
dFrom := STOD( '19000101' )
dUpto := STOD( '30001212' )
aPrev := Isowd( dFrom )
dDate := dFrom + 1
do while dDate <= dUpto
aThis := isowd( dDate )
if aThis[ 3 ] == 1
if aPrev[ 3 ] != 7
AAdd( aErr, { dDate, 1 } )
endif
if aThis[ 2 ] == 1
// first week should start between Dec 29 and Jan 4
nMonth := Month( dDate )
nDay := Day( dDate )
if !( ( nMonth == 12 .and. nDay >= 29 ) .or. ( nMonth == 1 .and. nDay <= 4 ) )
AAdd( aErr, { dDate, 2 } )
endif
if aThis[ 1 ] - aPrev[ 1 ] != 1
AAdd( aErr, { dDate, 3 } )
endif
else
if aThis[ 2 ] - aPrev[ 2 ] != 1
AAdd( aErr, { dDate, 4 } )
endif
if aThis[ 1 ] != aPrev[ 1 ]
AAdd( aErr, { dDate, 5 } )
endif
endif
else
if aThis[ 3 ] - aPrev[ 3 ] != 1
AAdd( aErr, { dDate, 6 } )
endif
if aThis[ 2 ] != aPrev[ 2 ] .or. aThis[ 1 ] != aPrev[ 1 ]
AAdd( aErr, { dDate, 7 } )
endif
endif
ACopy( aThis, aPrev )
dDate++
enddo
if Len( aErr ) > 0
xbrowse( aErr, 'Errors' )
else
MsgInfo( 'All Correct' )
endif
return nil
function ISOWD( dDate )
local nWeekDay, dThu
nWeekDay := DoW( dDate ) - 1 // Find weekday
if nWeekDay == 0 // Mon to Sun
nWeekDay := 7 // as 1 to 7
endif
dThu := dDate - nWeekDay + 4 // Nearest Thursday
return { Year( dThu ), Int( ( DOY( dThu ) - 1 ) / 7 ) + 1, nWeekDay }