デバッグ日付を使って未来へ過去へと行き来する

#はじめに
　何を書こうかいろいろ迷いましたが、gloopsで行っている開発のちょっとしたテクニックとして
　個人ごとに"現在時刻"を変更できるデバッグ機能を紹介したいと思います。
　ガチャ開催中の画面を確認したい、イベントが終了した後の画面を確認したいなど
　開発を進めていく上で時間の変化によってのプログラムの挙動を確認したいことは頻繁にあります。
　この要件を解決するためにRedisを使った時刻変更デバッグ機能を用意しています。

#デバッグ日付対応DateTimeクラスを作る

プログラム内で時間を取得する際、DateTimeを呼び出すのではなく下記のようなクラスを用意します。
(実際はもう少し複雑ですが、簡略化してます）

```csharp

    public class DebugDate
    {
        /// <summary>
        /// 個人識別ID
        /// </summary>
        public static int PersonalId
        {
            get
            {
              var personalId = 1;
              //本来はHttpContextクラス等から個人識別IDを取得してIDを返す
              return personalId;
            }
        }

        /// <summary>
        /// プロジェクト内で使用する現在日付を返す
        /// </summary>
        public static DateTime Now
        {
            get
            {
#if DEBUG
                return GetDatabaseTime(PersonalId);
#else
                return DateTime.Now;
#endif
            }
        }

#if DEBUG

        /// <summary>
        /// 現在時刻を取得する(Debug用)
        /// </summary>
        public static DateTime GetDatabaseTime(int personalId)
        {
            var databaseTime = DateTime.Now;

            TimeSpan? debugSpan;

            //RedisからTimeSpan?オブジェクトを取得
            var redisKey = "debug_time_test:" + personalId;
            var  isGetData = SampleRedisClass.TryGet(redisKey, out debugSpan);

            //debugSpanの値がある場合は現実時間に時間差分(debugSpan)加えて返す
            return isGetData  && debugSpan.HasValue
                ? databaseTime.Add(debugSpan.Value)
                : databaseTime;
        }
#endif
    }
}
```

ポイントとなるのはGetDatabaseTimeメソッドの部分で
RedisからTimeSpanクラスを取得し...

```csharp
            //RedisからTimeSpan?オブジェクトを取得
            var redisKey = "debug_time_test:" + personalId;
            var  isGetData = SampleRedisClass.TryGet(redisKey, out debugSpan);
```

現在時刻にTimeSpanを加算してから返すことで日付を偽装してます。

```csharp
            //debugSpanの値がある場合は現実時間に時間差分(debugSpan)加えて返す
            return isGetData  && debugSpan.HasValue
                ? databaseTime.Add(debugSpan.Value)
                : databaseTime;
```

#デバッグ日付設定用ページを作る
　適当なTextBoxとButtonを配置して下記のようなアクションを用意すれば準備完了です。
　あとはメインのプログラムでDebugDateクラスを呼び出すだけ

```csharp
        /// <summary>
        /// デバッグ日付を設定する
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void OnEditDate(object sender, EventArgs e)
        {
            //画面で入力されたテキストの日付をDateTime変換
            DateTime resultDate;
            var debugTime = text_debug_time.Text;
            var debugDate = DateTime.TryParse(debugTime, out resultDate);
            if (!debugDate)
            {
                l_result_message.Text = "時間フォーマットが不正";
                return;
            }

            //変更したい時間と現在の時間の差を計算
            var timeSpan = resultDate - DateTime.Now;

            try
            {
                var personalId = txt_personal_id.Text;

                var redisKey = "debug_time_test:" + personalId; //redisに設定するキー
                var redisValue = timeSpan; //redisに設定する値
                var limit = TimeSpan.FromHours(2.0); //有効時間

                //RedisにTimeSpanを設定する
                SampleRedisClass.Set(redisKey, redisValue, limit);
            }
            catch (Exception ex)
            {
                l_result_message.Text = ex.Message;
            }

            text_debug_time.Text = DebugDate.Now.ToString("yyyy/MM/dd HH:mm:ss");
            l_result_message.Text = "変更成功";
        }

```

#メインページでの呼び出し方
呼び出し方はこんな感じです

```csharp
    public partial class main : SampleDebugPage
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            var now = DebugDate.Now; //デバッグ用の日付を取得する
            l_debug_time.Text = now.ToString("yyyy/MM/dd HH:mm:ss");
            l_message.Text = GetMassage(now);
        }

        private string GetMassage(DateTime now)
        {
            if (now.Year == 2005 && now.Month == 8 && now.Day == 2)
            {
                return "gloops 設立!";

            } else if (now.Year == 2017 && now.Month == 12 && now.Day == 18)
            {
                return "ちょうど1年後！";
            }
            else if(now.Year == 2016 && now.Month == 12 && now.Day == 25)
            {
                return "お仕事中...";

            }

            return "-";
        }
    }

```

#実行結果
デバッグ画面で日付を設定して・・・
![image](https://qiita-image-store.s3.amazonaws.com/0/54037/696648e3-dff4-1f1a-3da9-a4d369157b9f.png)

メインページでの表示はこのとおり
![image](https://qiita-image-store.s3.amazonaws.com/0/54037/08d876ad-ef05-3927-280a-ec8cce3113da.png)

※なお設定した時間はその時刻で固定されることなく進行していきます
![image](https://qiita-image-store.s3.amazonaws.com/0/54037/a43fd07e-4097-69ca-f82e-1e25e553bab8.png)

これを使えば・・
![image](https://qiita-image-store.s3.amazonaws.com/0/54037/a69eab60-236f-8172-80c3-d125a7ce2431.png)

おりゃ！！！
![image](https://qiita-image-store.s3.amazonaws.com/0/54037/6e89fbdd-39a5-64d8-db3a-ae4117bb0a16.png)

ほれ！！！
![image](https://qiita-image-store.s3.amazonaws.com/0/54037/f8130c8d-c754-1f79-d308-b4abf4a31ca7.png)

このように未来にも過去にも自由自在！
開発メンバーは自分だけのすきな時間軸で開発・テストをすすめることができるのです。