diff --git a/doc/devdocs/modules/launcher/plugins/timedate.md b/doc/devdocs/modules/launcher/plugins/timedate.md index ca04b53293..d7dece2ff0 100644 --- a/doc/devdocs/modules/launcher/plugins/timedate.md +++ b/doc/devdocs/modules/launcher/plugins/timedate.md @@ -71,12 +71,14 @@ The following formats are currently available: - All available settings for the plugin are defined in the [`TimeDateSettings`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeDateSettings.cs) class of the plugin. The settings can be accessed everywhere in the plugin code via the static class instance `TimeDateSettings.Instance`. - We have the following settings that the user can configure to change the behavior of the plugin: - | Key | Default value | Name | Description | - |--------------|-----------|------------|------------| - | `OnlyDateTimeNowGlobal` | `true` | Show only 'Time', 'Date', and 'Now' result for system time on global queries | Regardless of this setting, for global queries the first word of the query has to be a complete match. | - | `TimeWithSeconds` | `false` | Show time with seconds | This setting applies to the 'Time' and 'Now' result. | - | `DateWithWeekday` | `false` | Show date with weekday and name of month | This setting applies to the 'Date' and 'Now' result. | - | `HideNumberMessageOnGlobalQuery` | `false` | Hide 'Invalid number input' error message on global queries | | + | Key | Type | Default value | Name | Description | + |--------------|--------------|-----------|------------|------------| + | `CalendarFirstWeekRule` | Combo box | `-1` (Use system settings) | First week of the year | Configure the calendar rule for the first week of the year. | + | `FirstDayOfWeek` | Combo box | `-1` (Use system settings) | First day of the week | | + | `OnlyDateTimeNowGlobal` | Checkbox | `true` | Show only 'Time', 'Date', and 'Now' result for system time on global queries | Regardless of this setting, for global queries the first word of the query has to be a complete match. | + | `TimeWithSeconds` | Checkbox | `false` | Show time with seconds | This setting applies to the 'Time' and 'Now' result. | + | `DateWithWeekday` | Checkbox | `false` | Show date with weekday and name of month | This setting applies to the 'Date' and 'Now' result. | + | `HideNumberMessageOnGlobalQuery` | Checkbox | `false` | Hide 'Invalid number input' error message on global queries | | ## Classes @@ -97,6 +99,7 @@ The following formats are currently available: ### [`TimeAndDateHelper.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs) - The [`TimeAndDateHelper`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs) class contains methods to format/convert date and time formats/strings. +- And it contains methods to return the `first week day` and `first week of the year rule` based on the current plugin settings. ### [`TimeDateSettings.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeDateSettings.cs) - The [`TimeDateSettings`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeDateSettings.cs) class provides access to all optional plugin settings. @@ -129,11 +132,6 @@ On global queries the high score returned by `FuzzySearch` has negative impacts ## [Unit Tests](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests) We have a [Unit Test project](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests) that executes various test to ensure that the plugin works as expected. -### [`TimeDateResultTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs) -- The [`TimeDateResultTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs) class contains tests to validate that the time and date values are correctly formatted/calculated. -- That we can execute the tests at any time on any machine, we use a specified date/time value and set the thread culture always to `en-us` while executing the tests. -- Some tests contain checks that calculate the expected result at runtime instead of using an expected value written fix in the code. This is done to get valid results on every machine at any time. - ### [`ImageTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/ImageTests.cs) - The [`ImageTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/ImageTests.cs) class contains tests to validate that each result shows the expected and correct image. - That we can execute the tests at any time on any machine, we set the thread culture always to `en-us` while executing the tests. @@ -147,4 +145,13 @@ We have a [Unit Test project](/src/modules/launcher/Plugins/Microsoft.PowerToys. ### [`StringParserTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/StringParserTests.cs) - The [`StringParserTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/StringParserTests.cs) class contains tests to validate that the typed string gets converted correctly into a `DateTime` object. -- That we can execute the tests at any time on any machine, we set the thread culture always to `en-us` while executing the tests. \ No newline at end of file +- That we can execute the tests at any time on any machine, we set the thread culture always to `en-us` while executing the tests. + +### [`TimeAndDateHelperTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeAndDateHelperTests.cs) +- The [`TimeAndDateHelperTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeAndDateHelperTests.cs) class contains tests to validate important methods form the `TimeAndDateHelper` class that are not used for string parsing. + +### [`TimeDateResultTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs) +- The [`TimeDateResultTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs) class contains tests to validate that the time and date values are correctly formatted/calculated. +- That we can execute the tests at any time on any machine, we use a specified date/time value and set the thread culture always to `en-us` while executing the tests. +- Some tests use custom settings for the first day of week and the first week of year. (This is done in the tests for the affected results to validate them for different settings/cultures.) +- Some tests contain checks that calculate the expected result at runtime instead of using an expected value written fix in the code. This is done to get valid results on every machine at any time. \ No newline at end of file diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/PluginSettingsTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/PluginSettingsTests.cs index d87bf92f0a..f87da47eae 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/PluginSettingsTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/PluginSettingsTests.cs @@ -22,10 +22,12 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests var result = settings?.Length; // Assert - Assert.AreEqual(4, result); + Assert.AreEqual(6, result); } [DataTestMethod] + [DataRow("CalendarFirstWeekRule")] + [DataRow("FirstDayOfWeek")] [DataRow("OnlyDateTimeNowGlobal")] [DataRow("TimeWithSeconds")] [DataRow("DateWithWeekday")] @@ -59,5 +61,21 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests // Assert Assert.AreEqual(valueExpected, result); } + + [DataTestMethod] + [DataRow("CalendarFirstWeekRule", -1)] + [DataRow("FirstDayOfWeek", -1)] + public void DefaultEnumValues(string name, int valueExpected) + { + // Setup + TimeDateSettings setting = TimeDateSettings.Instance; + + // Act + PropertyInfo propertyInfo = setting?.GetType()?.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Instance); + var result = propertyInfo?.GetValue(setting); + + // Assert + Assert.AreEqual(valueExpected, result); + } } } diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeAndDateHelperTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeAndDateHelperTests.cs new file mode 100644 index 0000000000..a8ceb8387d --- /dev/null +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeAndDateHelperTests.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Globalization; +using Microsoft.PowerToys.Run.Plugin.TimeDate.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests +{ + [TestClass] + public class TimeAndDateHelperTests + { + [DataTestMethod] + [DataRow(-1, null)] // default setting + [DataRow(0, CalendarWeekRule.FirstDay)] + [DataRow(1, CalendarWeekRule.FirstFullWeek)] + [DataRow(2, CalendarWeekRule.FirstFourDayWeek)] + [DataRow(30, null)] // wrong setting + public void GetCalendarWeekRuleBasedOnPluginSetting(int setting, CalendarWeekRule? valueExpected) + { + // Act + var result = TimeAndDateHelper.GetCalendarWeekRule(setting); + + // Assert + if (valueExpected == null) + { + // falls back to system setting. + Assert.AreEqual(DateTimeFormatInfo.CurrentInfo.CalendarWeekRule, result); + } + else + { + Assert.AreEqual(valueExpected, result); + } + } + + [DataTestMethod] + [DataRow(-1, null)] // default setting + [DataRow(1, DayOfWeek.Monday)] + [DataRow(2, DayOfWeek.Tuesday)] + [DataRow(3, DayOfWeek.Wednesday)] + [DataRow(4, DayOfWeek.Thursday)] + [DataRow(5, DayOfWeek.Friday)] + [DataRow(6, DayOfWeek.Saturday)] + [DataRow(0, DayOfWeek.Sunday)] + [DataRow(70, null)] // wrong setting + public void GetFirstDayOfWeekBasedOnPluginSetting(int setting, DayOfWeek? valueExpected) + { + // Act + var result = TimeAndDateHelper.GetFirstDayOfWeek(setting); + + // Assert + if (valueExpected == null) + { + // falls back to system setting. + Assert.AreEqual(DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek, result); + } + else + { + Assert.AreEqual(valueExpected, result); + } + } + } +} diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs index 8cf602ab6d..b5ff2ddb1c 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs @@ -65,7 +65,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests public void LocalFormatsWithShortTimeAndShortDate(string formatLabel, string expectedResult) { // Setup - var helperResults = AvailableResultsList.GetList(true, false, false, GetDateTimeForTest()); + var helperResults = AvailableResultsList.GetList(true, false, false, GetDateTimeForTest(), CalendarWeekRule.FirstDay, DayOfWeek.Sunday); // Act var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase)); @@ -100,7 +100,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests public void LocalFormatsWithShortTimeAndLongDate(string formatLabel, string expectedResult) { // Setup - var helperResults = AvailableResultsList.GetList(true, false, true, GetDateTimeForTest()); + var helperResults = AvailableResultsList.GetList(true, false, true, GetDateTimeForTest(), CalendarWeekRule.FirstDay, DayOfWeek.Sunday); // Act var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase)); @@ -135,7 +135,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests public void LocalFormatsWithLongTimeAndShortDate(string formatLabel, string expectedResult) { // Setup - var helperResults = AvailableResultsList.GetList(true, true, false, GetDateTimeForTest()); + var helperResults = AvailableResultsList.GetList(true, true, false, GetDateTimeForTest(), CalendarWeekRule.FirstDay, DayOfWeek.Sunday); // Act var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase)); @@ -170,7 +170,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests public void LocalFormatsWithLongTimeAndLongDate(string formatLabel, string expectedResult) { // Setup - var helperResults = AvailableResultsList.GetList(true, true, true, GetDateTimeForTest()); + var helperResults = AvailableResultsList.GetList(true, true, true, GetDateTimeForTest(), CalendarWeekRule.FirstDay, DayOfWeek.Sunday); // Act var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase)); @@ -189,7 +189,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests public void UtcFormatsWithShortTimeAndShortDate(string formatLabel, string expectedFormat) { // Setup - var helperResults = AvailableResultsList.GetList(true, false, false, GetDateTimeForTest(true)); + var helperResults = AvailableResultsList.GetList(true, false, false, GetDateTimeForTest(true), CalendarWeekRule.FirstDay, DayOfWeek.Sunday); var expectedResult = GetDateTimeForTest().ToString(expectedFormat, CultureInfo.CurrentCulture); // Act @@ -209,7 +209,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests public void UtcFormatsWithShortTimeAndLongDate(string formatLabel, string expectedFormat) { // Setup - var helperResults = AvailableResultsList.GetList(true, false, true, GetDateTimeForTest(true)); + var helperResults = AvailableResultsList.GetList(true, false, true, GetDateTimeForTest(true), CalendarWeekRule.FirstDay, DayOfWeek.Sunday); var expectedResult = GetDateTimeForTest().ToString(expectedFormat, CultureInfo.CurrentCulture); // Act @@ -229,7 +229,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests public void UtcFormatsWithLongTimeAndShortDate(string formatLabel, string expectedFormat) { // Setup - var helperResults = AvailableResultsList.GetList(true, true, false, GetDateTimeForTest(true)); + var helperResults = AvailableResultsList.GetList(true, true, false, GetDateTimeForTest(true), CalendarWeekRule.FirstDay, DayOfWeek.Sunday); var expectedResult = GetDateTimeForTest().ToString(expectedFormat, CultureInfo.CurrentCulture); // Act @@ -249,7 +249,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests public void UtcFormatsWithLongTimeAndLongDate(string formatLabel, string expectedFormat) { // Setup - var helperResults = AvailableResultsList.GetList(true, true, true, GetDateTimeForTest(true)); + var helperResults = AvailableResultsList.GetList(true, true, true, GetDateTimeForTest(true), CalendarWeekRule.FirstDay, DayOfWeek.Sunday); var expectedResult = GetDateTimeForTest().ToString(expectedFormat, CultureInfo.CurrentCulture); // Act @@ -265,7 +265,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests // Setup string formatLabel = "Unix epoch time"; DateTime timeValue = DateTime.Now.ToUniversalTime(); - var helperResults = AvailableResultsList.GetList(true, false, false, timeValue); + var helperResults = AvailableResultsList.GetList(true, false, false, timeValue, CalendarWeekRule.FirstDay, DayOfWeek.Sunday); var expectedResult = (long)timeValue.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; // Act @@ -281,7 +281,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests // Setup string formatLabel = "Unix epoch time in milliseconds"; DateTime timeValue = DateTime.Now.ToUniversalTime(); - var helperResults = AvailableResultsList.GetList(true, false, false, timeValue); + var helperResults = AvailableResultsList.GetList(true, false, false, timeValue, CalendarWeekRule.FirstDay, DayOfWeek.Sunday); var expectedResult = (long)timeValue.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds; // Act @@ -297,7 +297,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests // Setup string formatLabel = "Windows file time (Int64 number)"; DateTime timeValue = DateTime.Now; - var helperResults = AvailableResultsList.GetList(true, false, false, timeValue); + var helperResults = AvailableResultsList.GetList(true, false, false, timeValue, CalendarWeekRule.FirstDay, DayOfWeek.Sunday); var expectedResult = timeValue.ToFileTime().ToString(CultureInfo.CurrentCulture); // Act @@ -313,7 +313,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests // Setup string formatLabel = "Era"; DateTime timeValue = DateTime.Now; - var helperResults = AvailableResultsList.GetList(true, false, false, timeValue); + var helperResults = AvailableResultsList.GetList(true, false, false, timeValue, CalendarWeekRule.FirstDay, DayOfWeek.Sunday); var expectedResult = DateTimeFormatInfo.CurrentInfo.GetEraName(CultureInfo.CurrentCulture.Calendar.GetEra(timeValue)); // Act @@ -329,7 +329,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests // Setup string formatLabel = "Era abbreviation"; DateTime timeValue = DateTime.Now; - var helperResults = AvailableResultsList.GetList(true, false, false, timeValue); + var helperResults = AvailableResultsList.GetList(true, false, false, timeValue, CalendarWeekRule.FirstDay, DayOfWeek.Sunday); var expectedResult = DateTimeFormatInfo.CurrentInfo.GetAbbreviatedEraName(CultureInfo.CurrentCulture.Calendar.GetEra(timeValue)); // Act @@ -339,6 +339,48 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests Assert.AreEqual(expectedResult, result?.Value); } + [DataTestMethod] + [DataRow(CalendarWeekRule.FirstDay, "3")] + [DataRow(CalendarWeekRule.FirstFourDayWeek, "2")] + [DataRow(CalendarWeekRule.FirstFullWeek, "2")] + public void DifferentFirstWeekSettingConfigurations(CalendarWeekRule weekRule, string expectedWeekOfYear) + { + // Setup + DateTime timeValue = new DateTime(2021, 1, 12); + var helperResults = AvailableResultsList.GetList(true, false, false, timeValue, weekRule, DayOfWeek.Sunday); + + // Act + var resultWeekOfYear = helperResults.FirstOrDefault(x => x.Label.Equals("week of the year (calendar week, week number)", StringComparison.OrdinalIgnoreCase)); + + // Assert + Assert.AreEqual(expectedWeekOfYear, resultWeekOfYear?.Value); + } + + [DataTestMethod] + [DataRow(DayOfWeek.Monday, "2", "2", "5")] + [DataRow(DayOfWeek.Tuesday, "3", "3", "4")] + [DataRow(DayOfWeek.Wednesday, "3", "3", "3")] + [DataRow(DayOfWeek.Thursday, "3", "3", "2")] + [DataRow(DayOfWeek.Friday, "3", "3", "1")] + [DataRow(DayOfWeek.Saturday, "2", "2", "7")] + [DataRow(DayOfWeek.Sunday, "2", "2", "6")] + public void DifferentFirstDayOfWeekSettingConfigurations(DayOfWeek dayOfWeek, string expectedWeekOfYear, string expectedWeekOfMonth, string expectedDayInWeek) + { + // Setup + DateTime timeValue = new DateTime(2024, 1, 12); // Friday + var helperResults = AvailableResultsList.GetList(true, false, false, timeValue, CalendarWeekRule.FirstDay, dayOfWeek); + + // Act + var resultWeekOfYear = helperResults.FirstOrDefault(x => x.Label.Equals("week of the year (calendar week, week number)", StringComparison.OrdinalIgnoreCase)); + var resultWeekOfMonth = helperResults.FirstOrDefault(x => x.Label.Equals("week of the month", StringComparison.OrdinalIgnoreCase)); + var resultDayInWeek = helperResults.FirstOrDefault(x => x.Label.Equals("day of the week (week day)", StringComparison.OrdinalIgnoreCase)); + + // Assert + Assert.AreEqual(expectedWeekOfYear, resultWeekOfYear?.Value); + Assert.AreEqual(expectedWeekOfMonth, resultWeekOfMonth?.Value); + Assert.AreEqual(expectedDayInWeek, resultDayInWeek?.Value); + } + [TestCleanup] public void CleanUp() { diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs index 641fa3530a..6e1e105143 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs @@ -10,25 +10,30 @@ using Microsoft.PowerToys.Run.Plugin.TimeDate.Properties; namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components { - internal class AvailableResultsList + internal static class AvailableResultsList { /// /// Returns a list with all available date time formats /// /// Is this a search with plugin activation keyword or not - /// Required for UnitTest: Show time in long format - /// Required for UnitTest: Show date in long format - /// Required for UnitTest: Use custom object to calculate results + /// Required for UnitTest: Show time in long format + /// Required for UnitTest: Show date in long format + /// Use custom object to calculate results instead of the system date/time + /// Required for UnitTest: Use custom first week of the year instead of the plugin setting. + /// Required for UnitTest: Use custom first day of the week instead the plugin setting. /// List of results - internal static List GetList(bool isKeywordSearch, bool? timeLong = null, bool? dateLong = null, DateTime? timestamp = null) + internal static List GetList(bool isKeywordSearch, bool? timeLongFormat = null, bool? dateLongFormat = null, DateTime? timestamp = null, CalendarWeekRule? firstWeekOfYear = null, DayOfWeek? firstDayOfWeek = null) { List results = new List(); - bool timeExtended = timeLong ?? TimeDateSettings.Instance.TimeWithSeconds; - bool dateExtended = dateLong ?? TimeDateSettings.Instance.DateWithWeekday; - bool isSystemDateTime = timestamp == null; Calendar calendar = CultureInfo.CurrentCulture.Calendar; + + bool timeExtended = timeLongFormat ?? TimeDateSettings.Instance.TimeWithSeconds; + bool dateExtended = dateLongFormat ?? TimeDateSettings.Instance.DateWithWeekday; + bool isSystemDateTime = timestamp == null; DateTime dateTimeNow = timestamp ?? DateTime.Now; DateTime dateTimeNowUtc = dateTimeNow.ToUniversalTime(); + CalendarWeekRule firstWeekRule = firstWeekOfYear ?? TimeAndDateHelper.GetCalendarWeekRule(TimeDateSettings.Instance.CalendarFirstWeekRule); + DayOfWeek firstDayOfTheWeek = firstDayOfWeek ?? TimeAndDateHelper.GetFirstDayOfWeek(TimeDateSettings.Instance.FirstDayOfWeek); results.AddRange(new[] { @@ -62,7 +67,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components // We use long instead of int for unix time stamp because int is too small after 03:14:07 UTC 2038-01-19 long unixTimestamp = ((DateTimeOffset)dateTimeNowUtc).ToUnixTimeSeconds(); long unixTimestampMilliseconds = ((DateTimeOffset)dateTimeNowUtc).ToUnixTimeMilliseconds(); - int weekOfYear = calendar.GetWeekOfYear(dateTimeNow, DateTimeFormatInfo.CurrentInfo.CalendarWeekRule, DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek); + int weekOfYear = calendar.GetWeekOfYear(dateTimeNow, firstWeekRule, firstDayOfTheWeek); string era = DateTimeFormatInfo.CurrentInfo.GetEraName(calendar.GetEra(dateTimeNow)); string eraShort = DateTimeFormatInfo.CurrentInfo.GetAbbreviatedEraName(calendar.GetEra(dateTimeNow)); @@ -133,7 +138,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components }, new AvailableResult() { - Value = TimeAndDateHelper.GetNumberOfDayInWeek(dateTimeNow).ToString(CultureInfo.CurrentCulture), + Value = TimeAndDateHelper.GetNumberOfDayInWeek(dateTimeNow, firstDayOfTheWeek).ToString(CultureInfo.CurrentCulture), Label = Resources.Microsoft_plugin_timedate_DayOfWeek, AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"), IconType = ResultIconType.Date, @@ -154,7 +159,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components }, new AvailableResult() { - Value = TimeAndDateHelper.GetWeekOfMonth(dateTimeNow).ToString(CultureInfo.CurrentCulture), + Value = TimeAndDateHelper.GetWeekOfMonth(dateTimeNow, firstDayOfTheWeek).ToString(CultureInfo.CurrentCulture), Label = Resources.Microsoft_plugin_timedate_WeekOfMonth, AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"), IconType = ResultIconType.Date, diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs index 55668dc8dc..72379c14cb 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs @@ -57,16 +57,17 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components /// /// date /// Number of week in the month - internal static int GetWeekOfMonth(DateTime date) + internal static int GetWeekOfMonth(DateTime date, DayOfWeek formatSettingFirstDayOfWeek) { DateTime beginningOfMonth = new DateTime(date.Year, date.Month, 1); + int adjustment = 1; // We count from 1 to 7 and not from 0 to 6 - while (date.Date.AddDays(1).DayOfWeek != CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek) + while (date.Date.AddDays(1).DayOfWeek != formatSettingFirstDayOfWeek) { date = date.AddDays(1); } - return (int)Math.Truncate((double)date.Subtract(beginningOfMonth).TotalDays / 7f) + 1; + return (int)Math.Truncate((double)date.Subtract(beginningOfMonth).TotalDays / 7f) + adjustment; } /// @@ -74,13 +75,12 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components /// /// Date /// Number of the day in the week - internal static int GetNumberOfDayInWeek(DateTime date) + internal static int GetNumberOfDayInWeek(DateTime date, DayOfWeek formatSettingFirstDayOfWeek) { int daysInWeek = 7; int adjustment = 1; // We count from 1 to 7 and not from 0 to 6 - int formatSettingFirstDayOfWeek = (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek; - return ((int)(date.DayOfWeek + daysInWeek - formatSettingFirstDayOfWeek) % daysInWeek) + adjustment; + return ((date.DayOfWeek + daysInWeek - formatSettingFirstDayOfWeek) % daysInWeek) + adjustment; } /// @@ -133,6 +133,52 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components { return Regex.IsMatch(input, @"^.*(u|ums|ft)\d"); } + + /// + /// Returns a CalendarWeekRule enum value based on the plugin setting. + /// + internal static CalendarWeekRule GetCalendarWeekRule(int pluginSetting) + { + switch (pluginSetting) + { + case 0: + return CalendarWeekRule.FirstDay; + case 1: + return CalendarWeekRule.FirstFullWeek; + case 2: + return CalendarWeekRule.FirstFourDayWeek; + default: + // Wrong json value and system setting (-1). + return DateTimeFormatInfo.CurrentInfo.CalendarWeekRule; + } + } + + /// + /// Returns a DayOfWeek enum value based on the FirstDayOfWeek plugin setting. + /// + internal static DayOfWeek GetFirstDayOfWeek(int pluginSetting) + { + switch (pluginSetting) + { + case 0: + return DayOfWeek.Sunday; + case 1: + return DayOfWeek.Monday; + case 2: + return DayOfWeek.Tuesday; + case 3: + return DayOfWeek.Wednesday; + case 4: + return DayOfWeek.Thursday; + case 5: + return DayOfWeek.Friday; + case 6: + return DayOfWeek.Saturday; + default: + // Wrong json value and system setting (-1). + return DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek; + } + } } /// diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeDateSettings.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeDateSettings.cs index a4e3ff8377..cd38a40bff 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeDateSettings.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeDateSettings.cs @@ -2,7 +2,9 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Runtime.CompilerServices; using Microsoft.PowerToys.Run.Plugin.TimeDate.Properties; @@ -28,6 +30,16 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components /// private static TimeDateSettings instance; + /// + /// Gets the value of the "First Week Rule" setting + /// + internal int CalendarFirstWeekRule { get; private set; } + + /// + /// Gets the value of the "First Day Of Week" setting + /// + internal int FirstDayOfWeek { get; private set; } + /// /// Gets a value indicating whether to show only the time and date for system time in global results or not /// @@ -87,6 +99,29 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components { var optionList = new List { + new PluginAdditionalOption() + { + Key = nameof(CalendarFirstWeekRule), + DisplayLabel = Resources.Microsoft_plugin_timedate_SettingFirstWeekRule, + DisplayDescription = Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_Description, + PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Combobox, + ComboBoxItems = new List> + { + new KeyValuePair(Resources.Microsoft_plugin_timedate_Setting_UseSystemSetting, "-1"), + new KeyValuePair(Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_FirstDay, "0"), + new KeyValuePair(Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFullWeek, "1"), + new KeyValuePair(Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFourDayWeek, "2"), + }, + ComboBoxValue = -1, + }, + new PluginAdditionalOption() + { + Key = nameof(FirstDayOfWeek), + DisplayLabel = Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek, + PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Combobox, + ComboBoxItems = GetSortedListForWeekDaySetting(), + ComboBoxValue = -1, + }, new PluginAdditionalOption() { Key = nameof(OnlyDateTimeNowGlobal), @@ -130,6 +165,8 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components return; } + CalendarFirstWeekRule = GetEnumSettingOrDefault(settings, nameof(CalendarFirstWeekRule)); + FirstDayOfWeek = GetEnumSettingOrDefault(settings, nameof(FirstDayOfWeek)); OnlyDateTimeNowGlobal = GetSettingOrDefault(settings, nameof(OnlyDateTimeNowGlobal)); TimeWithSeconds = GetSettingOrDefault(settings, nameof(TimeWithSeconds)); DateWithWeekday = GetSettingOrDefault(settings, nameof(DateWithWeekday)); @@ -150,5 +187,57 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components // We can use First() instead of FirstOrDefault() because the values must exist. Otherwise, we made a mistake when defining the settings. return option?.Value ?? GetAdditionalOptions().First(x => x.Key == name).Value; } + + /// + /// Return the combobox value of the given settings list with the given name. + /// + /// The object that contain all settings. + /// The name of the setting. + /// A settings value. + private static int GetEnumSettingOrDefault(PowerLauncherPluginSettings settings, string name) + { + var option = settings?.AdditionalOptions?.FirstOrDefault(x => x.Key == name); + + // If a setting isn't available, we use the value defined in the method GetAdditionalOptions() as fallback. + // We can use First() instead of FirstOrDefault() because the values must exist. Otherwise, we made a mistake when defining the settings. + return option?.ComboBoxValue ?? GetAdditionalOptions().First(x => x.Key == name).ComboBoxValue; + } + + /// + /// Returns a sorted list of values for the combo box of 'first day of week' setting. + /// The list is sorted based on the current system culture setting. + /// + /// In the world we have three groups of countries: Saturday, Sunday, Monday (Everything else is chosen by the user.) + /// List of values for combo box. + private static List> GetSortedListForWeekDaySetting() + { + // List (Sorted for first day is Sunday) + var list = new List> + { + new KeyValuePair(Resources.Microsoft_plugin_timedate_Setting_UseSystemSetting, "-1"), + new KeyValuePair(Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek_Sunday, "0"), + new KeyValuePair(Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek_Monday, "1"), + new KeyValuePair(Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek_Tuesday, "2"), + new KeyValuePair(Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek_Wednesday, "3"), + new KeyValuePair(Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek_Thursday, "4"), + new KeyValuePair(Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek_Friday, "5"), + new KeyValuePair(Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek_Saturday, "6"), + }; + + // Order Rules + string[] orderRuleSaturday = new string[] { "-1", "6", "0", "1", "2", "3", "4", "5" }; + string[] orderRuleMonday = new string[] { "-1", "1", "2", "3", "4", "5", "6", "0" }; + + switch (DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek) + { + case DayOfWeek.Saturday: + return list.OrderBy(x => Array.IndexOf(orderRuleSaturday, x.Value)).ToList(); + case DayOfWeek.Monday: + return list.OrderBy(x => Array.IndexOf(orderRuleMonday, x.Value)).ToList(); + default: + // DayOfWeek.Sunday + return list; + } + } } } diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.Designer.cs index ae496cd95c..da32b13a6d 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.Designer.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.Designer.cs @@ -438,6 +438,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties { } } + /// + /// Looks up a localized string similar to Use system setting. + /// + internal static string Microsoft_plugin_timedate_Setting_UseSystemSetting { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_Setting_UseSystemSetting", resourceCulture); + } + } + /// /// Looks up a localized string similar to Show date with weekday and name of month. /// @@ -456,6 +465,123 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties { } } + /// + /// Looks up a localized string similar to First day of the week. + /// + internal static string Microsoft_plugin_timedate_SettingFirstDayOfWeek { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstDayOfWeek", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Friday. + /// + internal static string Microsoft_plugin_timedate_SettingFirstDayOfWeek_Friday { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstDayOfWeek_Friday", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Monday. + /// + internal static string Microsoft_plugin_timedate_SettingFirstDayOfWeek_Monday { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstDayOfWeek_Monday", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Saturday. + /// + internal static string Microsoft_plugin_timedate_SettingFirstDayOfWeek_Saturday { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstDayOfWeek_Saturday", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sunday. + /// + internal static string Microsoft_plugin_timedate_SettingFirstDayOfWeek_Sunday { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstDayOfWeek_Sunday", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Thursday. + /// + internal static string Microsoft_plugin_timedate_SettingFirstDayOfWeek_Thursday { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstDayOfWeek_Thursday", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tuesday. + /// + internal static string Microsoft_plugin_timedate_SettingFirstDayOfWeek_Tuesday { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstDayOfWeek_Tuesday", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Wednesday. + /// + internal static string Microsoft_plugin_timedate_SettingFirstDayOfWeek_Wednesday { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstDayOfWeek_Wednesday", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to First week of the year. + /// + internal static string Microsoft_plugin_timedate_SettingFirstWeekRule { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstWeekRule", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Configure the calendar rule for the first week of the year.. + /// + internal static string Microsoft_plugin_timedate_SettingFirstWeekRule_Description { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstWeekRule_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to First day of year. + /// + internal static string Microsoft_plugin_timedate_SettingFirstWeekRule_FirstDay { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstWeekRule_FirstDay", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to First four day week. + /// + internal static string Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFourDayWeek { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFourDayWeek", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to First full week. + /// + internal static string Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFullWeek { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFullWeek", resourceCulture); + } + } + /// /// Looks up a localized string similar to Hide 'Invalid number input' error message on global queries. /// diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.resx b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.resx index c78b2bb108..3cc62b0bbd 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.resx +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.resx @@ -315,4 +315,46 @@ Unix epoch time in milliseconds + + First day of the week + + + Friday + + + Monday + + + Saturday + + + Sunday + + + Thursday + + + Tuesday + + + Wednesday + + + First week of the year + + + Configure the calendar rule for the first week of the year. + + + First day of year + + + First four day week + + + First full week + + + Use system setting + \ No newline at end of file