Initial commit: CCU621_M firmware project with BLE debug link support.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,247 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#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 */
|
||||
Reference in New Issue
Block a user