[PTRun][DateTime]Setting for First week of year and First day of week (#33406)

## Summary of the Pull Request

This PR implements two new plugin settings:
- **First week of year**

![image](https://github.com/microsoft/PowerToys/assets/61519853/c866ffc2-2a21-438c-9a1a-5f4c7f68a22e)

- **First day of week**

![image](https://github.com/microsoft/PowerToys/assets/61519853/b2ec125b-d87c-40c5-8793-743a1ffae237)

## Detailed Description of the Pull Request / Additional comments

For both settings the users can decide to be in sync with the system
settings (default) or to use their own setting. The order of days for
the `first day of week` setting is based on the current system culture.

PT Run respects these settings for the relevant results:
- calendar week
- week of month
- number of day in week
This commit is contained in:
Heiko 2024-07-18 15:29:01 +02:00 committed by GitHub
parent 98cfeb0776
commit 7808033436
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 483 additions and 43 deletions

View File

@ -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.
- 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.

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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()
{

View File

@ -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
{
/// <summary>
/// Returns a list with all available date time formats
/// </summary>
/// <param name="isKeywordSearch">Is this a search with plugin activation keyword or not</param>
/// <param name="timeLong">Required for UnitTest: Show time in long format</param>
/// <param name="dateLong">Required for UnitTest: Show date in long format</param>
/// <param name="timestamp">Required for UnitTest: Use custom <see cref="DateTime"/> object to calculate results</param>
/// <param name="timeLongFormat">Required for UnitTest: Show time in long format</param>
/// <param name="dateLongFormat">Required for UnitTest: Show date in long format</param>
/// <param name="timestamp">Use custom <see cref="DateTime"/> object to calculate results instead of the system date/time</param>
/// <param name="firstWeekOfYear">Required for UnitTest: Use custom first week of the year instead of the plugin setting.</param>
/// <param name="firstDayOfWeek">Required for UnitTest: Use custom first day of the week instead the plugin setting.</param>
/// <returns>List of results</returns>
internal static List<AvailableResult> GetList(bool isKeywordSearch, bool? timeLong = null, bool? dateLong = null, DateTime? timestamp = null)
internal static List<AvailableResult> GetList(bool isKeywordSearch, bool? timeLongFormat = null, bool? dateLongFormat = null, DateTime? timestamp = null, CalendarWeekRule? firstWeekOfYear = null, DayOfWeek? firstDayOfWeek = null)
{
List<AvailableResult> results = new List<AvailableResult>();
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,

View File

@ -57,16 +57,17 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
/// </summary>
/// <param name="date">date</param>
/// <returns>Number of week in the month</returns>
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;
}
/// <summary>
@ -74,13 +75,12 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
/// </summary>
/// <param name="date">Date</param>
/// <returns>Number of the day in the week</returns>
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;
}
/// <summary>
@ -133,6 +133,52 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
{
return Regex.IsMatch(input, @"^.*(u|ums|ft)\d");
}
/// <summary>
/// Returns a CalendarWeekRule enum value based on the plugin setting.
/// </summary>
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;
}
}
/// <summary>
/// Returns a DayOfWeek enum value based on the FirstDayOfWeek plugin setting.
/// </summary>
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;
}
}
}
/// <summary>

View File

@ -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
/// </summary>
private static TimeDateSettings instance;
/// <summary>
/// Gets the value of the "First Week Rule" setting
/// </summary>
internal int CalendarFirstWeekRule { get; private set; }
/// <summary>
/// Gets the value of the "First Day Of Week" setting
/// </summary>
internal int FirstDayOfWeek { get; private set; }
/// <summary>
/// Gets a value indicating whether to show only the time and date for system time in global results or not
/// </summary>
@ -87,6 +99,29 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
{
var optionList = new List<PluginAdditionalOption>
{
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<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_Setting_UseSystemSetting, "-1"),
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_FirstDay, "0"),
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFullWeek, "1"),
new KeyValuePair<string, string>(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;
}
/// <summary>
/// Return the combobox value of the given settings list with the given name.
/// </summary>
/// <param name="settings">The object that contain all settings.</param>
/// <param name="name">The name of the setting.</param>
/// <returns>A settings value.</returns>
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;
}
/// <summary>
/// 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.
/// </summary>
/// <remarks>In the world we have three groups of countries: Saturday, Sunday, Monday (Everything else is chosen by the user.)</remarks>
/// <returns>List of values for combo box.</returns>
private static List<KeyValuePair<string, string>> GetSortedListForWeekDaySetting()
{
// List (Sorted for first day is Sunday)
var list = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_Setting_UseSystemSetting, "-1"),
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek_Sunday, "0"),
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek_Monday, "1"),
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek_Tuesday, "2"),
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek_Wednesday, "3"),
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek_Thursday, "4"),
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek_Friday, "5"),
new KeyValuePair<string, string>(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;
}
}
}
}

View File

@ -438,6 +438,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Use system setting.
/// </summary>
internal static string Microsoft_plugin_timedate_Setting_UseSystemSetting {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Setting_UseSystemSetting", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Show date with weekday and name of month.
/// </summary>
@ -456,6 +465,123 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to First day of the week.
/// </summary>
internal static string Microsoft_plugin_timedate_SettingFirstDayOfWeek {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstDayOfWeek", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Friday.
/// </summary>
internal static string Microsoft_plugin_timedate_SettingFirstDayOfWeek_Friday {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstDayOfWeek_Friday", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Monday.
/// </summary>
internal static string Microsoft_plugin_timedate_SettingFirstDayOfWeek_Monday {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstDayOfWeek_Monday", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Saturday.
/// </summary>
internal static string Microsoft_plugin_timedate_SettingFirstDayOfWeek_Saturday {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstDayOfWeek_Saturday", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Sunday.
/// </summary>
internal static string Microsoft_plugin_timedate_SettingFirstDayOfWeek_Sunday {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstDayOfWeek_Sunday", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Thursday.
/// </summary>
internal static string Microsoft_plugin_timedate_SettingFirstDayOfWeek_Thursday {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstDayOfWeek_Thursday", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Tuesday.
/// </summary>
internal static string Microsoft_plugin_timedate_SettingFirstDayOfWeek_Tuesday {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstDayOfWeek_Tuesday", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Wednesday.
/// </summary>
internal static string Microsoft_plugin_timedate_SettingFirstDayOfWeek_Wednesday {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstDayOfWeek_Wednesday", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to First week of the year.
/// </summary>
internal static string Microsoft_plugin_timedate_SettingFirstWeekRule {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstWeekRule", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Configure the calendar rule for the first week of the year..
/// </summary>
internal static string Microsoft_plugin_timedate_SettingFirstWeekRule_Description {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstWeekRule_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to First day of year.
/// </summary>
internal static string Microsoft_plugin_timedate_SettingFirstWeekRule_FirstDay {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstWeekRule_FirstDay", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to First four day week.
/// </summary>
internal static string Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFourDayWeek {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFourDayWeek", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to First full week.
/// </summary>
internal static string Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFullWeek {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFullWeek", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Hide &apos;Invalid number input&apos; error message on global queries.
/// </summary>

View File

@ -315,4 +315,46 @@
<data name="Microsoft_plugin_timedate_Unix_Milliseconds" xml:space="preserve">
<value>Unix epoch time in milliseconds</value>
</data>
<data name="Microsoft_plugin_timedate_SettingFirstDayOfWeek" xml:space="preserve">
<value>First day of the week</value>
</data>
<data name="Microsoft_plugin_timedate_SettingFirstDayOfWeek_Friday" xml:space="preserve">
<value>Friday</value>
</data>
<data name="Microsoft_plugin_timedate_SettingFirstDayOfWeek_Monday" xml:space="preserve">
<value>Monday</value>
</data>
<data name="Microsoft_plugin_timedate_SettingFirstDayOfWeek_Saturday" xml:space="preserve">
<value>Saturday</value>
</data>
<data name="Microsoft_plugin_timedate_SettingFirstDayOfWeek_Sunday" xml:space="preserve">
<value>Sunday</value>
</data>
<data name="Microsoft_plugin_timedate_SettingFirstDayOfWeek_Thursday" xml:space="preserve">
<value>Thursday</value>
</data>
<data name="Microsoft_plugin_timedate_SettingFirstDayOfWeek_Tuesday" xml:space="preserve">
<value>Tuesday</value>
</data>
<data name="Microsoft_plugin_timedate_SettingFirstDayOfWeek_Wednesday" xml:space="preserve">
<value>Wednesday</value>
</data>
<data name="Microsoft_plugin_timedate_SettingFirstWeekRule" xml:space="preserve">
<value>First week of the year</value>
</data>
<data name="Microsoft_plugin_timedate_SettingFirstWeekRule_Description" xml:space="preserve">
<value>Configure the calendar rule for the first week of the year.</value>
</data>
<data name="Microsoft_plugin_timedate_SettingFirstWeekRule_FirstDay" xml:space="preserve">
<value>First day of year</value>
</data>
<data name="Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFourDayWeek" xml:space="preserve">
<value>First four day week</value>
</data>
<data name="Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFullWeek" xml:space="preserve">
<value>First full week</value>
</data>
<data name="Microsoft_plugin_timedate_Setting_UseSystemSetting" xml:space="preserve">
<value>Use system setting</value>
</data>
</root>