11using System ;
22using System . Collections . Generic ;
33using System . Linq ;
4- using Npgsql ;
54using NUnit . Framework ;
65using Xtensive . Sql . Drivers . PostgreSql ;
6+ using PostgreSqlDriver = Xtensive . Sql . Drivers . PostgreSql . Driver ;
77
88namespace Xtensive . Orm . Tests . Sql . PostgreSql
99{
1010 [ TestFixture ]
1111 public sealed class PostgreSqlHelperTest : SqlTest
1212 {
13- private IReadOnlyDictionary < string , TimeSpan > serverTimeZones ;
14-
15- private string [ ] testTimezones ;
13+ private string [ ] timezoneIdsWithWinAnalogue ;
14+ private string [ ] timezoneIdsWithoutWinAnalogue ;
1615
1716 public static TimeSpan [ ] Intervals
1817 {
@@ -44,61 +43,73 @@ public static TimeSpan[] Intervals
4443 } ;
4544 }
4645
47- protected override void CheckRequirements ( ) => Require . ProviderIs ( StorageProvider . PostgreSql ) ;
48-
49- protected override void TestFixtureSetUp ( )
46+ public static string [ ] PosixOffsetFormatValues
5047 {
51- base . TestFixtureSetUp ( ) ;
52- var nativeDriver = ( Xtensive . Sql . Drivers . PostgreSql . Driver ) Driver ;
53-
54- serverTimeZones = nativeDriver . PostgreServerInfo . ServerTimeZones ;
55- testTimezones = serverTimeZones . Keys . Union ( new [ ] {
48+ get => new [ ] {
5649 "<+02>-02" ,
57- "<+2>-2" ,
5850 "<+05>-05" ,
59- "<+5>-5" ,
6051 "<+07>-07" ,
61- "<+7>-7" ,
6252 "<-02>+02" ,
63- "<-2>+2" ,
6453 "<-05>+05" ,
65- "<-5>+5" ,
6654 "<-07>+07" ,
67- "<-7>+7"
68- } ) . ToArray ( ) ;
55+ "<-0730>+0730"
56+ } ;
57+ }
58+
59+ public static string [ ] PseudoPosixOffsetFormatValues
60+ {
61+ get => new [ ] {
62+ "<+2>-2" ,
63+ "<+5>-5" ,
64+ "<+7>-7" ,
65+ "<-2>+2" ,
66+ "<-5>+5" ,
67+ "<-7>+7" ,
68+ "<ulalala>not-ulalala"
69+ } ;
70+ }
71+
72+ protected override void CheckRequirements ( ) => Require . ProviderIs ( StorageProvider . PostgreSql ) ;
73+
74+ protected override void TestFixtureSetUp ( )
75+ {
76+ base . TestFixtureSetUp ( ) ;
77+
78+ LoadServerTimeZones ( Connection , out timezoneIdsWithWinAnalogue , out timezoneIdsWithoutWinAnalogue ) ;
6979
7080 Connection . Close ( ) ;
7181 }
7282
7383 [ Test ]
74- public void TimeZoneRecognitionTest ( )
84+ [ TestCaseSource ( nameof ( PosixOffsetFormatValues ) ) ]
85+ public void PosixOffsetRecognitionTest ( string offset )
7586 {
76- foreach ( var timezone in testTimezones ) {
77-
78- if ( timezone . StartsWith ( '<' ) ) {
79- if ( timezone . Contains ( '0' ) ) {
80-
81-
82- Assert . That ( serverTimeZones . TryGetValue ( timezone , out var result1 ) , Is . False ) ;
83- Assert . That ( serverTimeZones . TryGetValue ( PostgreSqlHelper . TryGetZoneFromPosix ( timezone ) , out var result2 ) , Is . True ) ;
84- Assert . That ( result2 , Is . EqualTo ( TimeSpan . FromHours ( 2 ) )
85- . Or . EqualTo ( TimeSpan . FromHours ( 5 ) )
86- . Or . EqualTo ( TimeSpan . FromHours ( 7 ) )
87- . Or . EqualTo ( TimeSpan . FromHours ( - 2 ) )
88- . Or . EqualTo ( TimeSpan . FromHours ( - 5 ) )
89- . Or . EqualTo ( TimeSpan . FromHours ( - 7 ) ) ) ;
90- }
91- else {
92- Assert . That ( serverTimeZones . TryGetValue ( timezone , out var result1 ) , Is . False ) ;
93- Assert . That ( serverTimeZones . TryGetValue ( PostgreSqlHelper . TryGetZoneFromPosix ( timezone ) , out var result2 ) , Is . False ) ;
94- }
87+ var systemTimezone = PostgreSqlHelper . GetTimeZoneInfoForServerTimeZone ( offset ) ;
88+ Assert . That ( systemTimezone , Is . Not . Null ) ;
89+ Assert . That ( systemTimezone . Id . Contains ( "UTC" ) ) ;
90+ }
9591
96- }
97- else {
98- Assert . That ( serverTimeZones . TryGetValue ( timezone , out var result1 ) , Is . True ) ;
99- Assert . That ( serverTimeZones . TryGetValue ( PostgreSqlHelper . TryGetZoneFromPosix ( timezone ) , out var result2 ) , Is . True ) ;
100- Assert . That ( result1 , Is . EqualTo ( result2 ) ) ;
101- }
92+ [ Test ]
93+ [ TestCaseSource ( nameof ( PseudoPosixOffsetFormatValues ) ) ]
94+ public void PseudoPosixOffsetRecognitionTest ( string offset )
95+ {
96+ var systemTimezone = PostgreSqlHelper . GetTimeZoneInfoForServerTimeZone ( offset ) ;
97+ Assert . That ( systemTimezone , Is . Null ) ;
98+ }
99+
100+ [ Test ]
101+ public void ResolvableTimeZonesTest ( )
102+ {
103+ foreach ( var tz in timezoneIdsWithWinAnalogue ) {
104+ Assert . That ( PostgreSqlHelper . GetTimeZoneInfoForServerTimeZone ( tz ) , Is . Not . Null ) ;
105+ }
106+ }
107+
108+ [ Test ]
109+ public void UnresolvableTimeZonesTest ( )
110+ {
111+ foreach ( var tz in timezoneIdsWithoutWinAnalogue ) {
112+ Assert . That ( PostgreSqlHelper . GetTimeZoneInfoForServerTimeZone ( tz ) , Is . Null ) ;
102113 }
103114 }
104115
@@ -110,5 +121,64 @@ public void TimeSpanToIntervalConversionTest(TimeSpan testValue)
110121 var backToTimeSpan = PostgreSqlHelper . ResurrectTimeSpanFromNpgsqlInterval ( nativeInterval ) ;
111122 Assert . That ( backToTimeSpan , Is . EqualTo ( testValue ) ) ;
112123 }
124+
125+
126+ private static void LoadServerTimeZones ( Xtensive . Sql . SqlConnection connection ,
127+ out string [ ] timezoneIdsWithWinAnalogue ,
128+ out string [ ] timezoneIdsWithoutWinAnalogue )
129+ {
130+ var timezoneIdsWithWinAnalogueList = new List < string > ( ) ;
131+ var timezoneIdsWithoutWinAnalogueList = new List < string > ( ) ;
132+
133+ var existing = new HashSet < string > ( ) ;
134+ var serverTimeZoneAbbrevs = new HashSet < string > ( ) ;
135+ using ( var command = connection . CreateCommand ( "SELECT \" name\" , \" abbrev\" FROM pg_catalog.pg_timezone_names" ) )
136+ using ( var reader = command . ExecuteReader ( ) ) {
137+ while ( reader . Read ( ) ) {
138+ var name = reader . GetString ( 0 ) ;
139+ var abbrev = reader . GetString ( 1 ) ;
140+
141+ if ( name . Equals ( "ZULU" , StringComparison . OrdinalIgnoreCase )
142+ || abbrev . Equals ( "ZULU" , StringComparison . OrdinalIgnoreCase ) )
143+ continue ;
144+
145+ if ( TimeZoneInfo . TryConvertIanaIdToWindowsId ( name , out var winAnalogue ) )
146+ timezoneIdsWithWinAnalogueList . Add ( name ) ;
147+ else
148+ timezoneIdsWithoutWinAnalogueList . Add ( name ) ;
149+
150+ if ( abbrev [ 0 ] != '-' && abbrev [ 0 ] != '+' && existing . Add ( abbrev ) ) {
151+ if ( TimeZoneInfo . TryConvertIanaIdToWindowsId ( abbrev , out var winAnalogue1 ) )
152+ timezoneIdsWithWinAnalogueList . Add ( abbrev ) ;
153+ else
154+ timezoneIdsWithoutWinAnalogueList . Add ( abbrev ) ;
155+ }
156+ }
157+ }
158+
159+ using ( var command = connection . CreateCommand ( "SELECT \" abbrev\" FROM pg_catalog.pg_timezone_abbrevs" ) )
160+ using ( var reader = command . ExecuteReader ( ) ) {
161+ while ( reader . Read ( ) ) {
162+ var abbrev = reader . GetString ( 0 ) ;
163+
164+ if ( abbrev . Equals ( "ZULU" , StringComparison . OrdinalIgnoreCase ) || ! existing . Add ( abbrev ) )
165+ continue ;
166+
167+ if ( TimeZoneInfo . TryConvertIanaIdToWindowsId ( abbrev , out var winAnalogue ) )
168+ timezoneIdsWithWinAnalogueList . Add ( abbrev ) ;
169+ else
170+ timezoneIdsWithoutWinAnalogueList . Add ( abbrev ) ;
171+
172+ if ( abbrev [ 0 ] != '-' && abbrev [ 0 ] != '+' && existing . Add ( abbrev ) ) {
173+ if ( TimeZoneInfo . TryConvertIanaIdToWindowsId ( abbrev , out var winAnalogue1 ) )
174+ timezoneIdsWithWinAnalogueList . Add ( abbrev ) ;
175+ else
176+ timezoneIdsWithoutWinAnalogueList . Add ( abbrev ) ;
177+ }
178+ }
179+ }
180+ timezoneIdsWithoutWinAnalogue = timezoneIdsWithoutWinAnalogueList . ToArray ( ) ;
181+ timezoneIdsWithWinAnalogue = timezoneIdsWithWinAnalogueList . ToArray ( ) ;
182+ }
113183 }
114184}
0 commit comments