单元测试 NodaTime 别名 DateTimeZone

Unit Testing NodaTime Aliased DateTimeZone

我正在为我的扩展方法进行单元测试。

public static DateTimeZone GetDateTimeZone(this IDateTimeZoneProvider dateTimeZoneProvider, String timezoneId)
{
    DateTimeZone dateTimeZone;
    if ((dateTimeZone = dateTimeZoneProvider.GetZoneOrNull(timezoneId)) != null)
        return dateTimeZone;    

    // Continues to try and map from TimeZoneInfo if dateTimeZone == null
}

对该方法进行单元测试时,测试失败,因为正在为 DateTimeZone Id 返回别名。

[TestMethod]
public void GetDateTimeZone_DateTimeZoneProviderHasTimezoneIdAsAlias_ReturnsDateTimeZone()
{
    var expectedTimezoneId = "America/New_York";
    var timezoneId = "US/Eastern";
    var dateTimeZone = _dateTimeZoneProvider.GetDateTimeZone(timezoneId); 

    Assert.AreEqual(expectedTimezoneId, dateTimeZone.Id);
}

如您所见,我期待区域 ID 列 (https://nodatime.org/TimeZones) 中的内容,但返回的是别名 "US/Eastern"。 这尤其令人惊讶,因为有关 GetZoneOrNull (https://nodatime.org/2.4.x/api/NodaTime.IDateTimeZoneProvider.html#NodaTime_IDateTimeZoneProvider_GetZoneOrNull_System_String_) 的文档 说 "Note that this may return a DateTimeZone that has a different ID to that requested, if the ID provided is an alias."

看完后,"Note also that this method is not required to return the same DateTimeZone instance for successive requests for the same ID; however, all instances returned for a given ID must compare as equal.", 我想,也许带别名的 DateTimeZone 等同于未带别名的。

[TestMethod]
public void GetDateTimeZone_DateTimeZoneProviderHasTimezoneIdAsAlias_ReturnsDateTimeZone()
{
    var expectedDateTimeZone = _dateTimeZoneProvider.GetZoneOrNull("America/New_York");
    var timezoneId = "US/Eastern";
    var dateTimeZone = _dateTimeZoneProvider.GetDateTimeZone(timezoneId);

    Assert.AreEqual(expectedDateTimeZone, dateTimeZone);
}

但是,事实并非如此。调用扩展方法的代码并不真正关心 Id,因为它使用 DateTimeZone 从 LocalTime 转换为 Utc。

IDateTimeZoneProvider dateTimeZoneProvider = DateTimeZoneProviders.Tzdb;
var dateTimeZone = dateTimeZoneProvider.GetDateTimeZone(timezoneId);

var zonedDateTime = dateTimeZone.AtLeniently(localDateTime);
return zonedDateTime.ToDateTimeUtc(); 

因此,它将采用 localDateTime 时的当前偏移量。但是,出于单元测试的目的,我不能像 "America/New_York" 示例中那样只检查偏移量,因为它有时是 -5,有时是 -4。在单元测试中断言它是 -5 或 -4 并不符合标准。我应该如何测试别名的 DateTimeZone?

更新

这是使用已知偏移量的特定日期和时间的更新单元测试。

[TestMethod]
public void GetDateTimeZone_DateTimeZoneProviderHasTimezoneIdAsAlias_ReturnsDateTimeZone()
{
    var expectedOffset = Offset.FromHours(-5);
    var specificDateTimeWithKnownUtcOffset = new DateTime(2020, 1, 15, 8, 29, 15, DateTimeKind.Utc);
    var instant = Instant.FromDateTimeUtc(specificDateTimeWithKnownUtcOffset);
    var timezoneId = "US/Eastern";
    var dateTimeZone = _dateTimeZoneProvider.GetDateTimeZone(timezoneId);

    Assert.AreEqual(expectedOffset, dateTimeZone.GetUtcOffset(instant));
}

看过代码后,我们可能想稍微修改一下文档。 TzdbDateTimeZoneSource 的当前行为是 return 具有请求 ID 的 DateTimeZone,但它使用与规范 ID 关联的数据。

我认为它实际上并没有违反记录在案的行为 - 声明传递别名 可能 为您提供一个具有规范 ID 的区域 - 但它充其量是最好的误导。

我可能会将测试编写为不依赖于任何一种行为,或者依赖于当前行为——这在未来的版本中不太可能改变。 (保留传递的 ID 感觉更好。)我需要研究是否存在我们实际上想要 return 具有规范 ID 的区域的情况,但我 怀疑 我们可以收紧 3.0 的文档。

如果你想编写 可以 处理两者的测试,你可以只检查 "the canonical ID of the returned ID is the same as the canonical ID of the passed ID" - 但老实说,这似乎有点过分了。

请注意,UTC-4 或 UTC-5 的偏移量不是任意的 - 您可以轻松编写使用特定时区 ID 和本地 date/time 的测试,并期望基于那。