Sorry if the title is confusing, not sure how to phrase this. Basically I want to write a formula which takes a date from a cell specified by the user (myDate) and the address of a starting cell (FirstCell) and based on that:
Sets a range (myArea) which starts from the specified cell and extends for a number of cells equal to the number of days in the month and year of the date we selected (numDays).
Counts cells in that range that do not have a blank interior (there is no conditional formatting in the sheet) and returns that number.
For example let's say I have the date 1/4/2022 and April in 2022 has 5 days but my table has 6 columns. I want to define a range based on the length of this specific month so that the number of cells which do not have a blank interior is equal to 1 and not 2. And I want this to be reproducible for different months.
The 2nd point is done and works with a simple user-specified range, the 1st point is the one giving me trouble because I don't want it to return anything in the sheet. The specific issue is setting numDays but there may be other errors I didn't catch - basically I tried to transplant the excel function solution to counting this into VBA but I'm pretty sure I'm getting the syntax wrong and/or this is not doable. Couldn't find anything that would answer my question on here, when I try to use the function it returns #VALUE! in the spreadsheet.
Function SPECIALDAYS (FirstCell as Range, myDate as Date)
Dim myCell as Range
Dim myArea as Range
Dim numDays as Integer
numDays = Application.Evaluate("Day(Eomonth(" & myDate & ",0))")
Set myArea = Range(FirstCell, FirstCell.Offset(0, numDays-1))
For Each myCell In myArea
If myCell.Interior.ColorIndex <> -4142 Then
SPECIALDAYS=SPECIALDAYS+1
End If
Next myCell
End Function
Instead of using Evaluate and formula use a pure VBA solution with WorksheetFunctions:
numDays = Day(Application.WorksheetFunction.EoMonth(Date, 0))
See WorksheetFunction.EoMonth method.
Related
I have a list of dates (work days, no weekends) on column A that is sorted in ascending order. The column has a string header that reads "Dates." The format of the column is m/d/yyyy. The first date is 5/28/1998 on cell A2 and the last date is 2/9/2023 on cell A6216.
I also have a date on cell AG275 with the date 6/28/1999. I need to find the row for that date on column A.
This code has no problem finding the date, as long as both column A and cell AG275 are formatted as General. In that case, cell AG275 reads as 36337 and the answer (variable a in the code below) returns 275, which indeed is the row where 36337 is.
Sub findDate()
Dim dateToFind
Dim arr()
Dim Rng As Range
dateToFind = Cells(275, "ag").Value
Set Rng = Worksheets("calcSheet").Range("A1:A6216")
arr = Rng
a = WorksheetFunction.Match(dateToFind, arr, 1)
End Sub
The problem is that if both column A and cell AG275 are formatted as short date then the code answer (variable a) is not 275 but 1530, which is 6/25/2004, which in General formatting is 38163 (the following date on cell A1531 is 6/28/2004).
I suppose I could add code to reformat everything before starting the code and work with numbers instead of dates, but I'd rather not do that and work with the given dates. How can I get the right answer if everything is formatted as short dates?
I suspect the problem may have to do with the way I am declaring the variables and the array, but I have tried multiple variations of declaring as Date, as Variant, as Long and as Integer to no avail. I also tried, as I read in other related posts,
dateToFind = CDate(Cells(275, "ag").Value)
and
dateToFind = CLng(CDate(Cells(275, "ag").Value))
but neither one helped me get any closer to a solution. Incidentally the behavior is the same for long dates (mm/dd/yyyy).
If anyone can chime in I would be very grateful. Many thanks
No mater the formatting if the content of the cells are dates and the content of the cell to find is a date, the following will work:
Sub findDate()
Dim dateToFind As Variant
Dim Rng As Range
dateToFind = Cells(275, "ag")
Set Rng = Worksheets("calcSheet").Range("A1:A6216")
a = Application.Match(CLng(dateToFind), Rng, 1)
End Sub
What I'm trying to do is create a journaling spreadsheet that records the time and date of the entry at the time its submitted from a UserForm then updates the calendar on a "Splash" worksheet to change the cell interior and font colors to show that a journal entry has been created for that specific day.
I have a module created to iterate through what has already been imported from older journal entries from earlier this year and I want to change the interior color and text color of a cell in the named ranges named after the months. In the image below, the month names are not in the named ranges, just the list of numbers.
Calendar View
Basically, I want to search the dates, select and change the color of the cell of those dates in the calendar (see above). I can make it through the first month named range just fine but when it becomes a new month, it gives me run-time error 91.
Sub updateCells()
Dim rCell As Range
Dim rRng As Range: Set rRng = Worksheets("Journals").Range("A2:A44")
Dim thisDate, thisMonth, thisDay
Dim thisMonthRange As Range
For Each rCell In rRng.Cells
thisDate = Split(rCell.Text, " ")(0)
thisMonth = MonthName(month(thisDate))
thisDay = day(thisDate)
Range(thisMonth).Find(what:=thisDay).Interior.ColorIndex = 10
Range(thisMonth).Find(what:=thisDay).Font.Color = vbWhite
Next rCell
I'm am relatively new to VBA so I don't understand what would be causing the run-time error.
This takes having named ranges that are the actual names of the month, e.g., "January" list of dates (1-31) are referenced by Range("January").
I would use the following code to highlight the "18" in my Range("May") for today's date (2022-05-18):
Sub markCurrentDate()
Cells.ClearFormats
Dim currentMonth As String
currentMonth = Format(Date, "mmmm")
Dim currentDay As Long
currentDay = Format(Date, "dd")
Dim foundDate As Range
Set foundDate = Range(currentMonth).Find(currentDay)
foundDate.Interior.ColorIndex = 27
End Sub
Since we can't tell what your source cell for the date you're referring is, based on the current post, I used Date rather than a reference to a cell. The reference can be updated through, similar to being able to use With foundDate to add multiple format changes.
I think the problem is likely to be that one of your named ranges does not cover the entire range of days. February surely doesn't, you're missing the 28th!
At any rate, as a consequence (and apparently only on the second turn (a Feb 28?)), you run into the Run-time error '91', because Range(thisMonth).Find(what:=thisDay) is resolving to Nothing instead of an expected Range object once you fail to find thisDay inside the named range.
Evidently, the code cannot execute Nothing.Interior.ColorIndex = 10.
If correct, your solution should be to double-check and fix the incorrect named ranges.
Incidentally, Range(thisMonth).Find(what:=thisDay) is also superfluous. For obvious reasons, each range simply starts at 1 and increments with 1. So we could simply use thisDay as the index. Instead of this:
Range(thisMonth).Find(what:=thisDay).Interior.ColorIndex = 10
Range(thisMonth).Find(what:=thisDay).Font.Color = vbWhite
Simply use this:
With Range(thisMonth).Cells(thisDay)
.Interior.ColorIndex = 10
.Font.Color = vbWhite
End With
Update: come to think of this, if you want to insist on using Range(thisMonth).Find(what:=thisDay), you should at the very least change the snippet to Range(thisMonth).Find(what:=thisDay, LookAt:=xlWhole).
Counterintuitively, Range.Find(...) seems to accept a partial match by default (xlPart) and it actually remembers the settings you used on your last find (in the same Excel 'session'). Also, it will not always start where you expect it to do (see further this documentation and this post: Using the .Find Function VBA - not returning the first value). E.g. a realistic error depending on your settings / active cell position might be that your code (and incidentally, also the code provided by Cyril) will change the formatting for a day 10, when in fact you were trying to change the formatting for a day 1.
I have no experience with VBA and would love some help. As the title indicates, I'm looking for a script that fills a certain number of blank cells within column G with the average of all nonblank cells within that same range (e.g. fill all blank cells in G16:G59 with the average of all nonblank cells within G16:G59).
To make things more complicated, I'd need to vary the range somewhat dynamically as I wouldn't be sure as to how many rows I'd need to apply this script to and a work colleague who would be using this script might not have any experience with VBA either... The easiest solution I can think of is to have another cell contain the name of the last row in the range, or something like this: "Fill all blank cells in G16:Gx with the average of all nonblank cells within G16:Gx, where x = the row name listed in cell G12". Within G12 I'd have a text that states the last row to define the range, e.g. cell G12 contains the text "G80", which makes the range within the script to read G16:G80.
I know I'm asking for a lot, so if you can even just provide guidance on the first bit, I'd be very grateful! Thank you in advance for your time.
I think we can all remember what it was like when we first started out with VBA coding – which is why I’m helping you here. Normally, you’d be lucky to get any assistance with your question without providing at least some code & a description where it wasn’t doing what you wanted. Using the Record Macro button is always a good place to start.
Assumptions made here are that the data is on “Sheet1” in your file, and that there is a value in the last cell in Column G in the range you’re interested in. If that isn’t the case, let me know and I’ll show an alternative method to find the last row.
I’ve added descriptions about what (most) code does in each case to help you understand what’s going on. Let me know how you go with it.
Option Explicit '<~~ get in the habit of putting this at the top of your code
Sub FillInBlanks()
Dim ws As Worksheet '<~~ declare all variables
Dim LastRow As Long '<~~ use Long not Integer
Dim cel As Range '<~~ use intuitive variable names
Dim avg As Double '<~~ Double used here if you want decimal places in the average
Set ws = Sheets("Sheet1") '<~~ be explicit with sheet references
'Best practice to determine last used row in column
LastRow = ws.Cells(Rows.Count, "G").End(xlUp).Row '<~~ finds the last used row in column G
'Calculate the average & assign it to the variable "avg"
avg = Application.Average(ws.Range("G16:G" & LastRow))
'Loop through each cell in the defined range - one at a time
For Each cel In ws.Range("G16:G" & LastRow)
If cel = "" Then '<~~ = If the cell is blank, then...
cel = avg '<~~ ...put the average value (avg) in the cell
cel.Font.Color = RGB(51, 102, 0) '<~~ change color to suit
End If
Next cel '<~~ go to the next cell in the range
End Sub
I have an attendance form. Cells A1-G1, which will get an X for present. I have it set up to calculate the attendance percentage. I also have it set up to change the calculation based on number of days eligible ie new person starts mid month, they would only have 15 days, changing the numerator. I have to manually change the range for the countif statement.
A1:G1 is the range
J1 is the area we'd put in for how many days eligible (this is for those who are not on the whole term)
I3 is the default days eligible (regular whole term)
=COUNTIF(A1:G1,"X")/(IF(J1="",I3,J1))
This works fine, I'd like to set it up to change the countif to only count the range that has white cells vs other colors. Change the A1:G1 to say C1:G1 if cells A1, B2 were red.
I can explain for if needed. I see lots on how to change the color of a cell, but not to act on a color in countif.
D
Excel doesn't have in-built functionality for handling colors within formulas. You need to use VBA. But a note of caution, the following UDF won't recalculate unless you manually refresh. The proposed solution also uses SUMPRODUCT instead of COUNTIF:
Public Function HasNoFill(ByVal rng As Range) As Boolean()
Dim temp() As Boolean
ReDim temp(1 To rng.Count)
Dim cell As Range
For Each cell In rng
Dim i As Long
i = i + 1
temp(i) = cell.Interior.Color = 16777215
Next
HasNoFill = temp
End Function
I can't seem to figure out how to overwrite my date with what's being displayed.
For example, what's being displayed is 06/01/2016, but the actual data behind that is showing 01/06/2016. The date I want to have in there is June 1, 2016 (which is what's displayed). Changing the format of the cell doesn't help, because I'm doing some formulas with the Dates later, so actually need to have Excel have the correct date.
I have about 10,000 of such dates, where the displayed date is exactly what I want to have, but the actual formula "reversed".
Essentially, I would love to just copy that column (or run a sub) that puts what the cell literally displays into the cell.
What sort of works is doing this, but it only works on those cells with "backwards" dates...essentially passing the day as a month, and month as a day.
=DATE(YEAR(C1),DAY(C1),MONTH(C1)).
It "falls apart" though when the date is actually correct and I don't want it to change:
So, what'd be best is to just literally overwrite the cell with what's displayed.
Or, what would an IF statement be that I could use to somehow check if the date displayed is what I want, and if so keep that, otherwise use that =DATE(YEAR(),DAY(),MONTH()) "trick"?
I also tried =IF(DAY(C2)>12,DATE(YEAR(C2),DAY(C2),MONTH(C2)),C2) but that doesn't work either because it returns June 01 2016 for both 01/06/2016 and 06/01/2016.
I may be overlooking something simple, I've been staring at this for an hour or so...Any thoughts/ideas are appreciated!
Say wee have dates in column C from C1 through C100 that appear to be correct. (so if you see 06/12/2017 you want it to be June 12th and not December 6th)
Try this short macro:
Sub dateFixer()
Dim ary(1 To 100) As String
Dim rng As Range, r As Range
Dim i As Long
Set rng = Range("C1:C100")
i = 1
For Each r In rng
ary(i) = r.Text
i = i + 1
Next r
rng.Clear
rng.NumberFormat = "mm/dd/yyyy"
i = 1
For Each r In rng
r.Value = ary(i)
i = i + 1
Next r
End Sub
A really clean solution is to use the CELL() function.
=CELL("format",A1) will return "D1" for cells formatted as d/m/y, and "D4" for cells formatted m/d/y. So, with this you can conditionally flip month and day:
=IF(CELL("format",A2)="D1",DATE(YEAR(A2),DAY(A2),MONTH(A2)),A2)
The Text property of a range returns the display text: "what the cell literally displays."
Note that literal is the right word - for example, if your column is too narrow, Text will return the displayed ##### characters instead of any useful value.
The Text property will not return an array, so you'll have to loop through your range and read/write individual cells with something like this:
For Each c in rng
c.Value = c.Text
Next c
Incidentally, the documentation on this property is almost nonexistent. This blog post goes into a more detailed review of the property and how it relates to Value and Value2.