Using Visual Basic 6

Previous chapterNext chapterContents


- 16 -
Working with Time and Timers


Understanding Serial Time

Visual Basic deals with time differently than you might be accustomed: It uses the Date data type, which uses the day as the basic unit of time. Thus, an hour is 1/24 of a day, and a second is 1/86,400 of a day. You would represent a week as 7 because a week contains seven days. The Date data type displays dates according to how your computer's time format is set: by 12-hour or 24-hour format.

In Julian time, Day 1 is January 1, 0000. Visual Basic, on the other hand, considers Day 1 to be December 31, 1899. Thus, Day 2 is January 1, 1900, and June 12, 1968, is Day 25001 or simply 25001, the 25001st day from December 31, 1899. The serial date 25001.5 translates to noon, June 12, 1968. Dates before December 31, 1899 are represented by negative numbers; for instance, July 4, 1776, is -45103--45,103 days before December 30, 1899.

You can assign literal date values to Date variables by enclosing the literal within a pair of number signs (#)--for example,

Dim MyDate as Date
MyDate = #May 15, 1998#


Year 2000 compliance

Although Visual Basic 6 is Year 2000 compliant, realize that programs you write won't automatically be compliant. When using dates in an application, be careful to validate user input to make sure that you're using the appropriate date. Also be sure to test your application thoroughly before deployment.


The Date data type can be converted into any other data type. For example, 2 p.m. May 15, 1998, would be 35390.58333 as a Double. Everything to the left of the decimal point is the day; everything to the right of it is the time or the portion of the day: hours, minutes, seconds, and milliseconds. Notice that decimal values less than 1 can be used to specify only a time; for example, 0.12345 represents 2:57:46 a.m.

Understanding the Timer Control

In Visual Basic, the Timer control enables you to track time. Think of a Timer as a clock that fires a programmable event at an interval you specify (see Figure 16.1). The event the Timer fires is called a Timer event, and the event procedure that you program is TimerName_Timer(), in which TimerName is the value of the Name property of the Timer control.

FIGURE 16.1 The Timer control is an invisible control. You don't see it at runtime.

You can set the interval at which the Timer event fires by assigning a value to the Timer control's Interval property. The unit of measure for the Interval property is a millisecond, so if you want the Timer event to fire every half second, you would use the following:

Timer1.Interval = 500


Counting long intervals

The maximum value for Interval is 65,535, which means that the maximum interval you can set is just over 65.5 seconds. To use a longer interval, such as 10 minutes, you must actually count 10 intervals of 60,000 milliseconds.


The Interval can be as small as once every thousandth of a second or as large as your program requires. However, the shortest interval is 55ms because the system clock ticks only 18 times per second.

Because all the Timer control does is fire a Timer event, the control doesn't have many properties (see Table 16.1).

TABLE 16.1  Timer Control Properties

Property Description
Name Defaults to Timer1 if you have one Timer control, Timer2 if you have two Timer controls, and so on.
Property Description
Enabled Turns a Timer on and off. The default is True, or on.
Index Reports the position of a specific Timer control within a control array.
Interval Determines, in milliseconds, when a Timer event will fire (1 second = 1000).
Left The position of the left edge of a Timer.
Tag The Tag property is like having a variable built right into the control. It's a catch-all property to which you can assign any necessary data to your program and that you want to persist throughout the life of your program.
Top The position of the top edge of a Timer.


Timers are invisible

The Left and Top properties are irrelevant because a Timer isn't shown on the form at runtime.


Using the Time, Date, and Now Functions

Although you can program a Timer to fire a Timer event, the Timer doesn't know what time it is at any given point during its operation. For the Timer to report the time of day, it must check the computer's system clock and report back the time that the system clock passes to it.

When accessing a computer's system clock, you use the Time function to obtain the system time and use the Date function to obtain the system date. If you want to find out both the time and date on the system clock, use the Now function.

To see how these functions work, create a project and add three CommandButtons to a form. Name these buttons cmdTime, cmdDate, and cmdNow and add the appropriate code from Listing 16.1 to the event procedure for each button. (You can download this code from http://www.mcp.com/info.) Figure 16.2 shows the data that each function returns.

LISTING 16.1  16LIST01--Event Procedures That Use the Time, Date, and Now Functions

01 Private Sub cmdTime_Click()
02 `Get the time and convert it to a string
03 cmdTime.Caption = CStr(Time)
04 End Sub
05
06 Private Sub cmdDate_Click()
07 `Get the date and convert it to a string
08 cmdDate.Caption = CStr(Date)
09 End Sub
10
11 Private Sub cmdNow_Click()
12 `Get the date and time. Convert them to a string
13 cmdNow.Caption = CStr(Now)
14 End Sub

FIGURE 16.2 The Time, Date, and Now functions return a variant (Date) data type, which can be converted to a string with the CStr() function.

Use a Timer to Build a Clock Program

Let's create a clock program to see how a Timer works. This program shows the running time on the client area of the form and the current date in the form's title bar. When you minimize the form, the running time appears in the form's caption on the taskbar. (Keep in mind that you can customize this program to your specifications.)

Create a clock program

1. Start a standard EXE project and size the default form as shown earlier in Figure 16.1 or as you see fit.

2. Place a Label control and Timer control on the form, as shown in Figure 16.1. (In Figure 16.1, the Label takes up the entire form. Also, you can put the Timer anywhere because it isn't visible at runtime.)

3. Name the form frmClock and the label lblTimeDisplay. Leave the value of the Timer's Name property as the default, Timer1.


Changing the BorderStyle property

When you change the BorderStyle property, the values of the button properties can change automatically. Keep this in mind when you design your application so that you choose a BorderStyle that contains the appropriate buttons.


4. Set the value of the BorderStyle property of the form to 1 - Fixed Single. Set the value of the MinButton property of the form to True.

5. From Listing 16.2, add the code between the lines Private Sub Form_Load() and End Sub into the Form_Load() event procedure.

6. Set the value of the Timer's Interval property to 500 (one-half second). Set the value of the Timer's Enabled property to True.

7. From Listing 16.3, add the code between the lines Private Sub Timer1_Timer() and End Sub into the Timer1_Timer() event procedure.

8. Save and run the code (see Figure 16.3).

LISTING 16.2  16LIST02.TXT--The Form_Load() Event Procedure

01 Private Sub Form_Load()
02 `Set the position and size of the Label control
03 `to the form's client area.
04 lblTimeDisplay.Top = ScaleTop
05 lblTimeDisplay.Left = ScaleLeft
06 lblTimeDisplay.Width = ScaleWidth
07 lblTimeDisplay.Height = ScaleHeight
08 End Sub

LISTING 16.3  16FIG03.TXT--The Timer1_Timer() Event Procedure

01 Private Sub Timer1_Timer()
02 `If the form is diplayed as a window, show
03 `the time in the client area and the date
04 `in the window's title bar.
05 If frmClock.WindowState = vbNormal Then
06 lblTimeDisplay.Caption = CStr(Time)
07 frmClock.Caption = Format(Date, "Long Date")
08 Else
09 `If the form is minimized into the Task Bar,
10 `set the caption to show the time. This will
11 `make the time appear in the Task Bar.
12 frmClock.Caption = CStr(Time)
13 End If
14 End Sub

FIGURE 16.3 Setting the form's MaxButton property to False disables it.

The Clock program uses a few things that you might not have seen before. The Format() function sets the string that displays the date to show the complete day, month, and year. (You'll find a detailed discussion of the Format() function in the following section.) You also set the form's MinButton property to True to enable the minimize button on the title bar, thus allowing the form to be minimized to the taskbar.

Forms have three properties--MinButton, MaxButton, and ControlBox--that affect the three buttons on the right side of the title bar. MinButton and MaxButton are used to turn on (value set to True) or off (value set to False) the Maximum and Minimum buttons on a form. These properties are available only at design time, when the BorderStyle of the form is set to 1 - Fixed Single or 2 - Sizable, and when the ControlBox property of the form must be set to True.

The ControlBox property turns on (value set to True) or off (value set to False) the control-menu box, located in the left side of the title bar. You can turn on the control-menu box only when the form's BorderStyle property is set to 1 - Fixed Single, 2 - Sizable, or 3 - Fixed Dialog.


The WindowState property

Be careful when you set the WindowState property. You might run into a situation in which you accidentally set the BorderStyle to be Fixed Single and the WindowState to be Maximized. This will result in a nonsizable splash screen type of window that fills the entire screen, which users can't control.


The program also checks the value of the form's WindowState property to determine whether the form is minimized. The WindowState property has three values: 0 - Normal (vbNormal), 1 - Minimized (vbMinimized), and 2 - Maximized (vbMaximized). You can read the value of the WindowState property to determine whether a window is sized regular, full screen, or in the taskbar. You can also set the value of the WindowState property to make a window full screen, restore it to normal, or minimize it to the taskbar.

Using the Format() Function

The Format() function is a powerful Visual Basic function that allows you to control the way strings present themselves. The function is used primarily to display time/date values and numbers, although you can use it to give string values a consistent look.

The Format() function takes the following syntax:

MyString$ = Format(Expression[, Format_String[, _
            FirstDayofWeek[, FirstWeekOfYear]]])


Creating new format settings

The Format() function has many levels of complexity. You can use its intrinsic settings, or you can make up your own user-defined settings. For more information, refer to the online documentation that comes with Visual Basic.


In this syntax,

The key to working with the Format() function is understanding the Format_String parameter. This parameter tells the value (as described by the Expression parameter) how to appear as a string. Table 16.2 shows you how to use the settings that come built into the Format() function to manipulate the appearance of time and date strings.


Using the Format_String parameter

You must put the Format_String parameter between quotation marks because the function is looking for the literal string. When learning to use the Format() function, many people forget to do this, in which case the function will fail. For example, the statement MyString$ = Format(.50, Percent) will generate an error, but this line is correct: MyString$ = Format(.50, "Percent")


TABLE 16.2  Using the Format() Function for the Time and Date

Format String Example Result
"Long Date" Format(36000, "Long Date") Friday, July 24, 1998
"Medium Date" Format(36000, "Medium Date") 24-Jul-98
"Short Date" Format(36000, "Short Date") 7/24/98
"Long Time" Format(0.874, "Long Time") 8:58:34 p.m.
"Medium Time" Format(0.874, "Medium Time") 08:58 p.m.
"Short Time" Format(0.874, "Short Time") 20:58

Table 16.3 shows how to use the Format() function to manipulate numeric values into a desired string display.

TABLE 16.3  Using the Format() Function for Numbers

Format String Example Result
"General Number" Format(36000, "General Number") 36000
"Currency" Format(36000, "Currency") $36,000.00
"Fixed" Format(36000, "Fixed") 36000.00
"Standard" Format(36000, "Standard") 36,000.00
"Percent" Format(36000, "Percent") 3600000.00%
"Scientific" Format(36000, "Scientific") 3.60E+04
Format String Example Result
"Yes/No" Format(36000, "Yes/No") Yes
"True/False" Format(36000, "True/False") True
"On/Off" Format(36000, "On/Off") On

You also can create other format strings to fit your specific needs. For example, if you want to make sure user input always includes two decimal places, use the line Format(235.6, "###,##0.00"), which would return the value 235.60.

Calculating Date Differences

Use the DateDiff() function when you need to know the amount of time between two dates. The DateDiff() function has the following syntax:

MyLong = DateDiff(Interval, Start_Date, End_Date[, _          FirstDayOfWeek[, FirstWeekOfYear]])

In this syntax,

The DateDiff() function works by taking the first date, represented by the parameter Start_Date, and subtracting it from the second date, represented by the parameter End_Date. After the subtraction takes place, the function returns a number of data type Long, which is the difference between the dates. The unit of measure by which the difference is reported is dictated by the string value of the Interval parameter (see Table 16.4).


Day and day of year, week, and weekday intervals

Although the day ("d") and day of year ("y") intervals are generally interchangeable, the week ("ww") and weekday ("w") intervals can return different results in specific cases. For more information, see the online documentation that comes with Visual Basic.


TABLE 16.4  The Different Values for the DateDiff() Interval Parameter

Value Interval Usage Return Value
"yyyy" Year DateDiff("yyyy", "7/4/76", "7/4/86") 10
"q" Quarter DateDiff("q", "7/4/76", "7/4/86") 40
"m" Month DateDiff("m", "7/4/76", "7/4/86") 120
"y" Day of year DateDiff("y", "7/4/76", "7/4/86") 3652
"d" Day DateDiff("d", "7/4/76", "7/4/86") 3652
"w" Weekday DateDiff("w", "7/4/76", "7/4/86") 521
"ww" Week DateDiff("ww", "7/4/76", "7/4/86") 521
"h" Hour DateDiff("h", "7/4/76", "7/4/86") 87648
"n" Minute DateDiff("n", "7/4/76", "7/4/86") 5258880
"s" Second DateDiff("s", "7/4/76", "7/4/86") 315532800


Using IsDate()

IsDate() is a VB function that checks a string or date value to see whether it's a valid date. If the string or date looks like a valid date, IsDate() returns True; otherwise, it returns False. For example, IsDate("September 16, 1998") and IsDate(#9/16/98#) return True, but IsDate("Birthday") returns False.


The project DateDff.vbp (which you can find on the Web site set up for this book) illustrates how to use the DateDiff() function to report the age of a person in days and in years. It works by having users enter their birthday in a text box, and then click the Start Counting button. Within the Click event procedure for that button, code verifies that the entry looks like a valid date string by using the IsDate() function. If the string is valid, the birthday text is converted to a date and assigned to a global date variable, gf_dtBirthday (gf_ is a prefix denoting that the variable is global to the form), and the Timer is enabled. If the string isn't valid, an error message is shown. After users close the message box, the code sets the cursor back to the text box, highlights the problematic text, and exits the event procedure.

Within the Timer1_Timer() event procedure, which fires every half second (Interval = 500), the Timer control looks at the system date and time by using the Now function. The event procedure uses the DateDiff() function twice--once to measure the difference between the instantaneous time and the birthday time (as assigned to the global birthday variable) in terms of days and once to measure the difference in dates in terms of years. The different return values are assigned to their respective local variables, lYourAgeInDays and lYourAgeInYears. Listing 16.4 shows the Click event procedure and Timer event procedure from the code; Figure 16.4 shows the program displaying calculated values.


Highlighting text in a text box

The code in lines 13 through 18 highlight the user's input by setting the focus to the text box (line 15), selecting the beginning of the string (line 17), and then selecting the entire string (line 19). This technique makes a nice addition because it helps your users locate the spot where they need to re-enter information.


LISTING 16.4  16LIST04.TXT--Source Code That Uses DateDiff()

01 Private Sub cmdStart_Click()
02
03 `Check to make sure that the string "looks"
04 `like a date.
05 If IsDate(txtBDate.Text) Then
06 `If it is a date, convert the text to a date data
07 `type and assign it to the global birthday date
08 `variable
09 gf_dtBirthday = CDate(txtBDate.Text)
10 Else
11 `If it isn't, then report an error
12 MsgBox "You must enter a proper date!", _
vbCritical, "Data error"
13 `Set the cursor back to the textbox
14 txtBDate.SetFocus
15 `Set the cursor to the beginning of the text box
16 txtBDate.SelStart = 0
17 `Highlight the erroneous text
18 txtBDate.SelLength = Len(txtBDate.Text)
19 `Leave the sub
20 Exit Sub
21 End If
22
23 `Turn on the timer
24 Timer1.Enabled = True
25 End Sub
26
27 Private Sub Timer1_Timer()
28 Dim lYourAgeInSecs As Long
29 Dim lYourAgeInDays As Long
30 Dim lYourAgeInYears As Long
31
32 `Calculate the date difference in seconds
33 lYourAgeInSecs = DateDiff("s", gf_dtBirthday, Now)
34
35 `Calculate the date difference in days
36 lYourAgeInDays = DateDiff("d", gf_dtBirthday, Now)
37
38 `Calculate the date difference in years
39 lYourAgeInYears = DateDiff("yyyy", gf_dtBirthday, Now)
40
41 `Report the date differences
42 lblAgeSecs.Caption = CStr(lYourAgeInSecs)
43 lblAgeDays.Caption = CStr(lYourAgeInDays)
44 lblAgeYears.Caption = CStr(lYourAgeInYears)
45 End Sub

FIGURE 16.4 Using the Format() function makes the days and seconds values easier to read.

Using Static Variables with a Timer

Suppose you need to write a program that attempts to do an activity every half second for no more than 10 tries. To do this, you would need to create a variable that keeps track of the number of times the Timer control fires the Timer event. If you create a counter variable within the Timer event, however, every time the Timer event procedure terminates, the variable will go out of scope and reset its value to zero.

Clearly, you can't accomplish what you need to do by using this method. However, there are two alternative methods. First, you can set a global variable, and then increment and check the value of the variable within the Timer event, as shown in Listing 16.5.

LISTING 16.5  16List05.TXT--Using a Global Variable to Keep Track of Timer
Events

01 Sub Timer1_Timer()
02 If g_TimeLimit% > 10 Then
03 MsgBox "Attempts exceeded!"
04 Else
05 `Attempt to do something
06 End if
07
08 g_TimeLimit% = g_TimeLimit% + 1
09
10 End Sub

Although this code will work, it's not optimal because creating a global variable creates a dependency external to the Timer control. If you decided to eliminate the Timer from the code for some reason, this global variable would still be hanging around with no real purpose and at some point would come back to haunt you.


Be careful with Static variables!

Using Static variables doesn't mean that you should abandon declaring global variables within the General section of a form or module. Actually, you should be judicious in your use of Static variables. Nothing is more frustrating than finding a bug in your program that might be caused by a misbehaving global value, and then having to search through your code to determine whether a wrongly assigned Static variable is the culprit. If you need to create variables that will be shared among many areas of your code, you should declare them as Public or Private variables in the General section.


A better strategy for keeping track of the number of times the Timer event procedure executes is to declare a counter variable with the Static keyword, as in Listing 16.6.

LISTING 16.6  16LIST06.TXT--Using a Static Variable to Keep Track of Timer
Events

01 Private Sub Timer1_Timer()
02 `Make a static variable that will retain value even
03 `after the event procedure terminates
04 Static i%
05
06 `Report the present value of i%
07 frmMain.Caption = "Present value of i%: " & CStr(i%)
08
09 `Report the running time
10 lblTime.Caption = Format(Time, "Long Time")
11
12 `Check to see if the counter has exceeded 10 loops
13 If i% >= 10 Then
14 `If the counter is exceeded, send a notice
15 lblLimitNotice.Caption = "Limit Exceeded!"
16 Else
17 `If not, keep the reporting label blank
18 lblLimitNotice.Caption = ""
19 End If
20
21 `Increment the counter
22 i% = i% + 1
23 End Sub

When you create a Static variable, it keeps its value even after the procedure in which it's declared goes out of scope. The advantage of doing this is that the variable is encapsulated within the control on which it depends. The value of the variable persists, regardless of the state of the procedure in which you created it. You can find a program that uses a static variable in the StatVar.vbp project at http://www.mcp.com/info.


Previous chapterNext chapterContents

© Copyright, Macmillan Computer Publishing. All rights reserved.