From 606dac3e1a36d528f99bf64fb5685202eaef0b49 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Sat, 14 Sep 2019 09:38:26 -0700 Subject: [PATCH] fix: Use double precision for X-Reset-After, set CultureInfo when parsing numeric types (#1375) * Parse double for X-Reset-After instead of float, needs more precision Float did not contain enough precision to store the millisecond unix time value, which resulted in the second and millisecond values being slightly off. This can be easily tested using: ```cs > DateTimeOffset.FromUnixTimeMilliseconds((long)(1470173022.123f * 1000)).Millisecond 160 // wrong > DateTimeOffset.FromUnixTimeMilliseconds((long)(1470173022.123 * 1000)).Millisecond 123 // correct ``` * Parse RateLimit-Reset using an IFormatProvider * State NumberStyle and use CultureInfo.InvariantCulture for any parsing This updates most occurances in the code where a Parse or TryParse method was used to explicitly state the NumberStyle, and to use CultureInfo.InvariantCulture. CultureInfo was used over NumberInfo, as it also works on DateTime parsing too. * Use default format spec in Commands --- src/Discord.Net.Core/Utils/TokenUtils.cs | 3 ++- src/Discord.Net.Rest/DiscordRestApiClient.cs | 2 +- src/Discord.Net.Rest/Entities/Users/RestUser.cs | 3 ++- .../Net/Converters/UInt64EntityOrIdConverter.cs | 5 +++-- src/Discord.Net.Rest/Net/RateLimitInfo.cs | 11 ++++++----- .../Entities/Users/SocketUser.cs | 5 +++-- src/Discord.Net.Webhook/DiscordWebhookClient.cs | 3 ++- 7 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/Discord.Net.Core/Utils/TokenUtils.cs b/src/Discord.Net.Core/Utils/TokenUtils.cs index 2efb1822a..c3dd39237 100644 --- a/src/Discord.Net.Core/Utils/TokenUtils.cs +++ b/src/Discord.Net.Core/Utils/TokenUtils.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Text; namespace Discord @@ -76,7 +77,7 @@ namespace Discord var bytes = Convert.FromBase64String(encoded); var idStr = Encoding.UTF8.GetString(bytes); // try to parse a ulong from the resulting string - if (ulong.TryParse(idStr, out var id)) + if (ulong.TryParse(idStr, NumberStyles.None, CultureInfo.InvariantCulture, out var id)) return id; } catch (DecoderFallbackException) diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index c76f31835..d3db3e301 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -1488,7 +1488,7 @@ namespace Discord.API builder.Append(format, lastIndex, leftIndex - lastIndex); int rightIndex = format.IndexOf("}", leftIndex); - int argId = int.Parse(format.Substring(leftIndex + 1, rightIndex - leftIndex - 1)); + int argId = int.Parse(format.Substring(leftIndex + 1, rightIndex - leftIndex - 1), NumberStyles.None, CultureInfo.InvariantCulture); string fieldName = GetFieldName(methodArgs[argId + 1]); var mappedId = BucketIds.GetIndex(fieldName); diff --git a/src/Discord.Net.Rest/Entities/Users/RestUser.cs b/src/Discord.Net.Rest/Entities/Users/RestUser.cs index 37385fb7e..d5fffca94 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestUser.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Immutable; using System.Diagnostics; +using System.Globalization; using System.Threading.Tasks; using Model = Discord.API.User; @@ -57,7 +58,7 @@ namespace Discord.Rest if (model.Avatar.IsSpecified) AvatarId = model.Avatar.Value; if (model.Discriminator.IsSpecified) - DiscriminatorValue = ushort.Parse(model.Discriminator.Value); + DiscriminatorValue = ushort.Parse(model.Discriminator.Value, NumberStyles.None, CultureInfo.InvariantCulture); if (model.Bot.IsSpecified) IsBot = model.Bot.Value; if (model.Username.IsSpecified) diff --git a/src/Discord.Net.Rest/Net/Converters/UInt64EntityOrIdConverter.cs b/src/Discord.Net.Rest/Net/Converters/UInt64EntityOrIdConverter.cs index ae8cf2cb2..e55534833 100644 --- a/src/Discord.Net.Rest/Net/Converters/UInt64EntityOrIdConverter.cs +++ b/src/Discord.Net.Rest/Net/Converters/UInt64EntityOrIdConverter.cs @@ -1,6 +1,7 @@ -using Discord.API; +using Discord.API; using Newtonsoft.Json; using System; +using System.Globalization; namespace Discord.Net.Converters { @@ -23,7 +24,7 @@ namespace Discord.Net.Converters { case JsonToken.String: case JsonToken.Integer: - return new EntityOrId(ulong.Parse(reader.ReadAsString())); + return new EntityOrId(ulong.Parse(reader.ReadAsString(), NumberStyles.None, CultureInfo.InvariantCulture)); default: T obj; if (_innerConverter != null) diff --git a/src/Discord.Net.Rest/Net/RateLimitInfo.cs b/src/Discord.Net.Rest/Net/RateLimitInfo.cs index f193ce6ec..fb6f5e2ce 100644 --- a/src/Discord.Net.Rest/Net/RateLimitInfo.cs +++ b/src/Discord.Net.Rest/Net/RateLimitInfo.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; namespace Discord.Net { @@ -17,15 +18,15 @@ namespace Discord.Net IsGlobal = headers.TryGetValue("X-RateLimit-Global", out string temp) && bool.TryParse(temp, out var isGlobal) && isGlobal; Limit = headers.TryGetValue("X-RateLimit-Limit", out temp) && - int.TryParse(temp, out var limit) ? limit : (int?)null; + int.TryParse(temp, NumberStyles.None, CultureInfo.InvariantCulture, out var limit) ? limit : (int?)null; Remaining = headers.TryGetValue("X-RateLimit-Remaining", out temp) && - int.TryParse(temp, out var remaining) ? remaining : (int?)null; + int.TryParse(temp, NumberStyles.None, CultureInfo.InvariantCulture, out var remaining) ? remaining : (int?)null; Reset = headers.TryGetValue("X-RateLimit-Reset", out temp) && - float.TryParse(temp, out var reset) ? DateTimeOffset.FromUnixTimeMilliseconds((long)(reset * 1000)) : (DateTimeOffset?)null; + double.TryParse(temp, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var reset) ? DateTimeOffset.FromUnixTimeMilliseconds((long)(reset * 1000)) : (DateTimeOffset?)null; RetryAfter = headers.TryGetValue("Retry-After", out temp) && - int.TryParse(temp, out var retryAfter) ? retryAfter : (int?)null; + int.TryParse(temp, NumberStyles.None, CultureInfo.InvariantCulture, out var retryAfter) ? retryAfter : (int?)null; Lag = headers.TryGetValue("Date", out temp) && - DateTimeOffset.TryParse(temp, out var date) ? DateTimeOffset.UtcNow - date : (TimeSpan?)null; + DateTimeOffset.TryParse(temp, CultureInfo.InvariantCulture, DateTimeStyles.None, out var date) ? DateTimeOffset.UtcNow - date : (TimeSpan?)null; } } } diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs index eceb071eb..d4798bedd 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Globalization; using System.Linq; using System.Threading.Tasks; using Discord.Rest; @@ -60,10 +61,10 @@ namespace Discord.WebSocket } if (model.Discriminator.IsSpecified) { - var newVal = ushort.Parse(model.Discriminator.Value); + var newVal = ushort.Parse(model.Discriminator.Value, NumberStyles.None, CultureInfo.InvariantCulture); if (newVal != DiscriminatorValue) { - DiscriminatorValue = ushort.Parse(model.Discriminator.Value); + DiscriminatorValue = ushort.Parse(model.Discriminator.Value, NumberStyles.None, CultureInfo.InvariantCulture); hasChanges = true; } } diff --git a/src/Discord.Net.Webhook/DiscordWebhookClient.cs b/src/Discord.Net.Webhook/DiscordWebhookClient.cs index 5dc3d51aa..542ec7997 100644 --- a/src/Discord.Net.Webhook/DiscordWebhookClient.cs +++ b/src/Discord.Net.Webhook/DiscordWebhookClient.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -132,7 +133,7 @@ namespace Discord.Webhook { // ensure that the first group is a ulong, set the _webhookId // 0th group is always the entire match, so start at index 1 - if (!(match.Groups[1].Success && ulong.TryParse(match.Groups[1].Value, out webhookId))) + if (!(match.Groups[1].Success && ulong.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out webhookId))) throw ex("The webhook Id could not be parsed."); if (!match.Groups[2].Success)