Skip to content

Commit 76eda8d

Browse files
nizhikovptupitsyn
andauthored
IGNITE-12824 .NET: Add BinaryConfiguration.TimestampConverter (#8568)
Co-authored-by: Pavel Tupitsyn <[email protected]>
1 parent 1619e8e commit 76eda8d

File tree

15 files changed

+412
-33
lines changed

15 files changed

+412
-33
lines changed

modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
package org.apache.ignite.platform;
1919

2020
import java.sql.Timestamp;
21+
import java.time.ZoneId;
22+
import java.time.ZonedDateTime;
2123
import java.util.ArrayList;
2224
import java.util.Calendar;
2325
import java.util.Collection;
@@ -530,6 +532,24 @@ public void testUTCDateFromCache() {
530532
assertEquals(new Timestamp(new Date(91, Calendar.OCTOBER, 1, 0, 0, 0).getTime()), cache.get(2));
531533
}
532534

535+
/** */
536+
public void testLocalDateFromCache() {
537+
IgniteCache<Integer, Timestamp> cache = ignite.cache("net-dates");
538+
539+
ZoneId msk = ZoneId.of("Europe/Moscow");
540+
541+
//This Date in Europe/Moscow have offset +4.
542+
Timestamp ts1 = new Timestamp(ZonedDateTime.of(1982, 4, 1, 1, 0, 0, 0, msk).toInstant().toEpochMilli());
543+
//This Date in Europe/Moscow have offset +3.
544+
Timestamp ts2 = new Timestamp(ZonedDateTime.of(1982, 3, 31, 22, 0, 0, 0, msk).toInstant().toEpochMilli());
545+
546+
assertEquals(ts1, cache.get(5));
547+
assertEquals(ts2, cache.get(6));
548+
549+
cache.put(7, ts1);
550+
cache.put(8, ts2);
551+
}
552+
533553
/** */
534554
public void sleep(long delayMs) {
535555
try {

modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryDateTimeTest.cs

Lines changed: 208 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,24 @@ namespace Apache.Ignite.Core.Tests.Binary
2121
using System.Linq;
2222
using Apache.Ignite.Core.Binary;
2323
using Apache.Ignite.Core.Cache.Configuration;
24+
using Apache.Ignite.Core.Impl.Binary;
2425
using NUnit.Framework;
2526

2627
/// <summary>
2728
/// DateTime binary serialization tests.
2829
/// </summary>
2930
public class BinaryDateTimeTest
3031
{
32+
/** */
33+
internal const String FromErrMsg = "FromJavaTicks Error!";
34+
35+
/** */
36+
internal const String ToErrMsg = "ToJavaTicks Error!";
37+
38+
/** */
39+
private const String NotUtcDate =
40+
"DateTime is not UTC. Only UTC DateTime can be used for interop with other platforms.";
41+
3142
/// <summary>
3243
/// Sets up the test fixture.
3344
/// </summary>
@@ -80,7 +91,7 @@ public void TestSerializerForceTimestamp()
8091
.Select(x => x.Serializer)
8192
.OfType<BinaryReflectiveSerializer>()
8293
.Single();
83-
94+
8495
Assert.IsTrue(ser.ForceTimestamp);
8596

8697
AssertTimestampField<DateTimeObj2>((o, d) => o.Value = d, o => o.Value, "Value");
@@ -110,6 +121,152 @@ public void TestClassAttributes()
110121
AssertTimestampField<DateTimeClassAttribute2>((o, d) => o.Value = d, o => o.Value, "Value");
111122
}
112123

124+
/// <summary>
125+
/// Tests custom timestamp converter that modifies the values by adding one year on write and read.
126+
/// This test verifies that actual converted values are used by Ignite.
127+
/// </summary>
128+
[Test]
129+
public void TestAddYearTimestampConverter()
130+
{
131+
var cfg = new IgniteConfiguration(TestUtils.GetTestConfiguration())
132+
{
133+
AutoGenerateIgniteInstanceName = true,
134+
BinaryConfiguration = new BinaryConfiguration
135+
{
136+
ForceTimestamp = true,
137+
TimestampConverter = new AddYearTimestampConverter()
138+
}
139+
};
140+
141+
var ignite = Ignition.Start(cfg);
142+
143+
var dt = DateTime.UtcNow;
144+
var expected = dt.AddYears(2);
145+
146+
// Key & value.
147+
var cache = ignite.GetOrCreateCache<DateTime, DateTime>(TestUtils.TestName);
148+
cache[dt] = dt;
149+
150+
var resEntry = cache.Single();
151+
152+
Assert.AreEqual(expected, resEntry.Key);
153+
Assert.AreEqual(expected, resEntry.Value);
154+
155+
// Key & value array.
156+
157+
// Object field.
158+
var cache2 = ignite.GetOrCreateCache<DateTimePropertyAttribute, DateTimePropertyAttribute>(
159+
TestUtils.TestName);
160+
161+
cache2.RemoveAll();
162+
163+
var obj = new DateTimePropertyAttribute {Value = dt};
164+
cache2[obj] = obj;
165+
166+
var resEntry2 = cache2.Single();
167+
Assert.AreEqual(expected, resEntry2.Key.Value);
168+
Assert.AreEqual(expected, resEntry2.Value.Value);
169+
170+
// Object array field.
171+
var cache3 = ignite.GetOrCreateCache<DateTimeArr, DateTimeArr>(TestUtils.TestName);
172+
cache3.RemoveAll();
173+
174+
var obj2 = new DateTimeArr {Value = new[]{dt}};
175+
cache3[obj2] = obj2;
176+
cache3[obj2] = obj2;
177+
178+
var resEntry3 = cache3.Single();
179+
Assert.AreEqual(expected, resEntry3.Key.Value.Single());
180+
Assert.AreEqual(expected, resEntry3.Value.Value.Single());
181+
}
182+
183+
/// <summary>
184+
/// Tests custom timestamp converter.
185+
/// </summary>
186+
[Test]
187+
public void TestCustomTimestampConverter()
188+
{
189+
var cfg = new IgniteConfiguration(TestUtils.GetTestConfiguration(name: "ignite-1"))
190+
{
191+
BinaryConfiguration = new BinaryConfiguration
192+
{
193+
ForceTimestamp = true,
194+
TimestampConverter = new TimestampConverter()
195+
}
196+
};
197+
198+
var ignite = Ignition.Start(cfg);
199+
var binary = ignite.GetBinary();
200+
201+
// Check config.
202+
Assert.NotNull(ignite.GetConfiguration().BinaryConfiguration.TimestampConverter);
203+
204+
AssertTimestampField<DateTimeObj2>((o, d) => o.Value = d, o => o.Value, "Value", ignite);
205+
206+
var dt1 = new DateTime(1997, 8, 29, 0, 0, 0, DateTimeKind.Utc);
207+
208+
var ex = Assert.Throws<BinaryObjectException>(() => binary.ToBinary<DateTime>(dt1));
209+
Assert.AreEqual(ToErrMsg, ex.Message);
210+
211+
ex = Assert.Throws<BinaryObjectException>(() => binary.ToBinary<DateTime?[]>(new DateTime?[] {dt1}));
212+
Assert.AreEqual(ToErrMsg, ex.Message);
213+
214+
var dt2 = new DateTime(1997, 8, 4, 0, 0, 0, DateTimeKind.Utc);
215+
216+
ex = Assert.Throws<BinaryObjectException>(() => binary.ToBinary<DateTime>(dt2));
217+
Assert.AreEqual(FromErrMsg, ex.Message);
218+
219+
ex = Assert.Throws<BinaryObjectException>(() => binary.ToBinary<DateTime?[]>(new DateTime?[] {dt2}));
220+
Assert.AreEqual(FromErrMsg, ex.Message);
221+
222+
var datesCache = ignite.CreateCache<DateTime, DateTime>("dates");
223+
224+
var check = new Action<DateTime, DateTime, String>((date1, date2, errMsg) =>
225+
{
226+
ex = Assert.Throws<BinaryObjectException>(() => datesCache.Put(date1, date2), "Timestamp fields should throw an error on non-UTC values");
227+
228+
Assert.AreEqual(errMsg, ex.Message);
229+
});
230+
231+
check.Invoke(DateTime.Now, DateTime.UtcNow, NotUtcDate);
232+
check.Invoke(DateTime.UtcNow, DateTime.Now, NotUtcDate);
233+
check.Invoke(dt1, DateTime.UtcNow, ToErrMsg);
234+
check.Invoke(DateTime.UtcNow, dt1, ToErrMsg);
235+
236+
var now = DateTime.UtcNow;
237+
238+
datesCache.Put(now, dt2);
239+
ex = Assert.Throws<BinaryObjectException>(() => datesCache.Get(now), "Timestamp fields should throw an error on non-UTC values");
240+
Assert.AreEqual(FromErrMsg, ex.Message);
241+
242+
datesCache.Put(now, now);
243+
Assert.AreEqual(now, datesCache.Get(now));
244+
245+
var datesObjCache = ignite.CreateCache<DateTimeObj, DateTimeObj>("datesObj");
246+
247+
check = (date1, date2, errMsg) =>
248+
{
249+
ex = Assert.Throws<BinaryObjectException>(() => datesObjCache.Put(new DateTimeObj {Value = date1}, new DateTimeObj {Value = date2}),
250+
"Timestamp fields should throw an error on non-UTC values");
251+
252+
Assert.AreEqual(errMsg, ex.Message);
253+
};
254+
255+
check.Invoke(DateTime.Now, DateTime.UtcNow, NotUtcDate);
256+
check.Invoke(DateTime.UtcNow, DateTime.Now, NotUtcDate);
257+
check.Invoke(dt1, DateTime.UtcNow, ToErrMsg);
258+
check.Invoke(DateTime.UtcNow, dt1, ToErrMsg);
259+
260+
var nowObj = new DateTimeObj {Value = now};
261+
262+
datesObjCache.Put(nowObj, new DateTimeObj {Value = dt2});
263+
ex = Assert.Throws<BinaryObjectException>(() => datesObjCache.Get(nowObj), "Timestamp fields should throw an error on non-UTC values");
264+
Assert.AreEqual(FromErrMsg, ex.Message);
265+
266+
datesObjCache.Put(nowObj, nowObj);
267+
Assert.AreEqual(nowObj.Value, datesObjCache.Get(nowObj).Value);
268+
}
269+
113270
/// <summary>
114271
/// Asserts that specified field is serialized as DateTime object.
115272
/// </summary>
@@ -136,10 +293,10 @@ private static void AssertDateTimeField<T>(Action<T, DateTime> setValue,
136293
/// Asserts that specified field is serialized as Timestamp.
137294
/// </summary>
138295
private static void AssertTimestampField<T>(Action<T, DateTime> setValue,
139-
Func<T, DateTime> getValue, string fieldName) where T : new()
296+
Func<T, DateTime> getValue, string fieldName, IIgnite ignite = null) where T : new()
140297
{
141298
// Non-UTC DateTime throws.
142-
var binary = Ignition.GetIgnite().GetBinary();
299+
var binary = ignite != null ? ignite.GetBinary() : Ignition.GetIgnite().GetBinary();
143300

144301
var obj = new T();
145302

@@ -148,8 +305,7 @@ private static void AssertTimestampField<T>(Action<T, DateTime> setValue,
148305
var ex = Assert.Throws<BinaryObjectException>(() => binary.ToBinary<IBinaryObject>(obj),
149306
"Timestamp fields should throw an error on non-UTC values");
150307

151-
Assert.AreEqual("DateTime is not UTC. Only UTC DateTime can be used for interop with other platforms.",
152-
ex.Message);
308+
Assert.AreEqual(NotUtcDate, ex.Message);
153309

154310
// UTC DateTime works.
155311
setValue(obj, DateTime.UtcNow);
@@ -171,6 +327,11 @@ private class DateTimeObj2
171327
public DateTime Value { get; set; }
172328
}
173329

330+
private class DateTimeArr
331+
{
332+
public DateTime[] Value { get; set; }
333+
}
334+
174335
private class DateTimePropertyAttribute
175336
{
176337
[Timestamp]
@@ -200,5 +361,47 @@ private class DateTimeClassAttribute2
200361
{
201362
public DateTime Value;
202363
}
364+
365+
private class TimestampConverter : ITimestampConverter
366+
{
367+
/** <inheritdoc /> */
368+
public void ToJavaTicks(DateTime date, out long high, out int low)
369+
{
370+
if (date.Year == 1997 && date.Month == 8 && date.Day == 29)
371+
throw new BinaryObjectException(BinaryDateTimeTest.ToErrMsg);
372+
373+
BinaryUtils.ToJavaDate(date, out high, out low);
374+
}
375+
376+
/** <inheritdoc /> */
377+
public DateTime FromJavaTicks(long high, int low)
378+
{
379+
var date = new DateTime(BinaryUtils.JavaDateTicks + high * TimeSpan.TicksPerMillisecond + low / 100,
380+
DateTimeKind.Utc);
381+
382+
if (date.Year == 1997 && date.Month == 8 && date.Day == 4)
383+
throw new BinaryObjectException(BinaryDateTimeTest.FromErrMsg);
384+
385+
return date;
386+
}
387+
}
388+
389+
private class AddYearTimestampConverter : ITimestampConverter
390+
{
391+
/** <inheritdoc /> */
392+
public void ToJavaTicks(DateTime date, out long high, out int low)
393+
{
394+
BinaryUtils.ToJavaDate(date.AddYears(1), out high, out low);
395+
}
396+
397+
/** <inheritdoc /> */
398+
public DateTime FromJavaTicks(long high, int low)
399+
{
400+
var date = new DateTime(BinaryUtils.JavaDateTicks + high * TimeSpan.TicksPerMillisecond + low / 100,
401+
DateTimeKind.Utc);
402+
403+
return date.AddYears(1);
404+
}
405+
}
203406
}
204407
}

modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ public interface IJavaService
186186
/** */
187187
void testUTCDateFromCache();
188188

189+
/** */
190+
void testLocalDateFromCache();
191+
189192
/** */
190193
void sleep(long delayMs);
191194
}

modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,12 @@ public void testUTCDateFromCache()
349349
_svc.testDateFromCache();
350350
}
351351

352+
/** <inheritDoc /> */
353+
public void testLocalDateFromCache()
354+
{
355+
_svc.testLocalDateFromCache();
356+
}
357+
352358
/** <inheritDoc /> */
353359
public void sleep(long delayMs)
354360
{

0 commit comments

Comments
 (0)