💡

仕事で遭遇したバグからのちょっとした思い

2023/06/10に公開

はじめに

Apple Vision Proがめっちゃくちゃほしい!キョです。

エンジニアの方なら、
誰でもバグに遭遇したことはあると思いますね。

今回は私が最近、仕事で遭遇したバグと、
バグの調査を行った後思ったことを共有したいと思います。

どんなバグ?

バグが発生した処理は以下になります。

  • 入力したクレジットカード有効期限の「月/年(西暦の後ろ2桁)」が、
    正しい未来日付になっているかのチェック処理

バグ現象は以下です。

  • 年の部分に30より大きい数字を入力したら、過去日付として誤判断されてしまう
  • 開発環境では発生せず、本番環境でしか発生しない

現象から見ると、特定の環境でしか発生しないので、
環境の設定問題と関係ありそうと最初推測しました。

バグの調査

日付チェック処理のロジックの確認

バグの原因を調べるために、
まずは、この日付チェック処理はどんなロジックになっているかを確認する必要がありますね。
ロジックを一通り確認した結果、処理はシンプルでした。
入力した2桁の年を4桁に変換した後、4桁年と入力した月を持って、
コンピューター上の現在の年月と比較しています。

日付の比較ロジックは特に問題ありませんでしたので、
私はバグの原因は以下の二つ中のどっちかと推測しました。

  1. サーバーの年月日設定が正しくない
  2. 年を2桁から4桁に変換する処理がうまく機能していない

確認した結果、
サーバーの年月日設定は問題なかったでした。
それなら、変換処理の問題になる可能性が高いです。
開発環境では発生せず、本番環境でしか発生しないので、
一体どう変換しているか気になりますね。

原因

年を2桁から4桁に変換する処理は簡単で、
C#のSystem.Globalization名前空間にあるToFourDigitYearメソッドを利用しています。

// 指定した年を4桁表記に変換
var fourDigitYear = System.Globalization.CultureInfo.CurrentCulture.Calendar.ToFourDigitYear(year);

一見、提供されているメソッドを利用しているので、
特に問題ないかなと思うかもしれないですが、
例えば、30を入力した場合、
この30を1930、2030どっちに変換するか、
どんな基準で変換ルールを決めているかはわからないですね。

そして、このメソッドのドキュメントを確認してみました。
https://learn.microsoft.com/ja-jp/dotnet/api/system.globalization.calendar.tofourdigityear?view=netframework-4.7.2

TwoDigitYearMax プロパティを使用して、指定した年を 4 桁表記に変換し、適切な世紀を判断します。
TwoDigitYearMax は、2 桁の年で表すことができる 100 年の範囲の最後の年です。 世紀は、その 100 年の範囲内の 2 桁 year の唯一の出現を見つけることによって決定されます。 たとえば、2029 に設定されている場合 TwoDigitYearMax 、100 年の範囲は 1930 から 2029 です。 したがって、2 桁の値 30 は 1930 として解釈され、2 桁の値 29 は 2029 と解釈されます。

なるほど、TwoDigitYearMaxというプロパティを見てどう変換するかを判断していますね。
このTwoDigitYearMaxプロパティについても、ドキュメントを確認してみました。
https://learn.microsoft.com/ja-jp/dotnet/api/system.globalization.calendar.twodigityearmax?view=netframework-4.7.2#system-globalization-calendar-twodigityearmax

public virtual int TwoDigitYearMax { get; set; }

設定可能なプロパティなので、どこかで値設定していないかを確認した結果、
値の設定処理はありませんでしたので、初期値のままになっていると思います。
初期値についてはドキュメントの説明は以下になります。

このプロパティの初期値は、コントロール パネルの地域と言語のオプションの部分の設定から派生します。 ただし、その情報は の有効期間中に変更される AppDomain可能性があります。 クラスは Calendar 、システム設定の変更を自動的に検出しません。 カレンダーが地域と言語のオプションでサポートされていない場合、このプロパティの初期値は クラスによって定義された Calendar 既定値です。

Windowsのコントロールパネルでの設定を見ていますね。これだ!

そして、実際に各環境の設定内容を確認した結果、

  • 開発環境では「2049」
  • 本番環境では「2029」

になっていますので、

  • 開発環境では30→2030に変換
  • 本番環境では30→1930に変換

これで、原因が確定できました。

https://learn.microsoft.com/ja-jp/dotnet/core/compatibility/globalization/8.0/twodigityearmax-default

ちょっとした思い

このバグの調査から思ったことは

  1. 実装を行う際は、自分が何をやっているのかを理解した上で進めるべきである
    • 特に、「よくわからないけど、他のところからコピーしてきたら動いた!」のようなことはやるべきではない
  2. 特定の環境に依存するものを利用する際には、それに明確な理由がなければ避けるべきである

例えば、今回の場合は、
ToFourDigitYearメソッドが、
「指定した年を4桁表記に変換するメソッド」だけ見て使うのではなく、
どんなルールで変換しているか、そのルールは要件に満たしているか等も、
確認した上で使う必要があると思いますね。

正直1については、
今回のような、フレームワークが提供されている、
ドキュメントがすごくちゃんと整備されているメソッドの場合は、
まだやりやすいと思いますが、
システム上の独自実装したメソッドを利用する場合は、
結構苦労するかもしれないですが、できるところまではやる必要はあると思います。

最後に

今回のバグ調査から思ったことを共有しました。
それでは、みなさんよき良い開発ライフをお送りください!

Discussion