#include #include #include #include "publicdata/type.h" typedef unsigned char U8_T; typedef unsigned short U16_T; // 闰年检查 static U8_T u8_is_leap_year(U16_T year) { return ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)); } // 获取月份天数 static U8_T u8_get_days_in_month(U16_T year, U8_T month) { static const U8_T days_in_month[] = {31,28,31,30,31,30,31,31,30,31,30,31}; if (month < 1 || month > 12) return 0; U8_T days = days_in_month[month-1]; // 闰年二月特殊处理 if (month == 2 && u8_is_leap_year(year)) { days = 29; } return days; } // 日期时间验证 static U8_T u8_validate_datetime(const Comm_Time *ctTime) { // 年份范围检查 if (ctTime->iYear < 2020 || ctTime->iYear > 2100) { return 0; } // 月份检查 if (ctTime->ucMonth < 1 || ctTime->ucMonth > 12) { return 0; } // 日期检查 U8_T max_days = u8_get_days_in_month(ctTime->iYear, ctTime->ucMonth); if (ctTime->ucDay < 1 || ctTime->ucDay > max_days) { return 0; } // 时间检查 if (ctTime->ucHour > 23 || ctTime->ucMin > 59 || ctTime->ucSec > 59) { return 0; } return 1; } #if 0 /* CCU621_M: duplicate of app_rtc.c — disabled to avoid L6200E */ /******************************************************************************* * 名称: v_adjust_time_with_timezone * 功能: 按给定时区偏移量调整 Comm_Time 中的时刻与日期,用于 OCPP 时间戳与本地/UTC 转换。 * 先将“时+分”与偏移量合并为总分钟数,归一化到 [0, 24*60) 并得到日进退数, * 再按日进退数正确跨月、跨年调整日期(含闰年与各月天数)。 * * 参数: * ctTime - 输入输出,待调整的日期时间结构(会直接修改 ucHour/ucMin/ucDay/ucMonth/iYear) * hour_offset - 时区偏移小时数,可为负(如 UTC+8 传入 8,UTC-5 传入 -5) * min_offset - 时区偏移分钟数,可为负(如 +08:30 则 hour_offset=8, min_offset=30) * * 说明: * - 仅修改时、分、日、月、年;秒(ucSec)不参与计算也不改动。 * - 日期跨月、跨年时按公历规则处理(含闰年二月 28/29 天)。 * - 若调整后年份超出合理范围,由调用方 u8_validate_datetime() 等做校验或裁剪。 ******************************************************************************/ void v_adjust_time_with_timezone(Comm_Time *ctTime, int hour_offset, int min_offset) { const int minutes_per_day = 24 * 60; int total_minutes; int days_adjust; int year; U8_T month; int day; U8_T max_days; if (ctTime == NULL) { return; } /* 1) 将“当前时+分”与偏移量合并为总分钟数 */ total_minutes = ctTime->ucHour * 60 + ctTime->ucMin; total_minutes += hour_offset * 60 + min_offset; /* 2) 归一化到 [0, minutes_per_day),并得到需要进退的天数 */ days_adjust = total_minutes / minutes_per_day; total_minutes = total_minutes % minutes_per_day; if (total_minutes < 0) { total_minutes += minutes_per_day; days_adjust--; } /* 3) 写回时、分 */ ctTime->ucHour = (U8_T)(total_minutes / 60); ctTime->ucMin = (U8_T)(total_minutes % 60); /* 4) 按日进退数调整日期,并正确处理跨月、跨年 */ if (days_adjust == 0) { return; } year = (int)ctTime->iYear; month = ctTime->ucMonth; day = (int)ctTime->ucDay + days_adjust; /* 向后推日期:day 超出当月则进入下月/下年 */ while (day > (max_days = u8_get_days_in_month((U16_T)year, month))) { day -= max_days; month++; if (month > 12) { month = 1; year++; } } /* 向前推日期:day 小于 1 则进入上月/上年 */ while (day < 1) { month--; if (month < 1) { month = 12; year--; } day += u8_get_days_in_month((U16_T)year, month); } ctTime->ucDay = (U8_T)day; ctTime->ucMonth = month; ctTime->iYear = (U16_T)year; } // 主函数:OCPP时间戳解析 // time_type: 0-存入国际标准时间(UTC), 1-存入北京时间(UTC+8) // 返回:0-成功,非0-失败 U8_T u8_ocpp_timestamp_to_tm(Comm_Time *ctTime, const char *time_str, U8_T time_type) { if (ctTime == NULL || time_str == NULL) { return 1; // 参数错误 } // 复制输入字符串,避免修改原始数据 char time_copy[64]; if (strlen(time_str) >= sizeof(time_copy)) { return 1; // 输入过长 } strcpy(time_copy, time_str); // 分割日期时间部分和时区部分 char *time_part = strchr(time_copy, 'T'); if (time_part == NULL) { return 1; // 格式错误,缺少'T' } *time_part = '\0'; time_part++; // 解析日期部分 (YYYY-MM-DD) if (sscanf(time_copy, "%hu-%hhu-%hhu", &ctTime->iYear, &ctTime->ucMonth, &ctTime->ucDay) != 3) { return 1; // 日期解析失败 } int hour, min, sec, millisec = 0; char tz_sign = 0; int tz_hour = 0, tz_min = 0; // 尝试解析带毫秒的格式 int parsed = sscanf(time_part, "%d:%d:%d.%d%c%d:%d", &hour, &min, &sec, &millisec, &tz_sign, &tz_hour, &tz_min); if (parsed == 7) { // Case 1: 带毫秒和时区偏移(如 "10:00:28.990+08:00") } else { // 尝试解析带毫秒的UTC格式(Z结尾) parsed = sscanf(time_part, "%d:%d:%d.%dZ", &hour, &min, &sec, &millisec); if (parsed == 4) { // Case 2: 带毫秒的UTC时间(如 "10:00:28.990Z") tz_sign = 'Z'; } else { // 尝试无毫秒的格式 parsed = sscanf(time_part, "%d:%d:%d%c%d:%d", &hour, &min, &sec, &tz_sign, &tz_hour, &tz_min); if (parsed == 6) { // Case 3: 无毫秒带时区偏移(如 "10:14:14+08:00") } else { parsed = sscanf(time_part, "%d:%d:%dZ", &hour, &min, &sec); if (parsed == 3) { // Case 4: 无毫秒UTC时间(如 "15:09:18Z") tz_sign = 'Z'; } else { return 1; // 时间格式解析失败 } } } } // 填充时间部分 ctTime->ucHour = hour; ctTime->ucMin = min; ctTime->ucSec = sec; // 时区处理逻辑 if (time_type == 1) { // 需要转换为北京时间 (UTC+8) if (tz_sign == 'Z' || tz_sign == 0) { // UTC时间直接加8小时 v_adjust_time_with_timezone(ctTime, 8, 0); } else { // 处理带时区偏移的情况 // 先将输入时间转换为UTC,再转换为北京时间 int original_offset_hour = (tz_sign == '+') ? tz_hour : -tz_hour; int original_offset_min = (tz_sign == '+') ? tz_min : -tz_min; // 转换为UTC时间(减去原始偏移) v_adjust_time_with_timezone(ctTime, -original_offset_hour, -original_offset_min); // 再转换为北京时间(加上8小时) v_adjust_time_with_timezone(ctTime, 8, 0); } } else { // time_type == 0,存入国际标准时间(UTC) if (tz_sign != 'Z' && tz_sign != 0) { // 非UTC时间需要转换为UTC int original_offset_hour = (tz_sign == '+') ? tz_hour : -tz_hour; int original_offset_min = (tz_sign == '+') ? tz_min : -tz_min; // 转换为UTC时间(减去原始偏移) v_adjust_time_with_timezone(ctTime, -original_offset_hour, -original_offset_min); } // 如果已经是UTC时间(Z结尾),则不需要转换 } // 最终数据验证 if (!u8_validate_datetime(ctTime)) { return 1; // 数据验证失败 } return 0; // 成功 } #endif /* CCU621_M: duplicate disabled */