I like to know if it is possible to make a vba code to find the week number of a date with these conditions:
Friday is the first day of the week
If the week consists of two months, (for example: May 27,2016 to June 2,2016), the week number will be determined by the number of days in each month. In this case, the number of days in the may part of the week is greater so the week number is equal to 5.
I tried to make a solution in a spreadsheet but I can't seem to figure out how to convert it all into vba code. If anyone has an idea to how this could be done, it is greatly appreciated.
Here is my attempt on the solution:
spreadsheet (green for input) (blue for output)
spreadsheet with formulas
here's a not so elegant solution
Option Explicit
Sub main2()
Dim cell As Range
Dim date1 As Date, date2 As Date
Dim weeks1 As Long, weeks2 As Long
With Worksheets("weeks")
For Each cell In .Range("B2:B" & .Cells(.Rows.Count, "B").End(xlUp).Row)
date1 = cell.Value
date2 = cell.Offset(, 1).Value
weeks1 = DateDiff("ww", date1, "01/01/1900", vbFriday)
weeks2 = DateDiff("ww", dateadd("d", -Day(date1), date1), "01/01/1900", vbFriday)
If DatePart("m", date1) <> DatePart("m", date2) Then
If DateDiff("d", date1, dateadd("d", -Day(date2), date2)) >= 3 Then
If IsDate(cell.Offset(-1)) Then
cell.Offset(, 8) = cell.Offset(-1, 8) + 1
Else
cell.Offset(, 8) = weeks2 - weeks1
End If
Else
cell.Offset(, 8) = 1
End If
Else
If IsDate(cell.Offset(-1)) Then
cell.Offset(, 8) = IIf(cell.Offset(-1, 8) > 3, 1, cell.Offset(-1, 8) + 1)
Else
cell.Offset(, 8) = weeks2 - weeks1
End If
End If
Next cell
End With
End Sub
There is probably a better algorithm, but here is a UDF that, given any date, will return the Weeknumber of that date according to your specifications (if I have understood them correctly).
You can adapt to your specific requirements as necessary
Option Explicit
Function wnMonth(DT As Date)
Dim dtFF As Date
Dim dtLF As Date
Dim lWN As Long
'First and Last Fridays of current month
dtFF = DT + 8 - Day(DT) - Weekday(DT - Day(DT) + 8 - 6)
dtLF = dtFF + 28 + 7 * (Day(dtFF + 28) < Day(dtFF + 21))
If DT >= dtFF And DT < dtLF Then
lWN = Int((DT - dtFF) / 7) + 1
If Day(dtFF) > 4 Then
lWN = lWN + 1
End If
Else
If DT < dtFF Then
If Day(dtFF) > 4 Then
lWN = 1
Else
'First Friday prior month
dtFF = DateAdd("m", -1, dtFF)
dtFF = dtFF + 8 - Day(dtFF) - Weekday(dtFF - Day(dtFF) + 8 - 6)
'Last Friday prior month
dtLF = dtFF + 28 + 7 * (Day(dtFF + 28) < Day(dtFF + 21))
'First Friday weeknumber
If Day(dtFF) > 4 Then
lWN = 2
Else
lWN = 1
End If
'Last Friday weeknumber = DT weeknumber
lWN = lWN + (dtLF - dtFF) / 7
End If
Else 'DT > dtLF
'days left in the month
If (8 - Day(dtLF + 7)) < 4 Then
lWN = 1
Else
lWN = (dtLF - dtFF) / 7 + IIf(Day(dtFF) > 4, 2, 1)
End If
End If
End If
wnMonth = lWN
End Function
Related
I would like to find the last date of quarter using the date on input and show all quarter for 10 years like this :
My code is :
Sub Trimestre()
If Month(Sheets("Paramétrage").Cells(3, 3).Value) >= 1 And
Month(Sheets("Paramétrage").Cells(3,
3).Value) <= 3 Then
Sheets("Paramétrage").Cells(12, 1).Value = "T1"
End If
If Month(Sheets("Paramétrage").Cells(3, 3).Value) > 3 And
Month(Sheets("Paramétrage").Cells(3,
3).Value) <= 6 Then
Sheets("Paramétrage").Cells(12, 1).Value = "T2"
End If
If Month(Sheets("Paramétrage").Cells(3, 3).Value) > 6 And Month(Sheets("Paramétrage").Cells(3,
3).Value) <= 9 Then
Sheets("Paramétrage").Cells(12, 1).Value = "T3"
End If
If Month(Sheets("Paramétrage").Cells(3, 3).Value) > 9 And Month(Sheets("Paramétrage").Cells(3,
3).Value) <= 12 Then
Sheets("Paramétrage").Cells(12, 1).Value = "T4"
End If
End Sub
I can find the first T (Quarter) but I have no idea how to find the last date of the quarter and show all the date after. Thank you for your precious help.
Loops are not needed. Two one-liners will do for the quarter date calculations:
DateThisQuarterFirst = DateAdd("q", -1, DateSerial(Year(Date), DatePart("q", Date) * 3 + 1, 1))
DateThisQuarterLast = DateAdd("q", 0, DateSerial(Year(Date), DatePart("q", Date) * 3 + 1, 0))
This function will always give you the last day of the quarter of whatever date you feed into it.
Function LastDayOfQuarter(refDate As Date) As Date
Dim DT As Date
DT = WorksheetFunction.EoMonth(refDate, 0)
Do Until Month(DT) Mod 3 = 0
DT = Application.WorksheetFunction.EoMonth(DT, 1)
Loop
LastDayOfQuarter = DT
End Function
And here is the code edited to just return the quarter:
Function Quarter(refDate As Date) As String
Dim DT As Date
DT = WorksheetFunction.EoMonth(refDate, 0)
Do Until Month(DT) Mod 3 = 0
DT = Application.WorksheetFunction.EoMonth(DT, 1)
Loop
Quarter = "Q" & Month(DT) / 3
End Function
If you star with the function `LastDayOfQuarter in cell B5, then each cell below "=eomonth(B5,3)" then you will get a list of the next number of end dates of quarters.
You can then use the "Quarter" Function to return which quarters those are.
Example:
I need to automatically calculate a Start Date (aka QRT_START) which is 5 years of Quarters back. A Quarter is 3 months. For example, there are 4 Quarters in a Year: March 31st, June 30th, September 30th and December 31st.
Since we are currently in November 16th 2022, the Start Date would be December 31st 2017. So depending on whatever the current date is, the Start Date needs to go back 5 years worth of Quarters.
I also need to automatically calculate the most recent End Date (aka QRT_END). So since, we are in November 16th 2022, the End Date would be the previous quarter end before today which is September 30th 2022. I have the VBA code written below, please help me fix.
Private Function getQRT_END() As String
Dim endmonth As Variant
Dim endyear As Variant
Dim Day As Variant
endmonth = Month(Date) - 1
If endmonth = 0 Then
endyear = Year(Date) - 1
endmonth = 12
day = 31
Else
endyear = Year(Date)
If endmonth = 3 Then
day = 31
Else
day = 30
End if
endmonth = “0” & endmonth
End If
getQRT_END = endyear & endmonth & day
End Function
Private Function getQRT_START() As String
Dim startmonth As Variant
Dim startyear As Variant
Dim Day As Variant
startyear = Year(Date) - 5
startmonth = Month(Date) + 2
If startmonth <10 Then
If startmonth = 3 Then
day = 31
Else
day = 30
End if
startmonth = “0” & startmonth
Else
day = 30
End If
getQRT_START = startyear & startmonth & day
End Function
Function GetQuartal(years, data)
d = DateAdd("yyyy", years, data)
q = (Month(d) + 2) \ 3
qstart = DateSerial(Year(d), (q - 1) * 3 + 1, 1)
qend = DateSerial(Year(d), q * 3 + 1, 1) - 1
GetQuartal = Array(data, d, qstart, qend)
End Function
Sub test()
Debug.Print "Date", "Date-5Y", "QY-5 Start", "QY-5 End"
For Each d In Array(Date, #2/29/2000#, #12/1/2021#, #5/5/1992#)
q = GetQuartal(-5, d)
Debug.Print q(0), q(1), q(2), q(3)
Next
End Sub
Date Date-5Y QY-5 Start QY-5 End
17.11.2022 17.11.2017 01.10.2017 31.12.2017
29.02.2000 28.02.1995 01.01.1995 31.03.1995
01.12.2021 01.12.2016 01.10.2016 31.12.2016
05.05.1992 05.05.1987 01.04.1987 30.06.1987
You can use two functions found in my library at GitHub: VBA.Date.
? DateThisQuarterUltimo(DateAdd("yyyy", -5, Date))
2017-12-31
? DatePreviousQuarterUltimo(DateAdd("yyyy", -5, Date))
2017-09-30
They are simple high-level functions:
' Returns the ultimo date of the quarter of the date passed.
'
' 2016-01-13. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function DateThisQuarterUltimo( _
ByVal DateThisQuarter As Date) _
As Date
Dim Interval As String
Dim Number As Double
Dim ResultDate As Date
Number = 0
Interval = IntervalSetting(DtInterval.dtQuarter)
ResultDate = DateIntervalUltimo(Interval, Number, DateThisQuarter)
DateThisQuarterUltimo = ResultDate
End Function
' Returns the ultimo date of the quarter preceding the quarter of the date passed.
'
' 2016-01-13. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function DatePreviousQuarterUltimo( _
ByVal DateThisQuarter As Date) _
As Date
Dim Interval As String
Dim Number As Double
Dim ResultDate As Date
Number = -1
Interval = IntervalSetting(DtInterval.dtQuarter)
ResultDate = DateIntervalUltimo(Interval, Number, DateThisQuarter)
DatePreviousQuarterUltimo = ResultDate
End Function
Haven't tested it completely
You can use DateSerial, DateAdd and DatePart to achieve what you want...
Option Explicit
Sub Sample()
Dim D As Date
Dim prevD As Date
D = DateSerial(2022, 11, 16)
'~~> Date 5 years years ago
prevD = DateAdd("q", -(4 * 5), D)
'~~> Last date of the quarter for a specific date
Debug.Print DateAdd("q", DatePart("q", prevD), DateSerial(Year(prevD), 1, 1)) - 1
'OUTPUT : 31-12-2017
'~~> Last date of previous quarter for a specific date
Debug.Print DateAdd("q", DatePart("q", D) - 1, DateSerial(Year(D), 1, 1)) - 1
'OUTPUT : 30-09-2022
End Sub
Changed string part to a proper date.
This question already has answers here:
I use OR to form a multiple condition IF ELSE statement on VBA, it's not working
(2 answers)
How to use OR in if statement in VBA [duplicate]
(1 answer)
Closed 1 year ago.
I'm new to VBA and trying to write something that will fill in a column with the appropriate dates for the first of the month date entered in cell D3. Ex. If 5/1/2021 is entered in D3, the dates 5/1/2021 - 5/31/2021 will be outputted into the B column starting from row 5. For some reason, even though the month of the entered date is correctly read (for ex. 5 from 5/1/2021) I get the wrong days back. For 5/1/2021 I get 30 days. This is despite the fact that 5 is not equal to any of the numbers in the if statement for the months with 30 days. It seems whichever statement is first in line is completed. When I was using simple Ifs instead of If/Else statements, the whole thing ran despite the logical statement being False in certain cases. I don't know much about this language so I'm hoping it's a simple syntax fix. Why is this happening and how can I fix it so that the logical statements are read correctly? My code is below. Thank you so much!
VBA Code:
Sub FillDays()
Dim row As Double
row = 0
Dim startdate As Date
Dim enddate As Date
startdate = Range("D3").Value
If Month(startdate) = 4 Or 6 Or 9 Or 11 Then
enddate = DateAdd("d", 29, startdate)
' 30 days
Range("B6").Select
Do Until DateAdd("d", 1, startdate) = enddate + 1
Activecell.Offset(row, 0).Value = DateAdd("d", 1, startdate)
startdate = startdate + 1
row = row + 1
Loop
ElseIf Month(startdate) = 2 Then
enddate = DateAdd("d", 27, startdate)
' 28 days
Range("B6").Select
Do Until DateAdd("d", 1, startdate) = enddate + 1
Activecell.Offset(row, 0).Value = DateAdd("d", 1, startdate)
startdate = startdate + 1
row = row + 1
Loop
ElseIf Month(startdate) = 2 And isLeapYear(Year(startdate)) = True Then
enddate = DateAdd("d", 28, startdate)
' 29 days
Range("B6").Select
Do Until DateAdd("d", 1, startdate) = enddate + 1
Activecell.Offset(row, 0).Value = DateAdd("d", 1, startdate)
startdate = startdate + 1
row = row + 1
Loop
ElseIf Month(startdate) = 1 Or 3 Or 5 Or 7 Or 8 Or 10 Or 12 Then
enddate = DateAdd("d", 30, startdate)
' 31 days
Range("B6").Select
Do Until DateAdd("d", 1, startdate) = enddate + 1
Activecell.Offset(row, 0).Value = DateAdd("d", 1, startdate)
startdate = startdate + 1
row = row + 1
Loop
End If
End Sub
I have a column of date time. I have to remove the date part. I simple want to run a macro that will do that. WHen I record macro, do the delete and then stop, then run it on the next row, it gives the value below. How does one globalize so I can run on all rows this task?
2017-06-26 14:41:00
the macro is this:
Sub Macro9()
'
' Macro9 Macro
'
'
ActiveCell.FormulaR1C1 = "2:41:00 PM"
ActiveCell.Offset(1, 0).Range("A1").Select
End Sub
Here is a simple macro to accomplish what you are looking to do. I assumed that you wanted to convert from military time to AM/PM. You will have to adjust the locations of cells to fit your spreadsheet. This is just going through all of the values in column A and turning them into just AM/PM time and spitting them out in coulmn B. Instead of looping through all of the rows you could also define your own single input function with the same logic.
Sub test()
Dim dt As String
Dim tm As String
Dim hr As String
Dim row_ct As Integer
row_ct = Range("A1").End(xlDown).Row
For i = 1 To row_ct
dt = Cells(i, 1)
tm = Right(Cells(i, 1), 8)
hr = Left(tm, 2)
If hr < 12 Then
tm = tm & " AM"
ElseIf hr = 12 Then tm = tm & " PM"
ElseIf hr > 12 and hr - 12 < 10 then tm = 0 & (Left(tm, 2) - 12) & Right(tm, 6) & " PM"
Else: tm = left(tm, 2) - 12 & right(tm, 6) & " PM"
End If
Cells(i, 2) = tm
Next i
End Sub
Here is how you can make a custom function that handles this:
Function tm(date_time)
If Left(Right(date_time, 8), 2) < 12 Then
tm = Right(date_time, 8) & " AM"
ElseIf Left(Right(date_time, 8), 2) = 12 Then tm = Right(date_time, 8) & " PM"
ElseIf Left(Right(date_time, 8), 2) > 12 Then tm = Left(Right(date_time, 8), 2)- 12 & Right(date_time, 6) & " PM"`
End If
End Function
Depending on the application, one will probably work better than the other.
I'm trying to calculate the time elapsed with the total amount of months, Days and Hours together using Datediff function. Is it not possible?
DateDiff("d hh", datein, Now)
What can I do?
That's not possible as the interval parameter can only be a single string.
You will have to do a bit more work like get the difference in hours and if it's above 24 convert the part before decimal separator into days
Sub Main()
Dim d1 As Date
d1 = "15/10/2014 08:00:03"
Dim d2 As Date
d2 = Now
Dim hrsDiff As Long
hrsDiff = DateDiff("h", d1, d2)
MsgBox IIf(hrsDiff >= 24, _
hrsDiff \ 24 & " days " & hrsDiff Mod 24 & " hours", _
hrsDiff & " hours")
End Sub
This is rough and ready, but is just directional. You could make a user defined function. This one returns 1:2:22:15 as a string (but you could return a custom class instance with variables for months, days, hours, minutes). It doesn't account for date2 being before date1 (not sure what happens then), nor does it account for date1 only being a partial day (assumes date1 is midnight).
Function MyDateDiff(date1 As Date, date2 As Date) As String
Dim intMonths As Integer
Dim datStartOfLastMonth As Date
Dim datStartOfLastHour As Date
Dim datEndOfMonth As Date
Dim intDays As Integer
Dim intHours As Integer
Dim intMinutes As Integer
Dim strResult As String
' Strip of any time
datStartOfLastMonth = DateSerial(Year(date2), Month(date2), Day(date2))
' check the dates arent in the same month
If Not ((Month(date1) = Month(date2) And Year(date1) = Year(date2))) Then
' how many months are there
intMonths = DateDiff("m", date1, date2)
Debug.Print (intMonths)
' how many days difference are there
intDays = DateDiff("d", DateAdd("m", intMonths, date1), date2)
Debug.Print (intDays)
' how many hours difference are there
intHours = DateDiff("h", datStartOfLastMonth, date2)
Debug.Print (intHours)
' how many minutes different are there
datStartOfLastHour = datStartOfLastMonth + (DatePart("h", date2) / 24)
intMinutes = DateDiff("n", datStartOfLastHour, date2)
Debug.Print (intMinutes)
Else
' Dates are in the same month
intMonths = 0
Debug.Print (intMonths)
' how many days difference are there
intDays = DateDiff("d", date1, date2)
Debug.Print (intDays)
' how many hours difference are there
intHours = DateDiff("h", datStartOfLastMonth, date2)
Debug.Print (intHours)
' how many minutes different are there
datStartOfLastHour = datStartOfLastMonth + (DatePart("h", date2) / 24)
intMinutes = DateDiff("n", datStartOfLastHour, date2)
Debug.Print (intMinutes)
End If
strResult = intMonths & ":" & intDays & ":" & intHours & ":" & intMinutes
MyDateDiff = strResult
End Function
Testing this:
?MyDateDiff("01-SEP-2014", "03-Oct-2014 22:15:33")
Gives:
1:2:22:15
i.e. 1 month, 2 days, 22 minutes and 15 seconds.
Reverse testing this by adding the components back onto date1 gives:
?DateAdd("n",15,DateAdd("h",22,DateAdd("d",2,DateAdd("m",1,"01-SEP-2014"))))
= "03-Oct-2014 22:15:33"
If we try with 2 dates in the same month:
?MyDateDiff("01-SEP-2014", "03-SEP-2014 22:15:33")
We get:
0:2:22:15
Reverse testing this:
?DateAdd("n",15,DateAdd("h",22,DateAdd("d",2,DateAdd("m",0,"01-SEP-2014"))))
Gives:
03/09/2014 22:15:00
But you may want to account for dates being the wrong way round...and you may only want date1 to be counted as a partial date if it starts later in the day....as I say, just a thought.
Regards
i
This may give you some ideas to correct for days in month or leap year Feb
Private Sub CommandButton1_Click()
DoDateA
End Sub
Sub DoDateA()
Dim D1 As Date, D2 As Date, DC As Date, DS As Date
Dim CA: CA = Array("", "yyyy", "m", "d", "h", "n", "s", "s")
Dim Va%(7), Da(7) As Date, Ci%
D1 = Now + Rnd() * 420 ' vary the * factors for range of dates
D2 = Now + Rnd() * 156
If D1 > D2 Then
[b4] = "Larger"
Else
[b4] = " smaller"
DS = D1
D1 = D2
D2 = DS
End If
[d4] = D1
[e4] = D2
DC = D2
For Ci = 1 To 6
Va(Ci) = DateDiff(CA(Ci), DC, D1)
DC = DateAdd(CA(Ci), Va(Ci), DC)
Va(Ci + 1) = DateDiff(CA(Ci + 1), DC, D1)
If Va(Ci + 1) < 0 Then ' added too much
Va(Ci) = Va(Ci) - 1
DC = DateAdd(CA(Ci), -1, DC)
Cells(9, Ci + 3) = Va(Ci + 1)
Cells(8, Ci + 3) = Format(DC, "yyyy:mm:dd hh:mm:ss")
End If
Da(Ci) = DC
Cells(5, Ci + 3) = CA(Ci)
Cells(6, Ci + 3) = Va(Ci)
Cells(7, Ci + 3) = Format(Da(Ci), "yyyy:mm:dd hh:mm:ss")
Cells(10, Ci + 3) = DateDiff(CA(Ci), D2, D1)
Next Ci
End Sub