VBA copy records that are between two date / times - vba

I am trying to create a report in Word that drags data from an Excel spreadsheet. Being on a works PC I am limited as to what I can do (cant open word from excel macro's) so my work-around is to copy all the Information I need from one excel worksheet into another so that is properly formatted / arranged as a data source for a word mail merge.
The problem I have is that I want to copy the records that run between 07:00 on one day to 07:00 the next. It went a bit wrong when I added a nested IF for the times.
Any help is much appreciated,
Rgds Iain
Sub CopyFromLog()
Dim LastRow As Long
Dim i As Long, j As Long, ns As Date, nf As Date, o As Date, f As String, s As String, t As Date
With Worksheets("Log")
LastRow = .Cells(.Rows.Count, "B").End(xlUp).Row
End With
With Worksheets("Data")
Worksheets("Data").Rows("3:" & LastRow).Clear
j = .Cells(.Rows.Count, "B").End(xlUp).Row + 2
End With
With Worksheets("Navigation")
ns = Worksheets("Navigation").Cells(3, "C").Value ' the report start date
nf = Worksheets("Navigation").Cells(4, "C").Value ' the report end date
End With
For i = 2 To LastRow
With Worksheets("Log")
o = Worksheets("Log").Cells(i, "B").Value 'start date
t = Worksheets("Log").Cells(i, "V").Value 'end date
s = Worksheets("Log").Cells(i, "R").Value 'start time
f = Worksheets("Log").Cells(i, "W").Value 'finish time
If o <= ns And s >= "07:00" Then
If t >= nf And f <= "07:00" Or t >= nf And f <= "R" Then
.Rows(i).Copy Destination:=Worksheets("Data").Range("A" & j)
j = j + 1
End If
End If
End With
Next i
End Sub`

Here is a minor rewrite of your procedure. Refer to the comments for individual observations.
Sub CopyFromLog()
Dim LastRow As Long, i As Long, j As Long
Dim ns As Date, nf As Date, o As Date, t As Date
Dim f As String, s As String
With Worksheets("Log")
LastRow = .Cells(.Rows.Count, "B").End(xlUp).Row
End With
With Worksheets("Data")
' You realize that LastRow was just defined as the last row on the Log workbook
' and you are now using it on the Data worksheet to clear rows
.Rows("3:" & LastRow).Clear
'I don;t understand this next line. You just cleared everthing from row 3 down so j should be 2
j = .Cells(.Rows.Count, "B").End(xlUp).Row + 2
End With
With Worksheets("Navigation")
ns = .Cells(3, "C").Value + TimeSerial(7, 0, 0) ' the report start dateTIME
nf = ns + 1 '24 hours later
End With
With Worksheets("Log")
For i = 2 To LastRow
o = .Cells(i, "B").Value + CDate(.Cells(i, "R").Value) 'start dateTIME
t = .Cells(i, "V").Value + CDate(.Cells(i, "W").Value) 'end dateTIME
'I couldn't figure out your logic so I made my own.
'Yours looked backwards and you were comparing f to the leffter "R" (f was never assigned a value either)
If o >= ns And t < nf Then
.Rows(i).Copy Destination:=Worksheets("Data").Range("A" & j)
j = j + 1
End If
Next i
End With
End Sub
The big difference is that I have discarded the notion of attempting to get a correct answer by comparing strings that look like times and resorted to bringing the times back into the dates so that the ns var would be something like 07/30/2016 07:00 I adopted a similar approach for all other datetimes in order that direct comparison could be made.

Related

How to one date per row for all dates between 2 dates?

I know this question seem complicated but what I want to do is simple, I got 2 columns:
I is my Starting Date
L is my ending date
G is where all the dates are supposed to be
What I want to do is get the number of days per period (EndDate - StartDate + 1), add this many rows and change value of G to be written day per day.
I already coded the part below, but it doesn't seem to be right:
Sub Dates()
Dim LastRow As Long
Dim addrows
Dim FindDates
Dim CountDays
Dim dddays
Dim ws As Worksheet
Dim ws2 As Worksheet
Dim i As Long
Dim ir As Long
Set ws = Sheets("Sheet1")
Set ws2 = Sheets("Sheet2")
With Sheets("Sheet1")
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row - 1
End With
addrows = 2
For ir = 1 To LastRow
FindDates = ws.Range("I" & addrows).Value
CountDays = ws.Range("L" & addrows).Value - ws.Range("I" & addrows).Value + 1
Adddays = 0
For i = 1 To CountDays
ws2.Rows("2:2").Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
ws2.Range("A2").Value = Sheets("Sheet1").Range("A" & addrows).Value
ws2.Range("C2").Value = FindDates + Adddays
ws2.Range("C2").Value = ws.Range("G" & addrows).Value
Adddays = Adddays + 1
Next i
addrows = addrows + 1
Next ir
End Sub
File looks as follows:
Can you help me a bit? "ws2.Range("C2").Value = FindDates + Adddays" is giving me an error 13
I is my Starting Date
L is my ending date
G is where all the dates are supposed to be
What I want to do is get the number of days per period (EndDate - StartDate + 1), add this many rows and change value of G to be written day per day.
for what above this should help you:
Sub Dates()
Dim ir As Long, countDays As Long
With Sheets("Sheet1")
For ir = .Cells(.Rows.Count, "I").End(xlUp).row To 2 Step -1
With .Rows(ir)
countDays = .range("L1") - .range("I1") + 1
If countDays > 1 Then
.Offset(1).Resize(countDays - 1).Insert xlDown
.Offset(1).Resize(countDays - 1).value = .value
With .Resize(countDays).Columns("G")
.FormulaR1C1 = "=RC9+ROW()-" & .Rows(1).row
.value = .value
End With
End If
End With
Next
End With
End Sub

Excel VBA - Week Ending Date

Column B is my data - if there is a date value in column B please return week ending date in column C. Need a VBA code to accomplish this
Column B Column C
11/9/2016 11/11/2016
11/8/2016 11/11/2016
4/4/2017 4/7/2017
(blank) (blank)
3/28/2017 3/31/2017
Below is all I could get, but it's not any good.
Dim FirstDayInWeek, LastDayInWeek As Variant
Dim dtmDate As Date
dtmDate = Range("B2:B")
LastDayInWeek = dtmDate - Weekday(dtmDate, vbUseSystem) + 7
MsgBox LastDayInWeek
I replied to your comment on how to find the start date of week from a given date?, but here it is as an answer:
Function ReturnDate(DateRange As Date, Optional DayInWeek = 1) As Date
ReturnDate = DateRange - Weekday(DateRange, vbUseSystem) + DayInWeek
End Function
=ReturnDate(A1) gives Monday
=ReturnDate(A1,2) gives Tuesday
.
=ReturnDate(A1,5) gives Friday < --- This is the one you're after.
=ReturnDate(A1,7) gives Sunday.
A blank cell will give 01/01/1900, but you could add a check for that or format the cell not to show 0.
Perhapse you could take an approach like the one below
Sub ReturnWeekEndDate()
Dim InpRng As Range
Dim i As Long
Set InpRng = ActiveSheet.Range("A2:B5")
For i = 1 To InpRng.Rows.Count
If IsDate(InpRng.Cells(i, 1).Value) And IsDate(InpRng.Cells(i, 2).Value) Then
InpRng.Cells(i, 1).Offset(0, 2) = InpRng.Cells(i, 1).Value - Weekday(InpRng.Cells(i, 1).Value, vbUseSystem) + 7
End If
Next i
End Sub
Give this a try:
Sub INeedADate()
Dim i As Long, N As Long, r As Range, Bigr As Range
N = Cells(Rows.Count, "B").End(xlUp).Row
For i = 1 To N
Set r = Cells(i, "B")
If IsDate(r.Value) Then
addy = r.Address
r.Offset(0, 1).Value = Evaluate(addy & "-WEEKDAY(" & addy & ",3)+IF(WEEKDAY(" & addy & ",3)>4,11,4)")
End If
Next i
End Sub
This is similar to using the worksheet formula:
=B1-WEEKDAY(B1,3)+IF(WEEKDAY(B1,3)>4,11,4)
Or try this...
Sub GetFridayDate()
Dim LastDayInWeek As Date
Dim Rng As Range, Cell As Range
Dim lr As Long
lr = Cells(Rows.Count, 2).End(xlUp).Row
Set Rng = Range("B2:B" & lr)
For Each Cell In Rng
If IsDate(Cell.Value) Then
LastDayInWeek = Cell + 8 - Weekday(Cell, vbFriday)
Cell.Offset(0, 1) = LastDayInWeek
End If
Next Cell
End Sub
You said that this would be part of a process...so, just call the function as I have shown, and you're golden! BOOM!
Sub FindEndOfWeek_Test()
Call FindEndOfWeek(ActiveSheet, 1, 2, 6, 1)
End Sub
Function FindEndOfWeek(Sht As Worksheet, KnownDate_Column As Integer, _
EndOfWeek_Column, EndOfWeek As Integer, _
StartingRow As Long)
' This function takes in a spreadsheet, and and determines the date at the end
' of the week, based on known parameters being passed into the function.
'
Dim a As Long
Dim LastRow As Long
Dim EvalDate As Date
Dim NewDate As Date
' Determine the last row of the column you are working with
LastRow = Sht.Cells(Sht.Rows.Count, KnownDate_Column).End(xlUp).Row
' Loop through your entire spreadsheet to determine the end of the week for all rows
For a = StartingRow To LastRow
If IsDate(Sht.Cells(a, KnownDate_Column).Value) = True Then
NewDate = Sht.Cells(a, KnownDate_Column).Value
EvalDay = Weekday(NewDate)
' Determine the known date day of the week, and add accordingly.
If EvalDay < EndOfWeek Then
Sht.Cells(a, EndOfWeek_Column).Value = NewDate + (EndOfWeek - EvalDay)
ElseIf EvalDay > EndOfWeek Then
Sht.Cells(a, EndOfWeek_Column).Value = NewDate + (7 - EvalDay + EndOfWeek)
Else
Sht.Cells(a, EndOfWeek_Column).Value = NewDate
End If
End If
Next a
End Function
I think no need for vba, you use below formula:
=IF(B2<>"",B2+(7-WEEKDAY(B2,16)),"")
If you really need VBA code for this problem, which I did, you can convert the excel formula into a one-line solution like so:
WeekendingDate = Date + 7 - WorksheetFunction.Weekday(Date + 7 - 6)

Trying to get this Subroutine to work to populate Pivot Table Data for each set of dates on each line in Excel VBA

I am getting an endless loop for "i". I think it has to do with data types as these are dates and I had to Dim the Start and Finish dates as a Variant in order to get around a type mismatch error. the code is a starting point to populate each date that is a week starting on a Monday in a vertical list for each occurrence where that date falls between the P1Start and P1Finish dates. Right now I just want it to list that date and then I'll add copy paste commands for the rest of the target data and another loop for each Person's name. I have a bunch of extra variables defined so that I can use them later, not all are being used right now.
Here are some SS of the Sheet that the work is happening on.
Excel Sheet
`enter code here`Option Explicit
'Constants for Project Information
Const Row_PD = 2 'First Row with Project Dates in Project Info Table
Const Col_PD = 26 'First Column for Project Dates to be used in Sub from "Project Information"
Const PI = "Project Information"
Const Row_DA = 3 'First Row that has a Valid Date for the Array in Timeline
'Constants for Pivot Data
Const C_PD_Col_Start = 1 'First Column for Project Info Table "Pivot Data"
Const C_PD_Row_Start = 2 'First Row with Data in Pivot Data
Const PD = "Pivot Data"
Sub Range_Loop()
'
' Range_Loop Macro
' Populate Pivot Data for each week and Project and Person in Range for Each Phase by the Various Dates
'
Dim rStart As Variant, rFinish As Variant, pStart As Variant, pFinish As Variant, nProj As Integer, TimeR As Range
Dim a As Long, b As Long, c As Long, d As Long, i As Long, j As Long, k As Long, l As Long, P1Row As Integer, LPRow As Integer, fRng As Range, DRng As String
Dim P1Start As Variant, P1Finish As Variant, P2Start As Variant, P2Finish As Variant, P3Start As Variant, P3Finish As Variant, P4Start As Variant, P4Finish As Variant
Worksheets(PD).Activate
Range("A2:L100000").Clear
Worksheets(PI).Activate
a = 2 'Home Row for Project Data
b = 26 'Home Column For Project Data
i = 2 'Home Row for Pivot Data
c = 27
P1Row = 2
LPRow = Cells(Rows.Count, "Z").End(xlUp).Row
rStart = Cells(4, 1).Value
Do Until a > LPRow
Do Until b = 31
P1Start = Cells(a, b).Value
P1Finish = Cells(a, c).Value
Do Until rStart > P1Finish
If rStart < P1Start And rStart > P1Finish Then
Worksheets(PD).Cells(i, 1).Value = rStart
Else
k = 1
End If
rStart = rStart + 7
i = i + 1
Loop
rStart = Cells(4, 1).Value
b = b + 1
c = c + 1
Loop
b = 26
c = 27
a = a + 1
Loop
i = 2`enter code here`
'Worksheets(PI).Cells(a, b).Activate
'MsgBox (rFinish)
End Sub
I setup spreadsheet per your example and this line (used twice): rStart = Cells(4, 1).Value references an empty cell. Tested code and not getting endless loop but don't get any output either.
This line makes no sense If rStart < P1Start And rStart > P1Finish Then as it will never be read when rStart is greater than P1Finish because of Do Until rStart > P1Finish so it will never be true and the line to output data will never be read. Logic is faulty.
Change the If to: If rStart < P1Start Then and you will get output, even if you don't change Cells(4, 1) to Cells(3, 1) although probably should. But is it the output you want?

'If ... Then' statement with loop

I've what seems like a pretty simple application with looping and 'If..Then' statements but need some help on structuring it.
In very a basic example, I have a list numbers in column A and the values PM or AM listed in column B. I want to write a loop that will search every value in column B until the end of the data set, and add 12 to each value in column A each time column B has a value of PM. In a nutshell, it would look like this:
If column B = PM
then add 12 to its corresponding cell in column A
else move down to the next row and do the same thing until you reach an empty cell
There are many ways, here is a typical one:
Sub dural()
Dim i As Long
i = 1
Do While Cells(i, "B").Value <> ""
If Cells(i, "B").Value = "PM" Then
Cells(i, "A").Value = Cells(i, "A").Value + 12
End If
i = i + 1
Loop
End Sub
you can set it with For next loop and 2 variables. one for last row and the 2nd for the row count:
Sub Macro1()
Dim LastRow As String
Dim i As Integer
LastRow = Cells(Rows.Count, "A").End(xlUp).Row
For i = 1 To LastRow
If Cells(i, 2).Value = "PM" Then Cells(i, 1).vlaue = Cells(i, 1).vlaue + 10
Next i
End
'
End Sub
This is another way to do this.
Option Explicit
Sub Add()
Dim rData As Range
Dim r As Range
Set rData = Cells(1, 1).CurrentRegion.Columns("B").Cells
For Each r In rData
If UCase$(r.Value) = "PM" Then
r.Offset(, -1).Value = r.Offset(, -1).Value + 12
End If
Next r
End Sub

Inefficient code that doesn't find matching data values

I have 3 issues with the following piece of code:
Intention of code: I have a table of data, 4 columns (F,G, H and I) wide and X rows long (X is typically between 5 and 400). I have a list of dates in column M, typically no more than 8 dates. Column H of table, contains dates as well. I want to find the dates that are in both columns (H and M) and whenever they appear, go to the same row in column I and set its value to zero, and the one after it (so if a match was in H100, then I100 and I101 would be zeroed).
issues with code: edited 1) as per feedback.
1) I have, using an if formula (=if(H100=M12,1,0), verified that there is one match, as how the spreadsheet sees it. The macro does not find this match, despite confirmation from the if formula. Cells I100 and I101 have nonzero values, when they should be zeroed.
2) the code runs, but takes about 3 minutes to go through 3 sheets of 180 rows of data. What can be done to make it run faster and more efficiently? It could have up to 30 sheets of data, and 400 rows (extreme example but possible, in this instance im happy to let it run a bit).
3) Assuming my data table before the macro is run, is 100 rows long, starting in row 12, after the macro, column I has nonzero values for 111 rows, and zeroes for the next 389. Is there a way I can prevent it from filling down zeroes, and leaving it blank?
I am using a correlate function afterwards on column I and there huge agreement of 0's with 0's is distorting this significantly. Thanks in advance,
Sub DeleteCells()
Dim ws As Worksheet
Dim cell As Range, search_cell As Range
Dim i As Long
Dim h As Long
Application.ScreenUpdating = False
For Each ws In ThisWorkbook.Worksheets
If Not ws.Name = "Cover" Then
For Each cell In ws.Range("H12:H500")
On Error Resume Next
h = ws.Range("G" & Rows.Count).End(xlUp).Row
i = ws.Range("L" & Rows.Count).End(xlUp).Row
Set search_cell = ws.Range("M12:M" & h).Find(what:=cell.Value, LookIn:=xlValues, lookat:=xlWhole)
On Error GoTo 0
If Not search_cell Is Nothing Then
ws.Range("I" & cell.Row).Value = 0
ws.Range("I" & cell.Row + 1).Value = 0
Set search_cell = Nothing
End If
Next cell
End If
Next ws
Application.ScreenUpdating = True
Set ws = Nothing: Set cell = Nothing: Set search_cell = Nothing
End Sub
EDIT: TESTED CODE, will work for 0, 1 row of data in H/M column starting from row 12?
EDIT: Updated the cell to handle case with 1 line of data, untested :|
I will give my solution first, this one should be much faster because it read the cells into memory first
Please comment if it doesn't work or you have further question
Sub DeleteCells()
Dim ws As Worksheet
Dim i As Long
Dim h As Long
Dim MColumn As Variant ' for convinence
Dim HColumn As Variant
Dim IColumn As Variant
Application.ScreenUpdating = False
For Each ws In ThisWorkbook.Worksheets
If Not ws.Name = "Cover" Then 'matching the target sheet
' matching the rows where column M's date matches column H's date
'starting row num is 12
With ws ' for simplifying the code
h = .Range("H" & .Rows.count).End(xlUp).Row
If h = 12 Then ' CASE for 1 row only
If Range("H12").Value = Range("M12").Value Then
Range("I12:I13").Value = ""
End If
ElseIf h < 12 Then
' do nothing
Else
ReDim HColumn(1 To h - 11, 1 To 1)
ReDim MColumn(1 To h - 11, 1 To 1)
ReDim IColumn(1 To h - 10, 1 To 1)
' copying the data from worksheet into 2D arrays
HColumn = .Range("H12:H" & h).Value
MColumn = .Range("M12:M" & h).Value
IColumn = .Range("I12:I" & h + 1).Value
For i = LBound(HColumn, 1) To UBound(HColumn, 1)
If Not IsEmpty(HColumn(i, 1)) And Not IsEmpty(MColumn(i, 1)) Then
If HColumn(i, 1) = MColumn(i, 1) Then
IColumn(i, 1) = ""
IColumn(i + 1, 1) = ""
End If
End If
Next i
'assigning back to worksheet cells
.Range("H12:H" & h).Value = HColumn
.Range("M12:M" & h).Value = MColumn
.Range("I12:I" & h + 1).Value = IColumn
End If
End With
End If
Next ws
Application.ScreenUpdating = True
End Sub