getLocalTime()がエラーになる
GPSから取得した時刻を、システム時刻として取り入た後、getLocalTime()関数を呼ぶとエラーが返ってくることがありました。
GPSから得た年月日と日時は、以下のようなプログラムでシステム時刻に設定しています。
struct tm t; t.tm_year = gps.date.year() - 1900; t.tm_mon = gps.date.month() - 1; t.tm_mday = gps.date.day(); t.tm_hour = gps.time.hour(); t.tm_min = gps.time.minute(); t.tm_sec = gps.time.second(); time_t timertc = mktime(&t) + JST; struct timeval tv = { .tv_sec = timertc }; settimeofday(&tv, NULL);
この後、getLocalTime()関数を呼ぶと、電源投入後しばらくの間エラーが返ってきてしまいます。
原因を調べていくと、エラーになる条件はGPSモジュールがコールドスタートした時に、しばらくの間、適当な年月日を返していて、年が2080年になっている時でした。
2080年が設定されたtm構造体でシステム時刻を設定しようとすると、おかしいことになるようです。
試しに、2080年が設定されたtm構造体をmktime()でUNIX Timeに変換した結果を見てみました。
Serial.println( mktime(&t) );
すると、結果は
マイナスの値が表示されました。
ESP32のtime_tは2038年問題が起こる?
負の値が返ってくるということは、もしかすると2038年問題に遭遇してしまうということです。
2038年問題は、時刻の情報をsigned int(32bit)の型で保持したシステムが、2038年1月19日3時14分8秒になった途端にsigned intが負の値になってしまい、正しく時刻の処理ができなくなる問題です。
最近のシステムは64bit化したり32bitでもunsignedにしたりして、対策を進めているようですが、ESP32の場合はsigned int(32bit)のままなのかもしれません。
試しに以下のプログラムを実行してみました。
#define Y2038_BUG 2147483648 //2038年1月19日3時14分8秒 time_t now; void setup() { Serial.begin(115200); now = Y2038_BUG; Serial.println(""); Serial.print("Time_t:"); Serial.println(now); Serial.print("Time_t size:"); Serial.print( sizeof(now) ); Serial.print(" Bytes"); } void loop() { }
time_tを、問題の発生する2038年1月19日3時14分8秒にして、time_tと、time_tのバイト数を見てみます。
結果は
Time_t size:4 Bytes
time_tはマイナスの値になってしまいました。型のサイズも4バイトなので32bitということですね。
マイナスになるということは、singed int(32bit)ということでしょう。
これが本当ならば、ESP32でtime_tを使うプログラムは、2038年にバグが発生してしまいます。NTPを使ったプログラムなんかも、対象になってしまうでしょう。
今回のGPSモジュールを使った時計の場合、2038年以降、getLocalTime()がエラーを返すため、時刻の表示できなくなってしまいます。とりあえず、time_tを使わないプログラムへ変更しようと思います。
signed なのか unsigned なのかは、time_tの定義を見ればはっきりとわかるのですが、見つけられませんでした。
ESP32のtime_tにはご注意ください。
コメント