lubridateパッケージ入門

※この記事は[Do more with dates and times in R with lubridate 1.3.0](https://cran.r-project.org/web/packages/lubridate/vignettes/lubridate.html)を翻訳したものですが、とても雑にやったので原文と大分違っていると思います。

# 日付と時刻のパース

Rで日付や時刻を含むデータを扱う方法は大分ややこしいが、`{lubridate}`パッケージを使えばもっと単純にやれる。

例えば文字列をパースする場合、`y`, `m`, `d`を文字列に含まれる年月日の順序に合わせて並び替えた関数を使えばよい。例を見たほうがわかりやすいだろう。

```r
library(lubridate)
```

```r
> ymd("20110604")
[1] "2011-06-04"
> mdy("06-04-2011")
[1] "2011-06-04"
> dmy("4/6/2011")
[1] "2011-06-04"
```

これらの関数は様々な形式、セパレータを扱えるので、パースの手順を単純にできる。

もし文字列に時刻の情報もあるのなら、関数名に続けて`_h`, `_hm`, `_hms`を追加することで、時間、時間と分、時間と分と秒を読み込むことができる。おそらく、`ymd_hms`がもっとも一般的な形式だろう。もしタイムゾーンを指定したければ`tz=`引数を使う。

```r
> ymd_hms("2011-06-04 12:00:00", tz = "Pacific/Auckland")
[1] "2011-06-04 12:00:00 NZST"
```

# 情報の代入と抽出

`second` `minute` `hour` `day` `wday` `yday` `week` `month` `year` `tz`、これらの関数を使って日時から特定の情報を抽出できる。また、これらの関数を使って代入もできる。

```r
> arrive <- ymd_hms("2011-06-04 12:00:00", tz = "Pacific/Auckland")
> second(arrive)
[1] 0
> second(arrive) <- 25
> second(arrive)
[1] 25
```

`wday`と`month`は`label=`引数に`TRUE`を指定することで、出力を数値でなく曜日名や月名に変更できる。

```r
> wday(arrive)
[1] 7
> wday(arrive, label = TRUE)
[1] Sat
Levels: Sun < Mon < Tues < Wed < Thurs < Fri < Sat
> month(arrive, label = TRUE)
[1] Jun
12 Levels: Jan < Feb < Mar < Apr < May < Jun < Jul < Aug < ... < Dec
```

# タイムゾーン

`with_tz`は特定のタイムゾーンにおける時刻を表示する。例えばオークランドにで2011/7/1 9:00のとき、シカゴでは何時か知りたければ次のようにする。

```r
> meeting <- ymd_hms("2011-07-01 09:00:00", tz = "Pacific/Auckland")
> meeting
[1] "2011-07-01 09:00:00 NZST"
> with_tz(meeting, "America/Chicago")
[1] "2011-06-30 16:00:00 CDT"
```

日時はそのままで、タイムゾーンのみを変更するには`force_tz`関数を使う。

```r
> force_tz(meeting, "America/Chicago")
[1] "2011-07-01 09:00:00 CDT"
```

# インターバル

`{lubridate}`はインターバルを表現するための専用のクラスを備えている。インターバルクラスのオブジェクトは`interval`関数または二項演算子`%--%`を使って作成できる。

```r
> arrive <- ymd_hms("2011-06-04 12:00:00", tz = "Pacific/Auckland")
> leave <- ymd_hms("2011-08-10 14:00:00", tz = "Pacific/Auckland")
> auckland <- interval(arrive, leave)
> auckland
[1] 2011-06-04 12:00:00 NZST--2011-08-10 14:00:00 NZST
> arrive %--% leave
[1] 2011-06-04 12:00:00 NZST--2011-08-10 14:00:00 NZST
```

`int_overlaps`関数は2つのインターバルに時期的な重なりがあるかどうかを確認する。

```r
> jsm <- interval(ymd(20110720, tz="Pacific/Auckland"), ymd(20110831, tz="Pacific/Auckland"))
> jsm
[1] 2011-07-20 NZST--2011-08-31 NZST
> int_overlaps(auckland, jsm)
[1] TRUE
```

インターバルオブジェクトに対しては、`int_start` `int_end`などの専用関数の他、`setdiff` `union` `intersect`のような集合演算も適用できる。

# 日付の計算

上述のインターバルは2つの特定の日時の間に定義されるものだった。`{lubridate}`はこれに加えて、一般的な時間間隔に関するクラスである、`durations`と`periods`を持っている。`periods`を作成するには、時間の単位を複数形にした関数を使う。

```r
> minutes(2)
[1] "2M 0S"
```

`durations`を作るには、`periods`を作成する関数の頭に`d`をつける。

```r
> dminutes(2)
[1] "120s (~2 minutes)"
```

`durations`クラスは数学的に正しい結果を返す。すなわち、`durations`における1年は常に365日である。一方で、`periods`は暦の上で受ける変動と同じ変動を受ける。この違いはうるう年の1月1日に「1年」を足す演算を見るとよく分かる。

```r
> ymd(20120101) + years(1)
[1] "2013-01-01"
> ymd(20120101) + dyears(1)
[1] "2012-12-31"
```

`durations`における1年は365日であるが、`periods`における1年はうるう年では366日になる。

インターバルと組み合わせて期間の計算などができる。

```r
> auckland / ddays(1)
[1] 67.08333
```

商と余りも計算できる。

```r
> auckland %/% months(1)
 注: シグネチャー ‘Timespan#Timespan’ を持つメソッドが、関数 ‘%/%’ に対して選ばれました,
 対象シグネチャー ‘Interval#Period’ です。
 "Interval#ANY", "ANY#Period" もまた有効かもしれません
[1] 2
```

余りの場合は新しいインターバルが返ってくるので、目的に応じて`as.period`や`as.duration`で変換すると良いだろう。

```r
> auckland %% months(1)
[1] 2011-08-04 12:00:00 NZST--2011-08-10 14:00:00 NZST
> as.period(auckland %% months(1))
[1] "6d 2H 0M 0S"
> as.duration(auckland %% months(1))
[1] "525600s (~6.08 days)"
```

# 月の長さが変わる場合の算術演算

月や年の長さはしばしば変化するので、算術演算が直感的でなくなる場合がある。例えば1月31日に1ヶ月を足す場合を考えてみよう。パッと思いつくだけでも3通りの答があり得る。

1. 2月31日 (単純に月に1を足す。こんな日は存在しないが。)
2. 3月4日  (1月31日の31日後として計算。)
3. 2月28日 (翌月末をうるう年ではないと仮定して計算。)

`a + b - b = a`というルールに従えるのは1のみであるが、しかし2月31日は日付として正しくない。`{lubridate}`では演算の結果として正しくない日付になる場合、`NA`を返すことになっている。これはversion 1.3.0からの変更なので古いバージョンの`{lubridate}`を使っていた場合は注意。

```r
> jan31 <- ymd("20130131")
> jan31 + months(0:11)
 [1] "2013-01-31" NA           "2013-03-31" NA           "2013-05-31"
 [6] NA           "2013-07-31" "2013-08-31" NA           "2013-10-31"
[11] NA           "2013-12-31"
```

もし2の結果(31日を足した結果を表示)が欲しければ、`floor_date`で月初の日付に変換した後に`days(31)`を足すという手順を取る。

```r
> floor_date(jan31, "month") + months(0:11) + days(31)
 [1] "2013-02-01" "2013-03-04" "2013-04-01" "2013-05-02" "2013-06-01"
 [6] "2013-07-02" "2013-08-01" "2013-09-01" "2013-10-02" "2013-11-01"
[11] "2013-12-02" "2014-01-01"
```

3の結果がほしければ、二項演算子`%m+%` `%m-%`を使うと、月末の日付を適当に調整してくれる。

```r
> jan31 %m+% months(0:11)
 [1] "2013-01-31" "2013-02-28" "2013-03-31" "2013-04-30" "2013-05-31"
 [6] "2013-06-30" "2013-07-31" "2013-08-31" "2013-09-30" "2013-10-31"
[11] "2013-11-30" "2013-12-31"
```