├── .gitignore ├── LICENSE ├── README.md ├── SETUP.md ├── icon.png ├── img └── icons │ ├── -1.png │ ├── -10.png │ ├── -11.png │ ├── -12.png │ ├── -2.png │ ├── -3.png │ ├── -4.png │ ├── -5.png │ ├── -6.png │ ├── -7.png │ ├── -8.png │ ├── -9.png │ ├── 0.png │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ ├── 9.png │ ├── home.png │ └── utc.png ├── info.plist ├── poetry.lock ├── poetry.toml ├── pyproject.toml ├── screenshots ├── config.png ├── friendly-names.png ├── meet.png ├── now.png └── offset.png ├── scripts ├── build.sh └── release.sh ├── src ├── __init__.py ├── data.py ├── formatters.py ├── helpers.py └── main.py ├── tests └── __init__.py └── tzdata ├── build.py ├── country.csv └── time_zone.csv /.gitignore: -------------------------------------------------------------------------------- 1 | *pyc 2 | .DS_Store 3 | 4 | tmp.py 5 | 6 | prefs.plist 7 | 8 | bin/ 9 | build/ 10 | dist/ 11 | releases/ 12 | 13 | .venv/ 14 | .idea/ 15 | .tmp/ 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Fede Calendino 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ALFRED-WORLD-CLOCK 2 | 3 | [Alfred Workflow](https://www.alfredapp.com/workflows/) to check time in multiple timezones. 🌐️ 4 | 5 | 6 | > timezone data provided by [timezonedb](https://timezonedb.com). 7 | 8 | 9 | #### Configuration: 10 | 11 | ![vars example](screenshots/config.png) 12 | 13 | ##### Friendly names 14 | ![friendly names](screenshots/friendly-names.png) 15 | 16 | 17 | #### Usage: 18 | 19 | ##### Now 20 | ![now](screenshots/now.png) 21 | 22 | ##### Meet 23 | ![meet](screenshots/meet.png) 24 | 25 | ##### Offset 26 | ![offset](screenshots/offset.png) 27 | -------------------------------------------------------------------------------- /SETUP.md: -------------------------------------------------------------------------------- 1 | ## SETUP 2 | 3 | This documentation is for those who want to setup this workflow locally to do some work on it. PRs welcomed! 4 | 5 | 6 | ### poetry 7 | 8 | This workflow uses a tool called [poetry](https://python-poetry.org) for depencency management. 9 | To install poetry in your local enviroment follow [these instructions](https://python-poetry.org/docs/#installation). 10 | 11 | 12 | ### dependencies 13 | 14 | Once you have poetry installed, the next thing is to fetch all of this project's dependencies using the command [poetry install](https://python-poetry.org/docs/cli/#install). 15 | This command will use the `pyproject.toml` file to create a virtual env and install all the necesary libraries from pypi. 16 | 17 | 18 | ### release 19 | 20 | To import the current state of the workflow into Alfred, you can use the script `./scripts/release.sh`. 21 | This will package all the required components to run the workflow in Alfred without any external depencencies. 22 | 23 | At the end, you'll be automatically prompted to import it into Alfred. 24 | 25 | > You can find the generated `.alfredworkflow` files in the `releases/` folder. 26 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/icon.png -------------------------------------------------------------------------------- /img/icons/-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/-1.png -------------------------------------------------------------------------------- /img/icons/-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/-10.png -------------------------------------------------------------------------------- /img/icons/-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/-11.png -------------------------------------------------------------------------------- /img/icons/-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/-12.png -------------------------------------------------------------------------------- /img/icons/-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/-2.png -------------------------------------------------------------------------------- /img/icons/-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/-3.png -------------------------------------------------------------------------------- /img/icons/-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/-4.png -------------------------------------------------------------------------------- /img/icons/-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/-5.png -------------------------------------------------------------------------------- /img/icons/-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/-6.png -------------------------------------------------------------------------------- /img/icons/-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/-7.png -------------------------------------------------------------------------------- /img/icons/-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/-8.png -------------------------------------------------------------------------------- /img/icons/-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/-9.png -------------------------------------------------------------------------------- /img/icons/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/0.png -------------------------------------------------------------------------------- /img/icons/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/1.png -------------------------------------------------------------------------------- /img/icons/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/10.png -------------------------------------------------------------------------------- /img/icons/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/11.png -------------------------------------------------------------------------------- /img/icons/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/12.png -------------------------------------------------------------------------------- /img/icons/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/2.png -------------------------------------------------------------------------------- /img/icons/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/3.png -------------------------------------------------------------------------------- /img/icons/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/4.png -------------------------------------------------------------------------------- /img/icons/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/5.png -------------------------------------------------------------------------------- /img/icons/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/6.png -------------------------------------------------------------------------------- /img/icons/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/7.png -------------------------------------------------------------------------------- /img/icons/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/8.png -------------------------------------------------------------------------------- /img/icons/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/9.png -------------------------------------------------------------------------------- /img/icons/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/home.png -------------------------------------------------------------------------------- /img/icons/utc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/img/icons/utc.png -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "alfred-pyflow" 5 | version = "0.4.6" 6 | description = "Minimal library for the development of Alfred Workflows." 7 | optional = false 8 | python-versions = ">=3.8,<4.0" 9 | files = [ 10 | {file = "alfred_pyflow-0.4.6-py3-none-any.whl", hash = "sha256:8754b203c481c89b9507805fd8e906057081868d6d00cb903f0c239bf05f3daa"}, 11 | {file = "alfred_pyflow-0.4.6.tar.gz", hash = "sha256:b622d6759017a29a041c1c33b52365a03f505406c1d2ae1232975a7ccb2a2f8b"}, 12 | ] 13 | 14 | [package.dependencies] 15 | requests = ">=2.30.0,<3.0.0" 16 | urllib3 = "1.26.6" 17 | 18 | [[package]] 19 | name = "black" 20 | version = "23.11.0" 21 | description = "The uncompromising code formatter." 22 | optional = false 23 | python-versions = ">=3.8" 24 | files = [ 25 | {file = "black-23.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dbea0bb8575c6b6303cc65017b46351dc5953eea5c0a59d7b7e3a2d2f433a911"}, 26 | {file = "black-23.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:412f56bab20ac85927f3a959230331de5614aecda1ede14b373083f62ec24e6f"}, 27 | {file = "black-23.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d136ef5b418c81660ad847efe0e55c58c8208b77a57a28a503a5f345ccf01394"}, 28 | {file = "black-23.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:6c1cac07e64433f646a9a838cdc00c9768b3c362805afc3fce341af0e6a9ae9f"}, 29 | {file = "black-23.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cf57719e581cfd48c4efe28543fea3d139c6b6f1238b3f0102a9c73992cbb479"}, 30 | {file = "black-23.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:698c1e0d5c43354ec5d6f4d914d0d553a9ada56c85415700b81dc90125aac244"}, 31 | {file = "black-23.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:760415ccc20f9e8747084169110ef75d545f3b0932ee21368f63ac0fee86b221"}, 32 | {file = "black-23.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:58e5f4d08a205b11800332920e285bd25e1a75c54953e05502052738fe16b3b5"}, 33 | {file = "black-23.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:45aa1d4675964946e53ab81aeec7a37613c1cb71647b5394779e6efb79d6d187"}, 34 | {file = "black-23.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c44b7211a3a0570cc097e81135faa5f261264f4dfaa22bd5ee2875a4e773bd6"}, 35 | {file = "black-23.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a9acad1451632021ee0d146c8765782a0c3846e0e0ea46659d7c4f89d9b212b"}, 36 | {file = "black-23.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:fc7f6a44d52747e65a02558e1d807c82df1d66ffa80a601862040a43ec2e3142"}, 37 | {file = "black-23.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7f622b6822f02bfaf2a5cd31fdb7cd86fcf33dab6ced5185c35f5db98260b055"}, 38 | {file = "black-23.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:250d7e60f323fcfc8ea6c800d5eba12f7967400eb6c2d21ae85ad31c204fb1f4"}, 39 | {file = "black-23.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5133f5507007ba08d8b7b263c7aa0f931af5ba88a29beacc4b2dc23fcefe9c06"}, 40 | {file = "black-23.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:421f3e44aa67138ab1b9bfbc22ee3780b22fa5b291e4db8ab7eee95200726b07"}, 41 | {file = "black-23.11.0-py3-none-any.whl", hash = "sha256:54caaa703227c6e0c87b76326d0862184729a69b73d3b7305b6288e1d830067e"}, 42 | {file = "black-23.11.0.tar.gz", hash = "sha256:4c68855825ff432d197229846f971bc4d6666ce90492e5b02013bcaca4d9ab05"}, 43 | ] 44 | 45 | [package.dependencies] 46 | click = ">=8.0.0" 47 | mypy-extensions = ">=0.4.3" 48 | packaging = ">=22.0" 49 | pathspec = ">=0.9.0" 50 | platformdirs = ">=2" 51 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 52 | typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} 53 | 54 | [package.extras] 55 | colorama = ["colorama (>=0.4.3)"] 56 | d = ["aiohttp (>=3.7.4)"] 57 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 58 | uvloop = ["uvloop (>=0.15.2)"] 59 | 60 | [[package]] 61 | name = "certifi" 62 | version = "2023.11.17" 63 | description = "Python package for providing Mozilla's CA Bundle." 64 | optional = false 65 | python-versions = ">=3.6" 66 | files = [ 67 | {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, 68 | {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, 69 | ] 70 | 71 | [[package]] 72 | name = "charset-normalizer" 73 | version = "3.3.2" 74 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 75 | optional = false 76 | python-versions = ">=3.7.0" 77 | files = [ 78 | {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, 79 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, 80 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, 81 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, 82 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, 83 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, 84 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, 85 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, 86 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, 87 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, 88 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, 89 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, 90 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, 91 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, 92 | {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, 93 | {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, 94 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, 95 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, 96 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, 97 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, 98 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, 99 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, 100 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, 101 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, 102 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, 103 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, 104 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, 105 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, 106 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, 107 | {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, 108 | {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, 109 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, 110 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, 111 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, 112 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, 113 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, 114 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, 115 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, 116 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, 117 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, 118 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, 119 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, 120 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, 121 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, 122 | {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, 123 | {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, 124 | {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, 125 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, 126 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, 127 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, 128 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, 129 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, 130 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, 131 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, 132 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, 133 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, 134 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, 135 | {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, 136 | {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, 137 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, 138 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, 139 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, 140 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, 141 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, 142 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, 143 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, 144 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, 145 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, 146 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, 147 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, 148 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, 149 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, 150 | {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, 151 | {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, 152 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, 153 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, 154 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, 155 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, 156 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, 157 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, 158 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, 159 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, 160 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, 161 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, 162 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, 163 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, 164 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, 165 | {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, 166 | {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, 167 | {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, 168 | ] 169 | 170 | [[package]] 171 | name = "click" 172 | version = "8.1.7" 173 | description = "Composable command line interface toolkit" 174 | optional = false 175 | python-versions = ">=3.7" 176 | files = [ 177 | {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, 178 | {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, 179 | ] 180 | 181 | [package.dependencies] 182 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 183 | 184 | [[package]] 185 | name = "colorama" 186 | version = "0.4.6" 187 | description = "Cross-platform colored terminal text." 188 | optional = false 189 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 190 | files = [ 191 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 192 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 193 | ] 194 | 195 | [[package]] 196 | name = "coverage" 197 | version = "7.3.2" 198 | description = "Code coverage measurement for Python" 199 | optional = false 200 | python-versions = ">=3.8" 201 | files = [ 202 | {file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"}, 203 | {file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"}, 204 | {file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"}, 205 | {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"}, 206 | {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"}, 207 | {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"}, 208 | {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"}, 209 | {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"}, 210 | {file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"}, 211 | {file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"}, 212 | {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"}, 213 | {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"}, 214 | {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"}, 215 | {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"}, 216 | {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"}, 217 | {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"}, 218 | {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"}, 219 | {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"}, 220 | {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"}, 221 | {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"}, 222 | {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"}, 223 | {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"}, 224 | {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"}, 225 | {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"}, 226 | {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"}, 227 | {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"}, 228 | {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"}, 229 | {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"}, 230 | {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"}, 231 | {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"}, 232 | {file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"}, 233 | {file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"}, 234 | {file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"}, 235 | {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"}, 236 | {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"}, 237 | {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"}, 238 | {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"}, 239 | {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"}, 240 | {file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"}, 241 | {file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"}, 242 | {file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"}, 243 | {file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"}, 244 | {file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"}, 245 | {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"}, 246 | {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"}, 247 | {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"}, 248 | {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"}, 249 | {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"}, 250 | {file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"}, 251 | {file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"}, 252 | {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"}, 253 | {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"}, 254 | ] 255 | 256 | [package.extras] 257 | toml = ["tomli"] 258 | 259 | [[package]] 260 | name = "ddt" 261 | version = "1.7.0" 262 | description = "Data-Driven/Decorated Tests" 263 | optional = false 264 | python-versions = "*" 265 | files = [ 266 | {file = "ddt-1.7.0-py2.py3-none-any.whl", hash = "sha256:a0719acde99dd32767cff64e653ef99c01ad5b042ff265f2ebecd28796ffd114"}, 267 | {file = "ddt-1.7.0.tar.gz", hash = "sha256:d178d115abf25a1b8327e94f85a03ef09b1d7b0ca256f6203284b024f2fc70df"}, 268 | ] 269 | 270 | [[package]] 271 | name = "idna" 272 | version = "3.6" 273 | description = "Internationalized Domain Names in Applications (IDNA)" 274 | optional = false 275 | python-versions = ">=3.5" 276 | files = [ 277 | {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, 278 | {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, 279 | ] 280 | 281 | [[package]] 282 | name = "mypy-extensions" 283 | version = "1.0.0" 284 | description = "Type system extensions for programs checked with the mypy type checker." 285 | optional = false 286 | python-versions = ">=3.5" 287 | files = [ 288 | {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, 289 | {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, 290 | ] 291 | 292 | [[package]] 293 | name = "packaging" 294 | version = "23.2" 295 | description = "Core utilities for Python packages" 296 | optional = false 297 | python-versions = ">=3.7" 298 | files = [ 299 | {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, 300 | {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, 301 | ] 302 | 303 | [[package]] 304 | name = "pathspec" 305 | version = "0.11.2" 306 | description = "Utility library for gitignore style pattern matching of file paths." 307 | optional = false 308 | python-versions = ">=3.7" 309 | files = [ 310 | {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, 311 | {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, 312 | ] 313 | 314 | [[package]] 315 | name = "platformdirs" 316 | version = "4.0.0" 317 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 318 | optional = false 319 | python-versions = ">=3.7" 320 | files = [ 321 | {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"}, 322 | {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, 323 | ] 324 | 325 | [package.extras] 326 | docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] 327 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] 328 | 329 | [[package]] 330 | name = "pytimeparse" 331 | version = "1.1.8" 332 | description = "Time expression parser" 333 | optional = false 334 | python-versions = "*" 335 | files = [ 336 | {file = "pytimeparse-1.1.8-py2.py3-none-any.whl", hash = "sha256:04b7be6cc8bd9f5647a6325444926c3ac34ee6bc7e69da4367ba282f076036bd"}, 337 | {file = "pytimeparse-1.1.8.tar.gz", hash = "sha256:e86136477be924d7e670646a98561957e8ca7308d44841e21f5ddea757556a0a"}, 338 | ] 339 | 340 | [[package]] 341 | name = "pytz" 342 | version = "2023.3.post1" 343 | description = "World timezone definitions, modern and historical" 344 | optional = false 345 | python-versions = "*" 346 | files = [ 347 | {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, 348 | {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, 349 | ] 350 | 351 | [[package]] 352 | name = "requests" 353 | version = "2.31.0" 354 | description = "Python HTTP for Humans." 355 | optional = false 356 | python-versions = ">=3.7" 357 | files = [ 358 | {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, 359 | {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, 360 | ] 361 | 362 | [package.dependencies] 363 | certifi = ">=2017.4.17" 364 | charset-normalizer = ">=2,<4" 365 | idna = ">=2.5,<4" 366 | urllib3 = ">=1.21.1,<3" 367 | 368 | [package.extras] 369 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 370 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 371 | 372 | [[package]] 373 | name = "tomli" 374 | version = "2.0.1" 375 | description = "A lil' TOML parser" 376 | optional = false 377 | python-versions = ">=3.7" 378 | files = [ 379 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 380 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 381 | ] 382 | 383 | [[package]] 384 | name = "typing-extensions" 385 | version = "4.8.0" 386 | description = "Backported and Experimental Type Hints for Python 3.8+" 387 | optional = false 388 | python-versions = ">=3.8" 389 | files = [ 390 | {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, 391 | {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, 392 | ] 393 | 394 | [[package]] 395 | name = "urllib3" 396 | version = "1.26.6" 397 | description = "HTTP library with thread-safe connection pooling, file post, and more." 398 | optional = false 399 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" 400 | files = [ 401 | {file = "urllib3-1.26.6-py2.py3-none-any.whl", hash = "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4"}, 402 | {file = "urllib3-1.26.6.tar.gz", hash = "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"}, 403 | ] 404 | 405 | [package.extras] 406 | brotli = ["brotlipy (>=0.6.0)"] 407 | secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)"] 408 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 409 | 410 | [metadata] 411 | lock-version = "2.0" 412 | python-versions = ">=3.8,<3.12" 413 | content-hash = "d5a7b8fb2a2e425de7403a0e296fe02ee0f95c83f38d2df824fadcbbfc955ffe" 414 | -------------------------------------------------------------------------------- /poetry.toml: -------------------------------------------------------------------------------- 1 | [virtualenvs] 2 | in-project = true 3 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "World Clock" 3 | version = "2.3.0" 4 | description = "Check time in multiple timezones." 5 | documentation = "https://github.com/fedecalendino/alfred-world-clock/blob/main/README.md" 6 | homepage = "https://github.com/fedecalendino/alfred-world-clock" 7 | license = "MIT" 8 | 9 | authors = [ 10 | "Fede Calendino " 11 | ] 12 | 13 | [tool.poetry.dependencies] 14 | python = ">=3.8,<3.12" 15 | alfred-pyflow = "^0.4.6" 16 | pytz = "^2023.3" 17 | pytimeparse = "^1.1.8" 18 | 19 | [tool.poetry.group.dev.dependencies] 20 | black = "^23.3.0" 21 | coverage = "^7.2.5" 22 | ddt = "^1.6.0" 23 | 24 | [tool.black] 25 | line-length = 150 26 | 27 | [build-system] 28 | requires = ["poetry-core>=1.0.0"] 29 | build-backend = "poetry.core.masonry.api" 30 | -------------------------------------------------------------------------------- /screenshots/config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/screenshots/config.png -------------------------------------------------------------------------------- /screenshots/friendly-names.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/screenshots/friendly-names.png -------------------------------------------------------------------------------- /screenshots/meet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/screenshots/meet.png -------------------------------------------------------------------------------- /screenshots/now.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/screenshots/now.png -------------------------------------------------------------------------------- /screenshots/offset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/screenshots/offset.png -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | rm -rf "./dist/" 2 | 3 | echo "Copy dependencies" 4 | cp -r $(find .venv | egrep "site-packages$") ./dist 5 | 6 | echo "Clean up dist folder" 7 | cd dist 8 | 9 | find . | grep __pycache__ | xargs rm -rf 10 | ls -d */ | grep info | xargs rm -rf 11 | ls | egrep "^\_.*" | xargs rm -rf 12 | 13 | rm -rf *.so black blackd blib2to3 distutils* pkg_resources pip* setuptools* wheel* 14 | 15 | cd .. 16 | 17 | echo "Copy source code" 18 | cp -r ./src/* dist 19 | 20 | echo "Finished" 21 | echo 22 | -------------------------------------------------------------------------------- /scripts/release.sh: -------------------------------------------------------------------------------- 1 | BUNDLEID=$(plutil -extract bundleid raw -o - ./info.plist) 2 | NAME=${BUNDLEID##*.} 3 | 4 | VERSION=${1:-$(poetry version --short)} 5 | 6 | FILENAME="$NAME.v$VERSION.alfredworkflow" 7 | 8 | poetry version $VERSION 9 | plutil -replace version -string $VERSION info.plist 10 | 11 | echo "NAME: $NAME" 12 | echo "BUNDLE ID: $BUNDLEID" 13 | echo "CURRENT VERSION: v$(poetry version --short)" 14 | echo "NEW VERSION: v$VERSION" 15 | echo 16 | 17 | 18 | echo "Testing workflow..." 19 | echo 20 | 21 | PYTHONPATH=src poetry run python3 -m unittest discover -s tests 22 | RESULT=$? 23 | 24 | if [ $RESULT != 0 ]; then 25 | echo "⚠️ TESTS FAILED" 26 | exit $RESULT 27 | fi 28 | 29 | 30 | echo "Building binaries..." 31 | echo 32 | ./scripts/build.sh > /dev/null 33 | echo 34 | 35 | 36 | echo "Building release..." 37 | echo 38 | mkdir releases 2> /dev/null 39 | zip "releases/$FILENAME" -r dist img *.png info.plist 40 | echo 41 | 42 | 43 | echo "Released $NAME v$VERSION" 44 | echo " * releases/$FILENAME" 45 | echo 46 | 47 | 48 | echo "Opening new release" 49 | open "./releases/$FILENAME" 50 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/src/__init__.py -------------------------------------------------------------------------------- /src/data.py: -------------------------------------------------------------------------------- 1 | from pytz import country_timezones 2 | 3 | emojis = { 4 | "AD": "🇦🇩", 5 | "AE": "🇦🇪", 6 | "AF": "🇦🇫", 7 | "AG": "🇦🇬", 8 | "AI": "🇦🇮", 9 | "AL": "🇦🇱", 10 | "AM": "🇦🇲", 11 | "AO": "🇦🇴", 12 | "AQ": "🇦🇶", 13 | "AR": "🇦🇷", 14 | "AS": "🇦🇸", 15 | "AT": "🇦🇹", 16 | "AU": "🇦🇺", 17 | "AW": "🇦🇼", 18 | "AX": "🇦🇽", 19 | "AZ": "🇦🇿", 20 | "BA": "🇧🇦", 21 | "BB": "🇧🇧", 22 | "BD": "🇧🇩", 23 | "BE": "🇧🇪", 24 | "BF": "🇧🇫", 25 | "BG": "🇧🇬", 26 | "BH": "🇧🇭", 27 | "BI": "🇧🇮", 28 | "BJ": "🇧🇯", 29 | "BL": "🇧🇱", 30 | "BM": "🇧🇲", 31 | "BN": "🇧🇳", 32 | "BO": "🇧🇴", 33 | "BQ": "🇧🇶", 34 | "BR": "🇧🇷", 35 | "BS": "🇧🇸", 36 | "BT": "🇧🇹", 37 | "BV": "🇧🇻", 38 | "BW": "🇧🇼", 39 | "BY": "🇧🇾", 40 | "BZ": "🇧🇿", 41 | "CA": "🇨🇦", 42 | "CC": "🇨🇨", 43 | "CD": "🇨🇩", 44 | "CF": "🇨🇫", 45 | "CG": "🇨🇬", 46 | "CH": "🇨🇭", 47 | "CI": "🇨🇮", 48 | "CK": "🇨🇰", 49 | "CL": "🇨🇱", 50 | "CM": "🇨🇲", 51 | "CN": "🇨🇳", 52 | "CO": "🇨🇴", 53 | "CR": "🇨🇷", 54 | "CU": "🇨🇺", 55 | "CV": "🇨🇻", 56 | "CW": "🇨🇼", 57 | "CX": "🇨🇽", 58 | "CY": "🇨🇾", 59 | "CZ": "🇨🇿", 60 | "DE": "🇩🇪", 61 | "DJ": "🇩🇯", 62 | "DK": "🇩🇰", 63 | "DM": "🇩🇲", 64 | "DO": "🇩🇴", 65 | "DZ": "🇩🇿", 66 | "EC": "🇪🇨", 67 | "EE": "🇪🇪", 68 | "EG": "🇪🇬", 69 | "EH": "🇪🇭", 70 | "ER": "🇪🇷", 71 | "ES": "🇪🇸", 72 | "ET": "🇪🇹", 73 | "FI": "🇫🇮", 74 | "FJ": "🇫🇯", 75 | "FK": "🇫🇰", 76 | "FM": "🇫🇲", 77 | "FO": "🇫🇴", 78 | "FR": "🇫🇷", 79 | "GA": "🇬🇦", 80 | "GB": "🇬🇧", 81 | "GD": "🇬🇩", 82 | "GE": "🇬🇪", 83 | "GF": "🇬🇫", 84 | "GG": "🇬🇬", 85 | "GH": "🇬🇭", 86 | "GI": "🇬🇮", 87 | "GL": "🇬🇱", 88 | "GM": "🇬🇲", 89 | "GN": "🇬🇳", 90 | "GP": "🇬🇵", 91 | "GQ": "🇬🇶", 92 | "GR": "🇬🇷", 93 | "GS": "🇬🇸", 94 | "GT": "🇬🇹", 95 | "GU": "🇬🇺", 96 | "GW": "🇬🇼", 97 | "GY": "🇬🇾", 98 | "HK": "🇭🇰", 99 | "HM": "🇭🇲", 100 | "HN": "🇭🇳", 101 | "HR": "🇭🇷", 102 | "HT": "🇭🇹", 103 | "HU": "🇭🇺", 104 | "ID": "🇮🇩", 105 | "IE": "🇮🇪", 106 | "IL": "🇮🇱", 107 | "IM": "🇮🇲", 108 | "IN": "🇮🇳", 109 | "IO": "🇮🇴", 110 | "IQ": "🇮🇶", 111 | "IR": "🇮🇷", 112 | "IS": "🇮🇸", 113 | "IT": "🇮🇹", 114 | "JE": "🇯🇪", 115 | "JM": "🇯🇲", 116 | "JO": "🇯🇴", 117 | "JP": "🇯🇵", 118 | "KE": "🇰🇪", 119 | "KG": "🇰🇬", 120 | "KH": "🇰🇭", 121 | "KI": "🇰🇮", 122 | "KM": "🇰🇲", 123 | "KN": "🇰🇳", 124 | "KP": "🇰🇵", 125 | "KR": "🇰🇷", 126 | "KW": "🇰🇼", 127 | "KY": "🇰🇾", 128 | "KZ": "🇰🇿", 129 | "LA": "🇱🇦", 130 | "LB": "🇱🇧", 131 | "LC": "🇱🇨", 132 | "LI": "🇱🇮", 133 | "LK": "🇱🇰", 134 | "LR": "🇱🇷", 135 | "LS": "🇱🇸", 136 | "LT": "🇱🇹", 137 | "LU": "🇱🇺", 138 | "LV": "🇱🇻", 139 | "LY": "🇱🇾", 140 | "MA": "🇲🇦", 141 | "MC": "🇲🇨", 142 | "MD": "🇲🇩", 143 | "ME": "🇲🇪", 144 | "MF": "🇲🇫", 145 | "MG": "🇲🇬", 146 | "MH": "🇲🇭", 147 | "MK": "🇲🇰", 148 | "ML": "🇲🇱", 149 | "MM": "🇲🇲", 150 | "MN": "🇲🇳", 151 | "MO": "🇲🇴", 152 | "MP": "🇲🇵", 153 | "MQ": "🇲🇶", 154 | "MR": "🇲🇷", 155 | "MS": "🇲🇸", 156 | "MT": "🇲🇹", 157 | "MU": "🇲🇺", 158 | "MV": "🇲🇻", 159 | "MW": "🇲🇼", 160 | "MX": "🇲🇽", 161 | "MY": "🇲🇾", 162 | "MZ": "🇲🇿", 163 | "NA": "🇳🇦", 164 | "NC": "🇳🇨", 165 | "NE": "🇳🇪", 166 | "NF": "🇳🇫", 167 | "NG": "🇳🇬", 168 | "NI": "🇳🇮", 169 | "NL": "🇳🇱", 170 | "NO": "🇳🇴", 171 | "NP": "🇳🇵", 172 | "NR": "🇳🇷", 173 | "NU": "🇳🇺", 174 | "NZ": "🇳🇿", 175 | "OM": "🇴🇲", 176 | "PA": "🇵🇦", 177 | "PE": "🇵🇪", 178 | "PF": "🇵🇫", 179 | "PG": "🇵🇬", 180 | "PH": "🇵🇭", 181 | "PK": "🇵🇰", 182 | "PL": "🇵🇱", 183 | "PM": "🇵🇲", 184 | "PN": "🇵🇳", 185 | "PR": "🇵🇷", 186 | "PS": "🇵🇸", 187 | "PT": "🇵🇹", 188 | "PW": "🇵🇼", 189 | "PY": "🇵🇾", 190 | "QA": "🇶🇦", 191 | "RE": "🇷🇪", 192 | "RO": "🇷🇴", 193 | "RS": "🇷🇸", 194 | "RU": "🇷🇺", 195 | "RW": "🇷🇼", 196 | "SA": "🇸🇦", 197 | "SB": "🇸🇧", 198 | "SC": "🇸🇨", 199 | "SD": "🇸🇩", 200 | "SE": "🇸🇪", 201 | "SG": "🇸🇬", 202 | "SH": "🇸🇭", 203 | "SI": "🇸🇮", 204 | "SJ": "🇸🇯", 205 | "SK": "🇸🇰", 206 | "SL": "🇸🇱", 207 | "SM": "🇸🇲", 208 | "SN": "🇸🇳", 209 | "SO": "🇸🇴", 210 | "SR": "🇸🇷", 211 | "SS": "🇸🇸", 212 | "ST": "🇸🇹", 213 | "SV": "🇸🇻", 214 | "SX": "🇸🇽", 215 | "SY": "🇸🇾", 216 | "SZ": "🇸🇿", 217 | "TC": "🇹🇨", 218 | "TD": "🇹🇩", 219 | "TF": "🇹🇫", 220 | "TG": "🇹🇬", 221 | "TH": "🇹🇭", 222 | "TJ": "🇹🇯", 223 | "TK": "🇹🇰", 224 | "TL": "🇹🇱", 225 | "TM": "🇹🇲", 226 | "TN": "🇹🇳", 227 | "TO": "🇹🇴", 228 | "TR": "🇹🇷", 229 | "TT": "🇹🇹", 230 | "TV": "🇹🇻", 231 | "TW": "🇹🇼", 232 | "TZ": "🇹🇿", 233 | "UA": "🇺🇦", 234 | "UG": "🇺🇬", 235 | "UM": "🇺🇲", 236 | "US": "🇺🇸", 237 | "UY": "🇺🇾", 238 | "UZ": "🇺🇿", 239 | "VA": "🇻🇦", 240 | "VC": "🇻🇨", 241 | "VE": "🇻🇪", 242 | "VG": "🇻🇬", 243 | "VI": "🇻🇮", 244 | "VN": "🇻🇳", 245 | "VU": "🇻🇺", 246 | "WF": "🇼🇫", 247 | "WS": "🇼🇸", 248 | "XK": "🇽🇰", 249 | "YE": "🇾🇪", 250 | "YT": "🇾🇹", 251 | "ZA": "🇿🇦", 252 | "ZM": "🇿🇲", 253 | "ZW": "🇿🇼", 254 | } 255 | 256 | flags = {} 257 | 258 | 259 | for code in country_timezones: 260 | timezones = country_timezones[code] 261 | 262 | for timezone in timezones: 263 | flags[timezone] = emojis[code] 264 | -------------------------------------------------------------------------------- /src/formatters.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | 4 | def default_12hs(now: datetime) -> str: 5 | return now.strftime("%I:%M:%S %p (%B %d, %Y)").lower() 6 | 7 | 8 | def default_24hs(now: datetime) -> str: 9 | return now.strftime("%H:%M:%S (%B %d, %Y)") 10 | 11 | 12 | def iso8601(now: datetime) -> str: 13 | return now.isoformat() 14 | 15 | 16 | def iso8601_without_microseconds(now: datetime) -> str: 17 | return now.replace(microsecond=0).isoformat() 18 | 19 | 20 | FORMATTERS = { 21 | "FORMAT_DEFAULT": default_24hs, 22 | "FORMAT_DEFAULT_12HS": default_12hs, 23 | "FORMAT_DEFAULT_24HS": default_24hs, 24 | "FORMAT_ISO8601_WITH_MICROSECONDS": iso8601, 25 | "FORMAT_ISO8601_WITHOUT_MICROSECONDS": iso8601_without_microseconds, 26 | } 27 | -------------------------------------------------------------------------------- /src/helpers.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from typing import Dict, Tuple, List 3 | 4 | import pytz as tz 5 | from pyflow import Workflow 6 | 7 | import formatters 8 | 9 | 10 | def get_formatter(workflow: Workflow) -> callable: 11 | timestamp_format = workflow.env.get("TIMESTAMP_FORMAT", "FORMAT_DEFAULT") 12 | return formatters.FORMATTERS[timestamp_format] 13 | 14 | 15 | def get_icon(timezone: str, now: datetime, home_tz: str) -> str: 16 | if timezone == home_tz: 17 | return "img/icons/home.png" 18 | 19 | if timezone == "UTC": 20 | return "img/icons/utc.png" 21 | 22 | utc_offset = now.utcoffset() 23 | utc_offset_hours = round(utc_offset.days * 24 + utc_offset.seconds / 60 / 60) 24 | return f"img/icons/{utc_offset_hours}.png" 25 | 26 | 27 | def get_home(workflow: Workflow) -> Tuple[str, datetime]: 28 | home_tz = workflow.env["HOME"][3:].replace("__", "/") 29 | 30 | home_now = ( 31 | datetime.utcnow() 32 | .replace(tzinfo=tz.utc) 33 | .astimezone( 34 | tz=tz.timezone(home_tz), 35 | ) 36 | ) 37 | 38 | return home_tz, home_now 39 | 40 | 41 | def get_name_replacements(workflow: Workflow): 42 | sep = "//" 43 | 44 | name_replacements = {} 45 | 46 | for line in workflow.env.get("NAME_REPLACEMENTS", "").split("\n"): 47 | if not line: 48 | continue 49 | 50 | if sep not in line: 51 | raise ValueError(f"name replacement '{line}' is missing the '{sep}' separator.") 52 | 53 | parts = line.split(sep) 54 | 55 | if len(parts) != 2: 56 | raise ValueError(f"name replacement '{line}' should have the format `old {sep} new`.") 57 | 58 | if "" in parts: 59 | raise ValueError(f"name replacement '{line}' should have the format `old {sep} new`.") 60 | 61 | old, new = parts 62 | name_replacements[old.strip()] = new.strip() 63 | 64 | return name_replacements 65 | 66 | 67 | def get_timezones(workflow: Workflow, now: datetime, include: List[str] = None) -> Dict[str, datetime]: 68 | timezones = set( 69 | map( 70 | lambda item: item[0][3:].replace("__", "/"), 71 | filter( 72 | lambda item: item[0].startswith("TZ_") and item[1] == "1", 73 | workflow.env.items(), 74 | ), 75 | ) 76 | ) 77 | 78 | timezones.update(include or []) 79 | 80 | return { 81 | timezone: now.replace( 82 | tzinfo=tz.utc, 83 | ).astimezone( 84 | tz=tz.timezone(timezone), 85 | ) 86 | for timezone in timezones 87 | } 88 | 89 | 90 | def get_home_offset_str(timezone: str, home_tz: str, now: datetime, home_now: datetime, utc: bool = False) -> str: 91 | utc_offset = get_utc_offset(now) 92 | 93 | if utc_offset == 0: 94 | utc_offset_str = "UTC" 95 | elif utc_offset > 0: 96 | utc_offset_str = f"UTC +{utc_offset}" 97 | else: 98 | utc_offset_str = f"UTC {utc_offset}" 99 | 100 | if timezone == home_tz: 101 | return f"({utc_offset_str})" 102 | 103 | now_tmp = now.replace(tzinfo=None) 104 | home_now_tmp = home_now.replace(tzinfo=None) 105 | 106 | if home_now_tmp > now_tmp: 107 | home_offset = home_now_tmp - now_tmp 108 | seconds = home_offset.seconds + 1 109 | text = "behind" 110 | else: 111 | home_offset = home_now_tmp - now_tmp 112 | seconds = 24 * 60 * 60 - home_offset.seconds + 1 113 | text = "ahead of" 114 | 115 | if not utc: 116 | return "({utc_offset}) · [{hours:02}:{minutes:02} hs {text} home 🏠]".format( 117 | hours=seconds // 3600, 118 | minutes=(seconds % 3600) // 60, 119 | text=text, 120 | utc_offset=utc_offset_str, 121 | ) 122 | else: 123 | return "({utc_offset})".format( 124 | utc_offset=utc_offset_str, 125 | ) 126 | 127 | 128 | def get_utc_offset(now: datetime) -> int: 129 | utc_offset = now.utcoffset() 130 | 131 | return round(utc_offset.days * 24 + utc_offset.seconds / 60 / 60) 132 | -------------------------------------------------------------------------------- /src/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from datetime import datetime, timedelta 3 | from typing import List, Tuple 4 | from uuid import uuid4 5 | 6 | import pytz 7 | from pyflow import Workflow 8 | from pytimeparse.timeparse import timeparse 9 | 10 | import data 11 | import formatters 12 | import helpers 13 | 14 | 15 | # parse parameter to a set of valid time formats 16 | def convert_to_time(time_arg: str) -> Tuple[int, int, int]: 17 | for format_str in ["%H:%M:%S", "%H:%M"]: 18 | try: 19 | time = datetime.strptime(time_arg, format_str) 20 | return time.hour, time.minute, time.second 21 | except ValueError: 22 | pass 23 | 24 | raise ValueError(f"'{time_arg}' should follow the formats 'HH:MM:SS' or 'HH:MM'") 25 | 26 | 27 | # parse parameter to a set of valid date formats 28 | def convert_to_date(date_arg: str) -> Tuple[int, int, int]: 29 | for format_str in ["%d/%m/%Y", "%Y-%m-%d"]: 30 | try: 31 | date = datetime.strptime(date_arg, format_str) 32 | return date.day, date.month, date.year 33 | except ValueError: 34 | pass 35 | 36 | raise ValueError(f"'{date_arg}' should follow the formats 'dd/mm/yyyy' or 'yyyy-mm-dd'") 37 | 38 | 39 | # parse a positive or negative time offset to create a delta in seconds 40 | def convert_to_delta(delta_arg: str) -> timedelta: 41 | try: 42 | return timedelta( 43 | seconds=timeparse(delta_arg), 44 | ) 45 | except TypeError: 46 | raise ValueError("invalid time offset") 47 | 48 | 49 | # parse arguments to find in which mode the workflow is running (always returns utc datetime) 50 | def parse_args(args: List[str], home_now: datetime) -> datetime: 51 | if len(args) > 2: 52 | raise ValueError("too many params, expected: [+offset] / [-offset] / [time] / [time date]") 53 | 54 | # mode now: shows current time 55 | if len(args) == 0: 56 | return datetime.utcnow() 57 | 58 | # mode offset: shows current time +/- a time offset 59 | if args[0][0] in "+-": 60 | return datetime.utcnow() + convert_to_delta(args[0]) 61 | 62 | # mode set: shows especific date and time as home 63 | hour, minute, second = convert_to_time(args[0]) 64 | 65 | now = home_now.replace( 66 | hour=hour, 67 | minute=minute, 68 | second=second, 69 | microsecond=0, 70 | ) 71 | 72 | if len(args) == 2: 73 | day, month, year = convert_to_date(args[1]) 74 | 75 | now = now.replace( 76 | day=day, 77 | month=month, 78 | year=year, 79 | ) 80 | 81 | return now.astimezone(pytz.utc) 82 | 83 | 84 | def main(workflow: Workflow): 85 | name_replacements = helpers.get_name_replacements(workflow) 86 | formatter = helpers.get_formatter(workflow) 87 | 88 | home_tz, home_now = helpers.get_home(workflow) 89 | utc_now = parse_args(workflow.args, home_now) 90 | 91 | timezones = helpers.get_timezones( 92 | workflow, 93 | utc_now, 94 | include=[home_tz], 95 | ) 96 | 97 | for timezone, now in sorted(timezones.items(), key=lambda kw: kw[1].isoformat()): 98 | location = timezone.split("/")[-1].replace("_", " ") 99 | location = name_replacements.get(location, location) 100 | 101 | workflow.new_item( 102 | title=formatter(now), 103 | subtitle="{flag} {location} {home_offset}".format( 104 | flag=data.flags.get(timezone, "🌐"), 105 | location=location, 106 | home_offset=helpers.get_home_offset_str( 107 | timezone=timezone, 108 | home_tz=home_tz, 109 | now=now, 110 | home_now=home_now, 111 | ), 112 | ), 113 | arg="{now} {flag} {location} {home_offset}".format( 114 | now = formatter(now), 115 | flag = data.flags.get(timezone, "🌐"), 116 | location = location, 117 | home_offset = helpers.get_home_offset_str( 118 | timezone=timezone, 119 | home_tz=home_tz, 120 | now=now, 121 | home_now=home_now, 122 | utc=True 123 | ) 124 | ), 125 | copytext=formatter(now), 126 | valid=True, 127 | uid=str(uuid4()), 128 | ).set_icon_file( 129 | path=helpers.get_icon(timezone, now, home_tz), 130 | ).set_alt_mod( 131 | subtitle="Copy ISO format (with microseconds)", 132 | arg=formatters.iso8601(now), 133 | ).set_cmd_mod( 134 | subtitle="Copy ISO format (without microseconds)", 135 | arg=formatters.iso8601_without_microseconds(now), 136 | ) 137 | 138 | 139 | if __name__ == "__main__": 140 | wf = Workflow() 141 | wf.run(main) 142 | wf.send_feedback() 143 | sys.exit() 144 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedecalendino/alfred-world-clock/bace898211ae76246424f754d48003a44262fec1/tests/__init__.py -------------------------------------------------------------------------------- /tzdata/build.py: -------------------------------------------------------------------------------- 1 | import csv 2 | 3 | from data import emojis 4 | 5 | with open("country.csv") as file: 6 | countries = dict(csv.reader(file)) 7 | 8 | 9 | with open("time_zone.csv") as file: 10 | db = csv.reader(file) 11 | 12 | found = set() 13 | 14 | for row in db: 15 | timezone, country_code = row[0], row[1] 16 | country_name = countries[country_code] 17 | country_flag = emojis[country_code] 18 | city_name = timezone.split("/")[-1].replace("_", " ") 19 | 20 | found.add((country_name, city_name, country_code, country_flag, timezone)) 21 | 22 | with open("timezones.checkboxes", "w+") as checkboxes: 23 | with open("timezones.homes", "w+") as homes: 24 | for item in sorted(found): 25 | country_name, city_name, country_code, country_flag, timezone = item 26 | 27 | checkbox = [ 28 | " ", 29 | " config", 30 | " ", 31 | " default", 32 | f" <{'false'}/>", 33 | " required", 34 | " ", 35 | " text", 36 | f" {country_flag} {country_name} / {city_name}", 37 | " ", 38 | " description", 39 | " ", 40 | " label", 41 | f" ", 42 | " type", 43 | " checkbox", 44 | " variable", 45 | f" TZ_{timezone.replace('/', '__')}", 46 | " ", 47 | "", 48 | ] 49 | 50 | checkboxes.write("\n".join(checkbox)) 51 | 52 | home = [ 53 | " ", 54 | f" {country_flag} {country_name} / {city_name}", 55 | f" TZ_{timezone.replace('/', '__')}", 56 | " ", 57 | "", 58 | ] 59 | 60 | homes.write("\n".join(home)) 61 | -------------------------------------------------------------------------------- /tzdata/country.csv: -------------------------------------------------------------------------------- 1 | AF,Afghanistan 2 | AL,Albania 3 | DZ,Algeria 4 | AS,American Samoa 5 | AD,Andorra 6 | AO,Angola 7 | AI,Anguilla 8 | AQ,Antarctica 9 | AG,Antigua and Barbuda 10 | AR,Argentina 11 | AM,Armenia 12 | AW,Aruba 13 | AU,Australia 14 | AT,Austria 15 | AZ,Azerbaijan 16 | BS,Bahamas 17 | BH,Bahrain 18 | BD,Bangladesh 19 | BB,Barbados 20 | BY,Belarus 21 | BE,Belgium 22 | BZ,Belize 23 | BJ,Benin 24 | BM,Bermuda 25 | BT,Bhutan 26 | BO,"Bolivia, Plurinational State of" 27 | BQ,"Bonaire, Sint Eustatius and Saba" 28 | BA,Bosnia and Herzegovina 29 | BW,Botswana 30 | BR,Brazil 31 | IO,British Indian Ocean Territory 32 | BN,Brunei Darussalam 33 | BG,Bulgaria 34 | BF,Burkina Faso 35 | BI,Burundi 36 | KH,Cambodia 37 | CM,Cameroon 38 | CA,Canada 39 | CV,Cape Verde 40 | KY,Cayman Islands 41 | CF,Central African Republic 42 | TD,Chad 43 | CL,Chile 44 | CN,China 45 | CX,Christmas Island 46 | CC,Cocos (Keeling) Islands 47 | CO,Colombia 48 | KM,Comoros 49 | CG,Congo 50 | CD,"Congo, the Democratic Republic of the" 51 | CK,Cook Islands 52 | CR,Costa Rica 53 | HR,Croatia 54 | CU,Cuba 55 | CW,Curaçao 56 | CY,Cyprus 57 | CZ,Czech Republic 58 | CI,Côte d'Ivoire 59 | DK,Denmark 60 | DJ,Djibouti 61 | DM,Dominica 62 | DO,Dominican Republic 63 | EC,Ecuador 64 | EG,Egypt 65 | SV,El Salvador 66 | GQ,Equatorial Guinea 67 | ER,Eritrea 68 | EE,Estonia 69 | ET,Ethiopia 70 | FK,Falkland Islands (Malvinas) 71 | FO,Faroe Islands 72 | FJ,Fiji 73 | FI,Finland 74 | FR,France 75 | GF,French Guiana 76 | PF,French Polynesia 77 | TF,French Southern Territories 78 | GA,Gabon 79 | GM,Gambia 80 | GE,Georgia 81 | DE,Germany 82 | GH,Ghana 83 | GI,Gibraltar 84 | GR,Greece 85 | GL,Greenland 86 | GD,Grenada 87 | GP,Guadeloupe 88 | GU,Guam 89 | GT,Guatemala 90 | GG,Guernsey 91 | GN,Guinea 92 | GW,Guinea-Bissau 93 | GY,Guyana 94 | HT,Haiti 95 | VA,Holy See (Vatican City State) 96 | HN,Honduras 97 | HK,Hong Kong 98 | HU,Hungary 99 | IS,Iceland 100 | IN,India 101 | ID,Indonesia 102 | IR,"Iran, Islamic Republic of" 103 | IQ,Iraq 104 | IE,Ireland 105 | IM,Isle of Man 106 | IL,Israel 107 | IT,Italy 108 | JM,Jamaica 109 | JP,Japan 110 | JE,Jersey 111 | JO,Jordan 112 | KZ,Kazakhstan 113 | KE,Kenya 114 | KI,Kiribati 115 | KP,"Korea, Democratic People's Republic of" 116 | KR,"Korea, Republic of" 117 | KW,Kuwait 118 | KG,Kyrgyzstan 119 | LA,Lao People's Democratic Republic 120 | LV,Latvia 121 | LB,Lebanon 122 | LS,Lesotho 123 | LR,Liberia 124 | LY,Libya 125 | LI,Liechtenstein 126 | LT,Lithuania 127 | LU,Luxembourg 128 | MO,Macao 129 | MK,"Macedonia, the Former Yugoslav Republic of" 130 | MG,Madagascar 131 | MW,Malawi 132 | MY,Malaysia 133 | MV,Maldives 134 | ML,Mali 135 | MT,Malta 136 | MH,Marshall Islands 137 | MQ,Martinique 138 | MR,Mauritania 139 | MU,Mauritius 140 | YT,Mayotte 141 | MX,Mexico 142 | FM,"Micronesia, Federated States of" 143 | MD,"Moldova, Republic of" 144 | MC,Monaco 145 | MN,Mongolia 146 | ME,Montenegro 147 | MS,Montserrat 148 | MA,Morocco 149 | MZ,Mozambique 150 | MM,Myanmar 151 | NA,Namibia 152 | NR,Nauru 153 | NP,Nepal 154 | NL,Netherlands 155 | NC,New Caledonia 156 | NZ,New Zealand 157 | NI,Nicaragua 158 | NE,Niger 159 | NG,Nigeria 160 | NU,Niue 161 | NF,Norfolk Island 162 | MP,Northern Mariana Islands 163 | NO,Norway 164 | OM,Oman 165 | PK,Pakistan 166 | PW,Palau 167 | PS,"Palestine, State of" 168 | PA,Panama 169 | PG,Papua New Guinea 170 | PY,Paraguay 171 | PE,Peru 172 | PH,Philippines 173 | PN,Pitcairn 174 | PL,Poland 175 | PT,Portugal 176 | PR,Puerto Rico 177 | QA,Qatar 178 | RO,Romania 179 | RU,Russian Federation 180 | RW,Rwanda 181 | RE,Réunion 182 | BL,Saint Barthélemy 183 | SH,"Saint Helena, Ascension and Tristan da Cunha" 184 | KN,Saint Kitts and Nevis 185 | LC,Saint Lucia 186 | MF,Saint Martin (French part) 187 | PM,Saint Pierre and Miquelon 188 | VC,Saint Vincent and the Grenadines 189 | WS,Samoa 190 | SM,San Marino 191 | ST,Sao Tome and Principe 192 | SA,Saudi Arabia 193 | SN,Senegal 194 | RS,Serbia 195 | SC,Seychelles 196 | SL,Sierra Leone 197 | SG,Singapore 198 | SX,Sint Maarten (Dutch part) 199 | SK,Slovakia 200 | SI,Slovenia 201 | SB,Solomon Islands 202 | SO,Somalia 203 | ZA,South Africa 204 | GS,South Georgia and the South Sandwich Islands 205 | SS,South Sudan 206 | ES,Spain 207 | LK,Sri Lanka 208 | SD,Sudan 209 | SR,Suriname 210 | SJ,Svalbard and Jan Mayen 211 | SZ,Swaziland 212 | SE,Sweden 213 | CH,Switzerland 214 | SY,Syrian Arab Republic 215 | TW,"Taiwan" 216 | TJ,Tajikistan 217 | TZ,"Tanzania, United Republic of" 218 | TH,Thailand 219 | TL,Timor-Leste 220 | TG,Togo 221 | TK,Tokelau 222 | TO,Tonga 223 | TT,Trinidad and Tobago 224 | TN,Tunisia 225 | TR,Turkey 226 | TM,Turkmenistan 227 | TC,Turks and Caicos Islands 228 | TV,Tuvalu 229 | UG,Uganda 230 | UA,Ukraine 231 | AE,United Arab Emirates 232 | GB,United Kingdom 233 | US,United States 234 | UM,United States Minor Outlying Islands 235 | UY,Uruguay 236 | UZ,Uzbekistan 237 | VU,Vanuatu 238 | VE,"Venezuela, Bolivarian Republic of" 239 | VN,Viet Nam 240 | VG,"Virgin Islands, British" 241 | VI,"Virgin Islands, U.S." 242 | WF,Wallis and Futuna 243 | EH,Western Sahara 244 | YE,Yemen 245 | ZM,Zambia 246 | ZW,Zimbabwe 247 | AX,Åland Islands --------------------------------------------------------------------------------