I was doing some work recently where I needed to group some data from SQL Server by year, month, week, etc.

The simplified version was something like this:

SELECT	
	COUNT(x),	
    YEAR(CreatedUtc)
FROM	
	dbo.[Order]
GROUP BY
	YEAR(CreatedUtc)

The CreatedUtc data type is datetimeoffset storing zulu datetimes.

Even though I knew I was only looking at data from 2020 I was still seeing 2 rows - one for 2019 and one for 2020.

I tested my date filtering (omitted for brevity in the sample above) and everything was fine. All dates was within the 2020 UTC bounds.

So after digging a bit more I found the offending row resulting in the 2019 group. The date was: ‘2019-12-31 23:56:43.0052080 +00:00’

Thinking of it, it of course makes perfect sense that the built-in YEAR function is not aware of timezones. YEAR(‘2019-12-31 23:56:43.0052080 +00:00’) will (naturally) return 2019.

We need to switch the offset in the datetimeoffset to the timezone required for the task. In my use-case of a building a report that means switching to CEST/Central European Standard Time.

The YEAR snippet now instead becomes:

YEAR(SWITCHOFFSET(CreatedUtc, DATEPART(TZOFFSET, CreatedUtc AT TIME ZONE 'Central Europe Standard Time')))

The full (still simplified) sample becomes:

`
SELECT	
	COUNT(x),	
    YEAR(SWITCHOFFSET(CreatedUtc, DATEPART(TZOFFSET, CreatedUtc AT TIME ZONE 'Central Europe Standard Time')))
FROM
	dbo.[Order]
GROUP BY
	YEAR(SWITCHOFFSET(CreatedUtc, DATEPART(TZOFFSET, CreatedUtc AT TIME ZONE 'Central Europe Standard Time')))

Notice: I haven’t yet done any benchmarking on the above approach and it might make sense to extract this to a function to clean things up a bit.

Also see the official SWITCHOFFSET documentation.

https://docs.microsoft.com/en-us/sql/t-sql/functions/switchoffset-transact-sql?view=sql-server-ver15

If you have another better approach please do let me know :-)