VBA - Decimal to Time - Wrong value no matter what - vba

I'm new to this community so please critizise all you want on this post, if I make any rookie mistakes.
I'm currently stuck on an assignment. I am somewhat new to visual basic, but my understanding so far has been fairly good. However, I can not seem to get a basic operation sorted. I have searched the internet and bashed my head against the wall for a day or two now. Hopefully, you can help me sort this out.
I am importing cycle times for different orders from an Oracle database. They are returned into a column as decimal values (number of hours), but I want them shown as "Long Time" ("hh:mm:ss"). What I have read, it should be as easy as dividing by 24 and changing the format. However, this gives the wrong values when using the cell values.
Example:
? ws.Cells(i,6) 'Cycle times are placed in column 6 while looping over rows i.
0.050000
? Format(0.050000/24, "hh:mm:ss")
00:03:00
but
? Format(ws.Cells(i,6)/24,"hh:mm:ss")
08:00:00
which is clearly not correct. 0.05 hours equivalent to 3 minutes. I have observed a change in date value as well:
? Format(0.05/24, "yyyy-mm-dd hh:mm:ss")
1899-12-30 00:03:00
? Format(ws.Cells(i,6)/24, "yyyy-mm-dd hh:mm:ss")
1905-09-13 08:00:00
How is this even possible!? ws.Cells(i,6) returns 0.05. Despite this Format(ws.Cells(i,6)/24,"hh:mm:ss")≠Format(0.05/24,"hh:mm:ss")
I've tried messing around with number formats (strings, dates etc.), .Value and .Value2
Does anybody know what is going on and why this is happening?
Thank you for your time in advance.

The problem seems to stem from the fact that your computer is thinking that 0.050000 is not actually the number 0.05 but is actually a string that is being interpreted as 50000.
Thus, when you divide 50000 by 12, you get the date/time of 1905-09-13 08:00:00 which is then displayed as just 08:00:00 when a "hh:mm:ss" format is used.
I think you need to check your date/time & language settings to ensure that they are set correctly.

Related

VB.NET CDate lops off 12:00:00 AM from 1/19/2021 12:00:00 AM

Environment: VS.Net 2013, .NET FW 4.5.2
We ran into something curious with CDate and datetime strings with midnight as the time.
When we pass in non-midnight datetimes we get
CDate("1/18/2021 9:00:00 PM") returns #1/18/2021 9:00:00 PM#
CDate("1/19/2021 12:01:00 AM") returns #1/19/2021 12:01:00 AM#
These are both expected results.
However when we pass in:
CDate("1/19/2021 12:00:00 AM") it only returns #1/19/2021# without the time portion.
Here's a screenshot of the value at runtime, at a break point: https://www.screencast.com/t/r0WOxWB0GGU
Is there any reason why time is omitted?
Update: this issue is solved in later releases of VS .NET and we have a programmatic workaround for the current environment.
It's purely about how the VB debugger represents DateTime literals. In 24-hour time, 12:00 AM is 00:00, so basically "no time". As such, the debugger just doesn't show the time. That doesn't actually affect what's in the DateTime value though.
As suggested in one of the comments, the behaviour of VS may well have changed, as I'm not seeing that behaviour in VS 2019. Maybe too many people were confused as you are. In that case, your version of VS will likely make that change in representation in code as well as in the debugger. Try writing this code:
Dim dt = #1/1/2000 12:00 AM#
In VS 2019, I typed that and it stayed as it was. I suspect that your version will change that to:
Dim dt = #1/01/2000#
It doesn't matter though. It's just a representation. The data is unaffected.
Whenever you want to display a DateTime value to the user, you must convert it to a String and display that. What format is used in the output is completely within your control, you if you want to include the time then you specify that when you do that conversion. That is completely unrelated to what the VB debugger displays.

How do you show milliseconds on the time on vb

I have a clock on VB and i have got it to say the date and time but it wont show the milliseconds on the time.
time = DateTime.Now.ToString("dd/mm/yy HH:mm:ss")
So I know how to put the hours minuets and seconds but im not sure how to add milliseconds.
Please could someone help.
Thank You
Use fff to represent the milliseconds.
For example:
time = DateTime.Now.ToString("dd/mm/yy HH:mm:ss.fff")
See MSDN for the possible placeholders.

Bizarre DateAdd behavior

Can anyone explain the following results:
?DateAdd("s", 54, 0) = #12:00:54 AM#
True
?DateAdd("s", 55, 0) = #12:00:55 AM#
False
?DateAdd("s", 56, 0) = #12:00:56 AM#
True
UPDATE: Ross Presser's answer provides the what: the difference has to do with the fact that binary fractions cannot always represent decimal fractions. But WHY is the floating point offset different when both expressions evaluate to the same data type?
?TypeName(DateAdd("s", 55, 0))
Date
?TypeName(#12:00:55 AM#)
Date
?VarType(DateAdd("s", 55, 0)) = VarType(#12:00:55 AM#)
True
When I've encountered this sort of floating point artifact in the past, it's usually been because the result was actually two different types, at least at some point during the evaluation. That does not seem to be the case here. I'm still confused.
UPDATE 2: Ross's updated answer provided additional insight into the problem. I've made progress in tracking this down. Each answer seems to raise new questions. It appears that both DateAdd and the date literal are using double precision, but for some reason DateAdd is rounding to 18 decimal places (or perhaps truncating at 19 and not rounding at all):
?CDbl(#12:00:55 AM#) - CDbl(55/86400)
0
?CDbl(DateAdd("s", 55, 0)) - CDbl(55/86400)
-1.0842021724855E-19
?0.000636574074074074 - 0.0006365740740740741
-1.0842021724855E-19
Any ideas why this might be the case?
Date in VBA is expressed as an integer number of days plus a floating point fraction representing the time. Since the time is a float (or perhaps a double), it cannot exactly express every second with perfect precision. 55 seconds is 55/86400, or 0.00063657407 of a day. This is probably not precisely representable in a float.
For more insight, try subtracting the Dateadd value from the literal value, and converting to float.
EDIT: Here's the insight I was talking about:
? cdbl(dateadd("s",55,0)) - cdbl(#12:00:55 AM#)
-1.0842021724855E-19
The parsing algorithm that takes the time literal to a Date structure is apparently doing something different than the dateadd function does, leading to an error in the 19th decimal place. My guess would be that one or the other of these is using Single where it should be using Double. You can call this a bug and report it to Microsoft, I suppose.
EDIT 2: A google search turned up this link where people are talking about the floating point reality beneath VBA's date type. They gave a different example, where the error is in the 17th place instead of the 19th:
? DateAdd("h",2,#8:00#) - #10:00#
-5.55111512312578E-17
And there's also this gentleman who wrote some VBA code to do DateAdd's job more accurately. (The site on that page presents the code in a badly formatted code block that destroys all the newlines, but the code is downloadable.)

find the difference between two datetimes in seconds

I have had a quick look around and I can't find an answer to this in vb.net or something that I can convert into vb.net.
I have two DateTimes in vb.net's 'Date' class. I would like to find the difference between these in seconds. I can do a-b, but the answer will still be a 'date'. I can use .seconds .minutes etc. and multiply but I will hit problems when I come to months.
Is there a simple way to do this, or do I need to write some elaborate-ish code?
Many Thanks
Subtract the DateTime values from each other - the returned type will be a TimeSpan.
Get the TotalSeconds value from it.
(date1 - date2).TotalSeconds
There actually is also a function made for this DateDiff(DateInterval.Second, d1, d2)
How about something like
Dim secs As Double = DateTime.Today.Subtract(DateTime.Today.AddDays(-1)).TotalSeconds
Look at DateTime.Subtract Method (DateTime) and TimeSpan.TotalSeconds Property

How do I nicely display relative time using VB.NET?

I want to display a "date/time submitted" value much the same way as Stack Overflow does
e.g.* 2 hours ago
* 3 days ago
* a month ago
I see extensive answers on how to do this in PHP but can someone help me with the VB version?
First calculate the exact time since submission:
Dim t As TimeSpan = DateTime.Now - submittedTime
Then you decide on what intervals you want to use for displaying the result. For example:
If t.TotalSeconds < 60 Then
display = "Less than a minute ago"
ElseIf t.TotalMinutes < 60 Then
display = t.Minutes.ToString() + " minutes ago"
ElseIf ...
... and so on
End If
All the way back on question 11, Jeff posted the code they use here on stackoverflow. It's in c#, but the conversion to vb.net should be pretty easy. There's heaps of other good suggestions in that question too.
Personally, I've used Sam Allen's code to get prettydates before. He modeled it off some code that John Resig wrote, so it's good quality stuff. Once again, c#, but it converts to vb.net pretty easily.
You need a DateTime structure
you simply substract [now] - [original message time] = difference time
you will need the DateTime.Substract method
What you're looking for is the TimeSpan structure. This structure stores a span of time (as the name suggests). It is the type returned when you subtract two DateTime structures
Dim start as DateTime = DateTime.Now
...
' Some time after the start
Dim span = DateTime.Now - start
I've posted a blog post to do just this. However it is in C#, but you can easily convert into vb.net
http://blog.nirandas.com/post/displaying-time-in-relative-format.aspx