Get same nth day of the month in x number of months - vb.net

I am needing some help converting a function from access vba to vb.net.
The script generates a new date, based on the date entered, and the number of months to be added.
“If today is the second Tuesday in March, what will be the second Tuesday in 4 months?”
Public Function NdNwk(dType As String, _
dtSpan As Integer, sDate As Date) As Variant
' This Function RETURNS the DAY of WHICH WEEK
' (e.g. Second Tuesday of the Month).
' FUNCTIONS to be passed to Variables:
' gtDoW: Day of the WEEK of the START DATE.
' (1 for Sunday, 2 for Monday, etc.)
' gtWoM: WEEK of the MONTH of the START DATE.
' (1 for First, 2 for Second, etc.)
' gtDSTdt: Desired DATE
' (generated by the [DateAdd] Function).
' CALL EXAMPLE: If TODAY is Tuesday, March 10, 2020,
‘ (second Tuesday of March), then using
' NdNwk(m, 2, #5/21/2020#)
' Would generate the DATE 5/12/2020,
' As the SECOND TUESDAY of MAY.
Dim gtDSTdt As Date, gtWoM As Integer, gtDoW As Integer
Dim iLoop As Integer, iPick As Integer, dstDTdom As Date
gtDoW = Weekday(sDate)
gtWoM = (Int((Day(sDate) - 1) / 7) + 1)
gtDSTdt = DateAdd(dType, dtSpan, sDate)
For iLoop = 1 To Day(DateSerial(Year(gtDSTdt), _
Month(gtDSTdt) + 1, 0))
dstDTdom = DateSerial(Year(gtDSTdt), _
Month(gtDSTdt), iLoop)
If Weekday(dstDTdom, 1) = gtDoW Then
iPick = iPick + 1
If iPick = gtWoM Then
NdNwk = dstDTdom
Exit Function
End If
End If
Next
End Function
Any and all help is appreciated here.

I used several of the properties and methods of the .net DateTime structure. https://learn.microsoft.com/en-us/dotnet/api/system.datetime?view=netcore-3.1
The arithmetic in the Function used the Mod operator which returns the remainder of the division. The integer division (the formard slash \) returns the integer portion of the division.
The only other thing that might be unfamiliar is the interpolated string, a string starting with $"". This allows you to directly embed variables in the string surround by { }.
Private Function NdNwk(InputDate As Date, MonthsAhead As Integer) As String
Dim newDate As Date
Dim DofWeek = InputDate.DayOfWeek
Dim Day = InputDate.Day
Dim OfInputMonth As Integer
If Day Mod 7 = 0 Then
OfInputMonth = Day \ 7
Else
OfInputMonth = (Day \ 7) + 1
End If
Dim TempDate = InputDate.AddMonths(MonthsAhead)
Dim NewMonth = TempDate.Month
Dim NewYear = TempDate.Year
Dim FirstWeek As Date
Dim NewDay As Integer
For d = 1 To 7
FirstWeek = New Date(TempDate.Year, TempDate.Month, d)
If FirstWeek.DayOfWeek = DofWeek Then
NewDay = d
Exit For
End If
Next
Dim DaysToAdd = (OfInputMonth - 1) * 7
newDate = New Date(NewYear, NewMonth, NewDay).AddDays(DaysToAdd)
Dim NewDateString = $"{newDate.ToString("MM/dd/yyyy")} is the {GetOrdinalString(OfInputMonth)} {DofWeek} of {TempDate.ToString("MMMM")}, {TempDate.Year}"
Return NewDateString
End Function
Private Function GetOrdinalString(input As Integer) As String
Dim output As String
Select Case input
Case 1
output = "1St"
Case 2
output = "2nd"
Case 3
output = "3rd"
Case 4
output = "4th"
Case 5
output = "5th"
Case Else
output = ""
End Select
Return output
End Function
Usage...
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim InputDate As Date
Dim MonthsToAdd As Integer
If Not Date.TryParse(TextBox1.Text, InputDate) Then
MessageBox.Show("Please enter a valid date in Date")
Return
End If
If Not Integer.TryParse(TextBox2.Text, MonthsToAdd) Then
MessageBox.Show("Please enter a valid number in Months To Add")
Return
End If
Dim d = NdNwk(InputDate, MonthsToAdd)
MessageBox.Show(d)
End Sub

First of all, thanks for all the feedback.
The solution that I was able to parse together is as follows:
A text box to show the number of months.
A text box to show the new date.
A button click action to run the following code:
Private Sub BtnMonth_Click(sender As Object, e As EventArgs) Handles BtnMonth.Click
Dim WrkDt As Date = Now
Dim QtyMnths As Integer = CType(TxtMntCount.Text, Int32)
Dim newFoM = New Date(WrkDt.Year, WrkDt.Month, 1).AddMonths(QtyMnths)
Dim DoWDt As Integer = WrkDt.DayOfWeek
Dim newMntdate = newFoM.AddDays(Enumerable.Range(0,
Date.DaysInMonth(newFoM.Year, newFoM.Month) - 1).Where(Function(i) newFoM.AddDays(i).DayOfWeek = DoWDt).Skip(1).First())
TxtNewDate.Text = Format(newMntdate, "MMMM dd, yyyy (ddd)")
End Sub
This works perfectly fine for me!
Mary's solution looks great, and I will give it a shot in the future when I need a modular input.
Thanks again for all the help!

I tested your solution, and it doesn’t produce the described results.
See test and correct solution at: https://dotnetfiddle.net/v5wGng
A couple of things from your original problem, you should prefer calculation to loops wherever possible, and you should use the required types wherever possible. If you have date in one format (string) and need it in another for your calculations, you should do the conversion and then call a function that does your calculations where all of the parameters are of the correct type.
Public Function GetSameWeekAndWeekDay(dt as date, months as integer) as Date
Dim newMonth =(new date(dt.year, dt.month, 1)).AddMonths(Months)
Dim week = getweek(dt)
Dim sameWeekDay = GetNthDayOfWeek(newMonth, week, dt.DayOfWeek)
Return SameWeekday
End Function
Public Function GetWeek(dt as date) as integer
Return(dt.day - 1) \ 7
End Function
Public Function GetNthDayOfWeek(dt as date, week as integer, weekDay as System.DayofWeek) as Date
Dim first = new Date(dt.year, dt.month, 1)
Dim baseDate = first.AddDays(-(first.DayOfWeek - system.dayofweek.Sunday))
Dim newDate = baseDate.AddDays((week * 7) + weekday)
If(newdate.DayOfWeek < first.DayOfWeek) then
newDate = newDate.AddDays(7)
End If
Return newdate
End Function

Related

vb.net 4th Tuesday when new year

I have written a Check Book application that stores the 4th Tuesday of the month in a SQLite Database and when the user writes a check and the Date Today is greater than the stored date a NEW 4th Tuesday date is updated in the DB and a function to deposit the SS Payment is loaded.
While testing I discovered that when the Year changes the first Function I wrote would fail.
So I have written a second Function to deal with the the change in Years.
The code seems to be working. I would like to combine the test into one Function as the code seems less than elegant. I will post the small TEST application code below.
The Two Functions are in a Module.
Public Class frmStart
Dim varSearchDate As Date
Dim varFTue As Date
Private Sub frmStart_Load(sender As Object, e As EventArgs) Handles MyBase.Load
tbBox1.Text = "2021-12-28" ' 2021-11-23 2021-12-28 TEST DATES
End Sub
Private Sub btnADD_Click(sender As Object, e As EventArgs) Handles btnADD.Click
varSearchDate = CDate(tbBox1.Text)
tbAns.Text = varSearchDate.ToString("M-d-yyyy")
Dim dateToday = Date.Today
Dim mo As String
mo = varSearchDate.ToString("MM")
If dateToday > varSearchDate And CInt(mo) <> 12 Then
varFTue = CDate(FourthTueOfNextMonth(Date.Today).ToString("yyyy-M-d"))
MsgBox("varFTue Next Mo " & varFTue)
tbBox2.Text = varFTue.ToString("yyyy-M-d")
'WriteNewFourthTue()
'gvTxType = "SS Deposit"
ElseIf dateToday > varSearchDate And CInt(mo) = 12 Then
varFTue = CDate(FourthTueOfNewYear(Date.Today).ToString("yyyy-M-d"))
MsgBox("varFTue New Yr " & varFTue)
tbBox3.Text = varFTue.ToString("yyyy-M-d")
'WriteNewFourthTue()
'gvTxType = "SS Deposit"
End If
End Sub
End Class
The two function and my TEST code to use ONLY ONE FUNCTION commented out
Module FunctionModule
'Function FourthTueOfNextMonth(dt As Date) As Date
' Dim currDate = New Date(dt.Year, dt.Month, 1)
' Dim nTuesday As Integer
' While nTuesday < 4
' If currDate.DayOfWeek = DayOfWeek.Tuesday Then
' nTuesday += 1
' End If
' currDate = currDate.AddDays(1)
' End While
' If dt.Month <> 12 Then
' Return New Date(dt.Year, dt.Month, currDate.Day - 1)
' ElseIf dt.Month = 12 Then
' Return New Date(dt.Year + 1, dt.Month - 11, currDate.Day - 1)
' End If
'End Function
Function FourthTueOfNextMonth(dt As Date) As Date
Dim currDate = New Date(dt.Year, dt.Month, 1)
Dim nTuesday As Integer
While nTuesday < 4
If currDate.DayOfWeek = DayOfWeek.Tuesday Then
nTuesday += 1
End If
currDate = currDate.AddDays(1)
End While
Return New Date(dt.Year, dt.Month, currDate.Day - 1)
End Function
Function FourthTueOfNewYear(dt As Date) As Date
Dim currDate = New Date(dt.Year + 1, dt.Month - 11, 1)
Dim nTuesday As Integer
While nTuesday < 4
If currDate.DayOfWeek = DayOfWeek.Tuesday Then
nTuesday += 1
End If
currDate = currDate.AddDays(1)
End While
Return New Date(dt.Year + 1, dt.Month - 11, currDate.Day - 1)
End Function
End Module
My Question is there a better way to write this code so I only have one Function?
Here's a function that finds the Fourth Tuesday of the NEXT Month, based on the Date passed in:
Function FourthTueOfNextMonth(dt As Date) As Date
' Start with the First Day of the Month, from the date passed in.
' Add one Month to that to get the first Day of the NEXT month.
Dim currDate As Date = (New Date(dt.Year, dt.Month, 1)).AddMonths(1)
' Find the First Tuesday of the Month
While currDate.DayOfWeek <> DayOfWeek.Tuesday
currDate = currDate.AddDays(1)
End While
' Add three more Weeks to jump to Fourth Tuesday
Return currDate.AddDays(21)
End Function
Note that the function always returns the fourth Tuesday of the NEXT month, and that it doesn't check to see if the date passed in is less than the Fourth Tuesday of the same month containing the date passed in.

VB.Net find last day of month with date format

I am trying to find the last day of the month and compare it to today's date
I do NOT want the integer number I would like the result in this format "MM-dd-yyyy"
Date Picker will not work for this project
Here is the code I using but the process seems overly complicated concocting strings
Side note when today is after the 4th Tue I write True and the Last Day of the month to a DB
when today is after the last day of the month and the bool is now True I write the new last day of the new month and false to the DB
Function FourthTueOfMonth(dt As Date) As Date
Dim currDate = New Date(dt.Year, dt.Month, 1)
Dim nTuesday = 0
While nTuesday < 4
If currDate.DayOfWeek = DayOfWeek.Tuesday Then
nTuesday += 1
End If
currDate = currDate.AddDays(1)
End While
Return New Date(dt.Year, dt.Month, currDate.Day - 1)
End Function
Private Sub btnFindDate_Click(sender As Object, e As EventArgs) Handles btnFindDate.Click
Dim tORf As Boolean = False
Dim dateToday = Date.Today
Dim dateFourthTue = (FourthTueOfMonth(Date.Today))
tbFourthTue.Text = dateFourthTue.ToString("MMM-dd-yyyy")
tbThree.Text = dateFourthTue.ToString("yyyy-MM-dd")
tbEndOFMonth.Text = Date.DaysInMonth(Date.Now.Year, Date.Now.AddMonths(0).Month).ToString
Dim dToday As Date
dToday = Date.Parse("10-01-2021")
Dim dtY = dateToday.ToString("yyyy")
Dim dtM = dateToday.ToString("MM")
Dim eom As String = Date.DaysInMonth(Date.Now.Year, Date.Now.AddMonths(0).Month).ToString
Dim dtALL As String
dtALL = dtM & "-" & eom & "-" + dtY
Dim testD As Date
testD = Date.Parse(dtALL)
If tORf = False And dToday > dateFourthTue Then
MessageBox.Show("Today > Fourth Tue")
'tORf = True'Write True
'tbMessage.Text = tORf.ToString
End If
If tORf = True And dToday > testD Then
MessageBox.Show("Today > End Of Last Month")
'tORf = False write False
'tbMessage.Text = tORf.ToString
End If
End Sub
The solution provided by #Albert D. Kallal is great for Visual Basic since DateSerial is in the Visual Basic namespace in the DateAndTime class. Here is a solution that should work in both vb and C#.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim dtToday As Date = Date.Today
Dim dtEndOfMonth = New Date(dtToday.Year, dtToday.Month + 1, 1).AddDays(-1)
Debug.Print(dtToday.ToString)
Debug.Print(dtEndOfMonth.ToString("MM-dd-yyyy"))
End Sub
A few things:
You want to use today - not "now()" as that includes a time portion. While a date type only has date, you should consider if you have datetime, and either way, no need to introduce and use a value that includes both date and time such as now does.
I reocmmend this code:
Dim dtToday As Date = Date.Today
Dim dtEndOfMonth As Date = DateSerial(dtToday.Year, dtToday.Month + 1, 0)
Debug.Print(dtToday)
Debug.Print(dtEndOfMonth)
Output:
Today happens to be the 1st, but any date would work. This includes end of year, and even leap years.
2021-10-01
2021-10-31
So, this is a long time old trick - goes back to old VB6, and even old VBA code from 20 years ago.
So, we use date serial to produce a date, but if you use 0 for the day, then you get the previous day, and thus gets you the last day of the current month.
So we toss in year, month + 1, and 0 for the date - that results in the last day of the current month.
I hate to admit I might have gave up the search too quick
found the answer here
Answer Here
Here is the code
Dim dateToday = Date.Now.AddMonths(0)
Dim dateEndOfMonth = New Date(dateToday.Year, dateToday.Month, DateTime.DaysInMonth(dateToday.Year, dateToday.Month))
tbMsg.Text = dateEndOfMonth.ToString("MM-dd-yyyy")
Code seems to be working ?
I have seen suggestions to use this format for comparing dates
TEST DATES use this format yyyyMMdd Please comment if you can add to the answer

The program will know the time span if else vb.net

I dont know how it is called thats why I called it time span.
I don't have any idea yet how to code it but this is what i want to do
Dim sy as String
If (June 2015) to (March 2016) Then
sy = "2015-2016"
End if
I know it is simple but i dont know how to code the June 2015 to 2016
Thanks in advance for the help
Edit :
Now i got to this code
I uses to do this
Dim value as DateTime = New DateTime(2017, 4, 1)
Dim value1 as DateTime = New DateTime(2018, 4, 1)
Dim sy as String
If my.computer.clock.localtime <= value then
sy = "2016-2017"
ElseIf my.computer.clock.localtime <= value1 then
sy = "2017-2018"
Else
msgbox("Check your current date")
End if
But when the computer's time changed to 2015 it shows "2016-2015"
So I use 4 textboxes to supply the data you need as variables. 2 for the years and 2 for the months. Also you need to think of how many moths left in the yeae and add the month number of the next year. If you span more than one year you have to account for this as well. O.K here is the snippet without error checking in a button event:
Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
Dim firstÝear As Integer = CInt(TextBox3.Text)
Dim secondYear As Integer = CInt(TextBox4.Text)
Dim firstmonthnumber As Integer = CInt(TextBox5.Text) 'months number in first year
Dim secondmonthnumber As Integer = CInt(TextBox6.Text)
Dim d1 As DateTime = New DateTime(firstÝear, firstmonthnumber, 1)
Debug.Print(d1.ToString)
Dim d2 As DateTime = New DateTime(secondYear, secondmonthnumber, 1)
Debug.Print(d2.ToString)
Dim M As Integer = Math.Abs((d1.Year - d2.Year)) - 1
Debug.Print(M.ToString)
Dim myYear As Integer = 12 - firstmonthnumber 'month left to the end of first year
Dim months As Integer = ((M * 12) + Math.Abs((d1.Month - d2.Month))) + myYear
Debug.Print(months.ToString)
End Sub

VBA function cannot return value to main sub

I have a problem:
Now I got a date range for a main sub (20160706-20160805) and I have to separate the value and put it as a date. What I do is:
Main sub date ()
Dim strDaterage as string
Dim startdate as date
'For the sake of simplicity, just use the hard code for the date
strDaterange = "20160706-20160805"
startdate = findstartdate()
msgbox (startdate)
End sub
And for the function to separate the date an extract the value:
Function findstartdate()
daterange = "20160706-20160805"
startDateange = Left(daterange, 8)
startYear = Left(startDateange, 4)
startMonth = Mid(startDateange, 5, 2)
startDay = Right(startDateange, 2)
startdate = DateSerial(startYear, startMonth, startDay)
End Function
I try using the byval and byfer but it still does not work. How can I return the date value back into the main sub?
Many thanks in advance!
Not sure why you are using a String as an input, but if you want you can use the code below (there are more efficient ways to do it).
Your Sub :
Sub SubDate()
Dim strDaterage As String
Dim startdate As Date
strDaterage = "20160706-20160805"
startdate = findstartdate(strDaterage)
MsgBox (startdate)
End Sub
Your function:
' need to add a String variable as an input, and return tha value as a Date
Function findstartdate(DateRange As String) As Date
startDateange = Left(DateRange, 8)
startYear = Left(startDateange, 4)
startMonth = Mid(startDateange, 5, 2)
startDay = Right(startDateange, 2)
startdate = DateSerial(startYear, startMonth, startDay)
' convert the string result to a Date
findstartdate = CDate(startdate)
End Function

Array of Workdays

I am looking for a way to store the last workday of each month between two "Input dates", and I need to store them as Strings in an array. I have tried to use the Worksheet function "Workday", but my input dates are of the format dd-mm-yyy, and I coundn't get it to work..
Any help is appreciated, Thanks.
Too bad, i've assumed that your VB.NET tag was the correct one, now it's removed. However, if someone needs something similar in .NET this might be helpful:
Dim fromDate = DateTime.Today.AddYears(-1) ' sample days
Dim toDate = DateTime.Today
Dim startDay = New Date(fromDate.Year, fromDate.Month, 1).AddMonths(1)
Dim endDay = New Date(toDate.Year, toDate.Month, 1).AddMonths(1)
Dim monthsBetween As Int32 = GetMonthsBetween(startDay, endDay)
Dim nonWorkingDays = {DayOfWeek.Saturday, DayOfWeek.Sunday}
Dim workingDatesBetween As New List(Of String)
For month As Int32 = 0 To monthsBetween
Dim d As DateTime = startDay.AddMonths(month)
' look into last months last days, shorter way
Dim lastWorkingDay As Date = Date.MinValue
While lastWorkingDay = Date.MinValue
d = d.AddDays(-1) ' look backwards into the last month to find the last working-day
If Not nonWorkingDays.Contains(d.DayOfWeek) Then
lastWorkingDay = d
workingDatesBetween.Add(lastWorkingDay.ToString("dd-MM-yyy", CultureInfo.InvariantCulture))
End If
End While
Next
Dim result = workingDatesBetween.ToArray()
This method was used to determine the number of months between two dates:
Public Shared Function GetMonthsBetween(date1 As DateTime, date2 As DateTime) As Int32
Dim months = Math.Abs(((date1.Year - date2.Year) * 12) + date1.Month - date2.Month)
Return months
End Function
or as reusable method (although i doubt that someone needs this method often):
Public Shared Function GetLastWorkingDatesInMonthBetween(fromDate As Date, toDate As Date) As Date()
Dim startDay = New Date(fromDate.Year, fromDate.Month, 1).AddMonths(1)
Dim endDay = New Date(toDate.Year, toDate.Month, 1).AddMonths(1)
Dim monthsBetween As Int32 = GetMonthsBetween(startDay, endDay)
Dim nonWorkingDays = {DayOfWeek.Saturday, DayOfWeek.Sunday}
Dim workingDatesBetween As New List(Of Date)
For month As Int32 = 0 To monthsBetween
Dim d As DateTime = startDay.AddMonths(month)
' look into last months last days, shorter way
Dim lastWorkingDay As Date = Date.MinValue
While lastWorkingDay = Date.MinValue
d = d.AddDays(-1) ' look backwards into the last month to find the last working-day
If Not nonWorkingDays.Contains(d.DayOfWeek) Then
lastWorkingDay = d
workingDatesBetween.Add(d)
End If
End While
Next
Return workingDatesBetween.ToArray()
End Function
Now you get the String() from the Date() via Array.ConvertAll:
Dim allNonWorkingDates = GetLastWorkingDatesInMonthBetween(DateTime.Today.AddYears(-1), DateTime.Today)
Dim result As String() = Array.ConvertAll(allNonWorkingDates, Function(d) d.ToString("dd-MM-yyy", CultureInfo.InvariantCulture))
Result with the sample year above:
30-09-2013
31-10-2013
29-11-2013
31-12-2013
31-01-2014
28-02-2014
31-03-2014
30-04-2014
30-05-2014
30-06-2014
31-07-2014
29-08-2014
30-09-2014