[PT Run][New Plugin] Time zone plugin (#11431)

* Initial commit - simple idea for a time zone plugin

* Translations, better search results, copy to clipboard, cleanup

* fix typo

* Add shortcut search and prepare JSON for later usage

* Fix typo

* Use timezone Data only from JSON

* Exclude json file from spell checker

* fix wrong dst

* Improved results (title, subtitle, tooltip) and fix namespace/class problem

* Always show full offset (-##:## and +##:##)

* Add and show timezone names (first pass)

* Fix typos

* fix build

* JSON: fix wrong minus sign and put extra country info the end

* Improved Subtitle for many matched countries and allow full offset search (+ and -)

* Allow more than one names for time zones and remove leftover

* Add military time zone names, and fix name result

* Only use one JSON entry for one time zone

* Use TimeSpan for offset, use build-in calculation for time in time zone

* add descriptions for JSON schema

* Fix typos

* Split out names in separate properties

* Add many time names, time zone names and shortcuts

* Add additional options and most code documentation

* Fix unreadable TimeSpans in JSON and rename helper class

* Fix not allowed commas in JSON file

* Cut to long time and time zone names in title

* Fix missing results for names and offsets

* Better result and show only one result when offset are identical (respect daylight saving time)

* Show generic name fot time zones without names

* Typo fixes

* Fix not working serach by shortcuts

* Fix german resx file -> english resx file

* Translate all names and countires

* Fix not working context menu

* Typo fixes, fix wrong shortcut in names, comments, few better variable names

* New symbols - thx to niels9001

* Search by shortcuts for time names

* update schema

* Add more time zone names and shortcuts (second pass), make spell checker happy

* Reduce matching checks

* Show shortcuts in tool-tips, avoid string converting

* Show only names that match the query

* Make all translatable (Part 1)

* Make all translatable (part 2 of 2)

* XML Doc

* Fix plugin name (type)

* Fix Typos

* Add TimeZone Plugint to WXS

* Add TimeZone plugin to sign pipeline

* Add Documentation

* Remove double spell entries

* Remove TODO leftovers

* Fix for results with no countries

* Fix typos

* fix typos

* Fix broken siolution after rebase

* Update target framework to make build happy

* fix wrong guid count in WXS

* fix wrong output folder (setup wasn’t found files)

* Address feedback from @jsoref - fix spell check

* typo fix - one leftover in expect.txt

* Switch to .NET6 and update dokumentation

* Address feedbacks, and fix search bug

* fix installer build error

* fix spellchecker

* Address feedback from @htcfreek

Co-authored-by: Sekan, Tobias <tobias.sekan@axp-consulting.de>
This commit is contained in:
Tobias Sekan
2022-02-23 14:26:48 +00:00
committed by GitHub
parent 8edfb8fe80
commit 84e142631e
23 changed files with 12484 additions and 11 deletions

View File

@@ -49,3 +49,5 @@ ignore$
^\.github/ ^\.github/
^\.github/actions/spell-check/ ^\.github/actions/spell-check/
^\.gitmodules$ ^\.gitmodules$
(?:^|/)WindowsSettings\.json$
(?:^|/)timezones\.json$

View File

@@ -34,6 +34,8 @@ AFX
AGGREGATABLE AGGREGATABLE
AHybrid AHybrid
Aissue Aissue
Akrotiri
Aktobe
ALarger ALarger
alekhyareddy alekhyareddy
alignas alignas
@@ -50,6 +52,7 @@ Amicrosoft
AModifier AModifier
AMPROPERTY AMPROPERTY
AMPROPSETID AMPROPSETID
Andreanof
anges anges
angularsen angularsen
ansicolor ansicolor
@@ -83,6 +86,7 @@ appx
appxmanifest appxmanifest
APSTUDIO APSTUDIO
AQS AQS
Aqtobe
arcosh arcosh
ARemapped ARemapped
argb argb
@@ -98,6 +102,7 @@ ARRAYSIZE
arsinh arsinh
artanh artanh
Artboard Artboard
Artsakh
asdf asdf
AShortcut AShortcut
ASingle ASingle
@@ -106,6 +111,7 @@ aspnet
ASSOCCHANGED ASSOCCHANGED
ASYNCWINDOWPLACEMENT ASYNCWINDOWPLACEMENT
ASYNCWINDOWPOS ASYNCWINDOWPOS
Atikokan
atl atl
atlbase atlbase
atlcom atlcom
@@ -115,6 +121,7 @@ atlstr
atop atop
Attribs Attribs
attrs attrs
Atyrau
aumid aumid
Aut Aut
Authenticode Authenticode
@@ -127,6 +134,7 @@ AUTOMATIONPROPERTIES
Autorun Autorun
AUTOUPDATE AUTOUPDATE
AValid AValid
Avanc
Awaitable Awaitable
awakeness awakeness
awakeversion awakeversion
@@ -136,13 +144,17 @@ AZCLI
azurecr azurecr
backend backend
backtracer backtracer
BADD
BAEC BAEC
BAF BAF
bak bak
Bashkortostan
Bayan
bbwe bbwe
bck bck
Bcl Bcl
BEEE BEEE
Belarus
betadele betadele
betsegaw betsegaw
BFB BFB
@@ -201,8 +213,11 @@ BUILDARCH
buildcommand buildcommand
buildtools buildtools
buildtransitive buildtransitive
Burkina
Buryatia
BValue BValue
bytearray bytearray
Caiguna
CALG CALG
callbackptr callbackptr
cameligo cameligo
@@ -235,12 +250,14 @@ ChaseKnowlden
chdir chdir
CHILDACTIVATE CHILDACTIVATE
CHILDWINDOW CHILDWINDOW
Choibalsan
chrdavis chrdavis
chromaticities chromaticities
chrono chrono
Chrzan Chrzan
chrzan
CHT CHT
Chukotka
Chuuk
cielab cielab
ciexyz ciexyz
CImage CImage
@@ -258,11 +275,11 @@ CLIENTEDGE
CLIENTPULL CLIENTPULL
clientside clientside
CLIPCHILDREN CLIPCHILDREN
Clipperton
CLIPSIBLINGS CLIPSIBLINGS
cljs cljs
clrcall clrcall
Cls Cls
cls
CLSCTX CLSCTX
clsid clsid
Clusion Clusion
@@ -278,6 +295,7 @@ CMONITORS
cmp cmp
cmyk cmyk
cnt cnt
Cocklebiddy
coclass coclass
codebase codebase
codecvt codecvt
@@ -302,6 +320,7 @@ comhost
cominterop cominterop
commandline commandline
commctrl commctrl
Comoros
companding companding
Compat Compat
COMPOSITIONFULL COMPOSITIONFULL
@@ -369,6 +388,7 @@ CTRLALTDEL
Ctrls Ctrls
Ctx Ctx
CUI CUI
Cunha
currentculture currentculture
CURSORINFO CURSORINFO
cursorpos cursorpos
@@ -388,6 +408,7 @@ czf
cziplib cziplib
Dac Dac
dacl dacl
Danmarkshavn
DARKPURPLE DARKPURPLE
DARKTEAL DARKTEAL
DARKYELLOW DARKYELLOW
@@ -572,9 +593,11 @@ ESettings
esize esize
esrp esrp
estdir estdir
Eswatini
etcore etcore
etl etl
etw etw
Eucla
EUQ EUQ
eventlog eventlog
everytime everytime
@@ -609,6 +632,7 @@ fancyzones
FANCYZONESDRAWLAYOUTTEST FANCYZONESDRAWLAYOUTTEST
FANCYZONESEDITOR FANCYZONESEDITOR
Farbraum Farbraum
Faroe
FARPROC FARPROC
FBF FBF
FCCD FCCD
@@ -638,13 +662,13 @@ findfast
findstr findstr
Firefox Firefox
FIXEDFILEINFO FIXEDFILEINFO
FIXME
FLASHZONES FLASHZONES
FLASHZONESONQUICKSWITCH FLASHZONESONQUICKSWITCH
flt flt
flyout flyout
fmtlib fmtlib
FOF FOF
fof
FOFX FOFX
FOLDERID FOLDERID
folderpath folderpath
@@ -663,6 +687,7 @@ fstream
FTYPE FTYPE
func func
Functiondiscoverykeys Functiondiscoverykeys
Futuna
fwlink fwlink
fwrite fwrite
fxcop fxcop
@@ -732,6 +757,7 @@ hdrop
HEB HEB
Heiko Heiko
helptext helptext
Heure
HEVC HEVC
hfile hfile
hglobal hglobal
@@ -774,6 +800,7 @@ hotkeycontrol
hotkeys hotkeys
hotlight hotlight
hotspot hotspot
Hovd
HPAINTBUFFER HPAINTBUFFER
HRAWINPUT HRAWINPUT
hread hread
@@ -1002,6 +1029,7 @@ ith
IThrottled IThrottled
ithumbnail ithumbnail
ITrigger ITrigger
Ittoqqortoormiit
IUI IUI
IUnknown IUnknown
IUri IUri
@@ -1060,14 +1088,23 @@ keystokes
Keystool Keystool
Keytool Keytool
keyup keyup
Khakassia
Khanty
Khovd
KILLFOCUS KILLFOCUS
Kitts
Knowlden Knowlden
Knownfolders Knownfolders
kotlin kotlin
Krai
KSPROPERTY KSPROPERTY
ktm ktm
kts kts
Kwango
Kwilu
Kybd Kybd
Kyrgyzstan
Kyzylorda
LAlt LAlt
Lambson Lambson
lamotile lamotile
@@ -1075,7 +1112,6 @@ langword
Lastdevice Lastdevice
LASTEXITCODE LASTEXITCODE
Laute Laute
laute
laviusmotileng laviusmotileng
LAYOUTRTL LAYOUTRTL
LBUTTON LBUTTON
@@ -1091,14 +1127,17 @@ Ldone
ldx ldx
LEFTSCROLLBAR LEFTSCROLLBAR
lego lego
lemy
len len
LEQ LEQ
LError LError
Lessthan Lessthan
LEVELID LEVELID
LExit LExit
lgii
lhs lhs
lhwnd lhwnd
lia
LIBID LIBID
LIGHTORANGE LIGHTORANGE
LIGHTTURQUOISE LIGHTTURQUOISE
@@ -1168,10 +1207,14 @@ lstrcmpi
lstrlen lstrlen
LTRB LTRB
LTRREADING LTRREADING
Luhansk
LWA LWA
lwin lwin
LZero LZero
lzw lzw
Maarten
Macquarie
Magadan
MAINICON MAINICON
Mainwindow Mainwindow
majortype majortype
@@ -1182,13 +1225,18 @@ MAKEINTRESOURCEW
MAKELPARAM MAKELPARAM
makepri makepri
malloc malloc
Mangere
Mangystau
manifestdependency manifestdependency
Mansi
MAPPEDTOSAMEKEY MAPPEDTOSAMEKEY
MAPTOSAMESHORTCUT MAPTOSAMESHORTCUT
MAPVK MAPVK
Markdig Markdig
Marquesas
martinchrzan martinchrzan
martinmoene martinmoene
Mato
MAXIMIZEBOX MAXIMIZEBOX
MAXSHORTCUTSIZE MAXSHORTCUTSIZE
maxversiontested maxversiontested
@@ -1197,6 +1245,8 @@ MBUTTON
MBUTTONDBLCLK MBUTTONDBLCLK
MBUTTONDOWN MBUTTONDOWN
MBUTTONUP MBUTTONUP
MCDT
MCST
MDICHILD MDICHILD
MDL MDL
mdpreviewhandler mdpreviewhandler
@@ -1236,6 +1286,7 @@ miniz
minlevel minlevel
Miracast Miracast
mirophone mirophone
Mishkeegogamang
mjpg mjpg
mkdir mkdir
mlcfg mlcfg
@@ -1249,6 +1300,8 @@ MODECHANGE
moderncop moderncop
modernwpf modernwpf
modulekey modulekey
Moldova
Mongala
MONITORINFO MONITORINFO
MONITORINFOEX MONITORINFOEX
MONITORINFOEXW MONITORINFOEXW
@@ -1302,23 +1355,27 @@ MULTIPLEUSE
Multiselect Multiselect
multiset multiset
multizone multizone
Mundrabilla
mutex mutex
mutexes mutexes
muxc muxc
mvvm mvvm
myfile myfile
MYICON MYICON
MYTZ
NAMECHANGE NAMECHANGE
nameof nameof
namespace namespace
namings namings
NATIVEFNTCTL NATIVEFNTCTL
Navassa
NCACTIVATE NCACTIVATE
ncc ncc
NCCALCSIZE NCCALCSIZE
NCCREATE NCCREATE
NCDESTROY NCDESTROY
NCHITTEST NCHITTEST
ncipe
NCLBUTTONDBLCLK NCLBUTTONDBLCLK
NCLBUTTONDOWN NCLBUTTONDOWN
NCLBUTTONUP NCLBUTTONUP
@@ -1333,6 +1390,7 @@ NCRBUTTONDBLCLK
NCRBUTTONDOWN NCRBUTTONDOWN
NCRBUTTONUP NCRBUTTONUP
NDEBUG NDEBUG
Ndombe
ndp ndp
NEEDDISPATCH NEEDDISPATCH
neq neq
@@ -1353,6 +1411,7 @@ newitem
newpath newpath
newrow newrow
Newtonsoft Newtonsoft
nia
niels niels
nielslaute nielslaute
NIF NIF
@@ -1389,6 +1448,7 @@ NOREPEAT
NOREPOSITION NOREPOSITION
NORMALDISPLAY NORMALDISPLAY
NORMALUSER NORMALUSER
Noronha
NOSEARCH NOSEARCH
NOSENDCHANGING NOSENDCHANGING
NOSIZE NOSIZE
@@ -1417,6 +1477,8 @@ nullptr
numberbox numberbox
NUMLOCK NUMLOCK
NUMPAD NUMPAD
Nunavut
Nusa
Nvidia Nvidia
nwc nwc
NWSE NWSE
@@ -1469,6 +1531,7 @@ Overridable
Oversampling Oversampling
OWNDC OWNDC
PACL PACL
pagos
PAINTSTRUCT PAINTSTRUCT
PAIT PAIT
PALEBLUE PALEBLUE
@@ -1491,7 +1554,6 @@ pch
PCIDLIST PCIDLIST
PCWSTR PCWSTR
Pdb Pdb
pdb
pdbonly pdbonly
pdfpreviewhandler pdfpreviewhandler
pdo pdo
@@ -1522,6 +1584,7 @@ pinfo
pinvoke pinvoke
Pipelinhttps Pipelinhttps
pipename pipename
Pitcairn
PKBDLLHOOKSTRUCT PKBDLLHOOKSTRUCT
PKEY PKEY
plib plib
@@ -1533,6 +1596,7 @@ plugin
pluginsmodel pluginsmodel
plx plx
PMSIHANDLE PMSIHANDLE
Pohnpei
policheck policheck
popd popd
popup popup
@@ -1579,6 +1643,7 @@ PREVIOUSVERSIONSINSTALLED
prevpane prevpane
prgms prgms
pri pri
Primorsky
PRINTCLIENT PRINTCLIENT
printf printf
prm prm
@@ -1633,6 +1698,7 @@ qit
QITAB QITAB
QITABENT QITABENT
qps qps
quateur
Queryable Queryable
QUERYENDSESSION QUERYENDSESSION
QUERYOPEN QUERYOPEN
@@ -1746,6 +1812,7 @@ robmensching
robocopy robocopy
Roboto Roboto
roslyn roslyn
Rothera
royvou royvou
Rpc Rpc
RRF RRF
@@ -1873,6 +1940,7 @@ signtool
SINGLEKEY SINGLEKEY
singlekeyremapcontrol singlekeyremapcontrol
singletones singletones
Sint
sipolicy sipolicy
SIZEBOX SIZEBOX
sizeg sizeg
@@ -1918,6 +1986,7 @@ sqlite
SRCCOPY SRCCOPY
Srch Srch
sre sre
Srednekolymsk
sregex sregex
SResize SResize
SRGB SRGB
@@ -1987,7 +2056,9 @@ subkey
SUBLANG SUBLANG
subquery subquery
substr substr
Sul
Superbar Superbar
Suri
sut sut
SVE SVE
SVGIn SVGIn
@@ -2024,7 +2095,7 @@ SYSTEMAPPS
SYSTEMTIME SYSTEMTIME
systemverilog systemverilog
Tadele Tadele
tadele Tajikistan
talynone talynone
TApp TApp
TApplication TApplication
@@ -2051,6 +2122,7 @@ telem
tellg tellg
Templated Templated
templatenamespace templatenamespace
Tenggara
testcase testcase
testhost testhost
testprocess testprocess
@@ -2069,6 +2141,8 @@ timediff
Timeline Timeline
timeunion timeunion
timeutil timeutil
timezone
timezones
Titlecase Titlecase
TLayout TLayout
tlb tlb
@@ -2081,7 +2155,6 @@ Toolchain
toolkitcontrols toolkitcontrols
toolkitconverters toolkitconverters
Toolset Toolset
toolset
toolstrip toolstrip
toolwindow toolwindow
TOPDOWNDIB TOPDOWNDIB
@@ -2095,14 +2168,18 @@ tracelogging
trackpad trackpad
traies traies
transcoded transcoded
Transnistria
transparrent transparrent
TRAYMOUSEMESSAGE TRAYMOUSEMESSAGE
triaging triaging
TRK TRK
trl trl
trunc trunc
Tshuapa
tspan tspan
TStr TStr
Tuva
TValue
TYMED TYMED
typedef typedef
TYPEKEY TYPEKEY
@@ -2118,6 +2195,7 @@ UAC
UAL UAL
uap uap
udit udit
Udmurtia
Udp Udp
uefi uefi
UHash UHash
@@ -2128,6 +2206,7 @@ uintptr
UIPI UIPI
UIs UIs
UITo UITo
Ulaanbaatar
ULARGE ULARGE
ULLONG ULLONG
ulong ulong
@@ -2169,6 +2248,7 @@ Uptool
upvote upvote
uri uri
URLs URLs
Urville
Usb Usb
USEDEFAULT USEDEFAULT
USEFILEATTRIBUTES USEFILEATTRIBUTES
@@ -2184,6 +2264,7 @@ Utc
utf utf
utils utils
uuidof uuidof
Uvs
uwp uwp
UWPUI UWPUI
uxtheme uxtheme
@@ -2402,6 +2483,7 @@ XResource
xsi xsi
XStr XStr
XVIRTUALSCREEN XVIRTUALSCREEN
Yamalia
YIncrement YIncrement
yinwang yinwang
YOffset YOffset
@@ -2412,6 +2494,7 @@ YUYV
YVIRTUALSCREEN YVIRTUALSCREEN
YVU YVU
YVYU YVYU
Zabaykalsky
ZEROINIT ZEROINIT
ZIndex ZIndex
zonable zonable
@@ -2420,4 +2503,5 @@ ZONEHIGHLIGHTCOLOR
zoneset zoneset
ZONESETCHANGE ZONESETCHANGE
Zoneszonabletester Zoneszonabletester
zzz Zonev
zzz

View File

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

View File

@@ -397,6 +397,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests-StlThumbnailProvi
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonacoPreviewHandler", "src\modules\previewpane\MonacoPreviewHandler\MonacoPreviewHandler.csproj", "{04B193D7-3E21-46B8-A958-89B63A8A69DE}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonacoPreviewHandler", "src\modules\previewpane\MonacoPreviewHandler\MonacoPreviewHandler.csproj", "{04B193D7-3E21-46B8-A958-89B63A8A69DE}"
EndProject 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
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64 Debug|x64 = Debug|x64
@@ -1080,6 +1082,15 @@ Global
{04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x64.Build.0 = Release|x64 {04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x64.Build.0 = Release|x64
{04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x86.ActiveCfg = Release|x64 {04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x86.ActiveCfg = Release|x64
{04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x86.Build.0 = Release|x64 {04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x86.Build.0 = Release|x64
{04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x86.ActiveCfg = Release|Any CPU
{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
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -1211,6 +1222,7 @@ Global
{F7C8C0F1-5431-4347-89D0-8E5354F93CF2} = {2F305555-C296-497E-AC20-5FA1B237996A} {F7C8C0F1-5431-4347-89D0-8E5354F93CF2} = {2F305555-C296-497E-AC20-5FA1B237996A}
{F1F6B6B6-9F18-4A17-8B5C-97DF552C53DC} = {2F305555-C296-497E-AC20-5FA1B237996A} {F1F6B6B6-9F18-4A17-8B5C-97DF552C53DC} = {2F305555-C296-497E-AC20-5FA1B237996A}
{04B193D7-3E21-46B8-A958-89B63A8A69DE} = {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}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

View File

@@ -0,0 +1,150 @@
# Time Zone Plugin
The Time Zone plugin allows users to search a time zone.
## Special functions (differ from the regular functions)
* Search for a country, like Kamchatka, Prince Edward Island, France
* Search for a shortcuts, like WEST, UTC, PST
* Search for a offset, like -12:00, -7, 5, 9:30
* Search for a military time zone name (must activate in plugin settings)
## How to add a new time zone or change one
All time zones are located in `TimeZone.json` in root folder of the project.
The `TimeZone.json` use a JSON schema file that make it easier to edit it.
| Key | Optional | Value type |
| ------------------- | -------- | ----------------- |
| `Offset` | **No** | String |
| `Name` | Yes | String |
| `MilitaryName` | Yes | String |
| `Shortcut` | Yes | String |
| `TimeNamesStandard` | Yes | List with strings |
| `TimeNamesDaylight` | Yes | List with strings |
| `ShortcutsStandard` | Yes | List with strings |
| `ShortcutsDaylight` | Yes | List with strings |
| `CountriesStandard` | Yes | List with strings |
| `CountriesDaylight` | Yes | List with strings |
A minimum entry for the `TimeZone.json` looks like:
```json
{
"Offset": "11:55",
"Name": "My crazy time zone",
}
```
A full entry for the `TimeZone.json` looks like:
```json
{
"Offset": "11:55",
"Name": "My crazy time zone",
"Shortcut" : "MYTZ",
"MilitaryName" : "Order Time Zone",
"TimeNamesStandard": [
"My crazy standard time"
],
"ShortcutsStandard": [
"MCST"
],
"TimeNamesDaylight": [
"My crazy daylight time"
],
"ShortcutsDaylight": [
"MCDT"
],
"CountriesStandard": [
"Crazy Land East"
],
"CountriesDaylight": [
"Crazy Land West"
]
}
```
### Remarks
* At minimum one of the optional value should be filled.
## Scores
* Scores are not used
## Important for developers
### General
* The assembly name is cached into `_assemblyName` (to avoid to many calls of `Assembly.GetExecutingAssembly()`)
## Microsoft.PowerToys.Run.Plugin.TimeZone project
### Important plugin values (meta-data)
| Name | Value |
| --------------- | ---------------------------------------------------- |
| ActionKeyword | `&` |
| ExecuteFileName | `Microsoft.PowerToys.Run.Plugin.TimeZone.dll` |
| ID | `BADD1B06EF0A4B61AD95395F24241D69` |
### Interfaces used by this plugin
The plugin use only these interfaces (all inside the `Main.cs`):
* `Wox.Plugin.IPlugin`
* `Wox.Plugin.IContextMenu`
* `Wox.Plugin.IPluginI18n`
* `Wox.Plugin.ISettingProvider`
* `IDisposable`
### Program files
| File | Content |
| -------------------------------------- | ----------------------------------------------------------------------- |
| `Classes\TimeZoneProperties.cs` | A class that represent one time zone |
| `Classes\TimeZones.cs` | A wrapper class that only contains a list with time zones (see 1) |
| `Classes\TimeZoneSettings.cs` | A class that contains all settings for the Time Zone plugin |
| `Extensions\StringBuilderExtension.cs` | Extension methods for `StringBuilder` Objects |
| `Helper\ContextMenuHelper.cs` | All functions to build the context menu (for each result entry) |
| `Helper\JsonHelper.cs` | All functions to load the time zones from a JSON file |
| `Helper\ResultHelper.cs` | All functions to convert internal results into WOX results |
| `Helper\TranslationHelper.cs` | All functions to translate the result in the surface language |
| `Images\timeZone.dark.png` | Symbol for the results for the dark theme |
| `Images\timeZone.light.png` | Symbol for the results for the light theme |
| `Properties\Resources.Designer.resx` | File that contain all translatable keys |
| `Properties\Resources.resx` | File that contain all translatable strings in the neutral language |
| `GlobalSuppressions.cs` | Code suppressions (no real file, linked via *.csproj) |
| `Main.cs` | Main class, the only place that implement the WOX interfaces |
| `plugin.json` | All meta-data for this plugin |
| `StyleCop.json` | Code style (no real file, linked via *.csproj) |
| `timezones.json` | File that contains all time zone information |
| `timeZones.schema.json` | JSON schema for `timezones.json` |
| `StyleCop.json` | Code style (no real file, linked via *.csproj) |
1. We need this extra wrapper class to make it possible that the JSON file can have and use a JSON schema file.
Because the JSON file must have a object as root type, instead of a array.
### Important project values (*.csproj)
| Name | Value |
| --------------- | ------------------------------------------------------------- |
| TargetFramework | `net6.0-windows` |
| Platforms | `x64` |
| Output | `..\..\..\..\..\x64\Debug\modules\launcher\Plugins\TimeZone\` |
| RootNamespace | `Microsoft.PowerToys.Run.Plugin.TimeZone` |
| AssemblyName | `Microsoft.PowerToys.Run.Plugin.TimeZone` |
### Project dependencies
#### Packages
| Package | Version |
| ------------------------------------------------------------------------------------- | ------- |
| [`StyleCop.Analyzers`](https://github.com/DotNetAnalyzers/StyleCopAnalyzers) | 1.1.118 |
#### Projects
* `Wox.Infrastructure`
* `Wox.Plugin`

View File

@@ -86,6 +86,8 @@
<?define SystemComponentFiles=plugin.json;Microsoft.PowerToys.Run.Plugin.System.deps.json;Microsoft.PowerToys.Run.Plugin.System.dll?> <?define SystemComponentFiles=plugin.json;Microsoft.PowerToys.Run.Plugin.System.deps.json;Microsoft.PowerToys.Run.Plugin.System.dll?>
<?define TimeZoneComponentFiles=plugin.json;Microsoft.PowerToys.Run.Plugin.TimeZone.deps.json;Microsoft.PowerToys.Run.Plugin.TimeZone.dll;PowerToys.ManagedTelemetry.dll?>
<?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 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 WinSetCmpFiles=plugin.json;Microsoft.PowerToys.Run.Plugin.WindowsSettings.deps.json;Microsoft.PowerToys.Run.Plugin.WindowsSettings.dll;PowerToys.ManagedTelemetry.dll?> <?define WinSetCmpFiles=plugin.json;Microsoft.PowerToys.Run.Plugin.WindowsSettings.deps.json;Microsoft.PowerToys.Run.Plugin.WindowsSettings.dll;PowerToys.ManagedTelemetry.dll?>
@@ -477,6 +479,10 @@
<Directory Id="SystemPluginFolder" Name="System"> <Directory Id="SystemPluginFolder" Name="System">
<Directory Id="SystemImagesFolder" Name="Images" /> <Directory Id="SystemImagesFolder" Name="Images" />
</Directory> </Directory>
<Directory Id="TimeZonePluginFolder" Name="TimeZone">
<Directory Id="TimeZoneImagesFolder" Name="Images" />
<Directory Id="TimeZoneLanguagesFolder" Name="Languages" />
</Directory>
<Directory Id="WindowsSettingsPluginFolder" Name="WindowsSettings"> <Directory Id="WindowsSettingsPluginFolder" Name="WindowsSettings">
<Directory Id="WindowsSettingsImagesFolder" Name="Images" /> <Directory Id="WindowsSettingsImagesFolder" Name="Images" />
<Directory Id="WindowsSettingsLanguagesFolder" Name="Languages" /> <Directory Id="WindowsSettingsLanguagesFolder" Name="Languages" />
@@ -1085,7 +1091,7 @@
<Fragment> <Fragment>
<!-- Resource directories should be added only if the installer is built on the build farm --> <!-- Resource directories should be added only if the installer is built on the build farm -->
<?ifdef env.IsPipeline?> <?ifdef env.IsPipeline?>
<?foreach ParentDirectory in LauncherInstallFolder;FancyZonesInstallFolder;ImageResizerInstallFolder;ColorPickerInstallFolder;FileExplorerPreviewInstallFolder;CalculatorPluginFolder;FolderPluginFolder;ProgramPluginFolder;ShellPluginFolder;IndexerPluginFolder;UnitConverterPluginFolder;UriPluginFolder;WindowWalkerPluginFolder;RegistryPluginFolder;VSCodeWorkspacesPluginFolder;ServicePluginFolder;SystemPluginFolder;WindowsSettingsPluginFolder;WindowsTerminalPluginFolder;WebSearchPluginFolder?> <?foreach ParentDirectory in LauncherInstallFolder;FancyZonesInstallFolder;ImageResizerInstallFolder;ColorPickerInstallFolder;FileExplorerPreviewInstallFolder;CalculatorPluginFolder;FolderPluginFolder;ProgramPluginFolder;ShellPluginFolder;IndexerPluginFolder;UnitConverterPluginFolder;UriPluginFolder;WindowWalkerPluginFolder;RegistryPluginFolder;VSCodeWorkspacesPluginFolder;ServicePluginFolder;SystemPluginFolder;TimeZonePluginFolder;WindowsSettingsPluginFolder;WindowsTerminalPluginFolder;WebSearchPluginFolder?>
<DirectoryRef Id="$(var.ParentDirectory)"> <DirectoryRef Id="$(var.ParentDirectory)">
<!-- Resource file directories --> <!-- Resource file directories -->
<?foreach Language in $(var.LocLanguageList)?> <?foreach Language in $(var.LocLanguageList)?>
@@ -1335,10 +1341,16 @@
Directory="Resource$(var.IdSafeLanguage)WindowsSettingsPluginFolder"> Directory="Resource$(var.IdSafeLanguage)WindowsSettingsPluginFolder">
<File Id="Launcher_WindowsSettings_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\launcher\Plugins\WindowsSettings\$(var.Language)\Microsoft.PowerToys.Run.Plugin.WindowsSettings.resources.dll" /> <File Id="Launcher_WindowsSettings_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\launcher\Plugins\WindowsSettings\$(var.Language)\Microsoft.PowerToys.Run.Plugin.WindowsSettings.resources.dll" />
</Component> </Component>
<Component
Id="Launcher_TimeZone_$(var.IdSafeLanguage)_Component"
Guid="$(var.CompGUIDPrefix)14"
Directory="Resource$(var.IdSafeLanguage)TimeZonePluginFolder">
<File Id="Launcher_TimeZone_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\launcher\Plugins\TimeZone\$(var.Language)\Microsoft.PowerToys.Run.Plugin.TimeZone.resources.dll" />
</Component>
<!-- Uncomment after Plugin receives the localization files. <!-- Uncomment after Plugin receives the localization files.
<Component <Component
Id="Launcher_WindowsTerminal_$(var.IdSafeLanguage)_Component" Id="Launcher_WindowsTerminal_$(var.IdSafeLanguage)_Component"
Guid="$(var.CompGUIDPrefix)14" Guid="$(var.CompGUIDPrefix)15"
Directory="Resource$(var.IdSafeLanguage)WindowsTerminalFolder"> Directory="Resource$(var.IdSafeLanguage)WindowsTerminalFolder">
<File Id="Launcher_WindowsTerminal_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\launcher\Plugins\WindowsTerminal\$(var.Language)\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.resources.dll" /> <File Id="Launcher_WindowsTerminal_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\launcher\Plugins\WindowsTerminal\$(var.Language)\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.resources.dll" />
</Component> </Component>
@@ -1549,7 +1561,20 @@
<File Id="SystemImagesComponentFile_$(var.File)" Source="$(var.BinX64Dir)modules\launcher\Plugins\System\Images\$(var.File)" /> <File Id="SystemImagesComponentFile_$(var.File)" Source="$(var.BinX64Dir)modules\launcher\Plugins\System\Images\$(var.File)" />
</Component> </Component>
<?endforeach?> <?endforeach?>
<!-- TimeZone Plugin -->
<?foreach File in $(var.TimeZoneComponentFiles)?>
<Component Id="TimeZoneComponent_$(var.File)" Win64="yes" Directory="TimeZonePluginFolder">
<File Id="TimeZoneComponentFile_$(var.File)" Source="$(var.BinX64Dir)modules\launcher\Plugins\TimeZone\$(var.File)" />
</Component>
<?endforeach?>
<Component Id="TimeZoneImagesComponentLight" Directory="TimeZoneImagesFolder" >
<File Id="TimeZoneLightIcon" Source="$(var.BinX64Dir)modules\launcher\Plugins\TimeZone\Images\timeZone.light.png" />
</Component>
<Component Id="TimeZoneImagesComponentDark" Directory="TimeZoneImagesFolder" >
<File Id="TimeZoneDarkIcon" Source="$(var.BinX64Dir)modules\launcher\Plugins\TimeZone\Images\timeZone.dark.png" />
</Component>
<!-- WindowsSettings Plugin --> <!-- WindowsSettings Plugin -->
<?foreach File in $(var.WinSetCmpFiles)?> <?foreach File in $(var.WinSetCmpFiles)?>
<Component Id="WinSetCmp_$(var.File)" Win64="yes" Directory="WindowsSettingsPluginFolder"> <Component Id="WinSetCmp_$(var.File)" Win64="yes" Directory="WindowsSettingsPluginFolder">

View File

@@ -0,0 +1,32 @@
// 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.Text.Json;
namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Classes
{
/// <summary>
/// A class that contains all time zones.
/// </summary>
public sealed class TimeZoneList
{
/// <summary>
/// Initializes a new instance of the <see cref="TimeZoneList"/> class with empty properties.
/// </summary>
/// <remarks>
/// The standard constructor is need by the <see cref="JsonSerializer.Deserialize{TValue}(string, JsonSerializerOptions?)"/>-Method.
/// </remarks>
public TimeZoneList()
{
TimeZones = Enumerable.Empty<TimeZoneProperties>();
}
/// <summary>
/// Gets or sets a list with all time zones.
/// </summary>
public IEnumerable<TimeZoneProperties> TimeZones { get; set; }
}
}

View File

@@ -0,0 +1,96 @@
// 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.Json;
namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Classes
{
/// <summary>
/// A time zone
/// </summary>
public sealed class TimeZoneProperties
{
/// <summary>
/// Initializes a new instance of the <see cref="TimeZoneProperties"/> class with empty properties.
/// </summary>
/// <remarks>
/// The standard constructor is need by the <see cref="JsonSerializer.Deserialize{TValue}(string, JsonSerializerOptions?)"/>-Method.
/// </remarks>
public TimeZoneProperties()
{
Offset = "00:00";
Name = string.Empty;
MilitaryName = string.Empty;
Shortcut = string.Empty;
TimeNamesStandard = Enumerable.Empty<string>();
TimeNamesDaylight = Enumerable.Empty<string>();
CountriesStandard = Enumerable.Empty<string>();
CountriesDaylight = Enumerable.Empty<string>();
ShortcutsStandard = Enumerable.Empty<string>();
ShortcutsDaylight = Enumerable.Empty<string>();
}
/// <summary>
/// Gets or sets the time offset of this time zone (the gap from the UTC time zone)
/// </summary>
public string Offset { get; set; }
/// <summary>
/// Gets or sets the name of this time zone.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets the military name of this time zone.
/// </summary>
public string MilitaryName { get; set; }
/// <summary>
/// Gets or sets the shortcuts of the name this time zone.
/// </summary>
public string Shortcut { get; set; }
/// <summary>
/// Gets or sets a list with names for the standard time.
/// </summary>
public IEnumerable<string> TimeNamesStandard { get; set; }
/// <summary>
/// Gets or sets a list with names for the daylight saving time.
/// </summary>
public IEnumerable<string> TimeNamesDaylight { get; set; }
/// <summary>
/// Gets or sets a list with all countries in this time zone that don't use a daylight saving time.
/// </summary>
public IEnumerable<string> CountriesStandard { get; set; }
/// <summary>
/// Gets or sets a list with all countries in this time zone that use a daylight saving time.
/// </summary>
public IEnumerable<string> CountriesDaylight { get; set; }
/// <summary>
/// Gets or sets a list with shortcuts for the names for the standard time.
/// </summary>
public IEnumerable<string> ShortcutsStandard { get; set; }
/// <summary>
/// Gets or sets a list with shortcuts for the names for the daylight saving time.
/// </summary>
public IEnumerable<string> ShortcutsDaylight { get; set; }
/// <summary>
/// Gets a compatible <see cref="TimeSpan"/> of the <see cref="Offset"/>.
/// </summary>
internal TimeSpan OffsetAsTimeSpan
{
get { return TimeSpan.TryParse(Offset, out var result) ? result : new TimeSpan(0, 0, 0); }
}
}
}

View File

@@ -0,0 +1,97 @@
// 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 Microsoft.PowerToys.Run.Plugin.TimeZone.Properties;
using Microsoft.PowerToys.Settings.UI.Library;
namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Classes
{
/// <summary>
/// Additional settings for the time zone plugin.
/// </summary>
internal sealed class TimeZoneSettings
{
/// <summary>
/// Gets or sets a value indicating whether the time zone name of a time zone is shown in the results.
/// </summary>
internal bool ShowTimeZoneNames { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the time name of a time zone is shown in the results.
/// </summary>
internal bool ShowTimeNames { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the military name of a time zone is shown in the results.
/// </summary>
internal bool ShowMilitaryTimeZoneNames { get; set; }
/// <summary>
/// Return a list with all settings. Additional
/// </summary>
/// <returns>A list with all settings.</returns>
internal static List<PluginAdditionalOption> GetAdditionalOptions()
{
var optionList = new List<PluginAdditionalOption>
{
new PluginAdditionalOption
{
Key = "ShowTimeZoneNames",
DisplayLabel = Resources.ShowTimeZoneNames,
Value = true,
},
new PluginAdditionalOption
{
Key = "ShowTimeNames",
DisplayLabel = Resources.ShowTimeNames,
Value = true,
},
new PluginAdditionalOption
{
Key = "ShowMilitaryTimeZoneNames",
DisplayLabel = Resources.ShowMilitaryTimeZoneNames,
Value = false,
},
};
return optionList;
}
/// <summary>
/// Update this settings.
/// </summary>
/// <param name="settings">The settings for all power launcher plugin.</param>
internal void UpdateSettings(PowerLauncherPluginSettings settings)
{
if (settings is null || settings.AdditionalOptions is null)
{
return;
}
ShowTimeZoneNames = GetSettingOrDefault(settings, "ShowTimeZoneNames");
ShowTimeNames = GetSettingOrDefault(settings, "ShowTimeNames");
ShowMilitaryTimeZoneNames = GetSettingOrDefault(settings, "ShowMilitaryTimeZoneNames");
}
/// <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);
// As a fallback if a setting isn't available, we use the value defined in the method GetAdditionalOptions()
var settingsValue = option?.Value
?? GetAdditionalOptions().FirstOrDefault(x => x.Key == name)?.Value
?? default;
return settingsValue;
}
}
}

View File

@@ -0,0 +1,50 @@
// 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.Text;
namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Extensions
{
/// <summary>
/// Extensions for <see cref="StringBuilder"/>-Objects
/// </summary>
internal static class StringBuilderExtensions
{
/// <summary>
/// Save append the given <see cref="string"/> value with the given maximum length to the <see cref="StringBuilder"/>
/// </summary>
/// <param name="stringBuilder">The <see cref="StringBuilder"/> to append the string.</param>
/// <param name="value">The value that should be append.</param>
/// <param name="maxLength">The max length of the <see cref="string"/> value that should append.</param>
internal static void SaveAppend(this StringBuilder stringBuilder, string value, int maxLength)
{
if (value.Length > maxLength)
{
stringBuilder.Append(value, 0, maxLength);
}
else
{
stringBuilder.Append(value);
}
}
/// <summary>
/// Cut too long texts to the given length and add three dots at the end of the text.
/// </summary>
/// <param name="stringBuilder">The <see cref="StringBuilder"/> that contain the text.</param>
/// <param name="maxLength">The maximum length for the text, inclusive the three dots.</param>
internal static void CutTooLong(this StringBuilder stringBuilder, int maxLength)
{
if (stringBuilder.Length <= maxLength)
{
return;
}
stringBuilder.Length = maxLength - 3;
stringBuilder.Append('.');
stringBuilder.Append('.');
stringBuilder.Append('.');
}
}
}

View File

@@ -0,0 +1,77 @@
// 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.Windows;
using System.Windows.Input;
using System.Windows.Media.Animation;
using Microsoft.PowerToys.Run.Plugin.TimeZone.Classes;
using Microsoft.PowerToys.Run.Plugin.TimeZone.Properties;
using Wox.Plugin;
using Wox.Plugin.Logger;
namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Helper
{
/// <summary>
/// Helper class to easier work with context menu entries
/// </summary>
internal static class ContextMenuHelper
{
/// <summary>
/// Return a list with all context menu entries for the given <see cref="Result"/>
/// <para>Symbols taken from <see href="https://docs.microsoft.com/en-us/windows/uwp/design/style/segoe-ui-symbol-font"/></para>
/// </summary>
/// <param name="result">The result for the context menu entires</param>
/// <param name="assemblyName">The name of the this assembly</param>
/// <returns>A list with context menu entries</returns>
internal static List<ContextMenuResult> GetContextMenu(in Result result, in string assemblyName)
{
if (!(result?.ContextData is DateTime dateTime))
{
return new List<ContextMenuResult>(0);
}
var list = new List<ContextMenuResult>
{
new ContextMenuResult
{
AcceleratorKey = Key.C,
AcceleratorModifiers = ModifierKeys.Control,
Action = _ => TryToCopyToClipBoard($"{dateTime:HH:mm:ss}"),
FontFamily = "Segoe MDL2 Assets",
Glyph = "\xE8C8", // E8C8 => Symbol: Copy
PluginName = assemblyName,
Title = $"{Resources.CopyTime} (Ctrl+C)",
},
};
return list;
}
#pragma warning disable CA1031 // Do not catch general exception types
/// <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>
private static bool TryToCopyToClipBoard(in string text)
{
try
{
Clipboard.Clear();
Clipboard.SetText(text);
return true;
}
catch (Exception exception)
{
Log.Exception("Can't copy to clipboard", exception, typeof(Main));
return false;
}
}
#pragma warning restore CA1031 // Do not catch general exception types
}
}

View File

@@ -0,0 +1,62 @@
// 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.IO;
using System.Reflection;
using System.Text.Json;
using Microsoft.PowerToys.Run.Plugin.TimeZone.Classes;
using Wox.Plugin.Logger;
namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Helper
{
/// <summary>
/// Helper class to easier work with the JSON files.
/// </summary>
internal static class JsonHelper
{
/// <summary>
/// The name of the file that contains all time zones.
/// </summary>
private const string _settingsFile = "timeZones.json";
/// <summary>
/// Read all possible time zones.
/// </summary>
/// <returns>A object that contain a list with time zones.</returns>
internal static TimeZoneList ReadAllPossibleTimeZones()
{
var assembly = Assembly.GetExecutingAssembly();
var type = Array.Find(assembly.GetTypes(), x => x.Name == nameof(Main));
TimeZoneList? settings = null;
try
{
var resourceName = $"{type?.Namespace}.{_settingsFile}";
using var stream = assembly.GetManifestResourceStream(resourceName);
if (stream is null)
{
throw new Exception("stream is null");
}
using var reader = new StreamReader(stream);
var text = reader.ReadToEnd();
settings = JsonSerializer.Deserialize<TimeZoneList>(text);
}
catch (JsonException exception)
{
Log.Exception("Error loading settings JSON file", exception, typeof(JsonHelper));
}
catch (Exception exception)
{
Log.Exception("Error loading settings JSON file", exception, typeof(JsonHelper));
throw;
}
return settings ?? new TimeZoneList();
}
}
}

View File

@@ -0,0 +1,848 @@
// 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.Linq;
using System.Text;
using Microsoft.PowerToys.Run.Plugin.TimeZone.Classes;
using Microsoft.PowerToys.Run.Plugin.TimeZone.Extensions;
using Microsoft.PowerToys.Run.Plugin.TimeZone.Properties;
using Mono.Collections.Generic;
using Wox.Plugin;
namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Helper
{
/// <summary>
/// Helper class to easier work with results
/// </summary>
internal static class ResultHelper
{
/// <summary>
/// Return a list of <see cref="Result"/>s based on the given <see cref="Query"/>.
/// </summary>
/// <param name="timeZones">A list with all possible time zones.</param>
/// <param name="options">Additional options to limit the results.</param>
/// <param name="query">The <see cref="Query"/> to filter the <see cref="Results"/>.</param>
/// <param name="iconPath">The path to the icon that is used for each result.</param>
/// <returns>A list with <see cref="Result"/>s.</returns>
internal static IEnumerable<Result> GetResults(in IEnumerable<TimeZoneProperties> timeZones, in TimeZoneSettings options, in Query query, in string iconPath)
{
var results = new List<Result>();
var dateTime = DateTime.UtcNow;
foreach (var timeZone in timeZones)
{
if (MatchTimeZoneShortcut(timeZone, query)
|| MatchStandardTimeShortcuts(timeZone, query)
|| MatchDaylightTimeShortcuts(timeZone, query)
|| MatchTimeZoneNames(timeZone, query)
|| MatchStandardTimeNames(timeZone, query)
|| MatchDaylightTimeNames(timeZone, query)
|| MatchStandardCountries(timeZone, query)
|| MatchDaylightCountries(timeZone, query)
|| MatchOffset(timeZone, query))
{
results.AddRange(GetResults(timeZone, options, query, iconPath, dateTime));
}
}
var orderResults = results.OrderBy(result => result.Title);
return orderResults;
}
/// <summary>
/// Return a list with <see cref="Result"/> based on the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain the information for the <see cref="Result"/>.</param>
/// <param name="options">Additional options to limit the results.</param>
/// <param name="query">The <see cref="Query"/> that should match.</param>
/// <param name="iconPath">The path to the icon that is used for each result.</param>
/// <param name="dateTime">The current time in UTC for the <see cref="Result"/>.</param>
/// <returns>A list with <see cref="Result"/>.</returns>
private static IEnumerable<Result> GetResults(in TimeZoneProperties timeZoneProperties, in TimeZoneSettings options, in Query query, in string iconPath, in DateTime dateTime)
{
var results = new Collection<Result>();
var standardTitle = GetTitle(timeZoneProperties, options, query, dateTime, false);
var daylightTitle = GetTitle(timeZoneProperties, options, query, dateTime, true);
if (standardTitle.Equals(daylightTitle))
{
results.Add(new Result
{
ContextData = GetTimeInTimeZone(timeZoneProperties, dateTime, false),
IcoPath = iconPath,
Title = standardTitle.ToString(),
SubTitle = GetAllCountries(timeZoneProperties, query, maxLength: 100).ToString(),
ToolTipData = new ToolTipData(standardTitle.ToString(), GetAllToolTip(timeZoneProperties, options).ToString()),
});
return results;
}
if (!MatchTimeZoneShortcut(timeZoneProperties, query)
&& !MatchTimeZoneNames(timeZoneProperties, query)
&& !MatchOffset(timeZoneProperties, query))
{
return results;
}
if (MatchStandardTimeShortcuts(timeZoneProperties, query)
|| MatchStandardTimeNames(timeZoneProperties, query)
|| MatchStandardCountries(timeZoneProperties, query))
{
var hasCountries = GetStandardCountries(timeZoneProperties, null, int.MaxValue).Length > 0;
if (!hasCountries)
{
return results;
}
results.Add(new Result
{
ContextData = GetTimeInTimeZone(timeZoneProperties, dateTime, false),
IcoPath = iconPath,
SubTitle = GetStandardCountries(timeZoneProperties, query, maxLength: 100).ToString(),
Title = standardTitle.ToString(),
ToolTipData = new ToolTipData(standardTitle.ToString(), GetStandardToolTip(timeZoneProperties, options).ToString()),
});
}
if (MatchDaylightTimeShortcuts(timeZoneProperties, query)
|| MatchDaylightTimeNames(timeZoneProperties, query)
|| MatchDaylightCountries(timeZoneProperties, query))
{
var hasCountries = GetDaylightCountries(timeZoneProperties, null, int.MaxValue).Length > 0;
if (!hasCountries)
{
return results;
}
results.Add(new Result
{
ContextData = GetTimeInTimeZone(timeZoneProperties, dateTime, true),
IcoPath = iconPath,
SubTitle = GetDaylightCountries(timeZoneProperties, query, maxLength: 100).ToString(),
Title = daylightTitle.ToString(),
ToolTipData = new ToolTipData(daylightTitle.ToString(), GetDaylightToolTip(timeZoneProperties, options).ToString()),
});
}
return results;
}
/// <summary>
/// Return the current local time of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain all information.</param>
/// <param name="dateTime">The current time in UTC.</param>
/// <param name="daylightSavingTime">indicate that the result is for a time zone that use a daylight saving time.</param>
/// <returns>The current local time in a time zone.</returns>
private static DateTime GetTimeInTimeZone(in TimeZoneProperties timeZoneProperties, in DateTime dateTime, in bool daylightSavingTime)
{
foreach (var timeZoneInfo in TimeZoneInfo.GetSystemTimeZones())
{
if (timeZoneInfo.BaseUtcOffset == timeZoneProperties.OffsetAsTimeSpan
&& timeZoneInfo.SupportsDaylightSavingTime == daylightSavingTime)
{
return TimeZoneInfo.ConvertTime(dateTime, timeZoneInfo);
}
}
// Fall-back
var result = dateTime + timeZoneProperties.OffsetAsTimeSpan;
return result;
}
/// <summary>
/// Return the title for the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain all information.</param>
/// <param name="timeZoneSettings">Additional options to limit the results.</param>
/// <param name="query">The <see cref="Query"/> that should match.</param>
/// <param name="dateTime">The current time in UTC.</param>
/// <param name="daylightSavingTime">indicate that the result is for a time zone that use a daylight saving time.</param>
/// <returns>A title for a time zone.</returns>
private static StringBuilder GetTitle(in TimeZoneProperties timeZoneProperties, in TimeZoneSettings timeZoneSettings, in Query query, in DateTime dateTime, in bool daylightSavingTime)
{
var stringBuilder = new StringBuilder();
var timeInZoneTime = GetTimeInTimeZone(timeZoneProperties, dateTime, daylightSavingTime);
var timeZoneNames = GetNames(timeZoneProperties, timeZoneSettings, query, maxLength: 50);
stringBuilder.AppendFormat(CultureInfo.CurrentCulture, "{0:HH:mm:ss}", timeInZoneTime);
stringBuilder.Append(' ');
stringBuilder.Append('-');
stringBuilder.Append(' ');
stringBuilder.Append(timeZoneNames);
return stringBuilder;
}
/// <summary>
/// Return a tool-tip for the given time zone with countries that use the standard time.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain all information.</param>
/// <param name="timeZoneSettings">Additional options to limit the results.</param>
/// <returns>A tool-tip with countries that use the standard time.</returns>
private static StringBuilder GetStandardToolTip(in TimeZoneProperties timeZoneProperties, in TimeZoneSettings timeZoneSettings)
{
var countries = GetStandardCountries(timeZoneProperties, null, maxLength: int.MaxValue);
var names = GetNames(timeZoneProperties, timeZoneSettings, null, maxLength: int.MaxValue);
var shortcuts = GetStandardShortcuts(timeZoneProperties);
if (!string.IsNullOrWhiteSpace(timeZoneProperties.Shortcut))
{
shortcuts.Append(',');
shortcuts.Append(' ');
shortcuts.Append(timeZoneProperties.Shortcut);
}
var stringBuilder = new StringBuilder();
stringBuilder.Append(Resources.Offset).Append(':').Append(' ').AppendLine(timeZoneProperties.Offset);
stringBuilder.Append(Resources.UseDst).Append(':').Append(' ').AppendLine(Resources.No);
stringBuilder.AppendLine(string.Empty);
stringBuilder.Append(Resources.Names).Append(':').Append(' ').Append(names);
stringBuilder.AppendLine(string.Empty);
stringBuilder.AppendLine(string.Empty);
stringBuilder.Append(Resources.Shortcuts).Append(':').Append(' ').Append(shortcuts);
stringBuilder.AppendLine(string.Empty);
stringBuilder.AppendLine(string.Empty);
stringBuilder.Append(Resources.Countries).Append(':').Append(' ').Append(countries);
return stringBuilder;
}
/// <summary>
/// Return a tool-tip for the given time zone with countries that use the daylight saving time.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain all information.</param>
/// <param name="timeZoneSettings">Additional options to limit the type of the names.</param>
/// <returns>A tool-tip with countries that use the daylight saving time.</returns>
private static StringBuilder GetDaylightToolTip(in TimeZoneProperties timeZoneProperties, in TimeZoneSettings timeZoneSettings)
{
var dstCountries = GetDaylightCountries(timeZoneProperties, null, maxLength: int.MaxValue);
var names = GetNames(timeZoneProperties, timeZoneSettings, null, maxLength: int.MaxValue);
var shortcuts = GetDaylightShortcuts(timeZoneProperties);
if (!string.IsNullOrWhiteSpace(timeZoneProperties.Shortcut))
{
shortcuts.Append(',');
shortcuts.Append(' ');
shortcuts.Append(timeZoneProperties.Shortcut);
}
var stringBuilder = new StringBuilder();
stringBuilder.Append(Resources.Offset).Append(':').Append(' ').AppendLine(timeZoneProperties.Offset);
stringBuilder.Append(Resources.UseDst).Append(':').Append(' ').AppendLine(Resources.Yes);
stringBuilder.AppendLine(string.Empty);
stringBuilder.Append(Resources.Names).Append(':').Append(' ').Append(names);
stringBuilder.AppendLine(string.Empty);
stringBuilder.AppendLine(string.Empty);
stringBuilder.Append(Resources.Shortcuts).Append(':').Append(' ').Append(shortcuts);
stringBuilder.AppendLine(string.Empty);
stringBuilder.AppendLine(string.Empty);
stringBuilder.Append(Resources.CountriesWithDst).Append(':').Append(' ').Append(dstCountries);
return stringBuilder;
}
/// <summary>
/// Return a tool-tip for the given time zone with countries.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain all information.</param>
/// <param name="timeZoneSettings">Additional options to limit the type of the names.</param>
/// <returns>A tool-tip with countries.</returns>
private static StringBuilder GetAllToolTip(in TimeZoneProperties timeZoneProperties, in TimeZoneSettings timeZoneSettings)
{
var countries = GetStandardCountries(timeZoneProperties, null, maxLength: int.MaxValue);
var dstCountries = GetDaylightCountries(timeZoneProperties, null, maxLength: int.MaxValue);
var names = GetNames(timeZoneProperties, timeZoneSettings, null, maxLength: int.MaxValue);
var shortcuts = GetStandardShortcuts(timeZoneProperties);
var dstShortcuts = GetDaylightShortcuts(timeZoneProperties);
if (dstShortcuts.Length > 0)
{
shortcuts.Append(',');
shortcuts.Append(' ');
shortcuts.Append(dstShortcuts);
}
if (!string.IsNullOrWhiteSpace(timeZoneProperties.Shortcut))
{
shortcuts.Append(',');
shortcuts.Append(' ');
shortcuts.Append(timeZoneProperties.Shortcut);
}
var stringBuilder = new StringBuilder();
stringBuilder.Append(Resources.Offset).Append(':').Append(' ').AppendLine(timeZoneProperties.Offset);
stringBuilder.Append(Resources.UseDst).Append(':').Append(' ').AppendLine(Resources.Yes);
stringBuilder.AppendLine(string.Empty);
stringBuilder.Append(Resources.Names).Append(':').Append(' ').Append(names);
stringBuilder.AppendLine(string.Empty);
stringBuilder.AppendLine(string.Empty);
stringBuilder.Append(Resources.Shortcuts).Append(':').Append(' ').Append(shortcuts);
stringBuilder.AppendLine(string.Empty);
stringBuilder.AppendLine(string.Empty);
stringBuilder.Append(Resources.Countries).Append(':').Append(' ').Append(countries);
stringBuilder.AppendLine(string.Empty);
stringBuilder.AppendLine(string.Empty);
stringBuilder.Append(Resources.CountriesWithDst).Append(':').Append(' ').Append(dstCountries);
return stringBuilder;
}
/// <summary>
/// Return all names of the given time zone that match the given query.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain a hand of names.</param>
/// <param name="timeZoneSettings">Additional options to limit the type of the names.</param>
/// <param name="query">The query that should match.</param>
/// <param name="maxLength">The maximum length of the result.</param>
/// <returns>All know names of the given time zone.</returns>
private static StringBuilder GetNames(in TimeZoneProperties timeZoneProperties, in TimeZoneSettings timeZoneSettings, Query? query, in int maxLength)
{
var allNames = new List<string>();
if (!string.IsNullOrWhiteSpace(timeZoneProperties.Name) && timeZoneSettings.ShowTimeZoneNames)
{
allNames.Add(timeZoneProperties.Name);
}
if (!string.IsNullOrWhiteSpace(timeZoneProperties.MilitaryName) && timeZoneSettings.ShowMilitaryTimeZoneNames)
{
allNames.Add(timeZoneProperties.MilitaryName);
}
if (timeZoneProperties.TimeNamesStandard != null && timeZoneSettings.ShowTimeZoneNames)
{
allNames.AddRange(timeZoneProperties.TimeNamesStandard);
}
if (timeZoneProperties.TimeNamesDaylight != null && timeZoneSettings.ShowTimeZoneNames)
{
allNames.AddRange(timeZoneProperties.TimeNamesDaylight);
}
IEnumerable<string> names;
if (query is null || string.IsNullOrWhiteSpace(query.Search))
{
names = allNames;
}
else
{
names = allNames.Where(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase));
}
var stringBuilder = new StringBuilder();
if (names.Any())
{
var lastEntry = names.LastOrDefault();
foreach (var name in names)
{
stringBuilder.Append(name);
if (name != lastEntry)
{
stringBuilder.Append(',');
stringBuilder.Append(' ');
}
}
// To many names (first pass) => use shortcuts
if (stringBuilder.Length > maxLength)
{
stringBuilder.Replace(Resources.TimeZone, Resources.TimeZoneShortcut);
stringBuilder.Replace(Resources.StandardTime, Resources.StandardTimeShortcut);
stringBuilder.Replace(Resources.DaylightTime, Resources.DaylightTimeShortcut);
stringBuilder.Replace(Resources.Time, Resources.TimeShortcut);
}
// To many names (second pass) => cut name length
if (stringBuilder.Length > maxLength)
{
foreach (var country in names)
{
stringBuilder.SaveAppend(country, maxLength: 5);
if (country != lastEntry)
{
stringBuilder.Append(',');
stringBuilder.Append(' ');
}
}
}
stringBuilder.CutTooLong(maxLength);
}
else
{
stringBuilder.Append("UTC");
var totalMinutes = timeZoneProperties.OffsetAsTimeSpan.TotalMinutes;
if (totalMinutes < 0)
{
stringBuilder.Append(timeZoneProperties.Offset);
}
else if (totalMinutes > 0)
{
stringBuilder.Append('+');
stringBuilder.Append(timeZoneProperties.Offset);
}
else
{
stringBuilder.Append("±00:00");
}
}
return stringBuilder;
}
/// <summary>
/// Return all standard time names shortcuts of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain a hand of names.</param>
/// <returns>All standard time names shortcuts of the given time zone.</returns>
private static StringBuilder GetStandardShortcuts(in TimeZoneProperties timeZoneProperties)
{
var stringBuilder = new StringBuilder();
var lastEntry = timeZoneProperties.ShortcutsStandard.LastOrDefault();
foreach (var name in timeZoneProperties.ShortcutsStandard)
{
stringBuilder.Append(name);
if (name != lastEntry)
{
stringBuilder.Append(',');
stringBuilder.Append(' ');
}
}
return stringBuilder;
}
/// <summary>
/// Return all know daylight time names shortcuts of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain a hand of names.</param>
/// <returns>All know daylight time names shortcuts of the given time zone.</returns>
private static StringBuilder GetDaylightShortcuts(in TimeZoneProperties timeZoneProperties)
{
var stringBuilder = new StringBuilder();
var lastEntry = timeZoneProperties.ShortcutsDaylight.LastOrDefault();
foreach (var name in timeZoneProperties.ShortcutsDaylight)
{
stringBuilder.Append(name);
if (name != lastEntry)
{
stringBuilder.Append(',');
stringBuilder.Append(' ');
}
}
return stringBuilder;
}
/// <summary>
/// Return all countries that use the standard time of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain the countries.</param>
/// <param name="query">The <see cref="Query"/> that should match a country that use standard time.</param>
/// <param name="maxLength">The maximum length of the result.</param>
/// <returns>All countries that use the standard time of the given time zone.</returns>
private static StringBuilder GetStandardCountries(in TimeZoneProperties timeZoneProperties, Query? query, in int maxLength)
{
IEnumerable<string> countries;
if (query is null || string.IsNullOrWhiteSpace(query.Search))
{
countries = timeZoneProperties.CountriesStandard;
}
else
{
countries = timeZoneProperties.CountriesStandard.Where(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase));
}
var stringBuilder = new StringBuilder();
var lastEntry = countries.LastOrDefault();
foreach (var country in countries)
{
stringBuilder.Append(country);
if (country != lastEntry)
{
stringBuilder.Append(',');
stringBuilder.Append(' ');
}
}
// To many countries (first pass) => remove extra info
if (stringBuilder.Length > maxLength)
{
stringBuilder.Clear();
foreach (var country in countries)
{
var extraInfoStart = country.IndexOf('(', StringComparison.InvariantCultureIgnoreCase);
if (extraInfoStart > 0)
{
stringBuilder.Append(country[..extraInfoStart]);
}
else
{
stringBuilder.Append(country);
}
if (country != lastEntry)
{
stringBuilder.Append(',');
stringBuilder.Append(' ');
}
}
}
// To many countries (second pass) => remove extra info and cut country length
if (stringBuilder.Length > maxLength)
{
foreach (var country in countries)
{
var extraInfoStart = country.IndexOf('(', StringComparison.InvariantCultureIgnoreCase);
if (extraInfoStart > 0)
{
stringBuilder.SaveAppend(country[..extraInfoStart], maxLength: 5);
}
else
{
stringBuilder.SaveAppend(country, maxLength: 5);
}
if (country != lastEntry)
{
stringBuilder.Append(',');
stringBuilder.Append(' ');
}
}
}
stringBuilder.CutTooLong(maxLength);
return stringBuilder;
}
/// <summary>
/// Return all countries that use the daylight saving time of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain the countries.</param>
/// <param name="query">The <see cref="Query"/> that should match a country that use daylight time.</param>
/// <param name="maxLength">The maximum length of the result.</param>
/// <returns>All countries that use the daylight saving time of the given time zone.</returns>
private static StringBuilder GetDaylightCountries(in TimeZoneProperties timeZoneProperties, Query? query, in int maxLength)
{
IEnumerable<string> countries;
if (query is null || string.IsNullOrWhiteSpace(query.Search))
{
countries = timeZoneProperties.CountriesDaylight;
}
else
{
countries = timeZoneProperties.CountriesDaylight.Where(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase));
}
var stringBuilder = new StringBuilder();
var lastEntry = countries.LastOrDefault();
foreach (var country in countries)
{
stringBuilder.Append(country);
if (country != lastEntry)
{
stringBuilder.Append(',');
stringBuilder.Append(' ');
}
}
// To many countries (first pass) => remove extra info
if (stringBuilder.Length > maxLength)
{
stringBuilder.Clear();
foreach (var country in countries)
{
var extraInfoStart = country.IndexOf('(', StringComparison.InvariantCultureIgnoreCase);
if (extraInfoStart > 0)
{
stringBuilder.Append(country[..extraInfoStart]);
}
else
{
stringBuilder.Append(country);
}
if (country != lastEntry)
{
stringBuilder.Append(',');
stringBuilder.Append(' ');
}
}
}
// To many countries (second pass) => remove extra info and cut country length
if (stringBuilder.Length > maxLength)
{
foreach (var country in countries)
{
var extraInfoStart = country.IndexOf('(', StringComparison.InvariantCultureIgnoreCase);
if (extraInfoStart > 0)
{
stringBuilder.SaveAppend(country[..extraInfoStart], maxLength: 5);
}
else
{
stringBuilder.SaveAppend(country, maxLength: 5);
}
if (country != lastEntry)
{
stringBuilder.Append(',');
stringBuilder.Append(' ');
}
}
}
stringBuilder.CutTooLong(maxLength);
return stringBuilder;
}
/// <summary>
/// Return all countries of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain the countries.</param>
/// <param name="query">The <see cref="Query"/> that should match a country that use standard or daylight time.</param>
/// <param name="maxLength">The maximum length of the result.</param>
/// <returns>All countries of the given time zone.</returns>
private static StringBuilder GetAllCountries(in TimeZoneProperties timeZoneProperties, Query? query, in int maxLength)
{
IEnumerable<string> countries;
if (query is null || string.IsNullOrWhiteSpace(query.Search))
{
countries = timeZoneProperties.CountriesDaylight.Concat(timeZoneProperties.CountriesStandard);
}
else
{
countries = timeZoneProperties.CountriesDaylight.Where(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase))
.Concat(timeZoneProperties.CountriesStandard.Where(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)));
}
var stringBuilder = new StringBuilder();
var lastEntry = countries.LastOrDefault();
foreach (var country in countries)
{
stringBuilder.Append(country);
if (country != lastEntry)
{
stringBuilder.Append(',');
stringBuilder.Append(' ');
}
}
// To many countries (first pass) => remove extra info
if (stringBuilder.Length > maxLength)
{
stringBuilder.Clear();
foreach (var country in countries)
{
var extraInfoStart = country.IndexOf('(', StringComparison.InvariantCultureIgnoreCase);
if (extraInfoStart > 0)
{
stringBuilder.Append(country[..extraInfoStart]);
}
else
{
stringBuilder.Append(country);
}
if (country != lastEntry)
{
stringBuilder.Append(',');
stringBuilder.Append(' ');
}
}
}
// To many countries (second pass) => remove extra info and cut country length
if (stringBuilder.Length > maxLength)
{
foreach (var country in countries)
{
var extraInfoStart = country.IndexOf('(', StringComparison.InvariantCultureIgnoreCase);
if (extraInfoStart > 0)
{
stringBuilder.SaveAppend(country[..extraInfoStart], maxLength: 5);
}
else
{
stringBuilder.SaveAppend(country, maxLength: 5);
}
if (country != lastEntry)
{
stringBuilder.Append(',');
stringBuilder.Append(' ');
}
}
}
stringBuilder.CutTooLong(maxLength);
return stringBuilder;
}
/// <summary>
/// Indicate that the given query match the time zone shortcut of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone to check.</param>
/// <param name="query">The query that should match.</param>
/// <returns><see langword="true"/>if the query match, otherwise <see langword="false"/>.</returns>
private static bool MatchTimeZoneShortcut(in TimeZoneProperties timeZoneProperties, Query query)
{
var result = timeZoneProperties.Shortcut.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase);
return result;
}
/// <summary>
/// Indicate that the given query match one of the time zone names of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone to check.</param>
/// <param name="query">The query that should match.</param>
/// <returns><see langword="true"/>if the query match, otherwise <see langword="false"/>.</returns>
private static bool MatchTimeZoneNames(in TimeZoneProperties timeZoneProperties, Query query)
{
var result = timeZoneProperties.Name.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)
|| timeZoneProperties.MilitaryName.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase);
return result;
}
/// <summary>
/// Indicate that the given query match the offset of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone to check.</param>
/// <param name="query">The query that should match.</param>
/// <returns><see langword="true"/>if the query match, otherwise <see langword="false"/>.</returns>
private static bool MatchOffset(in TimeZoneProperties timeZoneProperties, Query query)
{
// allow search for "-xx:xx"
if (timeZoneProperties.Offset.StartsWith('-') && query.Search.StartsWith('-'))
{
if (timeZoneProperties.Offset[1..].Contains(query.Search[1..], StringComparison.InvariantCultureIgnoreCase))
{
return true;
}
}
// allow search for "+xx:xx"
if (!timeZoneProperties.Offset.StartsWith('-') && query.Search.StartsWith('+'))
{
if (timeZoneProperties.Offset.Contains(query.Search[1..], StringComparison.InvariantCultureIgnoreCase))
{
return true;
}
}
return false;
}
/// <summary>
/// Indicate that the given query match one of the standard time names of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone to check.</param>
/// <param name="query">The query that should match.</param>
/// <returns><see langword="true"/>if the query match, otherwise <see langword="false"/>.</returns>
private static bool MatchStandardTimeNames(in TimeZoneProperties timeZoneProperties, Query query)
{
var result = timeZoneProperties.TimeNamesDaylight?.Any(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)) == true;
return result;
}
/// <summary>
/// Indicate that the given query match one of the daylight time names of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone to check.</param>
/// <param name="query">The query that should match.</param>
/// <returns><see langword="true"/>if the query match, otherwise <see langword="false"/>.</returns>
private static bool MatchDaylightTimeNames(in TimeZoneProperties timeZoneProperties, Query query)
{
var result = timeZoneProperties.TimeNamesDaylight?.Any(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)) == true;
return result;
}
/// <summary>
/// Indicate that the given query match one of the countries that use the standard time of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone to check.</param>
/// <param name="query">The query that should match.</param>
/// <returns><see langword="true"/>if the query match, otherwise <see langword="false"/>.</returns>
private static bool MatchStandardCountries(in TimeZoneProperties timeZoneProperties, Query query)
{
var result = timeZoneProperties.CountriesStandard?.Any(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)) == true;
return result;
}
/// <summary>
/// Indicate that the given query match one of the countries that use the daylight time of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone to check.</param>
/// <param name="query">The query that should match.</param>
/// <returns><see langword="true"/>if the query match, otherwise <see langword="false"/>.</returns>
private static bool MatchDaylightCountries(in TimeZoneProperties timeZoneProperties, Query query)
{
var result = timeZoneProperties.CountriesDaylight?.Any(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)) == true;
return result;
}
/// <summary>
/// Indicate that the given query match the time zone shortcut of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone to check.</param>
/// <param name="query">The query that should match.</param>
/// <returns><see langword="true"/>if the query match, otherwise <see langword="false"/>.</returns>
private static bool MatchStandardTimeShortcuts(in TimeZoneProperties timeZoneProperties, Query query)
{
var result = timeZoneProperties.ShortcutsStandard?.Any(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)) == true;
return result;
}
/// <summary>
/// Indicate that the given query match the time zone shortcut of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone to check.</param>
/// <param name="query">The query that should match.</param>
/// <returns><see langword="true"/>if the query match, otherwise <see langword="false"/>.</returns>
private static bool MatchDaylightTimeShortcuts(in TimeZoneProperties timeZoneProperties, Query query)
{
var result = timeZoneProperties.ShortcutsDaylight?.Any(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)) == true;
return result;
}
}
}

View File

@@ -0,0 +1,133 @@
// 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.Globalization;
using Microsoft.PowerToys.Run.Plugin.TimeZone.Classes;
using Microsoft.PowerToys.Run.Plugin.TimeZone.Properties;
using Wox.Plugin.Logger;
namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Helper
{
/// <summary>
/// Helper class to easier work with translations.
/// </summary>
internal static class TranslationHelper
{
/// <summary>
/// Translate all names and countries of the <see cref="TimeZoneList"/> class.
/// </summary>
/// <param name="timeZoneList">A class that contain all possible time zones.</param>
internal static void TranslateAllSettings(in TimeZoneList timeZoneList)
{
if (timeZoneList?.TimeZones is null)
{
return;
}
foreach (var timeZone in timeZoneList.TimeZones)
{
// Translate Name
if (!string.IsNullOrWhiteSpace(timeZone.Name))
{
var name = Resources.ResourceManager.GetString(timeZone.Name, CultureInfo.InvariantCulture);
if (string.IsNullOrEmpty(name))
{
Log.Warn($"Resource string for [{timeZone.Name}] not found", typeof(TranslationHelper));
}
timeZone.Name = name ?? timeZone.Name ?? string.Empty;
}
// Translate MilitaryName
if (!string.IsNullOrWhiteSpace(timeZone.MilitaryName))
{
var militaryName = Resources.ResourceManager.GetString(timeZone.MilitaryName, CultureInfo.InvariantCulture);
if (string.IsNullOrEmpty(militaryName))
{
Log.Warn($"Resource string for [{timeZone.MilitaryName}] not found", typeof(TranslationHelper));
}
timeZone.MilitaryName = militaryName ?? timeZone.MilitaryName ?? string.Empty;
}
// Translate TimeNamesDaylight
if (!(timeZone.TimeNamesDaylight is null))
{
var timeNamesDaylight = new List<string>();
foreach (var nameDaylight in timeZone.TimeNamesDaylight)
{
var nameDaylightT = Resources.ResourceManager.GetString(nameDaylight, CultureInfo.InvariantCulture);
if (string.IsNullOrEmpty(nameDaylightT))
{
Log.Warn($"Resource string for [{nameDaylight}] not found", typeof(TranslationHelper));
}
timeNamesDaylight.Add(nameDaylightT ?? nameDaylight ?? string.Empty);
}
timeZone.TimeNamesDaylight = timeNamesDaylight;
}
// Translate TimeNamesStandard
if (!(timeZone.TimeNamesStandard is null))
{
var timeNamesStandard = new List<string>();
foreach (var nameStandard in timeZone.TimeNamesStandard)
{
var nameStandardT = Resources.ResourceManager.GetString(nameStandard, CultureInfo.InvariantCulture);
if (string.IsNullOrEmpty(nameStandardT))
{
Log.Warn($"Resource string for [{nameStandard}] not found", typeof(TranslationHelper));
}
timeNamesStandard.Add(nameStandardT ?? nameStandard ?? string.Empty);
}
timeZone.TimeNamesStandard = timeNamesStandard;
}
// Translate CountriesDaylight
if (!(timeZone.CountriesDaylight is null))
{
var countriesDaylight = new List<string>();
foreach (var countryDaylight in timeZone.CountriesDaylight)
{
var countryDaylightT = Resources.ResourceManager.GetString(countryDaylight, CultureInfo.InvariantCulture);
if (string.IsNullOrEmpty(countryDaylightT))
{
Log.Warn($"Resource string for [{countryDaylight}] not found", typeof(TranslationHelper));
}
countriesDaylight.Add(countryDaylightT ?? countryDaylight ?? string.Empty);
}
timeZone.CountriesDaylight = countriesDaylight;
}
// Translate CountriesStandard
if (!(timeZone.CountriesStandard is null))
{
var countriesStandard = new List<string>();
foreach (var countryStandard in timeZone.CountriesStandard)
{
var countryStandardT = Resources.ResourceManager.GetString(countryStandard, CultureInfo.InvariantCulture);
if (string.IsNullOrEmpty(countryStandardT))
{
Log.Warn($"Resource string for [{countryStandard}] not found", typeof(TranslationHelper));
}
countriesStandard.Add(countryStandardT ?? countryStandard ?? string.Empty);
}
timeZone.CountriesStandard = countriesStandard;
}
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,217 @@
// 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.Reflection;
using System.Windows.Controls;
using ManagedCommon;
using Microsoft.PowerToys.Run.Plugin.TimeZone.Classes;
using Microsoft.PowerToys.Run.Plugin.TimeZone.Helper;
using Microsoft.PowerToys.Run.Plugin.TimeZone.Properties;
using Microsoft.PowerToys.Settings.UI.Library;
using Wox.Plugin;
namespace Microsoft.PowerToys.Run.Plugin.TimeZone
{
/// <summary>
/// A power launcher plugin to search across time zones.
/// </summary>
public class Main : IPlugin, IContextMenu, IPluginI18n, ISettingProvider, IDisposable
{
/// <summary>
/// The name of this assembly
/// </summary>
private readonly string _assemblyName;
/// <summary>
/// The settings for this plugin.
/// </summary>
private readonly TimeZoneSettings _timeZoneSettings;
/// <summary>
/// The initial context for this plugin (contains API and meta-data)
/// </summary>
private PluginInitContext? _context;
/// <summary>
/// The path to the icon for each result
/// </summary>
private string _defaultIconPath;
/// <summary>
/// Indicate that the plugin is disposed
/// </summary>
private bool _disposed;
/// <summary>
/// A class that contain all possible time zones.
/// </summary>
private TimeZoneList? _timeZoneList;
/// <summary>
/// Initializes a new instance of the <see cref="Main"/> class.
/// </summary>
public Main()
{
_assemblyName = Assembly.GetExecutingAssembly().GetName().Name ?? GetTranslatedPluginTitle();
_defaultIconPath = "Images/timeZone.light.png";
_timeZoneSettings = new TimeZoneSettings();
}
/// <summary>
/// Gets the localized name.
/// </summary>
public string Name
{
get { return Resources.PluginTitle; }
}
/// <summary>
/// Gets the localized description.
/// </summary>
public string Description
{
get { return Resources.PluginDescription; }
}
/// <summary>
/// Gets the additional options for this plugin.
/// </summary>
public IEnumerable<PluginAdditionalOption> AdditionalOptions
{
get { return TimeZoneSettings.GetAdditionalOptions(); }
}
/// <summary>
/// Initialize the plugin with the given <see cref="PluginInitContext"/>
/// </summary>
/// <param name="context">The <see cref="PluginInitContext"/> for this plugin</param>
public void Init(PluginInitContext context)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
_context.API.ThemeChanged += OnThemeChanged;
UpdateIconPath(_context.API.GetCurrentTheme());
_timeZoneList = JsonHelper.ReadAllPossibleTimeZones();
TranslationHelper.TranslateAllSettings(_timeZoneList);
}
/// <summary>
/// Return a filtered list, based on the given query
/// </summary>
/// <param name="query">The query to filter the list</param>
/// <returns>A filtered list, can be empty when nothing was found</returns>
public List<Result> Query(Query query)
{
if (_timeZoneList?.TimeZones is null)
{
return new List<Result>(0);
}
if (query is null)
{
return new List<Result>(0);
}
var results = ResultHelper.GetResults(_timeZoneList.TimeZones, _timeZoneSettings, query, _defaultIconPath);
return results.ToList();
}
/// <summary>
/// Return a list context menu entries for a given <see cref="Result"/> (shown at the right side of the result)
/// </summary>
/// <param name="selectedResult">The <see cref="Result"/> for the list with context menu entries</param>
/// <returns>A list context menu entries</returns>
public List<ContextMenuResult> LoadContextMenus(Result selectedResult)
{
return ContextMenuHelper.GetContextMenu(selectedResult, _assemblyName);
}
/// <summary>
/// Change all theme-based elements (typical called when the plugin theme has changed)
/// </summary>
/// <param name="oldtheme">The old <see cref="Theme"/></param>
/// <param name="newTheme">The new <see cref="Theme"/></param>
private void OnThemeChanged(Theme oldtheme, Theme newTheme)
{
UpdateIconPath(newTheme);
}
/// <summary>
/// Update all icons (typical called when the plugin theme has changed)
/// </summary>
/// <param name="theme">The new <see cref="Theme"/> for the icons</param>
private void UpdateIconPath(Theme theme)
{
_defaultIconPath = theme == Theme.Light || theme == Theme.HighContrastWhite
? "Images/timeZone.light.png"
: "Images/timeZone.dark.png";
}
/// <inheritdoc/>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Wrapper method for <see cref="Dispose"/> that dispose additional objects and events form the plugin itself
/// </summary>
/// <param name="disposing">Indicate that the plugin is disposed</param>
protected virtual void Dispose(bool disposing)
{
if (_disposed || !disposing)
{
return;
}
if (!(_context is null))
{
_context.API.ThemeChanged -= OnThemeChanged;
}
_disposed = true;
}
/// <summary>
/// Return the translated plugin title.
/// </summary>
/// <returns>A translated plugin title.</returns>
public string GetTranslatedPluginTitle()
{
return Resources.PluginTitle;
}
/// <summary>
/// Return the translated plugin description.
/// </summary>
/// <returns>A translated plugin description.</returns>
public string GetTranslatedPluginDescription()
{
return Resources.PluginDescription;
}
/// <summary>
/// Return a additional setting panel for this plugin.
/// </summary>
/// <returns>A additional setting panel.</returns>
public Control CreateSettingPanel()
{
throw new NotImplementedException();
}
/// <summary>
/// Update the plugin settings
/// </summary>
/// <param name="settings">The settings for all power launcher plugin.</param>
public void UpdateSettings(PowerLauncherPluginSettings settings)
{
_timeZoneSettings.UpdateSettings(settings);
}
}
}

View File

@@ -0,0 +1,119 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\Version.props" />
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<RootNamespace>Microsoft.PowerToys.Run.Plugin.TimeZone</RootNamespace>
<AssemblyName>Microsoft.PowerToys.Run.Plugin.TimeZone</AssemblyName>
<Version>$(Version).0</Version>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<Platforms>x64</Platforms>
<NeutralLanguage>en-US</NeutralLanguage>
<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\..\..\..\..\x64\Debug\modules\launcher\Plugins\TimeZone\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<WarningLevel>4</WarningLevel>
<Optimize>false</Optimize>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutputPath>..\..\..\..\..\x64\Release\modules\launcher\Plugins\TimeZone\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Languages\**" />
<EmbeddedResource Remove="Languages\**" />
<None Remove="Languages\**" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\..\codeAnalysis\GlobalSuppressions.cs">
<Link>GlobalSuppressions.cs</Link>
</Compile>
<AdditionalFiles Include="..\..\..\..\codeAnalysis\StyleCop.json">
<Link>StyleCop.json</Link>
</AdditionalFiles>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="timeZones.json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Wox.Infrastructure\Wox.Infrastructure.csproj">
<Private>false</Private>
</ProjectReference>
<ProjectReference Include="..\..\Wox.Plugin\Wox.Plugin.csproj">
<Private>false</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<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>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Update="Images\timeZone.light.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Images\timeZone.dark.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,13 @@
{
"ID": "BADD1B06EF0A4B61AD95395F24241D69",
"ActionKeyword": "&",
"IsGlobal": false,
"Name": "Time zone",
"Author": "TobiasSekan",
"Version": "1.0.0",
"Language": "csharp",
"Website": "https://aka.ms/powertoys",
"ExecuteFileName": "Microsoft.PowerToys.Run.Plugin.TimeZone.dll",
"IcoPathDark": "Images\\timeZone.dark.png",
"IcoPathLight": "Images\\timeZone.light.png"
}

View File

@@ -0,0 +1,80 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"TimeZones": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [ "Offset" ],
"properties": {
"Offset": {
"type": "string",
"description": "The time offset of this time zone (the gap from the UTC time zone, must convertible to a C# TimeSpan)"
},
"Name": {
"type": "string",
"description": "The name of this time zone."
},
"MilitaryName": {
"type": "string",
"description": "The military name of this time zone."
},
"Shortcut": {
"type": "string",
"description": "A shortcut for the names this time zone."
},
"TimeNamesStandard": {
"type": "array",
"description": "A list with names for the standard time.",
"uniqueItems": true,
"items": {
"type": "string"
}
},
"TimeNamesDaylight": {
"type": "array",
"description": "A list with names for the daylight saving time.",
"uniqueItems": true,
"items": {
"type": "string"
}
},
"ShortcutsStandard": {
"type": "array",
"description": "A list with shortcuts for the names for the standard time.",
"uniqueItems": true,
"items": {
"type": "string"
}
},
"ShortcutsDaylight": {
"type": "array",
"description": "A list with shortcuts for the names for the daylight saving time.",
"uniqueItems": true,
"items": {
"type": "string"
}
},
"CountriesStandard": {
"type": "array",
"description": "A list with all countries in this time zone that use a standard time.",
"uniqueItems": true,
"items": {
"type": "string"
}
},
"CountriesDaylight": {
"type": "array",
"description": "A list with all countries in this time zone that use a daylight saving time.",
"uniqueItems": true,
"items": {
"type": "string"
}
}
}
}
}
}
}