Eric’s Complete Guide to VT_DATE

I find software horology fascinating.

The other day, Raymond said “The OLE automation date format is a floating point value, counting days since midnight 30 December 1899. Hours and minutes are represented as fractional days.”

That’s correct, but actually it is a little bit weirder than that. I suspect that I may be the world’s leading authority on bugs having to do with the OLEAUT date format: a dubious distinction at best. I call it the VT_DATE format because it is the data stored in an OLEAUT variant of type VT_DATE.

Here are some interesting (well, interesting to me) facts about OLEAUT dates.

First of all, let’s start with the obvious problem — Midnight 30 December 1899 in what time zone? We never say. OLEAUT dates are always “local” which makes it very difficult to write code that uses OLEAUT dates — any VB or VBScript program, for example — which must deal with two things happening at the same time in different time zones.

Next, what about daylight savings time? How does one represent those days which due to springing forward or falling back have 23 hours or 25 hours? Again, those who use the OLEAUT date format need to pretend that these days do not exist.

Now let’s get really weird. An OLEAUT date is, as Raymond noted, a double where the signed integer part is the number of days since 30 December 1899 and the fraction part is the amount of that day gone by. So what is 1.75? That’s 6:00 PM, 31 Dec 1899. What about -1.75? That’s 6:00 PM, 29 Dec 1899. Notice how the 0.75 part means 6:00 PM in both cases; three-quarters of the way through the day.

What about 0.75 and -0.75? Uh, those are zero and “minus zero” days from 30 December 1899, again at 6:00 PM . Those two numbers are the same time. This means that any program which must calculate the difference between two OLEAUT dates must say that (-0.75) – (0.75) = 0 difference in time.

The reason I know all this is because my first ever checkin as a full timer was a rewrite of the VBScript implementations of DateAdd and DateDiff, both of which used to be a mass of spaghetti code to handle all the special cases entailed by the discontinuities between -1.0 and 1.0. Incidentally, I now ask interview candidates to write me those algorithms during interviews! (Attention potential interview candidates reading this: writing a mass of spaghetti code on my whiteboard is a bad idea. Solving this problem by cases is a bad idea. There are better ways to solve this problem!)

Here’s another bogus one: how about -1.99999999 and -2.0? Those are 0.00000001 apart in numbers but almost 48 hours apart in time! But OLEAUT dates are rounded to the nearest half second by the operating system, and you guessed it: any dates less than a quarter second before midnight before 30 Dec 1899 are sometimes “rounded” two days wrong. I wrote the code that converts OLEAUT dates to JScript dates, and it at least does correctly handle this case, though I had to jump through some hoops to do it.

(UPDATE: I have no idea if in the 16 years since I first wrote this article that bug in Windows was ever fixed. Anyone want to try it out and see?)

Some more oddities: The range is enormous, and the precision varies greatly over the range! You can represent dates long before the creation of the Universe, though you lose precision as you go. However, the only valid dates for the date format are between 100 AD and 10000 AD, and since we have half-second granularity, we are wasting a whole lot of bits here.

And finally, why 30 December 1899? Why not, say, 31 December 1899, or 1 January 1900 as the zero day? Actually, it turns out that this is to work around a bug in Lotus 1-2-3! The details are lost in the mists of time; what I can tell you is that Lotus 1-2-3 used this date format, and wished to have “day one” be 1 January 1900. However, they forgot that 1900 was not a leap year, and therefore their “day count” was off by one for every day after 28 February 1900. Microsoft chose to use the Lotus date format for Excel, for compatibility, but “fixed” this bug by moving “day one” back one day. Therefore day zero is 30 December 1899.

Getting date code right is hard this is one of those areas where messy human requirements are hard to translate into crisp machine logic. There are huge localization problems for instance, in Japan it is legal to specify years as the fifth year of the reign of Emperor Hirohito . Thailand’s calendar doesn’t count from 1 AD. In Israel, the start and end days for daylight savings time are not standardized but rather are declared anew by the government after fierce debate every year.

These situations are fraught with peril for the unwary developer. I once drove our Microsoft support team in Israel to distraction by accidentally changing both Arabic and Hebrew locales to display dates right-to-left. Apparently in Arabic dates are customarily written right-to-left, but Israelis use left-to-right dates even when they are embedded in right-to-left Hebrew text.

You wouldn’t think that something as simple as asking “what day is it?” could lead to so many problems, but the world is seldom as cut-and-dried as we software developers would like.

For more history on this bizarre date format, see this article by Joel Spolsky.

Bad Hungarian

One more word about Hungarian Notation and then I’ll let it drop, honest. (Maybe.)

If you’re going to uglify your code with Hungarian Notation, please, at least do it right. The whole point is to make the code easier to read and reason about, and that means ensuring that the invariants expressed by the Hungarian prefixes and suffixes are actually invariant.

Here’s some code I found once in the “diagram save” code of a Visio-like tool:

long lCountOfConnectors = srpConnectors->Count();
while( --lCountOfConnectors)
{
// [Omitted: get the next connector]
// [Omitted: save the connector to a stream]
}

OK, first of all, that should be cConnectors, c for “count of”. But that’s just a trivial question of what lexical convention we use. There’s a far more serious problem here. The number of connectors is not decreasing as we iterate the loop, so cConnectors should not be decreasing.

Hungarian makes it easier to reason about code, but only when you make sure that the algorithm semantics and the Hungarian semantics match. Seriously, when I first read this code I naturally assumed that it was removing connectors from the collection for some reason, and therefore decreasing the count so that the count variable would continue to match reality. But in fact it was just using the count as an index, which is semantically wrong. The code should read something like:

long cConnectors = srpConnectors->Count();
for(long iConnector = 0 ; iConnector < cConnectors ; ++iConnector)
{
// [...]
}

In other words, the name of a variable should reflect its meaning throughout its lifetime, not merely its initialization.

What are the VBScript semantics for object members?

In the previous episode we discussed how VBScript supports two kinds of reference semantics — reference types, and pass-by-reference. Clearly in order for VBScript to support pass-by-reference on variables, there has to be a variable to reference.

Consider our earlier example:

Sub Change(ByRef XYZ)
   XYZ = 5
End Sub

Dim ABC
ABC = 123
Change ABC

If that had been Change (ABC) then based on what you know from two posts ago, you’d know that it passes ABC by value, not by reference. So the assignment to XYZ would not change ABC in that scenario.

The rule is pretty simple: if you want to pass a variable by reference, you’ve got
to pass the variable, period.

This series of posts was inspired by an intrepid scripter who was trying to combine our
previous two examples. They had a program that looked something like this:

Class Foo
   Public Bar
End Class
Sub Change(ByRef XYZ)
   XYZ = 5
End Sub

Dim Blah
Set Blah = New Foo
Blah.Bar = 123
Change Blah.Bar

This in fact does not change the value. This passes the value of Blah.Bar, not a reference to Blah.Bar.

The scripter asked me “why does this not work the way I expect?” Here’s my Socratic dialog reply:

Q: Why does this not work the way I expect?

A: Because your expectations are inconsistent with the real universe. Adjust your
expectations and they’ll start being met!

Q: That is remarkably unhelpful. Let me rephrase: What underlying design principle did the VBScript designers use to justify this decision to pass by value, not reference?

A: The fundamental principle that governs this case was “do not be unnecessarily different from VB6.” VB6 does the same thing. Try it if you don’t believe me!

Q: You are begging the question. Why does VB6 do that?

A: Probably for backwards compatibility with VB5, 4, 3, 2 and 1, which incidentally was
called “Object Basic”. Ah, the halcyon days of my youth.

Q: More question begging! What was the initial justification on the day that by-reference calling was added to VB?

A: That is lost in the mists of time. That was like ten years ago! There are not very many of the original design team left. I was an intern at the time and they weren’t exactly consulting me on these sorts of decisions on a regular basis. It wasn’t so much “Eric, what do you think about these by reference semantics?” as “Eric, the OLE Automation build machine needs more memory, here’s a screwdriver.”

However, you’re in luck. I seem to recall back in the dim mists of time someone telling me something about wanting to avoid copy-in-copy-out semantics on COM objects. Suppose for example you said:

Set Frob = CreateObject("BitBucket.Frobnicator")
SetToFive Frob.Rezrov

Now what happens? This isn’t a VB class, this is some third party COM object. COM objects do not have property slots, they have getter/setter accessor functions. There is no way to pass the value of Frob.Rezrov by reference because VB does not have psychic powers which tell it where in memory the implementers of BitBucket.Frobnicator happened to store the value of the Rezrov property.

Given that, how could you implement byref semantics? You could implement copy-in-copy-out semantics! VB would have to create a memory location, fill it with the value returned by get_Rezrov, pass the address of that location to SetToFive, and then upon SetToFive‘s return, it would have to call Frob::set_Rezrov with the new value put into the buffer.

Easy, right? Well, it gets weird once you start thinking about non-trivial functions.
Consider the case where SetToFive does not change the value of the by-ref variable. That call to set_Rezrov may have side effects, so do we really want to call it if nothing changed? It seems like that could potentially cause badness, and certainly cause poor performance. In a “realio-trulio byref” system we’d expect zero sets if there was no change but in copy-in-copy-out we end up with one call to the setter regardless. How could we avoid that unwanted call?

Well, we could create yet another temporary storage to keep the original value around and do a comparison when SetToFive returns. (Note that I’ve just waved my hands there; I’m assuming that the two values can sensibly be compared. Comparing two
things for equality is non-trivial, but that’s another posting.)

Anyway, what if the temporary storage variable changed during the execution of SetToFive and then changed back? In that case we’d expect two calls to the setter, but actually end up with no calls!

Naïve copy-in-copy-out doesn’t provide particularly good fidelity with true byref
addressing. The original designers of VB decided that it was simply not worth
the trouble to do it at all. It is much easier to simply say that members
of COM objects do not get copy-in-copy-out semantics, and therefore they cannot be
passed by reference. If you’re going to make that restriction for some COM objects,
it seems perverse to say “we’ll do this for third party COM objects but not for VB
class objects.” Thus, VBScript does not support passing object properties by
reference.

More on ByVal and ByRef

In my previous entry I discussed VBScript’s various syntaxes for passing values by reference. However it occurs to me that there may be some confusion about what exactly “pass by reference” (byref) and “pass by value” (byval) mean in JavaScript and VBScript. This is frequently a source of confusion, as VBScript has byref behaviour not supported in JavaScript.

The confusion arises because VBScript uses “by reference” to mean two similar-but-different things. VBScript supports reference types and variable references. JavaScript supports the former but not the latter.

The best way to illustrate the difference is with an example. Consider this VBScript class:

Class Foo 
      Public Bar 
End Class

Now we can create an instance of this class:

Dim Blah, Baz 
Set Blah = New Foo 
Set Baz = Blah 
Blah.Bar = 123

Both Blah and Baz are references to the same object. The fourth line changes both Blah.Bar and Baz.Bar because these are different names for the same thing.

That’s the “reference type” feature. We say that VBScript treats objects as reference types.

Now consider this little program:

Sub Change(ByRef XYZ) 
    XYZ = 5 
End Sub 
Dim ABC 
ABC = 123
Change ABC

This passes a reference to variable ABC. The local variable XYZ becomes an alias for ABC, so the assignment XYZ = 5 changes ABC as well.

JavaScript has reference types — all object types are reference types. But JavaScript does not support variable references; you cannot alias one variable with another in JavaScript.

Cannot use parentheses when calling a Sub

Every now and then someone will ask me what the VBScript error message “Cannot use parentheses when calling a Sub” means. I always smile when I hear that question. I tell people that the error means that you cannot use parentheses when calling a Sub — which word didn’t you understand?

Of course, there is a reason why people ask, even though the error message is perfectly straightforward. Usually what happens is someone writes code like this:

Result = MyFunc(MyArg)
MySub(MyArg)

and it works just fine, so they then write

MyOtherSub(MyArg1, MyArg2)

only to get the above error.

Here’s the deal: parentheses mean several different things in VB and hence in VBScript. They mean:

1) Define boundaries of a subexpression: Average = (First + Last) / 2
2) Dereference the index of an array: Item = MyArray(Index)
3) Call a function or subroutine: Limit = UBound(MyArray)
4) Pass an argument which would normally be byref as byval: in Result = MyFunction(Arg1, (Arg2)) , Arg1 is passed by reference, Arg2is passed by value.

That’s confusing enough already. Unfortunately, VB and hence VBScript has some weird rules about when #3 applies. The rules are

3.1) An argument list for a function call with an assignment to the returned value must be surrounded by parens: Result = MyFunc(MyArg)
3.2) An argument list for a subroutine call (or a function call with no assignment) that uses the Call keyword must be surrounded by parens: Call MySub(MyArg)
3.3) If 3.1 and 3.2 do not apply then the list must not be surrounded by parens.

And finally there is the byref rule: arguments are passed by reference when possible but if there are “extra” parens around a variable then the variable is passed by value, not by reference.

Now it should be clear why the statement

MySub(MyArg)

is legal but

MyOtherSub(MyArg1, MyArg2)

is not. The first case appears to be a subroutine call with parens around the argument list, but that would violate rule 3.3. Then why is it legal? In fact it is a subroutine call with no parens around the arg list, but has parens around the first argument! This passes the argument by value. The second case is a clear violation of rule 3.3, and there is no way to make it legal, so we give an error.

These rules are confusing and silly, as the designers of Visual Basic .NET realized. VB.NET does away with this rule, and insists that all function and subroutine calls be surrounded by parens. This means that in VB.NET, the statement MySub(MyArg) has different semantics than it does in VBScript and VB6 — this will pass MyArg by reference in VB.NET but by value in VBScript/VB6. This was one of those cases where strict backwards compatibility and usability were in conflict, and usability won.

Here’s a handy reference guide to what’s legal and what isn’t in VBScript: Suppose x and y are variables, f is a one-argument procedure and g is a two-argument procedure.

' to pass x byref, y byref:
f x
Call f(x)
z = f(x)
g x, y
Call g(x, y)
z = g(x, y)

' to pass x byval, y byref:
f(x)
Call f((x))
z = f((x))
g (x), y
g ((x)), y
Call g((x), y)
z = g((x), y)

The following are syntax errors:

Call f x
z = f x
g(x, y)
Call g x, y
z = g x, y

Ah, VBScript. It just wouldn’t be the same without these quirky gotchas.

Why does JavaScript have rounding errors?

Try this in JScript:

window.alert(9.2 * 100.0);

You might expect to get 920, but in fact you get 919.9999999999999. What the heck is going on here?

Boy, have I ever heard this question a lot.

Well, let me answer that question with another question. Suppose you did a simple division, say

window.alert(1.0 / 3.0);

Would you expect an infinitely large window that said “0.33333333333…” with an infinite number of threes, or would you expect ten or so digits?

Clearly you’d expect it to not fill your computer’s entire memory with threes. But that means that I must ask why are you willing to accept an error of 0.00000000000333333… in the case of dividing one by three but not willing to accept a smaller error of 0.000000000001 in the case of multiplying 9.2 by 100?

The simple fact is that all floating-point arithmetic accumulates tiny errors. In JavaScript and many other languages, arithmetic which results in numbers that cannot be represented by a small number of powers of two will result in these tiny errors.

Let’s look at this case a little closer, where we’re trying to multiply 9.2 by 100.0.

100.0 can be exactly represented as a floating point number because it’s a small integer. But 9.2 can’t be — you can’t represent 46/5 exactly in base two any more than you can represent 1/3 exactly in base ten. So when converting from the source code text “9.2” to the internal binary representation, a tiny error is accrued.

However, it’s not all bad; the 64 bit binary number which represents 9.2 internally is (a) the 64 bit float closest to 9.2, and (b) the algorithm which converts back and forth between strings and binary representation will round-trip — that binary representation will be converted back to “9.2” if you try to convert it to a string.

Now we go and throw a wrench in the works by multiplying by one hundred.

That’s going to lose the last few bits of precision because we just multiplied the accrued error by a factor of one hundred. The accrued error is now large enough that the string which most exactly represents the computed value is NOT “920” but rather “919.9999999999999”.

The ECMAScript specification mandates that JavaScript display as much precision as possible when displaying floating point numbers as strings.

VBScript does not do this. VBScript has heuristics which look for this situation and deliberately break the round-trip property in order to make this look better. In JavaScript you are guaranteed that when you convert back and forth between string and binary representations you lose no data. In VBScript you sometimes lose data; there are some legal floating point values in VBScript which are impossible to represent in strings accurately. In VBScript it is impossible to represent values like 919.9999999999999 precisely because they are automatically rounded!

Ultimately, the reason that this is an issue is because we as human beings see numbers like 9.2, 100.0 and 920 as “special”. If you multiply 3.63874692874 by 4.2984769284 and get a result which is one-billionth wrong no one cares, but when you multiply 9.2 by 100.0 and get a result which is one-billionth wrong everyone yells at me! The computer doesn’t know that 9.2 is more special than 3.63874692874 — it uses the same lossy algorithms for both.

All languages which use double-precision floating point arithmetic have this feature — C++, VBScript, JavaScript, whatever. If you don’t like it then either get in the habit of calling rounding functions, or don’t use floating point arithmetic, use only integer arithmetic.

Fabulous Adventures!

Welcome! I’m Eric Lippert; I design and implement programming language tools. This is my blog where I talk about programming and programming language design, with occasional diversions about my other various interests.

I wrote this blog from 2003 to 2012 for Microsoft and I am gradually migrating the content to this site; there is a lot of it, the formatting is inconsistent, and it will take some time.

If you’re looking for the original content, it is archived in several locations because Microsoft continues its multi-decade tradition of not knowing how to run a blog server for some reason.

The early part of this blog is mostly about JavaScript and VBScript; the latter part is mostly about C#. If that sounds like fun to you, let’s go have some fabulous adventures in coding!