[PT Run] TimeDate plugin (#16662)

* create plugin

* Update plugin code

* fix deps

* last changes

* unix

* new results and small changes

* Update settings name

* make spellcheck happy

* new time/date formats

* add comment

* code cleanup, installer, signing pipeline

* fix unix result

* UnitTests

* spell fix

* Update tests, Timestamp query feature

* new formats

* last changes

* last changes

* unit tests and fixes

* cjhanges and fixes

* fix installer

* fix settings class init

* context menu

* fix tests

* add settings tests

* update/fix DateTimeResult tests

* small improvements

* update pipeline

* enable analyzer

* fixes and improvements

* spell fix

* dev docs

* doc fixes

* spell fix

* last changes

* changes and fixes

* fixes and test updates

* improvements

* last changes

* try to fix tests

* remove obsolete code

* add info to test log

* fix search

* tag fix

* tests

* change tests

* update dev docs

* fix spelling

* fix culture for ui strings

* improvements based on feedback

* improve global search

* improve text

* docs improvement

* add settings note

* fix and update tests

* fix spelling
This commit is contained in:
Heiko 2022-03-17 20:33:05 +01:00 committed by GitHub
parent 5914fc1ffd
commit 34e4e7e5bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 3028 additions and 4 deletions

View File

@ -373,6 +373,7 @@ DARKYELLOW
datareader
Datavalue
DATAW
David
davidegiacometti
Dayof
Dbg
@ -1214,6 +1215,7 @@ MONITORINFOEXW
monitorinfof
Monthand
Moq
Morton
MOUSEACTIVATE
MOUSEHWHEEL
MOUSEINPUT
@ -1925,6 +1927,7 @@ subquery
substr
Sul
Superbar
supressions
Suri
sut
SVE
@ -1992,6 +1995,7 @@ testzones
TEXCOORD
textblock
TEXTINCLUDE
THH
THICKFRAME
THISCOMPONENT
THotkey

View File

@ -94,6 +94,7 @@
"modules\\launcher\\Plugins\\VSCodeWorkspaces\\Community.PowerToys.Run.Plugin.VSCodeWorkspaces.dll",
"modules\\launcher\\Plugins\\Service\\Microsoft.PowerToys.Run.Plugin.Service.dll",
"modules\\launcher\\Plugins\\System\\Microsoft.PowerToys.Run.Plugin.System.dll",
"modules\\launcher\\Plugins\\TimeDate\\Microsoft.PowerToys.Run.Plugin.TimeDate.dll",
"modules\\launcher\\Plugins\\TimeZone\\Microsoft.PowerToys.Run.Plugin.TimeZone.dll",
"modules\\launcher\\Plugins\\WebSearch\\Community.PowerToys.Run.Plugin.WebSearch.dll",
"modules\\launcher\\Plugins\\WindowsTerminal\\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.dll",

View File

@ -130,7 +130,8 @@ steps:
**\Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest.dll
**\Microsoft.Plugin.Uri.UnitTests.dll
**\Wox.Test.dll
**\Microsoft.PowerToys.Run.Plugin.System.UnitTests.dll
**\Microsoft.PowerToys.Run.Plugin.System.UnitTests.dll
**\Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests.dll
**\Microsoft.Plugin.WindowsTerminal.UnitTests.dll
**\Microsoft.Plugin.WindowWalker.UnitTests.dll
!**\obj\**

View File

@ -136,6 +136,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerLauncher", "src\module
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}
{0351ADA4-0C32-4652-9BA0-41F7B602372B} = {0351ADA4-0C32-4652-9BA0-41F7B602372B}
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4} = {787B8AA6-CA93-4C84-96FE-DF31110AD1C4}
{F44934A8-36F3-49B0-9465-3831BE041CDE} = {F44934A8-36F3-49B0-9465-3831BE041CDE}
{5BDBD6C9-A31F-4CEB-871A-5E9E709197EF} = {5BDBD6C9-A31F-4CEB-871A-5E9E709197EF}
{5043CECE-E6A7-4867-9CBE-02D27D83747A} = {5043CECE-E6A7-4867-9CBE-02D27D83747A}
{F8B870EB-D5F5-45BA-9CF7-A5C459818820} = {F8B870EB-D5F5-45BA-9CF7-A5C459818820}
{74F1B9ED-F59C-4FE7-B473-7B453E30837E} = {74F1B9ED-F59C-4FE7-B473-7B453E30837E}
@ -399,6 +401,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonacoPreviewHandler", "src
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.TimeZone", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.TimeZone\Microsoft.PowerToys.Run.Plugin.TimeZone.csproj", "{F44934A8-36F3-49B0-9465-3831BE041CDE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.TimeDate", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.TimeDate\Microsoft.PowerToys.Run.Plugin.TimeDate.csproj", "{5BDBD6C9-A31F-4CEB-871A-5E9E709197EF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests\Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests.csproj", "{FD464B4C-2F68-4D06-91E7-4208146C41F5}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Plugin.WindowWalker.UnitTests", "src\modules\launcher\Plugins\Microsoft.Plugin.WindowWalker.UnitTests\Microsoft.Plugin.WindowWalker.UnitTests.csproj", "{8FE5A5EE-1B59-401C-9FB3-B04ECD3E29C1}"
EndProject
Global
@ -1087,11 +1092,25 @@ Global
{F44934A8-36F3-49B0-9465-3831BE041CDE}.Debug|x64.ActiveCfg = Debug|x64
{F44934A8-36F3-49B0-9465-3831BE041CDE}.Debug|x64.Build.0 = Debug|x64
{F44934A8-36F3-49B0-9465-3831BE041CDE}.Debug|x86.ActiveCfg = Debug|x64
{F44934A8-36F3-49B0-9465-3831BE041CDE}.Debug|x86.Build.0 = Debug|x64
{F44934A8-36F3-49B0-9465-3831BE041CDE}.Release|x64.ActiveCfg = Release|x64
{F44934A8-36F3-49B0-9465-3831BE041CDE}.Release|x64.Build.0 = Release|x64
{F44934A8-36F3-49B0-9465-3831BE041CDE}.Release|x86.ActiveCfg = Release|x64
{F44934A8-36F3-49B0-9465-3831BE041CDE}.Release|x86.Build.0 = Release|x64
{5BDBD6C9-A31F-4CEB-871A-5E9E709197EF}.Debug|x64.ActiveCfg = Debug|x64
{5BDBD6C9-A31F-4CEB-871A-5E9E709197EF}.Debug|x64.Build.0 = Debug|x64
{5BDBD6C9-A31F-4CEB-871A-5E9E709197EF}.Debug|x86.ActiveCfg = Debug|x64
{5BDBD6C9-A31F-4CEB-871A-5E9E709197EF}.Release|x64.ActiveCfg = Release|x64
{5BDBD6C9-A31F-4CEB-871A-5E9E709197EF}.Release|x64.Build.0 = Release|x64
{5BDBD6C9-A31F-4CEB-871A-5E9E709197EF}.Release|x86.ActiveCfg = Release|x64
{5BDBD6C9-A31F-4CEB-871A-5E9E709197EF}.Release|x86.Build.0 = Release|x64
{FD464B4C-2F68-4D06-91E7-4208146C41F5}.Debug|x64.ActiveCfg = Debug|x64
{FD464B4C-2F68-4D06-91E7-4208146C41F5}.Debug|x64.Build.0 = Debug|x64
{FD464B4C-2F68-4D06-91E7-4208146C41F5}.Debug|x86.ActiveCfg = Debug|x64
{FD464B4C-2F68-4D06-91E7-4208146C41F5}.Debug|x86.Build.0 = Debug|x64
{FD464B4C-2F68-4D06-91E7-4208146C41F5}.Release|x64.ActiveCfg = Release|x64
{FD464B4C-2F68-4D06-91E7-4208146C41F5}.Release|x64.Build.0 = Release|x64
{FD464B4C-2F68-4D06-91E7-4208146C41F5}.Release|x86.ActiveCfg = Release|x64
{FD464B4C-2F68-4D06-91E7-4208146C41F5}.Release|x86.Build.0 = Release|x64
{8FE5A5EE-1B59-401C-9FB3-B04ECD3E29C1}.Debug|x64.ActiveCfg = Debug|x64
{8FE5A5EE-1B59-401C-9FB3-B04ECD3E29C1}.Debug|x64.Build.0 = Debug|x64
{8FE5A5EE-1B59-401C-9FB3-B04ECD3E29C1}.Debug|x86.ActiveCfg = Debug|x64
@ -1232,6 +1251,8 @@ Global
{F1F6B6B6-9F18-4A17-8B5C-97DF552C53DC} = {2F305555-C296-497E-AC20-5FA1B237996A}
{04B193D7-3E21-46B8-A958-89B63A8A69DE} = {2F305555-C296-497E-AC20-5FA1B237996A}
{F44934A8-36F3-49B0-9465-3831BE041CDE} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{5BDBD6C9-A31F-4CEB-871A-5E9E709197EF} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{FD464B4C-2F68-4D06-91E7-4208146C41F5} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{8FE5A5EE-1B59-401C-9FB3-B04ECD3E29C1} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution

View File

@ -0,0 +1,148 @@
# 'Time and Date' plugin
The 'Time and Date' plugin shows the date and time in different formats. For the date and time formats the plugin uses the culture setting in Windows, if the format is not commonly defined. The user can search for the system date/time or a custom date/time. The value of each result can be copied to clipboard.
**Query examples:**
- Format: `time`
- Date/time: `10:30 AM`
- Format and date/time: `Week number::10/10/2022`
!['Time and Date' plugin](/doc/images/launcher/plugins/timedate.png)
![Search for a date in a specified format](/doc/images/launcher/plugins/timedate2.png)
## Formats
### Available formats
**Remarks**
- The following formats requires a prefix in the query:
- Unix Timestamp: `u`
- Windows file time: `ft`
- On invalid number inputs we show a warning that tells the user which prefixes are allowed/required.
**List of available formats**
The following formats are currently available:
| Format | Example (Based on default settings) | As result | As input |
|--------------|-----------|------------|------------|
| Time | 5:10 PM | x | x |
| Date | 3/5/2022 | x | x |
| Now | 3/5/2022 5:10 PM | x | x |
| Time UTC | 4:10 PM | x | x |
| Now UTC | 3/5/2022 4:10 PM | x | x |
| Unix Timestamp | 1646496622 | x | x |
| Hour | 10 | x | |
| Minute | 30 | x | |
| Second | 45 | x | |
| Millisecond | 678 | x | |
| Day (Week day) | Saturday | x | |
| Day of the week | 6 | x | |
| Day of the month | 5 | x | |
| Day of the year | 64 | x | |
| Week of the month | 1 | x | |
| Week of the year (Calendar week, Week number) | 10 | x | |
| Month | March | x | |
| Month of the year | 3 | x | |
| Month and day | March 7 | x | x |
| Year | 2022 | x | |
| Era | AD | x | |
| Era abbreviation | A | x | |
| Month and year | March 2022 | x | x |
| Windows file time (Int64 number) | 637820976123938199 | x | x |
| Universal time format: YYYY-MM-DD hh:mm:ss| 2022-03-05 16:20:12Z | x | x |
| ISO 8601 | 2022-03-05T17:23:04 | x | x |
| ISO 8601 UTC | 2022-03-05T16:23:04 | x | x |
| ISO 8601 with time zone | 2022-03-05T17:23:04+01:00 | x | x |
| ISO 8601 UTC with time zone | 2022-03-05T16:23:04Z | x | x |
| RFC1123 | Sat, 05 Mar 2022 16:23:04 GMT | x | x |
### Add new formats
- To add a new formats you have to add them to the method `GetList()` of the [`AvailableResultsList`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs) class.
- Please add the new formats in the second range. The first one is reserved for the three main formats (Time, Date, Now).
- After adding the new formats you have to update the Unit Tests!
## Optional plugin settings
- The optional plugin settings are implemented via the [`ISettingProvider`](/src/modules/launcher/Wox.Plugin/ISettingProvider.cs) interface from `Wox.Plugin` project.
- 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 on global queries |
| `TimeWithSeconds` | `false` | Show time with seconds (Applies to 'Time' and 'Now' result) |
| `DateWithWeekday` | `false` | Show date with weekday and name of month (Applies to 'Date' and 'Now' result) |
| `HideNumberMessageOnGlobalQuery` | `false` | Hide 'Invalid number input' error message on global queries |
## Classes
### [`AvailableResult.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResult.cs)
- Each instance of the [`AvailableResult`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResult.cs) class represents a time/date result/format that the user can search for.
- The results/formats are defined in the `AvailableResultsList` class.
### [`AvailableResultsList.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs)
- The [`AvailableResultsList`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs) class contains the list of available formats/results in its method `GetList()`.
### [`ResultHelper.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/ResultHelper.cs)
- The [`ResultHelper`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/ResultHelper.cs) class contains methods for some of the result features (tool tip, copy to clipboard) and the error result on incorrect number input.
- And it contains the `SelectStringFromResources()` method for getting the resource strings based on the user input.
- The method has a parameter for the `stringId` which is the name of the string in the resource file. By default the word `Now` is automatically added at the end to get the string for a system time/date search.
- If a different/custom string is needed for a system time/date search the parameter `stringIdNow` can be used to override the default behavior of the method.
- If only a string for the system time/date search is required, you can set `stringId` to `string.Empty` and only `stringIdNow` to a valid string id.
### [`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.
### [`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.
- The class has a static property called `Instance` that holds an instance of the class itself. This allows us to access the settings from everywhere in the plugin code without having additional parameters in our methods.
### [`SearchController.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/SearchController.cs)
- The [`SearchController`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/SearchController.cs) encapsulates the methods needed to search and find matches.
## Search
### Tags
- We compare the user input with the label of each results. If it doesn't match we search the tags of the result too.
- For each result two tag strings are defined. One for a search with system time/date and one for a search with a custom time/date. Most of the results (except the era results) are using one of the generic tag lists: Date, Time or Format
- The selection of the tag (for "system time/date" or "custom time/date") is happening at search time in the `AvailableResultsList.cs` class.
- The different tags in a list are split by the `;` character.
### Score
- The plugin uses `FuzzyMatching` to get the matching formats, if the user searches for a specific format. The score is set based on the `FuzzySearch` result.
- To achieve a better balance between sub title matches and tag matches the score of tag matches is divided by two.
### Match requirements for global queries
On global queries the high score returned by `FuzzySearch` has negative impacts on the user experience and the search results priority/order of other plugins. To mitigate this we defined some matching requirements:
- If the query is a word of the following conjunction list, we don't return any results: for, and, nor, but, or, so
- We don't have 'yet' (synonym of 'now') on the list, because this could block results in some languages.
- The first word of the query has to be a full match with a word in the label or tag list.
- For both requirements we compare case-insensitive.
## [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.
### [`PluginSettingsTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/PluginSettingsTests.cs)
- The [`PluginSettingsTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/PluginSettingsTests.cs) class contains tests to validate that all settings exist and that they have the correct default values.
### [`QueryTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/QueryTests.cs)
- The [`QueryTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/QueryTests.cs) class contains tests to validate that the user gets the correct results when searching.,
- 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.
### [`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.

View File

@ -11,6 +11,7 @@
- [Program](/doc/devdocs/modules/launcher/plugins/program.md)
- [Registry](/doc/devdocs/modules/launcher/plugins/registry.md)
- [Shell](/doc/devdocs/modules/launcher/plugins/shell.md)
- [Time and Date](/doc/devdocs/modules/launcher/plugins/timedate.md)
- [Windows System Commands](/doc/devdocs/modules/launcher/plugins/system.md)
- [Uri](/doc/devdocs/modules/launcher/plugins/uri.md)
- [Window Walker](/doc/devdocs/modules/launcher/plugins/windowwalker.md)

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

View File

@ -90,7 +90,11 @@
<?define SystemImagesComponentFiles=lock.dark.png;lock.light.png;logoff.dark.png;logoff.light.png;recyclebin.dark.png;recyclebin.light.png;restart.dark.png;restart.light.png;shutdown.dark.png;shutdown.light.png;sleep.dark.png;sleep.light.png;firmwareSettings.dark.png;firmwareSettings.light.png?>
<?define TimeZoneComponentFiles=plugin.json;Microsoft.PowerToys.Run.Plugin.TimeZone.deps.json;Microsoft.PowerToys.Run.Plugin.TimeZone.dll;PowerToys.ManagedTelemetry.dll?>
<?define TimeDateComponentFiles=Microsoft.PowerToys.Run.Plugin.TimeDate.deps.json;Microsoft.PowerToys.Run.Plugin.TimeDate.dll;plugin.json;PowerToys.ManagedTelemetry.dll?>
<?define TimeDateImagesComponentFiles=calendar.dark.png;calendar.light.png;time.dark.png;time.light.png;timeDate.dark.png;timeDate.light.png;Warning.dark.png;Warning.light.png?>
<?define TimeZoneComponentFiles=plugin.json;Microsoft.PowerToys.Run.Plugin.TimeZone.deps.json;Microsoft.PowerToys.Run.Plugin.TimeZone.dll;PowerToys.ManagedTelemetry.dll?>
<?define WinSetCmpFiles=plugin.json;Microsoft.PowerToys.Run.Plugin.WindowsSettings.deps.json;Microsoft.PowerToys.Run.Plugin.WindowsSettings.dll;PowerToys.ManagedTelemetry.dll?>
@ -481,6 +485,10 @@
</Directory>
<Directory Id="SystemPluginFolder" Name="System">
<Directory Id="SystemImagesFolder" Name="Images" />
</Directory>
<Directory Id="TimeDatePluginFolder" Name="TimeDate">
<Directory Id="TimeDateImagesFolder" Name="Images" />
<Directory Id="TimeDateLanguagesFolder" Name="Languages" />
</Directory>
<Directory Id="TimeZonePluginFolder" Name="TimeZone">
<Directory Id="TimeZoneImagesFolder" Name="Images" />
@ -1094,7 +1102,7 @@
<Fragment>
<!-- Resource directories should be added only if the installer is built on the build farm -->
<?ifdef env.IsPipeline?>
<?foreach ParentDirectory in LauncherInstallFolder;FancyZonesInstallFolder;ImageResizerInstallFolder;ColorPickerInstallFolder;FileExplorerPreviewInstallFolder;CalculatorPluginFolder;FolderPluginFolder;ProgramPluginFolder;ShellPluginFolder;IndexerPluginFolder;UnitConverterPluginFolder;UriPluginFolder;WindowWalkerPluginFolder;RegistryPluginFolder;VSCodeWorkspacesPluginFolder;ServicePluginFolder;SystemPluginFolder;TimeZonePluginFolder;WindowsSettingsPluginFolder;WindowsTerminalPluginFolder;WebSearchPluginFolder?>
<?foreach ParentDirectory in LauncherInstallFolder;FancyZonesInstallFolder;ImageResizerInstallFolder;ColorPickerInstallFolder;FileExplorerPreviewInstallFolder;CalculatorPluginFolder;FolderPluginFolder;ProgramPluginFolder;ShellPluginFolder;IndexerPluginFolder;UnitConverterPluginFolder;UriPluginFolder;WindowWalkerPluginFolder;RegistryPluginFolder;VSCodeWorkspacesPluginFolder;ServicePluginFolder;SystemPluginFolder;TimeDatePluginFolder;TimeZonePluginFolder;WindowsSettingsPluginFolder;WindowsTerminalPluginFolder;WebSearchPluginFolder?>
<DirectoryRef Id="$(var.ParentDirectory)">
<!-- Resource file directories -->
<?foreach Language in $(var.LocLanguageList)?>
@ -1367,6 +1375,12 @@
Guid="$(var.CompGUIDPrefix)17"
Directory="Resource$(var.IdSafeLanguage)UnitConverterPluginFolder">
<File Id="Launcher_UnitConverter_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\launcher\Plugins\UnitConverter\$(var.Language)\Community.PowerToys.Run.Plugin.UnitConverter.resources.dll" />
</Component>
<Component
Id="Launcher_TimeDate_$(var.IdSafeLanguage)_Component"
Guid="$(var.CompGUIDPrefix)18"
Directory="Resource$(var.IdSafeLanguage)TimeDatePluginFolder">
<File Id="Launcher_TimeDate_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\launcher\Plugins\TimeDate\$(var.Language)\Microsoft.PowerToys.Run.Plugin.TimeDate.resources.dll" />
</Component>
<?undef IdSafeLanguage?>
<?undef CompGUIDPrefix?>
@ -1573,6 +1587,18 @@
<File Id="SystemImagesComponentFile_$(var.File)" Source="$(var.BinX64Dir)modules\launcher\Plugins\System\Images\$(var.File)" />
</Component>
<?endforeach?>
<!-- TimeDate Plugin -->
<?foreach File in $(var.TimeDateComponentFiles)?>
<Component Id="TimeDateComponent_$(var.File)" Win64="yes" Directory="TimeDatePluginFolder">
<File Id="TimeDateComponentFile_$(var.File)" Source="$(var.BinX64Dir)modules\launcher\Plugins\TimeDate\$(var.File)" />
</Component>
<?endforeach?>
<?foreach File in $(var.TimeDateImagesComponentFiles)?>
<Component Id="TimeDateImagesComponent_$(var.File)" Win64="yes" Directory="TimeDateImagesFolder">
<File Id="TimeDateImagesComponentFile_$(var.File)" Source="$(var.BinX64Dir)modules\launcher\Plugins\TimeDate\Images\$(var.File)" />
</Component>
<?endforeach?>
<!-- TimeZone Plugin -->
<?foreach File in $(var.TimeZoneComponentFiles)?>

View File

@ -49,6 +49,10 @@ using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.ThrowLastWin32Error(System.String)", Scope = "member", Target = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.#InternalGetTarget(Microsoft.Win32.SafeHandles.SafeFileHandle)", Justification = "Only used for local generation")]
[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.ThrowLastWin32Error(System.String)", Scope = "member", Target = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.#OpenReparsePoint(System.String,Microsoft.Templates.Core.Locations.JunctionNativeMethods+EFileAccess)", Justification = "Only used for local generation")]
[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Windows.Documents.InlineCollection.Add(System.String)", Scope = "member", Target = "Microsoft.Templates.UI.Extensions.TextBlockExtensions.#OnSequentialFlowStepChanged(System.Windows.DependencyObject,System.Windows.DependencyPropertyChangedEventArgs)", Justification = "No text here")]
[assembly: SuppressMessage("Globalization", "CA1309:Use ordinal string comparison", Justification = "The user's search term should be compared with culture based rules.", Scope = "type", Target = "~T:Microsoft.PowerToys.Run.Plugin.TimeDate.Components.SearchController")]
// FxCop warning suppression for uninstantiated TestFixture classes
[assembly: SuppressMessage("Microsoft.Performance", "CA1812: Avoid uninstantiated internal classes", Scope = "module", Justification = "CA1812 will be thrown for every file in the test project. This is mentioned here: dotnet/roslyn-analyzers#1830")]
// Code quality
[assembly: SuppressMessage("CodeQuality", "IDE0076:Invalid global 'SuppressMessageAttribute'", Justification = "Affect predefined supressions.")]

View File

@ -0,0 +1,131 @@
// 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.Globalization;
using System.Linq;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Wox.Infrastructure;
using Wox.Plugin;
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
{
[TestClass]
public class ImageTests
{
private CultureInfo originalCulture;
private CultureInfo originalUiCulture;
[TestInitialize]
public void Setup()
{
StringMatcher.Instance = new StringMatcher();
// Set culture to 'en-us'
originalCulture = CultureInfo.CurrentCulture;
CultureInfo.CurrentCulture = new CultureInfo("en-us");
originalUiCulture = CultureInfo.CurrentUICulture;
CultureInfo.CurrentUICulture = new CultureInfo("en-us");
}
[DataTestMethod]
[DataRow("time", "Time -", "Images\\time.dark.png")]
[DataRow("time u", "Time UTC -", "Images\\time.dark.png")]
[DataRow("date", "Date -", "Images\\calendar.dark.png")]
[DataRow("now", "Now -", "Images\\timeDate.dark.png")]
[DataRow("now u", "Now UTC -", "Images\\timeDate.dark.png")]
[DataRow("unix", "Unix epoch time -", "Images\\timeDate.dark.png")]
[DataRow("hour", "Hour -", "Images\\time.dark.png")]
[DataRow("minute", "Minute -", "Images\\time.dark.png")]
[DataRow("second", "Second -", "Images\\time.dark.png")]
[DataRow("millisecond", "Millisecond -", "Images\\time.dark.png")]
[DataRow("file", "Windows file time (Int64 number) -", "Images\\timeDate.dark.png")]
[DataRow("day", "Day (Week day) -", "Images\\calendar.dark.png")]
[DataRow("day of week", "Day of the week (Week day) -", "Images\\calendar.dark.png")]
[DataRow("day of month", "Day of the month -", "Images\\calendar.dark.png")]
[DataRow("day of year", "Day of the year -", "Images\\calendar.dark.png")]
[DataRow("week of month", "Week of the month -", "Images\\calendar.dark.png")]
[DataRow("week of year", "Week of the year (Calendar week, Week number) -", "Images\\calendar.dark.png")]
[DataRow("month", "Month -", "Images\\calendar.dark.png")]
[DataRow("month of year", "Month of the year -", "Images\\calendar.dark.png")]
[DataRow("month and", "Month and day -", "Images\\calendar.dark.png")]
[DataRow("year", "Year -", "Images\\calendar.dark.png")]
[DataRow("era", "Era -", "Images\\calendar.dark.png")]
[DataRow("era abb", "Era abbreviation -", "Images\\calendar.dark.png")]
[DataRow("month and", "Month and year -", "Images\\calendar.dark.png")]
[DataRow("universal", "Universal time format: YYYY-MM-DD hh:mm:ss -", "Images\\timeDate.dark.png")]
[DataRow("iso", "ISO 8601 -", "Images\\timeDate.dark.png")]
[DataRow("iso utc", "ISO 8601 UTC - ", "Images\\timeDate.dark.png")]
[DataRow("iso zone", "ISO 8601 with time zone - ", "Images\\timeDate.dark.png")]
[DataRow("iso utc zone", "ISO 8601 UTC with time zone - ", "Images\\timeDate.dark.png")]
[DataRow("rfc", "RFC1123 -", "Images\\timeDate.dark.png")]
public void IconThemeDarkTest(string typedString, string subTitleMatch, string expectedResult)
{
// Setup
Mock<Main> main = new ();
main.Object.IconTheme = "dark";
Query expectedQuery = new ("(" + typedString, "(");
// Act
string result = main.Object.Query(expectedQuery).FirstOrDefault(predicate: x => x.SubTitle.StartsWith(subTitleMatch, System.StringComparison.CurrentCulture)).IcoPath;
// Assert
Assert.AreEqual(expectedResult, result);
}
[DataTestMethod]
[DataRow("time", "Time -", "Images\\time.light.png")]
[DataRow("time u", "Time UTC -", "Images\\time.light.png")]
[DataRow("date", "Date -", "Images\\calendar.light.png")]
[DataRow("now", "Now -", "Images\\timeDate.light.png")]
[DataRow("now u", "Now UTC -", "Images\\timeDate.light.png")]
[DataRow("unix", "Unix epoch time -", "Images\\timeDate.light.png")]
[DataRow("hour", "Hour -", "Images\\time.light.png")]
[DataRow("minute", "Minute -", "Images\\time.light.png")]
[DataRow("second", "Second -", "Images\\time.light.png")]
[DataRow("millisecond", "Millisecond -", "Images\\time.light.png")]
[DataRow("file", "Windows file time (Int64 number) -", "Images\\timeDate.light.png")]
[DataRow("day", "Day (Week day) -", "Images\\calendar.light.png")]
[DataRow("day of week", "Day of the week (Week day) -", "Images\\calendar.light.png")]
[DataRow("day of month", "Day of the month -", "Images\\calendar.light.png")]
[DataRow("day of year", "Day of the year -", "Images\\calendar.light.png")]
[DataRow("week of month", "Week of the month -", "Images\\calendar.light.png")]
[DataRow("week of year", "Week of the year (Calendar week, Week number) -", "Images\\calendar.light.png")]
[DataRow("month", "Month -", "Images\\calendar.light.png")]
[DataRow("month of year", "Month of the year -", "Images\\calendar.light.png")]
[DataRow("month and", "Month and day -", "Images\\calendar.light.png")]
[DataRow("year", "Year -", "Images\\calendar.light.png")]
[DataRow("era", "Era -", "Images\\calendar.light.png")]
[DataRow("era abb", "Era abbreviation -", "Images\\calendar.light.png")]
[DataRow("Month and", "Month and year -", "Images\\calendar.light.png")]
[DataRow("universal", "Universal time format: YYYY-MM-DD hh:mm:ss -", "Images\\timeDate.light.png")]
[DataRow("iso", "ISO 8601 -", "Images\\timeDate.light.png")]
[DataRow("iso utc", "ISO 8601 UTC - ", "Images\\timeDate.light.png")]
[DataRow("iso zone", "ISO 8601 with time zone - ", "Images\\timeDate.light.png")]
[DataRow("iso utc zone", "ISO 8601 UTC with time zone - ", "Images\\timeDate.light.png")]
[DataRow("rfc", "RFC1123 -", "Images\\timeDate.light.png")]
public void IconThemeLightTest(string typedString, string subTitleMatch, string expectedResult)
{
// Setup
Mock<Main> main = new ();
main.Object.IconTheme = "light";
Query expectedQuery = new ("(" + typedString, "(");
// Act
var result = main.Object.Query(expectedQuery).FirstOrDefault(x => x.SubTitle.StartsWith(subTitleMatch, System.StringComparison.CurrentCulture)).IcoPath;
// Assert
Assert.AreEqual(expectedResult, result);
}
[TestCleanup]
public void CleanUp()
{
// Set culture to original value
CultureInfo.CurrentCulture = originalCulture;
CultureInfo.CurrentUICulture = originalUiCulture;
}
}
}

View File

@ -0,0 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<IsPackable>false</IsPackable>
<Platforms>x64</Platforms>
<RootNamespace>Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests</RootNamespace>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisMode>Recommended</AnalysisMode>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.3" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.PowerToys.Run.Plugin.TimeDate\Microsoft.PowerToys.Run.Plugin.TimeDate.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\..\codeAnalysis\GlobalSuppressions.cs">
<Link>GlobalSuppressions.cs</Link>
</Compile>
<AdditionalFiles Include="..\..\..\..\codeAnalysis\StyleCop.json">
<Link>StyleCop.json</Link>
</AdditionalFiles>
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers">
<Version>1.1.118</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,63 @@
// 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.Reflection;
using Microsoft.PowerToys.Run.Plugin.TimeDate.Components;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
{
[TestClass]
public class PluginSettingsTests
{
[TestMethod]
public void SettingsCount()
{
// Setup
PropertyInfo[] settings = TimeDateSettings.Instance?.GetType()?.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);
// Act
var result = settings?.Length;
// Assert
Assert.AreEqual(4, result);
}
[DataTestMethod]
[DataRow("OnlyDateTimeNowGlobal")]
[DataRow("TimeWithSeconds")]
[DataRow("DateWithWeekday")]
[DataRow("HideNumberMessageOnGlobalQuery")]
public void DoesSettingExist(string name)
{
// Setup
Type settings = TimeDateSettings.Instance?.GetType();
// Act
var result = settings?.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Instance);
// Assert
Assert.IsNotNull(result);
}
[DataTestMethod]
[DataRow("OnlyDateTimeNowGlobal", true)]
[DataRow("TimeWithSeconds", false)]
[DataRow("DateWithWeekday", false)]
[DataRow("HideNumberMessageOnGlobalQuery", false)]
public void DefaultValues(string name, bool 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,243 @@
// 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 System.Linq;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Wox.Infrastructure;
using Wox.Plugin;
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
{
[TestClass]
public class QueryTests
{
private CultureInfo originalCulture;
private CultureInfo originalUiCulture;
[TestInitialize]
public void Setup()
{
StringMatcher.Instance = new StringMatcher();
// Set culture to 'en-us'
originalCulture = CultureInfo.CurrentCulture;
CultureInfo.CurrentCulture = new CultureInfo("en-us");
originalUiCulture = CultureInfo.CurrentUICulture;
CultureInfo.CurrentUICulture = new CultureInfo("en-us");
}
[DataTestMethod]
[DataRow("time", 2)]
[DataRow("date", 2)]
[DataRow("now", 3)]
[DataRow("current", 3)]
[DataRow("year", 0)]
[DataRow("", 0)]
[DataRow("time::10:10:10", 2)]
[DataRow("date::10/10/10", 2)]
public void CountWithoutPluginKeyword(string typedString, int expectedResultCount)
{
// Setup
Mock<Main> main = new ();
Query expectedQuery = new (typedString);
// Act
var result = main.Object.Query(expectedQuery).Count;
// Assert
Assert.AreEqual(expectedResultCount, result, "Result depends on default plugin settings!");
}
[DataTestMethod]
[DataRow("(time", 16)]
[DataRow("(date", 24)]
[DataRow("(year", 7)]
[DataRow("(now", 30)]
[DataRow("(current", 30)]
[DataRow("(", 30)]
[DataRow("(now::10:10:10", 1)] // Windows file time
[DataRow("(current::10:10:10", 0)]
public void CountWithPluginKeyword(string typedString, int expectedResultCount)
{
// Setup
Mock<Main> main = new ();
Query expectedQuery = new (typedString, "(");
// Act
var result = main.Object.Query(expectedQuery);
// Assert
Assert.AreEqual(expectedResultCount, result.Count, result.ToString());
}
[DataTestMethod]
[DataRow("time", 2)] // Match if first word is a full word match
[DataRow("ime", 0)] // Don't match if first word is not a full match
[DataRow("and", 0)] // Don't match for only conjunctions
[DataRow("and time", 1)] // match if term is conjunction and other words
[DataRow("date and time", 1)] // Match if first word is a full word match
[DataRow("ate and time", 0)] // Don't match if first word is not a full word match
public void ValidateBehaviorOnGlobalQueries(string typedString, int expectedResultCount)
{
// Setup
Mock<Main> main = new ();
Query expectedQuery = new (typedString);
// Act
var result = main.Object.Query(expectedQuery);
// Assert
Assert.AreEqual(expectedResultCount, result.Count, result.ToString());
}
[DataTestMethod]
[DataRow("(time", "Time -")]
[DataRow("(time u", "Time UTC -")]
[DataRow("(date", "Date -")]
[DataRow("(now", "Now -")]
[DataRow("(now u", "Now UTC -")]
[DataRow("(unix", "Unix epoch time -")]
[DataRow("(file", "Windows file time (Int64 number) ")]
[DataRow("(hour", "Hour -")]
[DataRow("(minute", "Minute -")]
[DataRow("(second", "Second -")]
[DataRow("(millisecond", "Millisecond -")]
[DataRow("(day", "Day (Week day) -")]
[DataRow("(day of week", "Day of the week (Week day) -")]
[DataRow("(day of month", "Day of the month -")]
[DataRow("(day of year", "Day of the year -")]
[DataRow("(week of month", "Week of the month -")]
[DataRow("(week of year", "Week of the year (Calendar week, Week number) -")]
[DataRow("(month", "Month -")]
[DataRow("(month of year", "Month of the year -")]
[DataRow("(month and d", "Month and day -")]
[DataRow("(month and y", "Month and year -")]
[DataRow("(year", "Year -")]
[DataRow("(era", "Era -")]
[DataRow("(era a", "Era abbreviation -")]
[DataRow("(universal", "Universal time format: YYYY-MM-DD hh:mm:ss -")]
[DataRow("(iso", "ISO 8601 -")]
[DataRow("(iso utc", "ISO 8601 UTC -")]
[DataRow("(iso zone", "ISO 8601 with time zone - ")]
[DataRow("(iso utc zone", "ISO 8601 UTC with time zone -")]
[DataRow("(rfc", "RFC1123 -")]
[DataRow("(time::12:30", "Time -")]
[DataRow("(date::10.10.2022", "Date -")]
[DataRow("(time::u1646408119", "Time -")]
[DataRow("(time::ft637820085517321977", "Time -")]
[DataRow("(year", "Era -")]
[DataRow("(date", "Era -")]
[DataRow("(week day", "Day (Week day) -")]
[DataRow("(week day", "Day of the week (Week day) -")]
[DataRow("(cal week", "Week of the year (Calendar week, Week number) -")]
[DataRow("(week num", "Week of the year (Calendar week, Week number) -")]
public void CanFindFormatResult(string typedString, string expectedResult)
{
// Setup
Mock<Main> main = new ();
Query expectedQuery = new (typedString, "(");
// Act
var result = main.Object.Query(expectedQuery).FirstOrDefault(x => x.SubTitle.StartsWith(expectedResult, StringComparison.CurrentCulture));
// Assert
Assert.IsNotNull(result);
Assert.IsTrue(result?.Score >= 1, $"Score: {result?.Score}");
}
[DataTestMethod]
[DataRow("(12:30", "Time -")]
[DataRow("(10.10.2022", "Date -")]
[DataRow("(u1646408119", "Date and time -")]
[DataRow("(ft637820085517321977", "Date and time -")]
public void DateTimeNumberOnlyInput(string typedString, string expectedResult)
{
// Setup
Mock<Main> main = new ();
Query expectedQuery = new (typedString, "(");
// Act
var result = main.Object.Query(expectedQuery).FirstOrDefault(x => x.SubTitle.StartsWith(expectedResult, StringComparison.CurrentCulture));
// Assert
Assert.IsNotNull(result);
}
// [DataRow("(::")] -> Behaves different to PT Run user interface
// [DataRow("(time::")] -> Behaves different to PT Run user interface
// [DataRow("(::time")] -> Behaves different to PT Run user interface
[DataTestMethod]
[DataRow("(abcdefg")]
[DataRow("(timmmmeeee")]
[DataRow("(10.10.20.aa.22")]
[DataRow("(12::55")]
[DataRow("(12:aa:55")]
[DataRow("(timtaaaetetaae::u1646408119")]
[DataRow("(time:eeee")]
[DataRow("(time::eeee")]
[DataRow("(time//eeee")]
[DataRow("(date::12::55")]
[DataRow("(date::12:aa:55")]
public void InvalidInputNotShowsResults(string typedString)
{
// Setup
Mock<Main> main = new ();
Query expectedQuery = new (typedString, "(");
// Act
var result = main.Object.Query(expectedQuery).FirstOrDefault();
// Assert
Assert.IsNull(result, result?.ToString());
}
[DataTestMethod]
[DataRow("(ug1646408119")] // Invalid prefix
[DataRow("(u9999999999999")] // Unix number + prefix is longer than 12 characters
[DataRow("(0123456")] // Missing prefix
[DataRow("(ft63782008ab55173dasdas21977")] // Number contains letters
public void InvalidNumberInputShowsErrorMessage(string typedString)
{
// Setup
Mock<Main> main = new ();
Query expectedQuery = new (typedString, "(");
// Act
var result = main.Object.Query(expectedQuery).FirstOrDefault().Title;
// Assert
Assert.IsTrue(result.StartsWith("Error:", StringComparison.CurrentCulture));
}
[DataTestMethod]
[DataRow("(ft12..548")] // Number contains punctuation
[DataRow("(ft12..54//8")] // Number contains punctuation and other characters
[DataRow("(12..54//8")] // Number contains punctuation and other characters
[DataRow("(ft::1288gg8888")] // Number contains delimiter and other characters
public void InvalidInputNotShowsErrorMessage(string typedString)
{
// Setup
Mock<Main> main = new ();
Query expectedQuery = new (typedString, "(");
// Act
var result = main.Object.Query(expectedQuery).FirstOrDefault();
// Assert
Assert.IsNull(result);
}
[TestCleanup]
public void CleanUp()
{
// Set culture to original value
CultureInfo.CurrentCulture = originalCulture;
CultureInfo.CurrentUICulture = originalUiCulture;
}
}
}

View File

@ -0,0 +1,60 @@
// 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 System.Threading;
using Microsoft.PowerToys.Run.Plugin.TimeDate.Components;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
{
[TestClass]
public class StringParserTests
{
private CultureInfo originalCulture;
private CultureInfo originalUiCulture;
[TestInitialize]
public void Setup()
{
// Set culture to 'en-us'
originalCulture = CultureInfo.CurrentCulture;
CultureInfo.CurrentCulture = new CultureInfo("en-us");
originalUiCulture = CultureInfo.CurrentUICulture;
CultureInfo.CurrentUICulture = new CultureInfo("en-us");
}
[DataTestMethod]
[DataRow("10/29/2022 17:05:10", true, "G", "10/29/2022 5:05:10 PM")]
[DataRow("Saturday, October 29, 2022 5:05:10 PM", true, "G", "10/29/2022 5:05:10 PM")]
[DataRow("10/29/2022", true, "d", "10/29/2022")]
[DataRow("Saturday, October 29, 2022", true, "d", "10/29/2022")]
[DataRow("17:05:10", true, "T", "5:05:10 PM")]
[DataRow("5:05:10 PM", true, "T", "5:05:10 PM")]
[DataRow("10456", false, "", "")]
[DataRow("u10456", true, "", "")] // Value is UTC and can be different based on system
[DataRow("ft10456", true, "", "")] // Value is UTC and can be different based on system
public void ConvertStringToDateTime(string typedString, bool expectedBool, string stringType, string expectedString)
{
// Act
bool boolResult = TimeAndDateHelper.ParseStringAsDateTime(in typedString, out DateTime result);
// Assert
Assert.AreEqual(expectedBool, boolResult);
if (!string.IsNullOrEmpty(expectedString))
{
Assert.AreEqual(expectedString, result.ToString(stringType, CultureInfo.CurrentCulture));
}
}
[TestCleanup]
public void CleanUp()
{
// Set culture to original value
CultureInfo.CurrentCulture = originalCulture;
CultureInfo.CurrentUICulture = originalUiCulture;
}
}
}

View File

@ -0,0 +1,313 @@
// 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 System.Linq;
using Microsoft.PowerToys.Run.Plugin.TimeDate.Components;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
{
[TestClass]
public class TimeDateResultTests
{
private CultureInfo originalCulture;
private CultureInfo originalUiCulture;
[TestInitialize]
public void Setup()
{
// Set culture to 'en-us'
originalCulture = CultureInfo.CurrentCulture;
CultureInfo.CurrentCulture = new CultureInfo("en-us");
originalUiCulture = CultureInfo.CurrentUICulture;
CultureInfo.CurrentUICulture = new CultureInfo("en-us");
}
[DataTestMethod]
[DataRow("time", "10:30 AM")]
[DataRow("date", "3/2/2022")]
[DataRow("date and time", "3/2/2022 10:30 AM")]
[DataRow("hour", "10")]
[DataRow("minute", "30")]
[DataRow("second", "45")]
[DataRow("millisecond", "0")]
[DataRow("day (week day)", "Wednesday")]
[DataRow("day of the week (week day)", "4")]
[DataRow("day of the month", "2")]
[DataRow("day of the year", "61")]
[DataRow("week of the month", "1")]
[DataRow("week of the year (calendar week, week number)", "10")]
[DataRow("month", "March")]
[DataRow("month of the year", "3")]
[DataRow("month and day", "March 2")]
[DataRow("year", "2022")]
[DataRow("month and year", "March 2022")]
[DataRow("ISO 8601", "2022-03-02T10:30:45")]
[DataRow("ISO 8601 with time zone", "2022-03-02T10:30:45")]
[DataRow("RFC1123", "Wed, 02 Mar 2022 10:30:45 GMT")]
public void LocalFormatsWithShortTimeAndShortDate(string formatLabel, string expectedResult)
{
// Setup
var helperResults = AvailableResultsList.GetList(true, false, false, new DateTime(2022, 03, 02, 10, 30, 45));
// Act
var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase));
// Assert
Assert.AreEqual(expectedResult, result?.Value, $"Culture {CultureInfo.CurrentCulture.Name}, Culture UI: {CultureInfo.CurrentUICulture.Name}, Calendar: {CultureInfo.CurrentCulture.Calendar}, Region: {RegionInfo.CurrentRegion.Name}");
}
[DataTestMethod]
[DataRow("time", "10:30 AM")]
[DataRow("date", "Wednesday, March 2, 2022")]
[DataRow("date and time", "Wednesday, March 2, 2022 10:30 AM")]
[DataRow("hour", "10")]
[DataRow("minute", "30")]
[DataRow("second", "45")]
[DataRow("millisecond", "0")]
[DataRow("day (week day)", "Wednesday")]
[DataRow("day of the week (week day)", "4")]
[DataRow("day of the month", "2")]
[DataRow("day of the year", "61")]
[DataRow("week of the month", "1")]
[DataRow("week of the year (calendar week, week number)", "10")]
[DataRow("month", "March")]
[DataRow("month of the year", "3")]
[DataRow("month and day", "March 2")]
[DataRow("year", "2022")]
[DataRow("month and year", "March 2022")]
[DataRow("ISO 8601", "2022-03-02T10:30:45")]
[DataRow("ISO 8601 with time zone", "2022-03-02T10:30:45")]
[DataRow("RFC1123", "Wed, 02 Mar 2022 10:30:45 GMT")]
public void LocalFormatsWithShortTimeAndLongDate(string formatLabel, string expectedResult)
{
// Setup
var helperResults = AvailableResultsList.GetList(true, false, true, new DateTime(2022, 03, 02, 10, 30, 45));
// Act
var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase));
// Assert
Assert.AreEqual(expectedResult, result?.Value);
}
[DataTestMethod]
[DataRow("time", "10:30:45 AM")]
[DataRow("date", "3/2/2022")]
[DataRow("date and time", "3/2/2022 10:30:45 AM")]
[DataRow("hour", "10")]
[DataRow("minute", "30")]
[DataRow("second", "45")]
[DataRow("millisecond", "0")]
[DataRow("day (week day)", "Wednesday")]
[DataRow("day of the week (week day)", "4")]
[DataRow("day of the month", "2")]
[DataRow("day of the year", "61")]
[DataRow("week of the month", "1")]
[DataRow("week of the year (calendar week, week number)", "10")]
[DataRow("month", "March")]
[DataRow("month of the year", "3")]
[DataRow("month and day", "March 2")]
[DataRow("year", "2022")]
[DataRow("month and year", "March 2022")]
[DataRow("ISO 8601", "2022-03-02T10:30:45")]
[DataRow("ISO 8601 with time zone", "2022-03-02T10:30:45")]
[DataRow("RFC1123", "Wed, 02 Mar 2022 10:30:45 GMT")]
public void LocalFormatsWithLongTimeAndShortDate(string formatLabel, string expectedResult)
{
// Setup
var helperResults = AvailableResultsList.GetList(true, true, false, new DateTime(2022, 03, 02, 10, 30, 45));
// Act
var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase));
// Assert
Assert.AreEqual(expectedResult, result?.Value);
}
[DataTestMethod]
[DataRow("time", "10:30:45 AM")]
[DataRow("date", "Wednesday, March 2, 2022")]
[DataRow("date and time", "Wednesday, March 2, 2022 10:30:45 AM")]
[DataRow("hour", "10")]
[DataRow("minute", "30")]
[DataRow("second", "45")]
[DataRow("millisecond", "0")]
[DataRow("day (week day)", "Wednesday")]
[DataRow("day of the week (week day)", "4")]
[DataRow("day of the month", "2")]
[DataRow("day of the year", "61")]
[DataRow("week of the month", "1")]
[DataRow("week of the year (calendar week, week number)", "10")]
[DataRow("month", "March")]
[DataRow("month of the year", "3")]
[DataRow("month and day", "March 2")]
[DataRow("year", "2022")]
[DataRow("month and year", "March 2022")]
[DataRow("ISO 8601", "2022-03-02T10:30:45")]
[DataRow("ISO 8601 with time zone", "2022-03-02T10:30:45")]
[DataRow("RFC1123", "Wed, 02 Mar 2022 10:30:45 GMT")]
public void LocalFormatsWithLongTimeAndLongDate(string formatLabel, string expectedResult)
{
// Setup
var helperResults = AvailableResultsList.GetList(true, true, true, new DateTime(2022, 03, 02, 10, 30, 45));
// Act
var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase));
// Assert
Assert.AreEqual(expectedResult, result?.Value);
}
[DataTestMethod]
[DataRow("time utc", "t")]
[DataRow("date and time utc", "g")]
[DataRow("ISO 8601 UTC", "yyyy-MM-ddTHH:mm:ss")]
[DataRow("ISO 8601 UTC with time zone", "yyyy-MM-ddTHH:mm:ss'Z'")]
[DataRow("Universal time format: YYYY-MM-DD hh:mm:ss", "u")]
public void UtcFormatsWithShortTimeAndShortDate(string formatLabel, string expectedFormat)
{
// Setup
var helperResults = AvailableResultsList.GetList(true, false, false, new DateTime(2022, 03, 02, 10, 30, 45));
var expectedResult = new DateTime(2022, 03, 02, 10, 30, 45).ToUniversalTime().ToString(expectedFormat, CultureInfo.CurrentCulture);
// Act
var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase));
// Assert
Assert.AreEqual(expectedResult, result?.Value);
}
[DataTestMethod]
[DataRow("time utc", "t")]
[DataRow("date and time utc", "f")]
[DataRow("ISO 8601 UTC", "yyyy-MM-ddTHH:mm:ss")]
[DataRow("ISO 8601 UTC with time zone", "yyyy-MM-ddTHH:mm:ss'Z'")]
[DataRow("Universal time format: YYYY-MM-DD hh:mm:ss", "u")]
public void UtcFormatsWithShortTimeAndLongDate(string formatLabel, string expectedFormat)
{
// Setup
var helperResults = AvailableResultsList.GetList(true, false, true, new DateTime(2022, 03, 02, 10, 30, 45));
var expectedResult = new DateTime(2022, 03, 02, 10, 30, 45).ToUniversalTime().ToString(expectedFormat, CultureInfo.CurrentCulture);
// Act
var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase));
// Assert
Assert.AreEqual(expectedResult, result?.Value);
}
[DataTestMethod]
[DataRow("time utc", "T")]
[DataRow("date and time utc", "G")]
[DataRow("ISO 8601 UTC", "yyyy-MM-ddTHH:mm:ss")]
[DataRow("ISO 8601 UTC with time zone", "yyyy-MM-ddTHH:mm:ss'Z'")]
[DataRow("Universal time format: YYYY-MM-DD hh:mm:ss", "u")]
public void UtcFormatsWithLongTimeAndShortDate(string formatLabel, string expectedFormat)
{
// Setup
var helperResults = AvailableResultsList.GetList(true, true, false, new DateTime(2022, 03, 02, 10, 30, 45));
var expectedResult = new DateTime(2022, 03, 02, 10, 30, 45).ToUniversalTime().ToString(expectedFormat, CultureInfo.CurrentCulture);
// Act
var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase));
// Assert
Assert.AreEqual(expectedResult, result?.Value);
}
[DataTestMethod]
[DataRow("time utc", "T")]
[DataRow("date and time utc", "F")]
[DataRow("ISO 8601 UTC", "yyyy-MM-ddTHH:mm:ss")]
[DataRow("ISO 8601 UTC with time zone", "yyyy-MM-ddTHH:mm:ss'Z'")]
[DataRow("Universal time format: YYYY-MM-DD hh:mm:ss", "u")]
public void UtcFormatsWithLongTimeAndLongDate(string formatLabel, string expectedFormat)
{
// Setup
var helperResults = AvailableResultsList.GetList(true, true, true, new DateTime(2022, 03, 02, 10, 30, 45));
var expectedResult = new DateTime(2022, 03, 02, 10, 30, 45).ToUniversalTime().ToString(expectedFormat, CultureInfo.CurrentCulture);
// Act
var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase));
// Assert
Assert.AreEqual(expectedResult, result?.Value);
}
[TestMethod]
public void UnixTimestampFormat()
{
// Setup
string formatLabel = "Unix epoch time";
DateTime timeValue = DateTime.Now.ToUniversalTime();
var helperResults = AvailableResultsList.GetList(true, false, false, timeValue);
var expectedResult = (long)timeValue.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
// Act
var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase));
// Assert
Assert.AreEqual(expectedResult.ToString(CultureInfo.CurrentCulture), result?.Value);
}
[TestMethod]
public void WindowsFileTimeFormat()
{
// Setup
string formatLabel = "Windows file time (Int64 number)";
DateTime timeValue = DateTime.Now;
var helperResults = AvailableResultsList.GetList(true, false, false, timeValue);
var expectedResult = timeValue.Ticks.ToString(CultureInfo.CurrentCulture);
// Act
var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase));
// Assert
Assert.AreEqual(expectedResult, result?.Value);
}
[TestMethod]
public void ValidateEraResult()
{
// Setup
string formatLabel = "Era";
DateTime timeValue = DateTime.Now;
var helperResults = AvailableResultsList.GetList(true, false, false, timeValue);
var expectedResult = DateTimeFormatInfo.CurrentInfo.GetEraName(CultureInfo.CurrentCulture.Calendar.GetEra(timeValue));
// Act
var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase));
// Assert
Assert.AreEqual(expectedResult, result?.Value);
}
[TestMethod]
public void ValidateEraAbbreviationResult()
{
// Setup
string formatLabel = "Era abbreviation";
DateTime timeValue = DateTime.Now;
var helperResults = AvailableResultsList.GetList(true, false, false, timeValue);
var expectedResult = DateTimeFormatInfo.CurrentInfo.GetAbbreviatedEraName(CultureInfo.CurrentCulture.Calendar.GetEra(timeValue));
// Act
var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase));
// Assert
Assert.AreEqual(expectedResult, result?.Value);
}
[TestCleanup]
public void CleanUp()
{
// Set culture to original value
CultureInfo.CurrentCulture = originalCulture;
CultureInfo.CurrentUICulture = originalUiCulture;
}
}
}

View File

@ -0,0 +1,55 @@
// 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.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests")]
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
{
internal class AvailableResult
{
/// <summary>
/// Gets or sets the time/date value
/// </summary>
internal string Value { get; set; }
/// <summary>
/// Gets or sets the text used for the subtitle and as search term
/// </summary>
internal string Label { get; set; }
/// <summary>
/// Gets or sets an an alternative search tag that will be evaluated if label doesn't match. For example we like to show the era on searches for 'year' too.
/// </summary>
internal string AlternativeSearchTag { get; set; }
/// <summary>
/// Gets or sets a value indicating the type of result
/// </summary>
internal ResultIconType IconType { get; set; }
/// <summary>
/// Returns the path to the icon
/// </summary>
/// <param name="theme">Theme</param>
/// <returns>Path</returns>
internal string GetIconPath(string theme)
{
return IconType switch
{
ResultIconType.Time => $"Images\\time.{theme}.png",
ResultIconType.Date => $"Images\\calendar.{theme}.png",
ResultIconType.DateTime => $"Images\\timeDate.{theme}.png",
_ => string.Empty,
};
}
}
internal enum ResultIconType
{
Time,
Date,
DateTime,
}
}

View File

@ -0,0 +1,267 @@
// 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.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.PowerToys.Run.Plugin.TimeDate.Properties;
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
{
internal 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>
/// <returns>List of results</returns>
internal static List<AvailableResult> GetList(bool isKeywordSearch, bool? timeLong = null, bool? dateLong = null, DateTime? timestamp = 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;
DateTime dateTimeNow = timestamp ?? DateTime.Now;
DateTime dateTimeNowUtc = dateTimeNow.ToUniversalTime();
results.AddRange(new[]
{
// This range is reserved for the following three results: Time, Date, Now
// Don't add any new result in this range! For new results, please use the next range.
new AvailableResult()
{
Value = dateTimeNow.ToString(TimeAndDateHelper.GetStringFormat(FormatStringType.Time, timeExtended, dateExtended), CultureInfo.CurrentCulture),
Label = Resources.Microsoft_plugin_timedate_Time,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, string.Empty, "Microsoft_plugin_timedate_SearchTagTimeNow"),
IconType = ResultIconType.Time,
},
new AvailableResult()
{
Value = dateTimeNow.ToString(TimeAndDateHelper.GetStringFormat(FormatStringType.Date, timeExtended, dateExtended), CultureInfo.CurrentCulture),
Label = Resources.Microsoft_plugin_timedate_Date,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, string.Empty, "Microsoft_plugin_timedate_SearchTagDateNow"),
IconType = ResultIconType.Date,
},
new AvailableResult()
{
Value = dateTimeNow.ToString(TimeAndDateHelper.GetStringFormat(FormatStringType.DateTime, timeExtended, dateExtended), CultureInfo.CurrentCulture),
Label = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_DateAndTime", "Microsoft_plugin_timedate_Now"),
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
IconType = ResultIconType.DateTime,
},
});
if (isKeywordSearch || !TimeDateSettings.Instance.OnlyDateTimeNowGlobal)
{
// We use long instead of int for unix time stamp because int ist to small after 03:14:07 UTC 2038-01-19
long unixTimestamp = (long)dateTimeNowUtc.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
int weekOfYear = calendar.GetWeekOfYear(dateTimeNow, DateTimeFormatInfo.CurrentInfo.CalendarWeekRule, DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek);
string era = DateTimeFormatInfo.CurrentInfo.GetEraName(calendar.GetEra(dateTimeNow));
string eraShort = DateTimeFormatInfo.CurrentInfo.GetAbbreviatedEraName(calendar.GetEra(dateTimeNow));
results.AddRange(new[]
{
new AvailableResult()
{
Value = dateTimeNowUtc.ToString(TimeAndDateHelper.GetStringFormat(FormatStringType.Time, timeExtended, dateExtended), CultureInfo.CurrentCulture),
Label = Resources.Microsoft_plugin_timedate_TimeUtc,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, string.Empty, "Microsoft_plugin_timedate_SearchTagTimeNow"),
IconType = ResultIconType.Time,
},
new AvailableResult()
{
Value = dateTimeNowUtc.ToString(TimeAndDateHelper.GetStringFormat(FormatStringType.DateTime, timeExtended, dateExtended), CultureInfo.CurrentCulture),
Label = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_DateAndTimeUtc", "Microsoft_plugin_timedate_NowUtc"),
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
IconType = ResultIconType.DateTime,
},
new AvailableResult()
{
Value = unixTimestamp.ToString(CultureInfo.CurrentCulture),
Label = Resources.Microsoft_plugin_timedate_Unix,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
IconType = ResultIconType.DateTime,
},
new AvailableResult()
{
Value = dateTimeNow.Hour.ToString(CultureInfo.CurrentCulture),
Label = Resources.Microsoft_plugin_timedate_Hour,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagTime"),
IconType = ResultIconType.Time,
},
new AvailableResult()
{
Value = dateTimeNow.Minute.ToString(CultureInfo.CurrentCulture),
Label = Resources.Microsoft_plugin_timedate_Minute,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagTime"),
IconType = ResultIconType.Time,
},
new AvailableResult()
{
Value = dateTimeNow.Second.ToString(CultureInfo.CurrentCulture),
Label = Resources.Microsoft_plugin_timedate_Second,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagTime"),
IconType = ResultIconType.Time,
},
new AvailableResult()
{
Value = dateTimeNow.Millisecond.ToString(CultureInfo.CurrentCulture),
Label = Resources.Microsoft_plugin_timedate_Millisecond,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagTime"),
IconType = ResultIconType.Time,
},
new AvailableResult()
{
Value = DateTimeFormatInfo.CurrentInfo.GetDayName(dateTimeNow.DayOfWeek),
Label = Resources.Microsoft_plugin_timedate_Day,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
IconType = ResultIconType.Date,
},
new AvailableResult()
{
Value = TimeAndDateHelper.GetNumberOfDayInWeek(dateTimeNow).ToString(CultureInfo.CurrentCulture),
Label = Resources.Microsoft_plugin_timedate_DayOfWeek,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
IconType = ResultIconType.Date,
},
new AvailableResult()
{
Value = dateTimeNow.Day.ToString(CultureInfo.CurrentCulture),
Label = Resources.Microsoft_plugin_timedate_DayOfMonth,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
IconType = ResultIconType.Date,
},
new AvailableResult()
{
Value = dateTimeNow.DayOfYear.ToString(CultureInfo.CurrentCulture),
Label = Resources.Microsoft_plugin_timedate_DayOfYear,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
IconType = ResultIconType.Date,
},
new AvailableResult()
{
Value = TimeAndDateHelper.GetWeekOfMonth(dateTimeNow).ToString(CultureInfo.CurrentCulture),
Label = Resources.Microsoft_plugin_timedate_WeekOfMonth,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
IconType = ResultIconType.Date,
},
new AvailableResult()
{
Value = weekOfYear.ToString(CultureInfo.CurrentCulture),
Label = Resources.Microsoft_plugin_timedate_WeekOfYear,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
IconType = ResultIconType.Date,
},
new AvailableResult()
{
Value = DateTimeFormatInfo.CurrentInfo.GetMonthName(dateTimeNow.Month),
Label = Resources.Microsoft_plugin_timedate_Month,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
IconType = ResultIconType.Date,
},
new AvailableResult()
{
Value = dateTimeNow.Month.ToString(CultureInfo.CurrentCulture),
Label = Resources.Microsoft_plugin_timedate_MonthOfYear,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
IconType = ResultIconType.Date,
},
new AvailableResult()
{
Value = dateTimeNow.ToString("M", CultureInfo.CurrentCulture),
Label = Resources.Microsoft_plugin_timedate_DayMonth,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
IconType = ResultIconType.Date,
},
new AvailableResult()
{
Value = calendar.GetYear(dateTimeNow).ToString(CultureInfo.CurrentCulture),
Label = Resources.Microsoft_plugin_timedate_Year,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
IconType = ResultIconType.Date,
},
new AvailableResult()
{
Value = era,
Label = Resources.Microsoft_plugin_timedate_Era,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagEra"),
IconType = ResultIconType.Date,
},
new AvailableResult()
{
Value = era != eraShort ? eraShort : string.Empty, // Setting value to empty string if 'era == eraShort'. This result will be filtered later.
Label = Resources.Microsoft_plugin_timedate_EraAbbreviation,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagEra"),
IconType = ResultIconType.Date,
},
new AvailableResult()
{
Value = dateTimeNow.ToString("Y", CultureInfo.CurrentCulture),
Label = Resources.Microsoft_plugin_timedate_MonthYear,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
IconType = ResultIconType.Date,
},
new AvailableResult()
{
Value = dateTimeNow.Ticks.ToString(CultureInfo.CurrentCulture),
Label = Resources.Microsoft_plugin_timedate_WindowsFileTime,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
IconType = ResultIconType.DateTime,
},
new AvailableResult()
{
Value = dateTimeNowUtc.ToString("u"),
Label = Resources.Microsoft_plugin_timedate_UniversalTime,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
IconType = ResultIconType.DateTime,
},
new AvailableResult()
{
Value = dateTimeNow.ToString("s"),
Label = Resources.Microsoft_plugin_timedate_Iso8601,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
IconType = ResultIconType.DateTime,
},
new AvailableResult()
{
Value = dateTimeNowUtc.ToString("yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture),
Label = Resources.Microsoft_plugin_timedate_Iso8601Utc,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
IconType = ResultIconType.DateTime,
},
new AvailableResult()
{
Value = dateTimeNow.ToString("yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture),
Label = Resources.Microsoft_plugin_timedate_Iso8601Zone,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
IconType = ResultIconType.DateTime,
},
new AvailableResult()
{
Value = dateTimeNowUtc.ToString("yyyy-MM-ddTHH:mm:ss'Z'", CultureInfo.InvariantCulture),
Label = Resources.Microsoft_plugin_timedate_Iso8601ZoneUtc,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
IconType = ResultIconType.DateTime,
},
new AvailableResult()
{
Value = dateTimeNow.ToString("R"),
Label = Resources.Microsoft_plugin_timedate_Rfc1123,
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
IconType = ResultIconType.DateTime,
},
});
}
// Return only results where value is not empty
// This can happen, for example, when we can't read the 'era' or when 'era == era abbreviation' and we set value explicitly to an empty string.
return results.Where(x => !string.IsNullOrEmpty(x.Value)).ToList();
}
}
}

View File

@ -0,0 +1,92 @@
// 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 System.Runtime.CompilerServices;
using System.Windows;
using Microsoft.PowerToys.Run.Plugin.TimeDate.Properties;
using Wox.Plugin;
using Wox.Plugin.Logger;
[assembly: InternalsVisibleTo("Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests")]
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
{
internal static class ResultHelper
{
/// <summary>
/// Get the string based on the requested type
/// </summary>
/// <param name="isSystemTimeDate">Does the user search for system date/time?</param>
/// <param name="stringId">Id of the string. (Example: `MyString` for `MyString` and `MyStringNow`)</param>
/// <param name="stringIdNow">Optional string id for now case</param>
/// <returns>The string from the resource file, or <see cref="string.Empty"/> otherwise.</returns>
internal static string SelectStringFromResources(bool isSystemTimeDate, string stringId, string stringIdNow = default)
{
if (!isSystemTimeDate)
{
return Resources.ResourceManager.GetString(stringId, CultureInfo.CurrentUICulture) ?? string.Empty;
}
else if (!string.IsNullOrEmpty(stringIdNow))
{
return Resources.ResourceManager.GetString(stringIdNow, CultureInfo.CurrentUICulture) ?? string.Empty;
}
else
{
return Resources.ResourceManager.GetString(stringId + "Now", CultureInfo.CurrentUICulture) ?? string.Empty;
}
}
/// <summary>
/// Copy the given text to the clipboard
/// </summary>
/// <param name="text">The text to copy to the clipboard</param>
/// <returns><see langword="true"/>The text successful copy to the clipboard, otherwise <see langword="false"/></returns>
/// <remarks>Code copied from TimeZone plugin</remarks>
internal static bool CopyToClipBoard(in string text)
{
try
{
Clipboard.Clear();
Clipboard.SetText(text);
return true;
}
catch (Exception exception)
{
Log.Exception("Can't copy to clipboard", exception, typeof(ResultHelper));
return false;
}
}
/// <summary>
/// Create a tool tip for the alternative search tags
/// </summary>
/// <param name="result">The <see cref="AvailableResult"/>.</param>
/// <returns>New <see cref="ToolTipData"/> object or null if <see cref="AvailableResult.AlternativeSearchTag"/> is empty.</returns>
internal static ToolTipData GetSearchTagToolTip(AvailableResult result, out Visibility visibility)
{
switch (string.IsNullOrEmpty(result.AlternativeSearchTag))
{
case true:
visibility = Visibility.Hidden;
return null;
default:
visibility = Visibility.Visible;
return new ToolTipData(Resources.Microsoft_plugin_timedate_ToolTipAlternativeSearchTag, result.AlternativeSearchTag);
}
}
/// <summary>
/// Gets a result with an error message that only numbers can't be parsed
/// </summary>
/// <returns>Element of type <see cref="Result"/>.</returns>
internal static Result CreateNumberErrorResult(string theme) => new Result()
{
Title = Resources.Microsoft_plugin_timedate_ErrorResultTitle,
SubTitle = Resources.Microsoft_plugin_timedate_ErrorResultSubTitle,
IcoPath = $"Images\\Warning.{theme}.png",
};
}
}

View File

@ -0,0 +1,182 @@
// 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.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows;
using Microsoft.PowerToys.Run.Plugin.TimeDate.Properties;
using Wox.Infrastructure;
using Wox.Plugin;
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
{
/// <summary>
/// SearchController: Class tot hold the search method that filter available date time formats
/// Extra class to simplify code in <see cref="Main"/> class
/// </summary>
internal static class SearchController
{
/// <summary>
/// Var that holds the delimiter between format and date
/// </summary>
private const string InputDelimiter = "::";
/// <summary>
/// A list of conjunctions that we ignore on search
/// </summary>
private static readonly string[] _conjunctionList = Resources.Microsoft_plugin_timedate_Search_ConjunctionList.Split("; ");
/// <summary>
/// Searches for results
/// </summary>
/// <param name="query">Search query object</param>
/// <returns>List of Wox <see cref="Result"/>s.</returns>
internal static List<Result> ExecuteSearch(Query query, string iconTheme)
{
List<AvailableResult> availableFormats = new List<AvailableResult>();
List<Result> results = new List<Result>();
bool isKeywordSearch = !string.IsNullOrEmpty(query.ActionKeyword);
bool isEmptySearchInput = string.IsNullOrEmpty(query.Search);
string searchTerm = query.Search;
// Empty search without keyword => return no results
if (!isKeywordSearch && isEmptySearchInput)
{
return results;
}
// Conjunction search without keyword => return no results
// (This improves the results on global queries.)
if (!isKeywordSearch && _conjunctionList.Any(x => x.Equals(searchTerm, StringComparison.CurrentCultureIgnoreCase)))
{
return results;
}
// Switch search type
if (isEmptySearchInput)
{
// Return all results for system time/date on empty keyword search
availableFormats.AddRange(AvailableResultsList.GetList(isKeywordSearch));
}
else if (Regex.IsMatch(searchTerm, @".+" + Regex.Escape(InputDelimiter) + @".+"))
{
// Search for specified format with specified time/date value
var userInput = searchTerm.Split(InputDelimiter);
if (TimeAndDateHelper.ParseStringAsDateTime(userInput[1], out DateTime timestamp))
{
availableFormats.AddRange(AvailableResultsList.GetList(isKeywordSearch, null, null, timestamp));
searchTerm = userInput[0];
}
}
else if (TimeAndDateHelper.ParseStringAsDateTime(searchTerm, out DateTime timestamp))
{
// Return all formats for specified time/date value
availableFormats.AddRange(AvailableResultsList.GetList(isKeywordSearch, null, null, timestamp));
searchTerm = string.Empty;
}
else
{
// Search for specified format with system time/date
availableFormats.AddRange(AvailableResultsList.GetList(isKeywordSearch));
}
// Check searchTerm after getting results to select type of result list
if (string.IsNullOrEmpty(searchTerm))
{
// Generate list with all results
foreach (var f in availableFormats)
{
results.Add(new Result
{
Title = f.Value,
SubTitle = $"{f.Label} - {Resources.Microsoft_plugin_timedate_SubTitleNote}",
ToolTipData = ResultHelper.GetSearchTagToolTip(f, out Visibility v),
ToolTipVisibility = v,
IcoPath = f.GetIconPath(iconTheme),
Action = _ => ResultHelper.CopyToClipBoard(f.Value),
ContextData = f,
});
}
}
else
{
// Generate filtered list of results
foreach (var f in availableFormats)
{
var resultMatchScore = GetMatchScore(searchTerm, f.Label, f.AlternativeSearchTag, !isKeywordSearch);
if (resultMatchScore > 0)
{
results.Add(new Result
{
Title = f.Value,
SubTitle = $"{f.Label} - {Resources.Microsoft_plugin_timedate_SubTitleNote}",
ToolTipData = ResultHelper.GetSearchTagToolTip(f, out Visibility v),
ToolTipVisibility = v,
IcoPath = f.GetIconPath(iconTheme),
Action = _ => ResultHelper.CopyToClipBoard(f.Value),
Score = resultMatchScore,
ContextData = f,
});
}
}
}
// If search term is only a number that can't be parsed return an error message
if (!isEmptySearchInput && results.Count == 0 && searchTerm.Any(char.IsNumber) && Regex.IsMatch(searchTerm, @"\w+\d+$") &&
!searchTerm.Contains(InputDelimiter) && !searchTerm.Any(char.IsWhiteSpace) && !searchTerm.Any(char.IsPunctuation))
{
// Without plugin key word show only if message is not hidden by setting
if (isKeywordSearch || !TimeDateSettings.Instance.HideNumberMessageOnGlobalQuery)
{
results.Add(ResultHelper.CreateNumberErrorResult(iconTheme));
}
}
return results;
}
/// <summary>
/// Checks the format for a match with the user query and returns the score.
/// </summary>
/// <param name="query">The user query.</param>
/// <param name="label">The label of the format.</param>
/// <param name="tags">The search tag list as string.</param>
/// <param name="isGlobalSearch">Is this a global search?</param>
/// <returns>The score for the result.</returns>
private static int GetMatchScore(string query, string label, string tags, bool isGlobalSearch)
{
// The query is global and the first word don't match any word in the label or tags => Return score of zero
if (isGlobalSearch)
{
char[] chars = new char[] { ' ', ',', ';', '(', ')' };
string queryFirstWord = query.Split(chars)[0];
string[] words = $"{label} {tags}".Split(chars);
if (!words.Any(x => x.Trim().Equals(queryFirstWord, StringComparison.CurrentCultureIgnoreCase)))
{
return 0;
}
}
// Get match for label (or for tags if label score is <1)
int score = StringMatcher.FuzzySearch(query, label).Score;
if (score < 1)
{
foreach (string t in tags.Split(";"))
{
var tagScore = StringMatcher.FuzzySearch(query, t.Trim()).Score / 2;
if (tagScore > score)
{
score = tagScore / 2;
}
}
}
return score;
}
}
}

View File

@ -0,0 +1,129 @@
// 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 System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
[assembly: InternalsVisibleTo("Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests")]
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
{
internal static class TimeAndDateHelper
{
/// <summary>
/// Get the format for the time string
/// </summary>
/// <param name="targetFormat">Type of format</param>
/// <param name="timeLong">Show date with weekday and name of month (long format)</param>
/// <param name="dateLong">Show time with seconds (long format)</param>
/// <returns>String that identifies the time/date format (<see href="https://docs.microsoft.com/en-us/dotnet/api/system.datetime.tostring"/>)</returns>
internal static string GetStringFormat(FormatStringType targetFormat, bool timeLong, bool dateLong)
{
switch (targetFormat)
{
case FormatStringType.Time:
return timeLong ? "T" : "t";
case FormatStringType.Date:
return dateLong ? "D" : "d";
case FormatStringType.DateTime:
if (timeLong & dateLong)
{
return "F"; // Friday, October 31, 2008 5:04:32 PM
}
else if (timeLong & !dateLong)
{
return "G"; // 10/31/2008 5:04:32 PM
}
else if (!timeLong & dateLong)
{
return "f"; // Friday, October 31, 2008 5:04 PM
}
else
{
// (!timeLong & !dateLong)
return "g"; // 10/31/2008 5:04 PM
}
default:
return string.Empty; // Windows default based on current culture settings
}
}
/// <summary>
/// Returns the number week in the month (Used code from 'David Morton' from <see href="https://social.msdn.microsoft.com/Forums/vstudio/en-US/bf504bba-85cb-492d-a8f7-4ccabdf882cb/get-week-number-for-month"/>)
/// </summary>
/// <param name="date">date</param>
/// <returns>Number of week in the month</returns>
internal static int GetWeekOfMonth(DateTime date)
{
DateTime beginningOfMonth = new DateTime(date.Year, date.Month, 1);
while (date.Date.AddDays(1).DayOfWeek != CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek)
{
date = date.AddDays(1);
}
return (int)Math.Truncate((double)date.Subtract(beginningOfMonth).TotalDays / 7f) + 1;
}
/// <summary>
/// Returns the number of the day in the week
/// </summary>
/// <param name="date">Date</param>
/// <returns>Number of the day in the week</returns>
internal static int GetNumberOfDayInWeek(DateTime date)
{
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;
}
/// <summary>
/// Convert input string to a <see cref="DateTime"/> object in local time
/// </summary>
/// <param name="input">String with date/time</param>
/// <param name="timestamp">The new <see cref="DateTime"/> object</param>
/// <returns>True on success, otherwise false</returns>
internal static bool ParseStringAsDateTime(in string input, out DateTime timestamp)
{
if (DateTime.TryParse(input, out timestamp))
{
// Known date/time format
return true;
}
else if (Regex.IsMatch(input, @"^u\d+") && input.Length <= 12 && long.TryParse(input.TrimStart('u'), out long secondsInt))
{
// unix time stamp
// we use long instead of int because int ist to small after 03:14:07 UTC 2038-01-19
timestamp = new DateTime(1970, 1, 1).AddSeconds(secondsInt).ToLocalTime();
return true;
}
else if (Regex.IsMatch(input, @"^ft\d+") && long.TryParse(input.TrimStart("ft".ToCharArray()), out long secondsLong))
{
// windows file time
timestamp = new DateTime(secondsLong);
return true;
}
else
{
timestamp = new DateTime(1, 1, 1, 1, 1, 1);
return false;
}
}
}
/// <summary>
/// Type of time/date format
/// </summary>
internal enum FormatStringType
{
Time,
Date,
DateTime,
}
}

View File

@ -0,0 +1,154 @@
// 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.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Microsoft.PowerToys.Run.Plugin.TimeDate.Properties;
using Microsoft.PowerToys.Settings.UI.Library;
[assembly: InternalsVisibleTo("Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests")]
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
{
/// <summary>
/// Additional settings for the WindowWalker plugin.
/// </summary>
/// <remarks>Some code parts reused from TimeZone plugin.</remarks>
internal sealed class TimeDateSettings
{
/// <summary>
/// Are the class properties initialized with default values
/// </summary>
private readonly bool _initialized;
/// <summary>
/// An instance of the class <see cref="TimeDateSettings"></see>
/// </summary>
private static TimeDateSettings instance;
/// <summary>
/// Gets a value indicating whether to show only the time and date in global results or not
/// </summary>
internal bool OnlyDateTimeNowGlobal { get; private set; }
/// <summary>
/// Gets a value indicating whether to show the time with seconds or not
/// </summary>
internal bool TimeWithSeconds { get; private set; }
/// <summary>
/// Gets a value indicating whether the date with the weekday or not
/// </summary>
internal bool DateWithWeekday { get; private set; }
/// <summary>
/// Gets a value indicating whether to hide the number input error message on global results
/// </summary>
internal bool HideNumberMessageOnGlobalQuery { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="TimeDateSettings"/> class.
/// Private constructor to make sure there is never more than one instance of this class
/// </summary>
private TimeDateSettings()
{
// Init class properties with default values
UpdateSettings(null);
_initialized = true;
}
/// <summary>
/// Gets an instance property of this class that makes sure that the first instance gets created
/// and that all the requests end up at that one instance.
/// The benefit of this is that we don't need additional variables/parameters
/// to communicate the settings between plugin's classes/methods.
/// We can simply access this one instance, whenever we need the actual settings.
/// </summary>
internal static TimeDateSettings Instance
{
get
{
if (instance == null)
{
instance = new TimeDateSettings();
}
return instance;
}
}
/// <summary>
/// Return a list with all additional plugin options.
/// </summary>
/// <returns>A list with all additional plugin options.</returns>
internal static List<PluginAdditionalOption> GetAdditionalOptions()
{
var optionList = new List<PluginAdditionalOption>
{
new PluginAdditionalOption()
{
// ToDo: When description property is implemented (#15853), move the note in brackets to description.
Key = nameof(OnlyDateTimeNowGlobal),
DisplayLabel = Resources.Microsoft_plugin_timedate_SettingOnlyDateTimeNowGlobal,
Value = true,
},
new PluginAdditionalOption()
{
// ToDo: When description property is implemented (#15853), move the note in brackets to description.
Key = nameof(TimeWithSeconds),
DisplayLabel = Resources.Microsoft_plugin_timedate_SettingTimeWithSeconds,
Value = false,
},
new PluginAdditionalOption()
{
// ToDo: When description property is implemented (#15853), move the note in brackets to description.
Key = nameof(DateWithWeekday),
DisplayLabel = Resources.Microsoft_plugin_timedate_SettingDateWithWeekday,
Value = false,
},
new PluginAdditionalOption()
{
Key = nameof(HideNumberMessageOnGlobalQuery),
DisplayLabel = Resources.Microsoft_plugin_timedate_SettingHideNumberMessageOnGlobalQuery,
Value = false,
},
};
return optionList;
}
/// <summary>
/// Update this settings.
/// </summary>
/// <param name="settings">The settings for all power launcher plugins.</param>
internal void UpdateSettings(PowerLauncherPluginSettings settings)
{
if ((settings is null || settings.AdditionalOptions is null) & _initialized)
{
return;
}
OnlyDateTimeNowGlobal = GetSettingOrDefault(settings, nameof(OnlyDateTimeNowGlobal));
TimeWithSeconds = GetSettingOrDefault(settings, nameof(TimeWithSeconds));
DateWithWeekday = GetSettingOrDefault(settings, nameof(DateWithWeekday));
HideNumberMessageOnGlobalQuery = GetSettingOrDefault(settings, nameof(HideNumberMessageOnGlobalQuery));
}
/// <summary>
/// Return one <see cref="bool"/> setting 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 bool GetSettingOrDefault(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?.Value ?? GetAdditionalOptions().First(x => x.Key == name).Value;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@ -0,0 +1,115 @@
// 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.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Threading;
using System.Windows.Controls;
using System.Windows.Input;
using ManagedCommon;
using Microsoft.PowerToys.Run.Plugin.TimeDate.Components;
using Microsoft.PowerToys.Run.Plugin.TimeDate.Properties;
using Microsoft.PowerToys.Settings.UI.Library;
using Wox.Plugin;
namespace Microsoft.PowerToys.Run.Plugin.TimeDate
{
public class Main : IPlugin, IPluginI18n, ISettingProvider, IContextMenu
{
private PluginInitContext _context;
public string IconTheme { get; set; }
public string Name => Resources.Microsoft_plugin_timedate_plugin_name;
public string Description => GetTranslatedPluginDescription();
public IEnumerable<PluginAdditionalOption> AdditionalOptions
{
get
{
return TimeDateSettings.GetAdditionalOptions();
}
}
public List<ContextMenuResult> LoadContextMenus(Result selectedResult)
{
if (!(selectedResult?.ContextData is AvailableResult data))
{
return new List<ContextMenuResult>(0);
}
return new List<ContextMenuResult>()
{
new ContextMenuResult
{
AcceleratorKey = Key.C,
AcceleratorModifiers = ModifierKeys.Control,
FontFamily = "Segoe MDL2 Assets",
Glyph = "\xE8C8", // E8C8 => Symbol: Copy
Title = Resources.Microsoft_plugin_timedate_CopyToClipboard,
Action = _ => ResultHelper.CopyToClipBoard(data.Value),
},
};
}
public void Init(PluginInitContext context)
{
_context = context;
_context.API.ThemeChanged += OnThemeChanged;
UpdateIconTheme(_context.API.GetCurrentTheme());
}
public List<Result> Query(Query query)
{
if (query == null)
{
throw new ArgumentNullException(paramName: nameof(query));
}
return SearchController.ExecuteSearch(query, IconTheme);
}
private void UpdateIconTheme(Theme theme)
{
if (theme == Theme.Light || theme == Theme.HighContrastWhite)
{
IconTheme = "light";
}
else
{
IconTheme = "dark";
}
}
private void OnThemeChanged(Theme currentTheme, Theme newTheme)
{
UpdateIconTheme(newTheme);
}
public string GetTranslatedPluginDescription()
{
string timeExample = DateTime.Now.ToString("T", CultureInfo.CurrentCulture);
string dateExample = DateTime.Now.ToString("d", CultureInfo.CurrentCulture);
return string.Format(CultureInfo.CurrentCulture, Resources.Microsoft_plugin_timedate_plugin_description, dateExample, timeExample);
}
public string GetTranslatedPluginTitle()
{
return Resources.Microsoft_plugin_timedate_plugin_name;
}
public Control CreateSettingPanel()
{
throw new NotImplementedException();
}
public void UpdateSettings(PowerLauncherPluginSettings settings)
{
TimeDateSettings.Instance.UpdateSettings(settings);
}
}
}

View File

@ -0,0 +1,128 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\Version.props" />
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Microsoft.PowerToys.Run.Plugin.TimeDate</RootNamespace>
<AssemblyName>Microsoft.PowerToys.Run.Plugin.TimeDate</AssemblyName>
<Version>$(Version).0</Version>
<useWPF>true</useWPF>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<Platforms>x64</Platforms>
<NeutralLanguage>en-US</NeutralLanguage>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisMode>Recommended</AnalysisMode>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\..\..\..\..\x64\Debug\modules\launcher\Plugins\TimeDate\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>8.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<WarningLevel>4</WarningLevel>
<Optimize>false</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutputPath>..\..\..\..\..\x64\Release\modules\launcher\Plugins\TimeDate\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>8.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Wox.Infrastructure\Wox.Infrastructure.csproj" />
<ProjectReference Include="..\..\Wox.Plugin\Wox.Plugin.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\..\codeAnalysis\GlobalSuppressions.cs">
<Link>GlobalSuppressions.cs</Link>
</Compile>
<AdditionalFiles Include="..\..\..\..\codeAnalysis\StyleCop.json">
<Link>StyleCop.json</Link>
</AdditionalFiles>
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers">
<Version>1.1.118</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Update="Images\timeDate.light.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Images\timeDate.dark.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Images\time.light.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Images\time.dark.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Images\calendar.light.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Images\calendar.dark.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Images\Warning.dark.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Images\Warning.light.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,532 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.PowerToys.Run.Plugin.TimeDate.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Copy value (Ctrl+C).
/// </summary>
internal static string Microsoft_plugin_timedate_CopyToClipboard {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_CopyToClipboard", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Date.
/// </summary>
internal static string Microsoft_plugin_timedate_Date {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Date", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Date and time.
/// </summary>
internal static string Microsoft_plugin_timedate_DateAndTime {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_DateAndTime", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Date and time UTC.
/// </summary>
internal static string Microsoft_plugin_timedate_DateAndTimeUtc {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_DateAndTimeUtc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Day (Week day).
/// </summary>
internal static string Microsoft_plugin_timedate_Day {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Day", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Month and day.
/// </summary>
internal static string Microsoft_plugin_timedate_DayMonth {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_DayMonth", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Day of the month.
/// </summary>
internal static string Microsoft_plugin_timedate_DayOfMonth {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_DayOfMonth", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Day of the week (Week day).
/// </summary>
internal static string Microsoft_plugin_timedate_DayOfWeek {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_DayOfWeek", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Day of the year.
/// </summary>
internal static string Microsoft_plugin_timedate_DayOfYear {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_DayOfYear", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Era.
/// </summary>
internal static string Microsoft_plugin_timedate_Era {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Era", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Era abbreviation.
/// </summary>
internal static string Microsoft_plugin_timedate_EraAbbreviation {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_EraAbbreviation", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Valid prefixes: &apos;u&apos; for Unix Timestamp, &apos;ft&apos; for Windows file time.
/// </summary>
internal static string Microsoft_plugin_timedate_ErrorResultSubTitle {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_ErrorResultSubTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error: Invalid number input.
/// </summary>
internal static string Microsoft_plugin_timedate_ErrorResultTitle {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_ErrorResultTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Hour.
/// </summary>
internal static string Microsoft_plugin_timedate_Hour {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Hour", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ISO 8601.
/// </summary>
internal static string Microsoft_plugin_timedate_Iso8601 {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Iso8601", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ISO 8601 UTC.
/// </summary>
internal static string Microsoft_plugin_timedate_Iso8601Utc {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Iso8601Utc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ISO 8601 with time zone.
/// </summary>
internal static string Microsoft_plugin_timedate_Iso8601Zone {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Iso8601Zone", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ISO 8601 UTC with time zone.
/// </summary>
internal static string Microsoft_plugin_timedate_Iso8601ZoneUtc {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Iso8601ZoneUtc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Millisecond.
/// </summary>
internal static string Microsoft_plugin_timedate_Millisecond {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Millisecond", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Minute.
/// </summary>
internal static string Microsoft_plugin_timedate_Minute {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Minute", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Month.
/// </summary>
internal static string Microsoft_plugin_timedate_Month {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Month", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Month of the year.
/// </summary>
internal static string Microsoft_plugin_timedate_MonthOfYear {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_MonthOfYear", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Month and year.
/// </summary>
internal static string Microsoft_plugin_timedate_MonthYear {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_MonthYear", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Now.
/// </summary>
internal static string Microsoft_plugin_timedate_Now {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Now", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Now UTC.
/// </summary>
internal static string Microsoft_plugin_timedate_NowUtc {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_NowUtc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Provides time and date values for the system time or a custom time stamp (Examples: &apos;day&apos;, &apos;day::{0}&apos;, &apos;time::{1}&apos;, &apos;calendar week::{0}&apos;).
/// </summary>
internal static string Microsoft_plugin_timedate_plugin_description {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_plugin_description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Time and Date.
/// </summary>
internal static string Microsoft_plugin_timedate_plugin_name {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_plugin_name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to RFC1123.
/// </summary>
internal static string Microsoft_plugin_timedate_Rfc1123 {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Rfc1123", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to for; and; nor; but; or; so.
/// </summary>
internal static string Microsoft_plugin_timedate_Search_ConjunctionList {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Search_ConjunctionList", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Date.
/// </summary>
internal static string Microsoft_plugin_timedate_SearchTagDate {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagDate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Current date; Now.
/// </summary>
internal static string Microsoft_plugin_timedate_SearchTagDateNow {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagDateNow", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Year; Calendar era; Date.
/// </summary>
internal static string Microsoft_plugin_timedate_SearchTagEra {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagEra", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Current year; Current calendar era; Current date; Now.
/// </summary>
internal static string Microsoft_plugin_timedate_SearchTagEraNow {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagEraNow", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Date and time; Time and Date.
/// </summary>
internal static string Microsoft_plugin_timedate_SearchTagFormat {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagFormat", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Current date and time; Current time and date; Now.
/// </summary>
internal static string Microsoft_plugin_timedate_SearchTagFormatNow {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagFormatNow", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Time.
/// </summary>
internal static string Microsoft_plugin_timedate_SearchTagTime {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagTime", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Current Time; Now.
/// </summary>
internal static string Microsoft_plugin_timedate_SearchTagTimeNow {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagTimeNow", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Second.
/// </summary>
internal static string Microsoft_plugin_timedate_Second {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Second", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Show date with weekday and name of month (Applies to &apos;Date&apos; and &apos;Now&apos; result).
/// </summary>
internal static string Microsoft_plugin_timedate_SettingDateWithWeekday {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SettingDateWithWeekday", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Hide &apos;Invalid number input&apos; error message on global queries.
/// </summary>
internal static string Microsoft_plugin_timedate_SettingHideNumberMessageOnGlobalQuery {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SettingHideNumberMessageOnGlobalQuery", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Show only &apos;Time&apos;, &apos;Date&apos; and &apos;Now&apos; result on global queries
///(Regardless of this setting, on global queries the first search word has to be a full match.).
/// </summary>
internal static string Microsoft_plugin_timedate_SettingOnlyDateTimeNowGlobal {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SettingOnlyDateTimeNowGlobal", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Show time with seconds (Applies to &apos;Time&apos; and &apos;Now&apos; result).
/// </summary>
internal static string Microsoft_plugin_timedate_SettingTimeWithSeconds {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SettingTimeWithSeconds", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Select or press Ctrl+C to copy.
/// </summary>
internal static string Microsoft_plugin_timedate_SubTitleNote {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_SubTitleNote", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Time.
/// </summary>
internal static string Microsoft_plugin_timedate_Time {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Time", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Time UTC.
/// </summary>
internal static string Microsoft_plugin_timedate_TimeUtc {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_TimeUtc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Alternative search tags:.
/// </summary>
internal static string Microsoft_plugin_timedate_ToolTipAlternativeSearchTag {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_ToolTipAlternativeSearchTag", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Universal time format: YYYY-MM-DD hh:mm:ss.
/// </summary>
internal static string Microsoft_plugin_timedate_UniversalTime {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_UniversalTime", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unix epoch time.
/// </summary>
internal static string Microsoft_plugin_timedate_Unix {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Unix", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Week of the month.
/// </summary>
internal static string Microsoft_plugin_timedate_WeekOfMonth {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_WeekOfMonth", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Week of the year (Calendar week, Week number).
/// </summary>
internal static string Microsoft_plugin_timedate_WeekOfYear {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_WeekOfYear", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Windows file time (Int64 number).
/// </summary>
internal static string Microsoft_plugin_timedate_WindowsFileTime {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_WindowsFileTime", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Year.
/// </summary>
internal static string Microsoft_plugin_timedate_Year {
get {
return ResourceManager.GetString("Microsoft_plugin_timedate_Year", resourceCulture);
}
}
}
}

View File

@ -0,0 +1,294 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Microsoft_plugin_timedate_CopyToClipboard" xml:space="preserve">
<value>Copy value (Ctrl+C)</value>
<comment>'Ctrl+C' is a shortcut</comment>
</data>
<data name="Microsoft_plugin_timedate_Date" xml:space="preserve">
<value>Date</value>
</data>
<data name="Microsoft_plugin_timedate_DateAndTime" xml:space="preserve">
<value>Date and time</value>
</data>
<data name="Microsoft_plugin_timedate_DateAndTimeUtc" xml:space="preserve">
<value>Date and time UTC</value>
<comment>'UTC' means here 'Universal Time Convention'</comment>
</data>
<data name="Microsoft_plugin_timedate_Day" xml:space="preserve">
<value>Day (Week day)</value>
</data>
<data name="Microsoft_plugin_timedate_DayMonth" xml:space="preserve">
<value>Month and day</value>
</data>
<data name="Microsoft_plugin_timedate_DayOfMonth" xml:space="preserve">
<value>Day of the month</value>
</data>
<data name="Microsoft_plugin_timedate_DayOfWeek" xml:space="preserve">
<value>Day of the week (Week day)</value>
</data>
<data name="Microsoft_plugin_timedate_DayOfYear" xml:space="preserve">
<value>Day of the year</value>
</data>
<data name="Microsoft_plugin_timedate_Era" xml:space="preserve">
<value>Era</value>
</data>
<data name="Microsoft_plugin_timedate_EraAbbreviation" xml:space="preserve">
<value>Era abbreviation</value>
</data>
<data name="Microsoft_plugin_timedate_ErrorResultSubTitle" xml:space="preserve">
<value>Valid prefixes: 'u' for Unix Timestamp, 'ft' for Windows file time</value>
</data>
<data name="Microsoft_plugin_timedate_ErrorResultTitle" xml:space="preserve">
<value>Error: Invalid number input</value>
</data>
<data name="Microsoft_plugin_timedate_Hour" xml:space="preserve">
<value>Hour</value>
</data>
<data name="Microsoft_plugin_timedate_Iso8601" xml:space="preserve">
<value>ISO 8601</value>
</data>
<data name="Microsoft_plugin_timedate_Iso8601Utc" xml:space="preserve">
<value>ISO 8601 UTC</value>
<comment>'UTC' means here 'Universal Time Convention'</comment>
</data>
<data name="Microsoft_plugin_timedate_Iso8601Zone" xml:space="preserve">
<value>ISO 8601 with time zone</value>
</data>
<data name="Microsoft_plugin_timedate_Iso8601ZoneUtc" xml:space="preserve">
<value>ISO 8601 UTC with time zone</value>
<comment>'UTC' means here 'Universal Time Convention'</comment>
</data>
<data name="Microsoft_plugin_timedate_Millisecond" xml:space="preserve">
<value>Millisecond</value>
</data>
<data name="Microsoft_plugin_timedate_Minute" xml:space="preserve">
<value>Minute</value>
</data>
<data name="Microsoft_plugin_timedate_Month" xml:space="preserve">
<value>Month</value>
</data>
<data name="Microsoft_plugin_timedate_MonthOfYear" xml:space="preserve">
<value>Month of the year</value>
</data>
<data name="Microsoft_plugin_timedate_MonthYear" xml:space="preserve">
<value>Month and year</value>
</data>
<data name="Microsoft_plugin_timedate_Now" xml:space="preserve">
<value>Now</value>
</data>
<data name="Microsoft_plugin_timedate_NowUtc" xml:space="preserve">
<value>Now UTC</value>
<comment>'UTC' means here 'Universal Time Convention'</comment>
</data>
<data name="Microsoft_plugin_timedate_plugin_description" xml:space="preserve">
<value>Provides time and date values for the system time or a custom time stamp (Examples: 'day', 'day::{0}', 'time::{1}', 'calendar week::{0}')</value>
<comment>The character sequence '::' is a fixed delimiter in plugin code. Do not translate the placeholders '{0}' and '{1}' because it will be replaced in code.</comment>
</data>
<data name="Microsoft_plugin_timedate_plugin_name" xml:space="preserve">
<value>Time and Date</value>
</data>
<data name="Microsoft_plugin_timedate_Rfc1123" xml:space="preserve">
<value>RFC1123</value>
</data>
<data name="Microsoft_plugin_timedate_SearchTagDate" xml:space="preserve">
<value>Date</value>
<comment>Don't change order</comment>
</data>
<data name="Microsoft_plugin_timedate_SearchTagDateNow" xml:space="preserve">
<value>Current date; Now</value>
<comment>Don't change order</comment>
</data>
<data name="Microsoft_plugin_timedate_SearchTagEra" xml:space="preserve">
<value>Year; Calendar era; Date</value>
<comment>Don't change order</comment>
</data>
<data name="Microsoft_plugin_timedate_SearchTagEraNow" xml:space="preserve">
<value>Current year; Current calendar era; Current date; Now</value>
<comment>Don't change order</comment>
</data>
<data name="Microsoft_plugin_timedate_SearchTagFormat" xml:space="preserve">
<value>Date and time; Time and Date</value>
<comment>Don't change order</comment>
</data>
<data name="Microsoft_plugin_timedate_SearchTagFormatNow" xml:space="preserve">
<value>Current date and time; Current time and date; Now</value>
<comment>Don't change order</comment>
</data>
<data name="Microsoft_plugin_timedate_SearchTagTime" xml:space="preserve">
<value>Time</value>
<comment>Don't change order</comment>
</data>
<data name="Microsoft_plugin_timedate_SearchTagTimeNow" xml:space="preserve">
<value>Current Time; Now</value>
<comment>Don't change order</comment>
</data>
<data name="Microsoft_plugin_timedate_Search_ConjunctionList" xml:space="preserve">
<value>for; and; nor; but; or; so</value>
<comment>List of conjunctions. We don't add 'yet' because this can be a synonym of 'now' which might be problematic on localized searches.</comment>
</data>
<data name="Microsoft_plugin_timedate_Second" xml:space="preserve">
<value>Second</value>
</data>
<data name="Microsoft_plugin_timedate_SettingDateWithWeekday" xml:space="preserve">
<value>Show date with weekday and name of month (Applies to 'Date' and 'Now' result)</value>
</data>
<data name="Microsoft_plugin_timedate_SettingHideNumberMessageOnGlobalQuery" xml:space="preserve">
<value>Hide 'Invalid number input' error message on global queries</value>
</data>
<data name="Microsoft_plugin_timedate_SettingOnlyDateTimeNowGlobal" xml:space="preserve">
<value>Show only 'Time', 'Date' and 'Now' result on global queries
(Regardless of this setting, on global queries the first search word has to be a full match.)</value>
</data>
<data name="Microsoft_plugin_timedate_SettingTimeWithSeconds" xml:space="preserve">
<value>Show time with seconds (Applies to 'Time' and 'Now' result)</value>
</data>
<data name="Microsoft_plugin_timedate_SubTitleNote" xml:space="preserve">
<value>Select or press Ctrl+C to copy</value>
<comment>'Ctrl+C' is a shortcut</comment>
</data>
<data name="Microsoft_plugin_timedate_Time" xml:space="preserve">
<value>Time</value>
</data>
<data name="Microsoft_plugin_timedate_TimeUtc" xml:space="preserve">
<value>Time UTC</value>
<comment>'UTC' means here 'Universal Time Convention'</comment>
</data>
<data name="Microsoft_plugin_timedate_ToolTipAlternativeSearchTag" xml:space="preserve">
<value>Alternative search tags:</value>
</data>
<data name="Microsoft_plugin_timedate_UniversalTime" xml:space="preserve">
<value>Universal time format: YYYY-MM-DD hh:mm:ss</value>
</data>
<data name="Microsoft_plugin_timedate_Unix" xml:space="preserve">
<value>Unix epoch time</value>
</data>
<data name="Microsoft_plugin_timedate_WeekOfMonth" xml:space="preserve">
<value>Week of the month</value>
</data>
<data name="Microsoft_plugin_timedate_WeekOfYear" xml:space="preserve">
<value>Week of the year (Calendar week, Week number)</value>
</data>
<data name="Microsoft_plugin_timedate_WindowsFileTime" xml:space="preserve">
<value>Windows file time (Int64 number)</value>
</data>
<data name="Microsoft_plugin_timedate_Year" xml:space="preserve">
<value>Year</value>
</data>
</root>

View File

@ -0,0 +1,13 @@
{
"ID": "5D69806A5A474115821C3E4C56B9C793",
"ActionKeyword": "(",
"IsGlobal": true,
"Name": "Time and date",
"Author": "htcfreek",
"Version": "1.0.0",
"Language": "csharp",
"Website": "https://aka.ms/powertoys",
"ExecuteFileName": "Microsoft.PowerToys.Run.Plugin.TimeDate.dll",
"IcoPathDark": "Images\\timeDate.dark.png",
"IcoPathLight": "Images\\timeDate.light.png"
}

View File

@ -10,6 +10,7 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.Plugin.Program.UnitTests")]
[assembly: InternalsVisibleTo("Microsoft.PowerToys.Run.Plugin.System.UnitTests")]
[assembly: InternalsVisibleTo("Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests")]
namespace Wox.Infrastructure
{