Professional Documents
Culture Documents
AA200309 Problems Dealing With Date Values
AA200309 Problems Dealing With Date Values
Doug Steele
Doug will address commonly asked questions from Access developers. This month, he looks at
problems dealing with date values.
I'm running into problems using dates, especially among users with varying regional settings.
This problem is very common, and can cause headaches when developing for users in different countries.
First, be aware that dates are not stored in any particular format. Rather, the date/time variable type is an
IEEE 64-bit (8-byte) floating-point number. The integer part of the number represents the date as the
number of days relative to 30 Dec., 1899 (i.e.: 04 Apr, 1897 is -1000, 29 Dec, 1899 is -1, 04 Jan, 1899 is
+5, 05 Jun, 2003 is +37777 and so on). The decimal part of the number represents the time as a fraction of a
day (i.e.: 3:00 AM is .125, 8:00 AM is .3333333, Noon is .5, 6:00 PM is .75 and so on)
It doesn't matter how the date was input. As long as your application interpreted it correctly when it was
input (more about this later), it will be stored such that it can be used regardless of the date format. Note
that this also means that you must provide a complete date if you're going to use a date/time field: if you
only supply a partial date, Access is going to do its best to "fill in the gaps" and provide you with a
complete date:
?Format("05/30", "dd mmm, yyyy")
30 May, 2003
?Format("10/2004", "dd mmm, yyyy")
01 Oct, 2004
What it boils down to is that it's irrelevant what Regional Settings are in effect when the data is entered, the
dates will be stored consistently.
The second area to discuss is how to use dates in your application. There are essentially 3 options for literal
dates:
1. You can delimit the date with octothorpes (#)
2. You can delimit the date with quotes
3. You can enter the number for the date (as discussed above)
(Over the years, I've got in a number of arguments with people about whether # is the pound symbol, the hash symbol,
the sharp sign or the number sign. To avoid any such discussion, I'm going to refer to that character as the octothorpe.
See http://www.sigtel.com/tel_tech_octothorpe.html for more details. As well, for the purposes of simplification, I'm
going to ignore time in the following discussion. What I'm describingdoesn't change with the addition of time.
When you delimit your dates with octothorpes (#), it doesn't matter what the user locale settings has for
Short Date format. Access is very specific about how it treats dates delimited this way. Assuming that the
date is all numeric, the first choice is to assume that the date is in mm/dd/(yy)yy format. Only if it cannot
interpret the input as a date in that format is it going to try using a different format for interpretation. There
is some flexibility in how you represent octothorpe-delimited dates: unambiguous settings like yyyy-mm-dd
or dd mmm, (yy)yy will work as well. As you'll see, though,dd/mm/(yy)yy dates cannot be used reliably
with octothorpe-delimiters (and yes, I realize mm/dd/(yy)yy isn't unambiguous, but you have to remember
that Access was developed in the US).
When you delimit your dates with quotes, Access will take the string and convert it to a date using the
locale settings of the current user.
The following examples should illustrate the differences between the three approaches.
In this first example, the Short Date format is set to yyyy-mm-dd:
?Format(Date())
2003-05-31
?Format(#01/02/03#, "dd mmm, yyyy")
02 Jan, 2003
?Format("01/02/03", "dd mmm, yyyy")
03 Feb, 2001
?Format(37777, "dd mmm, yyyy")
05 Jun, 2003
In this example, the Short Date format is set to mm/dd/yyyy (the same results will be obtained if the Short
Date format is mm/dd/yy):
?Format(Date())
05/31/2003
?Format(#01/02/03#, "dd mmm, yyyy")
02 Jan, 2003
?Format("01/02/03", "dd mmm, yyyy")
02 Jan, 2003
?Format(37777, "dd mmm, yyyy")
05 Jun, 2003
Finally, for this example, the Short Date format is set to dd/mm/yyyy (the same results will be obtained if
the Short Date format is dd/mm/yy):
?Format(Date())
31/05/2003
?Format(#01/02/03#, "dd mmm, yyyy")
02 Jan, 2003
?Format("01/02/03", "dd mmm, yyyy")
01 Feb, 2003
?Format(37777, "dd mmm, yyyy")
05 Jun, 2003
As you can see, the date is interpreted identically in all three cases when delimited with octothorpes, but
differently in all three cases when delimited with quotes. (As expected, using a number as a date is treated
consistently, regardless of the Short Date format.)
What happens, though, if what's being passed isn't valid for the Short Date format that's in effect? The
following examples show the difference when using 13/12/11 (which obviously isn't valid in mm/ dd/yyyy
format, since there's no 13th month):
With the Short Date format set to yyyy-mm-dd:
?Format(Date())
2003-05-31
?Format(#13/12/11#, "dd mmm, yyyy")
11 Dec, 2013
?Format("13/12/11", "dd mmm, yyyy")
11 Dec, 2013
Again, in all three cases the date was interpreted consistently when delimited with octothorpes (wrongly,
possibly, but consistently!), but differently when delimited with quotes.
As was shown above, using octothorpes (#) as delimiters is consistent, regardless of the user's locale
settings.
I'll admit that the examples above were somewhat contrived, as they don't use 4-digit years. When you use
4-digit years, the results are more consistent, since now Access only has to decide between month and day.
Regardless of whether the Short Date format is yyyy-mm-dd, mm/dd/yyyy or dd/mm/yyyy, you will get the
following results:
?Format(#13/12/2011#, "dd mmm, yyyy")
13 Dec, 2011
?Format("13/12/2011", "dd mmm, yyyy")
13 Dec, 2011
?Format(#12/13/2011#, "dd mmm, yyyy")
13 Dec, 2011
?Format("12/13/2011", "dd mmm, yyyy")
13 Dec, 2011
I'm constantly surprised, though, how many people don't use 4 digit years. Wasn’t there some noise in the
press a few years ago about this?
Of course, if you can be confident that your dates are valid, you needn't worry about incorrect
interpretation. The only thing that matters is to present them for use in a manner that Access (or Jet, if
you're going running a query) can handle them.
Since you are developing the application, you have complete control over which approach your code will
use to represent dates. My recommendation is that you create a wrapper function to format all of your dates
whenever you want to use them in VBA code. In this way, you can be assured that the user's locale settings
won't matter.
A favourite of mine is the following:
Function SQLDate(DateIn As Variant) As String
If IsDate(DateIn) Then
SQLDate = Format$(DateIn, "\#mm\/dd\/yyyy\#")
End If
End Function
If IsDate(DateIn) Then
SQLDate = Format$(DateIn, "\#yyyy\-mm\-dd\#")
End If
End Function
(For the curious, the \ character is an escape character to ensure that what follows it is shown exactly as it appears.
Since # has special meaning in formatting, you need to escape it to ensure that you get # and not a digit. Similarly, \/
ensures that you get / as the separator between the date parts. If you didn't include the escape, you would get whatever
the user has specified as the date separator in their locale settings, and that won't always be interpreted correctly)
Now, you can use this, for example, in the WhereCondition of the OpenReport method:
DoCmd.OpenReport "MyReport", acViewPreview, , & _
"InvoiceDate > " & SQLDate(Me!txtInvoiceDT)
(where txtInvoiceDT is a text box on your form), or in generating an SQL string to be used:
strSQL = "SELECT Field1, Field2 " & _
"FROM MyTable " & _
"WHERE ActiveDTM Between " & _
SQLDate(Me!txtStartDT) & _
" AND " & SQLDate(Me!txtEndDT)
If IsDate(DateIn) Then
Select Case UCase$(DataSource)
Case "JET"
SQLDate = Format$(DateIn, "\#yyyy\-mm\-dd\#")
Case "SQL"
SQLDate = Format$(DateIn, "\'yyyy\-mm\-dd\'")
Case "ODBC"
SQLDate = Format$(DateIn, "\{\d \'yyyy\-mm\-dd\'}")
End Select
End If
End Function
Another consideration is in terms of constructing dates from values you already know. Consider the case
where you are trying to create a date when you know the year, month and day. Since you don't know what
Short Date format your user will have, you typically want to avoid having Access coerce the date. The
CDate function (as well as the CVDate function) respects the user's locale settings. That means you
shouldn't rely on that function to convert your string to a date. Rather, you should use theDateSerial
function. On the other hand, if you're letting the user type a date into an unbound text box, using theCDate
function (or CVDate function) in your code will ensure that you interpret the user's input as they intended
(provided, of course, that the locale is set appropriately on their machine).
(While the Help file states that the "CVDate function is also provided for compatibility with previous versions of
Visual Basic", it does have an advantage when dealing with Date fields in queries. CDate fails on Null values, whereas
CVDate doesn't.)
The last area of discussion is recognizing user input as dates. As you may suspect, since you do not
necessarily know what your user's locale settings have for Short Date format, this is where most of the
problems occur.
When you're using a text box bound to a Date field, it's not that difficult, as you'll get an error if Access
can't interpret the input as a date. There is still the possibility of the user using the wrong date format when
keying, and a possible solution to that is to set the field's format to something unambiguous, and they can
see how what they keyed is being interpreted. Figure 1 shows an example of how to set the field:
Figure 1. Setting the field format
Figure 2 shows the user entering the date in their preferred format: