パソコン活用研究5番街(Visual Basic、Excel(VBA)、BASIC プログラミング研究)
時刻・日付の制御 (Time, Time$, Timer, Interval, Date$)
時刻や日付を制御する関数、命令についてとりあげてみます。このうちTIME$,
DATE$は
どのBASICでもほぼ共通ですが、それ以外はBASICによって実装されていたり、いなかったり
また、仕様が違ったりと方言がいろいろ存在するする命令のひとつです。
1 TIME$, TIME, TIMER
(1) 基本的な用法
TIME$は時刻を与える関数です。この関数には以下の2つの用法があります。(一部のBASIC
にはAの用法がなかったりする)
@この関数を使用した時点のPCの内蔵タイマーの時刻を取得する
A時刻(内蔵タイマーの時刻)を変更する
では、それぞれの用法について説明します。
@時刻の取得
TIME$には現在の時刻が"HH:MM:SS" (例えば11時15分20秒なら、"11:15:20")の形式で格納
されています。時刻を表示するには、
PRINT TIME$
とすれば、現在時刻が 11:15:20 のように画面上に表示されます。
画面表示の例は99Basicでの実行の様子を参照して下さい。
【補足】
最近のPCは内蔵バッテリーで常に内蔵タイマーに電源が供給されているので、常に時刻は最新
の時刻を保持しています。しかし昔のPCは、電源を入れた直後に"00:00:00"からスタートするもの
もありました。こういう昔のPCで得られる時刻は、PC起動後の使用時間であり、今の時刻が得ら
れるわけではありません。(FM-7/8など相当昔のPCですけどね)
A時刻の変更
TIME$="HH:MM:SS" と記述すると、時刻をHH:MM:SSに変更することができます。
例えば、
TIME$="21:10:15"
とすると、時刻(PCの内蔵タイマー)が21時10分15秒に変更されます。
【補足】
99BASICでは、Aの用法はありません。
Windows上で使用する、N88互換BASIC for Win, QBasic, その他旧PCのエミュレーター
(NEXT, XM7)上のBASIC(N88Basic, F-Basic)でAの用法で時刻を変更すると、それぞれの
アプリケーション(エミュレーター)上では時刻が変更されますが、実際のPCの内蔵タイマーの
時刻は変わりません。
(2) 時刻の取り出し
例えば、TIME$から秒の部分だけを取り出したい、といような場合は
RIGHT$(TIME$, 2)
と記述すれば秒の部分だけ(文字変数)とりだせます。
取り出した秒の部分を数値変数にして使いたいなら
VAL(RIGHT$(TIME$, 2)
とすればいいわけです。
これは、よく乱数の系列を変更する場合に使うテクニックです。ゲームなどで起動のたびに、
乱数系列を変えたい場合(そうしないと、毎回同じ乱数系列になっちゃうからね)、
RANDOMIZE(VAL(RIGHT$(TIME$, 2)
という記述をしてやると、起動のタイミングにより、0から59の数値をランダムに、RANDOMIZE
に渡すことができます。
(3) 経過時間の計測
BASICによってはTIMEというある基点の時間からの経過時間を数値として保持している命令
を持っているものもありますが、ないBASICも多々あります。
ない場合は、TIME$から工夫して経過時間を計測できるようにしなければなりません。その場合
時、分、秒をそれぞれ取り出して、ひとつの数値変数にまとめます。例えば、以下のようにします。
(行番号省略)
TIM=VAL(LEFT$(TIME$,2))
MIN=VAL(MID$(TIME$,4,2))
SEC=VAL(RIGHT$(TIME$,2))
CURRENTTIME=TIM*3600 + MIN*60 + SEC
これを利用すると、1秒毎に時刻を表示して、30秒経過したら終了するプログラムは以下のように
なります。
10〜30 開始時刻startの計算
50〜70 現在時刻currentの計算
80 1秒毎に表示サブルーチン(100行)へ
90 30秒経過したら終了
10 cls:dummy$=time$ 20 starttime=val(left$(dummy$,2)): startmin=val(mid$(dummy$,4,2)): startsec=val(right$(dummy$,2)) 30 start=starttime*3600+startmin*60+startsec 40 print time$; 50 dummy$=time$: old=current 60 currenttime=val(left$(dummy$,2)): currentmin=val(mid$(dummy$,4,2)): currentsec=val(right$(dummy$,2)) 70 current=currenttime*3600+currentmin*60+currentsec 80 if current-old>0 then gosub 100 90 if current-start>30 then goto 200 else goto 50 100 print time$;" ";:return 200 print "30秒経過":end |
*このままだと、日付をまたぐ場合 ("23:59:59" -->"00:00:00")には、30秒を正しく計測でき
ません。各自で工夫してみて下さい。
(4) TIME, TIMER
TIMEはシステム起動後(PC起動後)の経過時間が格納されている、システム変数です。
TIMEはBASICによって仕様が違うもののひとつで、F-Basic, 99Basicにはあり,QBasicでは
TIMERというシステム変数名が同じような機能のものです。(それぞれ若干仕様が異なる)
N88-Basic, N88互換Basic for Winにはありません。
F-Basic, 99Basicでは、
PRINT TIME
とすると、システム起動後の経過時間を秒で表示します。(補足参照のこと)
QBasicのTimerはTime$と連携しており、深夜0時"00:00:00"から起算した現在時刻(TIME$)
の秒数を(小数点第2位まで)格納しています。従って、TIME$の値を変更すると、TIMERの
値もそれにつれて変化します。TIMERへの値の代入(すなわち値の変更)はできません。
TIMEが実装されていないBASICでも、1−(3) 経過時間の計測でやったように、
(行番号省略)
TIM=VAL(LEFT$(TIME$,2))
MIN=VAL(MID$(TIME$,4,2))
SEC=VAL(RIGHT$(TIME$,2))
CURRENTTIME=TIM*3600 + MIN*60 + SEC
という計算をしてやると、システム起動後の経過時間などを得ることができます。
【補足】
99BasicではTIME$とTIMEは連携しておらず、TIME$には常に現在時刻が格納され、TIME$への
変更は不可ですが、TIMEには代入もできて、TIME = 0 としてTIMEをリセットすることもできます。
ただし、TIME$とは連携しておらず、TIMEの値を変更しても、TIME$の値には何の影響もないよう
です。
F-BASICのTIMEは、"00:00:00"から起算したTIME$の秒数を格納していますが、FM-7/8の内蔵
タイマーは電源を入れた後の経過時間を保持しているので、TIME$は、現在時刻ではなく、システ
ム起動時間(≒PC起動後の経過時間)となります。従って、F-BASICの場合のTIMEは、深夜0時
から起算した現在時刻(TIME$)の秒数ではなく、システム起動時間をあらわす変数ととらえるのが
正解です。
BASICによって微妙に仕様が違いますので、お使いのBASICの仕様をよく確認して下さい。
N88BASIC, F-BASICでは、実機では、TIME$ = "hh:mm:ss" で内蔵タイマーの時刻が変更
されますが、エミュレーターNEXT, XM7 上ではそのエミュレーター上のシステム時刻は変わる
けど、本体のPCの内蔵タイマーの時刻が代るわけではない、とか・・・、ややこしいので、実際
に動作を確認して使ってみて下さい。
2 ON TIME$(TIME) GOSUB .... , TIME$(TIME) ON/OFF/STOP
(1) 基本的な用法
ON TIME$(TIME) GOSUB ... は、ある時刻になったらGOSUBで指定するサブルーチンに飛ばす
という命令(タイマ割り込み命令)です。PCで目覚まし時計などが作れそうですね。
TIMEのところで各種BASICの仕様が異なっていましたが、このため、ON TIME$(TIME)
GOSUB
もBASICによって微妙に仕様が異なる少々厄介な命令です。
N88BASICでは、ON TIME$="hh:mm:ss" GOSUB ... を使用します。たとえば、
ON TIME$="12:00:00" GOSUB 1000
とすると、12:00:00になると1000行目のサブルーチンに飛びます。
サブルーチンからの復帰はRETURNですが、単にRETURNとした時は中断した場所から再開し、
後ろに行番号を指定した時は、その行から再開します。
ただし、行番号つきのRETURNの場合は、割り込みが発生した箇所と同じスタックレベルの行に
戻るようにしないといけません。そうしないと、スタック領域が異常消費されプログラムの誤動作が
生じるかもしれません。(ということは行番号つきのRETURNは使わない方がいい、ということ)
TIME$ ON, TIME$ OFF, TIME$ STOP はそれぞれ、
タイマ割り込み許可、割り込み禁止、割り込み停止の命令です。
ON TIME$ GOSUB ... を記述しても、TIME$ ON によって割り込みが許可されないと、タイマ
割り込みは発生しません。
TIME$ STOP にするとタイマ割り込みは停止され、指定時刻になっても割り込みは発生しません。
しかし、TIME$ ON によって割り込みが許可されると指定の行に分岐します。
このタイマ割り込みの注意点ですが、この命令は、ON TIME$="hh:mm:ss"
GOSUB ... が実行さ
れた時点で、設定された時刻から現在の時刻を引き算して、割り込み時刻を決定します。従って、
この命令後にTIME$で現在時刻の変更を行っても、引き算で得られた所定の時間が経過すると、
割り込みが実行されます(当然、割り込み設定された時刻と現在時刻には狂いがでます)。
F-BASICでは、ON TIME GOSUB ... でタイマ割り込みルーチンの定義をし、タイマ割り込みの
時刻指定は、TIME "hh:mm:ss" という書式でやります。
例えば、12:00:00 に割り込みを書ける場合は
50 ON TIME GOSUB 1000
60 TIME "12:00:00"
のように記述します。
タイマ割り込みの許可、禁止、停止は
TIME ON, TIME OFF, TIME STOP
を使用します。
99Basicの場合は、タイマ割り込みの定義は ON TIME GOSUB .... です。
時刻指定は、TIME ON, 時間(684800までの数値) という書式です。
99BASICのTIMEはシステム起動後の経過時間を保持していますので、システム起動後1000秒後
に割り込みを発生させたいなら
50 ON TIME GOSUB 1000
60 TIME ON, 1000
のように記述できます。
タイマ割り込み禁止、停止は、TIME OFF, TIME STOP です。
99Basicでは一度タイマ割り込みが実行されると、自動的にTIME OFF(割り込み禁止)状態になり
ます。
QBasic、N88互換Basic for Winではこのタイマ割り込みは実装されていないようです。
(2) インタバルタイマー的に使う
99BasicのTIMEは値の代入ができるため、次のようにするとインターバルタイマー(一定間隔での
割り込み)のような使い方ができます。下の例は5秒毎に割り込み、現在時刻を表示するプログ
ラムです。40〜50行めは無限ループで、xが10になると脱出します。5秒毎に100行目からのサブ
ルーチンにとび、Xを+1しています。99Basicではタイマ割り込みが実行されると自動的に割り込み
禁止状態になるので、サブルーチンの中で、タイマのリセット(TIME = 0)と、タイマ割り込みの再設定
(TIME ON, 5)をしています。
まあ、99Basicには後述のように、インタバール割り込み専用の命令もあるので、こんな小細工は
不要ですが。
10 TIME=0 20 ON TIME GOSUB 100 30 TIME ON, 5 40 WHILE X<10 50 WEND 60 END 100 X=X+1 110 TIME=0 120 TIME ON, 5 130 PRINT TIME$ 140 RETURN |
実行の様子
N88Basicのタイマ割り込みを利用して、無理やりインターバルタイマ割り込みの機能を実現
させることもできます。下のプログラムも5秒毎のインターバルタイマ割り込みをしています。
ちょっと工夫したのは、
100行目 DUMMY$を使用して現在時刻TIME$をいったん退避し
140行目 TIME$=DUMMY$ で現在時刻をもどしてやっている
点です。
サブルーチンの中の、1000行目と1030行目も同様です。
こうすることにより、現在時刻を保持したまま、インターバルタイマ割り込みを実現させています。
まあ、そうは言っても余計な作業をこなす(1010〜1020行)分、少しづつ遅れていくので、長い
間には本当の時刻との誤差がそれなりにでてきてしまいますが。
100 DUMMY$=TIME$ 110 TIME$="00:00:00": F$="00:00:05" 120 ON TIME$=F$ GOSUB 1000 130 TIME$ ON 140 TIME$=DUMMY$ 200 WHILE X<10 210 WEND 1000 X=X+1: DUMMY$=TIME$ 1010 TIME$="00:00:00" 1020 ON TIME$=F$ GOSUB 1000 1030 TIME$=DUMMY$ 1040 PRINT TIME$ 1050 RETURN |
3 ON INTERVAL(TIMER) GOSUB
一定の間隔で定期的に割り込みをかける機能を「インターバルタイマ割り込み」と言います。
上記の2−(2)でタイマ割り込みを工夫してインターバルタイマ割り込みの機能を実現させる
プログラムを掲載しましたが、F-Basic, QBasic, 99Basicではインターバルタイマ割り込み専用
の命令をもっています。
この命令もBasicによって仕様が違うので、それぞれのBasicの用法をよく理解して使って下さい。
ちなみにN88Basic, N88互換Basic for Winにはインターバルタイマ割り込みはありません。
これらのBasicでインターバルタイマ割り込み機能を実現したい場合は、上記のようにタイマ割り込み
を工夫してインターバルタイマー的に使って下さい。
F-Basic
ON INTERVAL GOSUB ... でインターバルタイマ割り込みの定義をします。
割り込み間隔の設定は、
INTERVAL 割り込み間隔(秒)
で指定します。
割り込み間隔は秒で指定します。(最大65535秒まで)。このINTERVAL文が実行されると、
指定された秒間隔で割り込みが発生します。割り込み許可状態(INTERVAL ON)であれば、
そのつど、GOSUBで指定したサブルーチンへ飛びます。
インターバルタイマ割り込みの許可、禁止、停止は
INTERVAL ON, INTERVAL OFF, INTERVAL STOP
です。
INTERVAL STOP により、割り込み動作は一時停止されます。割り込み自体は受け付けられ、
次にINTERVAL ONが実行されると直後に割り込みルーチンの呼び出しが行われます。
例えば5秒毎に現在時刻を表示するプログラムは以下のようになります。
10 ON INTERVAL GOSUB 100 20 INTERVAL 5 30 INTERVAL ON 40 GOTO 40 100 PRINT TIME$: RETURN |
99Basic
99Basicのインターバルタイマ割り込みはF-Basicとだいたい同じですが、割り込み間隔の指定が
若干異なります。
インターバルタイマ割り込みの定義は、ON INTERVAL GOSUB ... です。
割り込み許可と割り込み間隔の指定ひとつの命令にまとめられており、
INTERVAL ON, 割り込み間隔(1/100秒単位)
となります。割り込み間隔が1/100秒単位の指定になる点に注意して下さい。
割り込み禁止、停止は、INTERVAL OFF, INTERVAL STOP です。
99Basicの仕様として、割込が発生して、ON INTERVAL GOSUB 命令で指定した割込ルーチンの
実行を開始する時、自動的に INTERVAL STOP の状態になりますので注意して下さい。
割込頻度が高すぎて、割込ルーチン実行中に再び INTERVAL 割込がかかってしまうのを防ぐた
めです。割込ルーチンから RETURN すると、自動的に割込 ON の状態に戻ります。
QBasic
QBasicではインターバルタイマ割り込みにTIMERという命令を使います。
インターバルタイマー割り込みの定義と割り込み間隔の指定は
ON TIMER(割り込み間隔) GOSUB ...
という書式になります。割り込み間隔は秒単位で指定します。
割り込み許可、禁止、停止は
TIMER ON, TIMER OFF, TIMER STOP
です。
1秒毎にビープ音をならし、現在時刻を表示し、10秒経過したら終了するプログラムです。
ON TIMER(1) GOSUB timeupdate TIMER ON CLS PRINT "TIME"; TIME$ starttime = TIMER WHILE timepast < 10 timepast = TIMER - starttime WEND END timeupdate: BEEP PRINT TIME$ RETURN |
4 DATE$, DATE
(1) DATE$
DATE$は(PCの内蔵タイマーが保持している)日付を与える関数です。
用法としては、以下の2つがありますが、BASICによってはAの用法がなかったりします。
@この関数を使用した時点のPCの内蔵タイマーの日付を取得する
A日付(内蔵タイマーの時刻)を変更する
それぞれの用法について説明します。
@日付の取得
DATE$には現在の日付が"yy/mm/dd" (例えば2005年12月24日なら、"05/12/24")の形式で格納
されています。時刻を表示するには、
PRINT DATE$
とすれば、現在時刻が 05/12/24 のように画面上に表示されます。
N88互換Basic for Win での PRINT DATE$ を実行例
*Qbasicは英語表示なので若干異なります。補足を参照して下さい。
A日付の変更
DATE$="yy/mm/dd" と記述すると、日付をyy/mm/ddに変更することができます。
例えば、
DATE$="05/12/25"
とすると、日付(PCの内蔵タイマー)が2005年12月25日に変更されます。
*Qbasicは英語表示なので若干異なります。補足を参照して下さい。
【補足】
99BASICでは、Aの用法はありません。
Windows上で使用する、N88互換BASIC for Win, QBasicでAの用法で日付を変更すると、実際の
PCの内蔵タイマーの日付も変更になるようですが、2000年問題のため正しく変更されない日付が
あります。その他旧PCのエミュレーター(NEXT, XM7)上のBASIC(N88Basic, F-Basic)でAの用法
で時刻を変更すると、それぞれのアプリケーション(エミュレーター)上では時刻が変更されますが、
実際のPCの内蔵タイマーの日付は変わりません。
また、QBasicは英語表示なので、mm-dd-yy (月-日-年)のようになります。
参考までにプログラム例をあげておきます。
実行例です。 2006年6月17日 =>1906年5月6日に変更
単純にDATE$="05-06-06"とすると1906年5月6日に変更されてしまいました。
2006年5月6日に変更したい場合は DATE$="05-06-2006"とすればいいようです。
(2) DATE
F-BASICには1月1日を基準日とした、現在の日付のトータル経過日数を示すシステム変数がありました。
例えば、1月25日だと
PRINT DATE
とすると 25 を返します。
他のBASICには実装されているのを見たことなく、使うこともないので忘れて下さい。