Hi,
We have an tiered modular application, abp version 9.2.0, database postgresql. Not sure if it is related but mentioning, we migrated from mysql previously and upgrade from abp 8.3.0. We’re investigating a timezone/filtering problem with ABP audit/security logs and would like to confirm the correct approach.
Audit log and security log date filters behave differently between local and cloud hosted environments. The __timezone header is present on requests, but filtering still appears incorrect and does not return the results relevant to the timezone, even with the ClockOptions.Kind set to DateTimeKind.Utc and the app.UseAbpTimeZone() middleware. We also have EnableLegacyTimestampBehavior set to true in the DbContext, but i suspect that this part of the problem as the EF module snapshots still define the timestamp columns as timestamps with time zone.
Supporting information: Incoming request log (from our host probe): Audit timezone probe Path=/api/audit-logging/audit-logs __timezone=Africa/Johannesburg startTime=2026-07-02T14:02:00.000 endTime= DB sample audit rows are stored as UTC/timestamptz, e.g. 2026-07-02 11:40:59.564458+00. Code pointers in our repo (current state): AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true) exists in: EntityFrameworkCoreModule.cs Migrations/snapshots show columns created as timestamp with time zone across modules (e.g. src/EntityFrameworkCore/Migrations/* and module snapshots).
modelBuilder.Entity("Volo.Abp.Identity.IdentitySecurityLog", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("CreationTime")
.HasColumnType("timestamp with time zone");
});
Questions:
Is my understanding correct that with Npgsql.EnableLegacyTimestampBehavior = true the runtime may interpret ambiguous/unspecified DateTime values using host-local timezone, causing behavior differences between local and UTC-hosted environments? Is that the intended/known effect of the legacy switch?
If we set Npgsql.EnableLegacyTimestampBehavior to false (or remove the AppContext.SetSwitch), will that cause incompatibility with existing data or require database migrations or data fixes? Any known upgrade pitfalls? Why is model snapshot still defining the timestamp related columns as timestamp with time zone if I have set Npgsql.EnableLegacyTimestampBehavior = true?
For an ABP v10 project, what is the recommended setting for Npgsql.EnableLegacyTimestampBehavior when using timestamp with time zone (timestamptz) for audit/security timestamps? Why do some ABP templates/solutions still set the switch to true by default — is that for backward compatibility? Are there official migration steps documented by ABP when switching off legacy behavior?
Do you recommend (a) keeping timestamptz and disabling legacy behavior or (b) switching to timestamp without time zone and handling conversion application-side? Template: app Version 9.2.0
Tiered: Yes
UI Framework: angular
Theme: leptonx
Theme Style: system
Progressive Web App: Yes
Database Provider: ef
Database Management System: postgresql
Separate Tenant Schema: Yes
Mobile Framework: none
Public Website: No
Optional Modules:
LanguageManagement
AuditLogging
SaaS
OpenIddictAdmin
2 Answer(s)
-
0
ABP-side (timezone header / filtering): In ABP v9.2+, multi-timezone behavior is based on enabling UTC normalization (
AbpClockOptions.Kind = DateTimeKind.Utc) and providing the timezone info (via the__timezoneheader/cookie/etc.) so ABP can convert between user time and UTC when needed.UseAbpTimeZone()is the middleware that reads__timezoneand determines the effective timezone (settings first, then request, then server fallback). This part is independent from Npgsql’s legacy timestamp switch.Npgsql-side (
Npgsql.EnableLegacyTimestampBehavior): That switch controls how the Npgsql provider maps and interprets PostgreSQL timestamp types to .NETDateTime(especially aroundtimestampvstimestamptz, andDateTimeKind.Unspecified/Local/Utc). With legacy behavior enabled, it can indeed lead to environment-dependent behavior (e.g., a value materialized/parameterized as “unspecified” getting interpreted relative to the server/container local timezone), which is exactly the kind of difference you see between dev machines and UTC-configured cloud hosts. So your understanding of the risk is directionally correct.Why your EF snapshots still show
timestamp with time zone: EF Core’s model snapshot column type (timestamp with time zone) is about the database schema type mapping for the property and provider; it is not controlled byEnableLegacyTimestampBehavior. That switch changes runtime conversion behavior, not what EF thinks the underlying PostgreSQL column type is. So it’s expected that your snapshot still saystimestamp with time zone.If you remove the switch, do you need migrations / data fixes?
Generally, changingNpgsql.EnableLegacyTimestampBehaviordoes not require schema migrations because it doesn’t change column types—your columns staytimestamptz. However, it can surface previously-hidden bugs where your application was:- sending
DateTimevalues withKind=Unspecified(orLocal) as query parameters for filtering, and - relying on legacy provider behavior to “do something” with them.
In that case, after disabling legacy behavior, you may need application-level fixes (ensuring filter inputs are converted to UTC correctly before querying, or using
DateTimeOffset).- sending
Recommended approach for ABP v10+ with PostgreSQL audit/security timestamps:
For ABP multi-timezone setups, the safest/most consistent approach is:- Keep storage in UTC (ABP recommends
AbpClockOptions.Kind = DateTimeKind.Utcfor multi-timezone normalization). - Keep PostgreSQL columns as
timestamp with time zone(timestamptz) for audit/security logs (storing an absolute instant). - Prefer using
DateTimeOffsetfor new domain properties where you control the schema and want fewer ambiguities (ABP also recommendsDateTimeOffsetfor storing time because it includes timezone/offset information). - Disable legacy timestamp behavior unless you have a known dependency on it (typically it exists only for backward compatibility with older Npgsql behavior).
- Keep storage in UTC (ABP recommends
(a) timestamptz + disable legacy vs (b) switch to timestamp without time zone:
(a) Keeptimestamptzand disable legacy behavior is the recommended direction for audit/security timestamps because:- audit/security times are “instants” and should not drift with server timezone,
timestamptzis designed to represent an absolute moment, and- ABP’s multi-timezone feature expects you to store/transport in UTC and convert for display/filtering at the boundaries.
Switching to
timestamp without time zonetends to reintroduce ambiguity (it becomes “a local date-time with no zone”), which is exactly what causes cross-environment inconsistencies.Practical ABP fix for filtering issues (what to check/change):
- Ensure all incoming filter datetimes are normalized to UTC before querying audit/security logs (don’t rely on implicit provider conversions).
- Ensure your UI/API is sending ISO8601 with
Zwhen it means UTC (e.g.,2026-07-02T12:02:00.000Z). If the UI sends2026-07-02T14:02:00.000without offset, it will be treated as ambiguous. - Keep:
Configure<AbpClockOptions>(options => { options.Kind = DateTimeKind.Utc; }); app.UseAbpTimeZone();- Remove
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true)and then regression-test audit/security log filtering in both environments.
No verified source available for ABP-specific “official migration steps” about toggling
Npgsql.EnableLegacyTimestampBehavior(this switch is owned by Npgsql/.NET data provider behavior rather than ABP), but ABP’s official multi-timezone mechanism and expectations (UTC normalization +__timezonehandling viaUseAbpTimeZone) are documented here:- https://abp.io/docs/latest/framework/infrastructure/timing
- https://abp.io/community/articles/zk7fnrdq
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
Hi,
Let me split this into the switch itself and the actual filtering behavior, because they're separate.
The Npgsql switch
Npgsql.EnableLegacyTimestampBehavioronly changes how Npgsql interprets aDateTimeat runtime — it doesn't touch the column type. So your snapshot keepingtimestamp with time zoneis expected and correct; that column type is Npgsql's default EF Core mapping forDateTime, not something the switch controls. The template sets it totruepurely for backward compatibility with the pre-Npgsql-6 behavior, so older code passing non-UTCDateTimevalues doesn't start throwing after the provider upgrade.Turning it off does not require a schema migration or data fix — the columns stay
timestamptzand your existing UTC data is untouched. It's a runtime/code change: with it off, anyDateTimethat isn'tKind=Utcthrows immediately when written to atimestamptzcolumn (... only UTC is supported) instead of being silently converted.Why the filter doesn't match the timezone (on 9.2)
One thing to be aware of on 9.2: the
__timezoneheader andUseAbpTimeZone()don't participate in binding thestartTime/endTimefilters at all — the model binder just normalizes the incoming value toKind=Utcwithout any timezone conversion. So a query string value without an offset, like2026-07-02T14:02:00, is treated as14:02Z, not as14:02Johannesburg (12:02Z). Only a value that carries an explicit offset orZis honored — e.g.2026-07-02T14:02:00+02:00is correctly read as12:02Z.So if the UI sends the picked local time without an offset, the query ends up off by your UTC offset. On 9.2 the way to make it match is to send the filter bounds already converted to UTC (with
Z), or with an explicit offset. This is the same on any host, so it isn't tied to the Npgsql switch.What changes on v10
This is relevant to your v10 question. ABP 10.2+ changed the
DateTimemodel binding so that a value with no offset is now converted from the current request timezone (the__timezone/UseAbpTimeZonevalue) to UTC before querying. So on 10.2+ the bare local time from the client is interpreted using__timezoneas you'd expect, whereas on 9.2 you have to send UTC explicitly. If moving the timezone handling out of your client code is worth it, that's a reason to upgrade. The template still keepsEnableLegacyTimestampBehavior = truein v10, and there are no ABP-specific migration steps for toggling it, since the switch is owned by Npgsql.The local vs cloud difference
I can't say for sure what's behind this part yet without seeing the actual request. What I can say is that the standard filter binding is host-independent — the bound value is the same on a UTC+2 box and a UTC box — so a genuine difference between environments would most likely come from a
DateTimereaching the query that isn'tKind=Utc. The usual source is custom or reporting code that builds filters withDateTime.Now/DateTime.Today/.ToLocalTime()instead ofIClock: those areKind=Local, and with the legacy switchtruethey get converted using the host machine's timezone, which would explain different boundaries per host. That's a direction to check, not a conclusion.To narrow it down, it would help to capture the same filter in both environments and compare:
- the raw audit-logs request (the
startTime/endTimequery values) from local and from cloud, and - the number of rows each returns.
If the two requests carry different values, it's the client computing UTC differently; if the requests are identical but the results differ, it's a server-side path passing a non-UTC value. In that second case, temporarily setting the switch to
falsein a staging environment is a fast way to surface it — a non-UTC value then throws right at the query and the stack trace points at the code that built it (staging only; in production it would make every non-UTC write fail). A snippet of any custom code that queries the logs directly would also help.On (a) vs (b)
Keep audit/security timestamps as
timestamptzand store UTC. Don't switch totimestamp without time zone— that just reintroduces ambiguity; these logs are absolute instants, which is exactly whattimestamptzrepresents.Thanks
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) - the raw audit-logs request (the