Skip to content

Commit

Permalink
Use safe DateTime conversion (#861)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexMacocian authored Nov 17, 2024
1 parent fcc5409 commit a656d4b
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 37 deletions.
3 changes: 2 additions & 1 deletion Daybreak/Services/Logging/JsonLogsManager.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Daybreak.Configuration.Options;
using Daybreak.Services.Database;
using Daybreak.Services.Logging.Models;
using Daybreak.Utils;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -65,7 +66,7 @@ public async void WriteLog(Log log)
Message = log.Exception is null ? log.Message : $"{log.Message}{Environment.NewLine}{log.Exception}",
Category = log.Category,
LogLevel = log.LogLevel,
LogTime = log.LogTime,
LogTime = log.LogTime.ToSafeDateTimeOffset(),
CorrelationVector = log.CorrelationVector
};

Expand Down
34 changes: 13 additions & 21 deletions Daybreak/Services/Notifications/NotificationService.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
using Daybreak.Models.Notifications;
using Daybreak.Models.Notifications.Handling;
using Daybreak.Services.Notifications.Models;
using Daybreak.Utils;
using Microsoft.Extensions.Logging;
using Slim;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Core.Extensions;
using System.Extensions;
using System.Extensions.Core;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
Expand Down Expand Up @@ -80,27 +80,19 @@ public NotificationToken NotifyError<THandlingType>(string title,

void INotificationProducer.OpenNotification(Notification notification, bool storeNotification)
{
var scopedLogger = this.logger.CreateScopedLogger();
if (storeNotification)
{
try
this.storage.OpenNotification(new NotificationDTO
{
this.storage.OpenNotification(new NotificationDTO
{
Title = notification.Title,
Description = notification.Description,
Id = notification.Id,
Level = (int)notification.Level,
MetaData = notification.Metadata,
HandlerType = notification.HandlingType?.AssemblyQualifiedName,
ExpirationTime = notification.ExpirationTime,
Closed = true
});
}
catch(ArgumentOutOfRangeException exception) when (exception.Message.Contains("The UTC time represented when the offset is applied must be between year 0 and 10000"))
{
scopedLogger.LogError(exception, "Encountered DateTime to DateTimeOffset exception. Failed to store notification and will discard it. DateTime: {dateTime}", notification.ExpirationTime);
}
Title = notification.Title,
Description = notification.Description,
Id = notification.Id,
Level = (int)notification.Level,
MetaData = notification.Metadata,
HandlerType = notification.HandlingType?.AssemblyQualifiedName,
ExpirationTime = notification.ExpirationTime.ToSafeDateTimeOffset(),
Closed = true
});
}

if (notification.HandlingType is null)
Expand Down Expand Up @@ -213,8 +205,8 @@ private static NotificationDTO ToDTO(Notification notification)
Level = (int)notification.Level,
Title = notification.Title,
Description = notification.Description,
ExpirationTime = notification.ExpirationTime,
CreationTime = notification.CreationTime,
ExpirationTime = notification.ExpirationTime.ToSafeDateTimeOffset(),
CreationTime = notification.CreationTime.ToSafeDateTimeOffset(),
MetaData = notification.Metadata,
Dismissible = notification.Dismissible,
Closed = notification.Closed,
Expand Down
13 changes: 7 additions & 6 deletions Daybreak/Services/TradeChat/PriceHistoryDatabase.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Daybreak.Models.Guildwars;
using Daybreak.Services.Database;
using Daybreak.Services.TradeChat.Models;
using Daybreak.Utils;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -48,10 +49,10 @@ public IEnumerable<TraderQuoteDTO> GetQuoteHistory(ItemBase item, DateTime? from
{
var scopedLogger = this.logger.CreateScopedLogger(nameof(this.GetQuoteHistory), item.Id.ToString());
var fromO = fromTimestamp.HasValue ?
new DateTimeOffset(fromTimestamp.Value) :
fromTimestamp.Value.ToSafeDateTimeOffset() :
DateTimeOffset.MinValue;
var toO = toTimestamp.HasValue ?
new DateTimeOffset(toTimestamp.Value) :
toTimestamp.Value.ToSafeDateTimeOffset() :
DateTimeOffset.MaxValue;
scopedLogger.LogDebug($"Retrieving quotes for item {item.Id} with timestamp between [{fromO}] and [{toO}]");
var modifiersHash = item.Modifiers is not null ? this.itemHashService.ComputeHash(item) : default;
Expand All @@ -69,10 +70,10 @@ public IEnumerable<TraderQuoteDTO> GetQuotesByTimestamp(TraderQuoteType type, Da
{
var scopedLogger = this.logger.CreateScopedLogger(nameof(this.GetQuotesByTimestamp), string.Empty);
var fromO = from.HasValue ?
new DateTimeOffset(from.Value) :
from.Value.ToSafeDateTimeOffset() :
DateTimeOffset.MinValue;
var toO = to.HasValue ?
new DateTimeOffset(to.Value) :
to.Value.ToSafeDateTimeOffset() :
DateTimeOffset.Now;
scopedLogger.LogDebug($"Retrieving all quotes by timestamp between [{fromO}] and [{toO}]");
var items = this.collection.FindAll(t => t.TimeStamp >= fromO && t.TimeStamp <= toO && t.TraderQuoteType == (int)type);
Expand All @@ -84,10 +85,10 @@ public IEnumerable<TraderQuoteDTO> GetQuotesByInsertionTime(TraderQuoteType type
{
var scopedLogger = this.logger.CreateScopedLogger(nameof(this.GetQuotesByInsertionTime), string.Empty);
var fromO = from.HasValue ?
new DateTimeOffset(from.Value) :
from.Value.ToSafeDateTimeOffset() :
DateTimeOffset.MinValue;
var toO = to.HasValue ?
new DateTimeOffset(to.Value) :
to.Value.ToSafeDateTimeOffset() :
DateTimeOffset.Now;
scopedLogger.LogDebug($"Retrieving all quotes by insertion time between [{fromO}] and [{toO}]");
var items = this.collection.FindAll(t => t.InsertionTime >= fromO && t.InsertionTime <= toO && t.TraderQuoteType == (int)type);
Expand Down
11 changes: 6 additions & 5 deletions Daybreak/Services/TradeChat/TradeAlertingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Daybreak.Services.Notifications;
using Daybreak.Services.TradeChat.Models;
using Daybreak.Services.TradeChat.Notifications;
using Daybreak.Utils;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
Expand Down Expand Up @@ -105,7 +106,7 @@ public void OnStartup()
private async void StartAlertingService(CancellationToken cancellationToken)
{
var lastCheckTime = this.options.Value.LastCheckTime;
var timeSinceLastCheckTime = DateTimeOffset.UtcNow - lastCheckTime;
var timeSinceLastCheckTime = DateTimeOffset.UtcNow - lastCheckTime.ToSafeDateTimeOffset();
if (timeSinceLastCheckTime > this.options.Value.MaxLookbackPeriod)
{
timeSinceLastCheckTime = this.options.Value.MaxLookbackPeriod;
Expand Down Expand Up @@ -146,7 +147,7 @@ private async Task CheckLiveTrades<T>(ITradeChatService<T> tradeChatService, Tra
{
var traderMessageDTO = new TraderMessageDTO
{
Timestamp = message.Timestamp,
Timestamp = message.Timestamp.ToSafeDateTimeOffset(),
Id = message.Timestamp.Ticks,
Message = message.Message,
Sender = message.Sender,
Expand Down Expand Up @@ -274,7 +275,7 @@ private static bool CheckMatch(string toCheck, string toMatch, bool isRegex)
}
else
{
return toCheck.ToLower().Contains(toMatch.ToLower());
return toCheck.Contains(toMatch, StringComparison.CurrentCultureIgnoreCase);
}
}

Expand All @@ -284,7 +285,7 @@ private static async Task<IEnumerable<TraderMessageDTO>> GetTraderMessages<T>(IT
var trades = await tradeChatService.GetLatestTrades(cancellationToken);
var orderedTrades = trades.OrderBy(t => t.Timestamp).Select(t => new TraderMessageDTO
{
Timestamp = t.Timestamp,
Timestamp = t.Timestamp.ToSafeDateTimeOffset(),
Id = t.Timestamp.Ticks,
Message = t.Message,
Sender = t.Sender,
Expand All @@ -308,7 +309,7 @@ private static async Task<IEnumerable<TraderMessageDTO>> GetTraderMessagesSince<
var trades = await tradeChatService.GetLatestTrades(cancellationToken, since);
var orderedTrades = trades.OrderBy(t => t.Timestamp).Select(t => new TraderMessageDTO
{
Timestamp = t.Timestamp,
Timestamp = t.Timestamp.ToSafeDateTimeOffset(),
Id = t.Timestamp.Ticks,
Message = t.Message,
Sender = t.Sender,
Expand Down
9 changes: 5 additions & 4 deletions Daybreak/Services/TradeChat/TraderQuoteService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Daybreak.Models.Guildwars;
using Daybreak.Models.Trade;
using Daybreak.Services.TradeChat.Models;
using Daybreak.Utils;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
Expand Down Expand Up @@ -121,7 +122,7 @@ private async Task<IEnumerable<TraderQuote>> GetSellQuotesInternal(CancellationT
{
var buyQuotes = await this.FetchBuyQuotesInternal(cancellationToken);
var sellQuotes = await this.FetchSellQuotesInternal(cancellationToken);
var insertionTime = DateTime.UtcNow;
var insertionTime = DateTimeOffset.UtcNow;
this.priceHistoryDatabase.AddTraderQuotes(buyQuotes
.Select(
quote => new TraderQuoteDTO
Expand All @@ -130,7 +131,7 @@ private async Task<IEnumerable<TraderQuote>> GetSellQuotesInternal(CancellationT
ItemId = quote.Item?.Id ?? 0,
ModifiersHash = quote.Item?.Modifiers is null ? string.Empty : this.itemHashService.ComputeHash(quote.Item),
InsertionTime = insertionTime,
TimeStamp = quote.Timestamp ?? insertionTime,
TimeStamp = quote.Timestamp.ToSafeDateTimeOffset() ?? insertionTime,
TraderQuoteType = (int)TraderQuoteType.Buy
}));

Expand All @@ -142,11 +143,11 @@ private async Task<IEnumerable<TraderQuote>> GetSellQuotesInternal(CancellationT
ItemId = quote.Item?.Id ?? 0,
ModifiersHash = quote.Item?.Modifiers is null ? string.Empty : this.itemHashService.ComputeHash(quote.Item),
InsertionTime = insertionTime,
TimeStamp = quote.Timestamp ?? insertionTime,
TimeStamp = quote.Timestamp.ToSafeDateTimeOffset() ?? insertionTime,
TraderQuoteType = (int)TraderQuoteType.Sell
}));

this.options.Value.LastCheckTime = insertionTime;
this.options.Value.LastCheckTime = insertionTime.UtcDateTime;
this.options.UpdateOption();
return (buyQuotes, sellQuotes);
}
Expand Down
32 changes: 32 additions & 0 deletions Daybreak/Utils/DateTimeExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;

namespace Daybreak.Utils;

public static class DateTimeExtensions
{
/// <summary>
/// Converts <see cref="DateTime"/> to <see cref="DateTimeOffset"/> safely and clamps the values between <see cref="DateTimeOffset.MinValue"/> and <see cref="DateTimeOffset.MaxValue"/>
/// </summary>
public static DateTimeOffset ToSafeDateTimeOffset(this DateTime dateTime)
{
var utcDateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc);
return utcDateTime.ToUniversalTime() <= DateTimeOffset.MinValue.UtcDateTime
? DateTimeOffset.MinValue
: utcDateTime.ToUniversalTime() >= DateTimeOffset.MaxValue.UtcDateTime
? DateTimeOffset.MaxValue
: new DateTimeOffset(utcDateTime);
}

/// <summary>
/// Converts <see cref="DateTime"/> to <see cref="DateTimeOffset"/> safely and clamps the values between <see cref="DateTimeOffset.MinValue"/> and <see cref="DateTimeOffset.MaxValue"/>
/// </summary>
public static DateTimeOffset? ToSafeDateTimeOffset(this DateTime? dateTime)
{
if (!dateTime.HasValue)
{
return default;
}

return dateTime.Value.ToSafeDateTimeOffset();
}
}

0 comments on commit a656d4b

Please sign in to comment.