Data types in VB and .NET
This article presents a technical look into the numeric data types supported by classic Visual Basic, VBA, VB.NET and the .NET framework. There are surprising peculiarities in several types. A conversion diagram shows how to achieve lossless type conversion.
How to store numbers in your programs? How to avoid Overflow or loss of decimals? How to work with dates? This article summarizes the elementary numeric data types in classic Visual Basic, Visual Basic for Applications, Visual Basic .NET and other .NET languages.
Reasonable development experience is assumed. This is no introduction to programming. You may find some of the information especially handy if you intend to migrate from VB6 to a .NET language.
Data type summary
With numeric data types we mean the elementary built-in types intended to store integer numbers, decimal numbers, flags and dates.
The following table shows numeric data types sorted by their storage requirements in bits and the numeric range they support. The VB6/VBA data types are used in classic VB and Visual Basic for Applications. The .NET data types are offered by the .NET framework versions from 2.0 to 4.0. The VB.NET data types are aliases for the underlying .NET data types, available in Visual Basic versions from 2005 to 2010.
* Decimal: ±79,228,162,514,264,337,593,543,950,335
Data type specifics
Boolean is really a flag and not a numeric data type. The storage size is 16 bits in VB6 and varies by platform in .NET.
Integer and Long
Integer and Long have the same name and type character in VB6/VBA and VB.NET, but they are different data types altogether. To stay clear one can use Int16, Int32 and Int64 in .NET.
Currency is a 64-bit integer divided by 10000. It is only available in VB6/VBA. In VB.NET one can replace it with Decimal, which provides more capability.
Introduced with Office 2010, LongLong is a 64-bit signed integer that is available in VBA7 running on a 64-bit platform. It is unsupported in VB6, VBA6 and on 32-bit platforms.
Decimal is a signed 96-bit integer, which is divided by a scaling factor. The scaling factor varies from 1 to 1E+28. A Decimal can represent 29 decimal digits: integers and decimal numbers with max 28 decimal places. The binary representation consists of a 1-bit sign, a 96-bit integer number and the scaling factor. The smallest nonzero Decimal is ±1E−28 = ±0.0000000000000000000000000001. The storage size is 128 bits, but not all the bits are used for the number.
VB6 supports Decimal with the Variant data type. You cannot declare a variable to be of type Decimal. You can, however, create a Variant whose subtype is Decimal using the CDec function.
The smallest non-zero Single is ±1.401298E-45.
VB6: The maximum Single is 3.4028235E+38, but it rounds down to 3.402823E+38 when displayed. The latter is also the maximum Single literal you can write in VB6 code.
The smallest non-zero Double is ±4.94065645841247E-324 in VB6 and ±4.94065645841246544E-324 in .NET.
VB6 stores max 1.7976931348623157E308, but this rounds down to 1.79769313486231E+308 when displayed. The latter value is also the maximum literal number you can write in VB6 code. The absolute maximum is 1.7976931348623158077E308, which equals 1.7976931348623157E308 due to rounding.
Date and time values
Dates and times are closely related to numeric data types, especially in VB6.
VB6 date and time
VB6 stores dates and times in the Date data type. Date can store either the calendar date, the time of day, or both. The range covered is midnight #1/1/0100# to #12/31/9999 11:59:59 PM# in the Gregorian calendar. The underlying data type is Double. Use CDbl(T) to convert a Date to its numeric representation and CDate(n) to convert a number to a Date.
The integral part of a Date represents the day. The value zero is equal to #12/30/1899#. This is the Epoch. The value 1 is equal to one day, or 24 hours. A week equals 7 and a year is either 365 or 366. Sample dates: #1/1/1900# = 2, #1/1/2000# = 36526, #1/1/2100# = 73051.
The time of day is in the decimal fraction. An hour is equal to 1/24, a minute is 1/1440 and a second is 1/86400. Noon is 0.5. Noon on #1/1/1900# equals 2.5 and so on. The maximum precision in VB6 time functions is 1 second, whose numeric value is 0.0000115740740740741. You can even store fractions of a second, but VB6 has no functionality for the fractions.
Peculiarities with historical dates
The integral part is the date, the fraction is the time.
With modern dates time always runs forwards, as you would suspect. With negative historical dates time actually runs backwards! Midnight #1/1/1800# equals −36522, but noon #1/1/1800# is −36522.5 (less than midnight!) and one second before midnight is −36522.9999884259 (even less). At midnight the clock jumps forward to -36521, which equals #1/2/1800#. The decimal fraction still shows the time and the integral part is the date, but each second decrements the clock while each new day increments it, not just by 1, but by almost 2. Negative times are really counterintuitive.
To make things worse, time values for #12/30/1899# are ambigous in two ways. First, a time value without a date equals that time on #12/30/1899#. This means that 0.5 is either noon or noon on #12/30/1899#, depending on context. Zero is either midnight or #12/30/1899# or midnight on #12/30/1899#. The other ambiguity is that all time values come in double for #12/30/1899#. 0.5 is noon #12/30/1899#, but -0.5 is noon #12/30/1899# as well. The integral part is the date, the fraction is the time. Another surprise is here:
Expressions for VB6 date/time
Bug with DateDiff: DateDiff("s", -0.5, 0.5)=1 second, even though it should be 0 seconds.
.NET date and time
DateTime represents dates and times with a 64-bit value, which includes 62 bits for the date/time and 2 bits for time zone information (the Kind property). The range covered is midnight #1/1/0001# to #12/31/9999 11:59:59.9999999 PM# in the Gregorian calendar. Date/time is stored as "ticks". Each tick equals 100 nanoseconds. The supported range of ticks is from 0 to 3,155,378,975,999,999,999. The "raw" numeric range of the data type is larger because of the time zone field. The value zero is equal to #1/1/0001#. The smallest non-zero time is 1 tick = 100ns.
TimeSpan represents a time interval (duration), either positive or negative. It can also represent the time of day unrelated to a particular date. TimeSpan is a 64-bit type whose underlying numeric range is equal to that of Int64. Time is stored as ticks of 100 nanoseconds each. The range covered is −10675199.02:48:05.4775808 to 10675199.02:48:05.4775807 as measured in days, hours, minutes, seconds and fractions of a second. This is approximately ±29228 years, which means a TimeSpan can go over the date range supported by DateTime.
DateTimeOffset is a structure representing a DateTime value along with its time difference to UTC. This structure is outside the scope of this article.
Data type conversion diagram
There are two considerations when converting a value from a data type to another: 1. magnitude and 2. precision. 1) An error occurs when the converted value doesn't fit in the target type. 2) Loss of precision may occur when converting to a Single or Double. In this case, magnitude is preserved, but some less significant digits may be lost.
The following diagram shows the possible data type conversions in VB6 and .NET.
Data types in VB and .NET
©Aivosto Oy -