I have a worksheet with records (database). In Column B is the date the record was created (dd-MMM-yyyy format). In Column C I have the time it was created (HH:MM 24hr format).
The problem I'm having, is purging the records older than 8 hours from current system time. This code works at purging previous day records for the current finance period, but it is not taking into account 24hr format and after midnight for records older than 8 hours. I have tried many different approaches to this but still unable to figure this out.
This is the code I have since the last time I tried to figure this out:
'------------------------
' Current Finance Period
'------------------------
cSheet = CStr(Format(cStartDate, "dd-MMM-yyyy")) & " - " & CStr(Format(cEndDate, "dd-MMM-yyyy")) `Set the sheet name to use (current finance period)
CreateSheetIf (cSheet) `Create sheet if not exists
cFTarget = wbFinance.Worksheets(cSheet).UsedRange.Rows.Count `count the rows used
Set wscFinance = wbFinance.Worksheets(cSheet)
MRCForm.Caption = "MRC [ Processing... " & cSheet & " Ready to Finance records... Please wait... ]"
Me.sysMsgBox.Value = " Purging records, between " & cSheet & ", marked Ready for Finance..."
Application.ScreenUpdating = False
If cFTarget = 1 Then
If Application.WorksheetFunction.CountA(wscFinance.UsedRange) = 0 Then cFTarget = 0
End If
Source = wsMRC.UsedRange.Rows.Count
Set xRg = wsMRC.Range("AF2:AF" & Source)
Set dRg = wsMRC.Range("B2:B" & Source) `Date column in dd-MMM-yyyy format
Set tRg = wsMRC.Range("C2:C" & Source) `Time column in HH:MM 24hr format
On Error Resume Next
For K = 1 To xRg.Count
If dRg(K).Value = "" Or tRg(K).Value = "" Or xRg(K).Value = "" Then Exit For
If Format(dRg(K).Value, "dd-MMM-yyyy") >= Format(cStartDate, "dd-MMM-yyyy") And Format(dRg(K).Value, "dd-MMM-yyyy") < CStr(Format(Now, "dd-MMM-yyyy")) Then ' If date is within current finance period then
If CStr(xRg(K).Text) = "Y" Then
xRg(K).EntireRow.Copy Destination:=wscFinance.Range("A" & cFTarget + 1)
xRg(K).EntireRow.Delete
cFTotal = cFTotal + 1
MRCForm.Caption = "MRC [ Processing... " & cSheet & " (" & cFTotal & ") Please wait... ]"
If CStr(xRg(K).Value) = "Y" Then
K = K - 1
End If
cFTarget = cFTarget + 1
End If
End If
Next
Source = wsMRC.UsedRange.Rows.Count
Set xRg = wsMRC.Range("AF2:AF" & Source)
Set dRg = wsMRC.Range("B2:B" & Source) `Date column in dd-MMM-yyyy format
Set tRg = wsMRC.Range("C2:C" & Source) `Time column in HH:MM 24hr format
On Error Resume Next
For K = 1 To xRg.Count
If dRg(K).Value = "" Or tRg(K).Value = "" Or xRg(K).Value = "" Then Exit For
If Format(dRg(K).Value, "dd-MMM-yyyy") = CStr(Format(Now, "dd-MMM-yyyy")) And Format(tRg(K).Value, "HH:MM") <= Format(Now - TimeValue("08:00"), "HH:MM") Then ' If time is greater or equal to 8 hours ago then
If CStr(xRg(K).Text) = "Y" Then
xRg(K).EntireRow.Copy Destination:=wscFinance.Range("A" & cFTarget + 1)
xRg(K).EntireRow.Delete
cFTotal = cFTotal + 1
MRCForm.Caption = "MRC [ Processing... " & cSheet & " (" & cFTotal & ") Please wait... ]"
If CStr(xRg(K).Value) = "Y" Then
K = K - 1
End If
cFTarget = cFTarget + 1
End If
End If
Next
wscFinance.Columns("A:AM").AutoFit
Application.ScreenUpdating = True
Application.ScreenUpdating = True
I know the code is not very clean, just trying to get something that will function for now, will try to clean it up at a later date. Might even look at creating Functions as reusable code is more efficient.
Mock-up:
current time 11:45 on 2018.03.06
storing log date in column A
storing log time in column B
Untested code:
Dim i as long, lr as long, y as long, a as long, b as long
lr = cells(rows.count,1).end(xlup).row
For i = lr to 2 Step -1
y = TimeValue(now())-8
If y < 0 Then
a = Date(Now())-1
b = 24 + y 'y should be a negative value
Else
a = Date(Now())
b = y
End If
If Cells(1,1)=a AND Cells(1,2)>=b Then
.Rows(i).Delete
End If
Next i
Intention of this code:
loop through each row and delete the whole row if criteria met
find what 8 hours before Now() was and store as y... with the current time/date it is 03:45 on 2018.03.06, y = 3:45
If we save the current time is 02:00 on 2018.03.06 then y = -6:00
based on y being +/-, you determine the day and time
24 hour based time for where y is negative, so you add the negative number... in the case of y = -6, 24+(-6) = 18, so 18:00 hours, and the previous date (z)
you then assess the current row based on if the date matches AND if the time is less than or equal to z and y, respectively
This should be a starting point.
Related
I have 2 worksheets that I'm trying to compare.
Problem is that I can't go row by row because on the second worksheet there are extra entries based on "Batch Size", please see the example below. Also we can found duplicated data or missing ones on the second worksheet.
example picture
I believe it would be a lot easier to find any discrepancies if I have the "Bolt ID"s already created on the first worksheet then just go down 1-by-1 on every row and find the corresponding row that includes the same "Bolt ID" somewhere on the second worksheet.
Based on Batch Size, if Batch Size = 0
Bolt ID = Program ID_Step Number
if Batch Size is bigger than 0 then (for example Batch Size = 4)
`Bolt ID = Program ID_Step Number_1
`Bolt ID = Program ID_Step Number_2
`Bolt ID = Program ID_Step Number_3
`Bolt ID = Program ID_Step Number_4`
Any help is much appreciated in advance
Thank you
#freeflow
Thank you, I've ended up adding "_0" to all "Bolt ID" where they were needed using the following formula:
=IF(LEN(A1)-LEN(SUBSTITUTE(A1,"_",""))>1,A1,IF(RIGHT(A1,LEN(A1)-FIND("#",SUBSTITUTE(A1,"_","#",LEN(A1)-LEN(SUBSTITUTE(A1,"_",""))),2))<>"0",A1&"_0",A1))
Then I had the consistent "Bolt ID" to work with as you recommended and I could use this loop to compare my 2 sheets:
lastrow = steps.Range("A" & steps.Rows.Count).End(xlUp).Row
counter = 2
Do While counter <= lastrow
If Not steps.Range("A" & counter).EntireRow.Hidden Then
sequence_id = steps.Range("A" & counter)
step_no = steps.Range("B" & counter)
descr = steps.Range("C" & counter)
type_of_op = steps.Range("D" & counter)
tool_id = steps.Range("E" & counter)
batch_size = steps.Range("H" & counter)
If steps.Range("C" & counter) <> "Scan Process Barcode" Then
If type_of_op = "Fastening" Then
fastening_id = 1
Else: fastening_id = 0
End If
Do While fastening_id <= batch_size
bolt_id = sequence_id & "_" & step_no & "_" & fastening_id
r = 0
With ActiveSheet.Range("P:P")
Set loc = .Cells.Find(bolt_id, , xlValues, xlWhole, , , True)
If loc Is Nothing Then
MsgBox bolt_id & " Not found"
Else
occured = 0
Do Until loc.Row <= r
colorrange = (loc.Address)
occured = occured + 1
If occured >= 1 Then
ActiveSheet.Range(colorrange).Interior.ColorIndex = 45
End If
r = loc.Row
Set loc = .FindNext(loc)
Loop
If occured = 1 Then
ActiveSheet.Range(colorrange).Interior.ColorIndex = 4
End If
End If
End With
fastening_id = fastening_id + 1
Loop
End If
End If
counter = counter + 1
Loop
All it does is just highlighting the duplicated values for me but offsetting loc.address I can compare the rest of the cells and let the code making decisions.
this is my first VBA project and I'm stuck on one problem:
I need to calculate the monthly skewness using daily returns for multiple assets. First, I detected the months and the cells where each month ends and put them in a new worksheet. Based on that, I calculate the monthly skewness for each asset to get a table with the month in the rows and the assets in the columns.
The problem is that the skewness for the first and last row (so the first and last month) are incorrect, although the month is correctly identified by the first part of the code. In the second part of the code where I calculate the skewness I get this error: "Unable to get the Skew property of the WorksheetFunction class" for the line where I want to store the skew in an array but the skewness is calculated nonetheless. How can I solve the error message and get the correct skewness for the first and last month?
Any help would be very appreciated!
This is the code im working with:
Sub step1()
Dim v()
Dim idm()
Dim MonthCount As Single
'find the last row
lastrow = Worksheets("Data").Range("A2").End(xlDown).Row
'save the dates to array
v = Worksheets("Data").Range("A2:A" & lastrow).Value
'count the number of months
M = 1 'initiate month counter
For Row = 1 To lastrow - 2
'extract months from dates
month1 = month(v(Row, 1))
month2 = month(v(Row + 1, 1))
'increase the counter when the month changes
If month1 <> month2 Then
M = M + 1
End If
Next
MonthCount = M
'resize the month arrays
ReDim idm(1 To MonthCount, 1 To 2)
'detect change in month and save cumulative days as row indexes
M = 1 'initiate month counter
For Row = 1 To lastrow - 2
month1 = month(v(Row, 1))
month2 = month(v(Row + 1, 1))
If month1 <> month2 Then
'save the month
idm(M, 1) = month1 & "/" & Year(v(Row, 1))
'save the cumulative days
idm(M, 2) = Row + 1
M = M + 1
End If
On Error Resume Next
Next
'save the last month, cannot detect month change with empty row below
idm(M, 1) = month1 & "-" & Year(v(Row, 1))
idm(M, 2) = Row
'End With
'write the month array to excel
Worksheets("Months").Range("A2:B" & MonthCount + 1).Value = idm
End Sub
Sub step2()
Dim idMonth()
'save last month index to array
lastmonth = Worksheets("Months").Range("B2").End(xlDown).Row
idMonth() = Worksheets("Months").Range("B2:B" & lastmonth).Value
Dim z As Variant
Dim r1, r2 As Double
nc = Worksheets("Data").Range("A1").CurrentRegion.Columns.Count
'initiate counters
r1 = 2
r2 = 2
For c = 1 To nc
For M = 1 To UBound(idMonth) 'm is month index
'end of the month
r2 = idMonth(M, 1)
'set range for skewness
Set month_range = Worksheets("Data").Range(Cells(r1, c + 1).Address, Cells(r2, c + 1).Address)
'store skew in array
'error in the following line
z = Application.WorksheetFunction.skew(month_range)
'write skew to cells
Worksheets("Months").Cells(M + 1, c + 2).Value = z
'start of next month
r1 = r2 + 1
On Error Resume Next
Next
Next
End Sub
Could you please help me to obtain the difference between 2 dates (working hours only, that's very important)
Take a look at this image:
First response is calculated by the difference between: Date First Response and Date of the problem
Elapsed time is calculated by the difference between: Date Last Response and Date of the problem
This is my macro so far (it is not working properly):
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Const WORKING_DAY_START As String = "09:00"
Const WORKING_DAY_END As String = "18:00"
Const FORMULA_WORKING_TIME As String = _
"=(INT(E2-D2)*(""" & WORKING_DAY_END & """-""" & WORKING_DAY_START & """)" & _
"+MEDIAN(MOD(E2,1),""" & WORKING_DAY_END & """,""" & WORKING_DAY_START & """)" & _
"-MEDIAN(MOD(D2,1),""" & WORKING_DAY_END & """,""" & WORKING_DAY_START & """))"
Const FORMULA_ELAPSED_TIME As String = "=F2-D2"
Dim lastrow As Long
On Error GoTo ws_bdc_exit
Application.ScreenUpdating = False
Application.EnableEvents = False
With Me
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
'input Elapsed Time
.Range("H2").Resize(lastrow - 1).Formula = FORMULA_ELAPSED_TIME
'input First Response time
.Range("G2").Resize(lastrow - 1).Formula = FORMULA_WORKING_TIME
With .Range("G2:H2").Resize(lastrow - 1)
.Value = .Value
.NumberFormat = "##0.00"
End With
End With
ws_bdc_exit:
Target.Offset(1).Select
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
EDIT #1: I should obtain working hours from Monday to Friday (weekend not included, but i dont know how to do it)
EDIT #2: The difference should be displayed in hours
EDIT #3: Before, i was using this macro (everything was working fine BUT i was not getting the working hours)
Public cVal
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim LastRow
LastRow = Range("A" & Rows.Count).End(xlUp).Row
For i = 2 To LastRow
t1 = TimeValue(CStr(Cells(i, "D").Value))
t2 = TimeValue(CStr(Cells(i, "E").Value))
t3 = TimeValue(CStr(Cells(i, "F").Value))
'input First Response time
If Hour(t2) - Hour(t1) = 0 Then
Cells(i, "G").Value = Round((Minute(t2) - Minute(t1)) / 60, 2)
Else
Cells(i, "G").Value = Hour(t2) - Hour(t1) + Round((Minute(t2) - Minute(t1)) / 60, 2)
End If
'input Elapsed Time
If Hour(t3) - Hour(t1) = 0 Then
Cells(i, "H").Value = Round((Minute(t3) - Minute(t1)) / 60, 2) '- Cells(i, "J").Value - Cells(i, "J").Value
Else
Cells(i, "H").Value = Hour(t3) - Hour(t1) + Round((Minute(t3) - Minute(t1)) / 60, 2) '- Cells(i, "J").Value
End If
Next i
Target.Offset(1).Select
End Sub
I wrote a function which should calculate the working hours, Mon-Fri, only.
Note that in your posted example, some of the dates are on Sat/Sun, so will calculate as zero.
The algorithm:
Calculate workhours for each day that is Mon-Fri as being WORKING_DAY_START -WORKING_DAY_END` hours.
Make an adjustment if the day happens to be the first or last day being calculated.
You can use this function either on the worksheet itself, you can call if from a Macro that fills in the cells with just the value.
Below I will show your original data, plus some extra lines altering your weekend work dates.
Option Explicit
Function elapsedWorkTime(startDT As Date, endDt As Date) As Date
Const WORKING_DAY_START As Date = #9:00:00 AM#
Const WORKING_DAY_END As Date = #6:00:00 PM#
Dim adjTimeStart As Date, adjTimeEnd As Date, totTime As Date
Dim D As Date
For D = DateValue(startDT) To DateValue(endDt)
Select Case Weekday(D)
Case 2 To 6
'Adj for first and last days
If D = DateValue(startDT) Then
If TimeValue(startDT) <= WORKING_DAY_START Then
adjTimeStart = 0
ElseIf TimeValue(startDT) >= WORKING_DAY_END Then
adjTimeStart = WORKING_DAY_START - WORKING_DAY_END
Else
adjTimeStart = WORKING_DAY_START - TimeValue(startDT)
End If
End If
If D = DateValue(endDt) Then
If TimeValue(endDt) >= WORKING_DAY_END Then
adjTimeEnd = 0
ElseIf TimeValue(endDt) <= WORKING_DAY_START Then
adjTimeEnd = WORKING_DAY_START - WORKING_DAY_END
Else
adjTimeEnd = TimeValue(endDt) - WORKING_DAY_END
End If
End If
totTime = totTime + WORKING_DAY_END - WORKING_DAY_START
End Select
Next D
elapsedWorkTime = totTime + adjTimeStart + adjTimeEnd
End Function
EDIT Corrected formatting on screenshot
Note that the formula in the worksheet cell, since you want the output expressed as hours, is something like:
=elapsedWorkTime(C2;D2)*24
Note the discrepancy for 5541. In your example, you show a value of 8,52 for elapsed time. But in your requirements statement you write you want to include working hours only. Working hours end at 18:00 so time spent after that should not be counted.
Maybe there is no need to use VBA.
Use NETWORKDAYS function to count workdays between dates.
Multiply them by working hours a day
Substract work hours from begin and end date (e.g. work started later than workday begins and so on)
Calculate totals.
I'd recommend to do every step in a single cell, in order to check step-by-step logics.
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