VBA to calculate when production will finish - vba

I am trying build a macro to give me the date and time production will end based on a value entered into cell A2. At present each unit takes 1 min 5 sec to produce. the shift will finish at 00:30 and restart at 05:30 so I would like it to take this into consideration.
Some examples
If the date/time is 14/03/2017 22:00 and I enter 55 into cell A2 I would expect a return of 14/03/2017 23:00 in cell E2.
If the date/time is 14/03/2017 22:00 and I entered 1 into cell A2 I would expect a return of 14/03/2017 22:01 in cell E2.
If the time is 14/03/2017 23:55 and I entered 55 into cell A2 I would expect a return of 15/03/2017 05:55
So at the moment I am using this code which works well within the shift but I cant get any further with it ie. going into the next days production.
Range("A2").Value * Range("C2").Value + Now()
A2 being the number of units and C2 being the process time 0,1,5
Many thanks for your help /advice

I'm not sure what the problem is. The maths are (units * time-per-unit) + start time. The brackets are for visual clrity only; mathematical precedence will ensure that the multiplication takes place before the addition.
With Worksheets("sheet1")
.Range("E2") = Now
.Range("F2") = .Range("E2").Value2 + (.Range("A2").Value2 * .Range("C2").Value2)
.Range("G2") = Application.Ceiling(.Range("F2").Value2, TimeSerial(0, 0, 1))
.Range("E2:G2").NumberFormat = "[Color10][$-en-US]dd-mmm-yyyy hh:mm;[Color3]#"
End With
FWIW, I've had better success using .Value2 instead of .Value as it is a raw numerical without additional date/time/currency information.

This took a little time but it turned out rather nice. Follow these instructions to test.
Create a standard code module in the workbook in which you want the action to occur. The name will be "Module1" by default. I suggest you change it to "Main" or some better, descriptive name. Paste the following enumerations at the top of your module, including the option statement.
Option Explicit
Enum Nws ' Worksheet navigation
NwsFirstDataRow = 2
NwsQty = 1 ' Columns (not necessarily contiguous):
NwsTime ' time to produce one unit
NwsStart ' date/time
NwsEnd ' date/time
End Enum
This enumeration serves to identify rows and columns in your worksheet. Note that columns are numbered (1=A, 2=B, 3=C etc.) Enumerations without a value assume the value of the previous one +1. So, NwsEnd = 4 = column D. You can set up the sheet according to the enum or adjust the enum values to match your sheet, but you must have a column for each of Quantity, Production time, Production Start time, and Production completion time. NwsFirstDataRow serves to prevent the macro from changing data you don't want changed - at least those in the caption row, here presumed to be row 1.
Now past the next enumeration below the above.
Enum Nsh ' Shift (use 24h format)
NshStart = 530 ' read as 05:30 (on current day)
NshEnd = 2430 ' read as 00:30 (on next day)
End Enum
This enum holds a code for your shift time. In the future, if your working hours change, you can modify the code's output simply by changing these numbers.
The next sub is doing most of the work.
Sub SetCompletion(Ws As Worksheet, R As Long)
' 20 Mar 2017
Dim Qty As Long
Dim ShiftQty As Long, DayQty As Long
Dim UnitTime As Double, StartTime As Double
Dim ComplDate As Double
Dim Days As Integer
With Rows(R)
Qty = .Cells(NwsQty).Value
UnitTime = .Cells(NwsTime).Value
StartTime = .Cells(NwsStart).Value
If Qty And (UnitTime > 0) And (StartTime > 0) Then
ComplDate = (UnitTime * Qty) + StartTime
ShiftQty = QtyTillShiftEnd(StartTime, UnitTime)
If ShiftQty < Qty Then
Qty = Qty - ShiftQty
DayQty = DailyProduction(UnitTime)
ComplDate = StartTime + 1 + Int(Qty / DayQty)
ComplDate = ComplDate + UnitTime * (Qty Mod DayQty)
End If
.Cells(NwsEnd).Value = ComplDate
End If
End With
End Sub
Its basic calculation method is to first calculate how many units can be produced from the start of production until the end of that day's shift. Then calculate the full days' production, and calculate the completion from the number of remaining units to be produced on the last day. No such calculation will be made while any of the 3 required components are missing (Qty, Time, StartTime). The following functions are helping in the calculation. Paste them at the bottom of the same "Main" code module.
Private Function QtyTillShiftEnd(ByVal StartTime As Double, _
ByVal UnitTime As Double) As Double
' 20 Mar 2017
Dim ProdTime As Double
ProdTime = (Int(StartTime) + NshToDays(NshEnd) - StartTime)
QtyTillShiftEnd = (ProdTime + 0.0001) / UnitTime
End Function
Calculates the quantity that can be produced from the start time until the end of the first production day's shift. The next function calculates a full day's production.
Private Function DailyProduction(UnitTime As Double) As Integer
' 19 Mar 2017
DailyProduction = Int((NshToDays(NshEnd) - NshToDays(NshStart) + 0.000001) / UnitTime)
End Function
When working with Double type numbers, required for time calculations, VB has difficulty calculating zero. The 0.000001 added to the result ensures that the calculation doesn't end up below zero when zero is required. The next function converts the encoded shift times from Enum Nsh into fractions of days which this program can work with.
Private Function NshToDays(TimeCode As Nsh) As Double
' 19 Mar 2017
Dim H As Double, M As Double
H = Int(TimeCode / 100)
M = TimeCode Mod 100
NshToDays = (1 / 24 * H) + (1 / 24 / 60 * M)
End Function
The next function corrects wrong entries in the StartTime column NwsStart.
Function AdjustedStartTime(ByVal StartTime As Double) As Double
' 19 Mar 2017
' return new StartTime or 0
Dim Fun As Double
Dim StartDate As Long
Dim ShiftStart As Double, ShiftEnd As Double
ShiftStart = NshToDays(NshStart)
ShiftEnd = NshToDays(NshEnd)
StartDate = Int(StartTime)
StartTime = StartTime - StartDate
Fun = StartTime
If ShiftEnd > 1 Then
If StartTime < (ShiftStart - Int(ShiftStart)) Then
If StartTime > (ShiftEnd - Int(ShiftEnd)) Then Fun = ShiftStart
End If
Else
If (StartTime - Int(StartTime)) < ShiftStart Then
Fun = ShiftStart
Else
If StartTime > ShiftEnd Then Fun = ShiftStart + 1
End If
End If
AdjustedStartTime = Fun + StartDate
End Function
This function's action is to make sure that no one enters a time like 4:00 am. If someone does the entry will be changed to 05:30 because that's when the shift starts.
The last procedure in this code sheet formats cells.
Sub FormatCells(Row As Range)
' 19 Mar 2017
Dim Fmt As Variant, Clm As Variant
Dim i As Integer
' match for number formats in 'Fmt' to the column numbers in 'Clm'
Clm = Array(NwsQty, NwsTime, NwsStart, NwsEnd)
Fmt = Array("#,##0", "hh:mm:ss", "dd mmm hh:mm", "dd mmm hh:mm")
For i = 0 To UBound(Clm)
Row.Cells(Clm(i)).NumberFormat = Fmt(i)
Next i
End Sub
This sub will be called whenever a production quantity is entered. You can adjust the cell formats here. This goes especially for the date formats which I may not have done to your liking.
Now, still in the VBE window, please find the code sheet for the worksheet on which you have the columns first above enumerated. It might be listed in the Project window as Sheet1(Sheet1) or similar. It is important that you identify the correct sheet and paste the following procedure there.
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
' 20 Mar 2017
Dim Entry As Variant
With Target
' no action when more than one cell is modified at once
If .Cells.Count = 1 And .Row >= NwsFirstDataRow Then
Application.EnableEvents = False
Entry = .Value
Select Case .Column
Case NwsQty
If Val(Entry) < 1 Then
If Len(Entry) > 0 Then
MsgBox "Please enter a number representing" & vbCr & _
"the quantity to be produced.", vbExclamation, _
"Invalid entry"
.Select
End If
Else
FormatCells Rows(.Row)
SetCompletion ActiveSheet, .Row
End If
Case NwsTime
If Val(Entry) Then
SetCompletion ActiveSheet, .Row
Else
If Len(Entry) > 0 Then
MsgBox "The production time must be entered" & vbCr & _
"in the format h:m:s", vbExclamation, _
"Invalid entry"
.Select
End If
End If
Case NwsStart
If Val(Entry) Then
If (Val(Entry) < 1) Then .Value = Entry + Date
Entry = AdjustedStartTime(.Value)
If .Value <> Entry Then
MsgBox "On " & Format(Entry, "mmm d") & " production doesn't " & _
"start until " & Format(Entry, "h:mm") & "." & vbCr & _
"Your entry was adjusted accordingly.", _
vbInformation, "Corrected time entry"
.Value = Entry
End If
SetCompletion ActiveSheet, .Row
Else
MsgBox "The production start must be entered as a Time" & vbCr & _
"or Dtae/Time value in the format [d/m/yy] HH:mm", _
vbExclamation, "Invalid entry"
.Select
End If
Case NwsEnd
MsgBox "Recalculate with a new production start?" & vbCr & _
"The completion date is the result of a calculation." & vbCr & _
"To change it, modify the unit production time.", _
vbInformation, "Invalid action"
Application.Undo
End Select
Application.EnableEvents = True
End If
End With
End Sub
You can get a fair idea of what this procedure does by reading the various error messages it can spit out. You can modify these texts. Note that you can enter the start time as a time or Date/Time. If you enter time only the macro will add the current date automatically. In the absence of errors this macro will call the sub SetCompletion which will write the completion date to the sheet if all criteria for its calculation are present.
Note that there is no protection of your data after change of Enum Nsh. To avoid overwriting existing completion dates which were calculated with previously correct shift times, set the enumeration NwsFirstDataRow to exclude rows you wish to protect in this way.
All components have been tested, but the project as a whole is rather complex and you would do well to watch the results in real life. You may have noticed that I have structured the code in such a way that faults can easily be attributed to specific functions which may then have to be improved without putting everything into question.

The problem is with the date format, more specifically, the input date format in the start time column. The macro will only calculate if a start time is given. So, when the start time is given but not recognised as such no calculation takes place.
I programmed for day/month/year and you are using the American system. Please replace the existing procedure of the same name with this one.
Sub FormatCells(Row As Range)
' 25 Mar 2017
Dim Fmt As Variant, Clm As Variant
Dim i As Integer
' match for number formats in 'Fmt' to the column numbers in 'Clm'
Clm = Array(NwsQty, NwsTime, NwsStart, NwsEnd)
Fmt = Array("#,##0", "hh:mm:ss", "mmm dd hh:mm", "mmm dd hh:mm")
For i = 0 To UBound(Clm)
Row.Cells(Clm(i)).NumberFormat = Fmt(i)
Next i
End Sub
Modifications have also been made to the event procedure which will now recognise dates entered in your format. Please remember that you are supposed to be able to enter the start time as 7:30 and have the cell display Mar 25 07:30. Try this. also try entering "Mar 20 7:30", "3/20/17 7:30", "3/20 7:30", and then "3/20/17 14:00" and perhaps "3/20/17 2:00 PM". If it doesn't work that would be indication of bigger wrongs behind the scenes.
Private Sub Worksheet_Change(ByVal Target As Range)
' 25 Mar 2017
Dim Entry As Variant
With Target
' no action when more than one cell is modified at once
If .Cells.Count = 1 And .Row >= NwsFirstDataRow Then
Application.EnableEvents = False
Entry = .Value
Select Case .Column
Case NwsQty
If Val(Entry) < 1 Then
If Len(Entry) > 0 Then
MsgBox "Please enter a number representing" & vbCr & _
"the quantity to be produced.", vbExclamation, _
"Invalid entry"
.Select
End If
Else
FormatCells Rows(.Row)
SetCompletion ActiveSheet, .Row
End If
Case NwsTime
If Val(Entry) Then
SetCompletion ActiveSheet, .Row
Else
If Len(Entry) > 0 Then
MsgBox "The production time must be entered" & vbCr & _
"in the format h:m:s", vbExclamation, _
"Invalid entry"
.Select
End If
End If
Case NwsStart
If IsDate(Entry) Then
If (CDbl(Entry) < 1) Then .Value = Entry + Date
Entry = AdjustedStartTime(.Value)
If .Value <> Entry Then
MsgBox "On " & Format(Entry, "mmm d") & " production doesn't " & _
"start until " & Format(Entry, "h:mm") & "." & vbCr & _
"Your entry was adjusted accordingly.", _
vbInformation, "Corrected time entry"
.Value = Entry
End If
SetCompletion ActiveSheet, .Row
Else
MsgBox "The production start must be entered as a Time" & vbCr & _
"or Date/Time value in the format [m/d/yy] HH:mm", _
vbExclamation, "Invalid entry"
.Select
End If
Case NwsEnd
MsgBox "Recalculate with a new production start?" & vbCr & _
"The completion date is the result of a calculation." & vbCr & _
"To change it, modify the unit production time.", _
vbInformation, "Invalid action"
Application.Undo
End Select
Application.EnableEvents = True
End If
End With
End Sub
Last but not least, I discovered an error in the calculation of the completion time affecting production time ending not on the same day. I corrected it. Please exchange the functions.
Sub SetCompletion(ws As Worksheet, R As Long)
' 25 Mar 2017
Dim Qty As Long
Dim ShiftQty As Long, DayQty As Long
Dim UnitTime As Double, StartTime As Double
Dim ComplDate As Double
Dim Days As Integer
With Rows(R)
Qty = .Cells(NwsQty).Value
UnitTime = .Cells(NwsTime).Value
StartTime = .Cells(NwsStart).Value
If Qty And (UnitTime > 0) And (StartTime > 0) Then
ComplDate = (UnitTime * Qty) + StartTime
ShiftQty = QtyTillShiftEnd(StartTime, UnitTime)
If ShiftQty < Qty Then
Qty = Qty - ShiftQty
DayQty = DailyProduction(UnitTime)
ComplDate = Int(StartTime) + 1 + NshToDays(NshStart) + Int(Qty / DayQty)
ComplDate = ComplDate + UnitTime * (Qty Mod DayQty)
End If
.Cells(NwsEnd).Value = ComplDate
End If
End With
End Sub
Actually, this procedure should be further amended to recognise weekends, but I hope that your production wouldn't stop on Sundays :-)
I shall appreciate your bearing with me in case you still face a problem with the dates. I could change the default settings on my PC for better testing, but so far I have avoided doing so. :-)

The text in your event procedure should be changed in the light of changes you want to make to the code. Please replace as follows:-
MsgBox "You entered a time during which production rests." & vbCr & _
"The next shift after that will start on " & _
Format(Entry, "dddd,") & vbCr & _
Format(Entry, "mmmm d,") & " at " & _
Format(Entry, "h:mm") & "." & vbCr & _
"Your entry was adjusted accordingly.", _
vbInformation, "Corrected time entry"
' MsgBox "On " & Format(Entry, "mmm d") & " production doesn't " & _
' "start until " & Format(Entry, "h:mm") & "." & vbCr & _
' "Your entry was adjusted accordingly.", _
' vbInformation, "Corrected time entry"
Meaning, look for the lines of code with apostrophes at their beginning above and replace them with the liens that don't have an apostrophe.
I suggest you post a link to this thread in the new thread you just started.

Related

VBA Code to Alert Against Expired Material Assistance

I am trying to create a popup window as soon as I open my excel file.
The excel workbook that I have has multiple sheets.
One of the sheets is titled "Inventory".
As shown in the image below, there is a column in the Inventory tab that is titled "Days Until Expiration".
I want to have my excel file display a pop up when opening the workbook. This popup will check the "Days Until Expiration" column in the "Inventory" tab and say something like "____ material" (from the 'Type' Column) "has ____" days until expiration.
This will only happen if the "Days Until Expiration" value is in between 0 and 14 days.
If the number is negative in the "Days Until Expiration" column, I want the message pop up to say "___ material has expired".
Shown below is what I have so far. I have created a workbook_open() event and this code is in my "ThisWorkbook" code tab.
I am also getting an error when I run what I have below, specifically saying:
Run-time error '13': Type mismatch
Private Sub Workbook_Open()
Application.WindowState = xlMaximized
Dim wb As Workbook
Dim ws As Worksheet
Dim rngUsed As Range, rngExpirationColumn As Range, rngCell As Range
Dim strExpirationMessage As String
Set wb = ThisWorkbook
Set ws = wb.Sheets("Inventory")
Set rngUsed = ws.UsedRange
Set rngExpirationColumn = Intersect(ws.Columns(4), rngUsed)
For Each rngCell In rngExpirationColumn.Cells
If Date - CDate(rngCell.Value2) >= 14 Then
If Len(strExpirationMessage) = 0 Then
strExpirationMessage = rngCell.Offset(0, -3).Value2 & " material has " & (Date - CDate(rngCell.Value2)) & " days left before expiration"
Else
strExpirationMessage = strExpirationMessage & Chr(13) & rngCell.Offset(0, -3).Value2 & " material has " & (Date - CDate(rngCell.Value2)) & " days left before expiration"
End If
End If
Next
MsgBox strExpirationMessage
End Sub
I'm posting this answer based on your request and with some assumptions, as follows:
You want to check the data for column "Days Until Expiration" (as per your request)
You want to grab the data from column "Type" to add on the popup (as per your request)
You want one message if the Expiration Days is between 0 and 14
You want another message if the Expiration Days is less than 0
The value on column "Days Until Expiration" is actually a number (this is an assumption, since no data was provided)
I'm assuming "Days Until Expiration" is merged into two rows (per screenshot)
I'm assuming your data is directly below the rows from your screenshot, so probably your actual data starts on Row 4
Here is the code (tested based on assumptions above, due to lack of actual data to reproduce the scenario):
Private Sub Workbook_Open()
Application.WindowState = xlMaximized
Dim ws As Worksheet
Dim strExpirationMessage As String
Dim rngExpirationCell As Range, rngTypeCell As Range
Dim lngRow As Long, lngExpirationCol As Long, lngTypeCol As Long
Set ws = ThisWorkbook.Worksheets("Inventory")
Set rngExpirationCell = ws.UsedRange.Find(What:="Days Until Expiration", LookAt:=xlWhole, MatchCase:=False, SearchFormat:=False)
Set rngTypeCell = ws.UsedRange.Find(What:="Type", LookAt:=xlWhole, MatchCase:=False, SearchFormat:=False)
If Not rngExpirationCell Is Nothing And Not rngTypeCell Is Nothing Then
lngExpirationCol = rngExpirationCell.Column
lngTypeCol = rngTypeCell.Column
For lngRow = rngExpirationCell.row + 2 To ws.UsedRange.Rows.Count
With ws.Cells(lngRow, lngExpirationCol)
If 0 <= .Value And .Value <= 14 Then
strExpirationMessage = strExpirationMessage & ws.Cells(lngRow, lngTypeCol).Value & " material has " & .Value & " days left before expiration" & vbCrLf
ElseIf .Value < 0 Then
strExpirationMessage = strExpirationMessage & ws.Cells(lngRow, lngTypeCol).Value & " material has expired" & vbCrLf
End If
End With
Next
strExpirationMessage = Left(strExpirationMessage, Len(strExpirationMessage) - 2) 'to remove trailing vbCrLf
MsgBox strExpirationMessage
End If
End Sub
Important Notes:
I've modified part of your logic, by eliminating some variables and using others instead.
I'm not working with range objects, but rather with specified cells
I'm performing a dynamic search for the desired Columns "Days Until Expiration" and "Type", instead of working with fixed column and with offsets, to allow you to change the columns position in future without changing the code.
I'm assuming "Days Until Expiration" is merged into two rows (per screenshot) and this is why I'm using rngExpirationCell.row + 2 in the For loop. If you have anything different than that, the code might need changes.
I hope this suits your needs. Let me know of any issues or concerns.
If this post answers your question, please Accept it by clicking on the Check mark on the left of it.
Update:
Based on the issues you found, below are two alternate solutions for the For loop that you could use. Everything else should be exactly the same. Just replace the For loop with either of the logic below and you should be good to go
Assuming you want to leave the logic once you found an Empty cell:
For lngRow = rngExpirationCell.row + 2 To ws.UsedRange.Rows.Count
With ws.Cells(lngRow, lngExpirationCol)
If IsEmpty(.Value) Then Exit For 'Will leave the For loop once an Empty cell is found
If 0 <= .Value And .Value <= 14 Then
strExpirationMessage = strExpirationMessage & ws.Cells(lngRow, lngTypeCol).Value & " material has " & .Value & " days left before expiration" & vbCrLf
ElseIf .Value < 0 Then
strExpirationMessage = strExpirationMessage & ws.Cells(lngRow, lngTypeCol).Value & " material has expired" & vbCrLf
End If
End With
Next
Assuming you want to skip the evaluation for the Empty cells, but continue checking the cells until the end of the Used Range:
For lngRow = rngExpirationCell.row + 2 To ws.UsedRange.Rows.Count
With ws.Cells(lngRow, lngExpirationCol)
If Not IsEmpty(.Value) Then 'Will skip empty cells but continuing validation until the end of the Used Range
If 0 <= .Value And .Value <= 14 Then
strExpirationMessage = strExpirationMessage & ws.Cells(lngRow, lngTypeCol).Value & " material has " & .Value & " days left before expiration" & vbCrLf
ElseIf .Value < 0 Then
strExpirationMessage = strExpirationMessage & ws.Cells(lngRow, lngTypeCol).Value & " material has expired" & vbCrLf
End If
End If
End With
Next

Create a list of dates and avoid variable not set (VBA error 91)

I have financial data about certain stocks in five sheets and am trying to create a function which will calculate the exponential moving average of a given range.
[columns(1) = date ; columns(2) = closing price]
The arguments of this function are the number of days taken into account to calculate the EMA, and an integer kol to calculate several EMAs on several columns, side by side (no use for now). Here is my code so far:
Public Function MME(Lmme As Double, kol As Long)
Dim Cmme As Range
Dim Todate, rcell As Range
Dim alpha, period, Udate, i, j, k As Long
Dim Ustock As String
Dim wsDest As Worksheet
Udate = ThisWorkbook.Worksheets("UserForm").Range("B2").Value
period = ThisWorkbook.Worksheets("UserForm").Range("B3").Value
Ustock = ThisWorkbook.Worksheets("UserForm").Range("B4").Value
' MsgBox (Udate)
Set wsDest = ThisWorkbook.Sheets(Ustock)
wsDest.Activate
With wsDest.Range("A2:A392")
Set Todate = Cells.Find(What:=Udate, _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Todate Is Nothing Then
MsgBox ("todate wasn't found")
Else
End If
End With
i = Todate.Row
j = i + period
k = i - Lmme
Set Cmme = Range(Cells(i, 9 + kol), Cells(j, 9 + kol))
alpha = (2 / (Lmme + 1))
With Cmme
For Each rcell In Cmme
If rcell.Row <> i Then
rcell.Formula = "=B" & rcell.Row & "*" & alpha & "+I" & rcell.Row - 1 & "*" & 1 - alpha & ""\
Else: rcell.Formula = "=AVERAGE(B" & k & ":B" & i & " ) "
End If
Next rcell
End With
End Function
I created a list on a separate sheet which allows the user to select a date in 2008, and another which lets him select a Stock. So I did set new variables in order to do the trick but it doesn't work.
Usaction, USdate and Uperiod are the name ranges in which the values selected by the user are stored. But I got "error 91 or object required" on the set = period.
I really want the EMA to be calculated only for a specific period, starting the date selected.
EDIT: I updated the code with the latest version i have. I still have an error 91 on endate
EDIT2: Code updated. I don't understand why the date is not found. On the sheet UserForm the date selected by the user is in "B2" (USdate). It is in format general, but with the CDate in the find function it should be considered a date right? I tried with the date format, it didn't change anything ...
EDIT3: Thanks to Branislav I managed to make the find works by switching every date to General format. Since the Find is working, anyway to make it work using date format? So that the user can see actual date, instead of the integer associated.
Another question: How can i bypass the Cells.Formula to operate directly within vba, and makes it so formula shows in the formula bar in excel once the code ran, except the result of SMAs and EMAs operation within the range?
ToDate is already a range
Set Endate = Todate.Row + period
Also, before you get to that point, you set ToDate by using .Find(). Since it's entirely possible that someone would enter an invalid date or a date that you don't have data for, I'd strongly recommend adding:
if ToDate is Nothing then
'do some date not found stuff here
else
'do your date found stuff here
End If
You may also want to consider changing LookIn:=xlFormulas to LookIn:=xlValues because I believe you're looking for a cell value, not a cell formula.

Check if date falls between some range

I have data that is similar to:
A1: ID
B1: Start date
C1: End Date
I have another worksheet (call it New) that has
A1: ID and
B1: Date
I need to find out if the date for the ID in New worksheet was already in the previous Worksheet. If the date is start date, end date or anything in between, I want it to show that there is a record that already exist.
Solution here assuming something more practical:
A Master sheet with ID, Start Date, End Date (multiple rows)
Other sheets with ID and Date (multiple rows)
Uses a User Defined Function (UDF) and the ID cell as input
One drawback is that you will need "Calculate Sheet" if other sheets has been updated
Sample screenshots:
Formula for Sheet1 D2: =FindDuplicates(A2)
Code in a Module:
Option Explicit
Function FindDuplicates(oRngID As Range) As String
Dim sID As String, dStart As Date, dEnd As Date, lCount As Long, sWhere As String
Dim oWS As Worksheet, oRngFound As Range, dFound As Date, sFirstFound As String
sID = oRngID.Text
dStart = oRngID.Offset(0, 1).Value
dEnd = oRngID.Offset(0, 2).Value
lCount = 0
sWhere = ""
For Each oWS In ThisWorkbook.Worksheets
' Find all IDs in other worksheeets
If oWS.Name <> oRngID.Worksheet.Name Then
sFirstFound = ""
Set oRngFound = oWS.Cells.Find(What:=sID)
If Not oRngFound Is Nothing Then
sFirstFound = oRngFound.Address
' Keep searching until the first found address is met
Do
' Check the dates, only add if within the dates
dFound = oRngFound.Offset(0, 1).Value
If dStart <= dFound And dFound <= dEnd Then
lCount = lCount + 1
If lCount = 1 Then
sWhere = sWhere & lCount & ") '" & oWS.Name & "'!" & oRngFound.Address
Else
sWhere = sWhere & vbCrLf & lCount & ") '" & oWS.Name & "'!" & oRngFound.Address
End If
End If
Set oRngFound = oWS.Cells.Find(What:=sID, After:=oRngFound)
Loop Until oRngFound.Address = sFirstFound
End If
End If
Next
If lCount = 0 Then sWhere = "Not Found"
FindDuplicates = Replace(sWhere, "$", "") ' Removes the $ sign in Addresses
End Function
So, you have 3 problems:
1) Take value from another worksheet:
Dim startDate As Date
startDate = ActiveWorkbook.worksheets("OtherSheetName").cells(row,col).Value
2) Compare data:
If startDate <= actualDate And actualDate <= endDate Then
...
Else
...
End If
3) Set cell value:
ActiveWorkbook.worksheets("SheetName").cells(row,col).Value = something
Combine those steps and you'll obtain a solution for your problem.
I kinda liked this question...
The way I would do it would be using the SUMPRODUCT() function to check for multiple criteria (there are many references both on this site and Google explaining how that works)
In your New worksheet, assuming the first row is for headers, in cell C2 put in the following formula:
=SUMPRODUCT(--(Sheet1!$A$2:$A$180=A2),--((B2-Sheet1!$B$2:$B$180)>=0),--((Sheet1!$C$2:$C$180-B2)>=0)) > 0
And drag it down for your entire range (Obviously, adjusting the 180 row reference to suit your data-set)
Basically, what you're saying is:
Give me a TRUE only if there is at least one row in my other sheet where:
- The IDs match
- [My Date] minus row's [Start date] >= 0
- Row's [End Date] - [My Date] >= 0
Hope that makes sense!

Excel: Enter dates in specific column

My requirement is:
I want to enter date from 1st Jan to 31st Jan in columns E5 to AI5. Currently using the below code which is not working.
Secondly year i m taking as user input which should change every time.
Sub LoopA()
Call Using_InputBox_Method
Dim i As Integer
Dim j As Integer
Dim PH As Integer
i = 5
For j = 5 To 35
Cells(i, j).Value = "=Date(E1,1,j)"
Next j
End Sub
Public Function Using_InputBox_Method() As Integer
Dim Response As Integer
' Run the Input Box.
Response = Application.InputBox("Enter a Year.", _
"Number Entry", , 250, 75, "", , 1)
' Check to see if Cancel was pressed.
If Response <> False Then
' If not, write the number to the first cell in the first sheet.
Worksheets(1).Range("E1").Value = Response
End If
Using_InputBox_Method = Response
End Function
A)
Anything within " will be considered as a String. So "=Date(E1,1,j)" is just a string. What you want, I guess is
"=Date(E1,1," & j & ")"
B)
For j = 5 To 35
Are you sure you want to go up till 35? The max you can have in any month is 31 :)
Syntax of =Date() is DATE(year,month,day)
Also you would need an additional check here to see if it is a valid date. For example 30th Feb will give you an error.
C)
InputBox should be avoided to accept dates. It can generate errors. You may want to use THIS. If you still want to use InputBox then you will have to do validations to ensure that there are no errors.
D)
Regarding, the Year changing automatically, You will have to increment the Year in Column E once the user automatically enters the date.
Is this what you are trying?
Sub Sample()
Dim Yr As Long, i As Long
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
Yr = Application.InputBox("Enter a Year.", _
"Number Entry", , 250, 75, "", , 1)
'~~> Set it to whatever Year Range you want
If Yr < 1900 Or Yr > 9999 Then
MsgBox "Incorrect Year"
Exit Sub
End If
With ws
.Range("E1").Value = Yr
For i = 5 To 35
.Cells(5, i).Formula = "=Date(E1,1," & (i - 4) & ")"
Next i
End With
End Sub

How to solve mixed date formats

I'm trying to write VBA code in Excel 2010 to make some time calculations. Everything is working as I want BUT for the cell date format. The Excel Sheets were created by merging several .xlsx files generated by different PCs and a Hardware Data-logger. The problem is that some sheets had the date as mm/dd/yy hh:mm:ss AM/PM and others dd/mm/yy hh:mm:ss AM/PM, with both mixed in one file.
I tried to change everything to Selection.NumberFormat = "dd/mm/yy hh:mm;#" but some cells just don't change. I also tried this function:
Function Arreglar_Fecha()
Dim temp As String
temp = ""
Do While ActiveCell.Value <> ""
temp = ActiveCell.Value
ActiveCell.Value = Day(temp) & "/" & Month(temp) & "/" & Year(temp) & " " & Hour(temp) & ":" & Minute(temp)
ActiveCell.Offset(1, 0).Select
Loop
End Function
But still, some cells changed, some did not. And what is worse, some get the day and month mixed!
I have access to some of the original .xlsx files and in there also wasn't able to change all the date formats.
Anyone have any idea how I can fix this?
EDIT Here I got permission for put an original Excel file Excel Data.
You will have to trace back to your source data. There is no way Excel itself knows whether 1/2/2014 for example should be the first of February or the second of January, only that it is either 41671 or 41641.
Edit In your second example, clearly 28/9/2013 17:59 is September 28. If 10/01/13 12:11:00 PM had the same formatting (perhaps came from the same file) then it is January 10. But if the formatting was different then it could be October 1. If you are seeing AMs and PMs with formatting as dd/mm/yy hh:mm;# then some of your data is text and there is no reliable 'automatic' way to switch this to a date/time serial number without knowing the text convention (ie whether DMY or MDY), hence the need to revert to source.
Obviously 'day' values greater than 12 are actually months but that does not help much when for less than 13 it depends upon the formatting.
In addition, given your various sources, there is a risk that both the 1900 and the 1904 conventions might have been used and even possibly others also (your data logger might be on UNIX time, which starts in 1970).
ActiveCell.Value = Day(temp) & "/" & Month(temp) & "/" & Year(temp) & " " & Hour(temp) & ":" & Minute(temp)
Maybe the code is reading it as text? Try this (UNTESTED)
Sub Arreglar_Fecha()
Dim temp As String, Tmp As String
Dim D As String, M As String, Y As String
Dim H As String, Mn As String
Do While ActiveCell.Value <> ""
temp = Trim(ActiveCell.Value)
D = Trim(Split(temp, "/")(0))
M = Trim(Split(temp, "/")(1))
Tmp = Trim(Split(temp, "/")(2))
Y = Trim(Split(Tmp, " ")(0))
Tmp = Trim(Split(Tmp, " ")(1))
H = Trim(Split(Tmp, ":")(0))
Mn = Trim(Split(Tmp, ":")(1))
ActiveCell.Value = Format(DateSerial(Val(Y), Val(M), Val(D)) & _
" " & TimeSerial(Val(H), Val(Mn), 0), _
"dd/mm/yy hh:mm;#")
ActiveCell.Offset(1, 0).Select
Loop
End Sub
Trying it with a single test scenario works. The below gives you 13/08/13 05:31
Sub Test()
Dim temp As String, Tmp As String
Dim D As String, M As String, Y As String
Dim H As String, Mn As String
temp = "13/8/2013 5:31"
D = Trim(Split(temp, "/")(0))
M = Trim(Split(temp, "/")(1))
Tmp = Trim(Split(temp, "/")(2))
Y = Trim(Split(Tmp, " ")(0))
Tmp = Trim(Split(Tmp, " ")(1))
H = Trim(Split(Tmp, ":")(0))
Mn = Trim(Split(Tmp, ":")(1))
Debug.Print Format(DateSerial(Val(Y), Val(M), Val(D)) & _
" " & TimeSerial(Val(H), Val(Mn), 0), _
"dd/mm/yy hh:mm;#")
End Sub