Working with dates and times in SQL Server can get tricky, especially once you add time zones into the mix. One common point of confusion is the difference between the server’s time zone and the session’s time zone. If you’re not careful, you can end up with inconsistent data or timestamps that don’t line up with what your users expect.
Let’s look at how SQL Server treats time zones and how you can handle server versus session time zone differences cleanly.
Server Time Zone vs Session Time Zone
First, SQL Server doesn’t have a built-in concept of “session time zones” like Oracle or PostgreSQL. Instead:
- The server time zone is determined by the operating system that SQL Server runs on. If the OS is set to UTC+2, then
GETDATE()will return local time in UTC+2. - The session (your connection) doesn’t automatically carry a time zone setting. However, you can control how you interpret or display times within a session by using functions like
AT TIME ZONEor by always working in UTC and converting when needed.
This means that two queries run on the same server at the same time will return the same timestamp with GETDATE(), regardless of which user or session runs them. But if you want user-specific time zones, you’ll need to explicitly convert.
Why This Matters
Imagine your SQL Server is hosted in New York (Eastern Time), but your users are in London (GMT). If you insert rows with GETDATE(), those times will be stamped in Eastern Time. A London-based user will see times that are five hours off. Without a clear strategy for dealing with such scenarios, your reporting, scheduling, or auditing could quickly get very messy.
Functions That Help
SQL Server has several built-in functions to make working with time zones manageable, including:
GETUTCDATE()– Returns the current UTC datetime (datetimetype). Use this when you want to store everything in UTC.SYSDATETIMEOFFSET()– Returns the current datetime including the server’s offset.AT TIME ZONE– Converts a date/time value to another time zone using Windows time zone identifiers.
Here’s an example that demonstrates some of these:
-- Current UTC datetime (use this when storing values)
SELECT GETUTCDATE() AS UtcNow;
-- Current server datetime with offset info
SELECT SYSDATETIMEOFFSET() AS ServerDateWithOffset;
-- Convert UTC to a user’s local time zone (e.g., Pacific)
DECLARE @UserTimeZone NVARCHAR(50) = 'Pacific Standard Time';
SELECT
GETUTCDATE() AT TIME ZONE 'UTC'
AT TIME ZONE @UserTimeZone AS PacificTime;
A Practical Approach
The safest approach in most systems is:
- Store in UTC – Insert all timestamps in UTC (using
GETUTCDATE()orSYSUTCDATETIME()). This makes the data consistent regardless of where your server is located. - Convert on output – When presenting data back to users, convert the UTC value to their preferred time zone using
AT TIME ZONE. - Track user time zones – If you have users across multiple zones, store their preferred time zone in a profile table and apply conversion dynamically.
Example
Here’s a quick example to demonstrate how it can be done:
-- Create the Orders table (storing all times in UTC)
CREATE TABLE dbo.Orders
(
OrderID INT IDENTITY(1,1) PRIMARY KEY,
CustomerName NVARCHAR(20) NOT NULL,
Product NVARCHAR(15) NOT NULL,
Quantity INT NOT NULL,
OrderDateUtc DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME()
);
GO
-- Insert sample data in UTC
INSERT INTO dbo.Orders (CustomerName, Product, Quantity, OrderDateUtc)
VALUES
('Shiza Johnson', 'Laptop', 1, SYSUTCDATETIME()),
('Shelly Smith', 'Headphones', 2, DATEADD(HOUR, -5, SYSUTCDATETIME())),
('Grant Green', 'Keyboard', 1, DATEADD(DAY, -1, SYSUTCDATETIME())),
('Ella Lopez', 'Ergonomic Chair', 1, DATEADD(DAY, -2, SYSUTCDATETIME())),
('Olly Brown', '4K Monitor', 2, DATEADD(DAY, -7, SYSUTCDATETIME()));
GO
-- Convert on output for a specific user time zone
DECLARE @UserTimeZone NVARCHAR(50) = 'GMT Standard Time';
SELECT
OrderID,
CustomerName,
Product,
Quantity,
OrderDateUtc AS StoredUtc,
OrderDateUtc AT TIME ZONE 'UTC'
AT TIME ZONE @UserTimeZone AS LocalOrderTime
FROM dbo.Orders;
Output:
OrderID CustomerName Product Quantity StoredUtc LocalOrderTime
----------- -------------------- --------------- ----------- -------------------------------------- ---------------------------------------------
1 Shiza Johnson Laptop 1 2025-08-26 23:56:01.2835046 2025-08-27 00:56:01.2835046 +01:00
2 Shelly Smith Headphones 2 2025-08-26 18:56:01.2835046 2025-08-26 19:56:01.2835046 +01:00
3 Grant Green Keyboard 1 2025-08-25 23:56:01.2835046 2025-08-26 00:56:01.2835046 +01:00
4 Ella Lopez Ergonomic Chair 1 2025-08-24 23:56:01.2835046 2025-08-25 00:56:01.2835046 +01:00
5 Olly Brown 4K Monitor 2 2025-08-19 23:56:01.2835046 2025-08-20 00:56:01.2835046 +01:00
By handling it this way, your data stays consistent, and you avoid problems when the server changes daylight saving rules or if you migrate SQL Server to a host in a different region.
Edge Cases to Watch
- Daylight Saving Time (DST): Using
AT TIME ZONEis safer than manually adding offsets because it automatically accounts for DST changes. - Old SQL Server versions: If you’re on SQL Server 2014 or earlier, you’ll need to handle offsets manually (storing UTC is even more important here).
- Applications with multiple time zones: Don’t rely on the server’s clock. Always normalize to UTC and convert for display.
Final Thoughts
SQL Server doesn’t give each session its own time zone setting, so the difference between “server” and “session” time zones really comes down to how you handle conversions. If you stick to storing in UTC and only convert on output, you’ll avoid most headaches. The AT TIME ZONE function is your friend – it bridges the gap between server-local timestamps and user-specific expectations without requiring messy math.