日時
時に関するクラスとメソッドを紹介。
ロケールと関係あったりしてなかなか興味深い。
主に紹介するクラスは以下。
- Date: 日付を保持している。
- Time: 時刻を保持している。
- Timestamp: 日付と時間(日時と表記)を保持している。
- Delay: 待つときに使う。
その他、以下のクラスも出てくる。
- Duration: 時間差を保持している。
- TimestampPrintPolicy: 国ごとの慣習に合わせたフォーマットで日時を扱うためのクラス。
Date today
最も基本的なメソッドとなる。それ以外は説明不要だろう。
Date newDay: 19 month: 'Sep' year: 2009
月は英語で指定する。最初の3文字まででもよい。
Date newDay: 19 monthNumber: 9 year: 2009
月も数値で指定する。
Date newDay: 262 year: 2009
素のままでは使い道はなさそうだが、Dateに対してdayで年の日を取得して計算してまた日付にする
といったことはできそうだ。
Date readFromString: 'September 19, 2009'
Date readFromString: '19 S 2009'
Date readFromString: '9.19/2009'
Date readFromString: '9-19,2009'
Date readFromString: '19Sep2009'
ここはなかなか奥が深い。基本的には月-日-年の順で指定する。なぜその順なのかというと、
(Locale named: #C) timePolicy dateMiniFormatでその順を指定しているから。
月に指定できる名前は(Locale named: #C) timePolicy xxxMonthNames(longまたはshortまたはnarrow)に
定義されている。
(TimestampPrintPolicy newFor: #ja_JP) reader readDateFrom: '2009年9月19日' readStream
こうやって#ja_JPまたは#jaのように国を指定すると、指定した国の文化に合わせた形式で
日付オブジェクトを作ることができる。
readLocaleDataOfType:for:という長~いメソッドでがんばって解析している。
Locale current: (Locale named: #sw).
(Locale current) readDateFrom: '2009 Septemba 19' readStream
#swがどこの国を表すのかまでは調べていません。
Date today printFormat: #(3 2 1 $/ 1 1)
最初の3つは1:日、2:月、3:年 を示し、年月日の順番を指定する。
4つめは区切り文字を表す。半角スペースにする場合は$だけ指定する。区切らない場合は0を指定するが、1桁の月日が0埋めされないのでお勧めしない。
5つめは月の表示方法で、1:数値、2:英語の月名を3文字省略(Jan、Febなど)、3:英語の月名 を指定する。
最後は年の表示方法で、1:YYYY形式(4桁)、2:YY形式(2桁)を指定する。
stream := WriteStream on: (String new: 20).
Date today printOn: stream policy: (TimestampPrintPolicy newFor: #ja_JP) format: #(#yyyy #年 #mm #月 #dd #日 #dddd).
stream contents
月名や曜日名の変換のため国の言語のコード(ロケールID)を指定する必要がある。
数字だけのフォーマットでよいならTimestampPrintPolicy newでよい。
WriteStreamは余裕を持って定義したほうが効率がよい。(といっても日付を格納する程度なら微々たる差)
以下、シンボルの説明
#yy: 2桁の年、#yyyy: 4桁の年、
#m: 前ゼロなしの月の数値、#mm: 前ゼロありの月の数値、#mmm: 省略型の月名(英語ならJanやFebなど)、#mmmm: 月のフルネーム
#d: 前ゼロなしの日の数値、#dd: 前ゼロありの日の数値、#ddd: 省略型の曜日名(日本語なら月や火など)、#mmmm: 曜日のフルネーム(日本語なら月曜日、火曜日)
Date daysInYear: 2000
Date today daysInYear
閏年なら366、平年なら355を返す。
Date daysInMonth: 'Feb' forYear: 2000
Date today daysInMonth
大の月なら31、2月以外の小の月なら30、2月のうるう年なら29、平年の2月なら28を返す。
Date leapYear: 2000
Date today leap
よくある法則の通り、4で割り切れない年か、100で割り切れて400で割り切れない年は平年、それ以外は閏年で、
平年は0、閏年は1を返す。
true、falseではなく0、1を返すのは、他の計算を行うメソッドで呼んでいるため。
(Date today) + (Date today)
日付オブジェクト同士を加算することに意味があるのかわからないけど、そういうメソッドはある。
(Date today) subtractDate: (Date newDay: 1 year: 2001)
何日経ったか、日数がわかる。時間差は無視。
(Date today) - (Date newDay: 1 year: 2001)
(Date today) differenceFromDate: (Date newDay: 1 year: 2001)
何日経ったか、日数がわかる。もっと差が細かいときは時分秒まで教えてくれる。
時間差を表すクラスDurationのインスタンスで返ってくる。
ちなみに上記の2例は符号が逆になる。使うときは書き順に注意。
Date today addDays: 3
破壊的メソッドで、変数そのものの中身が変わるので注意。
Date today subtractDays: 3
addDaysの逆。破壊的メソッドで、変数そのものの中身が変わるので注意。
foo := Date today.
foo day: foo day year: 2009
Dateの場合、年月日を個別に指定するメソッドは用意されていない(Timestampなら可能)。
foo := Date today.
Date newDay: foo firstDayOfMonth year: foo year
または
foo := Date today.
foo subtractDays: foo dayOfMonth - 1
foo := Date today.
Date newDay: foo firstDayOfMonth + foo daysInMonth - 1 year: foo year
または
foo := Date today.
foo addDays: foo daysInMonth - foo dayOfMonth
foo := Date today.
foo addDays: foo daysInMonth - foo dayOfMonth + ((foo addDays: foo daysInMonth - foo dayOfMonth + 1) daysInMonth min: foo dayOfMonth)
今月末日 + 翌月の日数または今月の日部分の小さい方 を計算すればよい。
bar := Date newDay: 31 month: 'Aug' year:2011.
foo := bar copy.
n timesRepeat:
[foo := foo addDays: foo daysInMonth - foo dayOfMonth + ((foo addDays: foo daysInMonth - foo dayOfMonth + 1) daysInMonth min: bar dayOfMonth)].
foo yourself
nの値を適当な数値に変えて実行。翌月を取得するの応用。
timesRepeatするので、マイナスの数値にしても数ヶ月前を取得することはできない。
Date today year
foo := Date today.
(foo subtractDays: (foo firstDayOfMonthIndex: 4) - 1) year
Date today monthName
Date today monthIndex
Date today day
Date today dayOfMonth
Date today daysLeftInYear
Date today firstDayOfMonth
Date today weekday
Date today weekdayIndex
Date today previous: #Monday
Date today asDays
Date today asSeconds
普通の秒のほか、ミリ秒、マイクロ秒、ナノ秒も取得できるメソッドがある。そこまで要らないと思うのだが・・・
Date today min: (Date fromDays: 20000)
lessFromDate:の結果でどちらを返すか決めている。
xxx < yyy ifTrue: [^xxx] ifFalse: [^yyy] みたいなことを短く書けるので覚えておくと便利(なことがたまにあるかも)。
Date today max: (Date fromDays: 20000)
min:の逆。lessFromDate:の結果でどちらを返すか決めている。
Time now
最も基本的なメソッドとなる。時差を考慮している。
ちなみに時差設定方法はランチャ→System→Settings→TimeZonesで表示された中から自分の国の部分を反転させてDoIt
(インストール手順なんかに書いてある)
Time fromSeconds: 34567
00:00:00からの秒数を指定する。
Time readFromString: '9:19:29 PM'
時間はどこの国でも時分秒の順で指定するので、日付ほどのレパートリーはない。
Time now hours
Time now minutes
Time now seconds
Time new hours: 23 minutes: 59 seconds:59
実はどんな数値でも設定できる。深夜番組の時間だって25:15:00とかをTimeオブジェクトとして持てる。
Time secondClock
Time totalSeconds
今この瞬間の秒数を取得することに何の意味があるのかわからない。
microsecondClockやmillisecondClockValueもある。
Transcript cr; show: (Time millisecondsToRun: [1 to: 10000 do: [:i | Time now]]) printString
ブロックで囲むだけでその部分の処理時間がわかるようになる。Smalltalkの柔軟性の高さがわかる。
もっと短い時間を計測する場合はmicrosecondsToRun:を使う。
(Time readFromString: '2:59:59') subtractTime: Time now
(Time readFromString: '23:59:59') addSeconds: 3600
(Time readFromString: '23:59:59') subtractTime: Time now
(Time readFromString: '23:59:59') subtractSeconds: 3600
Time now min: Timestamp zero asTime
lessFromTime:の結果でどちらを返すか決めている。
xxx < yyy ifTrue: [^xxx] ifFalse: [^yyy] みたいなことを短く書けるので覚えておくと便利(なことがたまにあるかも)。
Time now max: Timestamp zero asTime
min:の逆。lessFromTime:の結果でどちらを返すか決めている。
Timestamp zero
後付けで値を設定していきたい場合に使うのかと思うが、使いにくそうだ。
もちろん出荷時イメージの中で使われている箇所は一つもない。また Timestamp new と書いても同じ。
Timestamp now
最も基本的なメソッドとなる。時差を考慮している。
ちなみに時差設定方法はランチャ→System→Settings→TimeZonesで表示された中から自分の国の部分を反転させてDoIt
(インストール手順なんかに書いてある)
Timestamp nowUTC
時差を考慮しない日時を返す
Timestamp fromDate: Date today andTime: Time now
Timestamp fromArray: (Array with: Date today with: Time now)
Timestamp fromSeconds: 5000
1900年1月1日 00:00:00 からの経過秒を計算する。
もっと細かく設定したい場合は fromMilliseconds:、fromMicroseconds:、fromNanoseconds:も用意されている。
Timestamp readFromDateAndTime: '9/19/2009 11:11am' readStream
Timestamp readFrom: '9/19/2009 11:11am' readStream
ここはなかなか奥が深い。基本的には月-日-年の順で指定する。なぜその順なのかというと、
(Locale named: #C) timePolicy dateMiniFormatでその順を指定しているから。
月に指定できる名前は(Locale named: #C) timePolicy xxxMonthNames(longまたはshortまたはnarrow)に
定義されている。
(TimestampPrintPolicy newFor: #ja_JP) reader readTimestampFrom: '2009年9月19日 10:11:12午後' readStream
こうやって#ja_JPまたは#jaのように国を指定すると、指定した国の文化に合わせた形式で
日付オブジェクトを作ることができる。
readLocaleDataOfType:for:という長~いメソッドでがんばって解析している。
午前・午後を指定するフォーマットはこれだけだと思われる。
'2009年9月19日 午後10時11分12秒'などと指定してみたが22時にはならなかった。
Locale current: (Locale named: #sw).
(Locale current) readTimestampFrom: '2009 Septemba 19 10:11:12' readStream
#swがどこの国を表すのかまでは調べていません。
stream := WriteStream on: (String new: 40).
Timestamp now printOn: stream policy: (TimestampPrintPolicy newFor: #ja_JP) format: #(#yyyy '年' #mm '月' #dd '日 ' #dddd ' ' #AMPM #hh '時' #mm '分' #ss '秒').
stream contents
月名や曜日名の変換のため国のコードを指定する必要がある。
WriteStreamは余裕を持って定義したほうが効率がよい。(といっても日付を格納する程度なら微々たる差)
以下、シンボルの説明
#yy: 2桁の年、#yyyy: 4桁の年、
#m: #hや#hhの次に来ていれば前ゼロなしの分の数値それ以外は前ゼロなしの月の数値、#mm: #hや#hhの次に来ていれば前ゼロありの分の数値それ以外は前ゼロありの月の数値
#mmm: 省略型の月名(英語ならJanやFebなど)、#mmmm: 月のフルネーム
#d: 前ゼロなしの日の数値、#dd: 前ゼロありの日の数値、#ddd: 省略型の曜日名(日本語なら月や火など)、#mmmm: 曜日のフルネーム(日本語なら月曜日、火曜日)
#ap: ampmのaまたはpのみ(小文字)、#AP: AMPMのAまたはPのみ(大文字)、#ampm: amまたはpm(小文字)、#AMPM: AMまたはPM(大文字)、
※日本語ではどれを指定しても午前・午後のバリエーションしかない
#h: 前ゼロなしの時の数値、#hh: 前ゼロありの時の数値
#s: 前ゼロなしの秒の数値、#ss: 前ゼロありの秒の数値
シンボルだけがトークンとみなされるので、合の手(年や月)は
文字列で指定する方がよい。
すべてシンボルで指定すると #hh #時 #mm #分 の部分で、前のトークンが #hh となっていない #mm が来たので月とみなされてしまう。
Timestamp now daysInYear
閏年なら366、平年なら355を返す。
Timestamp now monthIndex: 360
インデックスが0スタートの月を返す。1月に相当するなら0、12月に相当するなら11になる。
マイナスの引数ならエラーになり、365以上の引数を指定すると11になる。
(Timestamp now) + (Timestamp now)
日時オブジェクト同士を加算することに意味があるのかわからないけど、そういうメソッドはある。
(Timestamp now) - (Date today asTimestamp)
Timestamp now differenceFromDate: Date today asTimestamp
Timestamp now differenceFromTimestamp: Date today asTimestamp
何秒経ったか、秒数がわかる。
時間差を表すクラスDurationのインスタンスで返ってくる。
ちなみに上記の-とdifference...は符号が逆になる。使うときは書き順に注意。
(Timestamp now) addSeconds: 3600
ミリ秒を加えるaddMilliseconds:もある。
(Timestamp now) addSeconds: 3600
ミリ秒を引くaddMilliseconds:もある。
Timestamp zero
year: 2009; month: 9; day: 19;
hour: 10; minute: 11; second: 12;
millisecond: 200; yourself
日時の場合は全て個別に設定できる。
Timestamp now year
Timestamp now month
Timestamp now day
Timestamp now dayOfMonth
Timestamp dayOfYear
Timestamp now hour
Timestamp now minute
Timestamp now second
Timestamp now millisecond
Timestamp now asDate
Timestamp now asTime
Timestamp now asSeconds
普通の秒のほか、ミリ秒、マイクロ秒、ナノ秒も取得できるメソッドがある。そこまで要らないと思うのだが・・・
Timestamp now min: (Date fromDays: 20000)
lessFromTimestamp:の結果でどちらを返すか決めている。
xxx < yyy ifTrue: [^xxx] ifFalse: [^yyy] みたいなことを短く書けるので覚えておくと便利(なことがたまにあるかも)。
この場合、Date fromDays: 20000の方が小さいのでDateクラスが返ってくる。
Timestamp now max: (Date fromDays: 20000)
min:の逆。lessFromTimestamp:の結果でどちらを返すか決めている。
(Delay forSeconds: 5) wait
一度インスタンスを作ってからwaitを送るという方式でちょっと面倒くさい。
もっと短く待ちたければforMilliseconds:やforMicroseconds:を使う。
(Delay untilSeconds: Timestamp now asSeconds + 5) wait
○時○分から処理を開始したいなんていう時に使う。
でも、そういうバッチ処理系はSmalltalkなんか使わないでスケジューラを組もう。
foo := Delay forSeconds: 1.
[foo wait] forkAt: 70.
foo inProgress
待ち状態ならtrueを返す。
ブロックにforkAt:を送ると、ブロックを引数の優先レベルで実行する。
100が最高で、70は優先度がまあまあ高いので、bar inProgressより早く実行される。
foo := Delay forSeconds: 1.
[foo wait] forkAt: 70.
foo resume
waitしているのをやめて、次の行を即実行し始める。
foo := Delay forSeconds: 1.
[foo wait] forkAt: 70.
foo shutdown
これをやるとずっと待ち続けたままになる。再開するには次のメソッドを参照。
foo := Delay forSeconds: 1.
[foo wait] forkAt: 70.
foo shutdown.
foo startup
再開するといっても、shutdownした時点での残りを待つのではなく、forXX:で指定した時間をもう一度最初から待つ。
foo := Delay forSeconds: 1.
[foo wait] forkAt: 70.
foo terminate
これをやると待ち状態ではなくなるし、ブロック内のその後の行も実行されないし、
startupで再開することもできなくなる。
最終更新:2011年11月26日 12:07