The dBASE Date Object

Last Modified: January 31, 2001
Ken Mayer, Senior SQA Engineer
dBASE, Inc.


NOTE: This document was originally written for Visual dBASE 7.0/7.01, it has been updated for dB2K (release 1) and later versions of dBASE to include information about new properties, events, etc., and any new controls since the document was first written. If a control is new it will be noted. In updating this document, the images were left "as is" unless it was felt to be absolutely necessary to change them ...

In addition, this document refers to dB2K a lot, but unless it is about a dB2K specific aspect, the text can be used for Visual dBASE 7.0 through Visual dBASE 7.5.


The Purpose of this Document

This document will attempt to explain the basics of using the Date Object in dBASE to manipulate dates and date/times ...


Properties and Methods of the Date Object

Before we do anything else, let's look at the properties and methods of the date object.

Some of these methods may seem a bit confusing -- for the most part you may not need to use some of them for much.


The Basics ...

The simplest way to create a date object is the same as you do with most objects in dBASE:


   d = new Date()

Note that this is not the same as:


   d = date()

In the first, you are creating an instance of the date object, in the second you are storing a date value to a memory variable. The date memory variable does not have access to the properties and methods of the date class (unlike a string memory variable which does -- see "Using the String Object" HOW TO in the Knowledgebase).

If you create a date object with no parameters (as above), you are automatically setting the date and time to whatever the date and time where at the instant that the object was instantiated.

It is possible to instantiate the date object with a specific value:


   d = new Date( "01/01/1999" )

Note that the value is a character string -- you can also include the time in this:


   d = new Date( "01/01/1999 10:01:10" )

You can instantiate the date with a value that represents the number of milliseconds since January 1, 1970 00:00:00 GMT (GMT = Greenwich Mean Time):


   d = new Date( 920568803828.00 ) // March 4, 1999, 09:33:23 AM

You can instantiate the date with explicit values for the year, month, day and optionally hours, minutes, and seconds:


   d = new Date( 99, 2, 4, 10, 45, 42 ) // March 4, 1999, 10:45:42 AM

Date Math
It is always useful to be able to perform math on a date. There are some problems involved in performing math on a date object -- if you treat the date object like a date memory variable, some of what you are used to doing will not function the same.

Example:


   d = new Date()
   ? d+1

What you might expect to see is the current date plus one day -- this is how it works if you use a date memory variable.

However, we have a date object. What happens if you do this?

This isn't all that bad -- the math happened like you expected, but ... the value printed is not just the date, it is a value like:


   3/05/1999 10:06:14 AM

This is not a date. It is the value returned as if you had typed:


   ? d.toLocaleString()

Interesting things can happen when you work with dates and times using the date object. The month, for example, is zero based -- if you simply display:

? d.month // or ? d.getMonth() // should display 2 if the month is March

This can get confusing if you're not ready for it. However, dBASE handles this stuff pretty well. For example, the CMONTH() function (which is in dBASE) will read the date correctly:


   ? cMonth( d )
   // should display: March

Note that the hard part is working with the year -- while the year is calculated properly, the display of the year, if you simply query it, will display with two digits:


   ? d.year // or ? d.getYear()
   // should display: 99 if the year is 1999

This is a bit of a problem ... however, the YEAR() function of dBASE will return the full four digits, IF the CENTURY setting is ON:


   ? year( d )
   // should display 1999 if the CENTURY setting is ON

International Issues
One of the best things about the date object is that you can use it to side-step issues involved in different operating systems, different locales, and so on. You can create methods that work perfectly for whatever language your users may be using ...

You can create functions (a small example below) that bypass the international issues, because internally the date is stored in the same fashion, it is largely a matter of display.

If you instantiate a date object and then assign values to the date object's properties, you can perform math (see above) on the date (and time), and return a value that is in the same date format ...

An example would be the addMonths() function shown below -- this can be used to add months to a date (this is a simplification of the method AddMonths of the dateex class in the dUFLP library in the Knowledgebase):


   function addmonths( dDate, nMonths )
      local dReturn, d
      d = new Date( dtoc( dDate ) )

      d.Month += nMonths

      // deal with century setting (either two digit or
      // four digit year)
      dReturn = CtoD( left( d.toLocaleString(), ;
                      iif( set("CENTURY") == "ON", 10, 8 ) ) )
   RETURN dReturn

Using a function like this is as simple as:


   ? addMonths( date(), 2 ) // add two months to current date

No matter what language drivers your users are using, this should return the current date plus two months set for whatever format their system is set to display the date in.

The statement that deals with extracting the actual date is important here -- it extracts either 10 characters (if SET CENTURY is ON) or it extracts 8 characters (if SET CENTURY is OFF). The toLocaleString() method by itself would return a much larger string -- including the time. For the purpose of this function we just wanted to manipulate the date ...


DateTime() And Related Functionality

dB2K has, in addition to the new Date class, some undocumented features noted below (most of the following is lifted directly from the paper on Undocumented Features in the Knowledgebase).

DateTime()
This is a feature that shows the date and the time. It is its own type, which can be seen by using the type() function.

NOTE: DateTime() and "new date()" are almost identical; dBASE will do an on-the-fly conversion from one to the other when necessary. The main reason DateTime() is undocumented is that there is so little difference ...


   dDT = DateTime()
   ? type( 'dDT')

Notice that the value that you get is "DT".

Internally, the value being stored is fractional days in scientific notation (i.e., .25 is six hours) -- the only reason that this might be important is that you can attempt to perform math using the DateTime() function, but the values appear meaningless:


   d1 = DateTime()
   // Wait a minute or so ...
   d2 = DateTime()
   ? d1 - d2
   // In the output pane ...:
   -.92245370370436E-4

The dateTime() function can be useful if you wish to compare the date and time that some event occurs to another occurance of the same event, however you should convert the values to something more useful than that shown above. This can be done by extracting the date and/or the time strings out of the returned value.

You can convert the value returned by the DateTime() function to character, using the function DTtoC() (DateTime to Character), and you can convert this back to a DateTime type by using the CtoDT() function (Character to DateTime).

If you are using the TimeStamp field in a (dBF 7 table), this is useful -- you could store the current date and time to a field that was defined as a TimeStamp type:


   queryName.rowset.fields["timestampfield"].value = DateTime()

This might be used for an audit trail ... or a variety of other situations.

NOTE: You can also use the date class to assign a value of this sort:


   queryName.rowset.fields["timestampfield"].value = new Date()

TTime()
This function displays the time (which can also be gotten with time() ) with the AM/PM indicator. This returns its own type, which can be shown by using the type() function:


   tT = TTime()
   ? type( 'tT')

This will return "T" for the value.

The actual value being used for the TTime() function is seconds.

You might want to convert to character and back using the TtoC() and CtoT() functions.

You can do math on the value returned by TTime(), which can be useful to find elapsed time values. Example:


   tBeginTime = TTime()
     // do some long process
   tEndTime = TTime()

   ? "Elapsed time = " + (tEndTime - tBeginTime) + "seconds"

Some Info NOT From the Undocumented Features paper
In addition to all of the above, you can do math (if you know the values) on date objects, and datetime values along these lines:


   dt = datetime()
   ? dt                       // current value of dt
   ? dt + 1                   // add a day
   ? dt + ( 1/24 )            // add an hour (24 hours/day)
   ? dt + ( 1/24/60)          // add a minute (1440 minutes/day) 
   ? dt + ( 1/24/60/60 )      // add a second (86400 seconds/day)

   d1 = CtoDT( "01/01/2000 10:00:00" )
   d2 = CtoDT( "01/01/2000 10:01:01" ) // 1 minute, 1 second later
                                       // 61 seconds difference
   ? int( ( d2 - d1 ) * 86400 )        // should return 61 seconds


Summary

There's not a lot to say here. Some of the functionality of the date class was created for IntraBuilder, a now "dead" software package created by Inprise/Borland. The internationalization issues involved with GMT, the toLocaleString() and other methods of the date class were created specifically with the idea of web commerce and such in mind. With dB2K now being more and more web-capable (Visual dBASE 7 through dB2K has always had some of this ability, but dB2K has added more functionality in this area), these abilities are going to be more and more useful to international commerce based web apps.

Since dB2K inherited these abilities, we might as well take advantage of them. You can find, in the dBASE Users Function Library Project (dUFLP) in the Knowledgebase a file called DATEEX.CC -- this is a subclassed date object with even more functionality -- including routines like AddMonths(), Age(), and others ... some of the functionality is fairly complex, and some of it ignores the date object itself altogether. A lot depends on what you need to do.


DISCLAIMER: the author is an employee of dBASE, Inc., but has written this on his own time. If you have questions regarding this .HOW document, or about dB2K you can communicate directly with the author and dBVIPS in the appropriate newsgroups on the internet.

.HOW files are created as a free service by members of dBVIPS and dBASE, Inc. employees to help users learn to use dB2K more effectively. They are edited by both dBVIPS members and dBASE, Inc. Technical Support (to ensure quality). This .HOW file MAY NOT BE POSTED ELSEWHERE without the explicit permission of the author, who retains all rights to the document.

Copyright 2001, Kenneth J. Mayer. All rights reserved.

Information about dBASE, Inc. can be found at:

       http://www.dbase.com
    

EoHT: DATEOBJECT.HTM -- January 31, 2001 -- KJM