Skip to content

Commit efe5a44

Browse files
committed
Make server side timezone available for tests
1 parent abca168 commit efe5a44

3 files changed

Lines changed: 206 additions & 50 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright (C) 2023 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
4+
5+
using System;
6+
7+
namespace Xtensive.Orm.Tests
8+
{
9+
public interface IStorageTimeZoneProvider
10+
{
11+
public TimeSpan TimeZoneOffset { get; }
12+
}
13+
}

Orm/Xtensive.Orm.Tests.Framework/StorageProviderInfo.cs

Lines changed: 62 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
// Copyright (C) 2003-2010 Xtensive LLC.
2-
// All rights reserved.
3-
// For conditions of distribution and use, see license.
1+
// Copyright (C) 2013-2023 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
44
// Created by: Denis Krjuchkov
55
// Created: 2013.07.23
66

77
using System;
88
using Xtensive.Orm.Providers;
9+
using Xtensive.Sql;
910

1011
namespace Xtensive.Orm.Tests
1112
{
@@ -30,44 +31,37 @@ public static StorageProviderInfo Instance
3031

3132
public ProviderInfo Info { get; private set; }
3233

33-
public bool CheckProviderIs(StorageProvider requiredProviders)
34-
{
35-
return (Provider & requiredProviders)!=0;
36-
}
34+
public IStorageTimeZoneProvider TimeZoneProvider { get; private set; }
3735

38-
public bool CheckProviderIsNot(StorageProvider disallowedProviders)
39-
{
40-
return (Provider & disallowedProviders)==0;
41-
}
36+
public bool CheckProviderIs(StorageProvider requiredProviders) =>
37+
(Provider & requiredProviders) != 0;
4238

43-
public bool CheckProviderVersionIsAtLeast(Version minimalVersion)
44-
{
45-
return Info.StorageVersion >= minimalVersion;
46-
}
39+
public bool CheckProviderIsNot(StorageProvider disallowedProviders) =>
40+
(Provider & disallowedProviders) == 0;
4741

48-
public bool CheckProviderVersionIsAtMost(Version maximalVersion)
49-
{
50-
return Info.StorageVersion <= maximalVersion;
51-
}
42+
public bool CheckProviderVersionIsAtLeast(Version minimalVersion) =>
43+
Info.StorageVersion >= minimalVersion;
5244

53-
public bool CheckAllFeaturesSupported(ProviderFeatures requiredFeatures)
54-
{
55-
return (Info.ProviderFeatures & requiredFeatures)==requiredFeatures;
56-
}
45+
public bool CheckProviderVersionIsAtMost(Version maximalVersion) =>
46+
Info.StorageVersion <= maximalVersion;
5747

58-
public bool CheckAllFeaturesNotSupported(ProviderFeatures disallowedFeatures)
59-
{
60-
return (Info.ProviderFeatures & disallowedFeatures)==0;
61-
}
48+
public bool CheckAllFeaturesSupported(ProviderFeatures requiredFeatures) =>
49+
(Info.ProviderFeatures & requiredFeatures) == requiredFeatures;
6250

63-
public bool CheckAnyFeatureSupported(ProviderFeatures requiredFeatures)
64-
{
65-
return (Info.ProviderFeatures & requiredFeatures)!=0;
66-
}
51+
public bool CheckAllFeaturesNotSupported(ProviderFeatures disallowedFeatures) =>
52+
(Info.ProviderFeatures & disallowedFeatures) == 0;
53+
54+
public bool CheckAnyFeatureSupported(ProviderFeatures requiredFeatures) =>
55+
(Info.ProviderFeatures & requiredFeatures) != 0;
56+
57+
public bool CheckAnyFeatureNotSupported(ProviderFeatures disallowedFeatures) =>
58+
(Info.ProviderFeatures & disallowedFeatures) != disallowedFeatures;
6759

68-
public bool CheckAnyFeatureNotSupported(ProviderFeatures disallowedFeatures)
60+
public IDisposable ReplaceTimeZoneProvider(IStorageTimeZoneProvider newProvder)
6961
{
70-
return (Info.ProviderFeatures & disallowedFeatures)!=disallowedFeatures;
62+
var oldProvider = TimeZoneProvider;
63+
TimeZoneProvider = newProvder;
64+
return new Core.Disposable((b) => TimeZoneProvider = oldProvider);
7165
}
7266

7367
private StorageProviderInfo()
@@ -76,28 +70,46 @@ private StorageProviderInfo()
7670
var providerName = config.ConnectionInfo.Provider;
7771

7872
Provider = ParseProvider(providerName);
79-
Info = ProviderInfoBuilder.Build(providerName, TestSqlDriver.Create(config.ConnectionInfo));
73+
var sqlDriver = TestSqlDriver.Create(config.ConnectionInfo);
74+
TimeZoneProvider = GetTimeZoneProvider(Provider, sqlDriver);
75+
Info = ProviderInfoBuilder.Build(providerName, sqlDriver);
8076
}
8177

8278
private static StorageProvider ParseProvider(string provider)
8379
{
8480
switch (provider) {
85-
case WellKnown.Provider.SqlServer:
86-
return StorageProvider.SqlServer;
87-
case WellKnown.Provider.SqlServerCe:
88-
return StorageProvider.SqlServerCe;
89-
case WellKnown.Provider.PostgreSql:
90-
return StorageProvider.PostgreSql;
91-
case WellKnown.Provider.Oracle:
92-
return StorageProvider.Oracle;
93-
case WellKnown.Provider.MySql:
94-
return StorageProvider.MySql;
95-
case WellKnown.Provider.Firebird:
96-
return StorageProvider.Firebird;
97-
case WellKnown.Provider.Sqlite:
98-
return StorageProvider.Sqlite;
99-
default:
100-
throw new ArgumentOutOfRangeException("provider");
81+
case WellKnown.Provider.SqlServer:
82+
return StorageProvider.SqlServer;
83+
case WellKnown.Provider.SqlServerCe:
84+
return StorageProvider.SqlServerCe;
85+
case WellKnown.Provider.PostgreSql:
86+
return StorageProvider.PostgreSql;
87+
case WellKnown.Provider.Oracle:
88+
return StorageProvider.Oracle;
89+
case WellKnown.Provider.MySql:
90+
return StorageProvider.MySql;
91+
case WellKnown.Provider.Firebird:
92+
return StorageProvider.Firebird;
93+
case WellKnown.Provider.Sqlite:
94+
return StorageProvider.Sqlite;
95+
default:
96+
throw new ArgumentOutOfRangeException("provider");
97+
}
98+
}
99+
100+
private static IStorageTimeZoneProvider GetTimeZoneProvider(StorageProvider provider, SqlDriver sqlDriver)
101+
{
102+
switch (provider) {
103+
case StorageProvider.SqlServer:
104+
return new SqlServerTimeZoneProvider(sqlDriver);
105+
case StorageProvider.PostgreSql:
106+
return new PgSqlTimeZoneProvider(sqlDriver);
107+
case StorageProvider.Oracle:
108+
return new OracleTimeZoneProvider(sqlDriver);
109+
case StorageProvider.Sqlite:
110+
return new SqliteTimeZoneProvider(sqlDriver);
111+
default:
112+
return new LocalTimeZoneProvider();
101113
}
102114
}
103115
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// Copyright (C) 2023 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
4+
5+
using System;
6+
using Xtensive.Sql;
7+
8+
namespace Xtensive.Orm.Tests
9+
{
10+
internal abstract class StorageTimeZoneProvider : IStorageTimeZoneProvider
11+
{
12+
public TimeSpan TimeZoneOffset { get; private set; }
13+
14+
protected abstract TimeSpan GetServerTimezone(SqlDriver sqlDriver);
15+
16+
public StorageTimeZoneProvider(SqlDriver sqlDriver)
17+
{
18+
TimeZoneOffset = GetServerTimezone(sqlDriver);
19+
}
20+
}
21+
22+
internal sealed class LocalTimeZoneProvider : IStorageTimeZoneProvider
23+
{
24+
public TimeSpan TimeZoneOffset => TimeZoneInfo.Local.BaseUtcOffset;
25+
}
26+
27+
internal sealed class SqlServerTimeZoneProvider : StorageTimeZoneProvider
28+
{
29+
protected override TimeSpan GetServerTimezone(SqlDriver sqlDriver)
30+
{
31+
using (var c = sqlDriver.CreateConnection()) {
32+
c.Open();
33+
using (var cmd = c.CreateCommand("SELECT SYSDATETIMEOFFSET();")) {
34+
var dateTimeOffset = (DateTimeOffset) cmd.ExecuteScalar();
35+
return dateTimeOffset.Offset;
36+
}
37+
}
38+
}
39+
40+
public SqlServerTimeZoneProvider(SqlDriver sqlDriver)
41+
: base(sqlDriver)
42+
{
43+
}
44+
}
45+
46+
internal sealed class OracleTimeZoneProvider : StorageTimeZoneProvider
47+
{
48+
protected override TimeSpan GetServerTimezone(SqlDriver sqlDriver)
49+
{
50+
var mappings = sqlDriver.TypeMappings[typeof(DateTimeOffset)];
51+
52+
TimeSpan value;
53+
using (var connection = sqlDriver.CreateConnection()) {
54+
connection.Open();
55+
var commandText = "select CAST(systimestamp AS timestamp with time zone) as \"local_time\" from dual";
56+
using (var cmd = connection.CreateCommand(commandText)) {
57+
using (var reader = cmd.ExecuteReader()) {
58+
_ = reader.Read();
59+
var dateTimeOffset = (DateTimeOffset) mappings.ReadValue(reader, 0);
60+
value = dateTimeOffset.Offset;
61+
}
62+
}
63+
connection.Close();
64+
}
65+
return value;
66+
}
67+
68+
public OracleTimeZoneProvider(SqlDriver sqlDriver)
69+
: base(sqlDriver)
70+
{
71+
}
72+
}
73+
74+
internal sealed class PgSqlTimeZoneProvider : StorageTimeZoneProvider
75+
{
76+
protected override TimeSpan GetServerTimezone(SqlDriver sqlDriver)
77+
{
78+
var mappings = sqlDriver.TypeMappings[typeof(DateTimeOffset)];
79+
var referenceDate = new DateTime(2016, 10, 23);
80+
TimeSpan value;
81+
using (var connection = sqlDriver.CreateConnection()) {
82+
connection.Open();
83+
var commandText = $"select CAST('{referenceDate.ToString("yyyy-MM-dd HH:mm:ss")}' AS timestamp with time zone) as \"local_time\"";
84+
using (var cmd = connection.CreateCommand(commandText)) {
85+
using (var reader = cmd.ExecuteReader()) {
86+
_ = reader.Read();
87+
var dateTimeOffset = (DateTimeOffset) mappings.ReadValue(reader, 0);
88+
89+
value = dateTimeOffset.Offset;
90+
if (dateTimeOffset.DateTime != referenceDate) {
91+
value = value.Add(referenceDate - dateTimeOffset.DateTime);
92+
}
93+
}
94+
}
95+
connection.Close();
96+
}
97+
return value;
98+
}
99+
100+
public PgSqlTimeZoneProvider(SqlDriver sqlDriver) : base(sqlDriver)
101+
{
102+
}
103+
}
104+
105+
internal sealed class SqliteTimeZoneProvider : StorageTimeZoneProvider
106+
{
107+
protected override TimeSpan GetServerTimezone(SqlDriver sqlDriver)
108+
{
109+
TimeSpan value;
110+
using (var connection = sqlDriver.CreateConnection()) {
111+
connection.Open();
112+
var commandText = "SELECT ((STRFTIME ('%s', '2016-01-01 12:00:00') - STRFTIME ('%s', '2016-01-01 12:00:00', 'UTC')) / 60)";
113+
using (var cmd = connection.CreateCommand(commandText)) {
114+
using (var reader = cmd.ExecuteReader()) {
115+
_ = reader.Read();
116+
var offsetInMinutes = reader.GetInt32(0);
117+
118+
value = new TimeSpan(offsetInMinutes / 60, offsetInMinutes % 60, 0);
119+
}
120+
}
121+
connection.Close();
122+
}
123+
return value;
124+
}
125+
126+
public SqliteTimeZoneProvider(SqlDriver sqlDriver)
127+
: base(sqlDriver)
128+
{
129+
}
130+
}
131+
}

0 commit comments

Comments
 (0)