Userform Timestamp Cell When textbox Update - excel

I have a userform where I have two textboxes txtCurrentStatus and txtDCPreviousStatus.
I want the information in txtCurrentStatus to display in txtDCPreviousStatus only if 15 days or more has passed since the last time txtCurrentStatus was last updated.
I have the following code:
Sub txtDCCurrentStatus_AfterUpdate()
dim oldDate as Date
dim timeStamp as String
dim numOfDaysSinceLastUpdated as Integer
'currentRow is the current row that is being updated from another part of my code
oldDate = CDate(Cells(currentRow, 219).Value)
timestamp = "Last updated on " & oldDate & Chr(13) & Chr(13)
numOfDaysSinceLastUpdated = DateDiff("d", oldDate, Date) 'Date is today
If (numOfDaysSinceLastUpdated > 15) Then
Me.txtDCPreviousStatus.Value = timestamp & Me.txtDCPreviousStatus.Value
Me.txtDCPreviousStatus.Value = Me.txtDCCurrentStatus.Value & _
Chr(13) & Me.txtDCPreviousStatus.Value
Else
'Do nothing
End If
Cells(currentRow, 219).Value = Format(Now, "mm-dd-yyyy")
End Sub
I am having two issues:
1) If the Cells(currentRow, 219).Value = ""because txtDCCurrentStatus was never updated then I get a message
Run-Time error '6': Overflow
at numOfDaysSinceLastUpdated = DateDiff("d", oldDate, Date)
2) To solved this issue, I moved Cells(currentRow, 219).Value = Format(Now, "mm-dd-yyyy") at the very top. When I do that the text never move to txtDCPreviousStatus because the date changes automatically to today the moment I make a change so it's never more than 15 days.
Basically, I am:
Putting into oldDate, the date txtDCCurrentStatus was last updated
Checking if 15 days has passed since txtDCCurrentStatus was updated by doing oldDate - Date then putting it in numOfDaysSinceLastUpdated
If yes, I then move the content of txtDCCurrentStatus to txtDCPreviousStatus
Save now the new Date that txtDCCurrentStatus is being updated to Cells(currentRow, 219).Value = Format(Now, "mm-dd-yyyy")
Is there a way I can fix these two issues?

Thanks to #ScottHoltzman answer. I fixed my issue
I added the following at the beginning of my Sub and it worked
if oldDate = Empty then
Me.txtDCPreviousStatus.Value = ""
Else ...
The issue I was having was that at first I was checking if oldDate = "". Even if oldDate is empty, oldDate is a Date and when it's empty, it print as "12:00:00 AM" and "" <> "12:00:00 AM" so I had to check if it was empty

Related

InputBox data validation and end loop

I have some code to enter dates via InputBox (see below). The problem is that the Else doesn't work (ie. if the user enters something other than the format mm/dd/yy it doesn't stop). How do I make it so that the user has to enter it in the format presented?
Also, I want to end the loop with the endDate. Right now, if you enter 01/10/20 as the start date and 12/31/20 as the end date, it will stop at January 1, 2021. How do I make it stop at December 31, 2020?
Dim startDate As Date
Dim endDate As Date
startDate = InputBox("Enter project start date in format mm/dd/yy", "User date", Format(Now(), "dd/mm/yy"))
endDate = InputBox("Enter project end date in format mm/dd/yy", "User date", Format(Now(), "dd/mm/yy"))
If IsDate(startDate) Then
startDate = Format(CDate(startDate), "mmm d, yyyy")
Else
MsgBox "Wrong date format"
End If
If IsDate(endDate) Then
endDate = Format(CDate(endDate), "mmm d, yyyy")
Else
MsgBox "Wrong date format"
End If
Range("A2").Value = startDate
Dim i As Long
Dim j As Long
Dim x As Integer
i = startDate
j = endDate
x = 3
Do Until i >= j
Cells(x, 1).Value = i + 7
i = i + 7
x = x + 1
Loop
End Sub```
When the date is incorrect it is displaying the message box but there is nothing to stop it from continuing through the code after the message box is closed. There are several ways you can fix this.
The first is to simply add the line
Exit Sub
after the msgbox is displayed.
However, I'm assuming you don't want the program to just give up after an incorrect input. Instead you likely want it to inform the user it was incorrect and ask for another input until they provide a valid format.
The way I typically do this is with a Do Until loop. Try this:
startDate = InputBox("Enter project start date in format mm/dd/yy", "User date",
Format(Now(), "dd/mm/yy"))
Do Until IsDate(startDate)
MsgBox "Wrong date format."
startDate = InputBox("Enter project start date in format mm/dd/yy", "User date",
Loop
endDate = InputBox("Enter project end date in format mm/dd/yy", "User date",
Format(Now(), "dd/mm/yy"))
Do Until IsDate(endDate)
MsgBox "Wrong date format."
startDate = InputBox("Enter project start date in format mm/dd/yy", "User date",
Loop
The second problem is because you are checking i's value before you add 7 to it. Plus, the endDate only gets printed if it happens to be a multiple of 7 away from the startDate. If you always want the end date to print you'll have to make some changes.
Try this instead:
i = startDate + 7
j = endDate
x = 3
Do Until i > j
Cells(x, 1).Value = i
i = i + 7
x = x + 1
Loop
Cells(x, 1).Value = endDate

How to get yyyy-mm-dd from mm/dd/yyy in Excel

I have a date in mm/dd/yyyy format from a textbox in a userform, and I want to get each value mm, dd, and yyyy to a number. (Example, 10/12/2020 would become 2020-10-12)
Here is my code so far, which gets the month part finished.
Dim DateDay As String, DateMonth As String, DateYear As String
Dim firstslash As Integer, secondslash As Integer
firstslash = InStr(TextBox3, "/")
DateMonth = Left(TextBox3, firstslash - 1)
If Len(DateMonth) = 1 Then
DateMonth = "0" & DateMonth
End If
'code for day and year
MsgBox("Date =" & DateYear & "-" & DateMonth & "-" & DateDay)
How can I add to this so it can get the day and year part as well?
The trick is to be sure that you are dealing with a date, that is a large integer (43861 for today). Therefore you should convert whatever is in the textbox to an integer representing a date. That true date you can then present in any format you want. The code below does exactly that.
Private Sub CallExtractDate()
Dim Dat As Date
Dat = ExtractDate("01/31/2020")
MsgBox Format(Dat, "yyyy-mm-dd") & vbCr & _
Format(Dat, "ddd, dd mmm, yyyy") & vbCr & _
Format(Dat, "dddd")
End Sub
Function ExtractDate(ByVal TxtDate As String) As Date
Dim Sp() As String
If IsDate(TxtDate) Then
ExtractDate = CDate(TxtDate)
Else
Sp = Split(TxtDate, "/")
On Error Resume Next
ExtractDate = DateSerial(Int(Sp(2)), Int(Sp(0)), Int(Sp(1)))
End If
End Function
In your project you would probably use the function with a call like this.
Dat = ExtractDate(TextBox1.Value)

How to rollover WeekNum If year does not start on a specific weekday (Like Friday)

I am currently working on a excel macro which generates Weekly data. I have to prepare multiple reports where my Week starting day is different e.g. if for one report my week start day is "Friday" whereas for other report the week start day is "Monday"
Right now, I am doing this in multiple steps:
First I am getting all data from source excel and adding a formula to get all records in a particular week. I have considered "Friday" as my first day of week.
I arranged the records in descending order and get the unique value for each AZ column. This way I got the last record from each week, which is what I was looking.
Code I am using for this is as follows:
Range("Data").AdvancedFilter _
Action:=xlFilterCopy, _
CriteriaRange:=Range("$A$1:$A$2"), _
CopyToRange:=Range("$BB$4:$BD$4")
FilterDataLastRow = Cells.Find(What:="*", _
After:=Range("BA999999"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
' Sort data in descending order of date
' Range("WeeklyFilteredData").Sort Key1:=Range("$BB$4:$BB$999999"), Order1:=xlDescending, Header:=xlYes
Range("AW4:BE999999").Sort Key1:=Range("BC4:BC999999"), order1:=xlDescending, Header:=xlYes
' Assign Unique Key for each record row. We are using RowNum for same
Range("BA5:BA" & FilterDataLastRow).Formula = "=ROW(RC[-2])"
' Assign SearchKey to filter Out all the data belonging to same week
Range("AZ5:AZ" & FilterDataLastRow).Formula = "=(TEXT(RC[3],""yyyy""))&(TEXT(WEEKNUM(RC[3],15),""00""))"
' Get all data in User View
Range("A5:A" & FilterDataLastRow).Formula = "=VLOOKUP(RC[51],C[51]:C[55],2,FALSE)"
Range("B5:B" & FilterDataLastRow).Formula = "=VLOOKUP(RC[50],C[50]:C[54],3,FALSE)"
Range("C5:C" & FilterDataLastRow).Formula = "=VLOOKUP(RC[49],C[49]:C[53],4,FALSE)"
Range("E5:E" & FilterDataLastRow).Formula = "=VLOOKUP(RC[47],C[47]:C[51],5,FALSE)"
Cells.RemoveDuplicates Columns:=Array(1)
This was working perfectly fine till WEEKNUM 53. January 2020 started on Wednesday and this was considered a WEEKNUM "1" which is not correct for my report.
Currently I am getting my Output as shown below:
I need to modify my Code to skip data for 12/31/2019 (Highlighted in red) as this data will be calculated as part of week which is ending on 01/02/2020.
Please suggest a better way to update my code to enter code here
[Update: 07 January 2020] ANSWER
I figured out a way to achieve my end result. But I know there is still better way to do same thing and hence I am keeping this question open for better approach.
Here is what I did:
1. Retrieve MONTH, DAY and WEEKDAY from given date
Range("AW5:AW" & FilterDataLastRow).Formula = "=MONTH(RC[6])"
Range("AX5:AX" & FilterDataLastRow).Formula = "=DAY(RC[5])"
Range("AY5:AY" & FilterDataLastRow).Formula = "=WEEKDAY(RC[4],16)"
Now added a for loop. I tried to explain each of my step in comments inside code.
For i = 5 To FilterDataLastRow
' Check for records with Month = 1 And DAY is 1-6 and WEEKDAY < 6
If Range("AW" & i).Value = 1 And Range("AX" & i).Value < 7 Then
CurrYear = Year(Range("BC" & i).Value)
PrevYear = CurrYear - 1
PrevYearLastDay = "12/31/" & PrevYear
Range("AV" & i).Value = PrevYearLastDay
'Get the Day of Weel on 31st December of Previous Year
Range("AU" & i).Value = "=WEEKDAY(RC[1],16)"
'Calculate Number of Days remaining for new week to start
DaysRemForNewWeek = 8 - Range("AU" & i).Value
'Calculate Date of First Friday of Current Year
Range("AT" & i).Value = PrevYearLastDay + DaysRemForNewWeek
'Compare all the dates prior to first Friday and rollover WeekNum from last year for these dates
If Range("BC" & i).Value < Range("AT" & i).Value Then
Range("AZ" & i).Formula = "=(TEXT(RC[-4],""yyyy""))&(TEXT(WEEKNUM(RC[-4],16),""00""))"
Else
Range("AZ" & i).Formula = "=(TEXT(RC[3],""yyyy""))&(TEXT(WEEKNUM(RC[3],16),""00""))"
End If
Else
Range("AZ" & i).Formula = "=(TEXT(RC[3],""yyyy""))&(TEXT(WEEKNUM(RC[3],16),""00""))"
End If
Next i
What is your definition of a Week given a particular start day?
If it is the first full week of the year starting with that date, then you can derive it more easily from the VBA DatePart function, e.g
DatePart("ww", myDate, vbFriday, vbFirstFullWeek)
If you need to have this as a function or part of a formula on your worksheet, use it as a UDF instead of the worksheet WEEEKNUM function which is not as flexible. Or, better yet, construct the year/wknum string in VBA using the vba Format function and write that string to the worksheet.
For example:
Function yrWkNum(dt As Date) As String
yrWkNum = Year(dt) & Format(DatePart("ww", dt, vbFriday, vbFirstFullWeek), "00")
End Function
I figured out a way to achieve my end result. But I know there is still better way to do same thing and hence I am keeping this question open for better approach.
Here is what I did:
1. Retrieve MONTH, DAY and WEEKDAY from given date
Range("AW5:AW" & FilterDataLastRow).Formula = "=MONTH(RC[6])"
Range("AX5:AX" & FilterDataLastRow).Formula = "=DAY(RC[5])"
Range("AY5:AY" & FilterDataLastRow).Formula = "=WEEKDAY(RC[4],16)"
Now added a for loop. I tried to explain each of my step in comments inside code.
For i = 5 To FilterDataLastRow
' Check for records with Month = 1 And DAY is 1-6 and WEEKDAY < 6
If Range("AW" & i).Value = 1 And Range("AX" & i).Value < 7 Then
CurrYear = Year(Range("BC" & i).Value)
PrevYear = CurrYear - 1
PrevYearLastDay = "12/31/" & PrevYear
Range("AV" & i).Value = PrevYearLastDay
'Get the Day of Weel on 31st December of Previous Year
Range("AU" & i).Value = "=WEEKDAY(RC[1],16)"
'Calculate Number of Days remaining for new week to start
DaysRemForNewWeek = 8 - Range("AU" & i).Value
'Calculate Date of First Friday of Current Year
Range("AT" & i).Value = PrevYearLastDay + DaysRemForNewWeek
'Compare all the dates prior to first Friday and rollover WeekNum from last year for these dates
If Range("BC" & i).Value < Range("AT" & i).Value Then
Range("AZ" & i).Formula = "=(TEXT(RC[-4],""yyyy""))&(TEXT(WEEKNUM(RC[-4],16),""00""))"
Else
Range("AZ" & i).Formula = "=(TEXT(RC[3],""yyyy""))&(TEXT(WEEKNUM(RC[3],16),""00""))"
End If
Else
Range("AZ" & i).Formula = "=(TEXT(RC[3],""yyyy""))&(TEXT(WEEKNUM(RC[3],16),""00""))"
End If
Next i

Day(Now) displaying January, 1900?

For whatever reason, everytime I try to use the Day(Now) function in VBA, it keeps displaying "1/9/1900". The Date function displays correctly, so I'm not sure what the issue here is.
Sub Test()
Dim datDay As Date
datDay = Day(Now)
MsgBox datDay
End Sub
Here's an image of the error.
The Day will be an integer somewhere between 1 and 31, depending on, well, the "day" part of the date returned by the DateTime.Now function.
The way dates are stored, they're essentially Double values, with the integer part being a number of days, and the decimal part being the time of day.
Debug.Print Format(CDate(0), "yyyy-mm-dd")
Output: 1899-12-30
We are June 10th, so the date value of 10 corresponds to January 9, 1900.
You want to store the value returned by Day, Month, and Year functions, into Long integer variables; not Date.
Dim datDay As Long
datDay = DateTime.Day(DateTime.Date) ' datDay is 10 because DateTime.Date is 2019-06-10.
Note: while unqualified Day, Date, Month, and Year (and others) functions work perfectly fine, it's probably a good idea to qualify them with the module they are declared in (VBA.DateTime), to avoid potentially confusing ambiguities, e.g. Date is both the name of a property of the DateTime module, and it's also a data type (Dim foo As Date), and the two have very different meanings.
Try:
Option Explicit
Sub Test()
Dim datDay As Date
datDay = Date
MsgBox "Whole date: " & datDay & vbNewLine & _
"Month: " & Month(Date) & " (" & Format(Date, "mmmm") & ")" & vbNewLine & _
"Day: " & Day(Date) & " (" & Format(Date, "dddd") & ")"
End Sub
Result:
Replace
datDay = Day(Now)
with
datDay = Day(Now())
Not sure if this will fix the problem, but =Day(Now()) works correctly when typed directly into a cell.
Your problem is datDay is typed as a Date. =Day(Now()) returns just 10, as today is June 10th. As a full Date value, this is 1/10/1900, since Excel indexes day 0 as 1/0/1900.

How to get current month?

I can't get the current month.
It seems very simple to get the current year and day, as tested with the following:
MsgBox Year(Date)
MsgBox Day(Date)
MsgBox Year(Now)
MsgBox Day(Now)
How is it possible to show the current month as either a number (1, 2 etc.) or a full name?
I could use TODAY() in a cell and convert that in VBA with something like CurrentMonth = MonthName(Month(Sheet1.Range("A1"))) but I would like to do this directly in VBA for Excel.
Try,
debug.print Format(Date, "mmm") 'Mar
debug.print Format(Date, "mmmm") 'March
debug.print Format(Date, "m") '3
debug.print Format(Date, "mm") '03
Month(Now)
Returns the index number associated with the current month.
Jeeped's code below is the most compact, but to give you an idea of how indexes work, the following code will return the month name based on the index returned:
Dim months(11) As String
months(0) = "Jan"
months(1) = "Feb"
months(2) = "Mar"
months(3) = "Apr"
months(4) = "May"
months(5) = "Jun"
months(6) = "Jul"
months(7) = "Aug"
months(8) = "Sep"
months(9) = "Oct"
months(10) = "Nov"
months(11) = "Dec"
Dim nowMonth As Integer
nowMonth = Month(Now)
For i = 0 To 11
If nowMonth = (i + 1) Then
MsgBox (months(i))
End If
Next
Found an easier solution to get the current Month Name
Just use MonthName(Month(Now)) and assign it to a string.
Month(Now) gives you the month number and MonthName() uses that number to display the current month
A really helpful and simple way is to combine the format function together with date.
Examples (assuming today is Oct 23, 2019):
To get current month as a number as in original question:
MsgBox Format(Date, "mm")
^ Will return: 10
To get current month as short text:
MsgBox Format(Date, "mmm")
^ Will return: Oct
To get current month with full text:
MsgBox Format(Date, "mmmm")
^ Will return: October
You can combine these with days and years as well.
Additional examples:
MsgBox Format(Date, "dd-mmm-yyyy")
^ Will return 23-Oct-2019
MsgBox Format(Date, "dddd-mmmm-dd-yyyy")
^ Will return: Wednesday-October-23-2019
This is creating a custom format, so you can rearrange the dd, mm, yyyy areas as you see fit, such as:
MsgBox Format(Date, "yyyy/mm/dd")
^ Will return: 2019/23/10
Here is the best way I have found to do it:
Sub getMonth()
'MsgBox DatePart("m", Date)
'MsgBox Evaluate("MONTH(""" & Date & """)")
'MsgBox VBA.DateTime.Month(Date)
MsgBox Format(Date, "mmmm")
End Sub
Below is how I found the previous month based on the current month name, the assignment to monthNum is the piece needed to solve your question.
month = "February"
'****'
monthNum = Application.Evaluate("=MONTH(1&" & Chr(34) & month & Chr(34) & ")") 'Returns month #
'****'
If monthNum = 1 Then
monthNum = 12
Else
monthNum = monthNum - 1
End If
month = MonthName(monthNum) 'Returns January

Resources