Files
CCU621M/BSP/app_rtc/ocpp_timestamp_parser.c
T

248 lines
8.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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 */