时间时区转换
本文档介绍如何使用 SDK 提供的时间转换方法,在 C# 脚本中处理 UTC 时间、 用户本地时间与中国时间(UTC+8)之间的转换。
业务场景
Chameleon 平台的数据库统一以 UTC 时间存储时间类型字段。当通过 C# 脚本获取数据时,时间字段通常为 DateTimeOffset 类型(带时区偏移)。在业务处理中,经常需要将 UTC 时间转换为用户所在时区的本地时间、或转换为固定的中国标准时间(UTC+8),反之亦然。
适用场景
- 将数据库查询出的 UTC 时间转换为用户本地时间显示
- 将用户输入的本地时间转换为 UTC 时间存储到数据库
- 将 UTC 时间转换为中国标准时间(北京时间)
- 在脚本中对时间进行跨时区计算和格式化
API 概览
sdk.Helper 提供了 4 组共 8 个时间转换方法:
用户时区转换
| 方法 | 说明 | 参数类型 |
|---|---|---|
UtcToLocalTime | UTC 时间 → 用户本地时间 | DateTime / DateTimeOffset |
LocalToUtcTime | 用户本地时间 → UTC | DateTime / DateTimeOffset |
注意:用户时区转换方法(
UtcToLocalTime、LocalToUtcTime)支持可选参数throwIfMissing(默认false),用于控制当用户时区信息缺失时的行为。详见下方方法说明。
中国时区转换(固定 UTC+8)
| 方法 | 说明 | 参数类型 |
|---|---|---|
UtcToChinaTime | UTC 时间 → 中国标准时间 | DateTime / DateTimeOffset |
ChinaToUtcTime | 中国标准时间 → UTC | DateTime / DateTimeOffset |
所有方法均提供
DateTime和DateTimeOffset两种参数重载,可直接传入从数据库查询得到的DateTimeOffset值。
C# 脚本示例
示例1:基础用法 — 数据库时间转换为本地时间和中国时间
以下示例 从列表数据中获取修改时间,分别转换为用户本地时间和中国时间:
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Chameleon.Common;
using Chameleon.Common.Context;
using Chameleon.Common.Exceptions;
using Chameleon.Common.Models;
using Chameleon.Database;
using Chameleon.SDK;
public class ChameleonCSharpScript
{
public async Task<object> ExecuteAsync(ComponentContext context, ChameleonSDK sdk)
{
var list = context.CmpResult as List<ObjectData>;
// 这是举例从上游获取数据,也可以从数据库查询(数据库返回的都是 UTC 时间
if (list == null || list.Count == 0)
return ResponseResult<object>.Success("empty data");
var first = list[0];
var modifiedTime = first["modifiedtime"];
// 数据库返回的是 DateTimeOffset 类型的 UTC 时间对象,如果上下游处理为 DateTime 类型,或者字符串,那就需要兼容处理
DateTimeOffset dto = modifiedTime switch
{
DateTimeOffset d => d,
DateTime dt => new DateTimeOffset(dt),
string s => DateTimeOffset.Parse(s),
_ => throw new InvalidOperationException($"Unsupported type: {modifiedTime?.GetType().Name}")
};
// 转换为用户当前时区的本地时间 (需要保证上下文时区信息设置正确)
var userLocalTime = sdk.Helper.UtcToLocalTime(dto);
// 转换为中国标准时间(北京时间,UTC+8)
var chinaTime = sdk.Helper.UtcToChinaTime(dto);
return ResponseResult<object>.Success(new
{
// 原始值(带偏移信息)
OriginalTime = dto.ToString("yyyy-MM-dd HH:mm:ss zzz"),
// 用户本地时间
UserLocalTime = userLocalTime.ToString("yyyy-MM-dd HH:mm:ss"),
// 中国时间(北京时间)
ChinaTime = chinaTime.ToString("yyyy-MM-dd HH:mm:ss"),
// UTC时间
UtcTime = dto.UtcDateTime.ToString("yyyy-MM-dd HH:mm:ss")
});
}
}
示例2:将用户输入的时间转换为 UTC 存储
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Chameleon.Common;
using Chameleon.Common.Context;
using Chameleon.Common.Exceptions;
using Chameleon.Common.Models;
import Chameleon.Database;
import Chameleon.SDK;
public class ChameleonCSharpScript
{
public async Task<object> ExecuteAsync(ComponentContext context, ChameleonSDK sdk)
{
// 场景:用户在界面上输入了一个本地时间,需要转为 UTC 存储到数据库
// 例如用户输入了 2026-01-01 00:00:00(用户本地时间)
var userInput = DateTime.Parse("2026-01-01T00:00:00");
// 转为 UTC 存储到数据库
var utcForDb = sdk.Helper.LocalToUtcTime(userInput);
// 如果是固定中国时间输入,转为中国时区后再存 UTC
var chinaInput = DateTime.Parse("2026-01-01T00:00:00");
var utcFromChina = sdk.Helper.ChinaToUtcTime(chinaInput);
return ResponseResult<object>.Success(new
{
UserInput = userInput.ToString("yyyy-MM-dd HH:mm:ss"),
UtcFromLocal = utcForDb.ToString("yyyy-MM-dd HH:mm:ss"),
UtcFromChina = utcFromChina.ToString("yyyy-MM-dd HH:mm:ss")
});
}
}
方法详细说明
UtcToLocalTime — UTC 转用户本地时间
功能:将 UTC 时间转换为当前请求用户的本地时区时间。
// DateTime 版本
DateTime result = sdk.Helper.UtcToLocalTime(DateTime utcTime);
DateTime result = sdk.Helper.UtcToLocalTime(DateTime utcTime, throwIfMissing: true);
// DateTimeOffset 版本(推荐用于数据库查询结果)
DateTime result = sdk.Helper.UtcToLocalTime(DateTimeOffset utcTimeOffset);
参数说明:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
utcTime | DateTime / DateTimeOffset | 是 | - | 待转换的 UTC 时间 |
throwIfMissing | bool | 否 | false | 当用户时区信息缺失时是否抛出异常。false 时静默回退到 UTC;true 时抛出 InvalidOperationException |
注意:
DateTimeOffset重载版本不支持throwIfMissing参数(始终使用默认值false)。如需严格模式,请使用DateTime重载并传入throwIfMissing: true。
LocalToUtcTime — 用户本地时间转 UTC
功能:将用户本地时间转换为 UTC 时间,通常用于数据存储前的时间标准化。
// DateTime 版本
DateTime result = sdk.Helper.LocalToUtcTime(DateTime localTime);
DateTime result = sdk.Helper.LocalToUtcTime(DateTime localTime, throwIfMissing: true);
// DateTimeOffset 版本
DateTime result = sdk.Helper.LocalToUtcTime(DateTimeOffset localTimeOffset);
参数说明:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
localTime | DateTime / DateTimeOffset | 是 | - | 待转换的本地时间 |
throwIfMissing | bool | 否 | false | 当用户时区信息缺失时是否抛出异常。false 时静默回退到 UTC;true 时抛出 InvalidOperationException |
注意:
DateTimeOffset重载版本不支持throwIfMissing参数(始终使用默认值false)。如需严格模 式,请使用DateTime重载并传入throwIfMissing: true。
UtcToChinaTime — UTC 转中国时间
功能:将 UTC 时间转换为中国标准时间(UTC+8,即北京时间)。
// DateTime 版本
DateTime result = sdk.Helper.UtcToChinaTime(DateTime utcTime);
// DateTimeOffset 版本
DateTime result = sdk.Helper.UtcToChinaTime(DateTimeOffset utcTimeOffset);
特点:
- 固定使用东八区时区(Windows:
China Standard Time,Linux:Asia/Shanghai) - 与用户当前时区无关,始终转换为北京时间
- 跨平台兼容(自动适配 Windows/Linux 的时区 ID 差异)
ChinaToUtcTime — 中国时间转 UTC
功能:将中国标准时间(北京时间)转换为 UTC 时间。
// DateTime 版本
DateTime result = sdk.Helper.ChinaToUtcTime(DateTime chinaTime);
// DateTimeOffset 版本
DateTime result = sdk.Helper.ChinaToUtcTime(DateTimeOffset chinaTimeOffset);
注意事项
1. 数据库时间类型为 DateTimeOffset
平台数据库的时间字段存储为 datetimeoffset 类型,查询出来的值为 DateTimeOffset 对象。不要使用 Convert.ToDateTime() 进行转换,这会导致类型转换异常。正确做法是直接强转或使用 DateTimeOffset.Parse():
// ❌ 错误方式:Convert.ToDateTime 不支持 DateTimeOffset
var updateTime = Convert.ToDateTime(first["modifiedtime"]); // 报错!
// ✅ 正确方式:直接解析为 DateTimeOffset 或使用上面示例中的模式匹配性能更高
var dto = DateTimeOffset.Parse(first["modifiedtime"].ToString());
// 然后直接传给 SDK 方法
var localTime = sdk.Helper.UtcToLocalTime(dto); // 支持 DateTimeOffset 重载
2. DateTime.Kind 的自动处理
SDK 内部已自动处理 DateTime.Kind 可能导致的冲突问题:
UtcToLocalTime/UtcToChinaTime:如果输入的 Kind 不是 Utc,会自动标记为 Utc 后再转换LocalToUtcTime/ChinaToUtcTime:如果输入的 Kind 不是 Unspecified,会自动清 除 Kind 标记再转换
因此调用者无需关心 DateTime.Kind 的值,直接传入即可。
3. 返回值类型
所有方法返回的都是 DateTime 类型(非可空),因为作为底层方法应遵循快速失败原则:
- 输入有效则返回有效的
DateTime - 输入有问题则抛出明确的异常,由上层决定如何处理
常见问题
Q: 为什么数据库时间是 DateTimeOffset 而不是 DateTime?
A: DateTimeOffset 包含完整的时区偏移信息(如 +08:00),能够精确记录事件发生的时刻,不会因跨时区传输而丢失信息。DateTime 则只记录日期和时间,不包含时区偏移,存在歧义性。
Q: 用户时区信息从哪里来?
A: 平台通过 前端请求头 X-Timezone-Id 传递用户所在的时区 ID(如 Asia/Shanghai),SDK 在服务端读取该值来确定用户时区。
Q: 如果用户时区信息缺失会怎样?
A: 默认情况下(throwIfMissing: false)会静默回退到 UTC 时区(相当于不转换)。如需在时区缺失时立即发现并处理问题,可使用严格模式 throwIfMissing: true,此时会抛出 InvalidOperationException 异常。
// 宽松模式(默认):时区缺失时静默回退到 UTC
var localTime = sdk.Helper.UtcToLocalTime(utcTime);
// 严格模式:时区缺失时抛出异常,由上层捕获处理
var localTime = sdk.Helper.UtcToLocalTime(utcTime, throwIfMissing: true);