vba excel datediff calculates different results - excel

I am trying to calculate time difference in hours between two times.
But i am not getting the exspected result, as a matter of fact the same function
throws me two different results.
time between 14:22:00 and 22:57:48 should come as 8 hours 35 minutes and 48 sec.
However i get two different numbers.
If i store the value as a date i get 14:19:12
If i calculate in a msgbox on the go i get 8,5966...
Neither is correct, or maybe it is using some sort of format i am unaware of.
Screenshot shows both the msgbox and the storage test.
Also posted in exspected result.
Any suggestions?
Public Sub DDtest()
Dim EDay As Date
Dim ETime As Date
Dim DtgA As Date
EDay = Format(CDate(Replace(Worksheets("Data2020").Range("E2").Value, ".", "/")), "dd-mmm-yyyy")
ETime = Format(Worksheets("Data2020").Range("F2"), "hh:mm:ss")
DtgA = EDay + ETime
Dim EDay2 As Date
Dim ETime2 As Date
Dim DtgB As Date
EDay2 = Format(CDate(Replace(Worksheets("Data2020").Range("E3").Value, ".", "/")), "dd-mmm-yyyy")
ETime2 = Format(Worksheets("Data2020").Range("F3"), "hh:mm:ss")
DtgB = EDay2 + ETime2
Dim result As Date
result = Format(DateDiff("s", DtgA, DtgB) / (60 * 60), "hh:mm:ss")
MsgBox "Date 1:" & DtgA & vbNewLine & "Date 2:" & DtgB & vbNewLine & vbNewLine & DateDiff("s", DtgA, DtgB) / (60 * 60) & vbNewLine & result
End Sub

DateDiff("s", DtgA, DtgB) / (60 * 60) will return a decimal value, in this case is 8.59666666666667 hours
When you apply Format to convert it into hh:mm:ss, the value 8.59666666666667 is not being treated as hours. Excel thinks it's a decimal value that must be converted into date, and it's being treated as days.
In Excel, Dates are always numbers. Integer part is the date itself, while decimal part is time, a part of that day but not the day itself.
First day Excel can use is 01/01/1900 and numeric value is 1, 2 is 02/01/1900 and so on.
So Excel thinks 8.59666666666667 is 08/01/1900 14:19:12
If you divide those hours between 24, you will get the right result:
result = Format(DateDiff("s", DtgA, DtgB) / (60 * 60) / 24, "hh:mm:ss")
You get this:
Note the first value is decimal value and the second one is formatted as hh:mm:ss. But both of them show the same value, with different format.
UPDATE: Actually, if you force your dates values to make a difference of 8 and a half hours exactly, you will see perfectly how Excel works. Same value but with different format.
I've forced dates to be 12/12/2019 14:22:00 and 12/12/2019 22:52:00 and I get this:
Exactly 8 hours and a half, but first in decimal and second is format hh:mm:ss.

Why are you formatting before calculation? If cell value is date formatting doesn't meter.
date & time = 441040.598611111111111
Sub calcDatediff()
date1 = Worksheets("masterdata").Range("C11")
time1 = Worksheets("masterdata").Range("D11")
date2 = Worksheets("masterdata").Range("C12")
time2 = Worksheets("masterdata").Range("D12")
dtime1 = date1 + time1
dtime2 = date2 + time2
difftime = Format(dtime2 - dtime1, "HH:mm:ss", vbMonday, vbFirstFourDays)
End Sub

Related

Why is Worksheet SUMIFS function returning zero when compared against certain dates, but not others?

In the code below, we are searching through a list of roughly 500 items to identify which items belong to Rate A and Rate B, then adding the number of hours recorded to a running total.
We also want to set an arbitrary number of rate periods - dividing the total period (approximately 2018 to 2021) into blocks. The first block will always begin before the start of the data and the last block will go right to the end of it.
Rate Period Dialog has a DTPicker object on it.
When we have only 1 rate period, the totals are summed correctly.
When we have 2 rate periods and the date is entered (for example) as 01/01/2020, the first rate period totals appear to be calculated correctly, but the second is lower than it should be.
When we have 2 rate periods and a different date is entered using the DTPicker, where the day is greater than 12 (for example - 13/01/2020), all rate periods are shown as zero for both Rate A and Rate B.
UK date format (dd/mm/yyyy) applies, though this will hopefully not matter (using DTPicker and DateSerial)
Public rateAHours() As Single
Public rateBHours() As Single
Sub DebugSumRateData()
'Define ranges for Work Hours (sumRange), column with rate data (A, B or otherwise) and column with date of work
Dim sumRange As Range
Dim rateRange As Range
Dim dateRange As Range
Dim periodStartDate As Date
Dim periodEndDate As Date
Set dateRange = Range("Data!B:B")
Set rateRange = Range("Data!E:E")
Set sumRange = Range("Data!F:F")
'Setup dates for rate period
numberOfRatePeriods = InputBox("How many rate periods apply to this schedule?", "Number of Rate Periods")
'Set all arrays to the size required for the number of rate periods
ReDim endDates(numberOfRatePeriods) As Date
ReDim rateAHours(numberOfRatePeriods) As Single
ReDim rateBHours(numberOfRatePeriods) As Single
If (numberOfRatePeriods > 1) Then
For i = 1 To numberOfRatePeriods - 1
RatePeriodDialog.DatePromptLabel = "Please enter end date of rate period " & i
RatePeriodDialog.Show
endDates(i) = ratePeriodInputDate
Next i
End If
'Final rate period is until end of time (or near enough)
endDates(numberOfRatePeriods) = DateSerial(9999, 1, 1)
periodStartDate = DateSerial(1900, 1, 1)
For i = 1 To UBound(endDates)
periodEndDate = endDates(i)
MsgBox "Start of loop " & i & " - Start Date is " & periodStartDate & " End Date is " & periodEndDate
rateAHours(i) = WorksheetFunction.SumIfs(sumRange, rateRange, "A", dateRange, ">=" & periodStartDate, dateRange, "<=" & periodEndDate)
rateBHours(i) = WorksheetFunction.SumIfs(sumRange, rateRange, "B", dateRange, ">=" & periodStartDate, dateRange, "<=" & periodEndDate)
periodStartDate = DateAdd("d", 1, periodEndDate)
MsgBox "End of loop " & i & " - Start Date is " & periodStartDate & " End Date is " & periodEndDate
'Debug Message Box - shows all rate totals for this loop
MsgBox rateAHours(i) & vbCrLf & _
rateBHours(i) & vbCrLf
Next i
End Sub
When entering 1 rate period, all work hours at Rate A and Rate B are calculated and stored in the array
(Debug Message Box at end of Loop reports 75.5 and 7.2)
When using 2 or more rate periods, work hours are inconsistent - some missing in each case
(2 rate periods, date for end of Rate Period of 01/01/2020 - Debug Message Box reports 49 and 7.1 (1st Loop) & 22.3 and 0.1 (2nd Loop))
Where the day element of an "end of period" date is greater than 12, all sums return as 0 from WorksheetFunction.SumIfs (checked in VBA debug stepthrough)
(2 rate periods, date for end of Rate Period of 13/01/2020 - Debug Message Box reports 0 and 0 (both loops)
On the edge case where the end of rate period is 12/01/2020, acts as though all data was before this date (2 rate periods, date for end of Rate Period of 12/01/2020 - Debug Message Box reports 75.5 and 7.2 (1st Loop) & 0 and 0 (2nd Loop) - data continues until Jan 2021, with many items for Rate A and B
Debug messages at start and end confirm correct dates being used for the SumIfs in each loop
You should convert the dates to Long integers using CLng - for example:
rateAHours(i) = WorksheetFunction.SumIfs(sumRange, rateRange, "A", dateRange, ">=" & CLng(periodStartDate), dateRange, "<=" & CLng(periodEndDate))

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 calculate business hours elapsed in excel

I need to calculate business hours elapsed in MS Excel. Here i have two dates, start and End date with respective timings. Some places i might not have end date and timings. Business hours are 7AM EST - 17 PM EST. how can i calculate number of business hours elapsed here ? (Excluding Weekends)
Tried "=IF(ISBLANK(P2),(NETWORKDAYS(O2,NOW())-1)*("17:00"-"7:00")+IF(NETWORKDAYS(NOW(),NOW()),MEDIAN(MOD(NOW(),1),"17:00","7:00"),"17:00")-MEDIAN(NETWORKDAYS(O2,O2)MOD(O2,1),"17:00","7:00"),(NETWORKDAYS(O2,P2)-1)("17:00"-"7:00")+IF(NETWORKDAYS(P2,P2),MEDIAN(MOD(P2,1),"17:00","7:00"),"17:00")-MEDIAN(NETWORKDAYS(O2,O2)*MOD(O2,1),"17:00","7:00"))", need to exclude holidays as well here.
You can split this into three components (fraction of first day, full workdays, fraction of last day)
Lets start with the middle part. Here you can use NETWORKDAYS and subtract the start and end date. I am assuming a start date in A1 and end date in B1. In order to exclude holidays you need to maintain a list of holidays in your sheet. The formula assumes that this list is in range C1:C10. The results is multiplied by 10 as there are 10 hours in your workday.
=MAX((NETWORKDAYS(A1,B1,C1:C10)-NETWORKDAYS(A1,A1,C1:C10)-NETWORKDAYS(B1,B1,C1:C10))*10,0)
For the fractions you will need to determine if the day itself is a holiday, we use the NETWORKDAYS function again as a factor which will be either 0 or 1. Now we only need to determine the hours to add for the day. Depending on the granularity you want you can consider hours, minutes or even seconds. I will use hours and minutes as a fraction (minutes/60 = hours).
For the first day you get
=MAX(17-MAX(HOUR(A1)+MINUTE(A1)/60,10),0)*NETWORKDAYS(A1,A1,C1:C10)
For the last day you get
=MAX(MIN(HOUR(B1)+MINUTE(B1)/60,17)-10,0)*NETWORKDAYS(B1,B1,C1:C10)
Putting it all together leaves us with:
=MAX(17-MAX(HOUR(A1)+MINUTE(A1)/60,10),0)*NETWORKDAYS(A1,A1,C1:C10)+MAX((NETWORKDAYS(A1,B1,C1:C10)-NETWORKDAYS(A1,A1,C1:C10)-NETWORKDAYS(B1,B1,C1:C10))*10,0)+MAX(MIN(HOUR(B1)+MINUTE(B1)/60,10)-10,0)*NETWORKDAYS(B1,B1,C1:C10)
I believe this UDF will do what you need.
It calculates the hours and returns it as a float, then you need to multiply that with 24 to get the hours.
Function workhours(startdate As Date, enddate As Date)
Opentime = "7:00"
Closetime = "17:00"
Fulldays = Int(enddate - startdate) - 1
DayOneHours = CDate(Year(startdate) & "-" & Month(startdate) & "-" & Day(startdate) & " " & Closetime) - startdate
BeforeOpen = CDate(Year(startdate) & "-" & Month(startdate) & "-" & Day(startdate) & " " & Opentime) - startdate
HoursDayOne = DayOneHours - BeforeOpen
If enddate < CDate(Year(enddate) & "-" & Month(enddate) & "-" & Day(enddate) & " " & Opentime) Then
HoursLastDay = 0
Else
HoursLastDay = enddate - CDate(Year(enddate) & "-" & Month(enddate) & "-" & Day(enddate) & " " & Opentime)
End If
workhours = Fulldays * (CDate(Closetime) - CDate(Opentime)) + HoursDayOne + HoursLastDay
End Function
Use it in Excel like:
=workhours(A1,B1)*24

Compare two dates but with current year

I have a cell with a date:
30/04/1991
I need to make a compare with today's date, but with day and month of that cell, but with current year. But it isn't working.
I have the following:
MsgBox Format(Day(cell.Value) & "/" & Month(cell.Value) & "/" & Year(Now), "dd/mm/yyyy") < Format(Now, "dd/mm/yyyy")
The result is "30/04/2017 < 01/05/2017"
But msgbox result is "False". Which is wrong, given today's date as "01/05/2017"
What am I doing wrong?
To avoid issues with February 29th, you can compare just the month and date:
MsgBox Format(cell, "mmdd") < Format(Now, "mmdd")
Update
DatePart("y", Date) can be used to get the Day of year:
MsgBox DatePart("y", cell) < DatePart("y", Now)
Debug.Print DatePart("y", "2 28") // 59
Debug.Print DatePart("y", "2 29 16") // 60
I would recommend using DateDiff fuinction.
You can use Date instead of Now since you only need the date, and not the time.
If you use DateDiff you can keep the 2 values as Date variable, and instead of using DateValue with some & and "/", you can have a shorter and cleaner version DateSerial(Year(Date), Month(cell.Value), Day(cell.Value)).
Code:
MsgBox DateDiff("d", DateSerial(Year(Date), Month(cell.Value), Day(cell.Value)), Date) > 1
If you want to get also the number of days between these 2 dates:
MsgBox DateDiff("d", DateSerial(Year(Date), Month(cell.Value), Day(cell.Value)), Date)
I solved by myself with
MsgBox DateValue(Day(cell.Value) & "/" & Month(cell.Value) & "/" & Year(Now)) < DateValue(Date)

Working with dates in Visual Basic / Excel

Very new to working with Visual Basic / Excel. I am trying to write a quick script that enters the current time in one column, and allows the user to enter how many days/hours/minutes will pass until a new time, and output that in another column.
I'm sure this isn't the best way to do it, but what I have so far is the following. I have given up on fiddling with dates, and am just working with the time:
Sub TimeModule()
Dim DaysLeft, HoursLeft, MinutesLeft As Double
DaysLeft = Val(InputBox("Days left"))
HoursLeft = Val(InputBox("Hours left"))
MinutesLeft = Val(InputBox("Minutes left"))
Dim CurrentTime As Date
CurrentTime = TimeValue(Now())
ActiveCell.Value = CurrentTime
ActiveCell.Offset(0, 1) = CurrentTime + Time(HoursLeft, MinutesLeft, 0)
End Sub
I am getting an error, of course. If anyone could shed some light on a better way to do this, along with the functions I'm misusing, I would really appreciate it!
Edit: I would, of course ultimately like for the script to handle days as well.
I think this is possible just using cell functions in Excel, if I've understood you correctly.
For example, this is what you'd see...
Time Now: Days: Hours: Minutes: New Time:
30/05/2012 23:34 15 6 23 15/06/2012 05:57
...and this is what is in each cell (assuming top-left cell is A1)...
Time Now: Days: Hours: Minutes: New Time:
=NOW() 15 6 23 =A2+B2+TIME(C2,D2,0)
Describing each function:
NOW() returns the current date and time formatted as a date and time.
DATE(year,month,day) returns the number that represents the date in MS Excel date-time code.
TIME(hours,minutes,seconds) converts hours, minutes, and seconds given as numbers to an Excel serial number, formatted with a time format.
Dissecting the equation in the last cell:
A2 is the cell containing the current date/time (as of last worksheet calculation).
B2 is the user-inputted value for days.
TIME(C2,D2,0) is the TIME() function, taking the user-inputted values for hours and minutes from cells C2 and D2 respectively.
Is this anything like your intended functionality...?
If you want to use VBA the only issue with your code is the "Time" function.
You can use CDate instead :
Sub TimeModule()
Dim DaysLeft, HoursLeft, MinutesLeft As Double
DaysLeft = Val(InputBox("Days left"))
HoursLeft = Val(InputBox("Hours left"))
MinutesLeft = Val(InputBox("Minutes left"))
Dim CurrentTime As Date
CurrentTime = TimeValue(Now())
ActiveCell.Value = Now()
ActiveCell.Offset(0, 1) = ActiveCell.Value + DaysLeft + CDate(HoursLeft & ":" & MinutesLeft)
'ActiveCell.Offset(0, 1) = CurrentTime + Time(HoursLeft, MinutesLeft, 0)
End Sub
When you 'Dim' in that fashion, you have to record the data type for each variable. The way you have it MinutesLeft is a Double and everything is (by default) a Variant.
The Time function you're looking for is TimeSerial.
Dates are stored as the number of days since a certain date. To add days to a date, you can simply add the numbers together.
Sub TimeModule()
Dim lDaysLeft As Long
Dim lHoursLeft As Long
Dim lMinutesLeft As Double
Dim dtCurrent As Date
lDaysLeft = Val(InputBox("Days left"))
lHoursLeft = Val(InputBox("Hours left"))
lMinutesLeft = Val(InputBox("Minutes left"))
dtCurrent = Now()
ActiveCell.Value = dtCurrent
ActiveCell.Offset(0, 1).Value = dtCurrent + lDaysLeft + TimeSerial(lHoursLeft, lMinutesLeft, 0)
End Sub

Resources