diff --git a/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java b/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java index cb3f8d7c91024..688b3b125452c 100644 --- a/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java +++ b/modules/core/src/test/java/org/apache/ignite/platform/PlatformDeployServiceTask.java @@ -18,6 +18,8 @@ package org.apache.ignite.platform; import java.sql.Timestamp; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; @@ -530,6 +532,24 @@ public void testUTCDateFromCache() { assertEquals(new Timestamp(new Date(91, Calendar.OCTOBER, 1, 0, 0, 0).getTime()), cache.get(2)); } + /** */ + public void testLocalDateFromCache() { + IgniteCache cache = ignite.cache("net-dates"); + + ZoneId msk = ZoneId.of("Europe/Moscow"); + + //This Date in Europe/Moscow have offset +4. + Timestamp ts1 = new Timestamp(ZonedDateTime.of(1982, 4, 1, 1, 0, 0, 0, msk).toInstant().toEpochMilli()); + //This Date in Europe/Moscow have offset +3. + Timestamp ts2 = new Timestamp(ZonedDateTime.of(1982, 3, 31, 22, 0, 0, 0, msk).toInstant().toEpochMilli()); + + assertEquals(ts1, cache.get(5)); + assertEquals(ts2, cache.get(6)); + + cache.put(7, ts1); + cache.put(8, ts2); + } + /** */ public void sleep(long delayMs) { try { diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryDateTimeTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryDateTimeTest.cs index e971eeac04b3c..9eae89307d24e 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryDateTimeTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryDateTimeTest.cs @@ -21,6 +21,7 @@ namespace Apache.Ignite.Core.Tests.Binary using System.Linq; using Apache.Ignite.Core.Binary; using Apache.Ignite.Core.Cache.Configuration; + using Apache.Ignite.Core.Impl.Binary; using NUnit.Framework; /// @@ -28,6 +29,16 @@ namespace Apache.Ignite.Core.Tests.Binary /// public class BinaryDateTimeTest { + /** */ + internal const String FromErrMsg = "FromJavaTicks Error!"; + + /** */ + internal const String ToErrMsg = "ToJavaTicks Error!"; + + /** */ + private const String NotUtcDate = + "DateTime is not UTC. Only UTC DateTime can be used for interop with other platforms."; + /// /// Sets up the test fixture. /// @@ -80,7 +91,7 @@ public void TestSerializerForceTimestamp() .Select(x => x.Serializer) .OfType() .Single(); - + Assert.IsTrue(ser.ForceTimestamp); AssertTimestampField((o, d) => o.Value = d, o => o.Value, "Value"); @@ -110,6 +121,152 @@ public void TestClassAttributes() AssertTimestampField((o, d) => o.Value = d, o => o.Value, "Value"); } + /// + /// Tests custom timestamp converter that modifies the values by adding one year on write and read. + /// This test verifies that actual converted values are used by Ignite. + /// + [Test] + public void TestAddYearTimestampConverter() + { + var cfg = new IgniteConfiguration(TestUtils.GetTestConfiguration()) + { + AutoGenerateIgniteInstanceName = true, + BinaryConfiguration = new BinaryConfiguration + { + ForceTimestamp = true, + TimestampConverter = new AddYearTimestampConverter() + } + }; + + var ignite = Ignition.Start(cfg); + + var dt = DateTime.UtcNow; + var expected = dt.AddYears(2); + + // Key & value. + var cache = ignite.GetOrCreateCache(TestUtils.TestName); + cache[dt] = dt; + + var resEntry = cache.Single(); + + Assert.AreEqual(expected, resEntry.Key); + Assert.AreEqual(expected, resEntry.Value); + + // Key & value array. + + // Object field. + var cache2 = ignite.GetOrCreateCache( + TestUtils.TestName); + + cache2.RemoveAll(); + + var obj = new DateTimePropertyAttribute {Value = dt}; + cache2[obj] = obj; + + var resEntry2 = cache2.Single(); + Assert.AreEqual(expected, resEntry2.Key.Value); + Assert.AreEqual(expected, resEntry2.Value.Value); + + // Object array field. + var cache3 = ignite.GetOrCreateCache(TestUtils.TestName); + cache3.RemoveAll(); + + var obj2 = new DateTimeArr {Value = new[]{dt}}; + cache3[obj2] = obj2; + cache3[obj2] = obj2; + + var resEntry3 = cache3.Single(); + Assert.AreEqual(expected, resEntry3.Key.Value.Single()); + Assert.AreEqual(expected, resEntry3.Value.Value.Single()); + } + + /// + /// Tests custom timestamp converter. + /// + [Test] + public void TestCustomTimestampConverter() + { + var cfg = new IgniteConfiguration(TestUtils.GetTestConfiguration(name: "ignite-1")) + { + BinaryConfiguration = new BinaryConfiguration + { + ForceTimestamp = true, + TimestampConverter = new TimestampConverter() + } + }; + + var ignite = Ignition.Start(cfg); + var binary = ignite.GetBinary(); + + // Check config. + Assert.NotNull(ignite.GetConfiguration().BinaryConfiguration.TimestampConverter); + + AssertTimestampField((o, d) => o.Value = d, o => o.Value, "Value", ignite); + + var dt1 = new DateTime(1997, 8, 29, 0, 0, 0, DateTimeKind.Utc); + + var ex = Assert.Throws(() => binary.ToBinary(dt1)); + Assert.AreEqual(ToErrMsg, ex.Message); + + ex = Assert.Throws(() => binary.ToBinary(new DateTime?[] {dt1})); + Assert.AreEqual(ToErrMsg, ex.Message); + + var dt2 = new DateTime(1997, 8, 4, 0, 0, 0, DateTimeKind.Utc); + + ex = Assert.Throws(() => binary.ToBinary(dt2)); + Assert.AreEqual(FromErrMsg, ex.Message); + + ex = Assert.Throws(() => binary.ToBinary(new DateTime?[] {dt2})); + Assert.AreEqual(FromErrMsg, ex.Message); + + var datesCache = ignite.CreateCache("dates"); + + var check = new Action((date1, date2, errMsg) => + { + ex = Assert.Throws(() => datesCache.Put(date1, date2), "Timestamp fields should throw an error on non-UTC values"); + + Assert.AreEqual(errMsg, ex.Message); + }); + + check.Invoke(DateTime.Now, DateTime.UtcNow, NotUtcDate); + check.Invoke(DateTime.UtcNow, DateTime.Now, NotUtcDate); + check.Invoke(dt1, DateTime.UtcNow, ToErrMsg); + check.Invoke(DateTime.UtcNow, dt1, ToErrMsg); + + var now = DateTime.UtcNow; + + datesCache.Put(now, dt2); + ex = Assert.Throws(() => datesCache.Get(now), "Timestamp fields should throw an error on non-UTC values"); + Assert.AreEqual(FromErrMsg, ex.Message); + + datesCache.Put(now, now); + Assert.AreEqual(now, datesCache.Get(now)); + + var datesObjCache = ignite.CreateCache("datesObj"); + + check = (date1, date2, errMsg) => + { + ex = Assert.Throws(() => datesObjCache.Put(new DateTimeObj {Value = date1}, new DateTimeObj {Value = date2}), + "Timestamp fields should throw an error on non-UTC values"); + + Assert.AreEqual(errMsg, ex.Message); + }; + + check.Invoke(DateTime.Now, DateTime.UtcNow, NotUtcDate); + check.Invoke(DateTime.UtcNow, DateTime.Now, NotUtcDate); + check.Invoke(dt1, DateTime.UtcNow, ToErrMsg); + check.Invoke(DateTime.UtcNow, dt1, ToErrMsg); + + var nowObj = new DateTimeObj {Value = now}; + + datesObjCache.Put(nowObj, new DateTimeObj {Value = dt2}); + ex = Assert.Throws(() => datesObjCache.Get(nowObj), "Timestamp fields should throw an error on non-UTC values"); + Assert.AreEqual(FromErrMsg, ex.Message); + + datesObjCache.Put(nowObj, nowObj); + Assert.AreEqual(nowObj.Value, datesObjCache.Get(nowObj).Value); + } + /// /// Asserts that specified field is serialized as DateTime object. /// @@ -136,10 +293,10 @@ private static void AssertDateTimeField(Action setValue, /// Asserts that specified field is serialized as Timestamp. /// private static void AssertTimestampField(Action setValue, - Func getValue, string fieldName) where T : new() + Func getValue, string fieldName, IIgnite ignite = null) where T : new() { // Non-UTC DateTime throws. - var binary = Ignition.GetIgnite().GetBinary(); + var binary = ignite != null ? ignite.GetBinary() : Ignition.GetIgnite().GetBinary(); var obj = new T(); @@ -148,8 +305,7 @@ private static void AssertTimestampField(Action setValue, var ex = Assert.Throws(() => binary.ToBinary(obj), "Timestamp fields should throw an error on non-UTC values"); - Assert.AreEqual("DateTime is not UTC. Only UTC DateTime can be used for interop with other platforms.", - ex.Message); + Assert.AreEqual(NotUtcDate, ex.Message); // UTC DateTime works. setValue(obj, DateTime.UtcNow); @@ -171,6 +327,11 @@ private class DateTimeObj2 public DateTime Value { get; set; } } + private class DateTimeArr + { + public DateTime[] Value { get; set; } + } + private class DateTimePropertyAttribute { [Timestamp] @@ -200,5 +361,47 @@ private class DateTimeClassAttribute2 { public DateTime Value; } + + private class TimestampConverter : ITimestampConverter + { + /** */ + public void ToJavaTicks(DateTime date, out long high, out int low) + { + if (date.Year == 1997 && date.Month == 8 && date.Day == 29) + throw new BinaryObjectException(BinaryDateTimeTest.ToErrMsg); + + BinaryUtils.ToJavaDate(date, out high, out low); + } + + /** */ + public DateTime FromJavaTicks(long high, int low) + { + var date = new DateTime(BinaryUtils.JavaDateTicks + high * TimeSpan.TicksPerMillisecond + low / 100, + DateTimeKind.Utc); + + if (date.Year == 1997 && date.Month == 8 && date.Day == 4) + throw new BinaryObjectException(BinaryDateTimeTest.FromErrMsg); + + return date; + } + } + + private class AddYearTimestampConverter : ITimestampConverter + { + /** */ + public void ToJavaTicks(DateTime date, out long high, out int low) + { + BinaryUtils.ToJavaDate(date.AddYears(1), out high, out low); + } + + /** */ + public DateTime FromJavaTicks(long high, int low) + { + var date = new DateTime(BinaryUtils.JavaDateTicks + high * TimeSpan.TicksPerMillisecond + low / 100, + DateTimeKind.Utc); + + return date.AddYears(1); + } + } } } diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs index dfcaaff51e38b..07db779b2c8c4 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/IJavaService.cs @@ -186,6 +186,9 @@ public interface IJavaService /** */ void testUTCDateFromCache(); + /** */ + void testLocalDateFromCache(); + /** */ void sleep(long delayMs); } diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs index 26c06373a44e0..41566020355e9 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/JavaServiceDynamicProxy.cs @@ -349,6 +349,12 @@ public void testUTCDateFromCache() _svc.testDateFromCache(); } + /** */ + public void testLocalDateFromCache() + { + _svc.testLocalDateFromCache(); + } + /** */ public void sleep(long delayMs) { diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs index b2748aec0eb0c..682f651c82914 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs @@ -27,6 +27,7 @@ namespace Apache.Ignite.Core.Tests.Services using Apache.Ignite.Core.Cluster; using Apache.Ignite.Core.Common; using Apache.Ignite.Core.Impl; + using Apache.Ignite.Core.Impl.Binary; using Apache.Ignite.Core.Resource; using Apache.Ignite.Core.Services; using NUnit.Framework; @@ -973,6 +974,28 @@ public void TestCallJavaService() Assert.AreEqual(dt1, cache.Get(3)); Assert.AreEqual(dt2, cache.Get(4)); +#if NETCOREAPP + //This Date in Europe/Moscow have offset +4. + DateTime dt3 = new DateTime(1982, 4, 1, 1, 0, 0, 0, DateTimeKind.Local); + //This Date in Europe/Moscow have offset +3. + DateTime dt4 = new DateTime(1982, 3, 31, 22, 0, 0, 0, DateTimeKind.Local); + + cache.Put(5, dt3); + cache.Put(6, dt4); + + Assert.AreEqual(dt3.ToUniversalTime(), cache.Get(5).ToUniversalTime()); + Assert.AreEqual(dt4.ToUniversalTime(), cache.Get(6).ToUniversalTime()); + + svc.testLocalDateFromCache(); + + Assert.AreEqual(dt3, cache.Get(7).ToLocalTime()); + Assert.AreEqual(dt4, cache.Get(8).ToLocalTime()); + + var now = DateTime.Now; + cache.Put(9, now); + Assert.AreEqual(now.ToUniversalTime(), cache.Get(9).ToUniversalTime()); +#endif + Services.Cancel(javaSvcName); } @@ -1157,6 +1180,9 @@ private IgniteConfiguration GetConfiguration(string springConfigUrl) { NameMapper = BinaryBasicNameMapper.SimpleNameInstance, ForceTimestamp = true +#if NETCOREAPP + , TimestampConverter = new TimestampConverter() +#endif } }; } @@ -1539,5 +1565,28 @@ public class PlatformComputeBinarizable2 /** */ public int Field { get; set; } } + +#if NETCOREAPP + /// + /// Adds support of the local dates to the Ignite timestamp serialization. + /// + class TimestampConverter : ITimestampConverter + { + /** */ + public void ToJavaTicks(DateTime date, out long high, out int low) + { + if (date.Kind == DateTimeKind.Local) + date = date.ToUniversalTime(); + + BinaryUtils.ToJavaDate(date, out high, out low); + } + + /** */ + public DateTime FromJavaTicks(long high, int low) + { + return new DateTime(BinaryUtils.JavaDateTicks + high * TimeSpan.TicksPerMillisecond + low / 100, DateTimeKind.Utc); + } + } +#endif } } diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj index c315f08166b5b..55488ceb6a663 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj @@ -52,6 +52,7 @@ + diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs index 2a2bb9579512e..b25a9f58420aa 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryConfiguration.cs @@ -79,6 +79,7 @@ internal void CopyLocalProperties(BinaryConfiguration cfg) NameMapper = cfg.NameMapper; KeepDeserialized = cfg.KeepDeserialized; ForceTimestamp = cfg.ForceTimestamp; + TimestampConverter = cfg.TimestampConverter; if (cfg.Serializer != null) { @@ -136,6 +137,15 @@ public BinaryConfiguration(params Type[] binaryTypes) /// public IBinarySerializer Serializer { get; set; } + /// + /// Gets or sets a converter between and Java Timestamp. + /// Called from , , + /// , . + /// + /// See also . + /// + public ITimestampConverter TimestampConverter { get; set; } + /// /// Default keep deserialized flag. /// @@ -164,7 +174,7 @@ public bool CompactFooter /// should be written as a Timestamp. /// /// Timestamp format is required for values used in SQL and for interoperation with other platforms. - /// Only UTC values are supported in Timestamp format. Other values will cause an exception on write. + /// Only UTC values are supported in Timestamp format. Other values will cause an exception on write, unless is provided. /// /// Normally Ignite serializer uses for DateTime fields, /// keys and values. diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/ITimestampConverter.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/ITimestampConverter.cs new file mode 100644 index 0000000000000..c7604793b9360 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/ITimestampConverter.cs @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Apache.Ignite.Core.Binary +{ + using System; + + /// + /// Converts values to Java Timestamp and back. + /// + public interface ITimestampConverter + { + /// Converts date to Java ticks. + /// Date + /// High part (milliseconds). + /// Low part (nanoseconds) + void ToJavaTicks(DateTime date, out long high, out int low); + + /// Converts date from Java ticks. + /// High part (milliseconds). + /// Low part (nanoseconds) + DateTime FromJavaTicks(long high, int low); + } +} diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd index 42aa56b691522..b61031818a2f5 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd +++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteClientConfigurationSection.xsd @@ -174,6 +174,18 @@ + + + Default date time converter. + + + + + Assembly-qualified type name. + + + + diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd index 4bb0fef2b491c..a840d99001c13 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd +++ b/modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd @@ -263,6 +263,18 @@ + + + Default date time converter. + + + + + Assembly-qualified type name. + + + + diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs index 9153046ece52b..c1a6fed02ddd2 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs @@ -315,25 +315,25 @@ public double[] ReadDoubleArray() /** */ public DateTime? ReadTimestamp(string fieldName) { - return ReadField(fieldName, BinaryUtils.ReadTimestamp, BinaryTypeId.Timestamp); + return ReadField(fieldName, stream => BinaryUtils.ReadTimestamp(stream, _marsh.TimestampConverter), BinaryTypeId.Timestamp); } /** */ public DateTime? ReadTimestamp() { - return Read(BinaryUtils.ReadTimestamp, BinaryTypeId.Timestamp); + return Read(stream => BinaryUtils.ReadTimestamp(stream, _marsh.TimestampConverter), BinaryTypeId.Timestamp); } /** */ public DateTime?[] ReadTimestampArray(string fieldName) { - return ReadField(fieldName, BinaryUtils.ReadTimestampArray, BinaryTypeId.ArrayTimestamp); + return ReadField(fieldName, stream => BinaryUtils.ReadTimestampArray(stream, _marsh.TimestampConverter), BinaryTypeId.ArrayTimestamp); } /** */ public DateTime?[] ReadTimestampArray() { - return Read(BinaryUtils.ReadTimestampArray, BinaryTypeId.ArrayTimestamp); + return Read(stream => BinaryUtils.ReadTimestampArray(stream, _marsh.TimestampConverter), BinaryTypeId.ArrayTimestamp); } /** */ diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs index 3ffb1f978b680..8fdf3716fe623 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs @@ -54,9 +54,6 @@ static BinarySystemHandlers() ReadHandlers[BinaryTypeId.Double] = new BinarySystemReader(s => s.ReadDouble()); ReadHandlers[BinaryTypeId.Decimal] = new BinarySystemReader(BinaryUtils.ReadDecimal); - // 2. Date. - ReadHandlers[BinaryTypeId.Timestamp] = new BinarySystemReader(BinaryUtils.ReadTimestamp); - // 3. String. ReadHandlers[BinaryTypeId.String] = new BinarySystemReader(BinaryUtils.ReadString); @@ -92,10 +89,6 @@ static BinarySystemHandlers() ReadHandlers[BinaryTypeId.ArrayDecimal] = new BinarySystemReader(BinaryUtils.ReadDecimalArray); - // 6. Date array. - ReadHandlers[BinaryTypeId.ArrayTimestamp] = - new BinarySystemReader(BinaryUtils.ReadTimestampArray); - // 7. String array. ReadHandlers[BinaryTypeId.ArrayString] = new BinarySystemTypedArrayReader(); @@ -258,6 +251,20 @@ public static bool TryReadSystemType(byte typeId, BinaryReader ctx, out T res if (handler == null) { + if (typeId == BinaryTypeId.Timestamp) + { + // Date. + res = TypeCaster.Cast(BinaryUtils.ReadTimestamp(ctx.Stream, ctx.Marshaller.TimestampConverter)); + return true; + } + + if (typeId == BinaryTypeId.ArrayTimestamp) + { + // Date array. + res = TypeCaster.Cast(BinaryUtils.ReadTimestampArray(ctx.Stream, ctx.Marshaller.TimestampConverter)); + return true; + } + res = default(T); return false; } @@ -311,7 +318,7 @@ private static void WriteTimestamp(BinaryWriter ctx, DateTime obj) { ctx.Stream.WriteByte(BinaryTypeId.Timestamp); - BinaryUtils.WriteTimestamp(obj, ctx.Stream); + BinaryUtils.WriteTimestamp(obj, ctx.Stream, ctx.Marshaller.TimestampConverter); } /// @@ -455,7 +462,7 @@ private static void WriteTimestampArray(BinaryWriter ctx, DateTime?[] obj) { ctx.Stream.WriteByte(BinaryTypeId.ArrayTimestamp); - BinaryUtils.WriteTimestampArray(obj, ctx.Stream); + BinaryUtils.WriteTimestampArray(obj, ctx.Stream, ctx.Marshaller.TimestampConverter); } /// diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs index 5b30c7ef69dbb..82e2284c32867 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs @@ -69,7 +69,7 @@ internal static class BinaryUtils public const int ObjTypeId = -1; /** Ticks for Java epoch. */ - private static readonly long JavaDateTicks = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).Ticks; + public static readonly long JavaDateTicks = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).Ticks; /** Binding flags for static search. */ private const BindingFlags BindFlagsStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; @@ -388,13 +388,17 @@ public static double[] ReadDoubleArray(IBinaryStream stream) * Write date. * Date. * Stream. + * Timestamp Converter. */ - public static void WriteTimestamp(DateTime val, IBinaryStream stream) + public static void WriteTimestamp(DateTime val, IBinaryStream stream, ITimestampConverter converter) { long high; int low; - ToJavaDate(val, out high, out low); + if (converter != null) + converter.ToJavaTicks(val, out high, out low); + else + ToJavaDate(val, out high, out low); stream.WriteLong(high); stream.WriteInt(low); @@ -403,14 +407,18 @@ public static void WriteTimestamp(DateTime val, IBinaryStream stream) /** * Read date. * Stream. + * Timestamp Converter. * Date */ - public static DateTime? ReadTimestamp(IBinaryStream stream) + public static DateTime? ReadTimestamp(IBinaryStream stream, ITimestampConverter converter) { long high = stream.ReadLong(); int low = stream.ReadInt(); - return new DateTime(JavaDateTicks + high * TimeSpan.TicksPerMillisecond + low / 100, DateTimeKind.Utc); + if (converter != null) + return converter.FromJavaTicks(high, low); + else + return new DateTime(JavaDateTicks + high * TimeSpan.TicksPerMillisecond + low / 100, DateTimeKind.Utc); } /// @@ -438,7 +446,8 @@ public static long DateTimeToJavaTicks(DateTime dateTime) /// /// Values. /// Stream. - public static void WriteTimestampArray(DateTime?[] vals, IBinaryStream stream) + /// Timestamp Converter. + public static void WriteTimestampArray(DateTime?[] vals, IBinaryStream stream, ITimestampConverter converter) { stream.WriteInt(vals.Length); @@ -448,7 +457,7 @@ public static void WriteTimestampArray(DateTime?[] vals, IBinaryStream stream) { stream.WriteByte(BinaryTypeId.Timestamp); - WriteTimestamp(val.Value, stream); + WriteTimestamp(val.Value, stream, converter); } else stream.WriteByte(HdrNull); @@ -1165,15 +1174,16 @@ public static T[] ReadArray(BinaryReader ctx, bool typed) /// Read timestamp array. /// /// Stream. + /// Timestamp Converter. /// Timestamp array. - public static DateTime?[] ReadTimestampArray(IBinaryStream stream) + public static DateTime?[] ReadTimestampArray(IBinaryStream stream, ITimestampConverter converter) { int len = stream.ReadInt(); DateTime?[] vals = new DateTime?[len]; for (int i = 0; i < len; i++) - vals[i] = stream.ReadByte() == HdrNull ? null : ReadTimestamp(stream); + vals[i] = stream.ReadByte() == HdrNull ? null : ReadTimestamp(stream, converter); return vals; } @@ -1612,7 +1622,7 @@ public static void ValidateProtocolVersion(byte version) * High part (milliseconds). * Low part (nanoseconds) */ - private static void ToJavaDate(DateTime date, out long high, out int low) + public static void ToJavaDate(DateTime date, out long high, out int low) { if (date.Kind != DateTimeKind.Utc) { diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs index 9025b4df2d51d..a39bcd7e066f0 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs @@ -657,7 +657,7 @@ public void WriteTimestamp(string fieldName, DateTime? val) else { _stream.WriteByte(BinaryTypeId.Timestamp); - BinaryUtils.WriteTimestamp(val.Value, _stream); + BinaryUtils.WriteTimestamp(val.Value, _stream, _marsh.TimestampConverter); } } @@ -672,7 +672,7 @@ public void WriteTimestamp(DateTime? val) else { _stream.WriteByte(BinaryTypeId.Timestamp); - BinaryUtils.WriteTimestamp(val.Value, _stream); + BinaryUtils.WriteTimestamp(val.Value, _stream, _marsh.TimestampConverter); } } @@ -690,7 +690,7 @@ public void WriteTimestampArray(string fieldName, DateTime?[] val) else { _stream.WriteByte(BinaryTypeId.ArrayTimestamp); - BinaryUtils.WriteTimestampArray(val, _stream); + BinaryUtils.WriteTimestampArray(val, _stream, _marsh.TimestampConverter); } } @@ -705,7 +705,7 @@ public void WriteTimestampArray(DateTime?[] val) else { _stream.WriteByte(BinaryTypeId.ArrayTimestamp); - BinaryUtils.WriteTimestampArray(val, _stream); + BinaryUtils.WriteTimestampArray(val, _stream, _marsh.TimestampConverter); } } diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs index cc9aeff992d2f..76b9cbcbaf2a9 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs @@ -144,6 +144,14 @@ public bool ForceTimestamp get { return _cfg.ForceTimestamp; } } + /// + /// Gets date time converter. + /// + public ITimestampConverter TimestampConverter + { + get { return _cfg.TimestampConverter; } + } + /// /// Marshal object. ///