この記事はWanoアドベントカレンダーの7日目の記事です。
TL;DR
日時を扱う
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時間ずれた日付型 |
FixedOffsetはUtcとLocal以外を扱う時に使用します(例えば+03:00にしたい時)。
その他に複数の日時に関する型を抽象化して扱いたい時の為に、以下のtraitが提供されています。
よくある処理のやり方
現在時刻の取得
Utc
やLocal
のnow
関数や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
日時文字列のパース
DateTime
やNaiveDateTime
などのparse_from_str
やparse_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_utc
やnaive_local
メソッドを使います。
naive_local
はTz
での時刻でNaiveDateTime
を生成し、naive_utc
はUtc
での時刻で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_datetime
やfrom_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
日時に時間を足す・引く
DateTime
やNaiveDateTime
にDuration
を足したり引いたりすることで出来ます。
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"] }