diff --git a/.editorconfig b/.editorconfig index e72ea925..a77e827f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -18,6 +18,8 @@ insert_final_newline = true csharp_new_line_before_members_in_anonymous_types = true [*.cs] +#parsing and formatting should be implemented in a culture-invariant manner +dotnet_diagnostic.CA1305.severity = warning #place catch statements on a new line csharp_new_line_before_catch = true diff --git a/Ical.Net.Benchmarks/Ical.Net.Benchmarks.csproj b/Ical.Net.Benchmarks/Ical.Net.Benchmarks.csproj index 65c8583a..845b39aa 100644 --- a/Ical.Net.Benchmarks/Ical.Net.Benchmarks.csproj +++ b/Ical.Net.Benchmarks/Ical.Net.Benchmarks.csproj @@ -7,6 +7,7 @@ + diff --git a/Ical.Net.Tests/CalDateTimeTests.cs b/Ical.Net.Tests/CalDateTimeTests.cs index 5afdaefa..6463a158 100644 --- a/Ical.Net.Tests/CalDateTimeTests.cs +++ b/Ical.Net.Tests/CalDateTimeTests.cs @@ -93,7 +93,7 @@ public static IEnumerable ToTimeZoneTestCases() [Test(Description = "A certain date/time value applied to different timezones should return the same UTC date/time")] public void SameDateTimeWithDifferentTzIdShouldReturnSameUtc() { - var someTime = DateTimeOffset.Parse("2018-05-21T11:35:00-04:00"); + var someTime = DateTimeOffset.Parse("2018-05-21T11:35:00-04:00", CultureInfo.InvariantCulture); var someDt = new CalDateTime(someTime.DateTime, "America/New_York"); var firstUtc = someDt.AsUtc; @@ -111,7 +111,7 @@ public DateTimeKind DateTimeKindOverrideTests(DateTime dateTime, string tzId) public static IEnumerable DateTimeKindOverrideTestCases() { const string localTz = "America/New_York"; - var localDt = DateTime.SpecifyKind(DateTime.Parse("2018-05-21T11:35:33"), DateTimeKind.Unspecified); + var localDt = DateTime.SpecifyKind(DateTime.Parse("2018-05-21T11:35:33", CultureInfo.InvariantCulture), DateTimeKind.Unspecified); yield return new TestCaseData(localDt, "UTC") .Returns(DateTimeKind.Unspecified) @@ -317,7 +317,7 @@ public void Simple_PropertyAndMethod_HasTime_Tests() Assert.That(c.DayOfYear, Is.EqualTo(dt.DayOfYear)); Assert.That(c.Time?.ToTimeSpan(), Is.EqualTo(dt.TimeOfDay)); Assert.That(c.Add(-Duration.FromSeconds(dt.Second)).Value.Second, Is.EqualTo(0)); - Assert.That(c.ToString("dd.MM.yyyy"), Is.EqualTo("02.01.2025 Europe/Berlin")); + Assert.That(c.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture), Is.EqualTo("02.01.2025 Europe/Berlin")); // Create a date-only CalDateTime from a CalDateTime Assert.That(new CalDateTime(new CalDateTime(2025, 1, 1)), Is.EqualTo(new CalDateTime(2025, 1, 1))); }); diff --git a/Ical.Net.Tests/DocumentationExamples.cs b/Ical.Net.Tests/DocumentationExamples.cs index 342a6729..9c961258 100644 --- a/Ical.Net.Tests/DocumentationExamples.cs +++ b/Ical.Net.Tests/DocumentationExamples.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using Ical.Net.CalendarComponents; using Ical.Net.DataTypes; @@ -50,8 +51,8 @@ public void EveryOtherTuesdayUntilTheEndOfTheYear_Test() // An event taking place between 07:00 and 08:00, beginning July 5 (a Tuesday) var vEvent = new CalendarEvent { - DtStart = new CalDateTime(DateTime.Parse("2016-07-05T07:00")), - DtEnd = new CalDateTime(DateTime.Parse("2016-07-05T08:00")), + DtStart = new CalDateTime(DateTime.Parse("2016-07-05T07:00", CultureInfo.InvariantCulture)), + DtEnd = new CalDateTime(DateTime.Parse("2016-07-05T08:00",CultureInfo.InvariantCulture)), }; // Recurring every other Tuesday until Dec 31 @@ -77,8 +78,8 @@ public void FourthThursdayOfNovember_Tests() // An event taking place between 07:00 and 19:00, beginning July 5 (a Tuesday) var vEvent = new CalendarEvent { - DtStart = new CalDateTime(DateTime.Parse("2000-11-23T07:00")), - DtEnd = new CalDateTime(DateTime.Parse("2000-11-23T19:00")), + DtStart = new CalDateTime(DateTime.Parse("2000-11-23T07:00", CultureInfo.InvariantCulture)), + DtEnd = new CalDateTime(DateTime.Parse("2000-11-23T19:00", CultureInfo.InvariantCulture)), }; // Recurring every other Tuesday until Dec 31 @@ -108,8 +109,8 @@ public void DailyExceptSunday_Test() //An event that happens daily through 2016, except for Sundays var vEvent = new CalendarEvent { - DtStart = new CalDateTime(DateTime.Parse("2016-01-01T07:00")), - DtEnd = new CalDateTime(DateTime.Parse("2016-12-31T08:00")), + DtStart = new CalDateTime(DateTime.Parse("2016-01-01T07:00", CultureInfo.InvariantCulture)), + DtEnd = new CalDateTime(DateTime.Parse("2016-12-31T08:00", CultureInfo.InvariantCulture)), RecurrenceRules = new List { new RecurrencePattern(FrequencyType.Daily, 1) }, }; diff --git a/Ical.Net.Tests/EqualityAndHashingTests.cs b/Ical.Net.Tests/EqualityAndHashingTests.cs index cfd7f5d9..f379e3b4 100644 --- a/Ical.Net.Tests/EqualityAndHashingTests.cs +++ b/Ical.Net.Tests/EqualityAndHashingTests.cs @@ -6,6 +6,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Text; using Ical.Net.CalendarComponents; @@ -19,7 +20,7 @@ namespace Ical.Net.Tests; public class EqualityAndHashingTests { private const string TzId = "America/Los_Angeles"; - private static readonly DateTime _nowTime = DateTime.SpecifyKind(DateTime.Parse("2016-07-16T16:47:02.9310521-04:00"), DateTimeKind.Unspecified); + private static readonly DateTime _nowTime = DateTime.SpecifyKind(DateTime.Parse("2016-07-16T16:47:02.9310521-04:00", CultureInfo.InvariantCulture), DateTimeKind.Unspecified); private static readonly DateTime _later = _nowTime.AddHours(1); [Test, TestCaseSource(nameof(CalDateTime_TestCases))] diff --git a/Ical.Net.Tests/GetOccurrenceTests.cs b/Ical.Net.Tests/GetOccurrenceTests.cs index 9313183d..e1912808 100644 --- a/Ical.Net.Tests/GetOccurrenceTests.cs +++ b/Ical.Net.Tests/GetOccurrenceTests.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using Ical.Net.CalendarComponents; using Ical.Net.DataTypes; @@ -20,12 +21,12 @@ internal class GetOccurrenceTests [Test] public void WrongDurationTest() { - var firstStart = new CalDateTime(DateTime.Parse("2016-01-01")); - var firstEnd = new CalDateTime(DateTime.Parse("2016-01-05")); + var firstStart = new CalDateTime(DateTime.Parse("2016-01-01", CultureInfo.InvariantCulture)); + var firstEnd = new CalDateTime(DateTime.Parse("2016-01-05", CultureInfo.InvariantCulture)); var vEvent = new CalendarEvent { DtStart = firstStart, DtEnd = firstEnd, }; - var secondStart = new CalDateTime(DateTime.Parse("2016-03-01")); - var secondEnd = new CalDateTime(DateTime.Parse("2016-03-05")); + var secondStart = new CalDateTime(DateTime.Parse("2016-03-01", CultureInfo.InvariantCulture)); + var secondEnd = new CalDateTime(DateTime.Parse("2016-03-05", CultureInfo.InvariantCulture)); var vEvent2 = new CalendarEvent { DtStart = secondStart, DtEnd = secondEnd, }; var calendar = new Calendar(); diff --git a/Ical.Net.Tests/Ical.Net.Tests.csproj b/Ical.Net.Tests/Ical.Net.Tests.csproj index fe2b4581..a6cb41c8 100644 --- a/Ical.Net.Tests/Ical.Net.Tests.csproj +++ b/Ical.Net.Tests/Ical.Net.Tests.csproj @@ -14,6 +14,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Ical.Net.Tests/RecurrenceTests.cs b/Ical.Net.Tests/RecurrenceTests.cs index 781dff78..4c9ffb65 100644 --- a/Ical.Net.Tests/RecurrenceTests.cs +++ b/Ical.Net.Tests/RecurrenceTests.cs @@ -2773,7 +2773,7 @@ public void Issue432() }; var vEvent = new CalendarEvent { - Start = new CalDateTime(DateTime.Parse("2019-01-04T08:00Z").ToUniversalTime()), + Start = new CalDateTime(DateTime.Parse("2019-01-04T08:00Z", CultureInfo.InvariantCulture).ToUniversalTime()), }; vEvent.RecurrenceRules.Add(rrule); @@ -2806,8 +2806,8 @@ public void Issue432_AllDay() { var vEvent = new CalendarEvent { - Start = new CalDateTime(DateTime.Parse("2020-01-11")), // no time means all day - End = new CalDateTime(DateTime.Parse("2020-01-11T00:00")), + Start = new CalDateTime(DateTime.Parse("2020-01-11", CultureInfo.InvariantCulture)), // no time means all day + End = new CalDateTime(DateTime.Parse("2020-01-11T00:00", CultureInfo.InvariantCulture)), }; var occurrences = vEvent.GetOccurrences(new CalDateTime(2020,01, 10, 0, 0, 0)).TakeUntil(new CalDateTime(2020, 01, 11, 00, 00, 00)); @@ -3174,20 +3174,20 @@ public void RDateShouldBeUnionedWithRecurrenceSet() var calendar = Calendar.Load(ical)!; var firstEvent = calendar.Events.First(); - var startSearch = new CalDateTime(DateTime.Parse("2015-08-28T07:00:00"), _tzid); - var endSearch = new CalDateTime(DateTime.Parse("2016-08-28T07:00:00").AddDays(7), _tzid); + var startSearch = new CalDateTime(DateTime.Parse("2015-08-28T07:00:00", CultureInfo.InvariantCulture), _tzid); + var endSearch = new CalDateTime(DateTime.Parse("2016-08-28T07:00:00", CultureInfo.InvariantCulture).AddDays(7), _tzid); var occurrences = firstEvent.GetOccurrences(startSearch).TakeUntil(endSearch) .Select(o => o.Period) .ToList(); - var firstExpectedOccurrence = new CalDateTime(DateTime.Parse("2016-08-29T08:00:00"), _tzid); + var firstExpectedOccurrence = new CalDateTime(DateTime.Parse("2016-08-29T08:00:00", CultureInfo.InvariantCulture), _tzid); Assert.That(occurrences.First().StartTime, Is.EqualTo(firstExpectedOccurrence)); - var firstExpectedRDate = new CalDateTime(DateTime.Parse("2016-08-30T10:00:00"), _tzid); + var firstExpectedRDate = new CalDateTime(DateTime.Parse("2016-08-30T10:00:00", CultureInfo.InvariantCulture), _tzid); Assert.That(occurrences[1].StartTime.Equals(firstExpectedRDate), Is.True); - var secondExpectedRDate = new CalDateTime(DateTime.Parse("2016-08-31T10:00:00"), _tzid); + var secondExpectedRDate = new CalDateTime(DateTime.Parse("2016-08-31T10:00:00", CultureInfo.InvariantCulture), _tzid); Assert.That(occurrences[2].StartTime.Equals(secondExpectedRDate), Is.True); } @@ -3215,7 +3215,7 @@ public void OccurrenceMustBeCompletelyContainedWithinSearchRange() ByDay = new List { new WeekDay(DayOfWeek.Wednesday) }, }; - var start = DateTime.Parse("2016-08-01T07:00:00"); + var start = DateTime.Parse("2016-08-01T07:00:00", CultureInfo.InvariantCulture); var end = start.AddHours(1); var e = new CalendarEvent { @@ -3233,10 +3233,10 @@ public void OccurrenceMustBeCompletelyContainedWithinSearchRange() Assert.That(firstEvent, Is.EqualTo(e)); - var startSearch = new CalDateTime(DateTime.Parse("2016-07-01T00:00:00"), "UTC"); - var endSearch = new CalDateTime(DateTime.Parse("2016-08-31T07:00:00"), "UTC"); + var startSearch = new CalDateTime(DateTime.Parse("2016-07-01T00:00:00", CultureInfo.InvariantCulture), "UTC"); + var endSearch = new CalDateTime(DateTime.Parse("2016-08-31T07:00:00", CultureInfo.InvariantCulture), "UTC"); - var lastExpected = new CalDateTime(DateTime.Parse("2016-08-31T07:00:00"), "UTC"); + var lastExpected = new CalDateTime(DateTime.Parse("2016-08-31T07:00:00", CultureInfo.InvariantCulture), "UTC"); var occurrences = firstEvent.GetOccurrences(startSearch).TakeUntil(endSearch) .Select(o => o.Period) .ToList(); @@ -3320,16 +3320,16 @@ public void EventsWithShareUidsShouldGenerateASingleRecurrenceSet() .Select(o => o.Period) .ToList(); - var expectedSept1Start = new CalDateTime(DateTime.Parse("2016-09-01T16:30:00"), "Europe/Bucharest"); - var expectedSept1End = new CalDateTime(DateTime.Parse("2016-09-01T22:00:00"), "Europe/Bucharest"); + var expectedSept1Start = new CalDateTime(DateTime.Parse("2016-09-01T16:30:00", CultureInfo.InvariantCulture), "Europe/Bucharest"); + var expectedSept1End = new CalDateTime(DateTime.Parse("2016-09-01T22:00:00", CultureInfo.InvariantCulture), "Europe/Bucharest"); Assert.Multiple(() => { Assert.That(orderedOccurrences[3].StartTime, Is.EqualTo(expectedSept1Start)); Assert.That(orderedOccurrences[3].EndTime, Is.EqualTo(expectedSept1End)); }); - var expectedSept3Start = new CalDateTime(DateTime.Parse("2016-09-03T07:00:00"), "Europe/Bucharest"); - var expectedSept3End = new CalDateTime(DateTime.Parse("2016-09-03T12:30:00"), "Europe/Bucharest"); + var expectedSept3Start = new CalDateTime(DateTime.Parse("2016-09-03T07:00:00", CultureInfo.InvariantCulture), "Europe/Bucharest"); + var expectedSept3End = new CalDateTime(DateTime.Parse("2016-09-03T12:30:00", CultureInfo.InvariantCulture), "Europe/Bucharest"); Assert.Multiple(() => { Assert.That(orderedOccurrences[5].StartTime, Is.EqualTo(expectedSept3Start)); @@ -3432,8 +3432,8 @@ public void OneDayRange() { var vEvent = new CalendarEvent { - Start = new CalDateTime(DateTime.Parse("2019-06-07 0:00:00")), - End = new CalDateTime(DateTime.Parse("2019-06-08 00:00:00")) + Start = new CalDateTime(DateTime.Parse("2019-06-07 0:00:00", CultureInfo.InvariantCulture)), + End = new CalDateTime(DateTime.Parse("2019-06-08 00:00:00", CultureInfo.InvariantCulture)) }; //Testing on both the first day and the next, results used to be different @@ -3457,8 +3457,8 @@ public void SpecificMinute() }; var vEvent = new CalendarEvent { - Start = new CalDateTime(DateTime.Parse("2009-01-01 09:00:00")), - End = new CalDateTime(DateTime.Parse("2009-01-01 17:00:00")) + Start = new CalDateTime(DateTime.Parse("2009-01-01 09:00:00", CultureInfo.InvariantCulture)), + End = new CalDateTime(DateTime.Parse("2009-01-01 17:00:00", CultureInfo.InvariantCulture)) }; vEvent.RecurrenceRules.Add(rrule); @@ -3703,8 +3703,8 @@ public void InclusiveRruleUntil() const string timeZoneId = @"Eastern Standard Time"; var calendar = Calendar.Load(icalText)!; var firstEvent = calendar.Events.First(); - var startSearch = new CalDateTime(DateTime.Parse("2017-07-01T00:00:00"), timeZoneId); - var endSearch = new CalDateTime(DateTime.Parse("2018-07-01T00:00:00"), timeZoneId); + var startSearch = new CalDateTime(DateTime.Parse("2017-07-01T00:00:00", CultureInfo.InvariantCulture), timeZoneId); + var endSearch = new CalDateTime(DateTime.Parse("2018-07-01T00:00:00", CultureInfo.InvariantCulture), timeZoneId); var occurrences = firstEvent.GetOccurrences(startSearch).TakeUntil(endSearch).ToList(); Assert.That(occurrences, Has.Count.EqualTo(5)); diff --git a/Ical.Net.Tests/RecurrenceTests_From_Issues.cs b/Ical.Net.Tests/RecurrenceTests_From_Issues.cs index 1e53be41..50008b1b 100644 --- a/Ical.Net.Tests/RecurrenceTests_From_Issues.cs +++ b/Ical.Net.Tests/RecurrenceTests_From_Issues.cs @@ -6,6 +6,7 @@ #nullable enable using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using Ical.Net.CalendarComponents; using Ical.Net.DataTypes; @@ -314,8 +315,8 @@ public void Daylight_Savings_Changes_567() { // GetOccurrences Creates event with invalid time due to Daylight Savings changes #567 - var calStart = new CalDateTime(DateTimeOffset.Parse("2023-01-14T19:21:03.700Z").UtcDateTime, "UTC"); - var calFinish = new CalDateTime(DateTimeOffset.Parse("2023-03-14T18:21:03.700Z").UtcDateTime, "UTC"); + var calStart = new CalDateTime(DateTimeOffset.Parse("2023-01-14T19:21:03.700Z", CultureInfo.InvariantCulture).UtcDateTime, "UTC"); + var calFinish = new CalDateTime(DateTimeOffset.Parse("2023-03-14T18:21:03.700Z", CultureInfo.InvariantCulture).UtcDateTime, "UTC"); var tz = "Pacific Standard Time"; var pattern = new RecurrencePattern( "FREQ=WEEKLY;BYDAY=SU,MO,TU,WE"); //Adjust the date to today so that the times remain constant @@ -483,8 +484,8 @@ public void Except_Tuesday_Thursday_Saturday_Sunday() Summary = "BIO CLASS",//subject Description = "Details at CLASS",//description of meeting Location = "Building 101",//location - DtStart = new CalDateTime(DateTime.Parse("2017-06-01T08:00")), - DtEnd = new CalDateTime(DateTime.Parse("2017-06-01T09:30")), + DtStart = new CalDateTime(DateTime.Parse("2017-06-01T08:00", CultureInfo.InvariantCulture)), + DtEnd = new CalDateTime(DateTime.Parse("2017-06-01T09:30", CultureInfo.InvariantCulture)), RecurrenceRules = new List { new RecurrencePattern(FrequencyType.Daily, 1) }, }; diff --git a/Ical.Net.Tests/SerializationTests.cs b/Ical.Net.Tests/SerializationTests.cs index 7ac557a7..4a326ed4 100644 --- a/Ical.Net.Tests/SerializationTests.cs +++ b/Ical.Net.Tests/SerializationTests.cs @@ -7,7 +7,6 @@ using System.Collections; using System.Collections.Generic; using System.Globalization; -using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; @@ -115,19 +114,19 @@ public static void CompareEnumerables(IEnumerable a1, IEnumerable a2, string val public static string InspectSerializedSection(string serialized, string sectionName, IEnumerable elements) { const string notFound = "expected '{0}' not found"; - var searchFor = "BEGIN:" + sectionName; + var searchFor = string.Format(CultureInfo.InvariantCulture, "BEGIN:{0}", sectionName); var begin = serialized.IndexOf(searchFor, StringComparison.Ordinal); - Assert.That(begin, Is.Not.EqualTo(-1), () => string.Format(notFound, searchFor)); + Assert.That(begin, Is.Not.EqualTo(-1), () => string.Format(CultureInfo.InvariantCulture, notFound, searchFor)); - searchFor = "END:" + sectionName; + searchFor = string.Format(CultureInfo.InvariantCulture, "END:{0}", sectionName); var end = serialized.IndexOf(searchFor, begin, StringComparison.Ordinal); - Assert.That(end, Is.Not.EqualTo(-1), () => string.Format(notFound, searchFor)); + Assert.That(end, Is.Not.EqualTo(-1), () => string.Format(CultureInfo.InvariantCulture, notFound, searchFor)); var searchRegion = serialized.Substring(begin, end - begin + searchFor.Length); foreach (var e in elements) { - Assert.That(searchRegion, Does.Contain(SerializationConstants.LineBreak + e + SerializationConstants.LineBreak), () => string.Format(notFound, e)); + Assert.That(searchRegion, Does.Contain(SerializationConstants.LineBreak + e + SerializationConstants.LineBreak), () => string.Format(CultureInfo.InvariantCulture, notFound, e)); } return searchRegion; @@ -601,4 +600,48 @@ public void SerializeSubcomponent() Assert.That(!serialized.Contains("VCALENDAR", StringComparison.Ordinal), Is.True); }); } + + [TestCase("en-US")] // English (United States) + [TestCase("de-DE")] // German (Germany) + // failed before fixing CA1305 warnings + [TestCase("ar-SA")] // Arabic (Saudi Arabia) + // failed before fixing CA1305 warnings + [TestCase("he-IL")] // Hebrew (Israel) + [TestCase("hi-IN")] // Hindi (India) + [TestCase("zh-CN")] // Chinese (Simplified, China) + [TestCase("ja-JP")] // Japanese (Japan) + [TestCase("th-TH")] // Thai (Thailand) + [TestCase("ru-RU")] // Russian (Russia) + [TestCase("ko-KR")] // Korean (Korea) + public void TestConvertToInt32WithNegativeNumberInDifferentCultures(string cultureStr) + { + var originalCulture = CultureInfo.CurrentCulture; + try + { + var culture = new CultureInfo(cultureStr); + CultureInfo.CurrentCulture = culture; + + Assert.Multiple(() => + { + // Deserialize + Assert.That(() => Calendar.Load( + """ + BEGIN:VCALENDAR + BEGIN:VEVENT + DTSTART:20251231 + RRULE:FREQ=YEARLY;BYYEARDAY=-1 + END:VEVENT + END:VCALENDAR + """), Throws.Nothing); + // Serialize + Assert.That(() => new DurationSerializer().SerializeToString(new Duration(null, -1)), Is.EqualTo("-P1D")); + + }); + } + finally + { + CultureInfo.CurrentCulture = originalCulture; + } + } + } diff --git a/Ical.Net/DataTypes/GeographicLocation.cs b/Ical.Net/DataTypes/GeographicLocation.cs index 395f66b3..0727d88e 100644 --- a/Ical.Net/DataTypes/GeographicLocation.cs +++ b/Ical.Net/DataTypes/GeographicLocation.cs @@ -4,6 +4,7 @@ // using System.Diagnostics; +using System.Globalization; using Ical.Net.CalendarComponents; using Ical.Net.Serialization.DataTypes; @@ -48,7 +49,7 @@ public override void CopyFrom(ICopyable obj) Longitude = geo.Longitude; } - public override string ToString() => Latitude.ToString("0.000000") + ";" + Longitude.ToString("0.000000"); + public override string ToString() => Latitude.ToString("0.000000", CultureInfo.InvariantCulture) + ";" + Longitude.ToString("0.000000", CultureInfo.InvariantCulture); protected bool Equals(GeographicLocation other) => Latitude.Equals(other.Latitude) && Longitude.Equals(other.Longitude); diff --git a/Ical.Net/DataTypes/UTCOffset.cs b/Ical.Net/DataTypes/UTCOffset.cs index d7de9703..d7e65d04 100644 --- a/Ical.Net/DataTypes/UTCOffset.cs +++ b/Ical.Net/DataTypes/UTCOffset.cs @@ -4,6 +4,7 @@ // using System; +using System.Globalization; using Ical.Net.Serialization.DataTypes; namespace Ical.Net.DataTypes; @@ -64,7 +65,11 @@ public override bool Equals(object? obj) public override int GetHashCode() => Offset.GetHashCode(); - public override string ToString() => (Positive ? "+" : "-") + Hours.ToString("00") + Minutes.ToString("00") + (Seconds != 0 ? Seconds.ToString("00") : string.Empty); + public override string ToString() => (Positive ? "+" : "-") + + Hours.ToString("00", CultureInfo.InvariantCulture) + + Minutes.ToString("00", CultureInfo.InvariantCulture) + (Seconds != 0 + ? Seconds.ToString("00", CultureInfo.InvariantCulture) + : string.Empty); /// public override void CopyFrom(ICopyable obj) diff --git a/Ical.Net/Ical.Net.csproj b/Ical.Net/Ical.Net.csproj index 89e558aa..08627e3e 100644 --- a/Ical.Net/Ical.Net.csproj +++ b/Ical.Net/Ical.Net.csproj @@ -8,6 +8,7 @@ + diff --git a/Ical.Net/Serialization/DataTypes/DateTimeSerializer.cs b/Ical.Net/Serialization/DataTypes/DateTimeSerializer.cs index 5e557b15..73848981 100644 --- a/Ical.Net/Serialization/DataTypes/DateTimeSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/DateTimeSerializer.cs @@ -6,6 +6,7 @@ using Ical.Net.DataTypes; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Text; using System.Text.RegularExpressions; @@ -44,10 +45,11 @@ public DateTimeSerializer(SerializationContext ctx) : base(ctx) { } // properties whose time values are specified in UTC. var value = new StringBuilder(512); - value.Append($"{dt.Year:0000}{dt.Month:00}{dt.Day:00}"); + // NOSONAR: netstandard2.x does not support string.Create(CultureInfo.InvariantCulture, $"{...}"); + value.Append(FormattableString.Invariant($"{dt.Year:0000}{dt.Month:00}{dt.Day:00}")); // NOSONAR if (dt.HasTime) { - value.Append($"T{dt.Hour:00}{dt.Minute:00}{dt.Second:00}"); + value.Append(FormattableString.Invariant($"T{dt.Hour:00}{dt.Minute:00}{dt.Second:00}")); // NOSONAR if (dt.IsUtc) { value.Append("Z"); @@ -89,15 +91,15 @@ public DateTimeSerializer(SerializationContext ctx) : base(ctx) { } if (match.Groups[1].Success) { - datePart = new DateOnly(Convert.ToInt32(match.Groups[2].Value), - Convert.ToInt32(match.Groups[3].Value), - Convert.ToInt32(match.Groups[4].Value)); + datePart = new DateOnly(Convert.ToInt32(match.Groups[2].Value, CultureInfo.InvariantCulture), + Convert.ToInt32(match.Groups[3].Value, CultureInfo.InvariantCulture), + Convert.ToInt32(match.Groups[4].Value, CultureInfo.InvariantCulture)); } if (match.Groups.Count >= 6 && match.Groups[5].Success) { - timePart = new TimeOnly(Convert.ToInt32(match.Groups[6].Value), - Convert.ToInt32(match.Groups[7].Value), - Convert.ToInt32(match.Groups[8].Value)); + timePart = new TimeOnly(Convert.ToInt32(match.Groups[6].Value, CultureInfo.InvariantCulture), + Convert.ToInt32(match.Groups[7].Value, CultureInfo.InvariantCulture), + Convert.ToInt32(match.Groups[8].Value, CultureInfo.InvariantCulture)); } var isUtc = match.Groups[9].Success; diff --git a/Ical.Net/Serialization/DataTypes/DurationSerializer.cs b/Ical.Net/Serialization/DataTypes/DurationSerializer.cs index 4f15c95f..616f192c 100644 --- a/Ical.Net/Serialization/DataTypes/DurationSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/DurationSerializer.cs @@ -4,6 +4,7 @@ // using System; +using System.Globalization; using System.IO; using System.Text; using System.Text.RegularExpressions; @@ -34,20 +35,22 @@ private static string SerializeToString(Duration ts) sb.Append('-'); sb.Append('P'); + // NOSONAR: netstandard2.x does not support string.Create(CultureInfo.InvariantCulture, $"{...}"); if (ts.Weeks != null) - sb.Append($"{sign * ts.Weeks}W"); + sb.Append(FormattableString.Invariant($"{sign * ts.Weeks}W")); // NOSONAR if (ts.Days != null) - sb.Append($"{sign * ts.Days}D"); + sb.Append(FormattableString.Invariant($"{sign * ts.Days}D")); // NOSONAR if (ts.Hours != null || ts.Minutes != null || ts.Seconds != null) { sb.Append('T'); + if (ts.Hours != null) - sb.Append($"{sign * ts.Hours}H"); + sb.Append(FormattableString.Invariant($"{sign * ts.Hours}H")); // NOSONAR if (ts.Minutes != null) - sb.Append($"{sign * ts.Minutes}M"); + sb.Append(FormattableString.Invariant($"{sign * ts.Minutes}M")); // NOSONAR if (ts.Seconds != null) - sb.Append($"{sign * ts.Seconds}S"); + sb.Append(FormattableString.Invariant($"{sign * ts.Seconds}S")); // NOSONAR } return sb.ToString(); @@ -91,7 +94,7 @@ private static string SerializeToString(Duration ts) sign = -1; int? GetGroupInt(string key) - => match.Groups[key].Success ? Convert.ToInt32(match.Groups[key].Value) : null; + => match.Groups[key].Success ? Convert.ToInt32(match.Groups[key].Value, CultureInfo.InvariantCulture) : null; weeks = GetGroupInt("week"); if (match.Groups["main"].Success) diff --git a/Ical.Net/Serialization/DataTypes/IntegerSerializer.cs b/Ical.Net/Serialization/DataTypes/IntegerSerializer.cs index 9a50f2b2..56acb1a3 100644 --- a/Ical.Net/Serialization/DataTypes/IntegerSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/IntegerSerializer.cs @@ -4,6 +4,7 @@ // using System; +using System.Globalization; using System.IO; using Ical.Net.DataTypes; @@ -21,7 +22,7 @@ public IntegerSerializer(SerializationContext ctx) : base(ctx) { } { try { - var i = Convert.ToInt32(obj); + var i = Convert.ToInt32(obj, CultureInfo.InvariantCulture); if (SerializationContext.Peek() is ICalendarObject calObject) { @@ -30,9 +31,9 @@ public IntegerSerializer(SerializationContext ctx) : base(ctx) { } { AssociatedObject = calObject }; - return Encode(dt, i.ToString()); + return Encode(dt, i.ToString(CultureInfo.InvariantCulture)); } - return i.ToString(); + return i.ToString(CultureInfo.InvariantCulture); } catch { diff --git a/Ical.Net/Serialization/DataTypes/RecurrencePatternSerializer.cs b/Ical.Net/Serialization/DataTypes/RecurrencePatternSerializer.cs index 12833099..80c3af31 100644 --- a/Ical.Net/Serialization/DataTypes/RecurrencePatternSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/RecurrencePatternSerializer.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using Ical.Net.DataTypes; @@ -38,7 +39,7 @@ protected static void AddInt32Values(IList list, string value) var values = value.Split(','); foreach (var v in values) { - list.Add(Convert.ToInt32(v)); + list.Add(Convert.ToInt32(v, CultureInfo.InvariantCulture)); } } @@ -96,7 +97,7 @@ private static void SerializeByValue(List aggregate, IList byValue, { if (byValue.Any()) { - aggregate.Add($"{name}={string.Join(",", byValue.Select(i => i.ToString()))}"); + aggregate.Add($"{name}={string.Join(",", byValue.Select(i => i.ToString(CultureInfo.InvariantCulture)))}"); } } @@ -265,11 +266,11 @@ private void ProcessKeyValuePair(string key, string value, RecurrencePattern r, break; case "count": - r.Count = Convert.ToInt32(value); + r.Count = Convert.ToInt32(value, CultureInfo.InvariantCulture); break; case "interval": - r.Interval = Convert.ToInt32(value); + r.Interval = Convert.ToInt32(value, CultureInfo.InvariantCulture); break; case "bysecond": diff --git a/Ical.Net/Serialization/DataTypes/StatusCodeSerializer.cs b/Ical.Net/Serialization/DataTypes/StatusCodeSerializer.cs index 595287ed..deead0b8 100644 --- a/Ical.Net/Serialization/DataTypes/StatusCodeSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/StatusCodeSerializer.cs @@ -4,6 +4,7 @@ // using System; +using System.Globalization; using System.IO; using System.Text.RegularExpressions; using Ical.Net.DataTypes; @@ -28,7 +29,7 @@ public StatusCodeSerializer(SerializationContext ctx) : base(ctx) { } var vals = new string[sc.Parts.Length]; for (var i = 0; i < sc.Parts.Length; i++) { - vals[i] = sc.Parts[i].ToString(); + vals[i] = sc.Parts[i].ToString(CultureInfo.InvariantCulture); } return Encode(sc, Escape(string.Join(".", vals))); } diff --git a/Ical.Net/Serialization/DataTypes/UtcOffsetSerializer.cs b/Ical.Net/Serialization/DataTypes/UtcOffsetSerializer.cs index 0e62c0ba..aff8f0f3 100644 --- a/Ical.Net/Serialization/DataTypes/UtcOffsetSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/UtcOffsetSerializer.cs @@ -22,8 +22,9 @@ public UtcOffsetSerializer(SerializationContext ctx) : base(ctx) { } { if (obj is not UtcOffset offset) return null; - var value = (offset.Positive ? "+" : "-") + offset.Hours.ToString("00") + offset.Minutes.ToString("00") + - (offset.Seconds != 0 ? offset.Seconds.ToString("00") : string.Empty); + var value = (offset.Positive ? "+" : "-") + offset.Hours.ToString("00", CultureInfo.InvariantCulture) + + offset.Minutes.ToString("00", CultureInfo.InvariantCulture) + + (offset.Seconds != 0 ? offset.Seconds.ToString("00", CultureInfo.InvariantCulture) : string.Empty); // Encode the value as necessary return Encode(offset, value); diff --git a/Ical.Net/Serialization/DataTypes/WeekDaySerializer.cs b/Ical.Net/Serialization/DataTypes/WeekDaySerializer.cs index 721e16b5..ed725220 100644 --- a/Ical.Net/Serialization/DataTypes/WeekDaySerializer.cs +++ b/Ical.Net/Serialization/DataTypes/WeekDaySerializer.cs @@ -4,6 +4,7 @@ // using System; +using System.Globalization; using System.IO; using System.Text.RegularExpressions; using Ical.Net.DataTypes; @@ -68,7 +69,7 @@ public WeekDaySerializer(SerializationContext ctx) : base(ctx) { } if (match.Groups[2].Success) { - ds.Offset = Convert.ToInt32(match.Groups[2].Value); + ds.Offset = Convert.ToInt32(match.Groups[2].Value, CultureInfo.InvariantCulture); if (match.Groups[1].Success && match.Groups[1].Value.Contains("-")) { ds.Offset *= -1;