How do I calculate hours when there are fractional components - vba

I am trying to make a spreadsheet to calculate my hours for the week. The problem I am having is the DateDiff function only returns integers so I am getting incorrect results.
Function CalculateHoursDay(strStart As String, strEnd As String, intLunch As Integer) As Double
Dim dblHours As Double
dblHours = DateDiff("h", strStart, DateAdd("h", 12, strEnd)) - intLunch
CalculateHoursDay = dblHours
End Function
If I call the function with "7:00", "2:45", .5 then I get 7 back when I need 7.25. In fact I get 7 also for "7:00", "2:45", 0 so you can see the decimal value is getting truncated. I add 12 hours so times can be easily entered without regard for AM, PM or military time since over 99% (if not 100%) of the time the times will be AM for start time and PM for end time.
I am expecting to get 7.25 from the function with the stated parameters. Any suggestions?

Alex provided the correct answer and tbur also pointed out a bug that would've surfaced as soon as I applied Alex's solution.
Function CalculateHoursDay(strStart As String, strEnd As String, dblLunch As Double) As Double
Dim dblHours As Double
'"n" refers to minutes.
dblHours = (DateDiff("n", strStart, DateAdd("h", 12, strEnd)) / 60) - dblLunch
CalculateHoursDay = dblHours
End Function

Related

Format timespan with hours going above 24 instead of using a days part

In Visual Basic .NET, I am trying to convert time elapsed in seconds to hh:mm:ss format which can go over 24 hours.
As an example, when try to convert 86400 seconds to a timespan with the following code:
dim sec = 86400
dim res = TimeSpan.FromSeconds(sec).ToString
the output is "1.00:00:00"
while the desired output for me is "24:00:00"
How is it possible to do so?
You just need to do the calculation:
Dim secs = 86400
Dim ts = TimeSpan.FromSeconds(secs)
Dim res = String.Format("{0}:{1:00}:{2:00}", (ts.Days * 24 + ts.Hours), ts.Minutes, ts.Seconds)
Console.WriteLine(res)
Outputs:
24:00:00
If you want at least two digits for the hours, use "{0:00}:{1:00}:{2:00}".
(You could use String.Format("{0}:{1:00}:{2:00}", Math.Floor(ts.TotalHours), ts.Minutes, ts.Seconds) if you think it looks better.)
What you ask isn't available either as a standard or custom format.
You want to emit the total hours as a rounded-down integer followed by the minutes and seconds part. The custom format specifiers only work for specific parts.
You can use the TimeSpan.TotalHours property to retrieve the hours as a double and format the rest of the string using custom specifiers.
Dim ts=TimeSpan.FromDays(1)
String.Format("{0:00}:{1:mm\:ss}",Math.Floor(ts.TotalHours), ts)
Or
Dim ts=TimeSpan.FromSeconds(61)
String.Format("{0:00}:{1:mm\:ss}",Math.Floor(ts.TotalHours), ts)

VBA Double to Time

I have a VBA user form which when I press save after input data it writes to a spread sheet and set my time and date to Now() that is being written to a spread sheet where I split the now() value into time and date, when I read it back I get the date but my time in decimal format I would like to convert that decimal number back to time what is the formula for that thank you in advance my decimal number is something like '0.12425576899' so far I found this but it converts that to '00:the minutes on the double:00' here is the code
Public Function FromDecimalTime(ByVal t As Double) As Date
FromDecimalTime = TimeSerial(0, Fix(t), (t - Fix(t)) * 60)
End Function
The following will convert the Double into the Date Format. In order to only show the time portion of the date format, use the Format function:
Sub trythis()
Dim t As Double
t = Now()
Debug.Print CDate(t)
Debug.Print Format(t, "hh:mm:ss")
End Sub

TimeZone issue with Windows 7

When a date is returned from our UTC server database Windows very helpfully changes the Date to a DateTime (say 23 June 2017 to 2017-06-23 00:00) and then makes an adjustment for the current Time Zone (to, say 2017-06-22 16:00)... For years we have used the code below to convert it back...
About four months ago users running Windows 7 (Windows 10 doesn't seem to be effected) that did not have 'Automatically adjust clock for Daylight Saving Time' checked, or live in a state like Arizona where that option is not available, noticed that reports where returning the date from the day before - our DB is returning the correct date but the conversion is no longer working correctly..
Any suggestions?
Thanks
Public Function LocalDateFormat(ByVal InputDate As Date) As String
Dim vDate As String = InputDate.ToString("d", System.Globalization.CultureInfo.InvariantCulture)
Dim LocalZone As TimeZone = TimeZone.CurrentTimeZone
Dim CurrentOffset As TimeSpan = LocalZone.GetUtcOffset(InputDate)
Dim DayLightSaving As Boolean = LocalZone.IsDaylightSavingTime(InputDate)
Dim CalculatedOffset As New DateTime(InputDate.Ticks, DateTimeKind.Local)
If CurrentOffset.CompareTo(TimeSpan.Zero) < 0 Then
CalculatedOffset -= LocalZone.GetUtcOffset(InputDate)
If DayLightSaving = True Then
CalculatedOffset = CalculatedOffset.AddHours(1)
End If
Else
CalculatedOffset += LocalZone.GetUtcOffset(InputDate)
If DayLightSaving = True Then
CalculatedOffset = CalculatedOffset.AddHours(-1)
End If
End If
InputDate = CalculatedOffset
Dim vCulture As String = System.Globalization.CultureInfo.CurrentCulture.ToString
Dim vReturnDate As String = ""
Select Case vCulture
Case "en-US"
vReturnDate = Format(InputDate, "MM/dd/yyyy")
Case "en-GB"
vReturnDate = Format(InputDate, "dd/MM/yyyy")
Case Else
vReturnDate = Format(InputDate, "dd/MM/yyyy")
End Select
Return vReturnDate
End Function
Your entire function can be re-written as:
Public Function LocalDateFormat(ByVal InputDate As Date) As String
Return InputDate.ToLocalTime().ToShortDateString()
End Function
With the code so small, you may want to rethink whether there's any benefit of having this in a function at all.
As far as why your existing function didn't work properly:
TimeZone.GetUtcOffset already factors in whether DST is in effect or not, though it has some problems with that that are described in the MSDN docs.
You shouldn't be using TimeZone any more at all. If you need to work with time zones, use TimeZoneInfo, or Noda Time. In this case, you don't need either of them, as you're just converting from UTC to local time, which is what DateTime.ToLocalTime does.
This code has some logic that appears to think one should subtract an hour for DST if the offset is positive. Sorry, but DST doesn't work that way - you always add an hour (unless you are in Lord Howe Island, Australia, where you add 30 minutes).
It also seems to think you should add the offset from UTC if its positive and subtract it if its negative. This is essentially an absolute value function, and also incorrect.
All the culture stuff is handled for you by formatting within the current culture. There's no need to do it by hand.
You say it works fine on Windows 10, but I'm sorry - this is bad code that will have errors wherever it is run. Just use the built-in APIs provided by the framework. Don't reinvent the wheel. :)

Values vary after conversion from excel

I am trying to get values from one of the column from excel and i am facing strange issue that i cannot overcome so far.
Excel cells text we working on
Column format is set to: [h]:mm:ss so means hours could exceed 12/24.
When i am getting that values they are in double format as excel probbaly stores it in that way therefore i decided to write function to convert it back again to hours, minutes and seconds so i did that function:
Public Shared Function parseExcelHour(cellInput As String) As String
Dim excelHour As Double = 0
Dim hour As Integer
Dim min As Integer
Dim sec As Integer
Try
excelHour = [Double].Parse(cellInput)
Catch
End Try
sec = CInt((excelHour * 1440 * 60) Mod 60)
min = CInt((excelHour * 1440) Mod 60)
'mod (%) takes only the remainder as an int (if 5/4 = 1.25 , % only takes the number 1 that cannot be divided into an integer)
hour = CInt((excelHour * 1440) / 60)
' with the int cast you get only an integer.
Return hour & ":" & min & ":" & sec
End Function
However when i see the results, they are vary between excel and what i get after conversion. For three of them hours are either -1 or +1 if you compare. Also in one case we have additional + 1 minute. I suppose there is wrong hour calculation but i could be in wrong. See on screeshoot:
Results
Does anyone knows why i got those differences? Is that because i am missing something within my method or something else.
Excel stores a full Datetime equivalent as one double. The part before the decimal point is the days (since 1.1.1900; 1.1.1904 on Mac; note the bug that 1900 is faultily cosnider a leap year in Excel).
The part after is the time of the day, wich is what you apparently want:
https://stackoverflow.com/a/981865/3346583
What you are seeing in excel is meerely a ToString Foramting of the double value. Same way DateTime.ToString() would give you a string representation of whatever value is actually stored (most often a realy big unsigned int, with the ticks since a date).
A difference in full hours sounds like it might be a Timezone issue. But I am not aware that Excel stores Timezones in the first place (or what default timezone it asumes).

How to calculate elapsed time in seconds in VBA?

I have 2 strings, strStartTime and strEndTime.
strStartTime = "12:32:54"
strEndTime = "12:33:05"
I want to find out how many seconds elapsed between strStartTime and strEndTime so I did this:
Dim dtDuration as date
dtDuration = DateDiff("s", CDate(strStartTime), CDate(strEndTime))
The result I get is dtDuration = "#1/10/1900#" in the Locals watch window.
Why does this happen? How do I get dtDuration to equal 11 for the 11 seconds that elapsed between the start and end times?
Just change variable type to Long:
Dim dtDuration as Long
VBA converts numerical results of DateDiff functions into variable with date type.