Skip to content

Commit c7845d8

Browse files
Merge pull request #703 from fredericDelaporte/NH-3919
NH-3919 - Clean up and harmonize datetime types with regards to different dialects
2 parents faebdcc + 55ebc64 commit c7845d8

File tree

186 files changed

+5811
-2433
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

186 files changed

+5811
-2433
lines changed

doc/reference/modules/basic_mapping.xml

+83-20
Original file line numberDiff line numberDiff line change
@@ -2638,14 +2638,30 @@
26382638
<row>
26392639
<entry><literal>DateTime</literal></entry>
26402640
<entry><literal>System.DateTime</literal></entry>
2641-
<entry><literal>DbType.DateTime</literal> - ignores the milliseconds</entry>
2642-
<entry>Default when no <literal>type</literal> attribute specified.</entry>
2641+
<entry>
2642+
<literal>DbType.DateTime</literal> / <literal>DbType.DateTime2</literal><co id="basic_mapping.datetime-co" linkends="basic_mapping.datetime"/>
2643+
</entry>
2644+
<entry>
2645+
Default when no <literal>type</literal> attribute specified. Does no
2646+
more ignore milliseconds since NHibernate v5.0.
2647+
</entry>
2648+
</row>
2649+
<row>
2650+
<entry><literal>DateTimeNoMs</literal></entry>
2651+
<entry><literal>System.DateTime</literal></entry>
2652+
<entry>
2653+
<literal>DbType.DateTime</literal> / <literal>DbType.DateTime2</literal><coref linkend="basic_mapping.datetime-co" />
2654+
</entry>
2655+
<entry><literal>type="DateTimeNoMs"</literal> must be specified. Ignores milliseconds.</entry>
26432656
</row>
26442657
<row>
26452658
<entry><literal>DateTime2</literal></entry>
26462659
<entry><literal>System.DateTime</literal></entry>
26472660
<entry><literal>DbType.DateTime2</literal></entry>
2648-
<entry><literal>type="DateTime2"</literal> must be specified.</entry>
2661+
<entry>
2662+
<literal>type="DateTime2"</literal> must be specified. Obsolete since
2663+
NHibernate v5.0, use <literal>DateTime</literal> instead.
2664+
</entry>
26492665
</row>
26502666
<row>
26512667
<entry><literal>DateTimeOffset</literal></entry>
@@ -2656,9 +2672,14 @@
26562672
<row>
26572673
<entry><literal>DbTimestamp</literal></entry>
26582674
<entry><literal>System.DateTime</literal></entry>
2659-
<entry><literal>DbType.DateTime</literal> - as specific as database supports.</entry>
2660-
<entry><literal>type="DbTimestamp"</literal> must be specified. When used as a <literal>version</literal>
2661-
field, uses the database's current time rather than the client's current time.</entry>
2675+
<entry>
2676+
<literal>DbType.DateTime</literal> / <literal>DbType.DateTime2</literal><coref linkend="basic_mapping.datetime-co" />
2677+
</entry>
2678+
<entry>
2679+
<literal>type="DbTimestamp"</literal> must be specified. When used as a
2680+
<literal>version</literal> field, uses the database's current time retrieved
2681+
in dedicated queries, rather than the client's current time.
2682+
</entry>
26622683
</row>
26632684
<row>
26642685
<entry><literal>Decimal</literal></entry>
@@ -2699,9 +2720,26 @@
26992720
<row>
27002721
<entry><literal>LocalDateTime</literal></entry>
27012722
<entry><literal>System.DateTime</literal></entry>
2702-
<entry><literal>DbType.DateTime</literal> - ignores the milliseconds</entry>
2703-
<entry><literal>type="LocalDateTime"</literal> must be specified. Ensures the
2704-
<literal>DateTimeKind</literal> is set to <literal>DateTimeKind.Local</literal></entry>
2723+
<entry>
2724+
<literal>DbType.DateTime</literal> / <literal>DbType.DateTime2</literal><coref linkend="basic_mapping.datetime-co" />
2725+
</entry>
2726+
<entry>
2727+
<literal>type="LocalDateTime"</literal> must be specified. Ensures the
2728+
<literal>DateTimeKind</literal> is set to <literal>DateTimeKind.Local</literal>.
2729+
Throws if set with a date having another kind.
2730+
Does no more ignore milliseconds since NHibernate v5.0.
2731+
</entry>
2732+
</row>
2733+
<row>
2734+
<entry><literal>LocalDateTimeNoMs</literal></entry>
2735+
<entry><literal>System.DateTime</literal></entry>
2736+
<entry>
2737+
<literal>DbType.DateTime</literal> / <literal>DbType.DateTime2</literal><coref linkend="basic_mapping.datetime-co" />
2738+
</entry>
2739+
<entry>
2740+
<literal>type="LocalDateTimeNoMs"</literal> must be specified. Similar to
2741+
<literal>type="LocalDateTime"</literal> but ignores milliseconds.
2742+
</entry>
27052743
</row>
27062744
<row>
27072745
<entry><literal>PersistentEnum</literal></entry>
@@ -2750,15 +2788,13 @@
27502788
<row>
27512789
<entry><literal>Timestamp</literal></entry>
27522790
<entry><literal>System.DateTime</literal></entry>
2753-
<entry><literal>DbType.DateTime</literal> - as specific as database supports.</entry>
2754-
<entry><literal>type="Timestamp"</literal> must be specified.</entry>
2755-
</row>
2756-
<row>
2757-
<entry><literal>TimestampUtc</literal></entry>
2758-
<entry><literal>System.DateTime</literal></entry>
2759-
<entry><literal>DbType.DateTime</literal> - as specific as database supports.</entry>
2760-
<entry><literal>type="TimestampUtc"</literal> must be specified. Ensures the
2761-
<literal>DateTimeKind</literal> is set to <literal>DateTimeKind.Utc</literal></entry>
2791+
<entry>
2792+
<literal>DbType.DateTime</literal> / <literal>DbType.DateTime2</literal><coref linkend="basic_mapping.datetime-co" />
2793+
</entry>
2794+
<entry>
2795+
Obsolete, its <literal>Timestamp</literal> alias will be remapped to
2796+
<literal>DateTime</literal> in a future version.
2797+
</entry>
27622798
</row>
27632799
<row>
27642800
<entry><literal>TrueFalse</literal></entry>
@@ -2787,8 +2823,25 @@
27872823
<row>
27882824
<entry><literal>UtcDateTime</literal></entry>
27892825
<entry><literal>System.DateTime</literal></entry>
2790-
<entry><literal>DbType.DateTime</literal> - ignores the milliseconds</entry>
2791-
<entry>Ensures the <literal>DateTimeKind</literal> is set to <literal>DateTimeKind.Utc</literal></entry>
2826+
<entry>
2827+
<literal>DbType.DateTime</literal> / <literal>DbType.DateTime2</literal><coref linkend="basic_mapping.datetime-co" />
2828+
</entry>
2829+
<entry>
2830+
Ensures the <literal>DateTimeKind</literal> is set to <literal>DateTimeKind.Utc</literal>.
2831+
Throws if set with a date having another kind.
2832+
Does no more ignore milliseconds since NHibernate v5.0.
2833+
</entry>
2834+
</row>
2835+
<row>
2836+
<entry><literal>UtcDateTimeNoMs</literal></entry>
2837+
<entry><literal>System.DateTime</literal></entry>
2838+
<entry>
2839+
<literal>DbType.DateTime</literal> / <literal>DbType.DateTime2</literal><coref linkend="basic_mapping.datetime-co" />
2840+
</entry>
2841+
<entry>
2842+
<literal>type="UtcDateTimeNoMs"</literal> must be specified. Similar to
2843+
<literal>type="LocalDateTime"</literal> but ignores milliseconds.
2844+
</entry>
27922845
</row>
27932846
<row>
27942847
<entry><literal>YesNo</literal></entry>
@@ -2800,6 +2853,16 @@
28002853
</tgroup>
28012854
</table>
28022855

2856+
<calloutlist>
2857+
<callout arearefs="basic_mapping.datetime-co" id="basic_mapping.datetime">
2858+
<para>
2859+
Since NHibernate v5.0 and if the dialect supports it, <literal>DbType.DateTime2</literal>
2860+
is used instead of <literal>DbType.DateTime</literal>. This may be disabled by setting
2861+
<literal>sql_types.keep_datetime</literal> to <literal>true</literal>.
2862+
</para>
2863+
</callout>
2864+
</calloutlist>
2865+
28032866
<table>
28042867
<title>System.Object Mapping Types</title>
28052868
<tgroup cols="4">

src/NHibernate.Test/Async/Legacy/CriteriaTest.cs

+13-13
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,14 @@ public async Task SimpleSelectTestAsync()
4040
long simple1Key = 15;
4141
Simple simple1 = new Simple();
4242
simple1.Address = "Street 12";
43-
simple1.Date = DateTime.Now;
43+
simple1.Date = RoundForDialect(DateTime.Now);
4444
simple1.Name = "For Criteria Test";
4545
simple1.Count = 16;
4646

4747
long notSimple1Key = 17;
4848
Simple notSimple1 = new Simple();
4949
notSimple1.Address = "Street 123";
50-
notSimple1.Date = DateTime.Now;
50+
notSimple1.Date = RoundForDialect(DateTime.Now);
5151
notSimple1.Name = "Don't be found";
5252
notSimple1.Count = 18;
5353

@@ -62,19 +62,19 @@ public async Task SimpleSelectTestAsync()
6262
using (ISession s2 = OpenSession())
6363
using (ITransaction t2 = s2.BeginTransaction())
6464
{
65-
IList results2 = await (s2.CreateCriteria(typeof(Simple))
66-
.Add(Expression.Eq("Address", "Street 12"))
67-
.ListAsync());
65+
var results2 = await (s2.CreateCriteria<Simple>()
66+
.Add(Restrictions.Eq("Address", "Street 12"))
67+
.ListAsync<Simple>());
6868

69-
Assert.AreEqual(1, results2.Count);
69+
Assert.That(results2.Count, Is.EqualTo(1), "Unexpected result count");
7070

71-
Simple simple2 = (Simple) results2[0];
71+
var simple2 = results2[0];
7272

73-
Assert.IsNotNull(simple2, "Unable to load object");
74-
Assert.AreEqual(simple1.Count, simple2.Count, "Load failed");
75-
Assert.AreEqual(simple1.Name, simple2.Name, "Load failed");
76-
Assert.AreEqual(simple1.Address, simple2.Address, "Load failed");
77-
Assert.AreEqual(simple1.Date.ToString(), simple2.Date.ToString(), "Load failed");
73+
Assert.That(simple2, Is.Not.Null, "Unable to load object");
74+
Assert.That(simple2.Count, Is.EqualTo(simple1.Count), "Unexpected Count property value");
75+
Assert.That(simple2.Name, Is.EqualTo(simple1.Name), "Unexpected name");
76+
Assert.That(simple2.Address, Is.EqualTo(simple1.Address), "Unexpected address");
77+
Assert.That(simple2.Date, Is.EqualTo(simple1.Date), "Unexpected date");
7878

7979
await (s2.DeleteAsync("from Simple"));
8080

@@ -181,4 +181,4 @@ public async Task CriteriaLeftOuterJoinAsync()
181181
}
182182

183183
}
184-
}
184+
}

src/NHibernate.Test/Async/Legacy/SQLLoaderTest.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public async Task TSAsync()
6060
await (session.SaveAsync(sim, 1L));
6161
IQuery q = session.CreateSQLQuery("select {sim.*} from Simple {sim} where {sim}.date_ = ?")
6262
.AddEntity("sim", typeof(Simple));
63-
q.SetTimestamp(0, sim.Date);
63+
q.SetDateTime(0, sim.Date);
6464
Assert.AreEqual(1, (await (q.ListAsync())).Count, "q.List.Count");
6565
await (session.DeleteAsync(sim));
6666
await (txn.CommitAsync());
@@ -84,7 +84,7 @@ public async Task TSNamedAsync()
8484
IQuery q =
8585
session.CreateSQLQuery("select {sim.*} from Simple {sim} where {sim}.date_ = :fred")
8686
.AddEntity("sim", typeof(Simple));
87-
q.SetTimestamp("fred", sim.Date);
87+
q.SetDateTime("fred", sim.Date);
8888
Assert.AreEqual(1, (await (q.ListAsync())).Count, "q.List.Count");
8989
await (session.DeleteAsync(sim));
9090
await (txn.CommitAsync());
@@ -660,4 +660,4 @@ public async Task NamedSQLQueryAsync()
660660
s.Close();
661661
}
662662
}
663-
}
663+
}

src/NHibernate.Test/Async/NHSpecificTest/NH1756/Fixture.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,19 @@ protected override bool AppliesTo(Dialect.Dialect dialect)
2828
protected override bool AppliesTo(ISessionFactoryImplementor factory)
2929
{
3030
// ODBC driver DateTime handling with SQL Server 2008+ Client is broken and forbids using it as a time stamp
31-
// generated on db-side.
31+
// custom generated on db-side.
3232
// Due to NH-3895, we have to force the scale on date-time parameters to 3 (3 significant fractional seconds digits)
3333
// when using ODBC + SQL Server 2008+, otherwise DateTime values having milliseconds will be rejected. But the SQL
3434
// Server DateTime does not have actually a one millisecond resolution (it has 3.333), causing ODBC to convert the
35-
// parameter to DateTime2. A DateTime value ending by 3ms (indeed 3.333) or 7ms (indeed 6.666) is
35+
// parameter to DateTime2 (yes it supports it, only its .Net classes do not).
36+
// A DateTime value ending by 3ms (indeed 3.333) or 7ms (indeed 6.666) is
3637
// to be transmitted as having 3ms or 7ms and will match if transmitted as a DateTime. But when transmitted as
3738
// DateTime2, it will no more be considered equal, causing the test to be flaky and failing two thirds of tries.
3839
// Example failing update captured with profiler:
3940
// exec sp_executesql N'UPDATE book SET name_column = @P1 WHERE id = @P2 AND version_column = @P3',
4041
// N'@P1 nvarchar(18),@P2 int,@P3 datetime2',N'modified test book',1,'2017-08-02 16:37:16.0630000'
4142
// Setting the scale to 2 still causes failure for two thirds of tries, due to 3ms/7ms being truncated in such case
42-
// with ODBC and SQL Server 2008+ Client, which is rejected bu ODBC.
43-
// (Affects DbVersionFixture too.)
43+
// with ODBC and SQL Server 2008+ Client, which is rejected by ODBC.
4444
return !(factory.ConnectionProvider.Driver is OdbcDriver);
4545
}
4646

@@ -115,4 +115,4 @@ public async Task SaveTransient_Then_Update_BugAsync()
115115
}
116116
}
117117
}
118-
}
118+
}

src/NHibernate.Test/Async/NHSpecificTest/NH3564/FixtureByCode.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ protected override HbmMapping GetMappings()
103103
rc.Property(x => x.Name);
104104
rc.Property(x => x.DateOfBirth, pm =>
105105
{
106-
pm.Type(NHibernateUtil.Timestamp);
106+
pm.Type(NHibernateUtil.DateTime);
107107
});
108108
});
109109

src/NHibernate.Test/Async/NHSpecificTest/NH3818/Fixture.cs

+14-17
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,19 @@
1010

1111
using System;
1212
using System.Linq;
13-
using System.Linq.Dynamic;
14-
using NHibernate.Linq;
1513
using NUnit.Framework;
14+
using NHibernate.Linq;
1615

1716
namespace NHibernate.Test.NHSpecificTest.NH3818
1817
{
19-
using System.Threading.Tasks;
20-
[TestFixture]
21-
public class FixtureAsync : BugTestCase
22-
{
23-
public override string BugNumber
24-
{
25-
get { return "NH3818"; }
26-
}
27-
18+
using System.Threading.Tasks;
19+
[TestFixture]
20+
public class FixtureAsync : BugTestCase
21+
{
2822
[Test]
2923
public async Task SelectConditionalValuesTestAsync()
3024
{
25+
var now = RoundForDialect(DateTime.Now);
3126
using (var spy = new SqlLogSpy())
3227
using (var session = OpenSession())
3328
using (session.BeginTransaction())
@@ -37,7 +32,7 @@ public async Task SelectConditionalValuesTestAsync()
3732
var cat = new MyLovelyCat
3833
{
3934
GUID = Guid.NewGuid(),
40-
Birthdate = DateTime.Now.AddDays(-days),
35+
Birthdate = now.AddDays(-days),
4136
Color = "Black",
4237
Name = "Kitty",
4338
Price = 0
@@ -51,31 +46,33 @@ public async Task SelectConditionalValuesTestAsync()
5146
.Select(o => new
5247
{
5348
o.Color,
54-
AliveDays = (int)(DateTime.Now - o.Birthdate).TotalDays,
49+
AliveDays = (now - o.Birthdate).TotalDays,
5550
o.Name,
5651
o.Price,
5752
})
5853
.SingleAsync());
5954

6055
//Console.WriteLine(spy.ToString());
61-
Assert.That(catInfo.AliveDays == days);
56+
// We need a tolerance: we are diffing dates as a timespan instead of counting days boundaries,
57+
// yielding a float. TimeSpan.Days yould always truncate a resulting partial day, so do not use it.
58+
Assert.That(catInfo.AliveDays, Is.EqualTo(days).Within(0.1));
6259

6360
var catInfo2 =
6461
await (session.Query<MyLovelyCat>()
6562
.Select(o => new
6663
{
6764
o.Color,
68-
AliveDays = o.Price > 0 ? (DateTime.Now - o.Birthdate).TotalDays : 0,
65+
AliveDays = o.Price > 0 ? (now - o.Birthdate).TotalDays : 0,
6966
o.Name,
7067
o.Price,
7168
})
7269
.SingleAsync());
7370

7471
//Console.WriteLine(spy.ToString());
75-
Assert.That(catInfo2.AliveDays == 0);
72+
Assert.That(catInfo2.AliveDays, Is.EqualTo(0));
7673

7774
}
7875
}
7976

8077
}
81-
}
78+
}

src/NHibernate.Test/Async/NHSpecificTest/NH392/Fixture.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,15 @@ public async Task UnsavedMinusOneNoNullReferenceExceptionAsync()
4646
}
4747
}
4848

49-
protected override void OnTearDown()
49+
protected override void OnTearDown()
5050
{
5151
using (ISession s = Sfi.OpenSession())
5252
{
5353
// s.Delete("from UnsavedValueMinusOne") loads then delete entities one by one, checking the version.
54-
// This fails with ODBC & Sql Server 2008+, see NH-1756 test case or DbVersionFixture for more details.
54+
// This fails with ODBC & Sql Server 2008+, see NH-1756 test case for more details.
5555
// Use an in-db query instead.
5656
s.CreateQuery("delete from UnsavedValueMinusOne").ExecuteUpdate();
5757
}
58-
}
58+
}
5959
}
60-
}
60+
}

src/NHibernate.Test/Async/NHSpecificTest/NH3961/DateParametersComparedTo.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ public async Task NonNullableMappedAsDateTimeShouldBeCultureAgnosticAsync()
114114

115115
// Non-reg test case
116116
[Test]
117+
[Obsolete]
117118
public async Task NonNullableMappedAsTimestampShouldBeCultureAgnosticAsync()
118119
{
119120
using (ISession session = OpenSession())
@@ -227,6 +228,7 @@ public async Task NullableMappedAsDateTimeShouldBeCultureAgnosticAsync()
227228

228229
// Failing test case till NH-3961 is fixed
229230
[Test]
231+
[Obsolete]
230232
public async Task NullableMappedAsTimestampShouldBeCultureAgnosticAsync()
231233
{
232234
using (ISession session = OpenSession())
@@ -290,4 +292,4 @@ public async Task NullableShouldBeCultureAgnosticAsync()
290292
}
291293
}
292294
}
293-
}
295+
}

0 commit comments

Comments
 (0)