{"id":7603,"date":"2026-09-21T15:44:58","date_gmt":"2026-09-21T22:44:58","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/azure-sql\/?p=7603"},"modified":"2026-09-24T06:42:14","modified_gmt":"2026-09-24T13:42:14","slug":"pg-to-sql-date-time","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/azure-sql\/pg-to-sql-date-time\/","title":{"rendered":"PostgreSQL to SQL Field Notes: Date &#038; Time"},"content":{"rendered":"<h2 id=\"welcome-to-sql\" class=\"code-line\" dir=\"auto\" data-line=\"0\">Welcome to SQL<\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"2\"><strong>First, welcome to SQL!<\/strong> You&#8217;re going to like this.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"2\">Upgrading from PostgreSQL is real work, and depending on your old PostgreSQL, that process can vary widely in complexity. You\u2019re going to love the mindful and modern features of SQL. Take your time, make the most of the resources built to help you in Microsoft Learn, and know there is a huge community of SQL developers ready to help along the way.<\/p>\n<p dir=\"auto\" data-line=\"2\"><a href=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2026\/09\/pg-to-sql-datetime.webp\"><img decoding=\"async\" class=\"alignnone size-full wp-image-7614\" src=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2026\/09\/pg-to-sql-datetime.webp\" alt=\"pg to sql datetime image\" width=\"1774\" height=\"887\" srcset=\"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2026\/09\/pg-to-sql-datetime.webp 1774w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2026\/09\/pg-to-sql-datetime-300x150.webp 300w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2026\/09\/pg-to-sql-datetime-1024x512.webp 1024w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2026\/09\/pg-to-sql-datetime-768x384.webp 768w, https:\/\/devblogs.microsoft.com\/azure-sql\/wp-content\/uploads\/sites\/56\/2026\/09\/pg-to-sql-datetime-1536x768.webp 1536w\" sizes=\"(max-width: 1774px) 100vw, 1774px\" \/><\/a><\/p>\n<h3 dir=\"auto\" data-line=\"4\"><\/h3>\n<h3 id=\"this-blog-is-for-the-pg-developer\" class=\"code-line\" dir=\"auto\" data-line=\"4\">This blog is for the PG developer.<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"6\">The SQL standard has helped align the shape of the two engines, and that familiarity will help you get up to speed quickly. However, there are differences, and there aren\u2019t always equivalents. To help fast-track your learning, this series will highlight some differences worth knowing. Although some may surprise you, I believe most will be a delight.<\/p>\n<p dir=\"auto\" data-line=\"6\"><div class=\"alert alert-primary\">SQL, in this article and elsewhere in Microsoft documentation, means T-SQL (Transact-SQL), Microsoft\u2019s variant of the SQL standard shared across all Microsoft SQL database engines, in the same way that PostgreSQL has its own SQL dialect. In the Microsoft SQL world, T-SQL is referred to simply as SQL.<\/div><\/p>\n<h2 id=\"handling-date-and-time\" class=\"code-line\" dir=\"auto\" data-line=\"10\">Handling date and time<\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"12\">Let\u2019s start with an everyday feature: handling date and time. In PostgreSQL, you have the\u00a0<code>timestamp<\/code>\u00a0and\u00a0<code>timestamptz<\/code>\u00a0types.<\/p>\n<h3 id=\"postgresql-timestamp\" class=\"code-line\" dir=\"auto\" data-line=\"12\">PostgreSQL\u00a0<code>timestamp<\/code><\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"14\">In PG, you use the\u00a0<code>timestamp<\/code> type to store date and time values without any time zone information.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"14\"><div class=\"alert alert-primary\">PostgreSQL has had a type named <code>timestamp<\/code> since well before PostgreSQL 7.0, but its meaning evolved. PostgreSQL 7.0 replaced the older <code>datetime<\/code> type with <code>timestamp<\/code>, and PostgreSQL 7.2 introduced the modern distinction between <code>timestamp without time zone<\/code> and <code>timestamp with time zone<\/code>. Today, a bare <code>timestamp<\/code> means <code>timestamp without time zone<\/code>.<\/div><\/p>\n<h4 id=\"using-timestamp\" class=\"code-line\" dir=\"auto\" data-line=\"18\">Using\u00a0<code>timestamp<\/code><\/h4>\n<pre><code class=\"code-line language-sql\" dir=\"auto\" data-line=\"20\"><span class=\"hljs-keyword\">SELECT<\/span> <span class=\"hljs-type\">TIMESTAMP<\/span> <span class=\"hljs-string\">'2026-09-21 10:30:00'<\/span>;\r\n<\/code><\/pre>\n<p class=\"code-line\" dir=\"auto\" data-line=\"24\">The value of\u00a0<code>timestamp<\/code>\u00a0in this case is stored exactly as provided. No time zone information is provided, and none is stored.<\/p>\n<pre><code class=\"code-line language-sql\" dir=\"auto\" data-line=\"26\"><span class=\"hljs-keyword\">SELECT<\/span> <span class=\"hljs-type\">TIMESTAMP<\/span> <span class=\"hljs-string\">'2026-09-21 10:30:00-06:00'<\/span>;\r\n<\/code><\/pre>\n<p class=\"code-line\" dir=\"auto\" data-line=\"30\">If you include a time zone offset,\u00a0<code>timestamp<\/code>\u00a0simply ignores it. Even though you provided the\u00a0<code>-06:00<\/code>\u00a0offset from UTC, that information is discarded. This behavior remains documented in PostgreSQL today.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"32\">If your application needed to preserve the original time zone or offset, you had to store that information separately.<\/p>\n<pre><code class=\"code-line language-sql\" dir=\"auto\" data-line=\"34\"><span class=\"hljs-keyword\">CREATE<\/span> <span class=\"hljs-keyword\">TABLE<\/span> events (\r\n    event_id SERIAL <span class=\"hljs-keyword\">PRIMARY<\/span> KEY,\r\n    event_name TEXT <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-keyword\">NULL<\/span>,\r\n    event_timestamp <span class=\"hljs-type\">TIMESTAMP<\/span> <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-keyword\">NULL<\/span>,\r\n    event_timezone TEXT <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-keyword\">NULL<\/span>\r\n);\r\n<\/code><\/pre>\n<p class=\"code-line\" dir=\"auto\" data-line=\"43\">This works, but now two values describe one event. Your application is responsible for keeping them together and interpreting them correctly.<\/p>\n<h4 id=\"sqls-closest-equivalent-datetime2\" class=\"code-line\" dir=\"auto\" data-line=\"45\">SQL\u2019s closest equivalent:\u00a0<code>datetime2<\/code><\/h4>\n<p class=\"code-line\" dir=\"auto\" data-line=\"47\">SQL\u2019s closest equivalent to PostgreSQL\u00a0<code>timestamp<\/code>\u00a0is\u00a0<code>datetime2<\/code>. SQL\u2019s\u00a0<code>datetime2<\/code>\u00a0is the modern replacement for Microsoft SQL\u2019s legacy\u00a0<code>datetime<\/code>\u00a0type. Microsoft recommends\u00a0<code>datetime2<\/code>\u00a0for new development.<\/p>\n<p dir=\"auto\" data-line=\"47\"><div class=\"alert alert-info\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Info\"><\/i><strong>Incredible precision<\/strong><\/p><code>datetime2<\/code> supports variable fractional-second precision from 0 to 7 digits and defaults to datetime2(7). At the default, it can represent time in 100 ns increments. PostgreSQL timestamp supports 6 fractional-second digits, or 1 \u03bcs increments. This gives Microsoft SQL 10\u00d7 finer representational precision over PostgreSQL.<\/div><\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"51\">If your use case demands higher precision for scientific instruments, streaming data, or financial applications, SQL\u2019s\u00a0<code>datetime2<\/code>\u00a0means you no longer need to compromise on precision.<\/p>\n<h3 id=\"postgresql-timestamptz\" class=\"code-line\" dir=\"auto\" data-line=\"53\">PostgreSQL\u00a0<code>timestamptz<\/code><\/h3>\n<p>PostgreSQL 7.2 established the modern distinction between <code>timestamp without time zone<\/code> and <code>timestamp with time zone<\/code>, commonly written today using PostgreSQL&#8217;s <code>timestamptz<\/code> shorthand.<\/p>\n<pre><code class=\"code-line language-sql\" dir=\"auto\" data-line=\"57\"><span class=\"hljs-keyword\">SELECT<\/span> TIMESTAMPTZ <span class=\"hljs-string\">'2026-09-21 10:30:00-06:00'<\/span>;\r\n<\/code><\/pre>\n<p class=\"code-line\" dir=\"auto\" data-line=\"61\">This time, the offset matters. PostgreSQL uses it to determine the actual instant in time and stores that instant internally in UTC.<\/p>\n<h4>Unfortunately, you already know the caveat<\/h4>\n<p>PostgreSQL does not retain the original time zone or offset. When the value is retrieved, PostgreSQL converts it to the session&#8217;s current <code>TimeZone<\/code>.<\/p>\n<p><div class=\"alert alert-danger\">So PostgreSQL cannot answer, \u201cWhat offset did the application originally supply?\u201d And the session <code data-start=\"406\" data-end=\"416\">TimeZone<\/code> does not inherently know the user&#8217;s or application&#8217;s local time zone. The application must set the session <code data-start=\"524\" data-end=\"534\">TimeZone<\/code> appropriately or perform the conversion itself.<\/div><\/p>\n<p id=\"unfortunately-you-already-know-the-caveat\" class=\"code-line\" dir=\"auto\" data-line=\"63\">So the same stored value can look different depending on who asks for it:<\/p>\n<pre><code class=\"code-line language-sql\" dir=\"auto\" data-line=\"69\"><span class=\"hljs-keyword\">SET<\/span> <span class=\"hljs-type\">TIME<\/span> ZONE <span class=\"hljs-string\">'America\/Denver'<\/span>;\r\n\r\n<span class=\"hljs-keyword\">SELECT<\/span> TIMESTAMPTZ <span class=\"hljs-string\">'2026-09-21 10:30:00-06:00'<\/span>;\r\n<span class=\"hljs-comment\">-- 2026-09-21 10:30:00-06<\/span>\r\n\r\n<span class=\"hljs-keyword\">SET<\/span> <span class=\"hljs-type\">TIME<\/span> ZONE <span class=\"hljs-string\">'America\/New_York'<\/span>;\r\n\r\n<span class=\"hljs-keyword\">SELECT<\/span> TIMESTAMPTZ <span class=\"hljs-string\">'2026-09-21 10:30:00-06:00'<\/span>;\r\n<span class=\"hljs-comment\">-- 2026-09-21 12:30:00-04<\/span><\/code><\/pre>\n<p class=\"code-line\" dir=\"auto\" data-line=\"81\">Both results represent exactly the same instant. That\u2019s intentional PostgreSQL behavior. But if your application needs to know that the original value was\u00a0<code>10:30 -06:00<\/code>,\u00a0<code>timestamptz<\/code>\u00a0cannot tell you. That information is lost during the insert.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"83\"><strong>It can be frustrating.<\/strong> Application developers who need the original offset are left to handle it manually. The offset has to be stored separately rather than embedded in the value itself. The value of\u00a0<code>timestamptz<\/code>\u00a0is that PostgreSQL normalizes timestamps to a common instant and automatically converts them to the session\u2019s configured time zone when retrieved.<\/p>\n<h2 id=\"good-news-you-are-using-sql-now\" class=\"code-line\" dir=\"auto\" data-line=\"85\">Good news! You&#8217;re using SQL now.<\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"87\">Introducing SQL\u2019s\u00a0<code>datetimeoffset<\/code>\u00a0type. When time zone information is important,\u00a0<code>datetimeoffset<\/code>\u00a0allows you to store both the local date and time as well as the offset from UTC, preserving the original context of the timestamp.<\/p>\n<pre><code class=\"code-line language-sql\" dir=\"auto\" data-line=\"89\"><span class=\"hljs-keyword\">DECLARE<\/span> <span class=\"hljs-variable\">@dt<\/span> DATETIMEOFFSET <span class=\"hljs-operator\">=<\/span> <span class=\"hljs-string\">'2026-09-21 10:30:00-06:00'<\/span>;<\/code><\/pre>\n<p class=\"code-line\" dir=\"auto\" data-line=\"93\">This advanced type has been around since SQL Server 2008. Ubiquitous in modern Microsoft SQL applications, it provides a flexible way to handle time zone information.<\/p>\n<h3 id=\"heres-how-it-works\" class=\"code-line\" dir=\"auto\" data-line=\"95\">Here\u2019s how it works<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"97\">The\u00a0<code>datetimeoffset<\/code>\u00a0type stores both the local date and time as well as the offset from UTC. This means that when you insert a value like\u00a0<code>'2026-09-21 10:30:00-06:00'<\/code>, Microsoft SQL retains the\u00a0<code>-06:00<\/code>\u00a0offset along with the date and time, preserving the original context of the timestamp.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"99\">Do you want to show it as UTC? You have two easy options:<\/p>\n<pre><code class=\"code-line language-sql\" dir=\"auto\" data-line=\"101\"><span class=\"hljs-keyword\">SELECT<\/span> <span class=\"hljs-variable\">@dt<\/span> <span class=\"hljs-keyword\">AT<\/span> <span class=\"hljs-type\">TIME<\/span> ZONE <span class=\"hljs-string\">'UTC'<\/span>;\r\n\r\n<span class=\"hljs-keyword\">SELECT<\/span> SWITCHOFFSET(<span class=\"hljs-variable\">@dt<\/span>, <span class=\"hljs-string\">'+00:00'<\/span>);<\/code><\/pre>\n<p dir=\"auto\" data-line=\"109\"><div class=\"alert alert-success\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Lightbulb\"><\/i><strong>Both return the same instant at UTC.<\/strong><\/p>Both return the same instant at UTC.\u00a0<code>AT TIME ZONE<\/code>\u00a0is useful when working with named time zones and their conversion rules.\u00a0<code>SWITCHOFFSET<\/code>\u00a0is simpler when you already have a\u00a0<code>datetimeoffset<\/code>\u00a0and just want to change its displayed offset to UTC.<\/div><\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"109\">By the way, that same ultra-high precision you enjoy with SQL\u2019s\u00a0<code>datetime2<\/code>\u00a0is available with\u00a0<code>datetimeoffset<\/code>, but with the added benefit of time zone awareness.<\/p>\n<pre><code class=\"code-line language-sql\" dir=\"auto\" data-line=\"111\"><span class=\"hljs-comment\">-- Example showing high precision with datetimeoffset<\/span>\r\n\r\n<span class=\"hljs-keyword\">DECLARE<\/span> <span class=\"hljs-variable\">@dtHighPrecision<\/span> DATETIMEOFFSET(<span class=\"hljs-number\">7<\/span>) <span class=\"hljs-operator\">=<\/span>\r\n    <span class=\"hljs-string\">'2026-09-21 10:30:00.1234567-06:00'<\/span>;\r\n\r\n<span class=\"hljs-keyword\">SELECT<\/span> <span class=\"hljs-variable\">@dtHighPrecision<\/span>;\r\n<\/code><\/pre>\n<h3 id=\"storage-differences\" class=\"code-line\" dir=\"auto\" data-line=\"113\">Storage differences<\/h3>\n<p class=\"code-line\" dir=\"auto\" data-line=\"122\"><code>datetimeoffset<\/code>\u00a0requires 8 to 10 bytes of storage, depending on fractional-second precision. This is slightly more than\u00a0<code>datetime2<\/code>, which requires 6 to 8 bytes. The additional 2 bytes store the time zone offset.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"124\">Consider the impact of this additional storage across a billion rows. Attending to storage doesn\u2019t just save disk space; it can also reduce I\/O, memory pressure, and index size.<\/p>\n<table class=\"code-line\" dir=\"auto\" data-line=\"126\">\n<thead class=\"code-line\" dir=\"auto\" data-line=\"126\">\n<tr class=\"code-line\" dir=\"auto\" data-line=\"126\">\n<th>Rows<\/th>\n<th><code>datetimeoffset<\/code><\/th>\n<th><code>datetime2<\/code><\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"code-line\" dir=\"auto\" data-line=\"128\">\n<tr class=\"code-line\" dir=\"auto\" data-line=\"128\">\n<td>1 B<\/td>\n<td>8\u201310 GB<\/td>\n<td>6\u20138 GB<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"code-line\" dir=\"auto\" data-line=\"130\">That\u2019s a 2 GB difference in storage for a billion rows. Since dates are so common in many applications, this difference can have a significant impact on overall storage requirements and performance.<\/p>\n<h4 id=\"your-default-type-for-dates\" class=\"code-line\" dir=\"auto\" data-line=\"125\">Your default type for dates<\/h4>\n<p class=\"code-line\" dir=\"auto\" data-line=\"134\">Because of this storage difference, your default should not always be\u00a0<code>datetimeoffset<\/code>, especially when your app doesn\u2019t care about the time zone. In many cases,\u00a0<code>datetime2<\/code>\u00a0remains an excellent choice.<\/p>\n<p class=\"code-line\" dir=\"auto\" data-line=\"136\">Having said that, since precision is variable, you can also balance storage requirements with the need for time zone awareness by carefully choosing the appropriate precision for your application.<\/p>\n<table class=\"code-line\" dir=\"auto\" data-line=\"138\">\n<thead class=\"code-line\" dir=\"auto\" data-line=\"138\">\n<tr class=\"code-line\" dir=\"auto\" data-line=\"138\">\n<th>Rows<\/th>\n<th><code>datetimeoffset(0)<\/code><\/th>\n<th><code>datetimeoffset(7)<\/code><\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"code-line\" dir=\"auto\" data-line=\"140\">\n<tr class=\"code-line\" dir=\"auto\" data-line=\"140\">\n<td>1 B<\/td>\n<td>8 GB<\/td>\n<td>10 GB<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"code-line\" dir=\"auto\" data-line=\"142\">That\u2019s a 2 GB difference in storage for a billion rows when choosing between the lowest and highest precision for\u00a0<code>datetimeoffset<\/code>. What\u2019s more, you still have time zone support when you need it.<\/p>\n<h2 id=\"congratulations\" class=\"code-line\" dir=\"auto\" data-line=\"137\">Congratulations<\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"146\"><strong>Let me welcome you again to SQL!<\/strong> I hope it\u2019s clear that you\u2019ve joined a rich and thoughtful ecosystem. Choosing the right date and time type for your application is important, and SQL helps make that easier, simpler, and better.<\/p>\n<p class=\"code-line code-active-line\" dir=\"auto\" data-line=\"148\">Happy querying!<\/p>\n<p dir=\"auto\" data-line=\"148\"><div  class=\"d-flex justify-content-left\"><a class=\"cta_button_link btn-primary mb-24\" href=\"https:\/\/azure.microsoft.com\/en-us\/products\/azure-sql\/database\/?msockid=37cc3330cefc64fa18a126b6cf51658e\" target=\"_blank\">Learn more about Azure SQL database<\/a><\/div><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Moving from PostgreSQL to SQL Server? Learn the key differences between timestamp, timestamptz, datetime2, and datetimeoffset, including precision, time zones, offsets, and storage.<\/p>\n","protected":false},"author":96788,"featured_media":7617,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1,672,619],"tags":[733,751,750],"class_list":["post-7603","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure-sql","category-sql-server-2025","category-t-sql","tag-microsoft-sql","tag-modernize","tag-postgresql"],"acf":[],"blog_post_summary":"<p>Moving from PostgreSQL to SQL Server? Learn the key differences between timestamp, timestamptz, datetime2, and datetimeoffset, including precision, time zones, offsets, and storage.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/posts\/7603","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/users\/96788"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/comments?post=7603"}],"version-history":[{"count":2,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/posts\/7603\/revisions"}],"predecessor-version":[{"id":7631,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/posts\/7603\/revisions\/7631"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/media\/7617"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/media?parent=7603"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/categories?post=7603"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sql\/wp-json\/wp\/v2\/tags?post=7603"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}