Skip to content

Commit 1e3a03e

Browse files
add support for timestamp with timezone data type in postgres
1 parent ba7ef22 commit 1e3a03e

File tree

18 files changed

+213
-143
lines changed

18 files changed

+213
-143
lines changed

Drivers/NpgsqlDriver.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ public NpgsqlDriver(Options options, Dictionary<string, Table> tables) : base(op
6060
new Dictionary<string, DbTypeInfo>
6161
{
6262
{ "date", new DbTypeInfo(NpgsqlTypeOverride: "NpgsqlDbType.Date") },
63-
{ "timestamp", new DbTypeInfo(NpgsqlTypeOverride: "NpgsqlDbType.Timestamp") }
63+
{ "timestamp", new DbTypeInfo(NpgsqlTypeOverride: "NpgsqlDbType.Timestamp") },
64+
{ "timestamptz", new DbTypeInfo(NpgsqlTypeOverride: "NpgsqlDbType.TimestampTz") },
6465
}, ordinal => $"reader.GetDateTime({ordinal})"),
6566
new("object",
6667
new Dictionary<string, DbTypeInfo>

docs/04_Postgres.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ we consider support for the different data types separately for batch inserts an
2929
| double precision |||
3030
| date |||
3131
| timestamp, timestamp without time zone |||
32-
| timestamp with time zone | | |
32+
| timestamp with time zone | | |
3333
| time, time without time zone |||
3434
| time with time zone |||
3535
| interval |||

end2end/EndToEndScaffold/Program.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ public partial class {{testClassName}}
5151

5252
private static bool RecordsAreInUse(string testClassName, bool isLegacyDotnet)
5353
{
54-
return !isLegacyDotnet && !testClassName.Contains("Dapper");
54+
if (isLegacyDotnet)
55+
return false;
56+
return !testClassName.Contains("Dapper");
5557
}
5658

5759
private static string GetTestImplementation(string testClassName, bool isLegacyDotnet, KnownTestType testType)

end2end/EndToEndScaffold/Templates.cs

+34-23
Original file line numberDiff line numberDiff line change
@@ -483,9 +483,9 @@ public async Task TestMultipleArrays()
483483
{
484484
Impl = $$"""
485485
[Test]
486-
[TestCase(true, 35, -23423, 4235235263, 3.83f, 4.5534, 998.432, -8403284.321435, 42332.53, "2000-1-30", "1983-11-3 02:01:22", "E", "It takes a nation of millions to hold us back", "Rebel Without a Pause", "Prophets of Rage", new byte[] { 0x45, 0x42 }, new string[] { "Party", "Fight" }, new int[] { 543, -4234 })]
487-
[TestCase(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new byte[] { }, new string[] { }, new int[] { })]
488-
[TestCase(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)]
486+
[TestCase(true, 35, -23423, 4235235263, 3.83f, 4.5534, 998.432, -8403284.321435, 42332.53, "2000-1-30", "1983-11-3 02:01:22", "2022-10-2 15:44:01+09:00", "E", "It takes a nation of millions to hold us back", "Rebel Without a Pause", "Prophets of Rage", new byte[] { 0x45, 0x42 }, new string[] { "Party", "Fight" }, new int[] { 543, -4234 })]
487+
[TestCase(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new byte[] { }, new string[] { }, new int[] { })]
488+
[TestCase(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)]
489489
public async Task TestPostgresTypes(
490490
bool cBoolean,
491491
short cSmallint,
@@ -498,9 +498,10 @@ public async Task TestPostgresTypes(
498498
decimal cMoney,
499499
DateTime cDate,
500500
DateTime cTimestamp,
501-
string cChar,
502-
string cVarchar,
503-
string cCharacterVarying,
501+
DateTime cTimestampWithTz,
502+
string cChar,
503+
string cVarchar,
504+
string cCharacterVarying,
504505
string cText,
505506
byte[] cBytea,
506507
string[] cTextArray,
@@ -519,6 +520,7 @@ await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs
519520
CMoney = cMoney,
520521
CDate = cDate,
521522
CTimestamp = cTimestamp,
523+
CTimestampWithTz = cTimestampWithTz,
522524
CChar = cChar,
523525
CVarchar = cVarchar,
524526
CCharacterVarying = cCharacterVarying,
@@ -541,6 +543,7 @@ await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs
541543
CMoney = cMoney,
542544
CDate = cDate,
543545
CTimestamp = cTimestamp,
546+
CTimestampWithTz = cTimestampWithTz,
544547
CChar = cChar,
545548
CVarchar = cVarchar,
546549
CCharacterVarying = cCharacterVarying,
@@ -566,6 +569,7 @@ private static void AssertSingularEquals(QuerySql.GetPostgresTypesRow expected,
566569
Assert.That(actual.CMoney, Is.EqualTo(expected.CMoney));
567570
Assert.That(actual.CDate, Is.EqualTo(expected.CDate));
568571
Assert.That(actual.CTimestamp, Is.EqualTo(expected.CTimestamp));
572+
Assert.That(actual.CTimestampWithTz, Is.EqualTo(expected.CTimestampWithTz));
569573
Assert.That(actual.CChar, Is.EqualTo(expected.CChar));
570574
Assert.That(actual.CVarchar, Is.EqualTo(expected.CVarchar));
571575
Assert.That(actual.CCharacterVarying, Is.EqualTo(expected.CCharacterVarying));
@@ -580,29 +584,33 @@ private static void AssertSingularEquals(QuerySql.GetPostgresTypesRow expected,
580584
{
581585
Impl = $$"""
582586
[Test]
583-
[TestCase(100, true, 3, 453, -1445214231, 666.6f, 336.3431, -99.999, -1377.996, -43242.43, "1973-12-3", "1960-11-3 02:01:22", "z", "Sex Pistols", "Anarchy in the U.K", "Never Mind the Bollocks...", new byte[] { 0x53, 0x56 })]
584-
[TestCase(500, false, -4, 867, 8768769709, -64.8f, -324.8671, 127.4793, 423.9869, 32143.99, "2024-12-31", "1999-3-1 03:00:10", "1", "Fugazi", "Waiting Room", "13 Songs", new byte[] { 0x03 })]
585-
[TestCase(10, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new byte[] { })]
586-
[TestCase(10, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)]
587+
[TestCase(100, true, 3, 453, -1445214231, 666.6f, 336.3431, -99.999, -1377.996, -43242.43, "1973-12-3", "1960-11-3 02:01:22", "2030-07-20 15:44:01+09:00", "z", "Sex Pistols", "Anarchy in the U.K", "Never Mind the Bollocks...", new byte[] { 0x53, 0x56 })]
588+
[TestCase(500, false, -4, 867, 8768769709, -64.8f, -324.8671, 127.4793, 423.9869, 32143.99, "2024-12-31", "1999-3-1 03:00:10", "1999-9-13 08:30:11-04:00", "1", "Fugazi", "Waiting Room", "13 Songs", new byte[] { 0x03 })]
589+
[TestCase(10, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new byte[] { })]
590+
[TestCase(10, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)]
587591
public async Task TestCopyFrom(
588592
int batchSize,
589-
bool? cBoolean,
590-
short? cSmallint,
591-
int? cInteger,
592-
long? cBigint,
593-
float? cReal,
594-
decimal? cDecimal,
595-
decimal? cNumeric,
596-
double? cDoublePrecision,
597-
decimal? cMoney,
593+
bool cBoolean,
594+
short cSmallint,
595+
int cInteger,
596+
long cBigint,
597+
float cReal,
598+
decimal cDecimal,
599+
decimal cNumeric,
600+
double cDoublePrecision,
601+
decimal cMoney,
598602
DateTime? cDate,
599603
DateTime? cTimestamp,
600-
string{{UnknownNullableIndicatorPlaceholder}} cChar,
601-
string{{UnknownNullableIndicatorPlaceholder}} cVarchar,
602-
string{{UnknownNullableIndicatorPlaceholder}} cCharacterVarying,
603-
string{{UnknownNullableIndicatorPlaceholder}} cText,
604+
DateTime? cTimestampWithTz,
605+
string cChar,
606+
string cVarchar,
607+
string cCharacterVarying,
608+
string cText,
604609
byte[] cBytea)
605610
{
611+
DateTime? cTimestampWithTzAsUtc = null;
612+
if (cTimestampWithTz != null)
613+
cTimestampWithTzAsUtc = DateTime.SpecifyKind(cTimestampWithTz.Value, DateTimeKind.Utc);
606614
var batchArgs = Enumerable.Range(0, batchSize)
607615
.Select(_ => new QuerySql.InsertPostgresTypesBatchArgs
608616
{
@@ -617,6 +625,7 @@ public async Task TestCopyFrom(
617625
CMoney = cMoney,
618626
CDate = cDate,
619627
CTimestamp = cTimestamp,
628+
CTimestampWithTz = cTimestampWithTzAsUtc,
620629
CChar = cChar,
621630
CVarchar = cVarchar,
622631
CCharacterVarying = cCharacterVarying,
@@ -639,6 +648,7 @@ public async Task TestCopyFrom(
639648
CMoney = cMoney,
640649
CDate = cDate,
641650
CTimestamp = cTimestamp,
651+
CTimestampWithTz = cTimestampWithTz,
642652
CChar = cChar,
643653
CVarchar = cVarchar,
644654
CCharacterVarying = cCharacterVarying,
@@ -663,6 +673,7 @@ private static void AssertSingularEquals(QuerySql.GetPostgresTypesAggRow expecte
663673
Assert.That(actual.CMoney, Is.EqualTo(expected.CMoney));
664674
Assert.That(actual.CDate, Is.EqualTo(expected.CDate));
665675
Assert.That(actual.CTimestamp, Is.EqualTo(expected.CTimestamp));
676+
Assert.That(actual.CTimestampWithTz, Is.EqualTo(expected.CTimestampWithTz));
666677
Assert.That(actual.CChar, Is.EqualTo(expected.CChar));
667678
Assert.That(actual.CVarchar, Is.EqualTo(expected.CVarchar));
668679
Assert.That(actual.CCharacterVarying, Is.EqualTo(expected.CCharacterVarying));

end2end/EndToEndTests/NpgsqlDapperTester.generated.cs

+18-11
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,16 @@ private static bool SingularEquals(QuerySql.GetAuthorByIdRow x, QuerySql.GetAuth
125125
}
126126

127127
[Test]
128-
[TestCase(100, true, 3, 453, -1445214231, 666.6f, 336.3431, -99.999, -1377.996, -43242.43, "1973-12-3", "1960-11-3 02:01:22", "z", "Sex Pistols", "Anarchy in the U.K", "Never Mind the Bollocks...", new byte[] { 0x53, 0x56 })]
129-
[TestCase(500, false, -4, 867, 8768769709, -64.8f, -324.8671, 127.4793, 423.9869, 32143.99, "2024-12-31", "1999-3-1 03:00:10", "1", "Fugazi", "Waiting Room", "13 Songs", new byte[] { 0x03 })]
130-
[TestCase(10, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new byte[] { })]
131-
[TestCase(10, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)]
132-
public async Task TestCopyFrom(int batchSize, bool? cBoolean, short? cSmallint, int? cInteger, long? cBigint, float? cReal, decimal? cDecimal, decimal? cNumeric, double? cDoublePrecision, decimal? cMoney, DateTime? cDate, DateTime? cTimestamp, string? cChar, string? cVarchar, string? cCharacterVarying, string? cText, byte[] cBytea)
128+
[TestCase(100, true, 3, 453, -1445214231, 666.6f, 336.3431, -99.999, -1377.996, -43242.43, "1973-12-3", "1960-11-3 02:01:22", "2030-07-20 15:44:01+09:00", "z", "Sex Pistols", "Anarchy in the U.K", "Never Mind the Bollocks...", new byte[] { 0x53, 0x56 })]
129+
[TestCase(500, false, -4, 867, 8768769709, -64.8f, -324.8671, 127.4793, 423.9869, 32143.99, "2024-12-31", "1999-3-1 03:00:10", "1999-9-13 08:30:11-04:00", "1", "Fugazi", "Waiting Room", "13 Songs", new byte[] { 0x03 })]
130+
[TestCase(10, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new byte[] { })]
131+
[TestCase(10, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)]
132+
public async Task TestCopyFrom(int batchSize, bool cBoolean, short cSmallint, int cInteger, long cBigint, float cReal, decimal cDecimal, decimal cNumeric, double cDoublePrecision, decimal cMoney, DateTime? cDate, DateTime? cTimestamp, DateTime? cTimestampWithTz, string cChar, string cVarchar, string cCharacterVarying, string cText, byte[] cBytea)
133133
{
134-
var batchArgs = Enumerable.Range(0, batchSize).Select(_ => new QuerySql.InsertPostgresTypesBatchArgs { CBoolean = cBoolean, CSmallint = cSmallint, CInteger = cInteger, CBigint = cBigint, CReal = cReal, CDecimal = cDecimal, CNumeric = cNumeric, CDoublePrecision = cDoublePrecision, CMoney = cMoney, CDate = cDate, CTimestamp = cTimestamp, CChar = cChar, CVarchar = cVarchar, CCharacterVarying = cCharacterVarying, CText = cText, CBytea = cBytea }).ToList();
134+
DateTime? cTimestampWithTzAsUtc = null;
135+
if (cTimestampWithTz != null)
136+
cTimestampWithTzAsUtc = DateTime.SpecifyKind(cTimestampWithTz.Value, DateTimeKind.Utc);
137+
var batchArgs = Enumerable.Range(0, batchSize).Select(_ => new QuerySql.InsertPostgresTypesBatchArgs { CBoolean = cBoolean, CSmallint = cSmallint, CInteger = cInteger, CBigint = cBigint, CReal = cReal, CDecimal = cDecimal, CNumeric = cNumeric, CDoublePrecision = cDoublePrecision, CMoney = cMoney, CDate = cDate, CTimestamp = cTimestamp, CTimestampWithTz = cTimestampWithTzAsUtc, CChar = cChar, CVarchar = cVarchar, CCharacterVarying = cCharacterVarying, CText = cText, CBytea = cBytea }).ToList();
135138
await QuerySql.InsertPostgresTypesBatch(batchArgs);
136139
var expected = new QuerySql.GetPostgresTypesAggRow
137140
{
@@ -147,6 +150,7 @@ public async Task TestCopyFrom(int batchSize, bool? cBoolean, short? cSmallint,
147150
CMoney = cMoney,
148151
CDate = cDate,
149152
CTimestamp = cTimestamp,
153+
CTimestampWithTz = cTimestampWithTz,
150154
CChar = cChar,
151155
CVarchar = cVarchar,
152156
CCharacterVarying = cCharacterVarying,
@@ -171,6 +175,7 @@ private static void AssertSingularEquals(QuerySql.GetPostgresTypesAggRow expecte
171175
Assert.That(actual.CMoney, Is.EqualTo(expected.CMoney));
172176
Assert.That(actual.CDate, Is.EqualTo(expected.CDate));
173177
Assert.That(actual.CTimestamp, Is.EqualTo(expected.CTimestamp));
178+
Assert.That(actual.CTimestampWithTz, Is.EqualTo(expected.CTimestampWithTz));
174179
Assert.That(actual.CChar, Is.EqualTo(expected.CChar));
175180
Assert.That(actual.CVarchar, Is.EqualTo(expected.CVarchar));
176181
Assert.That(actual.CCharacterVarying, Is.EqualTo(expected.CCharacterVarying));
@@ -361,12 +366,12 @@ public async Task TestMultipleArrays()
361366
}
362367

363368
[Test]
364-
[TestCase(true, 35, -23423, 4235235263, 3.83f, 4.5534, 998.432, -8403284.321435, 42332.53, "2000-1-30", "1983-11-3 02:01:22", "E", "It takes a nation of millions to hold us back", "Rebel Without a Pause", "Prophets of Rage", new byte[] { 0x45, 0x42 }, new string[] { "Party", "Fight" }, new int[] { 543, -4234 })]
365-
[TestCase(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new byte[] { }, new string[] { }, new int[] { })]
366-
[TestCase(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)]
367-
public async Task TestPostgresTypes(bool cBoolean, short cSmallint, int cInteger, long cBigint, float cReal, decimal cNumeric, decimal cDecimal, double cDoublePrecision, decimal cMoney, DateTime cDate, DateTime cTimestamp, string cChar, string cVarchar, string cCharacterVarying, string cText, byte[] cBytea, string[] cTextArray, int[] cIntegerArray)
369+
[TestCase(true, 35, -23423, 4235235263, 3.83f, 4.5534, 998.432, -8403284.321435, 42332.53, "2000-1-30", "1983-11-3 02:01:22", "2022-10-2 15:44:01+09:00", "E", "It takes a nation of millions to hold us back", "Rebel Without a Pause", "Prophets of Rage", new byte[] { 0x45, 0x42 }, new string[] { "Party", "Fight" }, new int[] { 543, -4234 })]
370+
[TestCase(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, new byte[] { }, new string[] { }, new int[] { })]
371+
[TestCase(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)]
372+
public async Task TestPostgresTypes(bool cBoolean, short cSmallint, int cInteger, long cBigint, float cReal, decimal cNumeric, decimal cDecimal, double cDoublePrecision, decimal cMoney, DateTime cDate, DateTime cTimestamp, DateTime cTimestampWithTz, string cChar, string cVarchar, string cCharacterVarying, string cText, byte[] cBytea, string[] cTextArray, int[] cIntegerArray)
368373
{
369-
await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs { CBoolean = cBoolean, CSmallint = cSmallint, CInteger = cInteger, CBigint = cBigint, CReal = cReal, CNumeric = cNumeric, CDecimal = cDecimal, CDoublePrecision = cDoublePrecision, CMoney = cMoney, CDate = cDate, CTimestamp = cTimestamp, CChar = cChar, CVarchar = cVarchar, CCharacterVarying = cCharacterVarying, CText = cText, CBytea = cBytea, CTextArray = cTextArray, CIntegerArray = cIntegerArray });
374+
await QuerySql.InsertPostgresTypes(new QuerySql.InsertPostgresTypesArgs { CBoolean = cBoolean, CSmallint = cSmallint, CInteger = cInteger, CBigint = cBigint, CReal = cReal, CNumeric = cNumeric, CDecimal = cDecimal, CDoublePrecision = cDoublePrecision, CMoney = cMoney, CDate = cDate, CTimestamp = cTimestamp, CTimestampWithTz = cTimestampWithTz, CChar = cChar, CVarchar = cVarchar, CCharacterVarying = cCharacterVarying, CText = cText, CBytea = cBytea, CTextArray = cTextArray, CIntegerArray = cIntegerArray });
370375
var expected = new QuerySql.GetPostgresTypesRow
371376
{
372377
CBoolean = cBoolean,
@@ -380,6 +385,7 @@ public async Task TestPostgresTypes(bool cBoolean, short cSmallint, int cInteger
380385
CMoney = cMoney,
381386
CDate = cDate,
382387
CTimestamp = cTimestamp,
388+
CTimestampWithTz = cTimestampWithTz,
383389
CChar = cChar,
384390
CVarchar = cVarchar,
385391
CCharacterVarying = cCharacterVarying,
@@ -405,6 +411,7 @@ private static void AssertSingularEquals(QuerySql.GetPostgresTypesRow expected,
405411
Assert.That(actual.CMoney, Is.EqualTo(expected.CMoney));
406412
Assert.That(actual.CDate, Is.EqualTo(expected.CDate));
407413
Assert.That(actual.CTimestamp, Is.EqualTo(expected.CTimestamp));
414+
Assert.That(actual.CTimestampWithTz, Is.EqualTo(expected.CTimestampWithTz));
408415
Assert.That(actual.CChar, Is.EqualTo(expected.CChar));
409416
Assert.That(actual.CVarchar, Is.EqualTo(expected.CVarchar));
410417
Assert.That(actual.CCharacterVarying, Is.EqualTo(expected.CCharacterVarying));

0 commit comments

Comments
 (0)