我正在尝试创建一个单元测试,以测试当机器上的时区因设置不正确而更改时的情况。
在测试中,我需要能够在非本地时区创建DateTime对象,以确保运行测试的人可以成功地这样做,而不管他们位于哪里。
从我可以从DateTime构造函数中看到,我可以将TimeZone设置为本地时区,UTC时区或未指定。
我如何创建一个日期时间与特定的时区,如PST?
我正在尝试创建一个单元测试,以测试当机器上的时区因设置不正确而更改时的情况。
在测试中,我需要能够在非本地时区创建DateTime对象,以确保运行测试的人可以成功地这样做,而不管他们位于哪里。
从我可以从DateTime构造函数中看到,我可以将TimeZone设置为本地时区,UTC时区或未指定。
我如何创建一个日期时间与特定的时区,如PST?
当前回答
我扩展了乔恩·斯基特的回答,添加了一些额外的内容,让它感觉更接近日期时间。在很大程度上,这将简化比较、相等和转换。我发现datetimezone . now("")函数是特别有用的。
需要注意的一点是这个结构体是在。net 6中编写的。因此,如果您使用的是旧版本,您可能需要替换一些新语言功能的使用。
此外,操作符和接口的实现受到了GitHub上DateTime.cs的. net引用的启发。
/// <summary>
/// This value type represents a date and time with a specific time zone applied. If no time zone is provided, the local system time zone will be used.
/// </summary>
public readonly struct DateTimeZoned : IComparable, IComparable<DateTimeZoned>, IEquatable<DateTimeZoned>
{
/// <summary>
/// Creates a new zoned <see cref="DateTime"/> with the system time zone.
/// </summary>
/// <param name="dateTime">The local <see cref="DateTime"/> to apply a time zone to.</param>
public DateTimeZoned(DateTime dateTime)
{
var local = DateTime.SpecifyKind(dateTime, DateTimeKind.Local);
UniversalTime = TimeZoneInfo.ConvertTimeToUtc(local, TimeZoneInfo.Local);
TimeZone = TimeZoneInfo.Local;
}
/// <summary>
/// Creates a new zoned <see cref="DateTime"/> with the specified time zone.
/// </summary>
/// <param name="dateTime">The <see cref="DateTime"/> to apply a time zone to.</param>
/// <param name="timeZone">The time zone to apply.</param>
/// <remarks>
/// Assumes the provided <see cref="DateTime"/> is from the specified time zone.
/// </remarks>
public DateTimeZoned(DateTime dateTime, TimeZoneInfo timeZone)
{
var unspecified = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
UniversalTime = TimeZoneInfo.ConvertTimeToUtc(unspecified, timeZone);
TimeZone = timeZone;
}
/// <summary>
/// Creates a new zoned <see cref="DateTime"/> with the specified time zone.
/// </summary>
/// <param name="dateTime">The <see cref="DateTime"/> to apply a time zone to.</param>
/// <param name="timeZone">The time zone to apply.</param>
/// <remarks>
/// Assumes the provided <see cref="DateTime"/> is from the specified time zone.
/// </remarks>
public DateTimeZoned(DateTime dateTime, string timeZone)
{
var unspecified = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
UniversalTime = TimeZoneInfo.ConvertTimeToUtc(unspecified, timeZoneInfo);
TimeZone = timeZoneInfo;
}
/// <summary>
/// The UTC <see cref="DateTime"/> for the stored value.
/// </summary>
public DateTime UniversalTime { get; init; }
/// <summary>
/// The selected time zone.
/// </summary>
public TimeZoneInfo TimeZone { get; init; }
/// <summary>
/// The localized <see cref="DateTime"/> for the stored value.
/// </summary>
public DateTime LocalTime => TimeZoneInfo.ConvertTime(UniversalTime, TimeZone);
/// <summary>
/// Specifies whether UTC and localized values are the same.
/// </summary>
public bool IsUtc => UniversalTime == LocalTime;
/// <summary>
/// Returns a new <see cref="DateTimeZoned"/> with the current <see cref="LocalTime"/> converted to the target time zone.
/// </summary>
/// <param name="timeZone">The time zone to convert to.</param>
public DateTimeZoned ConvertTo(string timeZone)
{
var converted = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(LocalTime, TimeZone.Id, timeZone);
return new DateTimeZoned(converted, timeZone);
}
/// <summary>
/// Returns a new <see cref="DateTimeZoned"/> with the current <see cref="LocalTime"/> converted to the target time zone.
/// </summary>
/// <param name="timeZone">The time zone to convert to.</param>
public DateTimeZoned ConvertTo(TimeZoneInfo timeZone)
{
var converted = TimeZoneInfo.ConvertTime(LocalTime, TimeZone, timeZone);
return new DateTimeZoned(converted, timeZone.Id);
}
/// <summary>
/// Returns the value as a string in the round-trip date/time pattern.
/// </summary>
/// <remarks>
/// This applies the .ToString("o") option on <see cref="LocalTime"/>.
/// </remarks>
public string ToLocalString()
{
var local = new DateTimeOffset(LocalTime, TimeZone.BaseUtcOffset);
return local.ToString("o");
}
/// <summary>
/// Returns the value as a string in the universal sortable date/time pattern.
/// </summary>
/// <remarks>
/// This is applies the .ToString("u") option on <see cref="UniversalTime"/>.
/// </remarks>
public string ToUniversalString()
{
return UniversalTime.ToString("u");
}
/// <summary>
/// Returns a <see cref="DateTime"/> representing the current date and time adjusted to the system time zone.
/// </summary>
/// <remarks>
/// This is functionally equivalent to <see cref="DateTime.Now"/> and has been added for completeness.
/// </remarks>
public static DateTime Now() => TimeZoneInfo.ConvertTime(DateTime.UtcNow, TimeZoneInfo.Local);
/// <summary>
/// Returns a <see cref="DateTime"/> representing the current date and time adjusted to the specified time zone.
/// </summary>
/// <param name="timeZone">The time zone to apply.</param>
public static DateTime Now(TimeZoneInfo timeZone) => TimeZoneInfo.ConvertTime(DateTime.UtcNow, timeZone);
/// <summary>
/// Returns a <see cref="DateTime"/> representing the current date and time adjusted to the specified time zone.
/// </summary>
/// <param name="timeZone">The time zone to apply.</param>
public static DateTime Now(string timeZone)
{
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
return TimeZoneInfo.ConvertTime(DateTime.UtcNow, timeZoneInfo);
}
/// <inheritdoc/>
public override bool Equals(object? value)
{
return value is DateTimeZoned d2 && this == d2;
}
/// <inheritdoc/>
public bool Equals(DateTimeZoned value)
{
return this == value;
}
/// <summary>
/// Compares two <see cref="DateTimeZoned"/> values for equality.
/// </summary>
/// <param name="d1">The first value to compare.</param>
/// <param name="d2">The second value to compare.</param>
/// <returns>
/// Returns <see langword="true"/> if the two <see cref="DateTimeZoned"/> values are equal, or <see langword="false"/> if they are not equal.
/// </returns>
public static bool Equals(DateTimeZoned d1, DateTimeZoned d2)
{
return d1 == d2;
}
/// <summary>
/// Compares two <see cref="DateTimeZoned"/> values, returning an integer that indicates their relationship.
/// </summary>
/// <param name="d1">The first value to compare.</param>
/// <param name="d2">The second value to compare.</param>
/// <returns>
/// Returns 1 if the first value is greater than the second, -1 if the second value is greater than the first, or 0 if the two values are equal.
/// </returns>
public static int Compare(DateTimeZoned d1, DateTimeZoned d2)
{
var ticks1 = d1.UniversalTime.Ticks;
var ticks2 = d2.UniversalTime.Ticks;
if (ticks1 > ticks2)
return 1;
else if (ticks1 < ticks2)
return -1;
else
return 0;
}
/// <inheritdoc/>
public int CompareTo(object? value)
{
if (value == null)
return 1;
if (value is not DateTimeZoned)
throw new ArgumentException(null, nameof(value));
return Compare(this, (DateTimeZoned)value);
}
/// <inheritdoc/>
public int CompareTo(DateTimeZoned value)
{
return Compare(this, value);
}
/// <inheritdoc/>
public override int GetHashCode()
{
var ticks = UniversalTime.Ticks;
return unchecked((int)ticks) ^ (int)(ticks >> 32);
}
public static TimeSpan operator -(DateTimeZoned d1, DateTimeZoned d2) => new(d1.UniversalTime.Ticks - d2.UniversalTime.Ticks);
public static bool operator ==(DateTimeZoned d1, DateTimeZoned d2) => d1.UniversalTime.Ticks == d2.UniversalTime.Ticks;
public static bool operator !=(DateTimeZoned d1, DateTimeZoned d2) => d1.UniversalTime.Ticks != d2.UniversalTime.Ticks;
public static bool operator <(DateTimeZoned d1, DateTimeZoned d2) => d1.UniversalTime.Ticks < d2.UniversalTime.Ticks;
public static bool operator <=(DateTimeZoned d1, DateTimeZoned d2) => d1.UniversalTime.Ticks <= d2.UniversalTime.Ticks;
public static bool operator >(DateTimeZoned d1, DateTimeZoned d2) => d1.UniversalTime.Ticks > d2.UniversalTime.Ticks;
public static bool operator >=(DateTimeZoned d1, DateTimeZoned d2) => d1.UniversalTime.Ticks >= d2.UniversalTime.Ticks;
}
其他回答
您必须为此创建一个自定义对象。您的自定义对象将包含两个值:
DateTime值 一个时区对象
不确定是否已经有clr提供的数据类型具有此功能,但至少TimeZone组件已经可用。
对于具有特定时区(既不是本地时区,也不是UTC时区)偏移量的日期/时间,您可以使用DateTimeOffset类:
var time = TimeSpan.Parse("9:00");
var est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
var nationalDateTime = new DateTimeOffset(DateTime.Today.Ticks + time.Ticks, est.BaseUtcOffset);
TimeZoneInfo试试。ConvertTime(dateTime, sourceTimeZone, destinationTimeZone)
Jon的回答谈到了TimeZone,但我建议使用TimeZoneInfo代替。
就我个人而言,我喜欢尽可能将内容保存在UTC中(至少对于过去;为未来存储UTC有潜在的问题),所以我建议一个这样的结构:
public struct DateTimeWithZone
{
private readonly DateTime utcDateTime;
private readonly TimeZoneInfo timeZone;
public DateTimeWithZone(DateTime dateTime, TimeZoneInfo timeZone)
{
var dateTimeUnspec = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
utcDateTime = TimeZoneInfo.ConvertTimeToUtc(dateTimeUnspec, timeZone);
this.timeZone = timeZone;
}
public DateTime UniversalTime { get { return utcDateTime; } }
public TimeZoneInfo TimeZone { get { return timeZone; } }
public DateTime LocalTime
{
get
{
return TimeZoneInfo.ConvertTime(utcDateTime, timeZone);
}
}
}
您可能希望将“TimeZone”名称更改为“TimeZoneInfo”以使事情更清楚-我自己更喜欢简短的名称。
我扩展了乔恩·斯基特的回答,添加了一些额外的内容,让它感觉更接近日期时间。在很大程度上,这将简化比较、相等和转换。我发现datetimezone . now("")函数是特别有用的。
需要注意的一点是这个结构体是在。net 6中编写的。因此,如果您使用的是旧版本,您可能需要替换一些新语言功能的使用。
此外,操作符和接口的实现受到了GitHub上DateTime.cs的. net引用的启发。
/// <summary>
/// This value type represents a date and time with a specific time zone applied. If no time zone is provided, the local system time zone will be used.
/// </summary>
public readonly struct DateTimeZoned : IComparable, IComparable<DateTimeZoned>, IEquatable<DateTimeZoned>
{
/// <summary>
/// Creates a new zoned <see cref="DateTime"/> with the system time zone.
/// </summary>
/// <param name="dateTime">The local <see cref="DateTime"/> to apply a time zone to.</param>
public DateTimeZoned(DateTime dateTime)
{
var local = DateTime.SpecifyKind(dateTime, DateTimeKind.Local);
UniversalTime = TimeZoneInfo.ConvertTimeToUtc(local, TimeZoneInfo.Local);
TimeZone = TimeZoneInfo.Local;
}
/// <summary>
/// Creates a new zoned <see cref="DateTime"/> with the specified time zone.
/// </summary>
/// <param name="dateTime">The <see cref="DateTime"/> to apply a time zone to.</param>
/// <param name="timeZone">The time zone to apply.</param>
/// <remarks>
/// Assumes the provided <see cref="DateTime"/> is from the specified time zone.
/// </remarks>
public DateTimeZoned(DateTime dateTime, TimeZoneInfo timeZone)
{
var unspecified = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
UniversalTime = TimeZoneInfo.ConvertTimeToUtc(unspecified, timeZone);
TimeZone = timeZone;
}
/// <summary>
/// Creates a new zoned <see cref="DateTime"/> with the specified time zone.
/// </summary>
/// <param name="dateTime">The <see cref="DateTime"/> to apply a time zone to.</param>
/// <param name="timeZone">The time zone to apply.</param>
/// <remarks>
/// Assumes the provided <see cref="DateTime"/> is from the specified time zone.
/// </remarks>
public DateTimeZoned(DateTime dateTime, string timeZone)
{
var unspecified = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
UniversalTime = TimeZoneInfo.ConvertTimeToUtc(unspecified, timeZoneInfo);
TimeZone = timeZoneInfo;
}
/// <summary>
/// The UTC <see cref="DateTime"/> for the stored value.
/// </summary>
public DateTime UniversalTime { get; init; }
/// <summary>
/// The selected time zone.
/// </summary>
public TimeZoneInfo TimeZone { get; init; }
/// <summary>
/// The localized <see cref="DateTime"/> for the stored value.
/// </summary>
public DateTime LocalTime => TimeZoneInfo.ConvertTime(UniversalTime, TimeZone);
/// <summary>
/// Specifies whether UTC and localized values are the same.
/// </summary>
public bool IsUtc => UniversalTime == LocalTime;
/// <summary>
/// Returns a new <see cref="DateTimeZoned"/> with the current <see cref="LocalTime"/> converted to the target time zone.
/// </summary>
/// <param name="timeZone">The time zone to convert to.</param>
public DateTimeZoned ConvertTo(string timeZone)
{
var converted = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(LocalTime, TimeZone.Id, timeZone);
return new DateTimeZoned(converted, timeZone);
}
/// <summary>
/// Returns a new <see cref="DateTimeZoned"/> with the current <see cref="LocalTime"/> converted to the target time zone.
/// </summary>
/// <param name="timeZone">The time zone to convert to.</param>
public DateTimeZoned ConvertTo(TimeZoneInfo timeZone)
{
var converted = TimeZoneInfo.ConvertTime(LocalTime, TimeZone, timeZone);
return new DateTimeZoned(converted, timeZone.Id);
}
/// <summary>
/// Returns the value as a string in the round-trip date/time pattern.
/// </summary>
/// <remarks>
/// This applies the .ToString("o") option on <see cref="LocalTime"/>.
/// </remarks>
public string ToLocalString()
{
var local = new DateTimeOffset(LocalTime, TimeZone.BaseUtcOffset);
return local.ToString("o");
}
/// <summary>
/// Returns the value as a string in the universal sortable date/time pattern.
/// </summary>
/// <remarks>
/// This is applies the .ToString("u") option on <see cref="UniversalTime"/>.
/// </remarks>
public string ToUniversalString()
{
return UniversalTime.ToString("u");
}
/// <summary>
/// Returns a <see cref="DateTime"/> representing the current date and time adjusted to the system time zone.
/// </summary>
/// <remarks>
/// This is functionally equivalent to <see cref="DateTime.Now"/> and has been added for completeness.
/// </remarks>
public static DateTime Now() => TimeZoneInfo.ConvertTime(DateTime.UtcNow, TimeZoneInfo.Local);
/// <summary>
/// Returns a <see cref="DateTime"/> representing the current date and time adjusted to the specified time zone.
/// </summary>
/// <param name="timeZone">The time zone to apply.</param>
public static DateTime Now(TimeZoneInfo timeZone) => TimeZoneInfo.ConvertTime(DateTime.UtcNow, timeZone);
/// <summary>
/// Returns a <see cref="DateTime"/> representing the current date and time adjusted to the specified time zone.
/// </summary>
/// <param name="timeZone">The time zone to apply.</param>
public static DateTime Now(string timeZone)
{
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
return TimeZoneInfo.ConvertTime(DateTime.UtcNow, timeZoneInfo);
}
/// <inheritdoc/>
public override bool Equals(object? value)
{
return value is DateTimeZoned d2 && this == d2;
}
/// <inheritdoc/>
public bool Equals(DateTimeZoned value)
{
return this == value;
}
/// <summary>
/// Compares two <see cref="DateTimeZoned"/> values for equality.
/// </summary>
/// <param name="d1">The first value to compare.</param>
/// <param name="d2">The second value to compare.</param>
/// <returns>
/// Returns <see langword="true"/> if the two <see cref="DateTimeZoned"/> values are equal, or <see langword="false"/> if they are not equal.
/// </returns>
public static bool Equals(DateTimeZoned d1, DateTimeZoned d2)
{
return d1 == d2;
}
/// <summary>
/// Compares two <see cref="DateTimeZoned"/> values, returning an integer that indicates their relationship.
/// </summary>
/// <param name="d1">The first value to compare.</param>
/// <param name="d2">The second value to compare.</param>
/// <returns>
/// Returns 1 if the first value is greater than the second, -1 if the second value is greater than the first, or 0 if the two values are equal.
/// </returns>
public static int Compare(DateTimeZoned d1, DateTimeZoned d2)
{
var ticks1 = d1.UniversalTime.Ticks;
var ticks2 = d2.UniversalTime.Ticks;
if (ticks1 > ticks2)
return 1;
else if (ticks1 < ticks2)
return -1;
else
return 0;
}
/// <inheritdoc/>
public int CompareTo(object? value)
{
if (value == null)
return 1;
if (value is not DateTimeZoned)
throw new ArgumentException(null, nameof(value));
return Compare(this, (DateTimeZoned)value);
}
/// <inheritdoc/>
public int CompareTo(DateTimeZoned value)
{
return Compare(this, value);
}
/// <inheritdoc/>
public override int GetHashCode()
{
var ticks = UniversalTime.Ticks;
return unchecked((int)ticks) ^ (int)(ticks >> 32);
}
public static TimeSpan operator -(DateTimeZoned d1, DateTimeZoned d2) => new(d1.UniversalTime.Ticks - d2.UniversalTime.Ticks);
public static bool operator ==(DateTimeZoned d1, DateTimeZoned d2) => d1.UniversalTime.Ticks == d2.UniversalTime.Ticks;
public static bool operator !=(DateTimeZoned d1, DateTimeZoned d2) => d1.UniversalTime.Ticks != d2.UniversalTime.Ticks;
public static bool operator <(DateTimeZoned d1, DateTimeZoned d2) => d1.UniversalTime.Ticks < d2.UniversalTime.Ticks;
public static bool operator <=(DateTimeZoned d1, DateTimeZoned d2) => d1.UniversalTime.Ticks <= d2.UniversalTime.Ticks;
public static bool operator >(DateTimeZoned d1, DateTimeZoned d2) => d1.UniversalTime.Ticks > d2.UniversalTime.Ticks;
public static bool operator >=(DateTimeZoned d1, DateTimeZoned d2) => d1.UniversalTime.Ticks >= d2.UniversalTime.Ticks;
}