🌍

Rの日時データ #1 - 躓きポイント2つ

2022/12/15に公開約4,800字

はじめに

今回からRの日時データ[1]について何回か書いていきたいと思います。

#1の今回は、Rで日時データを扱っていて躓いた以下の2点を紹介します。

  1. 実行環境によりas.POSIXctの結果が異なる
  2. ISO8601形式をパースできない

1. 実行環境によりas.POSIXctの結果が異なる

以下は、POSIXct型のオブジェクトを作成する全く同一のコードas.POSIXct("2011-03-20 12:34:56")を、いくつかの実行環境で実行したものですが、結果が異なっています。

  • ローカルマシン(Mac)上
> as.POSIXct("2011-03-20 12:34:56")
[1] "2011-03-20 12:34:56 JST"
  • Google Compute Engineのインスタンス(Linux)上
> as.POSIXct("2011-03-20 12:34:56")
[1] "2011-03-20 12:34:56 UTC"
  • Google Colaboratory上
%%R
as.POSIXct("2011-03-20 12:34:56")

[1] "2011-03-20 12:34:56 UTC"

https://gist.github.com/kn1kn1/5575547653d227fa1ee3051777012b15

このようなコードがgitに入っていて複数人の環境で実行すると、環境によって結果が一致しないという事態が発生してしまいます。

原因

原因は、引数tzを指定せずas.POSIXct("2011-03-20 12:34:56")のように実行すると、システムのタイムゾーンを参照することにあります。

システムのタイムゾーンは、Sys.timezone()で調べることが可能です。上記のそれぞれの環境で、Sys.timezone()を実行すると、返ってくる値が異なることが分かると思います。

  • ローカルマシン(Mac)上
> Sys.timezone()
[1] "Asia/Tokyo"
  • Google Compute Engineのインスタンス(Linux)上
> Sys.timezone()
[1] "Etc/UTC"
  • Google Colaboratory上
%%R
Sys.timezone()

[1] "Etc/UTC"

対応方法

対応としては、2つの方法があります。

  • as.POSIXcttz引数を指定する
  • Sys.setenvで環境変数TZを設定する

as.POSIXcttz引数を指定する

以下のように、tz引数として"Asia/Tokyo"を明示的に指定すれば、どの環境でも同じ日時を差すPOSIXct型のオブジェクトを生成できます。

> as.POSIXct("2011-03-20 12:34:56", tz="Asia/Tokyo")
[1] "2011-03-20 12:34:56 JST"

Sys.setenvで環境変数TZを設定する

as.POSIXctを呼び出す前に、Sys.setenvで環境変数TZを必ず設定するようにしておくのが、もう一つの方法です。但しこれはコードの冒頭に書くなど工夫が必要なのが難点と言えます。

> Sys.timezone()
[1] "Asia/Tokyo"
> Sys.getenv('TZ')
[1] ""
> as.POSIXct("2011-03-20 12:34:56")
[1] "2011-03-20 12:34:56 JST"
> Sys.setenv(TZ='UTC')
> Sys.getenv('TZ')
[1] "UTC"
> as.POSIXct("2011-03-20 12:34:56")
[1] "2011-03-20 12:34:56 UTC"
> 
timezones {base} ヘルプの見解

timezones {base}のドキュメントには、以下のような記述があります。

Time zones are a system-specific topic,

とした上で、以下のように、環境変数TZによる設定は、Rのプロセス起動前に(.Rprofileに記述するなどで)実施すると、Sys.timezone()に反映されるとしていますが、この対応を実施したところで環境依存であることに変わりはないでしょう。コードのみで一致した結果を得るには、上で紹介した2つの対応方法が良さそうです。

It should be possible to set the current time zone via the environment variable TZ: see the section on ‘Time zone names’ for suitable values. Sys.timezone() will return the value of TZ if set initially (and on some OSes it is always set), otherwise it will try to retrieve from the OS a value which if set for TZ would give the initial time zone. (‘Initially’ means before any time-zone functions are used: if TZ is being set to override the OS setting or if the ‘try’ does not get this right, it should be set before the R process is started or (probably early enough) in file .Rprofile).

2. ISO8601形式をパースできない

baseパッケージにISO8601形式(よく使われる2022-11-24T00:30:38+09:00のような拡張形式と呼ばれるもの)をパースする関数が存在しないようでした。

> as.POSIXct("2022-11-24T00:30:38+09:00")
[1] "2022-11-24 JST"

ISO8601形式の文字列をas.POSIXct関数でパースしようとすると、T区切り記号(timeを表す cf. https://en.wikipedia.org/wiki/ISO_8601#Times )を解釈できず、年月日までのオブジェクトになっていました。

原因

POSIXctはその名のとおり、POSIX時間を対象とした型であり、ISO8601には対応していないのが原因と思われます。

as.POSIXct関数のヘルプを参照すると以下の記述があり、2001-02-03 14:52:03のようなスペース区切りの形式の文字列を受け付けるとありました。

They can also convert character strings of the formats "2001-02-03" and "2001/02/03" optionally followed by white space and a time in the format "14:52" or "14:52:03".

https://search.r-project.org/R/refmans/base/html/as.POSIXlt.html

また、as.POSIXct関数のヘルプが参照しているstrftime関数のヘルプによれば、formatのデフォルトとしては、"%Y-%m-%d %H:%M:%S"が使われているようでした。

The default for the format methods is "%Y-%m-%d %H:%M:%S"

https://search.r-project.org/R/refmans/base/html/strptime.html

対応方法

lubridateパッケージをインストールし、ymd_hms関数を使うと解決できます。

> lubridate::ymd_hms("2022-11-24T00:30:38+09:00")
[1] "2022-11-23 15:30:38 UTC"
> 

https://lubridate.tidyverse.org/

lubridateパッケージは、他にも

をはじめとして便利な関数があるので、一通りチェックするのがおすすめです。

まとめ

以上、Rの日時データについて私自身が特に嵌ったポイント2つでした。

次回の#2は、今回紹介した手順も含めた文字列⇔日時オブジェクトの変換について紹介したいと思います。

参考

脚注
  1. 「日時データ」や「日時オブジェクト」といった用語は、野間口謙太郎, 菊池泰樹 訳 「統計学:Rを用いた入門書 第2版」共立出版 2016 pp.348-349 を参考にしています。 ↩︎

GitHubで編集を提案

Discussion

ログインするとコメントできます