Skip to main content

时间时区转换

本文档介绍如何使用 SDK 提供的时间转换方法,在 C# 脚本中处理 UTC 时间、用户本地时间与中国时间(UTC+8)之间的转换。

业务场景​

Chameleon 平台的数据库统一以 UTC 时间存储时间类型字段。当通过 C# 脚本获取数据时,时间字段通常为 DateTimeOffset 类型(带时区偏移)。在业务处理中,经常需要将 UTC 时间转换为用户所在时区的本地时间、或转换为固定的中国标准时间(UTC+8),反之亦然。

适用场景​

  • 将数据库查询出的 UTC 时间转换为用户本地时间显示
  • 将用户输入的本地时间转换为 UTC 时间存储到数据库
  • 将 UTC 时间转换为中国标准时间(北京时间)
  • 在脚本中对时间进行跨时区计算和格式化

API 概览​

sdk.Helper 提供了 4 组共 8 个时间转换方法:

用户时区转换​

方法说明参数类型
UtcToLocalTimeUTC 时间 → 用户本地时间DateTime / DateTimeOffset
LocalToUtcTime用户本地时间 → UTCDateTime / DateTimeOffset

注意:用户时区转换方法(UtcToLocalTime、LocalToUtcTime)支持可选参数 throwIfMissing(默认 false),用于控制当用户时区信息缺失时的行为。详见下方方法说明。

中国时区转换(固定 UTC+8)​

方法说明参数类型
UtcToChinaTimeUTC 时间 → 中国标准时间DateTime / DateTimeOffset
ChinaToUtcTime中国标准时间 → UTCDateTime / 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);

参数说明:

参数类型必填默认值说明
utcTimeDateTime / DateTimeOffset是-待转换的 UTC 时间
throwIfMissingbool否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);

参数说明:

参数类型必填默认值说明
localTimeDateTime / DateTimeOffset是-待转换的本地时间
throwIfMissingbool否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);

Q: UtcToChinaTime 和 UtcToLocalTime 有什么区别?​

A: UtcToLocalTime 转换为当前登录用户的时区(动态,随用户变化),而 UtcToChinaTime 始终转换为固定的东八区北京时间(静态,与用户无关)。

Q: 这些方法可以在微服务中使用吗?​

A: 可以,只要能获取到 ChameleonSDK 实例即可使用。注意微服务环境中可能没有 HTTP 请求上下文,此时用户时区相关方法会回退到 UTC。

相关文档​