日付を扱う、Date入門

時間を競うタイピング練習ソフトや投稿の日付など、時間や日時を扱う事はちょくちょくあります。 AndroidではDateというものを使います。importはjava.util.Dateです。

Dateは日付、って意味の英単語です。

一応自分のためにJavaのDate型のドキュメントのリンクも貼っておく。 Date (Java Platform SE 8 )

Dateの最初の例

このweb上のplayground(下の奴)では、自分でimportを手書きしないといけないので、最初にimport java.util.Dateと書くようにしてください(これはコピペでいいです)。 AndroidStudio上ではいつもAlt+Enterとか一覧から選ぶとかで勝手にimportされます。

簡単なDateの使い方を見てみましょう。以下のようになります。

import java.util.Date fun main() { val dt = Date() println(dt) }

Date()と呼ぶと、呼んだ時点での「Dateオブジェクト」が得られます。型はDate型です。 そしてDateオブジェクトをそのままprintlnすると、いい感じの文字列として出力されます。

これは、dt.toString()でも同じ文字列が得られます。

import java.util.Date fun main() { val dt = Date() val v = dt.toString() println(v) }

なお、このページ上だとUTCの時間になってしまうので8時間ほど時差があるかもしれませんが、実機だと端末のタイムゾーンの時間になります。

Dateのプロパティのうち時間とか日付を取るもの

上のように取得したdtは、dt.yearとすると年(2023とか)とかが取れます。 2023/8/14 10:39 25秒 を例に幾つになるかを示しておきます。

  • year … 123 (2023じゃないのに注意!)
  • month … 7 (8じゃないのに注意!)
  • date … 14
  • hours … 10
  • minutes … 39
  • seconds … 25

yearは1900年を基準にした年数が帰ってきます。だから西暦にするには1900を足す必要がある。 monthも0から始まるので、月にするには1を足す必要がある。 まぁこの辺は何故?とあまり真面目に考えても仕方ないので、歴史的事情で変な事になってしまっている、くらいに思っておきましょう。

以下、試してみましょう。

import java.util.Date fun main() { val dt = Date() println(dt) println(dt.year) println(dt.month) println(dt.date) println(dt.hours) println(dt.minutes) println(dt.seconds) println("今は${dt.year+1900}年${dt.month+1}月${dt.date}日の${dt.hours}時${dt.minutes}分${dt.seconds}秒です") }

こういう事情をあまり手で計算するのは良くないので、DateFormatというのを使ってフォーマットするのが推奨なのですが、 このコースではこうやって手で計算する事にします。

一応DateFormatを使うコードも載せるだけ載せておきます。

import java.util.Date import java.text.DateFormat fun main() { val dt = Date() val dtf = DateFormat.getDateTimeInstance() val result = dtf.format(dt) println(result) }

このページ上だと英語になってしまいますが、スマホ上だと日本語のいい感じの日付表示になります。

では、hoursなどを使う課題をやってみましょう。

課題: Date型の引数を取り、”2023年8月14日 14:57”という感じの文字列を返す、formatDateという関数を作れ

結果は文字列な事に注意。二つ上のコード例のprintlnを参考にやってみましょう。

ちなみにformatというのは書式、みたいな意味で、いい感じな文字列に変換する時に使われる英単語です。

import java.util.Date // TODO: ここにformatDate関数を作れ // 以下はいじらない fun main() { val dt = Date() val result = formatDate(dt) println(result) }

課題: ListViewに挑戦、の課題で、ボタンを押した時にshowMessageする所で、タップされた時点での日付と時間を追加せよ

ListViewに挑む!表示編、二回目用で、アイテムのボタンを押されたら、以下のような感じのコードを実行していたと思います。(文字列は適当)

showMessage("${data}のボタンが押されたよ")

これを、上で作ったformatDateを使って、以下のように変更してみます。

val dt = Date()
val sdate = formatDate(dt)
showMessage("${data}のボタンが押されたよ (${sdate})")

ボタンを適当に押してみて、ちゃんと時間が表示されているか確認してください。

経過時間などを測ったり、保存したりする時に使うtimeプロパティ

Dateというのは、内部的には1970年1月1日0時0分0秒を基準に、そこからどれだけ過ぎたのか、という秒数を保持しています。 より正確に言うとミリ秒で保存しています。

これを取り出すのがtimeプロパティです。 試してみましょう。

import java.util.Date fun main() { val dt = Date() println(dt) println(dt.time) }

なんで1970年なのか、とかは気にしても仕方ない謎の数字と思っておいてください。そういうものです。

timeの型はIntでは無くLong

1970年から現在が何msec経ったか?というのはめちゃくちゃ大きな数字です。 こんな大きな数字は普段使っている数字、つまりIntという範囲には収まりません。

そこでこのtimeをおさめる変数の型は、Longという、容量を倍使う整数の型になっています。 細かい事はおいといて、timeをListなどに保存する時はList<Int>では無くList<Long>な事を覚えておいてください。

また、細かい計算をする時にもたまにこのIntをあふれてしまう事があります。

例えばミリ秒を年数に変換する事を考えます。つまりtimeの値から、1970年から何年後なのかを計算してみたい。

ミリ秒、という事なので、1秒は1000ミリ秒、1分は60秒なので60*1000ミリ秒、と考えていく。 閏年を無視すると、一年が365日で1日が24時間で1時間が60分で1分が60秒で1秒が1000ミリ秒なので、 365*24*60*60*1000 で割ると何年たっているかが分かる。

でも、これはちょっとIntには収まらないので、これをそのまま計算しようとするとたまにIntをはみ出して変な結果になってしまったりする。 そういう時は一旦1000で割ったものを変数に入れて、そのあと残りで割るとかやるとうまく行ったりする。

例えば以下みたいな感じ。

import java.util.Date fun main() { val dt = Date() println(dt) println(dt.time) val sec = dt.time/1000 val year = sec/(365*24*60*60) println(year) }

2023年現在で試すと53と出るので、1970年から53年経ってるという事であってそう。

こういう計算をやってもらう事はこのシリーズでは無いけれど、timeはめっちゃでかい値なのでLongという型になっていて、 気をつけないで計算するとたまにIntを経由して変な値になる事がある、と知っておくと良いです。

特定の時間のDateオブジェクトを作るにはDateに数値を渡す

保存したLongの数値を元にDateオブジェクトを作りたい時は、Dateを関数のように呼んで、引数にLongの値を代入すれば良い。 これは現時点では意味がわからないかもしれないけれど、後で使う事になるのでそこまで行ったら見直そう。

さっき自分が試した時はUTCの8月14日の朝6時27分だったのが1691994462475だったので、これをセットするとUTCの8月14日、6時27分が得られる。 試してみましょう。

import java.util.Date fun main() { val dt = Date(1691994462475) println(dt) }

こうやって大きい数字を覚えておくだけでその日、というのを復元出来るので、投稿日をファイルとかに保存する時などに便利です。

経過時間はtimeを引くと分かる

タイピング練習ソフトなどで、全部終わるまでの時間を測って時間でスコアを作りたい、みたいな事がよくある。

そういう時にはtimeの結果を引けば、何msecかかったかが分かる。

import java.util.Date fun main() { val beginTime = Date().time var sum = 0 for(i in 1..100000) { for(j in 1..100000) { sum +=i } } println(sum) val endTime = Date().time val diff = endTime - beginTime println("${diff}msec、つまり${diff/1000}秒かかりました") }

これを使って、OffTyping - Google Play のアプリのようなアプリを作る事が出来ます。 (Enter押さないでハンドルするのはまだやってないのでEnterを押さないといけないけれど)

課題: 「ListViewに挑む!編集編」に、投稿時間をつける

ListViewの編集編で作ったものに対し、 アイテム追加時の時間も表示したい、と考えたとします。 Twitterなどを見ると投稿した時間が下に小さく表示されていますよね。あれと同じです。

まず簡単な手順を書いておきます。

  1. itemのレイアウトに時刻表示用のTextViewを追加(文字のサイズは小さめにしておきましょう)
  2. これまでlistDataとして作っていたのと同じ感じで、Dateを保持するリストも作る
  3. getViewの所でDateのリストからも日付を取り出して1で追加したTextViewにセットする
  4. setOnClickListenerでlistDataに追加する所で、1で追加したリストに日付を追加する

以下簡単にヒントを書いていくので自分でやってみてください。

日付を保存するリストを作る

リストのアイテムは現状、String型となっている。

val listData = mutableListOf<String>()

これを、Dateも保持するように以下のように二つにする。

val listData = mutableListOf<String>()
val listDt = mutableListOf<Date>()

getViewで日付をTextViewにセット

getViewの所で以下のような感じにすれば良いでしょう。

val data = listData[position]
val dt = listDt[position]

view.findViewById<TextView>(R.id.itemLabel).text = data
view.findViewById<TextView>(R.id.itemDateLabel).text = dt.toString()

setOnClickListenerで日付もリストに追加する

これまで、以下みたいになっている所で、

findViewById<Button>(R.id.buttonSubmit).setOnClickListener { listData.add(findViewById<TextView>(R.id.edit1).text.toString()) }

以下みたいにリストに日付も追加する

findViewById<Button>(R.id.buttonSubmit).setOnClickListener { 
  listData.add(findViewById<TextView>(R.id.edit1).text.toString())
  listDt.add(Date())
}

これで投稿時間も追加されるようになったはずです。