【问题标题】:DateTime with timezone in ASP MVC Web API + EFDateTime 与 ASP MVC Web API + EF 中的时区
【发布时间】:2015-04-26 10:46:14
【问题描述】:

我正在使用 ASP MVC Web API + EF 并且我的客户正在获取 DateTime 而没有有关时区的信息。我试图在 WebApiConfig 中设置设置但没有成功:

config.Formatters.JsonFormatter.SerializerSettings.DateTimeZoneHandling 
= DateTimeZoneHandling.Local;

唯一对我有用的方法是:使用 DateTimeKind.Local 创建 DateTime 的新实例:

    public IEnumerable<ClientDto> Execute()
    {
        var clients = this.DbContext.Clients.Select(
            m => new ClientDto
        {
            Id = m.Id,
            NotificationSendingTime = m.NotificationSendingTime,
            . . .
        }).ToList();

        clients.ForEach(m => m.NotificationSendingTime = 
            m.NotificationSendingTime.HasValue 
            ? new DateTime(m.NotificationSendingTime.Value.Ticks, DateTimeKind.Local) 
            : m.NotificationSendingTime);

        return clients;
    }

但在这种情况下,我必须使用 .ToList() 并为每个项目设置新的 DateTime 和时区。

如何设置 WebApi 或 EF 以自动添加有关时区的信息?谢谢。

更新

看来我找到了解决办法。 我的html:

<!-- Timepicker -->
<input id="notificationSendingTime"
       name="notificationSendingTime"
       type="text"
       class="form-control"
       data-ng-model="notificationSendingTime"
       bs-timepicker
       data-time-format="HH:mm"
       data-time-type="date"
       data-length="1" data-minute-step="30"
       data-arrow-behavior="picker" />

<!-- Timezone -->
<button type="button" class="btn btn-default full-width time-zone" ng-model="formData.TimeZoneId"
        data-html="1" data-animation="" placeholder="Time Zone..."
        ng-options="timeZone.Id as timeZone.FriendlyName for timeZone in timeZones" bs-select>
    Action <span class="caret"></span>
</button>

我的客户地址:

public class ClientDto
{
    public int Id { get; set; }
    public DateTime? NotificationSendingTime { get; set; }

    public DateTimeOffset? NotificationSendingTimeOffset
    {
        get
        {
            if (!this.NotificationSendingTime.HasValue)
            {
                return null;
            }

            var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(string.IsNullOrWhiteSpace(this.TimeZoneId) ? "Greenwich Standard Time" : this.TimeZoneId);
            var offset = TimeZoneInfo.ConvertTimeFromUtc(this.NotificationSendingTime.Value, timeZoneInfo);
            return offset;
        }
    }
    public string TimeZoneId { get; set; }
}

在服务器端更新客户端:

if (command.CommandArg.NotificationSendingTime.HasValue)
{
    var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(string.IsNullOrWhiteSpace(command.CommandArg.TimeZoneId) ? "Greenwich Standard Time" : command.CommandArg.TimeZoneId);
    var utc = TimeZoneInfo.ConvertTimeToUtc(command.CommandArg.NotificationSendingTime.Value, timeZoneInfo);
    command.CommandArg.NotificationSendingTime = utc.Date + new TimeSpan(utc.Hour, 0, 0);
}

client.NotificationSendingTime = command.CommandArg.NotificationSendingTime;
client.TimeZoneId = command.CommandArg.TimeZoneId;

this.DbContext.SaveChanges();

获取数据后在我的 angularJS 控制器中:

$scope.notificationSendingTime = $scope.formData.NotificationSendingTimeOffset;

在提交数据之前在我的 angularJS 控制器中:

    $scope.formData.NotificationSendingTime 
= $filter('date')($scope.notificationSendingTime, 'HH:mm');

时间选择器适用于所有浏览器!谢谢。

【问题讨论】:

  • 如果您有任何不这样做的原因:NotificantionSendingTime = m.NotificationSendingTime.ToLocalTime(),即使用the DateTime.ToLocalTime method 转换您的Select
  • @Alex,当我在选择中使用 .ToLocalTime() 时出现以下异常:LINQ to Entities 无法识别方法 'System.DateTime ToLocalTime()' 方法,并且无法翻译此方法到商店表达式中。
  • 哦,是的,当然,你是对的,它会成为查询的一部分。
  • 您在 DB 中使用什么数据类型来存储日期字段?在您的 JSON 中,您是否希望日期格式为 2015-04-27T00:05:00+03:00
  • 在我的数据库表中,我有 TimeZoneId 列,其中时区以“FLE 标准时间”、“夏威夷标准时间”等格式存储。这个时区信息应该添加到我的 DateTime 中。据我了解,客户端是执行此操作的最佳位置。

标签: c# entity-framework datetime asp.net-web-api timezone


【解决方案1】:

如果您将时区信息存储在单独的列中,我认为让 EF 进行转换并不容易。我建议将此功能放在模型本身中。对于这个简单的示例,DTO 看起来是一个不错的选择:

[DataContract]
public class Client
{
    [DataMember]
    public DateTime Created { get; set; }

    [DataMember]
    public string CreatedTimeZoneId { get; set; }
}

public class ClientDto
{
    private readonly Client _client;

    public ClientDto(Client client)
    {
        _client = client;
    }

    public DateTimeOffset Created
    {
        get
        {
            //TODO: Should be moved in separate helper method.
            var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(_client.CreatedTimeZoneId);
            return new DateTimeOffset(_client.Created, timeZoneInfo.BaseUtcOffset);
        }
    }
}

通过使用DateTimeOffset 类型,您可以摆脱对时区处理的担忧。 JSON.NET 可以很好地处理这种类型:

// Simulate DB call
var clients = new List<Client>
{
    new Client
    {
        Created = new DateTime(2015, 4, 27, 11, 48, 22, DateTimeKind.Unspecified),
        CreatedTimeZoneId = "FLE Standard Time"
    }
};

var clientDtos = clients.Select(client => new ClientDto(client));
var json = JsonConvert.SerializeObject(clientDtos);

生成的 JSON 将是:

[{"Created":"2015-04-27T11:48:22+02:00"}]

【讨论】:

  • 感谢您的回复,好主意,但我得到“本地 dateTime 参数的 UTC 偏移量与偏移量参数不匹配。”当我以这种方式创建 DateTimeOffset 的新实例时出现异常。
  • 你能写一个抛出异常的日期和时区的例子吗?
猜你喜欢
  • 1970-01-01
  • 2017-07-20
  • 1970-01-01
  • 1970-01-01
  • 2014-10-22
  • 2013-05-07
  • 2014-11-13
  • 2013-02-27
  • 2015-07-23
相关资源
最近更新 更多