ces1a Posted March 4, 2019 Share Posted March 4, 2019 ;This script will calculate the Nth weekday of any month. Just replace the numbers for $Year, $Month, $Week, and $Weekday with numbers of your choice, ;Some lines are not really needed but are there to allow testing and proofing. It is based on Excel formula. #include <Date.au3> Global $tmp, $Year = "2019", $Month = "2", $Week = 2, $WeekDay = 4 Global $aNth = StringSplit("First ,Second ,Third ,Fourth ,Fifth ", ",") Func GetWeekDay($Week, $Year, $Month, $WeekDay) Local $LastDay = 8 - $WeekDay, $EndDay = $Week * 7 + 1 $Month = $Month > 10 ? $Month : "0" & $Month Local $iWday = _DateToDayOfWeek($YEAR, $Month, $LastDay) Return $YEAR & "/" & $Month & "/" & $EndDay - $iWday EndFunc MsgBox(0,'',$aNth[$Week] & _DateDayOfWeek($WeekDay) & " of " & _DateToMonth($Month) & _ " " & $Year & " is " & GetWeekDay($Week, $Year, $Month, $WeekDay) ) GetNthWeekDay.au3 Link to comment Share on other sites More sharing options...
Deye Posted March 4, 2019 Share Posted March 4, 2019 Was also discussed here Here is Yet another demo for anyone to try: #include <Date.au3> Global $aNth = StringSplit("First,Second,Third,Fourth,Fifth", ",") _DateDayByIstance() ;today $Y = 2019 For $m = 1 To 12 For $d = 1 To 7 ConsoleWrite(@LF) For $inst = 1 To 5 _DateDayByIstance($Y, $m, $d, $inst) Next Next Next Func _DateDayByIstance($iYear = @YEAR, $iMonth = @MON, $iWeekDay = @WDAY, $iInstance = Round(@MDAY / 7)) Local $iInst = ($iInstance * 7) - 7 + 1 Local $iDOW = $iWeekDay - _DateToDayOfWeek($iYear, $iMonth, 1) $Date = $iYear & "/" & $iMonth & "/" & $iDOW + ($iDOW < 0 ? 7 + $iInst : $iInst) If _DateIsValid($Date) Then ConsoleWrite("@ " & $aNth[$iInstance] & " instance of " & _DateTimeFormat($Date, 1) & @LF) EndFunc Deye Link to comment Share on other sites More sharing options...
FrancescoDiMuro Posted March 4, 2019 Share Posted March 4, 2019 @ces1a Thanks for sharing By the way, in the UDF Date.au3, there is an undocumented function, _WeekNumber(), which is very similiar to yours: expandcollapse popup; #NO_DOC_FUNCTION# ============================================================================================================= ; Name...........: _WeekNumber ; Description ...: Find out the week number of current date OR date given in parameters ; Syntax.........: _WeekNumber([$iYear = @YEAR[, $iMonth = @MON[, $iDay = @MDAY[, $iWeekStart = 1]]]]) ; Parameters ....: $iYear - Year value (default = current year) ; $iMonth - Month value (default = current month) ; $iDay - Day value (default = current day) ; $iWeekStart - Week starts from Sunday (1, default) or Monday (2) ; Return values .: Success - Returns week number of given date ; Failure - -1 and sets @ERROR to: ; | 1 - On faulty parameters ; |99 - On non-acceptable weekstart and uses default (Sunday) as starting day ; Author ........: JdeB ; Modified.......: ; Remarks .......: ; Related .......: ; Link ..........: ; Example .......: ; =============================================================================================================================== Func _WeekNumber($iYear = @YEAR, $iMonth = @MON, $iDay = @MDAY, $iWeekStart = 1) ; Check for erroneous input in $Day, $Month & $Year If $iDay > 31 Or $iDay < 1 Then Return SetError(1, 0, -1) ElseIf $iMonth > 12 Or $iMonth < 1 Then Return SetError(1, 0, -1) ElseIf $iYear < 1 Or $iYear > 2999 Then Return SetError(1, 0, -1) ElseIf $iWeekStart < 1 Or $iWeekStart > 2 Then Return SetError(2, 0, -1) EndIf ; Local $iStartWeek1, $iEndWeek1 ;$idow = _DateToDayOfWeekISO($iYear, $iMonth, $iDay); Local $iDow0101 = _DateToDayOfWeekISO($iYear, 1, 1); Local $iDate = $iYear & '/' & $iMonth & '/' & $iDay ;Calculate the Start and End date of Week 1 this year If $iWeekStart = 1 Then If $iDow0101 = 6 Then $iStartWeek1 = 0 Else $iStartWeek1 = -1 * $iDow0101 - 1 EndIf $iEndWeek1 = $iStartWeek1 + 6 Else $iStartWeek1 = $iDow0101 * - 1 $iEndWeek1 = $iStartWeek1 + 6 EndIf Local $iStartWeek1ny ;$iStartWeek1Date = _DateAdd('d',$iStartWeek1,$iYear & '/01/01') Local $iEndWeek1Date = _DateAdd('d', $iEndWeek1, $iYear & '/01/01') ;Calculate the Start and End date of Week 1 this Next year Local $iDow0101ny = _DateToDayOfWeekISO($iYear + 1, 1, 1); ; 1 = start on Sunday / 2 = start on Monday If $iWeekStart = 1 Then If $iDow0101ny = 6 Then $iStartWeek1ny = 0 Else $iStartWeek1ny = -1 * $iDow0101ny - 1 EndIf ;$IEndWeek1ny = $iStartWeek1ny + 6 Else $iStartWeek1ny = $iDow0101ny * - 1 ;$IEndWeek1ny = $iStartWeek1ny + 6 EndIf Local $iStartWeek1Dateny = _DateAdd('d', $iStartWeek1ny, $iYear + 1 & '/01/01') ;$iEndWeek1Dateny = _DateAdd('d',$IEndWeek1ny,$iYear+1 & '/01/01') ;number of days after end week 1 Local $iCurrDateDiff = _DateDiff('d', $iEndWeek1Date, $iDate) - 1 ;number of days before next week 1 start Local $iCurrDateDiffny = _DateDiff('d', $iStartWeek1Dateny, $iDate) ; ; Check for end of year If $iCurrDateDiff >= 0 And $iCurrDateDiffny < 0 Then Return 2 + Int($iCurrDateDiff / 7) ; > week 1 If $iCurrDateDiff < 0 Or $iCurrDateDiffny >= 0 Then Return 1 EndFunc ;==>_WeekNumber Click here to see my signature: Spoiler ALWAYS GOOD TO READ: Forum Rules Forum Etiquette Link to comment Share on other sites More sharing options...
Gianni Posted March 4, 2019 Share Posted March 4, 2019 (edited) @ces1a, Your function returns invalid date for certain requests, try to ask for the fifth Friday of February 2019 for example ...@Deye, your function will instead return nothing in that case or also if you cross to the next month(s).@FrancescoDiMuro, that function will return the number of the week of the year a date falls into. Is different from what OP intends to achieve. If allowed I would propose my 2 cents, instead of returning an invalid date or nothing at all, my proposed function will return the date where you're going to end up by counting the number of weekdays you specified. So if you ask the 9° sunday of january, the function will count 9 sundays starting from the first one of january and going on up counting 9 of the requested weekday. So is up to you to ask sensate questions, otherwise the function will return however the required date (a valid date anyway) and will just set the error flag if you go beyond the current month Hope it can be of use #include <date.au3> Global Enum $Sunday = 1, $Monday, $Tuesday, $Wednesday, $Thursday, $Friday, $Saturday MsgBox(0, "Demo", _DateGetWeekDay(@YEAR, 2, 5, $Friday) & @CRLF & @CRLF & "@error: " & @error) ; counts next n. weekdays starting from passed year/month and returns target date ; sets @error if required date goes beyond required month Func _DateGetWeekDay($Year = @YEAR, $Month = @MON, $Week = 1, $WeekDay = @WDAY, $DateFormat = 0) Local $iFirstDayOfMonth = _DateToDayOfWeek($Year, $Month, 1) - 1 ; the day of the Week Range is 0 to 6 where 0=Sunday. Local $DaysToSkip = Mod(7 + ($WeekDay - 1 - $iFirstDayOfMonth), 7) + (($Week - 1) * 7) Local $aTargetDate, $aDummyTime _DateTimeSplit(_DateAdd('D', $DaysToSkip, $Year & '/' & $Month & '/01'), $aTargetDate, $aDummyTime) Return SetError($Year <> $aTargetDate[1] Or $Month <> $aTargetDate[2], 0, _DateTimeFormat($aTargetDate[1] & '/' & $aTargetDate[2] & '/' & $aTargetDate[3], $DateFormat)) EndFunc ;==>_DateGetWeekDay Edited March 4, 2019 by Chimp added check also on target year Skeletor 1 Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt.... Link to comment Share on other sites More sharing options...
Deye Posted March 5, 2019 Share Posted March 5, 2019 (edited) For kicks, This one doesn't need to validate or Incept errors no matter what you throw at it So long as the syntax is correctly put, .. Or if one finds logic to use this otherwise (didn't look too much further..) expandcollapse popup#include <Date.au3> ; _DateAdd() Like way $Day = 6 $iInstance = 108 $fromDate = True ; will Start counting from the day number else for WeekDay "friday = 6" keep at default = false $Date = _DateDayInMonth_Istance(@YEAR, @MON, $Day, $iInstance, $fromDate) ConsoleWrite(_DateTimeFormat($Date, 1) & @LF) ; Confirm $Format = StringFormat("%04i/%02i/%02i", @YEAR, @MON, $Day) ConsoleWrite(_DateTimeFormat(_DateAdd("w", $iInstance, $Format), 1) & @LF) ; Get first Instance of the WeekDay from the returned year and month $iInstance = 1 $SplitDate = StringSplit($Date, "/") $Day = _DateToDayOfWeek($SplitDate[1], $SplitDate[2], $SplitDate[3]) $Date = _DateDayInMonth_Istance($SplitDate[1], $SplitDate[2], $Day, $iInstance) ConsoleWrite(_DateTimeFormat($Date, 1) & @LF) Func _DateDayInMonth_Istance($iYear = @YEAR, $iMonth = @MON, $iWeekDay = @WDAY, $iInstance = 1, $iStartDay = False) Local $iInst = ($iInstance * 7) - 7 + 1, $iDay If $iStartDay Then $iInst += 6 Else $iWeekDay -= _DateToDayOfWeek($iYear, $iMonth, 1) If $iWeekDay < 0 Then $iWeekDay += 7 EndIf $iDay = $iWeekDay + $iInst While $iDay > _DateDaysInMonth($iYear, $iMonth) $iDay = Abs($iDay - _DateDaysInMonth($iYear, $iMonth)) Switch $iMonth Case 12 $iYear += 1 $iMonth = 1 Case Else $iMonth += 1 EndSwitch WEnd Return StringFormat("%04i/%02i/%02i", $iYear, $iMonth, $iDay) EndFunc Deye Edited March 5, 2019 by Deye cleaning .. Earthshine 1 Link to comment Share on other sites More sharing options...
Gianni Posted March 5, 2019 Share Posted March 5, 2019 (edited) Hi @Deye, what the fifth parameter is intended for? I don't get it Edited March 5, 2019 by Chimp Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt.... Link to comment Share on other sites More sharing options...
Deye Posted March 5, 2019 Share Posted March 5, 2019 (edited) Hi Chimp, Just Added that for the small test, So using that switch will only mimic DateAdd() Deye Edited March 5, 2019 by Deye Link to comment Share on other sites More sharing options...
Gianni Posted March 6, 2019 Share Posted March 6, 2019 ha, thanks, got it, so, using true as fifth parameter then $day becomes the day of the month from wich starting counting the number of weeks ($iInstance) forward regardless of the week day. hmmm, if you allow , even if the 2 things seems similar, are 2 quite different concepts. in this way you loose track of the Week Day. I would keep two separate functions for that.. remaining on topic (counting the number of a given weekday) I would instead implement the possibility of counting a given weekday starting from a specific date rather than from the beginning of the month, (maybe leaving as default the beginning of the month. parameters should be, and be used differently in that case) sorry for saying, just a thought Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt.... Link to comment Share on other sites More sharing options...
Deye Posted March 6, 2019 Share Posted March 6, 2019 (edited) Thanks Chimp, @ Sticking back to topic, This is should be my last version for this function #include <Date.au3> ; 5 instances of Friday begining of the first instace DaysInMonth of February 2019 should return "Friday, March 1, 2019" $YEAR = @YEAR $February = 2 $Friday = 6 $Monday = 2 For $Instance = 1 To 5 $Date = _DateDayInMonth_Istance($YEAR, $February, $Friday, $Instance) ConsoleWrite("Instance " & $Instance & " - " & _DateTimeFormat($Date, 1) & @LF) $Date = _DateDayInMonth_Istance($YEAR, $February, $Monday, $Instance) ConsoleWrite("Instance " & $Instance & " - " & _DateTimeFormat($Date, 1) & @LF) ConsoleWrite(@LF) Next Func _DateDayInMonth_Istance($iYear = @YEAR, $iMonth = @MON, $iWeekDay = @WDAY, $iInstance = 1) $iWeekDay -= _DateToDayOfWeek($iYear, $iMonth, 1) - 8 Return _DateAdd("d", ($iInstance * 7) - 7, StringFormat("%04i/%02i/%02i", $iYear, $iMonth, $iWeekDay + ($iWeekDay > 7 ? -7 : 0))) EndFunc ;==>_DateDayInMonth_Istance Deye Edited March 9, 2019 by Deye Revised Code Gianni 1 Link to comment Share on other sites More sharing options...
Gianni Posted March 7, 2019 Share Posted March 7, 2019 @Deye, nice version, simple and concise. ... I post my last version too with some bonus extra: I've rearranged a bit the parameters sequence so to be able to pass not only the year and the month, but also optionally the day. In this way you can search for a weekday starting from any date. If you want to find the instance of a weekday starting from a given date instead of the beginning of the month, just pass also the optional starting day as fifth parameter. The function will return the date of nth weekday you requested. Note if you want you can also search backward instead of forward, just pass a negative instance. This can be useful to find the last weekday of a month for example, just pass the last day of the month as starting date and search backward by passing -1 as instance. I think can be of use sometime... expandcollapse popup#include <date.au3> Example() Func Example() ; just to simplify the association between the weekdays and the corresponding ISO numbers Local Enum $Monday = 1, $Tuesday, $Wednesday, $Thursday, $Friday, $Saturday, $Sunday ; (1=monday ... 7=sunday) ConsoleWrite('The first Sunday of August is ') ConsoleWrite(_DateTimeFormat(_DateFindWeekDay($Sunday, 1, @YEAR, 8), 1) & @CRLF) ConsoleWrite('The last Sunday of the year is ') ConsoleWrite(_DateTimeFormat(_DateFindWeekDay($Sunday, -1, @YEAR, 12, 31), 1) & @CRLF) ; the following resulting date falls into the next month, so the @extended flag is setted to 1 ConsoleWrite('The fifth Friday of February is ') Local $TargetDate = _DateFindWeekDay($Friday, 5, 2019, 2) Local $extended = @extended ConsoleWrite(_DateTimeFormat($TargetDate, 1) & @TAB & "@extended: " & $extended & @CRLF) ConsoleWrite('The last Monday of this century will be on ') ConsoleWrite(_DateTimeFormat(_DateFindWeekDay($Monday, -1, 2099, 12, 31), 1) & @CRLF) ConsoleWrite('The first Sunday of my life was on ') ConsoleWrite(_DateTimeFormat(_DateFindWeekDay($Sunday, 1, 1962, 5, 2), 1) & @CRLF) EndFunc ;==>Example ; #FUNCTION# ==================================================================================================================== ; Name ..........: _DateFindWeekDay ; Description ...: Find a date by counting a number of weekdays forward or backward starting from passed date ; Syntax ........: _DateFindWeekDay([$iWeekDay = DOW_ISO[, $iInstance = 1[, $iYear = @YEAR[, $iMonth = @MON[, $iDay = 1]]]]]) ; Parameters ....: $iWeekDay - [optional] An integer value. the weekday you want to find (1=Monday...7=Sunday) default is doday's weekday ; $iInstance - [optional] An integer value. the wanted instance of WeekDay. Default is 1. ; $iYear - [optional] An integer value. the year from which to start counting. Default is @YEAR. ; $iMonth - [optional] An integer value. the month from which to start counting @MON. ; $iDay - [optional] An integer value. the day from which to start counting. Default is 1 (starting of month). ; Return values .: The target date in the format "YYYY/MM/DD" ; Remark: If target date falls outside the starting month, the @extended flag is setted to 1 ; Author ........: Chimp ; Modified ......: ; Remarks .......: ; Related .......: ; Link ..........: ; Example .......: _DateFindWeekDay(7, 1) ; Finds the first Sunday of current Month ; =============================================================================================================================== Func _DateFindWeekDay($iWeekDay = _DateToDayOfWeekISO(@YEAR, @MON, @MDAY), $iInstance = 1, $iYear = @YEAR, $iMonth = @MON, $iDay = 1) Local $iDirection = ($iInstance < 0 ? -1 : 1), $aTarget, $aDummyTime Local $iWeekDayOfDate = _DateToDayOfWeekISO($iYear, $iMonth, $iDay) - 1 ; WeekDay (0 to 6 where 0=Monday) Local $iDaysToSkip = Mod($iDirection * 7 + ($iWeekDay - 1 - $iWeekDayOfDate), 7) + (($iInstance + -1 * $iDirection) * 7) _DateTimeSplit(_DateAdd('D', $iDaysToSkip, $iYear & '/' & $iMonth & '/' & $iDay), $aTarget, $aDummyTime) Return SetExtended($iYear <> $aTarget[1] Or $iMonth <> $aTarget[2], StringFormat("%04i/%02i/%02i", $aTarget[1], $aTarget[2], $aTarget[3])) EndFunc ;==>_DateFindWeekDay Deye 1 Chimp small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt.... Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now