Merge remote-tracking branch 'origin/dev' into feat/ProgramDataPath

This commit is contained in:
Drew Tingen
2019-09-16 14:05:48 -04:00
28 changed files with 565 additions and 265 deletions

View File

@@ -6,8 +6,30 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased] ## [Unreleased]
### Added
- Added a method for converting 24 hour to 12 hour format
- Added a method for determining if a culture uses 24 hour format
- Added math util method for modulus
- Added TimeSpan extension methods for cycling hours and minutes without modifying the day
- Added a dictionary extension method for getting or adding a new value via func
- Added CultureInfo extensions for converting between 12 hour and 24 hour time formatting
- Added environment methods for setting the current date and time
- Added BinarySearch extension method for all IList types
### Changed
- The Root Config path in Net Standard will now be the ICD.Connect folder in the current environments ProgramData directory
- Fixed a bug where CultureInfo was not being cloned correctly
- List AddSorted extensions now work for all IList types
## [9.8.0] - 2019-09-03
### Added
- Added Public API Properties to get the program install date based on the creation date of core dll file for NetStandard and SimplSharp
- Implemented processor utils for NetStandard to get the system uptime and the program uptime
- Added methods for deserializing an XML array
### Changed ### Changed
- Fixed a bug where ANSI color encoded strings with percentages were being scrambled - Fixed a bug where ANSI color encoded strings with percentages were being scrambled
- Improvements to JSON DateTime parsing, particularly in Net Standard
## [9.7.0] - 2019-08-15 ## [9.7.0] - 2019-08-15
### Added ### Added

View File

@@ -0,0 +1,17 @@
using NUnit.Framework;
namespace ICD.Common.Utils.Tests
{
[TestFixture]
public sealed class DateTimeUtilsTest
{
[TestCase(1, 1)]
[TestCase(0, 12)]
[TestCase(12, 12)]
[TestCase(23, 11)]
public void To12HourTest(int hour, int expected)
{
Assert.AreEqual(expected, DateTimeUtils.To12Hour(hour));
}
}
}

View File

@@ -3,16 +3,16 @@
namespace ICD.Common.Utils.Tests.Extensions namespace ICD.Common.Utils.Tests.Extensions
{ {
[TestFixture] [TestFixture]
public sealed class DateTimeExtensionsTest public sealed class DateTimeExtensionsTest
{ {
[Test] [Test]
public static void ToShortTimeStringTest() public void ToShortTimeStringTest()
{ {
Assert.Inconclusive(); Assert.Inconclusive();
} }
[Test] [Test]
public static void ToLongTimeStringWithMillisecondsTest() public void ToLongTimeStringWithMillisecondsTest()
{ {
Assert.Inconclusive(); Assert.Inconclusive();
} }

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using ICD.Common.Utils.Extensions; using ICD.Common.Utils.Extensions;
using NUnit.Framework; using NUnit.Framework;
@@ -43,82 +42,6 @@ namespace ICD.Common.Utils.Tests.Extensions
Assert.AreEqual(1, testList[3]); Assert.AreEqual(1, testList[3]);
} }
[Test]
public void PadRightTest()
{
List<int> testList = new List<int>();
testList.PadRight(10);
Assert.AreEqual(10, testList.Count);
Assert.AreEqual(0, testList.Sum());
testList = new List<int>
{
1,
2,
3,
4,
5
};
testList.PadRight(10);
Assert.AreEqual(10, testList.Count);
Assert.AreEqual(15, testList.Sum());
testList = new List<int>
{
1,
2,
3,
4,
5
};
testList.PadRight(1);
Assert.AreEqual(5, testList.Count);
Assert.AreEqual(15, testList.Sum());
}
[Test]
public void PadRightDefaultTest()
{
List<int> testList = new List<int>();
testList.PadRight(10, 1);
Assert.AreEqual(10, testList.Count);
Assert.AreEqual(10, testList.Sum());
testList = new List<int>
{
1,
2,
3,
4,
5
};
testList.PadRight(10, 1);
Assert.AreEqual(10, testList.Count);
Assert.AreEqual(20, testList.Sum());
testList = new List<int>
{
1,
2,
3,
4,
5
};
testList.PadRight(1, 1);
Assert.AreEqual(5, testList.Count);
Assert.AreEqual(15, testList.Sum());
}
private sealed class InverseComparer : IComparer<int> private sealed class InverseComparer : IComparer<int>
{ {
public int Compare(int x, int y) public int Compare(int x, int y)

View File

@@ -0,0 +1,43 @@
using System;
using ICD.Common.Utils.Extensions;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Extensions
{
[TestFixture]
public sealed class TimeSpanExtensionsTest
{
[Test]
public void ToReadableStringTest()
{
Assert.Inconclusive();
}
[TestCase(0, 0, 0)]
[TestCase(12, 1, 13)]
[TestCase(23, 1, 0)]
[TestCase(6, -12, 18)]
public void AddHoursAndWrapTest(int hours, int addHours, int expectedHours)
{
Assert.AreEqual(expectedHours, new TimeSpan(hours, 0, 0).AddHoursAndWrap(addHours).Hours);
}
[TestCase(0, 0, 0)]
[TestCase(12, 1, 13)]
[TestCase(23, 1, 12)]
[TestCase(6, -12, 6)]
public void AddHoursAndWrap12Hour(int hours, int addHours, int expectedHours)
{
Assert.AreEqual(expectedHours, new TimeSpan(hours, 0, 0).AddHoursAndWrap12Hour(addHours).Hours);
}
[TestCase(0, 0, 0)]
[TestCase(30, 1, 31)]
[TestCase(59, 1, 0)]
[TestCase(30, -60, 30)]
public void AddMinutesAndWrap(int minutes, int addMinutes, int expectedMinutes)
{
Assert.AreEqual(expectedMinutes, new TimeSpan(0, minutes, 0).AddMinutesAndWrap(addMinutes).Minutes);
}
}
}

View File

@@ -22,7 +22,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.14.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -10,29 +10,7 @@ namespace ICD.Common.Utils.Tests.Json
public sealed class JsonUtilsTest public sealed class JsonUtilsTest
{ {
[Test] [Test]
public void CacheTypeTest() public void FormatTest()
{
Assert.Inconclusive();
}
[Test]
public void ParseDateTimeTest()
{
const string dataA = "2016-02-26T19:24:59";
const string dataB = "2019-04-01T12:41:15-04:00";
Assert.DoesNotThrow(() => JsonUtils.ParseDateTime(dataA));
Assert.DoesNotThrow(() => JsonUtils.ParseDateTime(dataB));
}
[Test]
public void TryParseDateTimeTest()
{
Assert.Inconclusive();
}
[Test]
public void PrintTest()
{ {
Assert.Inconclusive(); Assert.Inconclusive();
} }

View File

@@ -1,14 +1,13 @@
using NUnit.Framework; using NUnit.Framework;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using ICD.Common.Properties;
namespace ICD.Common.Utils.Tests namespace ICD.Common.Utils.Tests
{ {
[TestFixture, UsedImplicitly] [TestFixture]
public sealed class MathUtilsTest public sealed class MathUtilsTest
{ {
[Test, UsedImplicitly] [Test]
public void ClampTest() public void ClampTest()
{ {
Assert.AreEqual(MathUtils.Clamp(-10, 0, 0), 0); Assert.AreEqual(MathUtils.Clamp(-10, 0, 0), 0);
@@ -24,7 +23,7 @@ namespace ICD.Common.Utils.Tests
Assert.AreEqual(MathUtils.Clamp(20, 10, 10), 10); Assert.AreEqual(MathUtils.Clamp(20, 10, 10), 10);
} }
[Test, UsedImplicitly] [Test]
public void MapRangeTest() public void MapRangeTest()
{ {
Assert.AreEqual(5, MathUtils.MapRange(-100, 100, 0, 10, 0)); Assert.AreEqual(5, MathUtils.MapRange(-100, 100, 0, 10, 0));
@@ -40,7 +39,7 @@ namespace ICD.Common.Utils.Tests
Assert.AreEqual(100, MathUtils.MapRange(0, 10, 0, 100, 10)); Assert.AreEqual(100, MathUtils.MapRange(0, 10, 0, 100, 10));
} }
[Test, UsedImplicitly] [Test]
public void GetRangesTest() public void GetRangesTest()
{ {
IEnumerable<int> values = new [] { 1, 3, 5, 6, 7, 8, 9, 10, 12 }; IEnumerable<int> values = new [] { 1, 3, 5, 6, 7, 8, 9, 10, 12 };
@@ -61,11 +60,21 @@ namespace ICD.Common.Utils.Tests
Assert.AreEqual(12, ranges[3][1]); Assert.AreEqual(12, ranges[3][1]);
} }
[Test, UsedImplicitly] [Test]
public void RoundToNearestTest() public void RoundToNearestTest()
{ {
IEnumerable<int> values = new [] { 0, 15, 30, 45 }; IEnumerable<int> values = new [] { 0, 15, 30, 45 };
Assert.AreEqual(15, MathUtils.RoundToNearest(21, values)); Assert.AreEqual(15, MathUtils.RoundToNearest(21, values));
} }
[TestCase(10, 10, 0)]
[TestCase(-10, 10, 0)]
[TestCase(9, 3, 0)]
[TestCase(3, 2, 1)]
[TestCase(-3, 2, 1)]
public void ModulusTest(int value, int mod, int expected)
{
Assert.AreEqual(expected, MathUtils.Modulus(value, mod));
}
} }
} }

View File

@@ -0,0 +1,35 @@
using System.Linq;
using ICD.Common.Utils.Xml;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Xml
{
[TestFixture]
public sealed class IcdXmlConvertTest
{
[Test]
public void DeserializeArrayGenericTest()
{
const string xml = @"<A>
<B>1</B>
<B>2</B>
</A>";
using (IcdXmlReader reader = new IcdXmlReader(xml))
{
// Read to the first element
reader.Read();
int[] values = IcdXmlConvert.DeserializeArray<int>(reader).ToArray();
Assert.AreEqual(new[] {1, 2}, values);
}
}
[Test]
public void DeserializeArrayTest()
{
Assert.Inconclusive();
}
}
}

View File

@@ -0,0 +1,15 @@
namespace ICD.Common.Utils
{
public static class DateTimeUtils
{
/// <summary>
/// Converts the hour in 24 hour format to 12 hour format (1 through 12).
/// </summary>
/// <param name="hour"></param>
/// <returns></returns>
public static int To12Hour(int hour)
{
return MathUtils.Modulus(hour + 11, 12) + 1;
}
}
}

View File

@@ -0,0 +1,90 @@
using System;
using System.Globalization;
namespace ICD.Common.Utils.Extensions
{
public static class CultureInfoExtensions
{
/// <summary>
/// Returns true if the given culture uses a 24 hour time format.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static bool Uses24HourFormat(this CultureInfo extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.DateTimeFormat.ShortTimePattern.Contains("H");
}
/// <summary>
/// Updates the time patterns for the given culture to use 12 hour time.
/// </summary>
/// <param name="extends"></param>
public static void ConvertTo12HourCulture(this CultureInfo extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (extends.IsReadOnly)
throw new InvalidOperationException("Culture is readonly");
DateTimeFormatInfo dateTimeFormat = extends.DateTimeFormat;
dateTimeFormat.FullDateTimePattern = To12HourPattern(dateTimeFormat.FullDateTimePattern);
dateTimeFormat.LongTimePattern = To12HourPattern(dateTimeFormat.LongTimePattern);
dateTimeFormat.ShortTimePattern = To12HourPattern(dateTimeFormat.ShortTimePattern);
}
/// <summary>
/// Updates the time patterns for the given culture to use 24 hour time.
/// </summary>
/// <param name="extends"></param>
public static void ConvertTo24HourCulture(this CultureInfo extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (extends.IsReadOnly)
throw new InvalidOperationException("Culture is readonly");
DateTimeFormatInfo dateTimeFormat = extends.DateTimeFormat;
dateTimeFormat.FullDateTimePattern = To24HourPattern(dateTimeFormat.FullDateTimePattern);
dateTimeFormat.LongTimePattern = To24HourPattern(dateTimeFormat.LongTimePattern);
dateTimeFormat.ShortTimePattern = To24HourPattern(dateTimeFormat.ShortTimePattern);
}
/// <summary>
/// Converts the given format pattern to use 24 hour time.
/// </summary>
/// <param name="pattern"></param>
/// <returns></returns>
private static string To24HourPattern(string pattern)
{
if (pattern == null)
return null;
pattern = pattern.Replace(" tt", "");
return pattern.Replace("h", "H");
}
/// <summary>
/// Converts the given format pattern to use 12 hour time.
/// </summary>
/// <param name="pattern"></param>
/// <returns></returns>
private static string To12HourPattern(string pattern)
{
if (pattern == null)
return null;
if (!pattern.Contains("t"))
pattern = pattern + " tt";
return pattern.Replace("H", "h");
}
}
}

View File

@@ -147,14 +147,38 @@ namespace ICD.Common.Utils.Extensions
if (extends == null) if (extends == null)
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");
// ReSharper disable once CompareNonConstrainedGenericWithNull // ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null) if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
return extends.GetOrAddNew(key, () => ReflectionUtils.CreateInstance<TValue>());
}
/// <summary>
/// If the key is present in the dictionary return the value, otherwise add a new value to the dictionary and return it.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="extends"></param>
/// <param name="key"></param>
/// <param name="valueFunc"></param>
/// <returns></returns>
[PublicAPI]
public static TValue GetOrAddNew<TKey, TValue>(this IDictionary<TKey, TValue> extends, TKey key, Func<TValue> valueFunc)
{
if (extends == null)
throw new ArgumentNullException("extends");
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key"); throw new ArgumentNullException("key");
TValue value; TValue value;
if (!extends.TryGetValue(key, out value)) if (!extends.TryGetValue(key, out value))
{ {
value = ReflectionUtils.CreateInstance<TValue>(); value = valueFunc();
extends.Add(key, value); extends.Add(key, value);
} }

View File

@@ -1,6 +1,5 @@
using System; using System;
using ICD.Common.Properties; using ICD.Common.Properties;
using ICD.Common.Utils.Json;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace ICD.Common.Utils.Extensions namespace ICD.Common.Utils.Extensions
@@ -21,7 +20,12 @@ namespace ICD.Common.Utils.Extensions
if (extends == null) if (extends == null)
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");
JsonSerializer serializer = new JsonSerializer(); JsonSerializer serializer =
#if SIMPLSHARP
new JsonSerializer();
#else
JsonSerializer.CreateDefault();
#endif
return extends.ReadAsObject<T>(serializer); return extends.ReadAsObject<T>(serializer);
} }
@@ -145,7 +149,13 @@ namespace ICD.Common.Utils.Extensions
{ {
if (extends == null) if (extends == null)
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");
#if !SIMPLSHARP
// Newer versions of NewtonSoft try to be helpful and interpret strings as DateTimes without any consideration for different DateTime formats.
if (extends.TokenType == JsonToken.Date && extends.DateParseHandling != DateParseHandling.None)
throw new InvalidOperationException("DateParseHandling needs to be set to None");
#endif
if (!extends.TokenType.IsPrimitive()) if (!extends.TokenType.IsPrimitive())
throw new FormatException("Expected primitive token type but got " + extends.TokenType); throw new FormatException("Expected primitive token type but got " + extends.TokenType);
@@ -213,12 +223,36 @@ namespace ICD.Common.Utils.Extensions
if (extends == null) if (extends == null)
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");
#if SIMPLSHARP #if !SIMPLSHARP
string stringValue = extends.GetValueAsString(); // Newer NewtonSoft tries to be helpful by assuming that anything that looks like a DateTime must be a date.
return JsonUtils.ParseDateTime(stringValue); if (extends.DateParseHandling != DateParseHandling.None)
#else return (DateTime)extends.Value;
return (DateTime)extends.Value;
#endif #endif
string stringValue = extends.GetValueAsString();
return DateTime.Parse(stringValue);
}
/// <summary>
/// Gets the current value as a date.
/// </summary>
/// <param name="extends"></param>
/// <param name="format"></param>
/// <param name="provider"></param>
/// <returns></returns>
public static DateTime GetValueAsDateTimeExact(this JsonReader extends, string format, IFormatProvider provider)
{
if (extends == null)
throw new ArgumentNullException("extends");
#if !SIMPLSHARP
// Newer NewtonSoft tries to be helpful by assuming that anything that looks like a DateTime must be a date.
if (extends.DateParseHandling != DateParseHandling.None)
throw new InvalidOperationException("DateParseHandling needs to be set to None");
#endif
string stringValue = extends.GetValueAsString();
return DateTime.ParseExact(stringValue, format, provider);
} }
} }
} }

View File

@@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties; using ICD.Common.Properties;
using ICD.Common.Utils.Comparers; using ICD.Common.Utils.Comparers;
@@ -18,7 +17,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param> /// <param name="extends"></param>
/// <param name="items"></param> /// <param name="items"></param>
[PublicAPI] [PublicAPI]
public static void AddSorted<T>(this List<T> extends, IEnumerable<T> items) public static void AddSorted<T>(this IList<T> extends, IEnumerable<T> items)
{ {
if (extends == null) if (extends == null)
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");
@@ -37,7 +36,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="items"></param> /// <param name="items"></param>
/// <param name="comparer"></param> /// <param name="comparer"></param>
[PublicAPI] [PublicAPI]
public static void AddSorted<T>(this List<T> extends, IEnumerable<T> items, IComparer<T> comparer) public static void AddSorted<T>(this IList<T> extends, IEnumerable<T> items, IComparer<T> comparer)
{ {
if (extends == null) if (extends == null)
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");
@@ -60,7 +59,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="items"></param> /// <param name="items"></param>
/// <param name="predicate"></param> /// <param name="predicate"></param>
[PublicAPI] [PublicAPI]
public static void AddSorted<T, TProp>(this List<T> extends, IEnumerable<T> items, Func<T, TProp> predicate) public static void AddSorted<T, TProp>(this IList<T> extends, IEnumerable<T> items, Func<T, TProp> predicate)
{ {
if (extends == null) if (extends == null)
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");
@@ -82,7 +81,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="extends"></param> /// <param name="extends"></param>
/// <param name="item"></param> /// <param name="item"></param>
[PublicAPI] [PublicAPI]
public static int AddSorted<T>(this List<T> extends, T item) public static int AddSorted<T>(this IList<T> extends, T item)
{ {
if (extends == null) if (extends == null)
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");
@@ -98,7 +97,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="item"></param> /// <param name="item"></param>
/// <param name="comparer"></param> /// <param name="comparer"></param>
[PublicAPI] [PublicAPI]
public static int AddSorted<T>(this List<T> extends, T item, IComparer<T> comparer) public static int AddSorted<T>(this IList<T> extends, T item, IComparer<T> comparer)
{ {
if (extends == null) if (extends == null)
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");
@@ -124,7 +123,7 @@ namespace ICD.Common.Utils.Extensions
/// <param name="item"></param> /// <param name="item"></param>
/// <param name="predicate"></param> /// <param name="predicate"></param>
[PublicAPI] [PublicAPI]
public static int AddSorted<T, TProp>(this List<T> extends, T item, Func<T, TProp> predicate) public static int AddSorted<T, TProp>(this IList<T> extends, T item, Func<T, TProp> predicate)
{ {
if (extends == null) if (extends == null)
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");
@@ -137,44 +136,40 @@ namespace ICD.Common.Utils.Extensions
} }
/// <summary> /// <summary>
/// Pads the list to the given total length. /// Returns the index of the item in the list.
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <param name="extends"></param> /// <param name="extends"></param>
/// <param name="totalLength"></param>
[PublicAPI]
public static void PadRight<T>(this List<T> extends, int totalLength)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (totalLength < 0)
throw new ArgumentOutOfRangeException("totalLength", "totalLength must be greater or equal to 0");
extends.PadRight(totalLength, default(T));
}
/// <summary>
/// Pads the list to the given total length with the given item.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="totalLength"></param>
/// <param name="item"></param> /// <param name="item"></param>
/// <param name="comparer"></param>
/// <returns></returns>
[PublicAPI] [PublicAPI]
public static void PadRight<T>(this List<T> extends, int totalLength, T item) public static int BinarySearch<T>(this IList<T> extends, T item, IComparer<T> comparer)
{ {
if (extends == null) if (extends == null)
throw new ArgumentNullException("extends"); throw new ArgumentNullException("extends");
if (totalLength < 0) if (comparer == null)
throw new ArgumentOutOfRangeException("totalLength", "totalLength must be greater or equal to 0"); throw new ArgumentNullException("comparer");
int pad = totalLength - extends.Count; int lo = 0;
if (pad <= 0) int hi = extends.Count - 1;
return;
extends.AddRange(Enumerable.Repeat(item, pad)); while (lo <= hi)
{
int i = lo + ((hi - lo) >> 1);
int order = comparer.Compare(extends[i], item);
if (order == 0)
return i;
if (order < 0)
lo = i + 1;
else
hi = i - 1;
}
return ~lo;
} }
} }
} }

View File

@@ -25,5 +25,48 @@ namespace ICD.Common.Utils.Extensions
return builder.ToString(); return builder.ToString();
} }
/// <summary>
/// Adds the given number of hours to the time, wrapping every 24 hours without modifying the day.
/// </summary>
/// <param name="extends"></param>
/// <param name="hours"></param>
/// <returns></returns>
public static TimeSpan AddHoursAndWrap(this TimeSpan extends, int hours)
{
hours = MathUtils.Modulus(hours + extends.Hours, 24);
return new TimeSpan(extends.Days, hours, extends.Minutes, extends.Seconds, extends.Milliseconds);
}
/// <summary>
/// Adds the given number of hours to the time, wrapping within the current 12 hour span, without modifying the day.
/// </summary>
/// <param name="extends"></param>
/// <param name="hours"></param>
/// <returns></returns>
public static TimeSpan AddHoursAndWrap12Hour(this TimeSpan extends, int hours)
{
int currentHour = extends.Hours;
bool am = extends.Hours < 12;
int current12Hour = MathUtils.Modulus(currentHour, 12);
int new12Hour = MathUtils.Modulus(current12Hour + hours, 12);
return am
? new TimeSpan(extends.Days, new12Hour, extends.Minutes, extends.Seconds, extends.Milliseconds)
: new TimeSpan(extends.Days, new12Hour + 12, extends.Minutes, extends.Seconds, extends.Milliseconds);
}
/// <summary>
/// Adds the given number of minutes to the time, wrapping every 60 minutes without modifying the hour.
/// </summary>
/// <param name="extends"></param>
/// <param name="minutes"></param>
/// <returns></returns>
public static TimeSpan AddMinutesAndWrap(this TimeSpan extends, int minutes)
{
minutes = MathUtils.Modulus(minutes + extends.Minutes, 60);
return new TimeSpan(extends.Days, extends.Hours, minutes, extends.Seconds, extends.Milliseconds);
}
} }
} }

View File

@@ -204,16 +204,17 @@ namespace ICD.Common.Utils.Globalization
{ {
get get
{ {
if (m_IsResident)
return base.DateTimeFormat;
ThrowIfNeutralCulture(this);
if (m_DatetimeFormat == null) if (m_DatetimeFormat == null)
{ {
if (m_IsResident)
return base.DateTimeFormat;
ThrowIfNeutralCulture(this);
DateTimeFormatInfo dateTimeFormatInfo = GetDateTimeFormat(m_DatetimeFormatId); DateTimeFormatInfo dateTimeFormatInfo = GetDateTimeFormat(m_DatetimeFormatId);
if (IsReadOnly) if (IsReadOnly)
dateTimeFormatInfo = DateTimeFormatInfo.ReadOnly(dateTimeFormatInfo); dateTimeFormatInfo = DateTimeFormatInfo.ReadOnly(dateTimeFormatInfo);
m_DatetimeFormat = dateTimeFormatInfo; m_DatetimeFormat = dateTimeFormatInfo;
} }
return m_DatetimeFormat; return m_DatetimeFormat;
@@ -221,13 +222,13 @@ namespace ICD.Common.Utils.Globalization
set set
{ {
if (m_IsResident) if (m_IsResident)
{
base.DateTimeFormat = value; base.DateTimeFormat = value;
return;
}
ThrowIfReadOnly(); ThrowIfReadOnly();
if (value == null) if (value == null)
throw new ArgumentException("value"); throw new ArgumentException("value");
m_DatetimeFormat = value; m_DatetimeFormat = value;
} }
} }
@@ -246,16 +247,17 @@ namespace ICD.Common.Utils.Globalization
{ {
get get
{ {
if (m_IsResident)
return base.NumberFormat;
ThrowIfNeutralCulture(this);
if (m_NumberFormat == null) if (m_NumberFormat == null)
{ {
if (m_IsResident)
return base.NumberFormat;
ThrowIfNeutralCulture(this);
NumberFormatInfo numberFormatInfo = GetNumberFormat(m_NumberFormatId); NumberFormatInfo numberFormatInfo = GetNumberFormat(m_NumberFormatId);
if (IsReadOnly) if (IsReadOnly)
numberFormatInfo = NumberFormatInfo.ReadOnly(numberFormatInfo); numberFormatInfo = NumberFormatInfo.ReadOnly(numberFormatInfo);
m_NumberFormat = numberFormatInfo; m_NumberFormat = numberFormatInfo;
} }
return m_NumberFormat; return m_NumberFormat;
@@ -263,13 +265,13 @@ namespace ICD.Common.Utils.Globalization
set set
{ {
if (m_IsResident) if (m_IsResident)
{
base.NumberFormat = value; base.NumberFormat = value;
return;
}
ThrowIfReadOnly(); ThrowIfReadOnly();
if (value == null) if (value == null)
throw new ArgumentException("value"); throw new ArgumentException("value");
m_NumberFormat = value; m_NumberFormat = value;
} }
} }
@@ -851,24 +853,25 @@ namespace ICD.Common.Utils.Globalization
try try
{ {
m_CompareInfo = ci.CompareInfo; m_CompareInfo = ci.CompareInfo;
goto IL_CE;
} }
catch (PlatformNotSupportedException) catch (PlatformNotSupportedException)
{ {
m_CompareInfo = CultureInfo.InvariantCulture.CompareInfo; m_CompareInfo = CultureInfo.InvariantCulture.CompareInfo;
goto IL_CE;
} }
} }
m_Calendar = icdCultureInfo.m_Calendar; else
m_CalendarType = icdCultureInfo.m_CalendarType; {
m_GregorianCalendarType = icdCultureInfo.m_GregorianCalendarType; m_Calendar = icdCultureInfo.m_Calendar;
m_OptionalCalendars = icdCultureInfo.m_OptionalCalendars; m_CalendarType = icdCultureInfo.m_CalendarType;
m_OptionalCalendarTypes = icdCultureInfo.m_OptionalCalendarTypes; m_GregorianCalendarType = icdCultureInfo.m_GregorianCalendarType;
m_OptionalGregorianCalendarTypes = icdCultureInfo.m_OptionalGregorianCalendarTypes; m_OptionalCalendars = icdCultureInfo.m_OptionalCalendars;
m_Parent = icdCultureInfo.m_Parent; m_OptionalCalendarTypes = icdCultureInfo.m_OptionalCalendarTypes;
m_TextInfo = icdCultureInfo.m_TextInfo; m_OptionalGregorianCalendarTypes = icdCultureInfo.m_OptionalGregorianCalendarTypes;
m_CompareInfo = icdCultureInfo.m_CompareInfo; m_Parent = icdCultureInfo.m_Parent;
IL_CE: m_TextInfo = icdCultureInfo.m_TextInfo;
m_CompareInfo = icdCultureInfo.m_CompareInfo;
}
m_EnglishName = ci.EnglishName; m_EnglishName = ci.EnglishName;
m_IsNeutralCulture = ci.IsNeutralCulture; m_IsNeutralCulture = ci.IsNeutralCulture;
m_Lcid = ci.LCID; m_Lcid = ci.LCID;
@@ -877,19 +880,21 @@ namespace ICD.Common.Utils.Globalization
m_ThreeLetterIsoLanguageName = ci.ThreeLetterISOLanguageName; m_ThreeLetterIsoLanguageName = ci.ThreeLetterISOLanguageName;
m_ThreeLetterWindowsLanguageName = ci.ThreeLetterWindowsLanguageName; m_ThreeLetterWindowsLanguageName = ci.ThreeLetterWindowsLanguageName;
m_TwoLetterIsoLanguageName = ci.TwoLetterISOLanguageName; m_TwoLetterIsoLanguageName = ci.TwoLetterISOLanguageName;
if (!m_IsNeutralCulture)
if (m_IsNeutralCulture)
return;
if (icdCultureInfo == null)
{ {
if (icdCultureInfo == null) m_DatetimeFormat = ci.DateTimeFormat.Clone() as DateTimeFormatInfo;
{ m_NumberFormat = ci.NumberFormat.Clone() as NumberFormatInfo;
m_DatetimeFormat = ci.DateTimeFormat; return;
m_NumberFormat = ci.NumberFormat;
return;
}
m_DatetimeFormatId = icdCultureInfo.m_DatetimeFormatId;
m_DatetimeFormat = icdCultureInfo.m_DatetimeFormat;
m_NumberFormatId = icdCultureInfo.m_NumberFormatId;
m_NumberFormat = icdCultureInfo.m_NumberFormat;
} }
m_DatetimeFormatId = icdCultureInfo.m_DatetimeFormatId;
m_DatetimeFormat = icdCultureInfo.m_DatetimeFormat.Clone() as DateTimeFormatInfo;
m_NumberFormatId = icdCultureInfo.m_NumberFormatId;
m_NumberFormat = icdCultureInfo.m_NumberFormat.Clone() as NumberFormatInfo;
} }
#region Methods #region Methods

View File

@@ -86,6 +86,7 @@
<Compile Include="Comparers\PredicateComparer.cs" /> <Compile Include="Comparers\PredicateComparer.cs" />
<Compile Include="Comparers\SequenceComparer.cs" /> <Compile Include="Comparers\SequenceComparer.cs" />
<Compile Include="ConsoleColor.cs" /> <Compile Include="ConsoleColor.cs" />
<Compile Include="DateTimeUtils.cs" />
<Compile Include="Email\EmailClient.cs" /> <Compile Include="Email\EmailClient.cs" />
<Compile Include="Email\eMailErrorCode.cs" /> <Compile Include="Email\eMailErrorCode.cs" />
<Compile Include="Email\EmailStringCollection.cs" /> <Compile Include="Email\EmailStringCollection.cs" />
@@ -109,6 +110,7 @@
<None Include="ObfuscationSettings.cs" /> <None Include="ObfuscationSettings.cs" />
<Compile Include="Extensions\BoolExtensions.cs" /> <Compile Include="Extensions\BoolExtensions.cs" />
<Compile Include="Extensions\ByteExtensions.cs" /> <Compile Include="Extensions\ByteExtensions.cs" />
<Compile Include="Extensions\CultureInfoExtensions.cs" />
<Compile Include="Extensions\DayOfWeekExtensions.cs" /> <Compile Include="Extensions\DayOfWeekExtensions.cs" />
<Compile Include="Extensions\JsonSerializerExtensions.cs" /> <Compile Include="Extensions\JsonSerializerExtensions.cs" />
<Compile Include="Extensions\JsonTokenExtensions.cs" /> <Compile Include="Extensions\JsonTokenExtensions.cs" />

View File

@@ -1,4 +1,5 @@
#if SIMPLSHARP using ICD.Common.Utils.Extensions;
#if SIMPLSHARP
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Crestron.SimplSharp; using Crestron.SimplSharp;
@@ -190,6 +191,18 @@ namespace ICD.Common.Utils
return CrestronEnvironment.GetLocalTime(); return CrestronEnvironment.GetLocalTime();
} }
public static void SetLocalTime(DateTime localTime)
{
CrestronEnvironment.SetTimeAndDate((ushort)localTime.Hour,
(ushort)localTime.Minute,
(ushort)localTime.Second,
(ushort)localTime.Month,
(ushort)localTime.Day,
(ushort)localTime.Year);
OnSystemDateTimeChanged.Raise(null);
}
public static eEthernetEventType GetEthernetEventType(Crestron.SimplSharp.eEthernetEventType type) public static eEthernetEventType GetEthernetEventType(Crestron.SimplSharp.eEthernetEventType type)
{ {
switch (type) switch (type)

View File

@@ -79,6 +79,18 @@ namespace ICD.Common.Utils
return DateTime.Now; return DateTime.Now;
} }
public static void SetLocalTime(DateTime localTime)
{
#if DEBUG
IcdConsole.PrintLine(eConsoleColor.Magenta, "Debug Build - Skipped setting local time to {0}",
localTime.ToString("s"));
#else
throw new NotSupportedException();
OnSystemDateTimeChanged.Raise(null);
#endif
}
/// <summary> /// <summary>
/// Converts 12 digit address to XX:XX:XX... format /// Converts 12 digit address to XX:XX:XX... format
/// </summary> /// </summary>

View File

@@ -62,6 +62,11 @@ namespace ICD.Common.Utils
/// </summary> /// </summary>
public static event EventHandler OnProgramInitializationComplete; public static event EventHandler OnProgramInitializationComplete;
/// <summary>
/// Raised when the system date/time has been set.
/// </summary>
public static event EventHandler OnSystemDateTimeChanged;
private static readonly SafeCriticalSection s_ProgramInitializationSection = new SafeCriticalSection(); private static readonly SafeCriticalSection s_ProgramInitializationSection = new SafeCriticalSection();
private static bool s_ProgramInitializationComplete; private static bool s_ProgramInitializationComplete;

View File

@@ -5,7 +5,6 @@ using ICD.Common.Properties;
using ICD.Common.Utils.Extensions; using ICD.Common.Utils.Extensions;
using ICD.Common.Utils.IO; using ICD.Common.Utils.IO;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace ICD.Common.Utils.Json namespace ICD.Common.Utils.Json
{ {
@@ -18,63 +17,6 @@ namespace ICD.Common.Utils.Json
private const string MESSAGE_NAME_PROPERTY = "m"; private const string MESSAGE_NAME_PROPERTY = "m";
private const string MESSAGE_DATA_PROPERTY = "d"; private const string MESSAGE_DATA_PROPERTY = "d";
/// <summary>
/// Gets the data as a DateTime value.
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static DateTime ParseDateTime(string data)
{
return DateTime.Parse(data);
}
/// <summary>
/// Gets the token as a DateTime value.
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
[PublicAPI]
public static DateTime ParseDateTime(JToken token)
{
if (token == null)
throw new ArgumentNullException("token");
#if SIMPLSHARP
return ParseDateTime((string)token);
#else
return (DateTime)token;
#endif
}
/// <summary>
/// Gets the token as a DateTime value.
/// </summary>
/// <param name="token"></param>
/// <param name="output"></param>
/// <returns></returns>
[PublicAPI]
public static bool TryParseDateTime(JToken token, out DateTime output)
{
if (token == null)
throw new ArgumentNullException("token");
output = default(DateTime);
try
{
output = ParseDateTime(token);
return true;
}
catch (FormatException)
{
return false;
}
catch (InvalidCastException)
{
return false;
}
}
/// <summary> /// <summary>
/// Serializes the given item and formats the JSON into a human-readable form. /// Serializes the given item and formats the JSON into a human-readable form.
/// </summary> /// </summary>

View File

@@ -209,5 +209,18 @@ namespace ICD.Common.Utils
return nearest.Aggregate((x, y) => Math.Abs(x - number) < Math.Abs(y - number) ? x : y); return nearest.Aggregate((x, y) => Math.Abs(x - number) < Math.Abs(y - number) ? x : y);
} }
/// <summary>
/// Calculates the modulus of the given number.
/// </summary>
/// <param name="number"></param>
/// <param name="mod"></param>
/// <returns></returns>
/// <remarks>method name can't be "Mod", due to S+ compatability issues</remarks>
public static int Modulus(int number, int mod)
{
int remainder = number % mod;
return remainder < 0 ? remainder + mod : remainder;
}
} }
} }

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using ICD.Common.Properties; using ICD.Common.Properties;
using ICD.Common.Utils.Extensions; using ICD.Common.Utils.Extensions;
@@ -48,7 +49,7 @@ namespace ICD.Common.Utils
#elif LINUX #elif LINUX
return Join(RootPath, "opt", "ICD.Connect"); return Join(RootPath, "opt", "ICD.Connect");
#else #else
return Join(RootPath, "ProgramData", "ICD.Connect"); return Join(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "ICD.Connect");
#endif #endif
} }
} }

View File

@@ -142,8 +142,7 @@ namespace ICD.Common.Utils
[PublicAPI] [PublicAPI]
public static TimeSpan GetSystemUptime() public static TimeSpan GetSystemUptime()
{ {
// TODO return TimeSpan.FromMilliseconds(Environment.TickCount);
return default(TimeSpan);
} }
/// <summary> /// <summary>
@@ -153,8 +152,8 @@ namespace ICD.Common.Utils
[PublicAPI] [PublicAPI]
public static TimeSpan GetProgramUptime() public static TimeSpan GetProgramUptime()
{ {
// TODO var current = System.Diagnostics.Process.GetCurrentProcess();
return default(TimeSpan); return IcdEnvironment.GetLocalTime() - current.StartTime;
} }
#endregion #endregion

View File

@@ -1,4 +1,5 @@
using ICD.Common.Utils.Services; using ICD.Common.Utils.IO;
using ICD.Common.Utils.Services;
using ICD.Common.Utils.Services.Logging; using ICD.Common.Utils.Services.Logging;
#if SIMPLSHARP #if SIMPLSHARP
using Crestron.SimplSharp; using Crestron.SimplSharp;
@@ -86,6 +87,16 @@ namespace ICD.Common.Utils
} }
} }
/// <summary>
/// Returns the date and time the program was installed.
/// </summary>
/// <returns></returns>
[PublicAPI]
public static DateTime ProgramInstallDate
{
get { return IcdFile.GetCreationTime(PathUtils.Join(PathUtils.ProgramPath, ProgramFile)); }
}
/// <summary> /// <summary>
/// Parses the prog comments and pulls program information. /// Parses the prog comments and pulls program information.
/// </summary> /// </summary>

View File

@@ -55,6 +55,15 @@ namespace ICD.Common.Utils
return Assembly.GetEntryAssembly().GetName().Name; return Assembly.GetEntryAssembly().GetName().Name;
} }
} }
}
/// <summary>
/// Gets the date and time the program was installed.
/// </summary>
[PublicAPI]
public static DateTime ProgramInstallDate
{
get { return IcdFile.GetCreationTime(PathUtils.Join(PathUtils.ProgramPath, ProgramFile)); }
}
}
} }
#endif #endif

View File

@@ -4,4 +4,4 @@ using System.Reflection;
[assembly: AssemblyCompany("ICD Systems")] [assembly: AssemblyCompany("ICD Systems")]
[assembly: AssemblyProduct("ICD.Common.Utils")] [assembly: AssemblyProduct("ICD.Common.Utils")]
[assembly: AssemblyCopyright("Copyright © ICD Systems 2019")] [assembly: AssemblyCopyright("Copyright © ICD Systems 2019")]
[assembly: AssemblyVersion("9.7.0.0")] [assembly: AssemblyVersion("9.8.0.0")]

View File

@@ -1,5 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Text; using System.Text;
using ICD.Common.Utils.IO; using ICD.Common.Utils.IO;
#if SIMPLSHARP #if SIMPLSHARP
@@ -112,6 +114,64 @@ namespace ICD.Common.Utils.Xml
return converter.ReadXml(reader); return converter.ReadXml(reader);
} }
/// <summary>
/// Deserializes the child elements as items in an array.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="reader"></param>
/// <returns></returns>
public static IEnumerable<T> DeserializeArray<T>(IcdXmlReader reader)
{
if (reader == null)
throw new ArgumentNullException("reader");
return DeserializeArray(typeof(T), reader).Cast<T>();
}
/// <summary>
/// Deserializes the child elements as items in an array.
/// </summary>
/// <param name="type"></param>
/// <param name="reader"></param>
/// <returns></returns>
public static IEnumerable<object> DeserializeArray(Type type, IcdXmlReader reader)
{
if (type == null)
throw new ArgumentNullException("type");
if (reader == null)
throw new ArgumentNullException("reader");
if (reader.NodeType != XmlNodeType.Element)
throw new FormatException("Expected start element for array");
string arrayName = reader.Name;
// Read into the first element
do
{
reader.Read();
} while (reader.NodeType != XmlNodeType.Element && reader.NodeType != XmlNodeType.EndElement);
// Empty array
if (reader.NodeType == XmlNodeType.EndElement)
yield break;
// Read the items
IXmlConverter converter = XmlConverterAttribute.GetConverterForType(type);
while (reader.NodeType != XmlNodeType.EndElement)
{
yield return converter.ReadXml(reader);
reader.SkipInsignificantWhitespace();
}
if (reader.NodeType != XmlNodeType.EndElement || reader.Name != arrayName)
throw new FormatException("Expected end element for array");
// Read out of the array end element
reader.Read();
}
public static string ToString(int value) public static string ToString(int value)
{ {
return XmlConvert.ToString(value); return XmlConvert.ToString(value);