Rustで日付時刻を扱うTIPS

2024年3月13日水曜日

Rust

t f B! P L

Rustで日付と時刻を扱う際は、chronoクレートを使用するのが一般的です。
この記事では、chronoクレートを使用してRustで日付を扱う基本的な方法を紹介します。

はじめに

Rustのchronoクレートは、日付と時刻の操作に関連する機能を提供します。
UTCとタイムゾーン対応の両方の日時、時間の操作、フォーマットと解析など、さまざまな機能が含まれています。chronoを使用する前に、Cargo.tomlファイルに依存関係を追加する必要があります。

[dependencies]
chrono = "0.4"

chronoが提供する主要なデータ型

chronoクレートは、Rustで日付と時刻を扱うための複数のデータ型を提供します。
それぞれ用途と精度のニーズに応じて設計されています。主要なデータ型には以下です。

  1. DateTime: タイムゾーン付きの日時を表します。UTCやローカルタイムゾーンでの日時を扱うのに使用します。
  2. NaiveDateTime: タイムゾーンのない日時を表します。単に年、月、日、時、分、秒を格納しますが、その時間がどのタイムゾーンに属するかは考慮しません。
  3. NaiveDate: タイムゾーンのない日付のみを表します。年、月、日の情報を含みます。
  4. NaiveTime: タイムゾーンのない時刻のみを表します。時、分、秒(および必要に応じてナノ秒)の情報を含みます。
  5. Duration: 期間または時間の長さを表します。日数、時間、分、秒を扱うことができます。

スポンサーリンク

現在時刻の取得

現在の日時を取得するには、chronoUtc::now()またはLocal::now()を使用します。

UTCで現在時刻を取得

以下はUTCの現在時刻を取得する例です。

use chrono::Utc;

fn main() {
    let now = Utc::now();
    println!("{}", now);
}

ローカルタイムゾーンで現在時刻を取得

以下はローカルタイムゾーンの現在時刻を取得する例です。

use chrono::Local;

fn main() {
    let now = Local::now();
    println!("{}", now);
}

指定した日時の日付オブジェクト生成

特定の日時を指定して日付オブジェクトを生成する方法を紹介します。

NaiveDate

// 2023/02/25
let date = NaiveDate::from_ymd_opt(2023, 2, 25).unwrap();

このコードは、NaiveDate::from_ymd_opt関数を使用して、年月日を指定してNaiveDateオブジェクトを生成します。この関数は、有効な日付であればSome(NaiveDate)を返し、無効な日付(例えば、2月に31日を指定した場合など)であればNoneを返します。unwrap()メソッドを使用することで、Optionから値を取り出していますが、実際のアプリケーションでは無効な日付に対する適切なエラーハンドリングを行うべきです。

NaiveTime

// 12:00:00
let time = NaiveTime::from_hms_opt(12, 0, 0).unwrap();

NaiveDateTime

NaiveDate::from_ymdNaiveTime::from_hmsを組み合わせ、NaiveDateTimeを作成します。

let date = NaiveDate::from_ymd_opt(2023, 2, 25).unwrap();
let time = NaiveTime::from_hms_opt(12, 0, 0).unwrap();
let datetime: NaiveDateTime = NaiveDateTime::new(date, time);

DateTime<UTC>

let dt: DateTime<Utc> = Utc.with_ymd_and_hms(2015, 5, 15, 0, 0, 0).unwrap();

このコードは、UTCタイムゾーンで特定の年月日と時刻を持つDateTime<Utc>オブジェクトを生成します。

DateTime<Local>

let dt = Local.with_ymd_and_hms(2022, 7, 5, 10, 14, 31);
println!("{}", dt.unwrap());

このコードは、ローカルタイムゾーンで特定の年月日と時刻を持つDateTime<Local>オブジェクトを生成します。

日付の加減算

日付の加減算には、chrono::Durationを使用します。以下の例では、日付に5日を加算し、その後10日を減算します。

use chrono::{Duration, NaiveDate};

fn main() {
    let date = NaiveDate::from_ymd(2023, 2, 25);
    let after_five_days = date + Duration::days(5);
    let before_ten_days = after_five_days - Duration::days(10);

    println!("Original date: {}", date);
    println!("After 5 days: {}", after_five_days);
    println!("Before 10 days from that: {}", before_ten_days);
}

時間の加減算もchrono::Durationを使用します。以下の例では、日付に5時間を加算し、その後10時間を減算します。

use chrono::{Duration, NaiveDate, NaiveDateTime, NaiveTime};

fn main() {
    let after_five_hours = datetime + Duration::hours(5);
    let before_ten_hours = after_five_hours - Duration::hours(10);

    println!("Original datetime: {}", datetime);
    println!("After 5 hours: {}", after_five_hours);
    println!("Before 10 hours from that: {}", before_ten_hours);
}

スポンサーリンク

文字列から日時オブジェクトを作成

文字列から日付と時刻のデータをパースすることができます。これは、ユーザー入力や外部データソースから日付や時刻を読み込む場合に特に便利です。以下では、chronoを使用して異なる日付型のデータを文字列から作成する方法について解説します。

DateTimeの作成

DateTimeはタイムゾーン付きの日時を表します。UTCやローカルタイムゾーンなど、具体的なタイムゾーン情報を持つ日時のパースに使用します。

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

fn main() {
    let datetime_str = "2023-02-25T12:00:00Z"; // ISO 8601形式の日時文字列
    let datetime: DateTime<Utc> = Utc.datetime_from_str(datetime_str, "%+").unwrap();
    println!("DateTime in UTC: {}", datetime);
}

このコードでは、ISO 8601形式の日時文字列をDateTime<Utc>型にパースしています。%+chronoでISO 8601形式の日時を解析するためのフォーマット指定子です。

NaiveDateTimeの作成

NaiveDateTimeはタイムゾーンのない日時を表します。これは、タイムゾーンに依存しないアプリケーションで使用されます。

use chrono::{NaiveDateTime};

fn main() {
    let datetime_str = "2023-02-25 12:00:00"; // タイムゾーンのない日時文字列
    let naive_datetime = NaiveDateTime::parse_from_str(datetime_str, "%Y-%m-%d %H:%M:%S").unwrap();
    println!("NaiveDateTime: {}", naive_datetime);
}

ここでは、指定されたフォーマットの文字列をNaiveDateTimeにパースしています。フォーマット指定子%Y-%m-%d %H:%M:%Sは、年-月-日 時:分:秒を表します。

NaiveDateの作成

NaiveDateはタイムゾーンのない日付だけを表します。

use chrono::NaiveDate;

fn main() {
    let date_str = "2023-02-25"; // 日付文字列
    let naive_date = NaiveDate::parse_from_str(date_str, "%Y-%m-%d").unwrap();
    println!("NaiveDate: {}", naive_date);
}

このコードでは、指定されたフォーマットの文字列をNaiveDateにパースしています。フォーマット指定子%Y-%m-%dは、年-月-日を表します。

NaiveTimeの作成

NaiveTimeはタイムゾーンのない時刻だけを表します。

use chrono::NaiveTime;

fn main() {
    let time_str = "12:00:00"; // 時刻文字列
    let naive_time = NaiveTime::parse_from_str(time_str, "%H:%M:%S").unwrap();
    println!("NaiveTime: {}", naive_time);
}

ここでは、指定されたフォーマットの文字列をNaiveTimeにパースしています。フォーマット指定子%H:%M:%Sは、時:分:秒を表します。

これらの例を通じて、chronoクレートを使用して文字列から様々な日付型のデータを作成する方法を学びました。文字列から日付や時刻をパースする際には、適切なフォーマット指定子を使用することが重要です。また、無効な日付やフォーマットの文字列をパースしようとするとNoneが返されるため、適切なエラーハンドリングを行う必要があります。

2つの日時を比較する

DateTimeNaiveDateNaiveTimeの大小比較はシンプルです。基本的な比較演算子で大小比較が可能です。

NaiveDateの比較

NaiveDateオブジェクトはタイムゾーンのない日付を表し、直接比較することができます。

use chrono::NaiveDate;

fn main() {
    let date1 = NaiveDate::from_ymd_opt(2023, 2, 25).unwrap();
    let date2 = NaiveDate::from_ymd_opt(2023, 3, 1).unwrap();

    if date1 < date2 {
        println!("date1 is earlier than date2.");
    } else {
        println!("date2 is earlier than or equal to date1.");
    }
}

NaiveTimeの比較

NaiveTimeオブジェクトもタイムゾーンのない時刻を表し、直接比較が可能です。

use chrono::NaiveTime;

fn main() {
    let time1 = NaiveTime::from_hms_opt(12, 0, 0).unwrap();
    let time2 = NaiveTime::from_hms_opt(14, 30, 0).unwrap();

    if time1 < time2 {
        println!("time1 is earlier than time2.");
    } else {
        println!("time2 is earlier than or equal to time1.");
    }
}

異なるタイムゾーンのDateTime比較

DateTimeオブジェクトはタイムゾーン情報を含んでいるため、異なるタイムゾーンの日時を直接比較することは、意図した結果を得ることができない可能性があります。たとえば、次の例では、UTCの現在時刻とローカルの現在時刻を比較していますが、これら2つの日時は実際には同じ瞬間を指しているにも関わらず、タイムゾーンの差によってUTCの方が「早い」と判断される可能性があります。

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

fn main() {
    let utc_datetime: DateTime<Utc> = Utc::now();
    let local_datetime: DateTime<Local> = Local::now();

    if utc_datetime < local_datetime {
        println!("UTCの日時が早い");
    } else if utc_datetime == local_datetime {
        println!("日時が一致している");
    } else if utc_datetime > local_datetime {
        println!("UTCの日時が遅い");
    }
}

異なるタイムゾーンのDateTimeオブジェクトを比較する場合、正確な比較のためには、両方のオブジェクトを同じタイムゾーンに変換することが重要です。以下の例では、with_timezoneメソッドを使用してローカル時刻をUTC時刻に変換し、タイムゾーンを統一してから比較を行っています。これにより、2つの日時が実際に同じ瞬間を指しているかどうかを正確に判断することができます。

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

fn main() {
    let utc_datetime: DateTime<Utc> = Utc::now();
    let local_datetime: DateTime<Local> = Local::now();

    // ローカル時刻をUTCに変換して比較
    if utc_datetime < local_datetime.with_timezone(&Utc) {
        println!("UTCの日時が早い");
    } else if utc_datetime == local_datetime.with_timezone(&Utc) {
        println!("日時が一致している");
    } else if utc_datetime > local_datetime.with_timezone(&Utc) {
        println!("UTCの日時が遅い");
    }
}

このように、異なるタイムゾーン間での比較を正確に行うためには、比較前にタイムゾーンを揃える手順が不可欠です。with_timezoneメソッドを使うことで、異なるタイムゾーンの日時を同一のタイムゾーンに変換し、その上での比較が可能になります。

スポンサーリンク

2つの日時の日差・時間差を求める

2つの日付または日時の間の差(日差や時間差)を計算し、その結果を取得する方法を紹介します。

2つの日付の日差を求める

use chrono::{NaiveDate, Duration};

fn main() {
    let date1 = NaiveDate::from_ymd_opt(2023, 2, 25).unwrap();
    let date2 = NaiveDate::from_ymd_opt(2023, 2, 28).unwrap();

    let time_delta: Duration = date2 - date1;
    println!("date1とdate2の差は{}日です", time_delta.num_days());
}

この例では、NaiveDate::from_ymd_opt関数を使用して2つのNaiveDateオブジェクトを生成し、これらの日付の差を計算しています。date2 - date1により、Duration型のオブジェクトが生成され、このオブジェクトのnum_daysメソッドを使用して、2つの日付の差の日数を取得しています。

2つの日時の時間差を求める

use chrono::{NaiveDateTime, Duration};

fn main() {
    let datetime1 = NaiveDateTime::parse_from_str("2023-02-25 12:00:00", "%Y-%m-%d %H:%M:%S").unwrap();
    let datetime2 = NaiveDateTime::parse_from_str("2023-02-25 15:30:00", "%Y-%m-%d %H:%M:%S").unwrap();

    let time_delta: Duration = datetime2 - datetime1;
    println!("datetime1とdatetime2の差は{}分です", time_delta.num_minutes());
}

この例では、NaiveDateTime::parse_from_str関数を使用して2つのNaiveDateTimeオブジェクトを生成し、これらの日時の差を計算しています。datetime2 - datetime1により、Duration型のオブジェクトが生成され、このオブジェクトのnum_minutesメソッドを使用して、2つの日時の差の分数を取得しています。

Durationの主要な関数

chronoDuration型は、2つの日付や日時の差を表すために使用されます。Durationオブジェクトは、以下のような主要な関数を提供しています:

  • num_seconds(): 総秒数を返します。
  • num_minutes(): 総分数を返します。
  • num_hours(): 総時間数を返します。
  • num_days(): 総日数を返します。
  • num_weeks(): 総週数を返します。
スポンサーリンク
スポンサーリンク

このブログを検索

Profile

自分の写真
Webアプリエンジニア。 日々新しい技術を追い求めてブログでアウトプットしています。
プロフィール画像は、猫村ゆゆこ様に書いてもらいました。

仕事募集もしていたり、していなかったり。

QooQ