# 機能設計書 80-Time#strftime

## 概要

本ドキュメントは、mrubyにおけるTime#strftimeメソッドの設計について記述する。Time#strftimeによる日時の書式付き文字列出力機能を提供する。

### 本機能の処理概要

mruby-strftimeは、Timeクラスにstrftimeメソッドを追加し、日時オブジェクトを書式指定文字列に従ってフォーマットされた文字列に変換する機能を提供する。Cのstrftime(3)関数をベースとし、NULバイトを含む書式文字列にも対応する。

**業務上の目的・背景**：日時データを人間が読める形式やログ形式、ファイル名形式等の特定のフォーマットで出力する必要がある。strftimeはCRuby互換の書式ディレクティブを使用し、柔軟な日時フォーマットを可能にする。

**機能の利用シーン**：ログファイルのタイムスタンプ生成、日時のユーザ向け表示、ファイル名への日時埋め込み、JSONやCSV出力時の日時フォーマット。

**主要な処理内容**：
1. 書式文字列の解析（NULバイト対応）
2. strftime(3)による日時フォーマット
3. フォーマット結果の文字列連結

**関連システム・外部連携**：mruby-timeに依存（Timeクラスとstruct tmの取得）。OS標準のstrftime(3)ライブラリ関数に依存。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | ライブラリとして利用。画面なし |

## 機能種別

日時フォーマット / 文字列変換

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| format | String | Yes | strftime書式文字列 | 文字列型（NULバイト含可） |

### 主要書式ディレクティブ

| ディレクティブ | 説明 | 例 |
|--------------|------|-----|
| %Y | 4桁年 | 2023 |
| %m | 月（01-12） | 12 |
| %d | 日（01-31） | 25 |
| %H | 24時間制の時（00-23） | 10 |
| %M | 分（00-59） | 30 |
| %S | 秒（00-60） | 45 |
| %A | 完全な曜日名 | Monday |
| %B | 完全な月名 | December |
| %% | リテラル% | % |

### 入力データソース

- Timeオブジェクト（selfとして）
- 書式文字列（引数として）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| フォーマット済み文字列 | String | 書式に従って変換された日時文字列 |

### 出力先

Rubyオブジェクト（String）として返却。

## 処理フロー

### 処理シーケンス

```
1. 引数として書式文字列（format）を取得（"s"フォーマット: 長さ付き）
2. mrb_time_get_tm() でTimeオブジェクトからstruct tm*を取得
3. 空結果文字列を作成
4. 書式文字列をNULバイトで分割しながらセグメント処理:
   a. NULまたは末尾までのセグメントを取得
   b. セグメントのNUL終端コピーを作成（mrb_temp_alloc）
   c. MSVCの場合、%-フラグをチェック（非対応なら例外）
   d. strftime()でセグメントをフォーマット（MAX_BUFFER_SIZE=4096）
   e. 結果をresult文字列に連結
   f. NULバイトがあれば結果にNULを追加
5. フォーマット済み文字列を返す
```

### フローチャート

```mermaid
flowchart TD
    A[strftime format] --> B[mrb_get_args s]
    B --> C[mrb_time_get_tm]
    C --> D[結果文字列初期化]
    D --> E{残り > 0?}
    E -->|No| F[結果を返す]
    E -->|Yes| G[NULバイト検索]
    G --> H[セグメント取得]
    H --> I[NUL終端コピー作成]
    I --> J[strftime呼び出し]
    J --> K[結果に追加]
    K --> L{NULがあった?}
    L -->|Yes| M[NULを結果に追加]
    M --> N[次セグメントへ]
    L -->|No| F
    N --> E
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-80-1 | NULバイト保持 | 書式文字列中のNULバイトは出力にも保持される | 書式文字列にNULを含む場合 |
| BR-80-2 | セグメント処理 | NULバイトで書式を分割し、各セグメントを個別にstrftimeする | 常時 |
| BR-80-3 | 最大バッファ | 各セグメントのフォーマット結果はMAX_BUFFER_SIZE（4096バイト）まで | 常時 |
| BR-80-4 | MSVC制限 | MSVC環境では%-フラグ（GNU拡張）は使用不可 | _MSC_VER定義時 |
| BR-80-5 | 例外安全 | mrb_temp_allocを使用し、例外発生時もGCがメモリを回収 | 常時 |

### 計算ロジック

- strftime(3)ライブラリ関数に書式変換を委譲

## データベース操作仕様

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ArgumentError | MSVC環境で%-フラグ使用 | "strftime format flag '%-' not supported on this platform" |

### リトライ仕様

リトライは不要。

## トランザクション仕様

該当なし。

## パフォーマンス要件

- MAX_BUFFER_SIZE（4096バイト）が各セグメントの最大出力サイズ
- mrb_temp_allocで一時メモリを使用（GC管理）

## セキュリティ考慮事項

- 書式文字列はユーザ入力の可能性があるが、strftime(3)への直接パスのため特別なサニタイズは不要

## 備考

- mruby-timeに依存（mrb_time_get_tm関数の提供元）
- strftime(3)のサポートする書式ディレクティブはプラットフォームに依存
- NULバイト対応はCRuby互換のための実装

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | strftime.c | `mrbgems/mruby-strftime/src/strftime.c` | MAX_BUFFER_SIZE定数（15行目） |

**読解のコツ**: 本機能は単一の関数で完結する。mrb_time_get_tmはmruby-time gemが提供するAPIで、Timeオブジェクトからstruct tm*を取得する。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | strftime.c | `mrbgems/mruby-strftime/src/strftime.c` | mrb_mruby_strftime_gem_init関数（107-114行目） |

**主要処理フロー**:
1. **112行目**: Timeクラスを取得
2. **113行目**: strftimeメソッドをTimeクラスに登録（引数1つ必須）

#### Step 3: strftimeメソッドの実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | strftime.c | `mrbgems/mruby-strftime/src/strftime.c` | mrb_time_strftime関数（41-105行目） |

**主要処理フロー**:
- **47行目**: "s"フォーマットで長さ付き文字列取得（NULバイト対応）
- **48行目**: mrb_time_get_tmでstruct tm*取得
- **50行目**: 空結果文字列を作成
- **55行目**: NULバイト検索（memchr）
- **57行目**: セグメント長計算
- **67行目**: mrb_temp_allocでNUL終端コピー作成（例外安全）
- **84行目**: mrb_temp_allocでバッファ確保
- **87行目**: strftime()呼び出し
- **90行目**: 結果に追加
- **94-98行目**: NULバイトの追加と次セグメントへ

### プログラム呼び出し階層図

```
mrb_mruby_strftime_gem_init
    └─ mrb_define_method_id (Time#strftime)

Time#strftime(format)
    └─ mrb_time_strftime
        ├─ mrb_get_args ("s")
        ├─ mrb_time_get_tm (mruby-time提供)
        ├─ mrb_str_new (結果文字列初期化)
        └─ [セグメントループ]
            ├─ memchr (NULバイト検索)
            ├─ mrb_temp_alloc (セグメントコピー)
            ├─ strftime (Cライブラリ)
            └─ mrb_str_cat (結果に追加)
```

### データフロー図

```
[入力]                    [処理]                       [出力]

Timeオブジェクト ──▶ mrb_time_get_tm ──▶ struct tm*
                          │
書式文字列 ────────▶ セグメント分割
  "s"フォーマット          │
                          ▼
                    strftime(buf, MAX, segment, tm)
                          │
                          ▼
                    mrb_str_cat (連結)
                          │
                          ▼
                    フォーマット済みString
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| strftime.c | `mrbgems/mruby-strftime/src/strftime.c` | ソース | メインC実装 |
| mrbgem.rake | `mrbgems/mruby-strftime/mrbgem.rake` | 設定 | gem定義（mruby-time依存宣言） |
| strftime.rb | `mrbgems/mruby-strftime/test/strftime.rb` | テスト | テストコード |
