From c5506397ab61cde24e06d00a96dfa4d47e35fce8 Mon Sep 17 00:00:00 2001
From: Amir Shitrit <amirchip@gmail.com>
Date: Sat, 16 Dec 2023 20:36:47 +0200
Subject: [PATCH 1/4] Change TimeStamp to a readonly struct record in order to
 avoid unnecessary allocations.

---
 src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs | 14 +++-----------
 src/NRedisStack/TimeSeries/TimeSeriesAux.cs       |  4 ++--
 2 files changed, 5 insertions(+), 13 deletions(-)

diff --git a/src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs b/src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs
index 86892ac3..3bbbe446 100644
--- a/src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs
+++ b/src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs
@@ -4,14 +4,14 @@
     /// A class represents timestamp.
     /// Value can be either primitive long, DateTime or one of the strings "-", "+", "*".
     /// </summary>
-    public class TimeStamp
+    public readonly record struct TimeStamp
     {
         private static readonly string[] constants = { "-", "+", "*" };
 
         /// <summary>
         /// TimeStamp value.
         /// </summary>
-        public object Value { get; private set; }
+        public object Value { get; }
 
         /// <summary>
         /// Build a TimeStamp from primitive long.
@@ -34,7 +34,7 @@ public TimeStamp(string timestamp)
         {
             if (Array.IndexOf(constants, timestamp) == -1)
             {
-                throw new NotSupportedException(string.Format("The string {0} cannot be used", timestamp));
+                throw new NotSupportedException($"The string {timestamp} cannot be used");
             }
             Value = timestamp;
         }
@@ -78,14 +78,6 @@ public static implicit operator long(TimeStamp ts) =>
         /// <param name="timeStamp">TimeStamp</param>
         public static implicit operator DateTime(TimeStamp timeStamp) => new DateTime(timeStamp);
 
-        /// <summary>
-        /// Equality of TimeSeriesTuple objects
-        /// </summary>
-        /// <param name="obj">Object to compare</param>
-        /// <returns>If two TimeStamp objects are equal</returns>
-        public override bool Equals(object? obj) =>
-            obj is TimeStamp stamp && EqualityComparer<object>.Default.Equals(Value, stamp.Value);
-
         /// <summary>
         /// TimeStamp object hash code.
         /// </summary>
diff --git a/src/NRedisStack/TimeSeries/TimeSeriesAux.cs b/src/NRedisStack/TimeSeries/TimeSeriesAux.cs
index b7dfb289..5589dbff 100644
--- a/src/NRedisStack/TimeSeries/TimeSeriesAux.cs
+++ b/src/NRedisStack/TimeSeries/TimeSeriesAux.cs
@@ -250,11 +250,11 @@ public static List<object> BuildTsAddArgs(string key, TimeStamp timestamp, doubl
             return args;
         }
 
-        public static List<object> BuildTsIncrDecrByArgs(string key, double value, TimeStamp? timestamp, long? retentionTime,
+        public static List<object> BuildTsIncrDecrByArgs(string key, double value, TimeStamp? timestampMaybe, long? retentionTime,
             IReadOnlyCollection<TimeSeriesLabel>? labels, bool? uncompressed, long? chunkSizeBytes)
         {
             var args = new List<object> { key, value };
-            if (timestamp != null) args.AddTimeStamp(timestamp);
+            if (timestampMaybe is {} timestamp) args.AddTimeStamp(timestamp);
             args.AddRetentionTime(retentionTime);
             args.AddChunkSize(chunkSizeBytes);
             if (labels != null) args.AddLabels(labels);

From 9583a4293f55439b95994d789350179e4257b9fc Mon Sep 17 00:00:00 2001
From: Amir Shitrit <amirchip@gmail.com>
Date: Sat, 16 Dec 2023 20:36:47 +0200
Subject: [PATCH 2/4] Change TimeStamp to a readonly struct record in order to
 avoid unnecessary allocations.

---
 src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs | 14 +++-----------
 src/NRedisStack/TimeSeries/TimeSeriesAux.cs       | 11 ++++-------
 2 files changed, 7 insertions(+), 18 deletions(-)

diff --git a/src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs b/src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs
index 86892ac3..3bbbe446 100644
--- a/src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs
+++ b/src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs
@@ -4,14 +4,14 @@
     /// A class represents timestamp.
     /// Value can be either primitive long, DateTime or one of the strings "-", "+", "*".
     /// </summary>
-    public class TimeStamp
+    public readonly record struct TimeStamp
     {
         private static readonly string[] constants = { "-", "+", "*" };
 
         /// <summary>
         /// TimeStamp value.
         /// </summary>
-        public object Value { get; private set; }
+        public object Value { get; }
 
         /// <summary>
         /// Build a TimeStamp from primitive long.
@@ -34,7 +34,7 @@ public TimeStamp(string timestamp)
         {
             if (Array.IndexOf(constants, timestamp) == -1)
             {
-                throw new NotSupportedException(string.Format("The string {0} cannot be used", timestamp));
+                throw new NotSupportedException($"The string {timestamp} cannot be used");
             }
             Value = timestamp;
         }
@@ -78,14 +78,6 @@ public static implicit operator long(TimeStamp ts) =>
         /// <param name="timeStamp">TimeStamp</param>
         public static implicit operator DateTime(TimeStamp timeStamp) => new DateTime(timeStamp);
 
-        /// <summary>
-        /// Equality of TimeSeriesTuple objects
-        /// </summary>
-        /// <param name="obj">Object to compare</param>
-        /// <returns>If two TimeStamp objects are equal</returns>
-        public override bool Equals(object? obj) =>
-            obj is TimeStamp stamp && EqualityComparer<object>.Default.Equals(Value, stamp.Value);
-
         /// <summary>
         /// TimeStamp object hash code.
         /// </summary>
diff --git a/src/NRedisStack/TimeSeries/TimeSeriesAux.cs b/src/NRedisStack/TimeSeries/TimeSeriesAux.cs
index b7dfb289..dd97c2c9 100644
--- a/src/NRedisStack/TimeSeries/TimeSeriesAux.cs
+++ b/src/NRedisStack/TimeSeries/TimeSeriesAux.cs
@@ -200,11 +200,8 @@ public static void AddGroupby(this IList<object> args, (string groupby, TsReduce
 
         public static void AddTimeStamp(this IList<object> args, TimeStamp timeStamp)
         {
-            if (timeStamp != null)
-            {
-                args.Add(TimeSeriesArgs.TIMESTAMP);
-                args.Add(timeStamp.Value);
-            }
+            args.Add(TimeSeriesArgs.TIMESTAMP);
+            args.Add(timeStamp.Value);
         }
 
         public static void AddRule(this IList<object> args, TimeSeriesRule rule)
@@ -250,11 +247,11 @@ public static List<object> BuildTsAddArgs(string key, TimeStamp timestamp, doubl
             return args;
         }
 
-        public static List<object> BuildTsIncrDecrByArgs(string key, double value, TimeStamp? timestamp, long? retentionTime,
+        public static List<object> BuildTsIncrDecrByArgs(string key, double value, TimeStamp? timestampMaybe, long? retentionTime,
             IReadOnlyCollection<TimeSeriesLabel>? labels, bool? uncompressed, long? chunkSizeBytes)
         {
             var args = new List<object> { key, value };
-            if (timestamp != null) args.AddTimeStamp(timestamp);
+            if (timestampMaybe is {} timestamp) args.AddTimeStamp(timestamp);
             args.AddRetentionTime(retentionTime);
             args.AddChunkSize(chunkSizeBytes);
             if (labels != null) args.AddLabels(labels);

From 1c3862690d98b8a32c9a12ed0788e77b8ccf67e7 Mon Sep 17 00:00:00 2001
From: Amir Shitrit <amirchip@gmail.com>
Date: Sat, 16 Dec 2023 21:42:11 +0200
Subject: [PATCH 3/4] reverting to origin

---
 src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs | 14 +++++++++++---
 src/NRedisStack/TimeSeries/TimeSeriesAux.cs       | 15 +++++++++------
 2 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs b/src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs
index 3bbbe446..86892ac3 100644
--- a/src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs
+++ b/src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs
@@ -4,14 +4,14 @@
     /// A class represents timestamp.
     /// Value can be either primitive long, DateTime or one of the strings "-", "+", "*".
     /// </summary>
-    public readonly record struct TimeStamp
+    public class TimeStamp
     {
         private static readonly string[] constants = { "-", "+", "*" };
 
         /// <summary>
         /// TimeStamp value.
         /// </summary>
-        public object Value { get; }
+        public object Value { get; private set; }
 
         /// <summary>
         /// Build a TimeStamp from primitive long.
@@ -34,7 +34,7 @@ public TimeStamp(string timestamp)
         {
             if (Array.IndexOf(constants, timestamp) == -1)
             {
-                throw new NotSupportedException($"The string {timestamp} cannot be used");
+                throw new NotSupportedException(string.Format("The string {0} cannot be used", timestamp));
             }
             Value = timestamp;
         }
@@ -78,6 +78,14 @@ public static implicit operator long(TimeStamp ts) =>
         /// <param name="timeStamp">TimeStamp</param>
         public static implicit operator DateTime(TimeStamp timeStamp) => new DateTime(timeStamp);
 
+        /// <summary>
+        /// Equality of TimeSeriesTuple objects
+        /// </summary>
+        /// <param name="obj">Object to compare</param>
+        /// <returns>If two TimeStamp objects are equal</returns>
+        public override bool Equals(object? obj) =>
+            obj is TimeStamp stamp && EqualityComparer<object>.Default.Equals(Value, stamp.Value);
+
         /// <summary>
         /// TimeStamp object hash code.
         /// </summary>
diff --git a/src/NRedisStack/TimeSeries/TimeSeriesAux.cs b/src/NRedisStack/TimeSeries/TimeSeriesAux.cs
index 2cf7bb28..b7dfb289 100644
--- a/src/NRedisStack/TimeSeries/TimeSeriesAux.cs
+++ b/src/NRedisStack/TimeSeries/TimeSeriesAux.cs
@@ -79,9 +79,9 @@ public static void AddOnDuplicate(this IList<object> args, TsDuplicatePolicy? po
             }
         }
 
-        public static void AddAlign(this IList<object> args, TimeStamp? alignMaybe)
+        public static void AddAlign(this IList<object> args, TimeStamp? align)
         {
-            if (alignMaybe is {} align)
+            if (align != null)
             {
                 args.Add(TimeSeriesArgs.ALIGN);
                 args.Add(align.Value);
@@ -200,8 +200,11 @@ public static void AddGroupby(this IList<object> args, (string groupby, TsReduce
 
         public static void AddTimeStamp(this IList<object> args, TimeStamp timeStamp)
         {
-            args.Add(TimeSeriesArgs.TIMESTAMP);
-            args.Add(timeStamp.Value);
+            if (timeStamp != null)
+            {
+                args.Add(TimeSeriesArgs.TIMESTAMP);
+                args.Add(timeStamp.Value);
+            }
         }
 
         public static void AddRule(this IList<object> args, TimeSeriesRule rule)
@@ -247,11 +250,11 @@ public static List<object> BuildTsAddArgs(string key, TimeStamp timestamp, doubl
             return args;
         }
 
-        public static List<object> BuildTsIncrDecrByArgs(string key, double value, TimeStamp? timestampMaybe, long? retentionTime,
+        public static List<object> BuildTsIncrDecrByArgs(string key, double value, TimeStamp? timestamp, long? retentionTime,
             IReadOnlyCollection<TimeSeriesLabel>? labels, bool? uncompressed, long? chunkSizeBytes)
         {
             var args = new List<object> { key, value };
-            if (timestampMaybe is {} timestamp) args.AddTimeStamp(timestamp);
+            if (timestamp != null) args.AddTimeStamp(timestamp);
             args.AddRetentionTime(retentionTime);
             args.AddChunkSize(chunkSizeBytes);
             if (labels != null) args.AddLabels(labels);

From a033998b80e96644a4ada64a84326f392bac3fba Mon Sep 17 00:00:00 2001
From: Amir Shitrit <amirchip@gmail.com>
Date: Sat, 16 Dec 2023 21:43:20 +0200
Subject: [PATCH 4/4] Convert TimeStamp to DateTime using Unix milliseconds
 instead of ticks

---
 src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs b/src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs
index 86892ac3..5da7dc17 100644
--- a/src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs
+++ b/src/NRedisStack/TimeSeries/DataTypes/TimeStamp.cs
@@ -76,7 +76,7 @@ public static implicit operator long(TimeStamp ts) =>
         /// Implicit cast from TimeStamp to DateTime.
         /// </summary>
         /// <param name="timeStamp">TimeStamp</param>
-        public static implicit operator DateTime(TimeStamp timeStamp) => new DateTime(timeStamp);
+        public static implicit operator DateTime(TimeStamp timeStamp) => DateTimeOffset.FromUnixTimeMilliseconds(timeStamp).DateTime;
 
         /// <summary>
         /// Equality of TimeSeriesTuple objects