Values vary after conversion from excel - vb.net

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).

Related

Declare either .Text, .Value, .Value2 using array to fetch data from excel

I use Excel automation with Excel interop. My code takes excel sheet using array fetch. As you can see below i take all of them as .Value2, however i would like to specify for example that one of my excel column to be taken as .Text. How to achieve that?
'Convert from interop object to native vb.net object, indexed 1 to length
Dim data As Object(,) = DirectCast(_xlWorkSheet.UsedRange.Value2, Object(,))
For row As Integer = 2 To data.GetUpperBound(0) - 1
Dim newDataRow As DataRow = dt.NewRow()
Dim dattime As DateTime = DateTime.FromOADate(data(row, 11))
Next
i have one column in excel that has format: [h]:mm:ss means hours could exceed clock hour so means there could be e.g 783:34:12. When i tried to use formatting for instance: NumberFormat ="#" or whatever else i always got wrong result.
Based on that description, I will assume that the value is entered into Excel as a numeric Double. i.e.: 783:34:13 equals 32.64875.
In VB.Net, you could generate a TimeSpan structure to yield the resulting day, hour, minutes and seconds components. In the following, rng is an Excel.Range representing a single cell.
Dim val As Double = CDbl(rng.Value2)
Dim ts As TimeSpan = DateTime.FromOADate(val) - DateTime.FromOADate(0)
Now if you wanted to format this TimeSpan as a string similar to that displayed in Excel, you could do something like this:
Dim s As String = String.Format("{0}:{1:00}:{2:00}", (ts.Days * 24) + ts.Hours, ts.Minutes, ts.Seconds)
The reason I am recommending this technique over getting the Text property of an Excel.Range is that the Text property will return exactly what you would see in Excel including the ever helpful "###" when the column is not wide enough to display the formatted value.
Edit To Address Comments:
can you explain why you do this: - DateTime.FromOADate(0) ?
Excel encodes a date-time value as a decimal number of 24 hour periods (days) from a specified date-time that has the value of zero. Your data is apparently making use of this fact to allow you to have a cell with a value of 783:34:12 (783 hours, 34 minutes, 12 seconds) or 32.64875 as a decimal value.
In order to retrieve the offset in (days, hours, minutes, seconds) the original value represents, you need to subtract date-time represented by its basis value (zero).
i saw mismatch e.g: in excel : 0:04:07 (real value behind is: 12:04:07 AM) after your function i get this: 0:04:06 (so 1 sec diffrence why is that?
I can not reproduce this issue. It is likely a rounding issue due too the limitations of floating point value representation.
Also, be advised that Excel supports two different date basis systems; the 1900 Date System (the default) and the 1904 Date System. The DateTime.FromOADate function does its conversion based on the 1900 Date System. The difference between these two systems is the date that is treated as zero. You should check the WorkBook.Date1904 property to see if you need to add addition days (1462 days) to the value retrieved from Excel when converting to a .Net DateTime.
This could effect the result of this code:
Dim dattime As DateTime = DateTime.FromOADate(data(row, 11))
See: Differences between the 1900 and the 1904 date system in Excel for more information.

How to convert double to minutes in VB

I am currently storing a value as a double but will need to have the value converted to minutes for another function.
For example 1 would convert to 60 (minutes), 3,5 would equal to 210.
I tried timespan but I'm not getting what I am looking for.
TimeSpan.TotalMinutes should give you what you're looking for:
Dim minutes = TimeSpan.FromHours(hours).TotalMinutes

Convert Float value to Hours and Minutes

I have a float value, for example 0.999888, which I am getting from SQL database.
I have a variable in vbscript assigned to the float value from the SQL.
Lets say that I have this, for example
Dim TimeInFloat
TimeInFloat = 0.999888
I want to convert it to the hours and minutes either in SQL itself or VBscript.
Any suggestions?
You can convert it to hours and minutes in SQL by doing:
select floor(TimeInFloat * 24) as hours,
60 * (TimeInFloat * 24 - floor(TimeInFloat * 24)) as minutes
You can do similar logic in VBA.
In VBScript the CDate function allows you to convert float/double to time:
>>> WScript.Echo CDate(0.999888)
23:59:50

How do I calculate hours when there are fractional components

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

Problems format time in VBA

I have a big problem with formats.
When VBA write a number (Time() - actualtime) the result is in seconds, but excel thinks that is days!
so, when i put this command:
Selection.NumberFormat = "[h]:mm:ss.00" the seconds are days!
A solution is adding /(24*3600) but this is give me an error, perhaps because it is a really small number.
ActiveCell = ((Timer() - startTime) where starttime is a Timer() define some time back.
Try
Timeserial(0,0,Time() - actualtime)
Timeserial will return a time value, and putting the value you want in the "seconds" argument will ensure the number returned is treated correctly.
Let me know if this helps!
It's due to the type conversion. Because 3600 and 24 are both integers, the result of multiplying them are converted to a 16 bit Int. A 16 bit Int has a range of -32768 to +32767
You can see this by running the following line which does not give an error and returns back 32767
MsgBox 32766 + 1
Whereas the below line fails with an overflow error due to it exceeding the maximum value.
MsgBox 32766 + 2
As it has already been mentioned, you can circumvent this by dividing the values separately
(Time() - actualtime) / 3600 / 24 will work..
Also, you can tell excel that you want to use a different type,
MsgBox (Time() - actualtime) / (24# * 3600) 'use Double
MsgBox (Time() - actualtime) / (24& * 3600) 'use Long
The hash or ampersand after the 24 tells excel that you want the value to be used as a defined type and these statements won't fail with an overflow error.
Fascinating.
actualtime / (24 * 3600)
produces an overflow error in VBA.
actualtime / 24 / 3600
does not.
Something about multiplying 24 and 3600 produces an overflow error.