UTC 日期的 C# 服务器端管理

C# Server-side management of dates in UTC

我正在开发 .Net Core 3.1 REST Api。

我对接近午夜的日期和时区有疑问。
我以 UTC 格式将所有数据保存在数据库中,前端调用 API 以 UTC 格式传递时间。

我目前在GTM+2时区。

我必须将用户执行的 'actions' 分成不同的日子。

输出示例:

    {
        days: [
            {
                day: '2021-10-28',
                actions: [
                    {
                        id: 123456,
                        actionType: 1,
                        startDate: '2021-10-28T12:04:12Z',
                        endDate: '2021-10-28T13:04:39Z'
                    },
                    {
                        ...
                    }
                ],
                actualHours: 1,
                expectedHours: 4
            },
            {
                day: '2021-10-29',
                actions: [
                    {
                        id: 123467,
                        actionType: 1,
                        startDate: '2021-10-29T12:00:45Z',
                        endDate: '2021-10-29T14:00:40Z'
                    },
                    {
                        ...
                    }
                ],
                actualHours: 2,
                expectedHours: 4
            }
        ]
    }

当我们的时间接近午夜时会出现问题,因为在数据库中,这一天将保存为用户输入的前一天。

添加一整天操作的用户示例。

前端:

Json 请求:

    {
        actionType: 1,
        startDate: '2021-10-27T22:00:00.000Z',
        endDate: '2021-10-28T21:59:59.999Z'
    }

在数据库中,日期将保存为:

为了采取行动并将它们按天分开我写了这个代码片段..

    public async Task<IList<DailyActionItem>> GetAsync(string userId, DateTime startDate, DateTime? endDate)
    {
        // Other code here..
        
        // Get actions by dates
        var tActions = await _actionsQuery.GetPeriodAsync(userId, startDate, endDate).ConfigureAwait(false);

        // Create a list of days
        var days = new List<DailyActionItem>();

        // Cycling the range of dates and also adding the missing days
        var ed = endDate.HasValue ? endDate.Value.Date : DateTime.UtcNow.Date;
        for (var sd = startDate.Date; sd <= ed; sd = sd.AddDays(1))
        {
            var daily = new DailyActionItem
            {
                Day = sd.Date,
                Actions = new List<ActionItem>()
            };

            // Other code here..

            var actions = tActions
                    .Where(t => (t.StartDate <= sd.AddDays(1).AddTicks(-1) && t.StartDate >= sd) ||
                                (t.StartDate <= sd.AddDays(1).AddTicks(-1) && t.EndDate.HasValue && t.EndDate >= sd))
                    .OrderBy(c => c.StartDate);

            // Add actions nella risposta e calcolo i valori attuali
            if (actions?.Any() == true)
            {
                foreach (var action in actions)
                {
                    daily.Actions.Add(new ActionItem
                    {
                        Id = action.Id,
                        ActionType = action.ActionType,
                        StartDate = action.StartDate,
                        EndDate = action.EndDate,
                        Comment = action.Comment
                    });
                }

                // Other code here..
            }
            
            // Important: calculations made on the day
            daily.ActualHours = tActions.Sum(...);
            daily.ExpectedHours = contract.ExpectedHours;

            // Other code here..

            days.Add(daily);

        }

        return days;
    }

调试示例(单日 - 2021-10-28 从 00:00 本地到 23:59 本地):

在foreach中:
第一轮 -> sd = 2021-10-27T00:00:00.000Z 最后一轮 -> sd = 2021-10-28T00:00:00.000Z

输出:

    {
        days: [
            {
                day: '2021-10-27',
                actions: [
                    {
                        id: 123987,
                        actionType: 1,
                        startDate: '2021-10-27T22:00:00.000Z',
                        endDate: '2021-10-28T21:59:59.999Z'
                    }
                ],
                actualHours: 24, // full day, wrong place!
                expectedHours: 4
            },
            {
                day: '2021-10-28',
                actions: [
                    {
                        ...
                    },
                    {
                        ...
                    }
                ]
            }
        ]
    }

由于 APIs 在 UTC 中工作,此代码根据 UTC 中的值拆分日期。
例如,输入“2021-10-28T00:00:00.000 LOCAL”的最后一天将置于“2021-10-27 UTC”这一天之下。

我该如何解决这个问题?我错过了什么?
管理这些案例的最佳方法是什么?

理论上结果是正确的。 但是前端读取数据不正确

看起来像一个奇怪的错误。我建议在任何地方将 DateTime 替换为 DateTimeOffset,进行数据库迁移并更新数据库。

如果上述方法不起作用,请告诉我。