本文中的时区通过TimeZoneInfo.FindSystemTimeZoneById
获取的,其中的参数Id
是windows下特有的,因此其Id
在不同的操作系统下不一定存在,为标准起见可采用IANA时区库
,可以使用三方库NodaTime
来处理时间,本文暂不对NodaTime
讲解。
- DateTime 不储存时区信息
- DateTimeOffset 可以储存时区信息
代码:
/// <summary>
/// 转换字符串为DateTime
/// </summary>
/// <param name="sourceTimeString">
/// 源时间字符串
/// </param>
/// <param name="sourceFormat">
/// 源时间的字符串格式<br/>
/// 1. 例如: "yyyyMMddHHmmss","yyyy-MM-dd HH:mm:ss","yyyy-MM-ddTHH:mm:ssZ"<br/>
/// 2. 本参数仅用于解析源时间时指定的字符串格式,不会对字符串中是否存在时区信息进行处理和转换<br/>
/// </param>
/// <param name="sourceIsUtc">
/// 源时间是否UTC时间
/// </param>
/// <param name="sourceTimeZoneInfoId">
/// 时区信息ID<br/>
/// 1. 如果sourceIsUtc为true,该参数可以为空<br/>
/// 2. 可通过<seealso cref="GetAllTimeZoneId"/>获取所有时区查看需要的时区ID<br/>
/// 3. 中国时区ID为: "China Standard Time"
/// </param>
/// <param name="isToUtc">
/// 是否转换为UTC时间<br/>
/// 1. 如果为false,则转换为本地时间(本地时间取决于操作系统时区)<br/>
/// 2. 如果需要转换为其他时区的时间,请使"isToUtc"为"true"后得到UTC时间再自行进行转换
/// </param>
/// <returns></returns>
public static DateTime? FormatDate(
this string sourceTimeString,
string sourceFormat,
bool sourceIsUtc,
string sourceTimeZoneInfoId,
bool isToUtc
)
{
// 将指定时间转换为无时区的时间
var sourceProvider = CultureInfo.InvariantCulture;// 不指定区域文化
var srouceStyle = DateTimeStyles.None;// 不指定时区 System.DateTimeKind.Unspecified
// 这个时间是默认建立在操作系统所在地区的时区上,因此不能直接使用,需要进一步转换为带时区和偏移的时间
var sourceLocalTime = DateTime.ParseExact(sourceTimeString, sourceFormat, sourceProvider, srouceStyle);
// 获取源时间对应的国家时区信息,以便找到时间偏移量
var sourceTimeZoneInfo = GetTimeZoneOffsetHours(sourceIsUtc, sourceTimeZoneInfoId);
var sourceOffsetHours = sourceTimeZoneInfo.BaseUtcOffset.Hours;
// 将无时区时间转换为带时区的时间
var dateTimeOffset = new DateTimeOffset(sourceLocalTime, TimeSpan.FromHours(sourceOffsetHours));
if (isToUtc)
{
return dateTimeOffset.UtcDateTime;
}
else
{
return dateTimeOffset.LocalDateTime;
}
}
/// <summary>
/// 根据时区ID获取时区信息
/// </summary>
/// <param name="isUtc"></param>
/// <param name="timeZoneInfoId"></param>
/// <returns></returns>
private static TimeZoneInfo GetTimeZoneOffsetHours(bool isUtc, string timeZoneInfoId = "China Standard Time")
{
if (isUtc)
{
return TimeZoneInfo.Utc;
}
else
{
return TimeZoneInfo.FindSystemTimeZoneById(timeZoneInfoId);
}
}
/// <summary>
/// 获取系统中所有可用的时区信息
/// </summary>
/// <returns></returns>
public static List<Tuple<int, string>> GetAllTimeZoneId()
{
// 获取系统中所有可用的时区信息
var timeZones = TimeZoneInfo.GetSystemTimeZones();
return timeZones
.Select(t => new Tuple<int, string>(t.BaseUtcOffset.Hours, t.Id))
.OrderBy(t => t.Item1)
.ThenBy(t => t.Item2)
.ToList();
}
单元测试:
{
// 获取当前系统所在时区的时间
var dateTime = DateTime.Now;
// 转换为日本时间
TimeZoneInfo tokyoTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");
DateTime tokyoTime = TimeZoneInfo.ConvertTime(dateTime, tokyoTimeZone);
}
{
var timeZoneIds = StringExtension.GetAllTimeZoneId();
// 假设日本时间为 2024-08-16 21:00:00,对应utc时间为 12:00,对应北京时间(本机)为 20:00
var inputTimeString = "20240816210000";
string sourceFormat = "yyyyMMddHHmmss";
bool sourceIsUtc = false;
string sourceTimeZoneInfoId = "Tokyo Standard Time";
bool isToUtc = true;
var outDateTime = inputTimeString.FormatDate(sourceFormat, sourceIsUtc, sourceTimeZoneInfoId, isToUtc);
var beijingTime = DateTime.Parse("2024-08-16 20:00:00");
Assert.AreEqual(
outDateTime,
beijingTime.ToUniversalTime()
);
}