LoginSignup
102
57

More than 1 year has passed since last update.

Rustで日時を扱う

Last updated at Posted at 2018-12-07

この記事はWanoアドベントカレンダーの7日目の記事です。

TL;DR

  • 日時を扱う時はchronoを使う
  • timezoneをがっつり扱う時はchrono-tzが便利

日時を扱う

Rustで日時を扱う時はchronoがデファクトなのでこれを使います。
様々なタイムゾーンを扱う場合はchrono-tzを合わせて使うと便利です。

chronoが提供している型

主な日時に関する型は次の通りです。

DateTime<Tz>Date<Tz>はタイムゾーン情報を持ちます。
Naiveが頭に付いている型はタイムゾーン情報を持ちません。

また、タイムゾーン情報は型パラメータとして次の様に持ちます。

意味
DateTime<Utc> UTCの日時型
Date<Utc> UTCの日付型
DateTime<Local> ローカルタイムゾーンの日時型
Date<Local> ローカルタイムゾーンの日付型
DateTime<FixedOffset> UTCからN時間ずれた日時型
Date<FixedOffset> UTCからN時間ずれた日付型

FixedOffsetUtcLocal以外を扱う時に使用します(例えば+03:00にしたい時)。

その他に複数の日時に関する型を抽象化して扱いたい時の為に、以下のtraitが提供されています。

よくある処理のやり方

現在時刻の取得

UtcLocalnow関数やtoday関数を使って取得します。

use chrono::{Utc, Local, DateTime, Date};

fn main() {
    let utc_datetime: DateTime<Utc> = Utc::now();
    let utc_date: Date<Utc> = Utc::today();

    println!("{}", utc_datetime);
    println!("{}", utc_date);

    let local_datetime: DateTime<Local> = Local::now();
    let local_date: Date<Local> = Local::today();

    println!("{}", local_datetime);
    println!("{}", local_date);
}

実行結果:

2018-12-07 10:27:26.682066134 UTC
2018-12-07UTC
2018-12-07 19:27:26.682094317 +09:00
2018-12-07+09:00

日時文字列のパース

DateTimeNaiveDateTimeなどのparse_from_strparse_from_rfc3339などの関数を使います。

use chrono::{DateTime, Utc, Local, offset::{FixedOffset, TimeZone}, NaiveDateTime};

fn main() {
    let dt: Result<DateTime<FixedOffset>, _> = DateTime::parse_from_rfc3339("2018-12-07T19:31:28+09:00");
    println!("DateTime::parse_from_rfc3339: {:?}", dt);

    let dt: Result<DateTime<FixedOffset>, _> = DateTime::parse_from_str("2018/12/07 19:31:28 +0900", "%Y/%m/%d %H:%M:%S %z");
    println!("DateTime::parse_from_str: {:?}", dt);

    let dt: Result<DateTime<Utc>, _> = Utc.datetime_from_str("2018/12/07 19:31:28", "%Y/%m/%d %H:%M:%S");
    println!("Local.datetime_from_str: {:?}", dt);

    let dt: Result<DateTime<Local>, _> = Local.datetime_from_str("2018/12/07 19:31:28", "%Y/%m/%d %H:%M:%S");
    println!("Local.datetime_from_str: {:?}", dt);

    let dt: Result<NaiveDateTime, _> = NaiveDateTime::parse_from_str("2018/12/07 19:31:28", "%Y/%m/%d %H:%M:%S");
    println!("NaiveDateTime::parse_from_str: {:?}", dt);
}

実行結果:

DateTime::parse_from_rfc3339: Ok(2018-12-07T19:31:28+09:00)
DateTime::parse_from_str: Err(ParseError(NotEnough))
NaiveDateTime::parse_from_str: Ok(2018-12-07T19:31:28)

文字列に変換

formatメソッドを使います。
書式指定はよくあるやり方です(詳しくは chrono::format::strftime に書かれています)。

use chrono::Utc;

fn main() {
    let text = Utc::now().format("%Y年%m月%d日 %H時%M分%S秒 %Z").to_string();
    println!("{}", text);
}

実行結果:

2018年12月07日 10時43分54秒 UTC

TimeZone有りの型から無しの型への変換、またはその逆

DateTime<Tz>からNaiveDateTimeへ変換する場合は、naive_utcnaive_localメソッドを使います。
naive_localTzでの時刻でNaiveDateTimeを生成し、naive_utcUtcでの時刻でNaiveDateTimeを生成します。

use chrono::{Utc, Local, DateTime, NaiveDateTime};

fn main() {
    let utc: DateTime<Utc>= Utc::now();
    println!("[DateTime<Utc> -> NaiveDateTime] utc:         {:?}", utc);

    let naive: NaiveDateTime = utc.naive_local();
    println!("[DateTime<Utc> -> NaiveDateTime] naive_local: {:?}", naive);

    let naive: NaiveDateTime = utc.naive_utc();
    println!("[DateTime<Utc> -> NaiveDateTime] naive_utc:   {:?}", naive);

    let local: DateTime<Local> = Local::now();
    println!("[DateTime<Local> -> NaiveDateTime] local:       {:?}", local);

    let naive: NaiveDateTime = local.naive_local();
    println!("[DateTime<Local> -> NaiveDateTime] naive_local: {:?}", naive);

    let naive: NaiveDateTime = local.naive_utc();
    println!("[DateTime<Local> -> NaiveDateTime] naive_utc:   {:?}", naive);
}

実行結果:

[DateTime<Utc> -> NaiveDateTime] utc:         2018-12-07T10:53:33.933340068Z
[DateTime<Utc> -> NaiveDateTime] naive_local: 2018-12-07T10:53:33.933340068
[DateTime<Utc> -> NaiveDateTime] naive_utc:   2018-12-07T10:53:33.933340068
[DateTime<Local> -> NaiveDateTime] local:       2018-12-07T19:53:33.933364073+09:00
[DateTime<Local> -> NaiveDateTime] naive_local: 2018-12-07T19:53:33.933364073
[DateTime<Local> -> NaiveDateTime] naive_utc:   2018-12-07T10:53:33.933364073

上記の逆に、NaiveDateTimeからDateTime<Tz>へ変換する場合は、TimeZone traitのfrom_local_datetimefrom_utc_datetimeメソッドを使います。

use chrono::{Utc, Local, FixedOffset, DateTime, NaiveDateTime, TimeZone};

fn main() {
    let dt: NaiveDateTime = NaiveDateTime::parse_from_str("2018/12/07 19:31:28", "%Y/%m/%d %H:%M:%S").unwrap();

    let utc: DateTime<Utc> = Utc.from_local_datetime(&dt).unwrap();
    println!("{}", utc);

    let local: DateTime<Local> = Local.from_local_datetime(&dt).unwrap();
    println!("{}", local);

    let offset: DateTime<FixedOffset> = FixedOffset::east(3 * 3600).from_local_datetime(&dt).unwrap();
    println!("{}", offset);
}

実行結果:

2018-12-07 19:31:28 UTC
2018-12-07 19:31:28 +09:00
2018-12-07 19:31:28 +03:00

2つの日時の差を求める

単純に引き算で計算できます。
計算した結果はchrono::Duration型になります。

use chrono::{NaiveDateTime, Duration};

fn main() {
    let dt1: NaiveDateTime = NaiveDateTime::parse_from_str("2018/12/07 19:31:28", "%Y/%m/%d %H:%M:%S").unwrap();
    let dt2: NaiveDateTime = NaiveDateTime::parse_from_str("2018/12/07 12:31:28", "%Y/%m/%d %H:%M:%S").unwrap();
    let duration: Duration = dt1 - dt2;

    println!("secs: {}", duration.num_seconds());
    println!("minutes: {}", duration.num_minutes());
    println!("hours: {}", duration.num_hours());
}

実行結果:

secs: 25200
minutes: 420
hours: 7

日時に時間を足す・引く

DateTimeNaiveDateTimeDurationを足したり引いたりすることで出来ます。

use chrono::{DateTime, Local, TimeZone, Duration};

fn main() {
    let dt1: DateTime<Local> = Local.datetime_from_str("2018/12/07 19:31:28", "%Y/%m/%d %H:%M:%S").unwrap();
    let dt2 = dt1 + Duration::hours(3);
    let dt3 = dt1 - Duration::hours(3);

    println!("dt1: {}", dt1);
    println!("dt2: {}", dt2);
    println!("dt3: {}", dt3);
}

実行結果:

dt1: 2018-12-07 19:31:28 +09:00
dt2: 2018-12-07 22:31:28 +09:00
dt3: 2018-12-07 16:31:28 +09:00

TimeZoneの変更

with_timezoneメソッドを使います。

use chrono::{DateTime, Local, Utc, FixedOffset, TimeZone, Duration};

fn main() {
    let dt1: DateTime<Local> = Local.datetime_from_str("2018/12/07 19:31:28", "%Y/%m/%d %H:%M:%S").unwrap();
    let dt2: DateTime<Utc> = dt1.with_timezone(&Utc);
    let dt3: DateTime<FixedOffset> = dt1.with_timezone(&FixedOffset::east(3*3600));

    println!("dt1: {}", dt1);
    println!("dt2: {}", dt2);
    println!("dt3: {}", dt3);
}

実行結果:

dt1: 2018-12-07 19:31:28 +09:00
dt2: 2018-12-07 10:31:28 UTC
dt3: 2018-12-07 13:31:28 +03:00

unix timeからの変換

TimeZone traitのtimestampメソッドを使います。

use chrono::{DateTime, Local, Utc, FixedOffset, TimeZone};

fn main() {
    let dt1: DateTime<Local> = Local.timestamp(1_500_000_000, 0);
    let dt2: DateTime<Utc> = Utc.timestamp(1_500_000_000, 0);
    let dt3: DateTime<FixedOffset> = FixedOffset::east(3*3600).timestamp(1_500_000_000, 0);

    println!("dt1: {}", dt1);
    println!("dt2: {}", dt2);
    println!("dt3: {}", dt3);
}

実行結果:

dt1: 2017-07-14 11:40:00 +09:00
dt2: 2017-07-14 02:40:00 UTC
dt3: 2017-07-14 05:40:00 +03:00

unix timeへの変換

DateTimeなどのtimestampメソッドを使います。

use chrono::{DateTime, Local};

fn main() {
    let dt: DateTime<Local> = Local::now();
    let timestamp: i64 = dt.timestamp();

    println!("{}", timestamp);
}

実行結果:

1544182094

chronoをserdeでSerialize/Deserialize出来るようにする

chronoはデフォルトではserdeに対応していません (serdeを使わずにchronoだけ使う場合もある為)。
serdeでserialize/deserializeできるようにしたい場合はCargo.tomlの[dependencies]に次のように書きます。

[dependencies]
chrono = { version = "0.4", features = ["serde"] }

chronoをdieselで使えるようにする

Rustの代表的ORMのdieselもデフォルトではchronoに対応していません。
serdeの場合と同様に対応する事で使えるようになりますが、diesel側に設定を行います。

[dependencies]
chrono = "0.4"
diesel = { version = "1.3", features = ["chrono"] }
102
57
9

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
102
57