RustでWindowsのファイル時刻を扱うためのライブラリのnt-timeの紹介

RustでWindowsのファイル時刻を扱うためのライブラリのnt-timeを開発したので紹介します。

ファイル時刻とは

nt-timeクレートについて説明する前に、ファイル時刻について説明します。

ファイル時刻は、1601年1月1日0時0分0秒 (UTC) から経過した100ナノ秒間隔の数を表す64ビット整数値で、Unix系のシステムにおけるUNIX時間に相当するものです。 ファイル時刻は、NTFSや7zのタイムスタンプとして利用されています12。 ファイル時刻は、Win32 APIではGetSystemTime()を使用することで取得することができ、.NETではDateTime.ToFileTime()を使用することで取得することができます。 最大値は60056年5月28日5時36分10秒955161500 (UTC) ですが、WindowsのAPIでは最大値は64ビット符号付き整数の最大値に制限されます。

nt-timeとは

nt-timeクレートはこのファイル時刻を扱うためのライブラリです。

ファイル時刻を表す型としてnt_time::FileTimeを定義しています。 この型はNew Type Patternを利用しており、内部的にはu64です。

FileTimeは以下のような機能を実装しています。

  1. core::time::Durationなどとの加算や減算
  2. stdtimeクレートやchronoクレートの時間を表す型との相互変換
  3. UNIX時間との相互変換
  4. Serdeを利用したシリアライズとデシリアライズ

コード例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
use core::time::Duration;

use nt_time::{
    time::{macros::datetime, OffsetDateTime},
    FileTime,
};

let ft = FileTime::NT_TIME_EPOCH;
assert_eq!(
    OffsetDateTime::try_from(ft).unwrap(),
    datetime!(1601-01-01 00:00 UTC)
);

let ft = ft + Duration::from_secs(11_644_473_600);
assert_eq!(
    OffsetDateTime::try_from(ft).unwrap(),
    OffsetDateTime::UNIX_EPOCH
);
assert_eq!(ft.to_raw(), 116_444_736_000_000_000);

assert_eq!(FileTime::new(u64::MAX), FileTime::MAX);

定数

以下の3つの定数を定義しています。

演算

FileTimeAddを実装しているので、core::time::Durationtime::Durationの値を加算することができます。 Subではこれらに加えてstd::time::SystemTimeなどの時間を表す型との演算を実装しているので、FileTimeからこれらの型の値を減算することができます。 core::time::Durationとの演算ではオーバーフローした場合に飽和するメソッド (FileTime::saturating_add()FileTime::saturating_sub()) やNoneを返すメソッド (FileTime::checked_add()FileTime::checked_sub()) を定義しています。 また、PartialEqPartialOrdも実装しているので、これらの期間や時間を表す型との同値関係と順序関係を比較することができます。

相互変換

FileTimeFromTryFromによってstd::time::SystemTimetime::OffsetDateTimechrono::DateTime<chrono::offset::Utc>と相互変換することができます。 これらは変換元の値が変換先の型の範囲外の場合はTryFromを使うようになっています。 また、u64の値からFileTimeを作成すること (FileTime::new()) とその逆 (FileTime::to_raw()) にも対応しています。

UNIX時間との相互変換については、秒単位とナノ秒単位に対応しています3456

シリアライズとデシリアライズ

Serdeを利用してISO 8601形式、RFC 2822形式、RFC 3339形式、UNIX時間との間でシリアライズとデシリアライズを行うことができます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
use nt_time::{
    serde::{Deserialize, Serialize},
    serde_with::iso_8601,
    FileTime,
};

#[derive(Debug, Deserialize, PartialEq, Serialize)]
struct DateTime(#[serde(with = "iso_8601")] FileTime);

let json = serde_json::to_string(&DateTime(FileTime::UNIX_EPOCH)).unwrap();
assert_eq!(json, r#""+001970-01-01T00:00:00.000000000Z""#);

assert_eq!(
    serde_json::from_str::<DateTime>(&json).unwrap(),
    DateTime(FileTime::UNIX_EPOCH)
);

終わりに

RustでWindowsのファイル時刻を扱うためのライブラリのnt-timeクレートを紹介しました。

詳細なドキュメントはDocs.rsで確認することができます。

気に入ってもらえたらsorairolake/nt-timeでStarを付けてもらえるとありがたいです。 nt-timeクレートの改善のためにIssuePull Requestもお待ちしています。

Hugo で構築されています。
テーマ StackJimmy によって設計されています。