Skip to content

Commit 88745d5

Browse files
committed
Support for translation of non-default DateTime and TimeSpan constants
1 parent f75a05d commit 88745d5

File tree

2 files changed

+244
-2
lines changed

2 files changed

+244
-2
lines changed

ReadableExpressions.UnitTests/WhenTranslatingConstants.cs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,36 @@ public void ShouldTranslateADefaultDate()
153153
Assert.AreEqual("default(DateTime)", translated);
154154
}
155155

156+
[TestMethod]
157+
public void ShouldTranslateADateTimeWithNoTime()
158+
{
159+
var dateConstant = Expression.Constant(new DateTime(2015, 07, 02));
160+
161+
var translated = dateConstant.ToReadableString();
162+
163+
Assert.AreEqual("new DateTime(2015, 07, 02)", translated);
164+
}
165+
166+
[TestMethod]
167+
public void ShouldTranslateADateTimeWithATime()
168+
{
169+
var dateConstant = Expression.Constant(new DateTime(2016, 08, 01, 10, 23, 45));
170+
171+
var translated = dateConstant.ToReadableString();
172+
173+
Assert.AreEqual("new DateTime(2016, 08, 01, 10, 23, 45)", translated);
174+
}
175+
176+
[TestMethod]
177+
public void ShouldTranslateADateTimeWithMilliseconds()
178+
{
179+
var dateConstant = Expression.Constant(new DateTime(2017, 01, 10, 00, 00, 00, 123));
180+
181+
var translated = dateConstant.ToReadableString();
182+
183+
Assert.AreEqual("new DateTime(2017, 01, 10, 00, 00, 00, 123)", translated);
184+
}
185+
156186
[TestMethod]
157187
public void ShouldTranslateADefaultTimeSpan()
158188
{
@@ -163,6 +193,96 @@ public void ShouldTranslateADefaultTimeSpan()
163193
Assert.AreEqual("default(TimeSpan)", translated);
164194
}
165195

196+
[TestMethod]
197+
public void ShouldTranslateATimeSpanOfDays()
198+
{
199+
var timeSpanConstant = Expression.Constant(TimeSpan.FromDays(1));
200+
201+
var translated = timeSpanConstant.ToReadableString();
202+
203+
Assert.AreEqual("TimeSpan.FromDays(1)", translated);
204+
}
205+
206+
[TestMethod]
207+
public void ShouldTranslateATimeSpanOfHours()
208+
{
209+
var timeSpanConstant = Expression.Constant(TimeSpan.FromHours(2));
210+
211+
var translated = timeSpanConstant.ToReadableString();
212+
213+
Assert.AreEqual("TimeSpan.FromHours(2)", translated);
214+
}
215+
216+
[TestMethod]
217+
public void ShouldTranslateATimeSpanOfMinutes()
218+
{
219+
var timeSpanConstant = Expression.Constant(TimeSpan.FromMinutes(10));
220+
221+
var translated = timeSpanConstant.ToReadableString();
222+
223+
Assert.AreEqual("TimeSpan.FromMinutes(10)", translated);
224+
}
225+
226+
[TestMethod]
227+
public void ShouldTranslateATimeSpanOfSeconds()
228+
{
229+
var timeSpanConstant = Expression.Constant(TimeSpan.FromSeconds(58));
230+
231+
var translated = timeSpanConstant.ToReadableString();
232+
233+
Assert.AreEqual("TimeSpan.FromSeconds(58)", translated);
234+
}
235+
236+
[TestMethod]
237+
public void ShouldTranslateATimeSpanOfMilliseconds()
238+
{
239+
var timeSpanConstant = Expression.Constant(TimeSpan.FromMilliseconds(923));
240+
241+
var translated = timeSpanConstant.ToReadableString();
242+
243+
Assert.AreEqual("TimeSpan.FromMilliseconds(923)", translated);
244+
}
245+
246+
[TestMethod]
247+
public void ShouldTranslateATimeSpanOfTicks()
248+
{
249+
var timeSpanConstant = Expression.Constant(TimeSpan.FromTicks(428));
250+
251+
var translated = timeSpanConstant.ToReadableString();
252+
253+
Assert.AreEqual("TimeSpan.FromTicks(428)", translated);
254+
}
255+
256+
[TestMethod]
257+
public void ShouldTranslateADayTimeSpanWithMilliseconds()
258+
{
259+
var timeSpanConstant = Expression.Constant(new TimeSpan(2, 3, 4, 5, 6));
260+
261+
var translated = timeSpanConstant.ToReadableString();
262+
263+
Assert.AreEqual("new TimeSpan(2, 3, 4, 5, 6)", translated);
264+
}
265+
266+
[TestMethod]
267+
public void ShouldTranslateADayTimeSpanWithoutMilliseconds()
268+
{
269+
var timeSpanConstant = Expression.Constant(new TimeSpan(3, 4, 5, 6));
270+
271+
var translated = timeSpanConstant.ToReadableString();
272+
273+
Assert.AreEqual("new TimeSpan(3, 4, 5, 6)", translated);
274+
}
275+
276+
[TestMethod]
277+
public void ShouldTranslateAnHourTimeSpan()
278+
{
279+
var timeSpanConstant = Expression.Constant(new TimeSpan(6, 5, 4));
280+
281+
var translated = timeSpanConstant.ToReadableString();
282+
283+
Assert.AreEqual("new TimeSpan(6, 5, 4)", translated);
284+
}
285+
166286
[TestMethod]
167287
public void ShouldTranslateADefaultString()
168288
{
@@ -234,6 +354,16 @@ public void ShouldTranslateDbNullValue()
234354

235355
Assert.AreEqual("param.Value = DBNull.Value", translated);
236356
}
357+
358+
[TestMethod]
359+
public void ShouldTranslateAnObjectConstant()
360+
{
361+
var objectConstant = Expression.Constant(123, typeof(object));
362+
363+
var translated = objectConstant.ToReadableString();
364+
365+
Assert.AreEqual("123", translated);
366+
}
237367
}
238368

239369
internal enum OddNumber

ReadableExpressions/Translators/ConstantExpressionTranslator.cs

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ namespace AgileObjects.ReadableExpressions.Translators
22
{
33
using System;
44
using System.Collections.Generic;
5+
using System.Globalization;
56
using System.Linq.Expressions;
67
using System.Text.RegularExpressions;
78
using Extensions;
@@ -54,7 +55,9 @@ private static bool TryTranslateByTypeCode(ConstantExpression constant, out stri
5455
{
5556
return true;
5657
}
57-
break;
58+
59+
translation = TranslateDateTime((DateTime)constant.Value);
60+
return true;
5861

5962
case (TypeCode)2:
6063
// TypeCode.DBNull (2) is unavailable in a PCL, but can
@@ -78,7 +81,7 @@ private static bool TryTranslateByTypeCode(ConstantExpression constant, out stri
7881
if (IsType(constant, out translation) ||
7982
IsFunc(constant, out translation) ||
8083
IsDefault<Guid>(constant, out translation) ||
81-
IsDefault<TimeSpan>(constant, out translation))
84+
IsTimeSpan(constant, out translation))
8285
{
8386
return true;
8487
}
@@ -98,6 +101,115 @@ private static bool TryTranslateByTypeCode(ConstantExpression constant, out stri
98101
return false;
99102
}
100103

104+
private static string TranslateDateTime(DateTime value)
105+
{
106+
var month = PadToTwoDigits(value.Month);
107+
var day = PadToTwoDigits(value.Day);
108+
var hour = PadToTwoDigits(value.Hour);
109+
var minute = PadToTwoDigits(value.Minute);
110+
var second = PadToTwoDigits(value.Second);
111+
112+
if (value.Millisecond != 0)
113+
{
114+
return $"new DateTime({value.Year}, {month}, {day}, {hour}, {minute}, {second}, {value.Millisecond})";
115+
}
116+
117+
if ((value.Hour == 0) && (value.Minute == 0) && (value.Second == 0))
118+
{
119+
return $"new DateTime({value.Year}, {month}, {day})";
120+
}
121+
122+
return $"new DateTime({value.Year}, {month}, {day}, {hour}, {minute}, {second})";
123+
}
124+
125+
private static string PadToTwoDigits(int value)
126+
{
127+
return value.ToString(CultureInfo.InvariantCulture).PadLeft(2, '0');
128+
}
129+
130+
private static bool IsTimeSpan(ConstantExpression constant, out string translation)
131+
{
132+
if (constant.Type != typeof(TimeSpan))
133+
{
134+
translation = null;
135+
return false;
136+
}
137+
138+
if (IsDefault<TimeSpan>(constant, out translation))
139+
{
140+
return true;
141+
}
142+
143+
var timeSpan = (TimeSpan)constant.Value;
144+
145+
if (TryGetFactoryMethodCall(timeSpan.Days, timeSpan.TotalDays, "Days", out translation))
146+
{
147+
return true;
148+
}
149+
150+
if (TryGetFactoryMethodCall(timeSpan.Hours, timeSpan.TotalHours, "Hours", out translation))
151+
{
152+
return true;
153+
}
154+
155+
if (TryGetFactoryMethodCall(timeSpan.Minutes, timeSpan.TotalMinutes, "Minutes", out translation))
156+
{
157+
return true;
158+
}
159+
160+
if (TryGetFactoryMethodCall(timeSpan.Seconds, timeSpan.TotalSeconds, "Seconds", out translation))
161+
{
162+
return true;
163+
}
164+
165+
if (TryGetFactoryMethodCall(timeSpan.Milliseconds, timeSpan.TotalMilliseconds, "Milliseconds", out translation))
166+
{
167+
return true;
168+
}
169+
170+
if (timeSpan.Days != 0)
171+
{
172+
if (timeSpan.Milliseconds != 0)
173+
{
174+
translation = $"new TimeSpan({timeSpan.Days}, {timeSpan.Hours}, {timeSpan.Minutes}, {timeSpan.Seconds}, {timeSpan.Milliseconds})";
175+
return true;
176+
}
177+
178+
translation = $"new TimeSpan({timeSpan.Days}, {timeSpan.Hours}, {timeSpan.Minutes}, {timeSpan.Seconds})";
179+
return true;
180+
}
181+
182+
if ((timeSpan.Hours > 0) || (timeSpan.Minutes > 0) || (timeSpan.Seconds > 0))
183+
{
184+
translation = $"new TimeSpan({timeSpan.Hours}, {timeSpan.Minutes}, {timeSpan.Seconds})";
185+
return true;
186+
}
187+
188+
translation = $"TimeSpan.FromTicks({Math.Floor(timeSpan.TotalMilliseconds * 10000)})";
189+
return true;
190+
}
191+
192+
private static bool TryGetFactoryMethodCall(
193+
long value,
194+
double totalValue,
195+
string valueName,
196+
out string translation)
197+
{
198+
if (value != 0)
199+
{
200+
// ReSharper disable CompareOfFloatsByEqualityOperator
201+
if (value == totalValue)
202+
{
203+
translation = "TimeSpan.From" + valueName + "(" + value + ")";
204+
return true;
205+
}
206+
// ReSharper restore CompareOfFloatsByEqualityOperator
207+
}
208+
209+
translation = null;
210+
return false;
211+
}
212+
101213
private static string FormatNumeric(decimal value)
102214
{
103215
return (value % 1).Equals(0) ? value.ToString("0") : value.ToString();

0 commit comments

Comments
 (0)