- 
                Notifications
    
You must be signed in to change notification settings  - Fork 3.6k
 
Description
Issue Summary
Inconsistent time zone handling for user inputs: Date pickers submitted date time values are interpreted as UTC instead of the current TZ by the backend.
Description
I’m quite confused about ABP’s current behavior regarding time zone handling in multi-time-zone scenarios.
Assume that the current authenticated user’s time zone is configured as “Asia/Shanghai”.
Based on the official documentation:
- https://abp.io/community/articles/developing-a-multitimezone-application-using-the-abp-framework-zk7fnrdq#gsc.tab=0
 - https://abp.io/docs/latest/framework/infrastructure/timing
 
My understanding is as follows:
- All UI components should display times according to the user’s time zone. This already works correctly in components such as 
abp-date-pickerand DataTables, where times are shown in the user’s local (Shanghai) time zone. So this part seems consistent. - All date time values submitted to the backend should also be converted to UTC in the frontend; and then, in the backend, according to 
AbpClockOptions.Kind, theIClock.Normalize()method converts the DateTime value to the final kind. 
The current problem is that the (MVC) frontend doesn't convert values of the date time pickers to UTC, but submits them directly. And, in the backend, the DateTime values (selected according to the user TZ) are interpreted as UTC. This leads to inconsistency between UI displayed (correct, user time zone) and submitted (incorrect, UTC).
Why this is a problem
A very typical example is the built-in the /Identity/SecurityLogs page:
- The table correctly displays dates in the user’s (Shanghai) time zone.
 - However, when filtering logs by a Start Time using the date picker, the search returns unexpected results.
 
Investigation shows that the filter value is submitted without time zone information (e.g. 2025-10-22 19:42)  and are interpreted as UTC on the server.
As a result, the UI and backend use different time bases for the same data, which breaks consistency and user expectations.
Expected Behavior
When the multi-TZ is enabled and the AbpTimeZoneMiddleware has set the current TZ (no matter if the value of current TZ comes from the browser or the user's setting):
- UTC date time values should be converted to the current TZ before display. (Currently correct)
 - Convert the date time values of the date pickers to UTC before submitting them to the backend services. (Currently incorrect, at least for the MVC UI)
 
consistent within the same time zone context.
Current Workaround
The community article suggests handling this on the client side using a custom JavaScript handleDatepicker function to convert to UTC before submission.
This approach works. However, it is not scalable or maintainable, since we cannot override every module or date input one by one. Also, for front-end developers, manually converting all the time is also an unnecessary burden, because there are clearly agreed current TZ, the converting work can be handled by the framework.
I agree that the payload of the HTTP request should contain ISO standard time values. But currently, the common practice is submitting them plainly (e.g. 2025-10-22 19:42). So I think if MVC's Modelbinder is also included in the scope of the frontend, then processing the date time format converting in .NET code is also possible.
Suggested Fix
I am currently only thinking about MVC.
For the MVC UI, we can redo the AbpDateTimeModelBinder from
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
    await _dateTimeModelBinder.BindModelAsync(bindingContext);
    if (bindingContext.Result.IsModelSet && bindingContext.Result.Model is DateTime dateTime)
    {
        bindingContext.Result = ModelBindingResult.Success(_clock.Normalize(dateTime));
    }
}to
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
    await _dateTimeModelBinder.BindModelAsync(bindingContext);
    if (!bindingContext.Result.IsModelSet || bindingContext.Result.Model is not DateTime dateTime)
    {
        return;
    }
    // If the DateTime has no timezone info (most cases from input)
    if (dateTime.Kind == DateTimeKind.Unspecified)
    {
        // Try to get user's timezone
        var userTz = _currentTimezoneProvider.TimeZone;
        if (!userTz.IsNullOrWhiteSpace())
        {
            try
            {
                var tzInfo = _timezoneProvider.GetTimeZoneInfo(userTz);
                // Treat the input as user's local time and convert to UTC
                var utc = TimeZoneInfo.ConvertTimeToUtc(dateTime, tzInfo);
                bindingContext.Result = ModelBindingResult.Success(utc);
                return;
            }
            catch
            {
                // fallback to default clock normalization if invalid TZ
            }
        }
    }
    // fallback: original behavior
    bindingContext.Result = ModelBindingResult.Success(_clock.Normalize(dateTime));
}This way, the backend will receive UTC date time values that are converted from the specific TZ.