Tag Archives: timer

Elapsed Timer Using Pure DOS (MS-DOS)

A while back, I discussed a Windows Script Host script for printing elapsed time updates to the terminal screen. It was more a mental exercise for me than something I thought would wow my readers, but a few people found it useful, which I was pleased to learn about in the comments. As mentioned in that previous post, some of the machines I work on everyday are limited in their installed software and in their ability to download the web’s finest utilities, which was the impetus for my creating such a script. But suppose you’re even more of a purist and would like to run such a script using purely DOS? The script in the listing below will do it for you.

One technique worth highlighting here is my use of the poor man’s “SLEEP” command near the bottom, which is mimicked by pinging a bogus IP address. Since DOS has no “sleep” mechanism, leveraging the ping timeout is one way to simulate sleep. Also useful is the technique for extracting hours, minutes, and seconds from the current time variable (see a separate note about that below).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
@Echo Off
REM AUTHOR: www.dullsharpness.com
REM ~~~~~~~~~~~~~~~~~~~~~
REM     Set Variables Here
REM ~~~~~~~~~~~~~~~~~~~~~
REM Ping interval is the timeout in seconds before a ping tries again.
REM Therefore this variable dictates how often the time gets
REM printed to the screen. Consider it the "sleep" interval.
SET PINGINTERVALS=5
REM We use a bogus IP because we want the ping to timeout.
SET BOGUS_IP=128.0.0.1
REM ~~~~~~~~~~~~~~~~~~~~~
REM     END User Variables
REM ~~~~~~~~~~~~~~~~~~~~~

REM Convert the ping interval seconds to millseconds
SET /A PINGINTERVALMS=%PINGINTERVALS%*1000

REM Print Current Time
echo. | time | find /i "current"

REM Determine the start time values
FOR /F "usebackq tokens=1,2,3 delims=:" %%i in (`echo %Time:~0,8%`) DO (
 set START_HOUR=%%i
 set START_MINUTE=%%j
 set START_SECOND=%%k
)

REM Clean up leading zeroes if there are any,
REM otherwise calculations get screwy
If %START_HOUR:~0,1%==0 set START_HOUR=%START_HOUR:~1,1%
If %START_MINUTE:~0,1%==0 set START_MINUTE=%START_MINUTE:~1,1%
If %START_SECOND:~0,1%==0 set START_SECOND=%START_SECOND:~1,1%

REM Come back and iterate from this point after ping timeout
:iterate
FOR /F "usebackq tokens=1,2,3 delims=:" %%i in (`echo %Time:~0,8%`) DO (
 set CUR_HOUR=%%i
 set CUR_MINUTE=%%j
 set CUR_SECOND=%%k
)

REM Clean up leading zeroes if there are any
If %CUR_HOUR:~0,1%==0 set CUR_HOUR=%CUR_HOUR:~1,1%
If %CUR_MINUTE:~0,1%==0 set CUR_MINUTE=%CUR_MINUTE:~1,1%
If %CUR_SECOND:~0,1%==0 set CUR_SECOND=%CUR_SECOND:~1,1%

REM Calculate using a set of "CALC" values.
REM We want to leave "CUR" values untouched
set CALC_HOUR=%CUR_HOUR%
set CALC_MINUTE=%CUR_MINUTE%
set CALC_SECOND=%CUR_SECOND%

REM Start by calculating seconds, which need to rollover every minute
If %CUR_SECOND% LSS %START_SECOND% (
 set /A CALC_SECOND=%CALC_SECOND%+60-%START_SECOND%
 set /A CALC_MINUTE-=1
) Else (
 set /A CALC_SECOND-=%START_SECOND%
)

REM Minutes here. They need to rollover every hour.
If %CUR_MINUTE% LSS %START_MINUTE% (
 set /A CALC_MINUTE=%CALC_MINUTE%+60-%START_MINUTE%
 set /A CALC_HOUR-=1
) Else (
 set /A CALC_MINUTE-=%START_MINUTE%
)
 
set /A CALC_HOUR=%CALC_HOUR%-%START_HOUR%

REM Prepend a leading zero when necessary
If %CALC_HOUR% LSS 10 set CALC_HOUR=0%CALC_HOUR%
If %CALC_MINUTE% LSS 10 set CALC_MINUTE=0%CALC_MINUTE%
If %CALC_SECOND% LSS 10 set CALC_SECOND=0%CALC_SECOND%

REM Print the elapsed time
echo %CALC_HOUR%:%CALC_MINUTE%:%CALC_SECOND%

REM Prepare for the next iteration
set PREV_HOUR=%CUR_HOUR%
set PREV_MINUTE=%CUR_MINUTE%
set PREV_SECOND=%CUR_SECOND%

REM Ping a bogus IP to mimic "sleep" behavior
ping -n 1 -w %PINGINTERVALMS% %BOGUS_IP% > nul
GOTO Iterate

Note the following:

  • The ping timeout “sleep” approach is not perfect, and your elapsed time printouts won’t always be at exactly the interval you specify. But the elapsed time calculation itself is not a function of that interval, so printed times are still correct

  • Setting the ping interval to anything less than 3 or 4 seconds can cause the printouts to be a little erratic. This is related to the previous bullet about the ping/sleep approach being imperfect

  • While the FOR loop used above (line 23) to set hour/minute/second variables is effective, it could be simplified by setting the variables like so:

    set HOURVARIABLE=%TIME:~0,2%
    set MINUTEVARIABLE=%TIME:~3,2%
    set SECONDVARIABLE=%TIME:~6,2%

    This works because %TIME% is a special DOS variable that always represents the current timestamp, and the syntax shown performs string extraction. Additional special DOS variables (%DATE%, %CD%, etc.) and string extraction examples are shown in the SET command reference (type “help set” or “set /?” at a command prompt)

    The reason I chose to demonstrate the FOR loop technique in the main script is because it can be applied more universally, such as when the text strings you’re operating on are less predictable, delimited differently, etc.


  • Line 20 shows a technique I use often, wherein “echo.” issues a hard return. So I’m actually piping a hard return into the “TIME” command (which would otherwise prompt me to enter a new time). Then I’m piping that output into a “FIND” so it only grabs the line I’m interested in. Alternative approaches are:
    • “TIME /T” which prints current time as well, but not always in the format desired.
    • Echo The current time is: %TIME%” which could have produced results identical to the series of piped commands in the main listing.

  • Last Note: I think this script misbehaves when the day rolls over while it’s running. Fixing that issue is left as an exercise for the reader.

To be clear, I don’t expect this script to be one that anyone’s clamoring for. Sure, it could be useful. But it was really about the challenge of creating something seemingly simple, yet doing it with a very limited toolset. I’m publishing it primarily because it demonstrates some techniques that others might find useful when also trying to accomplish something seemingly simple using pure DOS.

As always, please leave comments if this is helpful. Enjoy.

Stopwatch Timer Using VBScript / WSH

Where I work, we run some pretty CPU-intensive reports. Users access the reports by clicking a web link, after which the server queries the hell out of database and then returns a neatly rendered PDF. Recently, we’ve run into some issues whereby the app server reaches its 5-minute timeout before the report server can spit back its PDF. The result is that the user’s session blows up and he has to start from scratch having never seen his report.

So for the last week, we’ve been hacking at a solution for this. The way it usually works throughout the day is the DBAs go and do their DBA thing by tweaking global this-that-or-the-other-parameter, and then they yell over the cubicle wall, “Ok, try it now!” Then one of the application guys clicks the link, starts a stopwatch (if he has one), and watches the clock mindlessly as it approaches the 5-minute session explosion threshold. We haven’t fixed the issue yet.

But since I had a few blocks of 5 minutes, and since I don’t have a stopwatch, I decided to create one with the tools at hand. And my tools were few because:

  • The machine used to monitor the app for this exercise is a Windows machine with little or no dev tools beyond what’s out of the box
  • I can’t download web software to this machine

That pretty much left me with a DOS prompt. But I like a challenge.

The code you see below is the quickest way I could come up with to tackle this. It uses Windows Script Host (WSH). I plan to write a follow-up to this post showing another way, which uses pure DOS.

The gist here is that when you kick this thing off, it’ll begin to spit out elapsed time updates to the console. See here:

'********************************
'* DESCRIPTION: WSH/VBS Stopwatch utility
'* AUTHOR: http://www.DullSharpness.com
'*
Option Explicit 'Option Explicit forces variable declaration

'Declare some variables
Dim Hour
Dim Minute
Dim Second
Dim StartTime
Dim Elapsed

StartTime = Timer 'Start the Timer
Wscript.Echo Time 'Print the current time

Elapsed = Timer - StartTime 'Initialize Elapsed variable (not critical)

'Modify the next 2 lines for your needs; e.g. "Do While 1 = 1" will
'make it run indefinitely until you Ctrl+C out of it
Do While Elapsed < 120   '120 means it'll only go for 2 minutes
  WScript.Sleep(1000) 'Pause for 1000 milliseconds before printing next update
  Elapsed = Timer - StartTime 'Calculate elapsed seconds
  WScript.Echo PrintHrMinSec(Elapsed) 'Print the time
Loop

Wscript.Echo Time 'Print the current time
'Main script ends here

'***********************
'* This function calculates hours, minutes
'* and seconds based on how many seconds
'* are passed in and returns a nice format
Public Function PrintHrMinSec(elap)
  Dim hr
  Dim min
  Dim sec
  Dim remainder

  elap = Int(elap) 'Just use the INTeger portion of the variable

  'Using "\" returns just the integer portion of a quotient
  hr = elap \ 3600 '1 hour = 3600 seconds
  remainder = elap - hr * 3600
  min = remainder \ 60
  remainder = remainder - min * 60
  sec = remainder

  'Prepend leading zeroes if necessary
  If Len(sec) = 1 Then sec = "0" & sec
  If Len(min) = 1 Then min = "0" & min

  'Only show the Hours field if it's non-zero
  If hr = 0 Then
     PrintHrMinSec = min & ":" & sec
  Else
     PrintHrMinSec = hr & ":" & min & ":" & sec
  End If

End Function

Assuming you save this text to a file named “VBSTimer.vbs”, run it using:

cscript VBSTimer.vbs

Things to note about this script:

  • It utilizes very little CPU, and thus runs nicely in the background.
  • It may fail after 2^15 (32,768) seconds or at some other notable power of 2 number. I’m not sure.
  • You definitely want to run this using the CScript engine and NOT WScript. WScript causes an interactive MsgBox to pop up everytime it hits a “WScript.Echo” command, whereas CScript echoes passively to the DOS console.
  • You can suppress the WSH version and copyright info by issuing the “//NOLOGO” switch on the command line when you run the script

Leave a comment if you find any of this useful, and enjoy!

Stopwatch in Action

Stopwatch in Action