解析 GPS NMEA 協議 GPRMC 句子,提取 UTC 日期時間,轉換為歐洲中部時間(CET/CEST),自動處理夏令時切換。
計算公式(y 為年份,如 2011):
31 - ((5*y/4 + 4) % 7)31 - ((5*y/4 + 1) % 7)// 假設 lineData 為 GPRMC 字段列表
// 日期字段:DDMMYY (字段 9)
int date = Integer.parseInt(lineData.get(9));
int day = date / 10000;
int month = (date / 100) % 100;
int year = date % 100 + 2000;
// 時間字段:HHMMSS (字段 1)
int time = Integer.parseInt(lineData.get(1));
int hour = time / 10000;
int minute = (time / 100) % 100;
int second = time % 100;
int totalSec = hour * 3600 + minute * 60 + second;
// 計算夏令時切換閾值
int marchDay = 31 - ((5 * year / 4 + 4) % 7);
int octDay = 31 - ((5 * year / 4 + 1) % 7);
boolean isSummer = false;
if (month > 3 && month < 10)
isSummer = true;
else if (month == 3 && day > marchDay)
isSummer = true;
else if (month == 3 && day == marchDay && totalSec >= 3600) // 01:00 UTC
isSummer = true;
else if (month == 10 && day < octDay)
isSummer = true;
else if (month == 10 && day == octDay && totalSec < 3600)
isSummer = true;
if (isSummer) hour += 2; // CEST = UTC+2
else hour += 1; // CET = UTC+1
// 處理跨日
if (hour >= 24) { hour -= 24; day++; /* 需處理月份翻轉 */ }
#include <stdio.h>
#include <time.h>
int is_eu_summer_time(int year, int month, int day, int hour, int min, int sec) {
int marchDay = 31 - ((5 * year / 4 + 4) % 7);
int octDay = 31 - ((5 * year / 4 + 1) % 7);
int totalSec = hour * 3600 + min * 60 + sec;
if (month > 3 && month < 10) return 1;
if (month == 3 && day > marchDay) return 1;
if (month == 3 && day == marchDay && totalSec >= 3600) return 1;
if (month == 10 && day < octDay) return 1;
if (month == 10 && day == octDay && totalSec < 3600) return 1;
return 0;
}
void utc_to_cet(int *year, int *month, int *day, int *hour, int *min, int *sec) {
int offset = is_eu_summer_time(*year, *month, *day, *hour, *min, *sec) ? 2 : 1;
*hour += offset;
if (*hour >= 24) {
*hour -= 24;
// 簡易日期推進(實際應用 mktime 規範化)
struct tm t = {0};
t.tm_year = *year - 1900;
t.tm_mon = *month - 1;
t.tm_mday = *day + 1;
t.tm_hour = *hour;
t.tm_min = *min;
t.tm_sec = *sec;
mktime(&t);
*year = t.tm_year + 1900;
*month = t.tm_mon + 1;
*day = t.tm_mday;
}
}
| GPRMC 時間 | UTC | 轉換後 | 說明 |
|---|---|---|---|
| 010001, 261008 | 2008-10-26 01:00:01 | 02:00:01 | 夏令時結束日 01:00 UTC 後 → CEST |
| 005959, 261008 | 2008-10-26 00:59:59 | 01:59:59 | 夏令時結束日 01:00 UTC 前 → CEST |
| 183731, 081111 | 2011-11-08 18:37:31 | 19:37:31 | 冬令時 (CET, UTC+1) |
| 183731, 291011 | 2011-10-29 18:37:31 | 20:37:31 | 夏令時結束日前 → CEST (UTC+2) |
| 003731, 270311 | 2011-03-27 00:37:31 | 01:37:31 | 夏令時開始日 01:00 UTC 前 → CET |
| 183731, 270311 | 2011-03-27 18:37:31 | 20:37:31 | 夏令時開始日 01:00 UTC 後 → CEST |
date.h (Howard Hinnant)、Java java.time.ZoneId.of("Europe/Berlin")、Python pytz/zoneinfo,自動處理歷史規則變更