├── .github
├── FUNDING.yml
└── workflows
│ ├── clippy.yml
│ ├── rustfmt.yml
│ ├── tests.yml
│ └── wasm.yml
├── .gitignore
├── .gitmodules
├── .vscode
└── settings.json
├── CHANGELOG.md
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── Makefile
├── README.md
├── assets
└── hello.png
├── cli
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
└── src
│ └── main.rs
├── libwhen
├── Cargo.toml
├── LICENSE
├── README.md
├── build.rs
└── src
│ ├── date_grammar.pest
│ ├── lib.rs
│ ├── location.rs
│ ├── parser.rs
│ └── utils.rs
└── web
├── Cargo.lock
├── Cargo.toml
├── src
└── lib.rs
├── tests
└── web.rs
└── www
├── .gitignore
├── bootstrap.js
├── index.html
├── index.jsx
├── package-lock.json
├── package.json
├── style.css
├── webpack.config.js
└── webpack.prod.js
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [mitsuhiko]
2 |
--------------------------------------------------------------------------------
/.github/workflows/clippy.yml:
--------------------------------------------------------------------------------
1 | name: Clippy
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 |
9 | steps:
10 | - uses: actions/checkout@v2
11 | with:
12 | submodules: recursive
13 | - uses: actions-rs/toolchain@v1
14 | with:
15 | toolchain: stable
16 | profile: minimal
17 | components: clippy, rustfmt
18 | override: true
19 | - name: Run clippy
20 | run: make lint
21 |
--------------------------------------------------------------------------------
/.github/workflows/rustfmt.yml:
--------------------------------------------------------------------------------
1 | name: Rustfmt
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 |
9 | steps:
10 | - uses: actions/checkout@v2
11 | with:
12 | submodules: recursive
13 | - uses: actions-rs/toolchain@v1
14 | with:
15 | toolchain: stable
16 | profile: minimal
17 | components: clippy, rustfmt
18 | override: true
19 | - name: Run rustfmt
20 | run: make format-check
21 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build-latest:
7 | name: Test on Latest
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v2
12 | with:
13 | submodules: recursive
14 | - uses: actions-rs/toolchain@v1
15 | with:
16 | toolchain: stable
17 | profile: minimal
18 | override: true
19 | - name: Test
20 | run: make test
21 |
--------------------------------------------------------------------------------
/.github/workflows/wasm.yml:
--------------------------------------------------------------------------------
1 | name: WASM Build
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | build-latest:
10 | name: WASM on Latest
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - uses: actions/checkout@v2
15 | with:
16 | submodules: recursive
17 | - uses: actions-rs/toolchain@v1
18 | with:
19 | toolchain: stable
20 | profile: minimal
21 | override: true
22 | - uses: jetli/wasm-pack-action@v0.3.0
23 | - name: Build
24 | run: (cd web/www; npm ci) && make web-dist && touch web/www/dist/.nojekyll
25 | - name: Deploy
26 | uses: JamesIves/github-pages-deploy-action@4.1.7
27 | with:
28 | branch: gh-pages
29 | folder: web/www/dist
30 | single-commit: true
31 | clean: true
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "libwhen/data"]
2 | path = libwhen/data
3 | url = https://github.com/mitsuhiko/when-data
4 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "rust-analyzer.checkOnSave.command": "clippy"
3 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to `when` are documented here.
4 |
5 | ## 0.4.0
6 |
7 | - Added `unix:` syntax to support timestamps.
8 |
9 | ## 0.3.0
10 |
11 | - Added support for multiple target locations.
12 | - Added timezone name to short output.
13 | - Added `--json` flag to enable JSON output.
14 | - Added timezone abbreviations to output.
15 | - Added `--list-timezones` option.
16 |
17 | ## 0.2.0
18 |
19 | - Fixed an issue where admin code was same as country code.
20 | - Add support for colors.
21 | - Improved error message output.
22 | - Added week day output.
23 | - If no `to` zone is requested, the current local timezone is assumed if the timezones do not match.
24 |
25 | ## 0.1.0
26 |
27 | - Initial version
28 |
--------------------------------------------------------------------------------
/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "anyhow"
7 | version = "1.0.51"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203"
10 |
11 | [[package]]
12 | name = "atty"
13 | version = "0.2.14"
14 | source = "registry+https://github.com/rust-lang/crates.io-index"
15 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
16 | dependencies = [
17 | "hermit-abi",
18 | "libc",
19 | "winapi",
20 | ]
21 |
22 | [[package]]
23 | name = "autocfg"
24 | version = "1.0.1"
25 | source = "registry+https://github.com/rust-lang/crates.io-index"
26 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
27 |
28 | [[package]]
29 | name = "bitflags"
30 | version = "1.3.2"
31 | source = "registry+https://github.com/rust-lang/crates.io-index"
32 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
33 |
34 | [[package]]
35 | name = "block-buffer"
36 | version = "0.7.3"
37 | source = "registry+https://github.com/rust-lang/crates.io-index"
38 | checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
39 | dependencies = [
40 | "block-padding",
41 | "byte-tools",
42 | "byteorder",
43 | "generic-array",
44 | ]
45 |
46 | [[package]]
47 | name = "block-padding"
48 | version = "0.1.5"
49 | source = "registry+https://github.com/rust-lang/crates.io-index"
50 | checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
51 | dependencies = [
52 | "byte-tools",
53 | ]
54 |
55 | [[package]]
56 | name = "bumpalo"
57 | version = "3.8.0"
58 | source = "registry+https://github.com/rust-lang/crates.io-index"
59 | checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
60 |
61 | [[package]]
62 | name = "byte-tools"
63 | version = "0.3.1"
64 | source = "registry+https://github.com/rust-lang/crates.io-index"
65 | checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
66 |
67 | [[package]]
68 | name = "byteorder"
69 | version = "1.4.3"
70 | source = "registry+https://github.com/rust-lang/crates.io-index"
71 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
72 |
73 | [[package]]
74 | name = "cfg-if"
75 | version = "1.0.0"
76 | source = "registry+https://github.com/rust-lang/crates.io-index"
77 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
78 |
79 | [[package]]
80 | name = "chrono"
81 | version = "0.4.19"
82 | source = "registry+https://github.com/rust-lang/crates.io-index"
83 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
84 | dependencies = [
85 | "libc",
86 | "num-integer",
87 | "num-traits",
88 | "serde",
89 | "time",
90 | "winapi",
91 | ]
92 |
93 | [[package]]
94 | name = "chrono-humanize"
95 | version = "0.2.1"
96 | source = "registry+https://github.com/rust-lang/crates.io-index"
97 | checksum = "2eddc119501d583fd930cb92144e605f44e0252c38dd89d9247fffa1993375cb"
98 | dependencies = [
99 | "chrono",
100 | ]
101 |
102 | [[package]]
103 | name = "chrono-tz"
104 | version = "0.6.1"
105 | source = "registry+https://github.com/rust-lang/crates.io-index"
106 | checksum = "58549f1842da3080ce63002102d5bc954c7bc843d4f47818e642abdc36253552"
107 | dependencies = [
108 | "chrono",
109 | "chrono-tz-build",
110 | "phf",
111 | ]
112 |
113 | [[package]]
114 | name = "chrono-tz-build"
115 | version = "0.0.2"
116 | source = "registry+https://github.com/rust-lang/crates.io-index"
117 | checksum = "db058d493fb2f65f41861bfed7e3fe6335264a9f0f92710cab5bdf01fef09069"
118 | dependencies = [
119 | "parse-zoneinfo",
120 | "phf",
121 | "phf_codegen",
122 | ]
123 |
124 | [[package]]
125 | name = "clap"
126 | version = "3.0.0-rc.0"
127 | source = "registry+https://github.com/rust-lang/crates.io-index"
128 | checksum = "79b70f999da60e6619a29b131739d2211ed4d4301f40372e94a8081422e9d6c7"
129 | dependencies = [
130 | "atty",
131 | "bitflags",
132 | "clap_derive",
133 | "indexmap",
134 | "lazy_static",
135 | "os_str_bytes",
136 | "strsim",
137 | "termcolor",
138 | "terminal_size",
139 | "textwrap",
140 | ]
141 |
142 | [[package]]
143 | name = "clap_derive"
144 | version = "3.0.0-rc.0"
145 | source = "registry+https://github.com/rust-lang/crates.io-index"
146 | checksum = "fe8c0f28022faaef0387fa54f8e33fee22b804a88bbd91303197da2ff8ca6a5d"
147 | dependencies = [
148 | "heck",
149 | "proc-macro-error",
150 | "proc-macro2",
151 | "quote",
152 | "syn",
153 | ]
154 |
155 | [[package]]
156 | name = "console"
157 | version = "0.15.0"
158 | source = "registry+https://github.com/rust-lang/crates.io-index"
159 | checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31"
160 | dependencies = [
161 | "encode_unicode",
162 | "libc",
163 | "once_cell",
164 | "regex",
165 | "terminal_size",
166 | "unicode-width",
167 | "winapi",
168 | ]
169 |
170 | [[package]]
171 | name = "digest"
172 | version = "0.8.1"
173 | source = "registry+https://github.com/rust-lang/crates.io-index"
174 | checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
175 | dependencies = [
176 | "generic-array",
177 | ]
178 |
179 | [[package]]
180 | name = "encode_unicode"
181 | version = "0.3.6"
182 | source = "registry+https://github.com/rust-lang/crates.io-index"
183 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
184 |
185 | [[package]]
186 | name = "fake-simd"
187 | version = "0.1.2"
188 | source = "registry+https://github.com/rust-lang/crates.io-index"
189 | checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
190 |
191 | [[package]]
192 | name = "generic-array"
193 | version = "0.12.4"
194 | source = "registry+https://github.com/rust-lang/crates.io-index"
195 | checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
196 | dependencies = [
197 | "typenum",
198 | ]
199 |
200 | [[package]]
201 | name = "getrandom"
202 | version = "0.2.3"
203 | source = "registry+https://github.com/rust-lang/crates.io-index"
204 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
205 | dependencies = [
206 | "cfg-if",
207 | "libc",
208 | "wasi",
209 | ]
210 |
211 | [[package]]
212 | name = "hashbrown"
213 | version = "0.11.2"
214 | source = "registry+https://github.com/rust-lang/crates.io-index"
215 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
216 |
217 | [[package]]
218 | name = "heck"
219 | version = "0.3.3"
220 | source = "registry+https://github.com/rust-lang/crates.io-index"
221 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
222 | dependencies = [
223 | "unicode-segmentation",
224 | ]
225 |
226 | [[package]]
227 | name = "hermit-abi"
228 | version = "0.1.19"
229 | source = "registry+https://github.com/rust-lang/crates.io-index"
230 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
231 | dependencies = [
232 | "libc",
233 | ]
234 |
235 | [[package]]
236 | name = "indexmap"
237 | version = "1.7.0"
238 | source = "registry+https://github.com/rust-lang/crates.io-index"
239 | checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
240 | dependencies = [
241 | "autocfg",
242 | "hashbrown",
243 | ]
244 |
245 | [[package]]
246 | name = "itoa"
247 | version = "0.4.8"
248 | source = "registry+https://github.com/rust-lang/crates.io-index"
249 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
250 |
251 | [[package]]
252 | name = "js-sys"
253 | version = "0.3.55"
254 | source = "registry+https://github.com/rust-lang/crates.io-index"
255 | checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
256 | dependencies = [
257 | "wasm-bindgen",
258 | ]
259 |
260 | [[package]]
261 | name = "lazy_static"
262 | version = "1.4.0"
263 | source = "registry+https://github.com/rust-lang/crates.io-index"
264 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
265 |
266 | [[package]]
267 | name = "libc"
268 | version = "0.2.108"
269 | source = "registry+https://github.com/rust-lang/crates.io-index"
270 | checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
271 |
272 | [[package]]
273 | name = "libwhen"
274 | version = "0.4.0"
275 | dependencies = [
276 | "chrono",
277 | "chrono-humanize",
278 | "chrono-tz",
279 | "localzone",
280 | "pest",
281 | "pest_derive",
282 | "serde",
283 | ]
284 |
285 | [[package]]
286 | name = "localzone"
287 | version = "0.2.0"
288 | source = "registry+https://github.com/rust-lang/crates.io-index"
289 | checksum = "7865f5a13ecb548180b8fe7e538d739090329fdb984eef9e9a16db214f216c6f"
290 | dependencies = [
291 | "js-sys",
292 | "windows",
293 | ]
294 |
295 | [[package]]
296 | name = "log"
297 | version = "0.4.14"
298 | source = "registry+https://github.com/rust-lang/crates.io-index"
299 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
300 | dependencies = [
301 | "cfg-if",
302 | ]
303 |
304 | [[package]]
305 | name = "maplit"
306 | version = "1.0.2"
307 | source = "registry+https://github.com/rust-lang/crates.io-index"
308 | checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
309 |
310 | [[package]]
311 | name = "memchr"
312 | version = "2.4.1"
313 | source = "registry+https://github.com/rust-lang/crates.io-index"
314 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
315 |
316 | [[package]]
317 | name = "num-integer"
318 | version = "0.1.44"
319 | source = "registry+https://github.com/rust-lang/crates.io-index"
320 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
321 | dependencies = [
322 | "autocfg",
323 | "num-traits",
324 | ]
325 |
326 | [[package]]
327 | name = "num-traits"
328 | version = "0.2.14"
329 | source = "registry+https://github.com/rust-lang/crates.io-index"
330 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
331 | dependencies = [
332 | "autocfg",
333 | ]
334 |
335 | [[package]]
336 | name = "once_cell"
337 | version = "1.8.0"
338 | source = "registry+https://github.com/rust-lang/crates.io-index"
339 | checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
340 |
341 | [[package]]
342 | name = "opaque-debug"
343 | version = "0.2.3"
344 | source = "registry+https://github.com/rust-lang/crates.io-index"
345 | checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
346 |
347 | [[package]]
348 | name = "os_str_bytes"
349 | version = "6.0.0"
350 | source = "registry+https://github.com/rust-lang/crates.io-index"
351 | checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
352 | dependencies = [
353 | "memchr",
354 | ]
355 |
356 | [[package]]
357 | name = "parse-zoneinfo"
358 | version = "0.3.0"
359 | source = "registry+https://github.com/rust-lang/crates.io-index"
360 | checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41"
361 | dependencies = [
362 | "regex",
363 | ]
364 |
365 | [[package]]
366 | name = "pest"
367 | version = "2.1.3"
368 | source = "registry+https://github.com/rust-lang/crates.io-index"
369 | checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
370 | dependencies = [
371 | "ucd-trie",
372 | ]
373 |
374 | [[package]]
375 | name = "pest_derive"
376 | version = "2.1.0"
377 | source = "registry+https://github.com/rust-lang/crates.io-index"
378 | checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
379 | dependencies = [
380 | "pest",
381 | "pest_generator",
382 | ]
383 |
384 | [[package]]
385 | name = "pest_generator"
386 | version = "2.1.3"
387 | source = "registry+https://github.com/rust-lang/crates.io-index"
388 | checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
389 | dependencies = [
390 | "pest",
391 | "pest_meta",
392 | "proc-macro2",
393 | "quote",
394 | "syn",
395 | ]
396 |
397 | [[package]]
398 | name = "pest_meta"
399 | version = "2.1.3"
400 | source = "registry+https://github.com/rust-lang/crates.io-index"
401 | checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
402 | dependencies = [
403 | "maplit",
404 | "pest",
405 | "sha-1",
406 | ]
407 |
408 | [[package]]
409 | name = "phf"
410 | version = "0.10.0"
411 | source = "registry+https://github.com/rust-lang/crates.io-index"
412 | checksum = "b9fc3db1018c4b59d7d582a739436478b6035138b6aecbce989fc91c3e98409f"
413 | dependencies = [
414 | "phf_shared",
415 | ]
416 |
417 | [[package]]
418 | name = "phf_codegen"
419 | version = "0.10.0"
420 | source = "registry+https://github.com/rust-lang/crates.io-index"
421 | checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
422 | dependencies = [
423 | "phf_generator",
424 | "phf_shared",
425 | ]
426 |
427 | [[package]]
428 | name = "phf_generator"
429 | version = "0.10.0"
430 | source = "registry+https://github.com/rust-lang/crates.io-index"
431 | checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
432 | dependencies = [
433 | "phf_shared",
434 | "rand",
435 | ]
436 |
437 | [[package]]
438 | name = "phf_shared"
439 | version = "0.10.0"
440 | source = "registry+https://github.com/rust-lang/crates.io-index"
441 | checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
442 | dependencies = [
443 | "siphasher",
444 | "uncased",
445 | ]
446 |
447 | [[package]]
448 | name = "ppv-lite86"
449 | version = "0.2.15"
450 | source = "registry+https://github.com/rust-lang/crates.io-index"
451 | checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
452 |
453 | [[package]]
454 | name = "proc-macro-error"
455 | version = "1.0.4"
456 | source = "registry+https://github.com/rust-lang/crates.io-index"
457 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
458 | dependencies = [
459 | "proc-macro-error-attr",
460 | "proc-macro2",
461 | "quote",
462 | "syn",
463 | "version_check",
464 | ]
465 |
466 | [[package]]
467 | name = "proc-macro-error-attr"
468 | version = "1.0.4"
469 | source = "registry+https://github.com/rust-lang/crates.io-index"
470 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
471 | dependencies = [
472 | "proc-macro2",
473 | "quote",
474 | "version_check",
475 | ]
476 |
477 | [[package]]
478 | name = "proc-macro2"
479 | version = "1.0.32"
480 | source = "registry+https://github.com/rust-lang/crates.io-index"
481 | checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
482 | dependencies = [
483 | "unicode-xid",
484 | ]
485 |
486 | [[package]]
487 | name = "quote"
488 | version = "1.0.10"
489 | source = "registry+https://github.com/rust-lang/crates.io-index"
490 | checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
491 | dependencies = [
492 | "proc-macro2",
493 | ]
494 |
495 | [[package]]
496 | name = "rand"
497 | version = "0.8.4"
498 | source = "registry+https://github.com/rust-lang/crates.io-index"
499 | checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
500 | dependencies = [
501 | "libc",
502 | "rand_chacha",
503 | "rand_core",
504 | "rand_hc",
505 | ]
506 |
507 | [[package]]
508 | name = "rand_chacha"
509 | version = "0.3.1"
510 | source = "registry+https://github.com/rust-lang/crates.io-index"
511 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
512 | dependencies = [
513 | "ppv-lite86",
514 | "rand_core",
515 | ]
516 |
517 | [[package]]
518 | name = "rand_core"
519 | version = "0.6.3"
520 | source = "registry+https://github.com/rust-lang/crates.io-index"
521 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
522 | dependencies = [
523 | "getrandom",
524 | ]
525 |
526 | [[package]]
527 | name = "rand_hc"
528 | version = "0.3.1"
529 | source = "registry+https://github.com/rust-lang/crates.io-index"
530 | checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
531 | dependencies = [
532 | "rand_core",
533 | ]
534 |
535 | [[package]]
536 | name = "regex"
537 | version = "1.5.4"
538 | source = "registry+https://github.com/rust-lang/crates.io-index"
539 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
540 | dependencies = [
541 | "regex-syntax",
542 | ]
543 |
544 | [[package]]
545 | name = "regex-syntax"
546 | version = "0.6.25"
547 | source = "registry+https://github.com/rust-lang/crates.io-index"
548 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
549 |
550 | [[package]]
551 | name = "ryu"
552 | version = "1.0.6"
553 | source = "registry+https://github.com/rust-lang/crates.io-index"
554 | checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568"
555 |
556 | [[package]]
557 | name = "serde"
558 | version = "1.0.131"
559 | source = "registry+https://github.com/rust-lang/crates.io-index"
560 | checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1"
561 | dependencies = [
562 | "serde_derive",
563 | ]
564 |
565 | [[package]]
566 | name = "serde_derive"
567 | version = "1.0.131"
568 | source = "registry+https://github.com/rust-lang/crates.io-index"
569 | checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2"
570 | dependencies = [
571 | "proc-macro2",
572 | "quote",
573 | "syn",
574 | ]
575 |
576 | [[package]]
577 | name = "serde_json"
578 | version = "1.0.72"
579 | source = "registry+https://github.com/rust-lang/crates.io-index"
580 | checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527"
581 | dependencies = [
582 | "itoa",
583 | "ryu",
584 | "serde",
585 | ]
586 |
587 | [[package]]
588 | name = "sha-1"
589 | version = "0.8.2"
590 | source = "registry+https://github.com/rust-lang/crates.io-index"
591 | checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
592 | dependencies = [
593 | "block-buffer",
594 | "digest",
595 | "fake-simd",
596 | "opaque-debug",
597 | ]
598 |
599 | [[package]]
600 | name = "siphasher"
601 | version = "0.3.7"
602 | source = "registry+https://github.com/rust-lang/crates.io-index"
603 | checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
604 |
605 | [[package]]
606 | name = "strsim"
607 | version = "0.10.0"
608 | source = "registry+https://github.com/rust-lang/crates.io-index"
609 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
610 |
611 | [[package]]
612 | name = "syn"
613 | version = "1.0.82"
614 | source = "registry+https://github.com/rust-lang/crates.io-index"
615 | checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
616 | dependencies = [
617 | "proc-macro2",
618 | "quote",
619 | "unicode-xid",
620 | ]
621 |
622 | [[package]]
623 | name = "termcolor"
624 | version = "1.1.2"
625 | source = "registry+https://github.com/rust-lang/crates.io-index"
626 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
627 | dependencies = [
628 | "winapi-util",
629 | ]
630 |
631 | [[package]]
632 | name = "terminal_size"
633 | version = "0.1.17"
634 | source = "registry+https://github.com/rust-lang/crates.io-index"
635 | checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
636 | dependencies = [
637 | "libc",
638 | "winapi",
639 | ]
640 |
641 | [[package]]
642 | name = "textwrap"
643 | version = "0.14.2"
644 | source = "registry+https://github.com/rust-lang/crates.io-index"
645 | checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
646 | dependencies = [
647 | "terminal_size",
648 | ]
649 |
650 | [[package]]
651 | name = "time"
652 | version = "0.1.44"
653 | source = "registry+https://github.com/rust-lang/crates.io-index"
654 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
655 | dependencies = [
656 | "libc",
657 | "wasi",
658 | "winapi",
659 | ]
660 |
661 | [[package]]
662 | name = "typenum"
663 | version = "1.14.0"
664 | source = "registry+https://github.com/rust-lang/crates.io-index"
665 | checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
666 |
667 | [[package]]
668 | name = "ucd-trie"
669 | version = "0.1.3"
670 | source = "registry+https://github.com/rust-lang/crates.io-index"
671 | checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
672 |
673 | [[package]]
674 | name = "uncased"
675 | version = "0.9.6"
676 | source = "registry+https://github.com/rust-lang/crates.io-index"
677 | checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0"
678 | dependencies = [
679 | "version_check",
680 | ]
681 |
682 | [[package]]
683 | name = "unicode-segmentation"
684 | version = "1.8.0"
685 | source = "registry+https://github.com/rust-lang/crates.io-index"
686 | checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
687 |
688 | [[package]]
689 | name = "unicode-width"
690 | version = "0.1.9"
691 | source = "registry+https://github.com/rust-lang/crates.io-index"
692 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
693 |
694 | [[package]]
695 | name = "unicode-xid"
696 | version = "0.2.2"
697 | source = "registry+https://github.com/rust-lang/crates.io-index"
698 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
699 |
700 | [[package]]
701 | name = "version_check"
702 | version = "0.9.3"
703 | source = "registry+https://github.com/rust-lang/crates.io-index"
704 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
705 |
706 | [[package]]
707 | name = "wasi"
708 | version = "0.10.0+wasi-snapshot-preview1"
709 | source = "registry+https://github.com/rust-lang/crates.io-index"
710 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
711 |
712 | [[package]]
713 | name = "wasm-bindgen"
714 | version = "0.2.78"
715 | source = "registry+https://github.com/rust-lang/crates.io-index"
716 | checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
717 | dependencies = [
718 | "cfg-if",
719 | "wasm-bindgen-macro",
720 | ]
721 |
722 | [[package]]
723 | name = "wasm-bindgen-backend"
724 | version = "0.2.78"
725 | source = "registry+https://github.com/rust-lang/crates.io-index"
726 | checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
727 | dependencies = [
728 | "bumpalo",
729 | "lazy_static",
730 | "log",
731 | "proc-macro2",
732 | "quote",
733 | "syn",
734 | "wasm-bindgen-shared",
735 | ]
736 |
737 | [[package]]
738 | name = "wasm-bindgen-macro"
739 | version = "0.2.78"
740 | source = "registry+https://github.com/rust-lang/crates.io-index"
741 | checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
742 | dependencies = [
743 | "quote",
744 | "wasm-bindgen-macro-support",
745 | ]
746 |
747 | [[package]]
748 | name = "wasm-bindgen-macro-support"
749 | version = "0.2.78"
750 | source = "registry+https://github.com/rust-lang/crates.io-index"
751 | checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
752 | dependencies = [
753 | "proc-macro2",
754 | "quote",
755 | "syn",
756 | "wasm-bindgen-backend",
757 | "wasm-bindgen-shared",
758 | ]
759 |
760 | [[package]]
761 | name = "wasm-bindgen-shared"
762 | version = "0.2.78"
763 | source = "registry+https://github.com/rust-lang/crates.io-index"
764 | checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
765 |
766 | [[package]]
767 | name = "when-cli"
768 | version = "0.4.0"
769 | dependencies = [
770 | "anyhow",
771 | "chrono",
772 | "chrono-tz",
773 | "clap",
774 | "console",
775 | "libwhen",
776 | "localzone",
777 | "pest",
778 | "pest_derive",
779 | "serde",
780 | "serde_json",
781 | ]
782 |
783 | [[package]]
784 | name = "winapi"
785 | version = "0.3.9"
786 | source = "registry+https://github.com/rust-lang/crates.io-index"
787 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
788 | dependencies = [
789 | "winapi-i686-pc-windows-gnu",
790 | "winapi-x86_64-pc-windows-gnu",
791 | ]
792 |
793 | [[package]]
794 | name = "winapi-i686-pc-windows-gnu"
795 | version = "0.4.0"
796 | source = "registry+https://github.com/rust-lang/crates.io-index"
797 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
798 |
799 | [[package]]
800 | name = "winapi-util"
801 | version = "0.1.5"
802 | source = "registry+https://github.com/rust-lang/crates.io-index"
803 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
804 | dependencies = [
805 | "winapi",
806 | ]
807 |
808 | [[package]]
809 | name = "winapi-x86_64-pc-windows-gnu"
810 | version = "0.4.0"
811 | source = "registry+https://github.com/rust-lang/crates.io-index"
812 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
813 |
814 | [[package]]
815 | name = "windows"
816 | version = "0.28.0"
817 | source = "registry+https://github.com/rust-lang/crates.io-index"
818 | checksum = "054d31561409bbf7e1ee4a4f0a1233ac2bb79cfadf2a398438a04d8dda69225f"
819 | dependencies = [
820 | "windows-sys",
821 | ]
822 |
823 | [[package]]
824 | name = "windows-sys"
825 | version = "0.28.0"
826 | source = "registry+https://github.com/rust-lang/crates.io-index"
827 | checksum = "82ca39602d5cbfa692c4b67e3bcbb2751477355141c1ed434c94da4186836ff6"
828 | dependencies = [
829 | "windows_aarch64_msvc",
830 | "windows_i686_gnu",
831 | "windows_i686_msvc",
832 | "windows_x86_64_gnu",
833 | "windows_x86_64_msvc",
834 | ]
835 |
836 | [[package]]
837 | name = "windows_aarch64_msvc"
838 | version = "0.28.0"
839 | source = "registry+https://github.com/rust-lang/crates.io-index"
840 | checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2"
841 |
842 | [[package]]
843 | name = "windows_i686_gnu"
844 | version = "0.28.0"
845 | source = "registry+https://github.com/rust-lang/crates.io-index"
846 | checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a"
847 |
848 | [[package]]
849 | name = "windows_i686_msvc"
850 | version = "0.28.0"
851 | source = "registry+https://github.com/rust-lang/crates.io-index"
852 | checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64"
853 |
854 | [[package]]
855 | name = "windows_x86_64_gnu"
856 | version = "0.28.0"
857 | source = "registry+https://github.com/rust-lang/crates.io-index"
858 | checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954"
859 |
860 | [[package]]
861 | name = "windows_x86_64_msvc"
862 | version = "0.28.0"
863 | source = "registry+https://github.com/rust-lang/crates.io-index"
864 | checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f"
865 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = ["libwhen", "cli"]
3 |
4 | [profile.release]
5 | opt-level = "s"
6 | lto = true
7 | codegen-units = 1
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | all: build
2 |
3 | build:
4 | @(cd cli; cargo build --all-features)
5 |
6 | test:
7 | @cargo test --all
8 |
9 | release:
10 | @(cd cli; cargo build --release)
11 |
12 | install:
13 | @cargo install --path=./cli
14 |
15 | format:
16 | @rustup component add rustfmt 2> /dev/null
17 | @cargo fmt --all
18 |
19 | format-check:
20 | @rustup component add rustfmt 2> /dev/null
21 | @cargo fmt --all -- --check
22 |
23 | lint:
24 | @rustup component add clippy 2> /dev/null
25 | @cargo clippy
26 |
27 | web-dev:
28 | @cd web; wasm-pack build
29 | @cd web/www; npm run start
30 |
31 | web-dist:
32 | @rm -rf web/www/dist
33 | @cd web; wasm-pack build --release
34 | @cd web/www; npm run build
35 |
36 | .PHONY: all test format format-check lint clean-data build release web-dev web-dist
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
when: a timezone utility for the command line
5 |
6 |
7 | [](https://github.com/mitsuhiko/when/actions?query=workflow%3ATests)
8 | [](https://crates.io/crates/when-cli)
9 | [](https://github.com/mitsuhiko/when/blob/main/LICENSE)
10 |
11 | ```
12 | $ when "now in vienna"
13 | ```
14 |
15 | `when` is a small utility which tells you what time it is somewhere or what some time is somewhere.
16 | You can use it from the command line or [uses it online from the browser](https://mitsuhiko.github.io/when/).
17 |
18 | **These are some input examples**:
19 |
20 | * `now`
21 | * `2 hours ago in yyz`
22 | * `5pm in yyz -> sfo`
23 | * `5pm in vienna -> london`
24 | * `4pm on 17.05.2021 in vienna -> tokyo`
25 | * `4pm yesterday in vienna -> vienna va`
26 | * `in 4 hours in san francisco`
27 | * `2pm in 2 days in new delhi`
28 | * `now in yyz -> sfo -> vie -> lhr`
29 | * `unix 1639067620 in tokyo`
30 |
31 | ## Installation
32 |
33 | Conveniently via cargo:
34 |
35 | ```
36 | $ cargo install when-cli
37 | ```
38 |
39 | There is also an [online version](https://mitsuhiko.github.io/when/) you can use
40 | from your browser.
41 |
42 | Note that this project requires a Rust 2021 compatible compiler (1.56.0 or
43 | later). Attempting to install this package on an older compiler will result
44 | in compilation errors (``feature `edition2021` is required``). If you're
45 | using rustup make sure to update (`rustup update`), you might be on an older
46 | version.
47 |
48 | ## Usage
49 |
50 | Basically takes a single argument which is a string which describes the format
51 | in roughly this syntax. Both locations are optional. The "local" location always
52 | refers to the current machine's timezone.
53 |
54 | ```
55 | time and date in location -> other location
56 | ```
57 |
58 | Multiple locations can be suplied by using the arrow operator multiple times. This
59 | means you can do things like `now in yyz -> sfo -> vie`.
60 |
61 | Time and date can be provided roughly like this:
62 |
63 | * `2:30pm`, `14:30`, `7:00`, `now`
64 | * `14:30 tomorrow`
65 | * `14:30`
66 | * `17:00 on 20.05.` (DD.MM.)
67 | * `17:00 on 20.05.2020` (DD.MM.YYYY)
68 | * relative times (`in 4 hours` or `4 hours ago`)
69 | * unix timestamps (`unix:TS` or `unix TS`)
70 |
71 | For locations many major cities are supported as well as common timezone names
72 | like `Europe/Vienna`. A certain amount of disambiugation is possible with city
73 | names. For instance `Vienna VA` (Virginia) is different than `Vienna AT`
74 | (Austria).
75 |
--------------------------------------------------------------------------------
/assets/hello.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mitsuhiko/when/4cc12ce4dcd5f1a155ecb866c70809b2332b5172/assets/hello.png
--------------------------------------------------------------------------------
/cli/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "anyhow"
7 | version = "1.0.51"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203"
10 |
11 | [[package]]
12 | name = "atty"
13 | version = "0.2.14"
14 | source = "registry+https://github.com/rust-lang/crates.io-index"
15 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
16 | dependencies = [
17 | "hermit-abi",
18 | "libc",
19 | "winapi",
20 | ]
21 |
22 | [[package]]
23 | name = "autocfg"
24 | version = "1.0.1"
25 | source = "registry+https://github.com/rust-lang/crates.io-index"
26 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
27 |
28 | [[package]]
29 | name = "bitflags"
30 | version = "1.3.2"
31 | source = "registry+https://github.com/rust-lang/crates.io-index"
32 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
33 |
34 | [[package]]
35 | name = "block-buffer"
36 | version = "0.7.3"
37 | source = "registry+https://github.com/rust-lang/crates.io-index"
38 | checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
39 | dependencies = [
40 | "block-padding",
41 | "byte-tools",
42 | "byteorder",
43 | "generic-array",
44 | ]
45 |
46 | [[package]]
47 | name = "block-padding"
48 | version = "0.1.5"
49 | source = "registry+https://github.com/rust-lang/crates.io-index"
50 | checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
51 | dependencies = [
52 | "byte-tools",
53 | ]
54 |
55 | [[package]]
56 | name = "bumpalo"
57 | version = "3.8.0"
58 | source = "registry+https://github.com/rust-lang/crates.io-index"
59 | checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
60 |
61 | [[package]]
62 | name = "byte-tools"
63 | version = "0.3.1"
64 | source = "registry+https://github.com/rust-lang/crates.io-index"
65 | checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
66 |
67 | [[package]]
68 | name = "byteorder"
69 | version = "1.4.3"
70 | source = "registry+https://github.com/rust-lang/crates.io-index"
71 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
72 |
73 | [[package]]
74 | name = "cfg-if"
75 | version = "1.0.0"
76 | source = "registry+https://github.com/rust-lang/crates.io-index"
77 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
78 |
79 | [[package]]
80 | name = "chrono"
81 | version = "0.4.19"
82 | source = "registry+https://github.com/rust-lang/crates.io-index"
83 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
84 | dependencies = [
85 | "libc",
86 | "num-integer",
87 | "num-traits",
88 | "serde",
89 | "time",
90 | "winapi",
91 | ]
92 |
93 | [[package]]
94 | name = "chrono-tz"
95 | version = "0.6.1"
96 | source = "registry+https://github.com/rust-lang/crates.io-index"
97 | checksum = "58549f1842da3080ce63002102d5bc954c7bc843d4f47818e642abdc36253552"
98 | dependencies = [
99 | "chrono",
100 | "chrono-tz-build",
101 | "phf",
102 | ]
103 |
104 | [[package]]
105 | name = "chrono-tz-build"
106 | version = "0.0.2"
107 | source = "registry+https://github.com/rust-lang/crates.io-index"
108 | checksum = "db058d493fb2f65f41861bfed7e3fe6335264a9f0f92710cab5bdf01fef09069"
109 | dependencies = [
110 | "parse-zoneinfo",
111 | "phf",
112 | "phf_codegen",
113 | ]
114 |
115 | [[package]]
116 | name = "clap"
117 | version = "3.0.0-rc.3"
118 | source = "registry+https://github.com/rust-lang/crates.io-index"
119 | checksum = "098d281b47bf725a0bddd829e0070ee76560faab8af123050a86c440d7f0a1fd"
120 | dependencies = [
121 | "atty",
122 | "bitflags",
123 | "clap_derive",
124 | "indexmap",
125 | "lazy_static",
126 | "os_str_bytes",
127 | "strsim",
128 | "termcolor",
129 | "terminal_size",
130 | "textwrap",
131 | ]
132 |
133 | [[package]]
134 | name = "clap_derive"
135 | version = "3.0.0-rc.3"
136 | source = "registry+https://github.com/rust-lang/crates.io-index"
137 | checksum = "26de8102ffb96701066cea36f9a104285b67fbcc302a520640289d476c15ed8a"
138 | dependencies = [
139 | "heck",
140 | "proc-macro-error",
141 | "proc-macro2",
142 | "quote",
143 | "syn",
144 | ]
145 |
146 | [[package]]
147 | name = "console"
148 | version = "0.15.0"
149 | source = "registry+https://github.com/rust-lang/crates.io-index"
150 | checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31"
151 | dependencies = [
152 | "encode_unicode",
153 | "libc",
154 | "once_cell",
155 | "regex",
156 | "terminal_size",
157 | "unicode-width",
158 | "winapi",
159 | ]
160 |
161 | [[package]]
162 | name = "digest"
163 | version = "0.8.1"
164 | source = "registry+https://github.com/rust-lang/crates.io-index"
165 | checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
166 | dependencies = [
167 | "generic-array",
168 | ]
169 |
170 | [[package]]
171 | name = "encode_unicode"
172 | version = "0.3.6"
173 | source = "registry+https://github.com/rust-lang/crates.io-index"
174 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
175 |
176 | [[package]]
177 | name = "fake-simd"
178 | version = "0.1.2"
179 | source = "registry+https://github.com/rust-lang/crates.io-index"
180 | checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
181 |
182 | [[package]]
183 | name = "generic-array"
184 | version = "0.12.4"
185 | source = "registry+https://github.com/rust-lang/crates.io-index"
186 | checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
187 | dependencies = [
188 | "typenum",
189 | ]
190 |
191 | [[package]]
192 | name = "getrandom"
193 | version = "0.2.3"
194 | source = "registry+https://github.com/rust-lang/crates.io-index"
195 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
196 | dependencies = [
197 | "cfg-if",
198 | "libc",
199 | "wasi",
200 | ]
201 |
202 | [[package]]
203 | name = "hashbrown"
204 | version = "0.11.2"
205 | source = "registry+https://github.com/rust-lang/crates.io-index"
206 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
207 |
208 | [[package]]
209 | name = "heck"
210 | version = "0.3.3"
211 | source = "registry+https://github.com/rust-lang/crates.io-index"
212 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
213 | dependencies = [
214 | "unicode-segmentation",
215 | ]
216 |
217 | [[package]]
218 | name = "hermit-abi"
219 | version = "0.1.20"
220 | source = "registry+https://github.com/rust-lang/crates.io-index"
221 | checksum = "c7a30908dbce072eca83216eab939d2290080e00ca71611b96a09e5cdce5f3fa"
222 | dependencies = [
223 | "libc",
224 | ]
225 |
226 | [[package]]
227 | name = "indexmap"
228 | version = "1.7.0"
229 | source = "registry+https://github.com/rust-lang/crates.io-index"
230 | checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
231 | dependencies = [
232 | "autocfg",
233 | "hashbrown",
234 | ]
235 |
236 | [[package]]
237 | name = "itoa"
238 | version = "0.4.8"
239 | source = "registry+https://github.com/rust-lang/crates.io-index"
240 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
241 |
242 | [[package]]
243 | name = "js-sys"
244 | version = "0.3.55"
245 | source = "registry+https://github.com/rust-lang/crates.io-index"
246 | checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
247 | dependencies = [
248 | "wasm-bindgen",
249 | ]
250 |
251 | [[package]]
252 | name = "lazy_static"
253 | version = "1.4.0"
254 | source = "registry+https://github.com/rust-lang/crates.io-index"
255 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
256 |
257 | [[package]]
258 | name = "libc"
259 | version = "0.2.110"
260 | source = "registry+https://github.com/rust-lang/crates.io-index"
261 | checksum = "b58a4469763e4e3a906c4ed786e1c70512d16aa88f84dded826da42640fc6a1c"
262 |
263 | [[package]]
264 | name = "localzone"
265 | version = "0.2.0"
266 | source = "registry+https://github.com/rust-lang/crates.io-index"
267 | checksum = "7865f5a13ecb548180b8fe7e538d739090329fdb984eef9e9a16db214f216c6f"
268 | dependencies = [
269 | "js-sys",
270 | "windows",
271 | ]
272 |
273 | [[package]]
274 | name = "log"
275 | version = "0.4.14"
276 | source = "registry+https://github.com/rust-lang/crates.io-index"
277 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
278 | dependencies = [
279 | "cfg-if",
280 | ]
281 |
282 | [[package]]
283 | name = "maplit"
284 | version = "1.0.2"
285 | source = "registry+https://github.com/rust-lang/crates.io-index"
286 | checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
287 |
288 | [[package]]
289 | name = "memchr"
290 | version = "2.4.1"
291 | source = "registry+https://github.com/rust-lang/crates.io-index"
292 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
293 |
294 | [[package]]
295 | name = "num-integer"
296 | version = "0.1.44"
297 | source = "registry+https://github.com/rust-lang/crates.io-index"
298 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
299 | dependencies = [
300 | "autocfg",
301 | "num-traits",
302 | ]
303 |
304 | [[package]]
305 | name = "num-traits"
306 | version = "0.2.14"
307 | source = "registry+https://github.com/rust-lang/crates.io-index"
308 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
309 | dependencies = [
310 | "autocfg",
311 | ]
312 |
313 | [[package]]
314 | name = "once_cell"
315 | version = "1.8.0"
316 | source = "registry+https://github.com/rust-lang/crates.io-index"
317 | checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
318 |
319 | [[package]]
320 | name = "opaque-debug"
321 | version = "0.2.3"
322 | source = "registry+https://github.com/rust-lang/crates.io-index"
323 | checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
324 |
325 | [[package]]
326 | name = "os_str_bytes"
327 | version = "6.0.0"
328 | source = "registry+https://github.com/rust-lang/crates.io-index"
329 | checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
330 | dependencies = [
331 | "memchr",
332 | ]
333 |
334 | [[package]]
335 | name = "parse-zoneinfo"
336 | version = "0.3.0"
337 | source = "registry+https://github.com/rust-lang/crates.io-index"
338 | checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41"
339 | dependencies = [
340 | "regex",
341 | ]
342 |
343 | [[package]]
344 | name = "pest"
345 | version = "2.1.3"
346 | source = "registry+https://github.com/rust-lang/crates.io-index"
347 | checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
348 | dependencies = [
349 | "ucd-trie",
350 | ]
351 |
352 | [[package]]
353 | name = "pest_derive"
354 | version = "2.1.0"
355 | source = "registry+https://github.com/rust-lang/crates.io-index"
356 | checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
357 | dependencies = [
358 | "pest",
359 | "pest_generator",
360 | ]
361 |
362 | [[package]]
363 | name = "pest_generator"
364 | version = "2.1.3"
365 | source = "registry+https://github.com/rust-lang/crates.io-index"
366 | checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
367 | dependencies = [
368 | "pest",
369 | "pest_meta",
370 | "proc-macro2",
371 | "quote",
372 | "syn",
373 | ]
374 |
375 | [[package]]
376 | name = "pest_meta"
377 | version = "2.1.3"
378 | source = "registry+https://github.com/rust-lang/crates.io-index"
379 | checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
380 | dependencies = [
381 | "maplit",
382 | "pest",
383 | "sha-1",
384 | ]
385 |
386 | [[package]]
387 | name = "phf"
388 | version = "0.10.0"
389 | source = "registry+https://github.com/rust-lang/crates.io-index"
390 | checksum = "b9fc3db1018c4b59d7d582a739436478b6035138b6aecbce989fc91c3e98409f"
391 | dependencies = [
392 | "phf_shared",
393 | ]
394 |
395 | [[package]]
396 | name = "phf_codegen"
397 | version = "0.10.0"
398 | source = "registry+https://github.com/rust-lang/crates.io-index"
399 | checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
400 | dependencies = [
401 | "phf_generator",
402 | "phf_shared",
403 | ]
404 |
405 | [[package]]
406 | name = "phf_generator"
407 | version = "0.10.0"
408 | source = "registry+https://github.com/rust-lang/crates.io-index"
409 | checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
410 | dependencies = [
411 | "phf_shared",
412 | "rand",
413 | ]
414 |
415 | [[package]]
416 | name = "phf_shared"
417 | version = "0.10.0"
418 | source = "registry+https://github.com/rust-lang/crates.io-index"
419 | checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
420 | dependencies = [
421 | "siphasher",
422 | "uncased",
423 | ]
424 |
425 | [[package]]
426 | name = "ppv-lite86"
427 | version = "0.2.15"
428 | source = "registry+https://github.com/rust-lang/crates.io-index"
429 | checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
430 |
431 | [[package]]
432 | name = "proc-macro-error"
433 | version = "1.0.4"
434 | source = "registry+https://github.com/rust-lang/crates.io-index"
435 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
436 | dependencies = [
437 | "proc-macro-error-attr",
438 | "proc-macro2",
439 | "quote",
440 | "syn",
441 | "version_check",
442 | ]
443 |
444 | [[package]]
445 | name = "proc-macro-error-attr"
446 | version = "1.0.4"
447 | source = "registry+https://github.com/rust-lang/crates.io-index"
448 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
449 | dependencies = [
450 | "proc-macro2",
451 | "quote",
452 | "version_check",
453 | ]
454 |
455 | [[package]]
456 | name = "proc-macro2"
457 | version = "1.0.33"
458 | source = "registry+https://github.com/rust-lang/crates.io-index"
459 | checksum = "fb37d2df5df740e582f28f8560cf425f52bb267d872fe58358eadb554909f07a"
460 | dependencies = [
461 | "unicode-xid",
462 | ]
463 |
464 | [[package]]
465 | name = "quote"
466 | version = "1.0.10"
467 | source = "registry+https://github.com/rust-lang/crates.io-index"
468 | checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
469 | dependencies = [
470 | "proc-macro2",
471 | ]
472 |
473 | [[package]]
474 | name = "rand"
475 | version = "0.8.4"
476 | source = "registry+https://github.com/rust-lang/crates.io-index"
477 | checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
478 | dependencies = [
479 | "libc",
480 | "rand_chacha",
481 | "rand_core",
482 | "rand_hc",
483 | ]
484 |
485 | [[package]]
486 | name = "rand_chacha"
487 | version = "0.3.1"
488 | source = "registry+https://github.com/rust-lang/crates.io-index"
489 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
490 | dependencies = [
491 | "ppv-lite86",
492 | "rand_core",
493 | ]
494 |
495 | [[package]]
496 | name = "rand_core"
497 | version = "0.6.3"
498 | source = "registry+https://github.com/rust-lang/crates.io-index"
499 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
500 | dependencies = [
501 | "getrandom",
502 | ]
503 |
504 | [[package]]
505 | name = "rand_hc"
506 | version = "0.3.1"
507 | source = "registry+https://github.com/rust-lang/crates.io-index"
508 | checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
509 | dependencies = [
510 | "rand_core",
511 | ]
512 |
513 | [[package]]
514 | name = "regex"
515 | version = "1.5.4"
516 | source = "registry+https://github.com/rust-lang/crates.io-index"
517 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
518 | dependencies = [
519 | "regex-syntax",
520 | ]
521 |
522 | [[package]]
523 | name = "regex-syntax"
524 | version = "0.6.25"
525 | source = "registry+https://github.com/rust-lang/crates.io-index"
526 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
527 |
528 | [[package]]
529 | name = "ryu"
530 | version = "1.0.6"
531 | source = "registry+https://github.com/rust-lang/crates.io-index"
532 | checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568"
533 |
534 | [[package]]
535 | name = "serde"
536 | version = "1.0.131"
537 | source = "registry+https://github.com/rust-lang/crates.io-index"
538 | checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1"
539 | dependencies = [
540 | "serde_derive",
541 | ]
542 |
543 | [[package]]
544 | name = "serde_derive"
545 | version = "1.0.131"
546 | source = "registry+https://github.com/rust-lang/crates.io-index"
547 | checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2"
548 | dependencies = [
549 | "proc-macro2",
550 | "quote",
551 | "syn",
552 | ]
553 |
554 | [[package]]
555 | name = "serde_json"
556 | version = "1.0.72"
557 | source = "registry+https://github.com/rust-lang/crates.io-index"
558 | checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527"
559 | dependencies = [
560 | "itoa",
561 | "ryu",
562 | "serde",
563 | ]
564 |
565 | [[package]]
566 | name = "sha-1"
567 | version = "0.8.2"
568 | source = "registry+https://github.com/rust-lang/crates.io-index"
569 | checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
570 | dependencies = [
571 | "block-buffer",
572 | "digest",
573 | "fake-simd",
574 | "opaque-debug",
575 | ]
576 |
577 | [[package]]
578 | name = "siphasher"
579 | version = "0.3.7"
580 | source = "registry+https://github.com/rust-lang/crates.io-index"
581 | checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
582 |
583 | [[package]]
584 | name = "strsim"
585 | version = "0.10.0"
586 | source = "registry+https://github.com/rust-lang/crates.io-index"
587 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
588 |
589 | [[package]]
590 | name = "syn"
591 | version = "1.0.82"
592 | source = "registry+https://github.com/rust-lang/crates.io-index"
593 | checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
594 | dependencies = [
595 | "proc-macro2",
596 | "quote",
597 | "unicode-xid",
598 | ]
599 |
600 | [[package]]
601 | name = "termcolor"
602 | version = "1.1.2"
603 | source = "registry+https://github.com/rust-lang/crates.io-index"
604 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
605 | dependencies = [
606 | "winapi-util",
607 | ]
608 |
609 | [[package]]
610 | name = "terminal_size"
611 | version = "0.1.17"
612 | source = "registry+https://github.com/rust-lang/crates.io-index"
613 | checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
614 | dependencies = [
615 | "libc",
616 | "winapi",
617 | ]
618 |
619 | [[package]]
620 | name = "textwrap"
621 | version = "0.14.2"
622 | source = "registry+https://github.com/rust-lang/crates.io-index"
623 | checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
624 | dependencies = [
625 | "terminal_size",
626 | ]
627 |
628 | [[package]]
629 | name = "time"
630 | version = "0.1.44"
631 | source = "registry+https://github.com/rust-lang/crates.io-index"
632 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
633 | dependencies = [
634 | "libc",
635 | "wasi",
636 | "winapi",
637 | ]
638 |
639 | [[package]]
640 | name = "typenum"
641 | version = "1.14.0"
642 | source = "registry+https://github.com/rust-lang/crates.io-index"
643 | checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
644 |
645 | [[package]]
646 | name = "ucd-trie"
647 | version = "0.1.3"
648 | source = "registry+https://github.com/rust-lang/crates.io-index"
649 | checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
650 |
651 | [[package]]
652 | name = "uncased"
653 | version = "0.9.6"
654 | source = "registry+https://github.com/rust-lang/crates.io-index"
655 | checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0"
656 | dependencies = [
657 | "version_check",
658 | ]
659 |
660 | [[package]]
661 | name = "unicode-segmentation"
662 | version = "1.8.0"
663 | source = "registry+https://github.com/rust-lang/crates.io-index"
664 | checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
665 |
666 | [[package]]
667 | name = "unicode-width"
668 | version = "0.1.9"
669 | source = "registry+https://github.com/rust-lang/crates.io-index"
670 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
671 |
672 | [[package]]
673 | name = "unicode-xid"
674 | version = "0.2.2"
675 | source = "registry+https://github.com/rust-lang/crates.io-index"
676 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
677 |
678 | [[package]]
679 | name = "version_check"
680 | version = "0.9.3"
681 | source = "registry+https://github.com/rust-lang/crates.io-index"
682 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
683 |
684 | [[package]]
685 | name = "wasi"
686 | version = "0.10.0+wasi-snapshot-preview1"
687 | source = "registry+https://github.com/rust-lang/crates.io-index"
688 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
689 |
690 | [[package]]
691 | name = "wasm-bindgen"
692 | version = "0.2.78"
693 | source = "registry+https://github.com/rust-lang/crates.io-index"
694 | checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
695 | dependencies = [
696 | "cfg-if",
697 | "wasm-bindgen-macro",
698 | ]
699 |
700 | [[package]]
701 | name = "wasm-bindgen-backend"
702 | version = "0.2.78"
703 | source = "registry+https://github.com/rust-lang/crates.io-index"
704 | checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
705 | dependencies = [
706 | "bumpalo",
707 | "lazy_static",
708 | "log",
709 | "proc-macro2",
710 | "quote",
711 | "syn",
712 | "wasm-bindgen-shared",
713 | ]
714 |
715 | [[package]]
716 | name = "wasm-bindgen-macro"
717 | version = "0.2.78"
718 | source = "registry+https://github.com/rust-lang/crates.io-index"
719 | checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
720 | dependencies = [
721 | "quote",
722 | "wasm-bindgen-macro-support",
723 | ]
724 |
725 | [[package]]
726 | name = "wasm-bindgen-macro-support"
727 | version = "0.2.78"
728 | source = "registry+https://github.com/rust-lang/crates.io-index"
729 | checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
730 | dependencies = [
731 | "proc-macro2",
732 | "quote",
733 | "syn",
734 | "wasm-bindgen-backend",
735 | "wasm-bindgen-shared",
736 | ]
737 |
738 | [[package]]
739 | name = "wasm-bindgen-shared"
740 | version = "0.2.78"
741 | source = "registry+https://github.com/rust-lang/crates.io-index"
742 | checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
743 |
744 | [[package]]
745 | name = "when-cli"
746 | version = "0.3.0"
747 | dependencies = [
748 | "anyhow",
749 | "chrono",
750 | "chrono-tz",
751 | "clap",
752 | "console",
753 | "localzone",
754 | "pest",
755 | "pest_derive",
756 | "serde",
757 | "serde_json",
758 | ]
759 |
760 | [[package]]
761 | name = "winapi"
762 | version = "0.3.9"
763 | source = "registry+https://github.com/rust-lang/crates.io-index"
764 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
765 | dependencies = [
766 | "winapi-i686-pc-windows-gnu",
767 | "winapi-x86_64-pc-windows-gnu",
768 | ]
769 |
770 | [[package]]
771 | name = "winapi-i686-pc-windows-gnu"
772 | version = "0.4.0"
773 | source = "registry+https://github.com/rust-lang/crates.io-index"
774 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
775 |
776 | [[package]]
777 | name = "winapi-util"
778 | version = "0.1.5"
779 | source = "registry+https://github.com/rust-lang/crates.io-index"
780 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
781 | dependencies = [
782 | "winapi",
783 | ]
784 |
785 | [[package]]
786 | name = "winapi-x86_64-pc-windows-gnu"
787 | version = "0.4.0"
788 | source = "registry+https://github.com/rust-lang/crates.io-index"
789 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
790 |
791 | [[package]]
792 | name = "windows"
793 | version = "0.28.0"
794 | source = "registry+https://github.com/rust-lang/crates.io-index"
795 | checksum = "054d31561409bbf7e1ee4a4f0a1233ac2bb79cfadf2a398438a04d8dda69225f"
796 | dependencies = [
797 | "windows-sys",
798 | ]
799 |
800 | [[package]]
801 | name = "windows-sys"
802 | version = "0.28.0"
803 | source = "registry+https://github.com/rust-lang/crates.io-index"
804 | checksum = "82ca39602d5cbfa692c4b67e3bcbb2751477355141c1ed434c94da4186836ff6"
805 | dependencies = [
806 | "windows_aarch64_msvc",
807 | "windows_i686_gnu",
808 | "windows_i686_msvc",
809 | "windows_x86_64_gnu",
810 | "windows_x86_64_msvc",
811 | ]
812 |
813 | [[package]]
814 | name = "windows_aarch64_msvc"
815 | version = "0.28.0"
816 | source = "registry+https://github.com/rust-lang/crates.io-index"
817 | checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2"
818 |
819 | [[package]]
820 | name = "windows_i686_gnu"
821 | version = "0.28.0"
822 | source = "registry+https://github.com/rust-lang/crates.io-index"
823 | checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a"
824 |
825 | [[package]]
826 | name = "windows_i686_msvc"
827 | version = "0.28.0"
828 | source = "registry+https://github.com/rust-lang/crates.io-index"
829 | checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64"
830 |
831 | [[package]]
832 | name = "windows_x86_64_gnu"
833 | version = "0.28.0"
834 | source = "registry+https://github.com/rust-lang/crates.io-index"
835 | checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954"
836 |
837 | [[package]]
838 | name = "windows_x86_64_msvc"
839 | version = "0.28.0"
840 | source = "registry+https://github.com/rust-lang/crates.io-index"
841 | checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f"
842 |
--------------------------------------------------------------------------------
/cli/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "when-cli"
3 | version = "0.4.0"
4 | edition = "2021"
5 | authors = ["Armin Ronacher "]
6 | license = "Apache-2.0"
7 | description = "a command line tool for converting times between timezones"
8 | repository = "https://github.com/mitsuhiko/when"
9 | keywords = ["timezone", "convert", "cli"]
10 | readme = "README.md"
11 | rust-version = "1.56.0"
12 |
13 | [[bin]]
14 | name = "when"
15 | path = "src/main.rs"
16 |
17 | [dependencies]
18 | libwhen = { version = "0.4.0", path = "../libwhen" }
19 | anyhow = "1.0.51"
20 | chrono = { version = "0.4.19", features = ["serde"] }
21 | chrono-tz = "0.6.1"
22 | clap = { version = "3.0.0-rc.0", features = ["color", "derive", "cargo", "wrap_help"] }
23 | console = "0.15.0"
24 | localzone = "0.2.0"
25 | pest = "2.1.3"
26 | pest_derive = "2.1.0"
27 | serde = { version = "1.0.130", features = ["derive"] }
28 | serde_json = "1.0.72"
29 |
--------------------------------------------------------------------------------
/cli/LICENSE:
--------------------------------------------------------------------------------
1 | ../LICENSE
--------------------------------------------------------------------------------
/cli/README.md:
--------------------------------------------------------------------------------
1 | ../README.md
--------------------------------------------------------------------------------
/cli/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::fmt;
2 |
3 | use anyhow::bail;
4 | use chrono::{DateTime, Utc};
5 | use chrono_tz::Tz;
6 | use clap::Parser;
7 | use console::style;
8 |
9 | use libwhen::{get_time_of_day, InputExpr, LocationKind, TimeAtLocation};
10 |
11 | /// A small utility to convert times from the command line.
12 | ///
13 | /// When takes a time and date expression and helps converting it into
14 | /// different timezones. If no arguments are supplied the current time
15 | /// in the current location is returned.
16 | ///
17 | /// The basic syntax for the expression is "time_spec [in location_spec]".
18 | /// Translations between locations is done by using the "->" operator.
19 | ///
20 | /// For instance "2pm in vie -> yyz" takes 14:00 in vienna time and
21 | /// translates it to toronto (airport). It then prints out both
22 | /// timestamps on stdout with additional information.
23 | ///
24 | /// For more examples see https://github.com/mitsuhiko/when
25 | #[derive(Parser)]
26 | #[clap(version = clap::crate_version!(), max_term_width = 100)]
27 | struct Cli {
28 | /// use short output.
29 | ///
30 | /// When short output is enabled one line per timezone is returned.
31 | #[clap(short = 's', long = "short")]
32 | short: bool,
33 |
34 | /// controls when to use colors. Choices are `auto`, `never`, `always`.
35 | #[clap(long = "colors")]
36 | colors: Option,
37 |
38 | /// output in JSON format.
39 | #[clap(long = "json")]
40 | json: bool,
41 |
42 | /// returns a list of all known IANA/Olson timezones.
43 | #[clap(long = "list-timezones")]
44 | list_timezones: bool,
45 |
46 | /// the input expression to evaluate.
47 | ///
48 | /// If this is not supplied then "now" is assumed to return the time
49 | /// in the current timezone.
50 | expr: Option,
51 | }
52 |
53 | pub struct ZoneOffset(DateTime);
54 |
55 | impl fmt::Display for ZoneOffset {
56 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 | let abbrev = self.0.format("%Z").to_string();
58 | if abbrev.chars().all(|x| x.is_ascii_alphabetic()) {
59 | write!(f, "{}; {}", abbrev, self.0.format("%z"))?
60 | } else {
61 | write!(f, "{}", self.0.format("%z"))?
62 | }
63 | Ok(())
64 | }
65 | }
66 |
67 | fn print_date(tod: &TimeAtLocation, now: DateTime) {
68 | let date = tod.datetime();
69 | let zone = tod.zone();
70 | let adjusted = date.with_timezone(&zone.tz());
71 | println!(
72 | "time: {} ({}; {})",
73 | style(adjusted.format("%H:%M:%S")).bold().cyan(),
74 | tod.relative_to_human(now),
75 | get_time_of_day(adjusted),
76 | );
77 | println!(
78 | "date: {} ({})",
79 | style(adjusted.format("%Y-%m-%d")).yellow(),
80 | style(adjusted.format("%A")),
81 | );
82 | println!(
83 | "zone: {} ({})",
84 | style(zone.tz().name()).underlined(),
85 | ZoneOffset(adjusted),
86 | );
87 | if zone.kind() != LocationKind::Timezone {
88 | print!("location: {}", style(zone.name()).bold());
89 | print!(" (");
90 | let mut with_code = false;
91 | if let Some(code) = zone.admin_code() {
92 | print!("{}", code);
93 | with_code = true;
94 | }
95 | if let Some(country) = zone.country() {
96 | if with_code {
97 | print!("; ");
98 | }
99 | print!("{}", country);
100 | }
101 | print!(")");
102 | println!();
103 | }
104 | }
105 |
106 | fn list_timezones() -> Result<(), anyhow::Error> {
107 | let now = Utc::now();
108 | let mut zone_list = Vec::new();
109 | for zone in chrono_tz::TZ_VARIANTS {
110 | let there = now.with_timezone(&zone);
111 | zone_list.push((zone, there));
112 | }
113 | zone_list.sort_by_key(|x| x.0.name());
114 |
115 | for (zone, there) in zone_list {
116 | println!("{} ({})", zone.name(), ZoneOffset(there));
117 | }
118 |
119 | Ok(())
120 | }
121 |
122 | pub fn execute() -> Result<(), anyhow::Error> {
123 | let cli = Cli::parse();
124 |
125 | match cli.colors.as_deref() {
126 | None | Some("auto") => {}
127 | Some("always") => console::set_colors_enabled(true),
128 | Some("never") => console::set_colors_enabled(false),
129 | Some(other) => bail!("unknown value for --colors ({})", other),
130 | };
131 |
132 | if cli.list_timezones {
133 | return list_timezones();
134 | }
135 |
136 | let expr = InputExpr::parse(cli.expr.as_deref().unwrap_or("now"))?;
137 | let timestamps = expr.process()?;
138 |
139 | if cli.json {
140 | println!("{}", serde_json::to_string_pretty(×tamps).unwrap());
141 | } else if cli.short {
142 | for t in timestamps.iter() {
143 | println!(
144 | "{} ({})",
145 | t.datetime().format("%Y-%m-%d %H:%M:%S %z"),
146 | t.zone()
147 | );
148 | }
149 | } else {
150 | let now = Utc::now();
151 | for (idx, t) in timestamps.iter().enumerate() {
152 | if idx > 0 {
153 | println!();
154 | }
155 | print_date(t, now);
156 | }
157 | }
158 |
159 | Ok(())
160 | }
161 |
162 | fn main() {
163 | match execute() {
164 | Ok(()) => {}
165 | Err(err) => {
166 | eprintln!("error: {}", err);
167 | std::process::exit(1);
168 | }
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/libwhen/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "libwhen"
3 | version = "0.4.0"
4 | edition = "2021"
5 | authors = ["Armin Ronacher "]
6 | license = "Apache-2.0"
7 | description = "utility crate for the when command line tool"
8 | repository = "https://github.com/mitsuhiko/when"
9 | keywords = ["timezone", "convert", "cli"]
10 | readme = "README.md"
11 | rust-version = "1.56.0"
12 |
13 | [dependencies]
14 | chrono = { version = "0.4.19", features = ["serde"] }
15 | chrono-humanize = "0.2.1"
16 | chrono-tz = "0.6.1"
17 | localzone = "0.2.0"
18 | pest = "2.1.3"
19 | pest_derive = "2.1.0"
20 | serde = { version = "1.0.131", features = ["derive"] }
21 |
--------------------------------------------------------------------------------
/libwhen/LICENSE:
--------------------------------------------------------------------------------
1 | ../LICENSE
--------------------------------------------------------------------------------
/libwhen/README.md:
--------------------------------------------------------------------------------
1 | ../README.md
--------------------------------------------------------------------------------
/libwhen/build.rs:
--------------------------------------------------------------------------------
1 | use std::env;
2 | use std::fs;
3 | use std::io::{BufRead, BufReader, Write};
4 | use std::path::PathBuf;
5 |
6 | fn main() {
7 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
8 | let mut out = fs::File::create(out_dir.join("locations.rs")).unwrap();
9 |
10 | writeln!(out, "static COUNTRIES: &[(&str, &str)] = &[",).unwrap();
11 | for line in BufReader::new(fs::File::open("data/countries.txt").unwrap()).lines() {
12 | let line = line.unwrap();
13 | let pieces = line.split('\t').collect::>();
14 | writeln!(out, " ({:?}, {:?}),", pieces[0], pieces[1]).unwrap();
15 | }
16 | writeln!(out, "];").unwrap();
17 |
18 | writeln!(out, "static LOCATIONS: &[Location] = &[",).unwrap();
19 | for line in BufReader::new(fs::File::open("data/locations.txt").unwrap()).lines() {
20 | let line = line.unwrap();
21 | let pieces = line.split('\t').collect::>();
22 | writeln!(
23 | out,
24 | " Location {{ name: {:?}, aliases: &{:?}, country: {:?}, admin_code: {:?}, kind: LocationKind::{}, tz: Tz::{} }},",
25 | pieces[0],
26 | if pieces[1].is_empty() { vec![] } else { pieces[1].split(';').collect::>() },
27 | pieces[2],
28 | if pieces[3].is_empty() { None } else { Some(pieces[3]) },
29 | match pieces[4] {
30 | "city" => "City",
31 | "airport" => "Airport",
32 | "division" => "Division",
33 | _ => unreachable!(),
34 | },
35 | pieces[5].replace(" ", "_").replace("-", "").replace("/", "__"),
36 | ).unwrap();
37 | }
38 | writeln!(out, "];").unwrap();
39 | }
40 |
--------------------------------------------------------------------------------
/libwhen/src/date_grammar.pest:
--------------------------------------------------------------------------------
1 | WHITESPACE = _{ WHITE_SPACE }
2 |
3 | spec = ${
4 | (unix_time ~ WHITE_SPACE* ~ ^"->" ~ WHITE_SPACE* ~ location) |
5 | ((neg_rel_time | abs_time | rel_time | unix_time) ~ (WHITE_SPACE+ ~ ^"in" ~ WHITE_SPACE+ ~ location)?)
6 | }
7 |
8 | number = { ASCII_DIGIT+ }
9 | abs_time = { time ~ (WHITE_SPACE+ ~ (^"on" ~ WHITE_SPACE+)? ~ date)? | date ~ WHITE_SPACE+ ~ time }
10 | rel_time = ${ ^"in" ~ WHITE_SPACE+ ~ rel_time_spec ~ (WHITE_SPACE+ ~ ^"and" ~ WHITE_SPACE+ ~ rel_time_spec)* }
11 | rel_time_spec = _{ rel_hours | rel_minutes | rel_seconds }
12 | neg_rel_time = ${ rel_time_spec ~ (WHITE_SPACE+ ~ ^"and" ~ WHITE_SPACE+ ~ rel_time_spec)* ~ WHITE_SPACE+ ~ ^"ago" }
13 | rel_hours = { number ~ WHITE_SPACE* ~ (^"hours" | ^"hour" | ^"h") }
14 | rel_minutes = { number ~ WHITE_SPACE* ~ (^"minutes" | ^"mins" | ^"min" | ^"m") }
15 | rel_seconds = { number ~ WHITE_SPACE* ~ (^"seconds" | ^ "secs" | ^"sec" | ^"s") }
16 | unix_time = { (^"unix:" ~ WHITE_SPACE* | ^"unix" ~ WHITE_SPACE+) ~ number }
17 |
18 | time = { time_special | time12 | time24 }
19 | location = @{ (LETTER | NUMBER | MARK | SEPARATOR | PUNCTUATION | SYMBOL | WHITE_SPACE)+ }
20 | time_special = { ^"midnight" | ^"noon" | ^"now" }
21 | time12 = _{ HH12 ~ (":" ~ MM)? ~ (":" ~ SS)? ~ meridiem }
22 | time24 = _{ HH24 ~ (":" ~ MM)? ~ (":" ~ SS)? }
23 | HH12 = { "12" | "11" | "10" | ("0" ~ '1'..'9') | '0'..'9' }
24 | HH24 = { ("1" ~ '0'..'9') | ("2" ~ '0'..'3') | ("0" ~ '1'..'9') | '0'..'9' }
25 | MM = { "00" | ('0'..'5' ~ '0'..'9') | '0'..'9' }
26 | SS = { "00" | ('0'..'5' ~ '0'..'9') | '0'..'9' }
27 | meridiem = { am | pm }
28 | am = { "AM" | "A.M." | "am" | "a.m." }
29 | pm = { "PM" | "P.M." | "pm" | "p.m." }
30 |
31 | date = _{ date_relative | date_absolute }
32 | date_relative = { tomorrow | yesterday | today | in_days }
33 | tomorrow = { (^"in" ~ WHITE_SPACE+ ~ "1" ~ WHITE_SPACE+ ~ ^"day") | ^"tomorrow" | ^"tmw" | ^"tmrw" }
34 | yesterday = { ^"yesterday" | ^"yd" }
35 | today = { ^"today" }
36 | in_days = ${ ^"in" ~ WHITE_SPACE+ ~ rel_days ~ WHITE_SPACE* ~ ^"days" }
37 | rel_days = { ASCII_DIGIT+ }
38 | date_absolute = { ddmmyyyy | english_date }
39 |
40 | ddmmyyyy = { dd ~ ( "-" | "." ) ~ mm ~ ((( "-" | "." ) ~ yyyy) | ".")? }
41 | english_date = ${
42 | (english_month ~ WHITE_SPACE+ ~ (english_day | dd) ~ (WHITE_SPACE+ ~ yyyy)?) |
43 | ((english_day | dd) ~ WHITE_SPACE+ ~ "of" ~ WHITE_SPACE+ ~ english_month ~ (WHITE_SPACE+ ~ yyyy)?)
44 | }
45 | english_day = { "1st" | "2nd" | "3rd" | ('4'..'9') ~ "th" | "1" ~ ('0'..'9') ~ "th" | ("2" ~ ("1st" | "2nd" | "3rd" | '4'..'9' ~ "th")) | "30th" | "31st" }
46 |
47 | english_month = { m01 | m02 | m03 | m04 | m05 | m06 | m07 | m08 | m09 | m10 | m11 | m12 }
48 | m01 = { ^"january" | ^"jan" ~ "."? }
49 | m02 = { ^"february" | ^"feb" ~ "."? }
50 | m03 = { ^"april" | ^"apr" ~ "."? }
51 | m04 = { ^"march" | ^"mar" ~ "."? }
52 | m05 = { ^"may" ~ "."? }
53 | m06 = { ^"june" | ^"jun" ~ "."? }
54 | m07 = { ^"july" | ^"jul" ~ "."? }
55 | m08 = { ^"august" | ^"aug" ~ "."? }
56 | m09 = { ^"september" | ^"sep" ~ ^"t"? ~ "."? }
57 | m10 = { ^"october" | ^"oct" ~ "."? }
58 | m11 = { ^"november" | ^"nov" ~ "."? }
59 | m12 = { ^"december" | ^"dec" ~ "."? }
60 |
61 | dd = { "00" | ('0'..'2' ~ '0'..'9') | "30" | "31" }
62 | mm = { "00" | ('0'..'1' ~ '0'..'9') | "11" | "12" }
63 | yyyy = { '0'..'9' ~ '0'..'9' ~ '0'..'9' ~ '0'..'9' }
64 |
--------------------------------------------------------------------------------
/libwhen/src/lib.rs:
--------------------------------------------------------------------------------
1 | //! This is the internal library for the
2 | //! [`when`](https://github.com/mitsuhiko/when) command line utility.
3 | //!
4 | //! Using this crate directly is not recommended as it's not maintained with a stable
5 | //! API interface. It primarily exists so that it can be compiled to web assembly
6 | //! independently of the CLI tool.
7 | mod location;
8 | mod parser;
9 | mod utils;
10 |
11 | pub use self::location::{find_zone, Location, LocationKind, ZoneRef};
12 | pub use self::parser::{InputExpr, TimeAtLocation};
13 | pub use self::utils::{get_time_of_day, TimeOfDay};
14 |
--------------------------------------------------------------------------------
/libwhen/src/location.rs:
--------------------------------------------------------------------------------
1 | use std::borrow::Cow;
2 | use std::fmt;
3 |
4 | use chrono_tz::Tz;
5 |
6 | /// The type of location.
7 | #[derive(Debug, Copy, Clone, PartialEq)]
8 | pub enum LocationKind {
9 | City,
10 | Timezone,
11 | Airport,
12 | Division,
13 | }
14 |
15 | /// Represents a timezone location.
16 | #[derive(Debug)]
17 | pub struct Location {
18 | pub(crate) name: &'static str,
19 | pub(crate) country: &'static str,
20 | pub(crate) admin_code: Option<&'static str>,
21 | pub(crate) aliases: &'static [&'static str],
22 | pub(crate) kind: LocationKind,
23 | pub(crate) tz: Tz,
24 | }
25 |
26 | /// Reference to a timezone.
27 | #[derive(Debug, Clone, Copy)]
28 | pub enum ZoneRef {
29 | Tz(Tz),
30 | Location(&'static Location),
31 | }
32 |
33 | impl fmt::Display for ZoneRef {
34 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 | if self.kind() == LocationKind::Timezone {
36 | write!(f, "{}", self.name())
37 | } else {
38 | write!(f, "{}", self.name())?;
39 | if let Some(code) = self.admin_code() {
40 | write!(f, ", {}", code)?;
41 | }
42 | if let Some(country) = self.country() {
43 | write!(f, "; ")?;
44 | write!(f, "{}", country)?;
45 | }
46 | Ok(())
47 | }
48 | }
49 | }
50 |
51 | impl ZoneRef {
52 | /// Returns the name of the zone reference.
53 | ///
54 | /// For actual timezones that can be the IANA name, for cities
55 | /// and airports this will be the actual name of the location.
56 | pub fn name(&self) -> &str {
57 | match self {
58 | ZoneRef::Tz(tz) => tz.name(),
59 | ZoneRef::Location(loc) => loc.name,
60 | }
61 | }
62 |
63 | /// True if this zone is the UTC zone.
64 | ///
65 | /// Note that this is different than checking if the zone is currently
66 | /// at UTC+0.
67 | pub fn is_utc(&self) -> bool {
68 | matches!(
69 | self.tz().name(),
70 | "Universal"
71 | | "UTC"
72 | | "UCT"
73 | | "Zulu"
74 | | "Etc/Universal"
75 | | "Etc/UCT"
76 | | "Etc/UTC"
77 | | "Etc/Zulu"
78 | )
79 | }
80 |
81 | /// Returns the kind of location.
82 | pub fn kind(&self) -> LocationKind {
83 | match self {
84 | ZoneRef::Tz(_) => LocationKind::Timezone,
85 | ZoneRef::Location(loc) => loc.kind,
86 | }
87 | }
88 |
89 | /// If this zone reference points to a country, returns the country name.
90 | pub fn country(&self) -> Option<&str> {
91 | match self {
92 | ZoneRef::Tz(_) => None,
93 | ZoneRef::Location(loc) => COUNTRIES
94 | .binary_search_by_key(&loc.country, |x| x.0)
95 | .ok()
96 | .map(|pos| COUNTRIES[pos].1),
97 | }
98 | }
99 |
100 | /// If the zone has an admin code returns it.
101 | ///
102 | /// For the US for instance this can be the name of the US state.
103 | pub fn admin_code(&self) -> Option<&str> {
104 | match self {
105 | ZoneRef::Tz(_) => None,
106 | ZoneRef::Location(loc) => loc.admin_code,
107 | }
108 | }
109 |
110 | /// Returns a `chrono_tz` timezone object.
111 | pub fn tz(&self) -> Tz {
112 | match self {
113 | ZoneRef::Tz(tz) => *tz,
114 | ZoneRef::Location(loc) => loc.tz,
115 | }
116 | }
117 | }
118 |
119 | include!(concat!(env!("OUT_DIR"), "/locations.rs"));
120 |
121 | /// Tries to locate a zone by name
122 | pub fn find_zone(name: &str) -> Option {
123 | let name = if name.eq_ignore_ascii_case("local") {
124 | match localzone::get_local_zone() {
125 | Some(zone) => Cow::Owned(zone),
126 | None => Cow::Borrowed("UTC"),
127 | }
128 | } else {
129 | Cow::Borrowed(name)
130 | };
131 |
132 | let tz_name = name.replace(" ", "_");
133 | for tz in chrono_tz::TZ_VARIANTS {
134 | if tz.name().eq_ignore_ascii_case(&tz_name) {
135 | return Some(ZoneRef::Tz(tz));
136 | }
137 | }
138 |
139 | for delim in [',', ' '] {
140 | if let Some((name, code)) = name.rsplit_once(delim) {
141 | let name = name.trim_end();
142 | let code = code.trim_start();
143 | if let Some(rv) = LOCATIONS.iter().find(|x| {
144 | x.name.eq_ignore_ascii_case(name)
145 | && (x.country.eq_ignore_ascii_case(code)
146 | || x.admin_code.map_or(false, |x| x.eq_ignore_ascii_case(code)))
147 | }) {
148 | return Some(ZoneRef::Location(rv));
149 | }
150 | }
151 | }
152 |
153 | if let Some(loc) = LOCATIONS
154 | .iter()
155 | .find(|x| x.name.eq_ignore_ascii_case(&name))
156 | .map(ZoneRef::Location)
157 | {
158 | return Some(loc);
159 | }
160 |
161 | if name.len() == 3 {
162 | if let Some(loc) = LOCATIONS
163 | .iter()
164 | .find(|x| x.aliases.iter().any(|x| x.eq_ignore_ascii_case(&name)))
165 | .map(ZoneRef::Location)
166 | {
167 | return Some(loc);
168 | }
169 | }
170 |
171 | None
172 | }
173 |
--------------------------------------------------------------------------------
/libwhen/src/parser.rs:
--------------------------------------------------------------------------------
1 | use std::fmt;
2 | use std::ops::Add;
3 |
4 | use chrono::{DateTime, Datelike, Duration, NaiveDateTime, TimeZone, Timelike, Utc};
5 | use chrono_humanize::HumanTime;
6 | use chrono_tz::Tz;
7 | use pest::error::ErrorVariant;
8 | use pest::iterators::Pair;
9 | use pest::Parser;
10 | use pest_derive::Parser;
11 | use serde::ser::SerializeMap;
12 | use serde::{Serialize, Serializer};
13 |
14 | use crate::location::{find_zone, LocationKind, ZoneRef};
15 | use crate::utils::get_time_of_day;
16 |
17 | /// Represents a parsing error.
18 | #[derive(Debug)]
19 | pub enum DateParseError {
20 | Parser(pest::error::Error),
21 | Garbage(String),
22 | OutOfRange(&'static str),
23 | MissingLocation(String),
24 | }
25 |
26 | impl std::error::Error for DateParseError {}
27 |
28 | impl fmt::Display for DateParseError {
29 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 | struct Enumerate<'a, T: fmt::Debug>(&'a [T]);
31 |
32 | impl<'a, T: fmt::Debug> fmt::Display for Enumerate<'a, T> {
33 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 | for (idx, item) in self.0.iter().enumerate() {
35 | if idx > 0 {
36 | write!(f, ", ")?;
37 | }
38 | write!(f, "{:?}", item)?;
39 | }
40 | Ok(())
41 | }
42 | }
43 |
44 | match self {
45 | DateParseError::Parser(p) => {
46 | write!(f, "invalid syntax (")?;
47 | match &p.variant {
48 | ErrorVariant::ParsingError {
49 | positives,
50 | negatives,
51 | } => match (negatives.is_empty(), positives.is_empty()) {
52 | (false, false) => write!(
53 | f,
54 | "unexpected {}; expected {}",
55 | Enumerate(negatives),
56 | Enumerate(positives)
57 | )?,
58 | (false, true) => write!(f, "unexpected {}", Enumerate(negatives))?,
59 | (true, false) => write!(f, "expected {}", Enumerate(positives))?,
60 | (true, true) => write!(f, "unknown parsing error")?,
61 | },
62 | ErrorVariant::CustomError { message } => write!(f, "{}", message)?,
63 | }
64 | write!(f, ")")?;
65 | Ok(())
66 | }
67 | DateParseError::Garbage(leftover) => {
68 | write!(f, "invalid syntax (unsure how to interpret {:?})", leftover)
69 | }
70 | DateParseError::OutOfRange(context) => {
71 | write!(f, "{} out of range", context)
72 | }
73 | DateParseError::MissingLocation(loc) => {
74 | write!(f, "unknown timezone '{}'", loc)
75 | }
76 | }
77 | }
78 | }
79 |
80 | #[derive(Parser)]
81 | #[grammar = "date_grammar.pest"]
82 | struct DateParser;
83 |
84 | /// Represents a human readable date expression
85 | #[derive(Debug)]
86 | pub struct InputExpr<'a> {
87 | time_spec: Option,
88 | date_spec: Option,
89 | locations: Vec<&'a str>,
90 | }
91 |
92 | /// A tuple of time and location.
93 | #[derive(Debug)]
94 | pub struct TimeAtLocation {
95 | datetime: DateTime,
96 | zone_ref: ZoneRef,
97 | }
98 |
99 | impl TimeAtLocation {
100 | /// Returns the timestamp in the given location.
101 | pub fn datetime(&self) -> DateTime {
102 | self.datetime
103 | }
104 |
105 | /// Gives me the relative time to now.
106 | pub fn relative_to(&self, now: DateTime) -> Duration {
107 | self.datetime.signed_duration_since(now)
108 | }
109 |
110 | /// Human readable relative date.
111 | pub fn relative_to_human(&self, now: DateTime) -> String {
112 | format!(
113 | "{:#}",
114 | HumanTime::from(
115 | self.datetime
116 | .with_second(0)
117 | .unwrap()
118 | .with_nanosecond(0)
119 | .unwrap()
120 | .signed_duration_since(now.with_second(0).unwrap().with_nanosecond(0).unwrap())
121 | )
122 | )
123 | }
124 |
125 | /// Returns the zone reference for the timestamp.
126 | pub fn zone(&self) -> ZoneRef {
127 | self.zone_ref
128 | }
129 | }
130 |
131 | impl<'a> Serialize for TimeAtLocation {
132 | fn serialize(&self, serializer: S) -> Result
133 | where
134 | S: Serializer,
135 | {
136 | let mut m = serializer.serialize_map(None)?;
137 | let now = Utc::now();
138 | m.serialize_entry("datetime", &self.datetime)?;
139 | m.serialize_entry("time_of_day", &get_time_of_day(self.datetime))?;
140 | m.serialize_entry("relative_to_now_sec", &self.relative_to(now).num_seconds())?;
141 | m.serialize_entry("relative_to_now_human", &self.relative_to_human(now))?;
142 | m.serialize_entry("timezone", &SerializeZone(&self.zone_ref, &self.datetime))?;
143 | if self.zone_ref.kind() != LocationKind::Timezone {
144 | m.serialize_entry("location", &SerializeLocation(&self.zone_ref))?;
145 | }
146 | m.end()
147 | }
148 | }
149 |
150 | pub struct SerializeZone<'a>(&'a ZoneRef, &'a DateTime);
151 |
152 | impl<'a> Serialize for SerializeZone<'a> {
153 | fn serialize(&self, serializer: S) -> Result
154 | where
155 | S: Serializer,
156 | {
157 | let mut m = serializer.serialize_map(None)?;
158 | m.serialize_entry("name", self.0.tz().name())?;
159 | m.serialize_entry("abbrev", &self.1.format("%Z").to_string())?;
160 | m.serialize_entry("utc_offset", &self.1.format("%z").to_string())?;
161 | m.end()
162 | }
163 | }
164 |
165 | pub struct SerializeLocation<'a>(&'a ZoneRef);
166 |
167 | impl<'a> Serialize for SerializeLocation<'a> {
168 | fn serialize(&self, serializer: S) -> Result
169 | where
170 | S: Serializer,
171 | {
172 | let mut m = serializer.serialize_map(None)?;
173 | m.serialize_entry("name", self.0.name())?;
174 | if let Some(admin_code) = self.0.admin_code() {
175 | m.serialize_entry("admin_code", &admin_code)?;
176 | }
177 | if let Some(country) = self.0.country() {
178 | m.serialize_entry("country", &country)?;
179 | }
180 | m.end()
181 | }
182 | }
183 |
184 | impl<'a> InputExpr<'a> {
185 | /// Parses an expression from a string.
186 | pub fn parse(value: &'a str) -> Result, DateParseError> {
187 | parse_input(value)
188 | }
189 |
190 | /// Returns the location if available.
191 | pub fn location(&self) -> Option<&str> {
192 | self.locations.get(0).copied()
193 | }
194 |
195 | /// Returns the target locations if available.
196 | pub fn to_locations(&self) -> &[&str] {
197 | self.locations.get(1..).unwrap_or_default()
198 | }
199 |
200 | /// Is this relative time?
201 | pub fn is_relative(&self) -> bool {
202 | matches!(self.time_spec, None | Some(TimeSpec::Rel { .. }))
203 | || matches!(self.date_spec, Some(DateSpec::Rel { .. }))
204 | }
205 |
206 | /// Resolves the expression into all referenced locations.
207 | pub fn process(&self) -> Result, DateParseError> {
208 | let zone_ref = self.location().unwrap_or("local");
209 | let from_zone = find_zone(zone_ref)
210 | .ok_or_else(|| DateParseError::MissingLocation(zone_ref.to_string()))?;
211 | let now = Utc::now().with_timezone(&from_zone.tz());
212 | let from = self.apply(now)?;
213 |
214 | let mut rv = vec![TimeAtLocation {
215 | datetime: from,
216 | zone_ref: from_zone,
217 | }];
218 |
219 | for to_zone_ref in self.to_locations() {
220 | let to_zone = find_zone(to_zone_ref)
221 | .ok_or_else(|| DateParseError::MissingLocation(to_zone_ref.to_string()))?;
222 | let to = from.with_timezone(&to_zone.tz());
223 | rv.push(TimeAtLocation {
224 | datetime: to,
225 | zone_ref: to_zone,
226 | });
227 | }
228 |
229 | if rv.len() == 1 {
230 | if let Some(to_zone) = find_zone("local") {
231 | if to_zone.tz().name() != from_zone.tz().name() {
232 | rv.push(TimeAtLocation {
233 | datetime: from.with_timezone(&to_zone.tz()),
234 | zone_ref: to_zone,
235 | });
236 | }
237 | }
238 | }
239 |
240 | Ok(rv)
241 | }
242 |
243 | /// Applies the expression to a current reference date.
244 | pub fn apply(&self, mut date: DateTime) -> Result, DateParseError> {
245 | match self.time_spec {
246 | Some(TimeSpec::Abs {
247 | hour,
248 | minute,
249 | second,
250 | }) => {
251 | date = date
252 | .with_hour(hour as u32)
253 | .unwrap()
254 | .with_minute(minute as u32)
255 | .unwrap()
256 | .with_second(second as u32)
257 | .unwrap();
258 | }
259 | Some(TimeSpec::Rel {
260 | hours,
261 | minutes,
262 | seconds,
263 | }) => {
264 | date = date.add(Duration::hours(hours as i64));
265 | date = date.add(Duration::minutes(minutes as i64));
266 | date = date.add(Duration::seconds(seconds as i64));
267 | }
268 | None => {}
269 | }
270 | match self.date_spec {
271 | Some(DateSpec::Abs { day, month, year }) => {
272 | date = date
273 | .with_day(day as u32)
274 | .ok_or(DateParseError::OutOfRange("day"))?;
275 | if let Some(month) = month {
276 | date = date
277 | .with_month(month as u32)
278 | .ok_or(DateParseError::OutOfRange("month"))?;
279 | }
280 | if let Some(year) = year {
281 | date = date
282 | .with_year(year)
283 | .ok_or(DateParseError::OutOfRange("year"))?;
284 | }
285 | }
286 | Some(DateSpec::Rel { days }) => {
287 | date = date.add(Duration::days(days as i64));
288 | }
289 | None => {}
290 | }
291 | Ok(date)
292 | }
293 | }
294 |
295 | #[derive(Debug)]
296 | enum TimeSpec {
297 | Abs {
298 | hour: i32,
299 | minute: i32,
300 | second: i32,
301 | },
302 | Rel {
303 | hours: i32,
304 | minutes: i32,
305 | seconds: i32,
306 | },
307 | }
308 |
309 | #[derive(Debug)]
310 | enum DateSpec {
311 | Abs {
312 | day: i32,
313 | month: Option,
314 | year: Option,
315 | },
316 | Rel {
317 | days: i32,
318 | },
319 | }
320 |
321 | fn as_int(pair: Pair) -> i32 {
322 | pair.into_inner().next().unwrap().as_str().parse().unwrap()
323 | }
324 |
325 | fn parse_input(expr: &str) -> Result, DateParseError> {
326 | let expr = expr.trim();
327 | let pair = DateParser::parse(Rule::spec, expr)
328 | .map_err(DateParseError::Parser)?
329 | .next()
330 | .unwrap();
331 |
332 | if pair.as_str() != expr {
333 | return Err(DateParseError::Garbage(
334 | expr[pair.as_str().len()..].to_string(),
335 | ));
336 | }
337 |
338 | let mut rv = InputExpr {
339 | time_spec: None,
340 | date_spec: None,
341 | locations: vec![],
342 | };
343 | let mut unix_time = false;
344 |
345 | for piece in pair.into_inner() {
346 | match piece.as_rule() {
347 | Rule::location => {
348 | for loc in piece.as_str().split("->") {
349 | let loc = loc.trim();
350 | if !loc.is_empty() {
351 | rv.locations.push(loc);
352 | }
353 | }
354 | }
355 | Rule::unix_time => {
356 | let ts: i64 = piece.into_inner().next().unwrap().as_str().parse().unwrap();
357 | let dt = NaiveDateTime::from_timestamp_opt(ts, 0)
358 | .ok_or(DateParseError::OutOfRange("unix timestamp"))?;
359 | rv.time_spec = Some(TimeSpec::Abs {
360 | hour: dt.hour() as _,
361 | minute: dt.minute() as _,
362 | second: dt.second() as _,
363 | });
364 | rv.date_spec = Some(DateSpec::Abs {
365 | day: dt.day() as _,
366 | month: Some(dt.month() as _),
367 | year: Some(dt.year() as _),
368 | });
369 | unix_time = true;
370 | }
371 | Rule::abs_time => {
372 | let mut now = false;
373 | for abs_time_piece in piece.into_inner() {
374 | match abs_time_piece.as_rule() {
375 | Rule::time => {
376 | let mut hour = 0;
377 | let mut minute = 0;
378 | let mut second = 0;
379 | for time_piece in abs_time_piece.into_inner() {
380 | match time_piece.as_rule() {
381 | Rule::HH12 | Rule::HH24 => {
382 | hour = time_piece.as_str().parse::().unwrap();
383 | }
384 | Rule::MM => {
385 | minute = time_piece.as_str().parse::().unwrap();
386 | }
387 | Rule::SS => {
388 | second = time_piece.as_str().parse::().unwrap();
389 | }
390 | Rule::meridiem => {
391 | if matches!(
392 | time_piece.into_inner().next().unwrap().as_rule(),
393 | Rule::pm
394 | ) {
395 | // don't change for 12pm
396 | if hour != 12 {
397 | hour += 12;
398 | }
399 | } else {
400 | // special case 12am = midnight
401 | if hour == 12 {
402 | hour = 0;
403 | }
404 | }
405 | }
406 | Rule::time_special => {
407 | if time_piece.as_str().eq_ignore_ascii_case("midnight") {
408 | hour = 0;
409 | } else if time_piece.as_str().eq_ignore_ascii_case("noon") {
410 | hour = 12;
411 | } else if time_piece.as_str().eq_ignore_ascii_case("now") {
412 | now = true;
413 | }
414 | }
415 | _ => unreachable!(),
416 | }
417 | }
418 | if !now {
419 | rv.time_spec = Some(TimeSpec::Abs {
420 | hour,
421 | minute,
422 | second,
423 | });
424 | }
425 | }
426 | Rule::date_absolute => {
427 | let mut day = 0;
428 | let mut month = None;
429 | let mut year = None;
430 | for date_piece in abs_time_piece.into_inner() {
431 | match date_piece.as_rule() {
432 | Rule::english_date => {
433 | for english_piece in date_piece.into_inner() {
434 | match english_piece.as_rule() {
435 | Rule::english_month => {
436 | month = Some(
437 | match english_piece
438 | .into_inner()
439 | .next()
440 | .unwrap()
441 | .as_rule()
442 | {
443 | Rule::m01 => 1,
444 | Rule::m02 => 2,
445 | Rule::m03 => 3,
446 | Rule::m04 => 4,
447 | Rule::m05 => 5,
448 | Rule::m06 => 6,
449 | Rule::m07 => 7,
450 | Rule::m08 => 8,
451 | Rule::m09 => 9,
452 | Rule::m10 => 10,
453 | Rule::m11 => 11,
454 | Rule::m12 => 12,
455 | _ => unreachable!(),
456 | },
457 | );
458 | }
459 | Rule::english_day => {
460 | day = english_piece.as_str()
461 | [0..english_piece.as_str().len() - 2]
462 | .parse()
463 | .unwrap();
464 | }
465 | Rule::dd => {
466 | day = english_piece
467 | .as_str()
468 | .parse::()
469 | .unwrap();
470 | }
471 | Rule::yyyy => {
472 | year = Some(
473 | english_piece.as_str().parse().unwrap(),
474 | );
475 | }
476 | _ => unreachable!(),
477 | }
478 | }
479 | }
480 | Rule::ddmmyyyy => {
481 | for date_piece in date_piece.into_inner() {
482 | match date_piece.as_rule() {
483 | Rule::dd => {
484 | day =
485 | date_piece.as_str().parse::().unwrap();
486 | }
487 | Rule::mm => {
488 | month = Some(
489 | date_piece.as_str().parse::().unwrap(),
490 | );
491 | }
492 | Rule::yyyy => {
493 | year =
494 | Some(date_piece.as_str().parse().unwrap());
495 | }
496 | _ => unreachable!(),
497 | }
498 | }
499 | }
500 | _ => unreachable!(),
501 | }
502 | }
503 | rv.date_spec = Some(DateSpec::Abs { day, month, year });
504 | }
505 | Rule::date_relative => {
506 | let mut days = 0;
507 | for days_piece in abs_time_piece.into_inner() {
508 | match days_piece.as_rule() {
509 | Rule::tomorrow => {
510 | days = 1;
511 | }
512 | Rule::yesterday => {
513 | days = -1;
514 | }
515 | Rule::today => {
516 | days = 0;
517 | }
518 | Rule::in_days => {
519 | days = as_int(days_piece);
520 | }
521 | _ => unreachable!(),
522 | }
523 | }
524 | rv.date_spec = Some(DateSpec::Rel { days });
525 | }
526 | _ => unreachable!(),
527 | }
528 | }
529 | }
530 | Rule::rel_time | Rule::neg_rel_time => {
531 | let mut hours = 0;
532 | let mut minutes = 0;
533 | let mut seconds = 0;
534 | let is_negative = piece.as_rule() == Rule::neg_rel_time;
535 | for time_piece in piece.into_inner() {
536 | match time_piece.as_rule() {
537 | Rule::rel_hours => {
538 | hours = as_int(time_piece);
539 | }
540 | Rule::rel_minutes => {
541 | minutes = as_int(time_piece);
542 | }
543 | Rule::rel_seconds => {
544 | seconds = as_int(time_piece);
545 | }
546 | _ => unreachable!(),
547 | }
548 | }
549 | if is_negative {
550 | hours *= -1;
551 | minutes *= -1;
552 | seconds *= -1;
553 | }
554 | rv.time_spec = Some(TimeSpec::Rel {
555 | hours,
556 | minutes,
557 | seconds,
558 | });
559 | }
560 | _ => unreachable!(),
561 | }
562 | }
563 |
564 | // if unix time is used there is always an implied utc location
565 | // as this is the main thing that makes sense with unix timestamps
566 | if unix_time
567 | && (rv.locations.is_empty() || !find_zone(rv.locations[0]).map_or(false, |x| x.is_utc()))
568 | {
569 | rv.locations.insert(0, "utc");
570 | }
571 |
572 | Ok(rv)
573 | }
574 |
--------------------------------------------------------------------------------
/libwhen/src/utils.rs:
--------------------------------------------------------------------------------
1 | use std::fmt;
2 |
3 | use chrono::{DateTime, Timelike};
4 | use chrono_tz::Tz;
5 | use serde::{Deserialize, Serialize};
6 |
7 | /// Human readable time-of-day description.
8 | #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
9 | #[serde(rename_all = "snake_case")]
10 | pub enum TimeOfDay {
11 | EarlyMorning,
12 | Morning,
13 | LateMorning,
14 | Noon,
15 | Afternoon,
16 | EarlyEvening,
17 | Evening,
18 | LateEvening,
19 | Night,
20 | }
21 |
22 | impl fmt::Display for TimeOfDay {
23 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 | write!(
25 | f,
26 | "{}",
27 | match self {
28 | TimeOfDay::EarlyMorning => "early morning",
29 | TimeOfDay::Morning => "morning",
30 | TimeOfDay::LateMorning => "late morning",
31 | TimeOfDay::Noon => "noon",
32 | TimeOfDay::Afternoon => "afternoon",
33 | TimeOfDay::EarlyEvening => "early evening",
34 | TimeOfDay::Evening => "evening",
35 | TimeOfDay::LateEvening => "late evening",
36 | TimeOfDay::Night => "night",
37 | }
38 | )
39 | }
40 | }
41 |
42 | /// Given a datetime object returns a human readable time-of-day description.
43 | pub fn get_time_of_day(dt: DateTime) -> TimeOfDay {
44 | match dt.hour() {
45 | 5 => TimeOfDay::EarlyMorning,
46 | 6..=8 => TimeOfDay::Morning,
47 | 9..=11 => TimeOfDay::LateMorning,
48 | 12 => TimeOfDay::Noon,
49 | 13..=16 => TimeOfDay::Afternoon,
50 | 17..=18 => TimeOfDay::EarlyEvening,
51 | 19..=20 => TimeOfDay::Evening,
52 | 21..=22 => TimeOfDay::LateEvening,
53 | 23 | 0..=4 => TimeOfDay::Night,
54 | 24.. => unreachable!(),
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/web/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "autocfg"
7 | version = "1.0.1"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
10 |
11 | [[package]]
12 | name = "block-buffer"
13 | version = "0.7.3"
14 | source = "registry+https://github.com/rust-lang/crates.io-index"
15 | checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
16 | dependencies = [
17 | "block-padding",
18 | "byte-tools",
19 | "byteorder",
20 | "generic-array",
21 | ]
22 |
23 | [[package]]
24 | name = "block-padding"
25 | version = "0.1.5"
26 | source = "registry+https://github.com/rust-lang/crates.io-index"
27 | checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
28 | dependencies = [
29 | "byte-tools",
30 | ]
31 |
32 | [[package]]
33 | name = "bumpalo"
34 | version = "3.8.0"
35 | source = "registry+https://github.com/rust-lang/crates.io-index"
36 | checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
37 |
38 | [[package]]
39 | name = "byte-tools"
40 | version = "0.3.1"
41 | source = "registry+https://github.com/rust-lang/crates.io-index"
42 | checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
43 |
44 | [[package]]
45 | name = "byteorder"
46 | version = "1.4.3"
47 | source = "registry+https://github.com/rust-lang/crates.io-index"
48 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
49 |
50 | [[package]]
51 | name = "cfg-if"
52 | version = "0.1.10"
53 | source = "registry+https://github.com/rust-lang/crates.io-index"
54 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
55 |
56 | [[package]]
57 | name = "cfg-if"
58 | version = "1.0.0"
59 | source = "registry+https://github.com/rust-lang/crates.io-index"
60 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
61 |
62 | [[package]]
63 | name = "chrono"
64 | version = "0.4.19"
65 | source = "registry+https://github.com/rust-lang/crates.io-index"
66 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
67 | dependencies = [
68 | "js-sys",
69 | "libc",
70 | "num-integer",
71 | "num-traits",
72 | "serde",
73 | "time",
74 | "wasm-bindgen",
75 | "winapi",
76 | ]
77 |
78 | [[package]]
79 | name = "chrono-humanize"
80 | version = "0.2.1"
81 | source = "registry+https://github.com/rust-lang/crates.io-index"
82 | checksum = "2eddc119501d583fd930cb92144e605f44e0252c38dd89d9247fffa1993375cb"
83 | dependencies = [
84 | "chrono",
85 | ]
86 |
87 | [[package]]
88 | name = "chrono-tz"
89 | version = "0.6.1"
90 | source = "registry+https://github.com/rust-lang/crates.io-index"
91 | checksum = "58549f1842da3080ce63002102d5bc954c7bc843d4f47818e642abdc36253552"
92 | dependencies = [
93 | "chrono",
94 | "chrono-tz-build",
95 | "phf",
96 | ]
97 |
98 | [[package]]
99 | name = "chrono-tz-build"
100 | version = "0.0.2"
101 | source = "registry+https://github.com/rust-lang/crates.io-index"
102 | checksum = "db058d493fb2f65f41861bfed7e3fe6335264a9f0f92710cab5bdf01fef09069"
103 | dependencies = [
104 | "parse-zoneinfo",
105 | "phf",
106 | "phf_codegen",
107 | ]
108 |
109 | [[package]]
110 | name = "console_error_panic_hook"
111 | version = "0.1.7"
112 | source = "registry+https://github.com/rust-lang/crates.io-index"
113 | checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
114 | dependencies = [
115 | "cfg-if 1.0.0",
116 | "wasm-bindgen",
117 | ]
118 |
119 | [[package]]
120 | name = "digest"
121 | version = "0.8.1"
122 | source = "registry+https://github.com/rust-lang/crates.io-index"
123 | checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
124 | dependencies = [
125 | "generic-array",
126 | ]
127 |
128 | [[package]]
129 | name = "fake-simd"
130 | version = "0.1.2"
131 | source = "registry+https://github.com/rust-lang/crates.io-index"
132 | checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
133 |
134 | [[package]]
135 | name = "generic-array"
136 | version = "0.12.4"
137 | source = "registry+https://github.com/rust-lang/crates.io-index"
138 | checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
139 | dependencies = [
140 | "typenum",
141 | ]
142 |
143 | [[package]]
144 | name = "getrandom"
145 | version = "0.2.3"
146 | source = "registry+https://github.com/rust-lang/crates.io-index"
147 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
148 | dependencies = [
149 | "cfg-if 1.0.0",
150 | "libc",
151 | "wasi",
152 | ]
153 |
154 | [[package]]
155 | name = "itoa"
156 | version = "0.4.8"
157 | source = "registry+https://github.com/rust-lang/crates.io-index"
158 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
159 |
160 | [[package]]
161 | name = "js-sys"
162 | version = "0.3.55"
163 | source = "registry+https://github.com/rust-lang/crates.io-index"
164 | checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
165 | dependencies = [
166 | "wasm-bindgen",
167 | ]
168 |
169 | [[package]]
170 | name = "lazy_static"
171 | version = "1.4.0"
172 | source = "registry+https://github.com/rust-lang/crates.io-index"
173 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
174 |
175 | [[package]]
176 | name = "libc"
177 | version = "0.2.109"
178 | source = "registry+https://github.com/rust-lang/crates.io-index"
179 | checksum = "f98a04dce437184842841303488f70d0188c5f51437d2a834dc097eafa909a01"
180 |
181 | [[package]]
182 | name = "libwhen"
183 | version = "0.4.0"
184 | dependencies = [
185 | "chrono",
186 | "chrono-humanize",
187 | "chrono-tz",
188 | "localzone",
189 | "pest",
190 | "pest_derive",
191 | "serde",
192 | ]
193 |
194 | [[package]]
195 | name = "localzone"
196 | version = "0.2.0"
197 | source = "registry+https://github.com/rust-lang/crates.io-index"
198 | checksum = "7865f5a13ecb548180b8fe7e538d739090329fdb984eef9e9a16db214f216c6f"
199 | dependencies = [
200 | "js-sys",
201 | "windows",
202 | ]
203 |
204 | [[package]]
205 | name = "log"
206 | version = "0.4.14"
207 | source = "registry+https://github.com/rust-lang/crates.io-index"
208 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
209 | dependencies = [
210 | "cfg-if 1.0.0",
211 | ]
212 |
213 | [[package]]
214 | name = "maplit"
215 | version = "1.0.2"
216 | source = "registry+https://github.com/rust-lang/crates.io-index"
217 | checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
218 |
219 | [[package]]
220 | name = "memory_units"
221 | version = "0.4.0"
222 | source = "registry+https://github.com/rust-lang/crates.io-index"
223 | checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3"
224 |
225 | [[package]]
226 | name = "num-integer"
227 | version = "0.1.44"
228 | source = "registry+https://github.com/rust-lang/crates.io-index"
229 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
230 | dependencies = [
231 | "autocfg",
232 | "num-traits",
233 | ]
234 |
235 | [[package]]
236 | name = "num-traits"
237 | version = "0.2.14"
238 | source = "registry+https://github.com/rust-lang/crates.io-index"
239 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
240 | dependencies = [
241 | "autocfg",
242 | ]
243 |
244 | [[package]]
245 | name = "opaque-debug"
246 | version = "0.2.3"
247 | source = "registry+https://github.com/rust-lang/crates.io-index"
248 | checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
249 |
250 | [[package]]
251 | name = "parse-zoneinfo"
252 | version = "0.3.0"
253 | source = "registry+https://github.com/rust-lang/crates.io-index"
254 | checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41"
255 | dependencies = [
256 | "regex",
257 | ]
258 |
259 | [[package]]
260 | name = "pest"
261 | version = "2.1.3"
262 | source = "registry+https://github.com/rust-lang/crates.io-index"
263 | checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
264 | dependencies = [
265 | "ucd-trie",
266 | ]
267 |
268 | [[package]]
269 | name = "pest_derive"
270 | version = "2.1.0"
271 | source = "registry+https://github.com/rust-lang/crates.io-index"
272 | checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
273 | dependencies = [
274 | "pest",
275 | "pest_generator",
276 | ]
277 |
278 | [[package]]
279 | name = "pest_generator"
280 | version = "2.1.3"
281 | source = "registry+https://github.com/rust-lang/crates.io-index"
282 | checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
283 | dependencies = [
284 | "pest",
285 | "pest_meta",
286 | "proc-macro2",
287 | "quote",
288 | "syn",
289 | ]
290 |
291 | [[package]]
292 | name = "pest_meta"
293 | version = "2.1.3"
294 | source = "registry+https://github.com/rust-lang/crates.io-index"
295 | checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
296 | dependencies = [
297 | "maplit",
298 | "pest",
299 | "sha-1",
300 | ]
301 |
302 | [[package]]
303 | name = "phf"
304 | version = "0.10.0"
305 | source = "registry+https://github.com/rust-lang/crates.io-index"
306 | checksum = "b9fc3db1018c4b59d7d582a739436478b6035138b6aecbce989fc91c3e98409f"
307 | dependencies = [
308 | "phf_shared",
309 | ]
310 |
311 | [[package]]
312 | name = "phf_codegen"
313 | version = "0.10.0"
314 | source = "registry+https://github.com/rust-lang/crates.io-index"
315 | checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
316 | dependencies = [
317 | "phf_generator",
318 | "phf_shared",
319 | ]
320 |
321 | [[package]]
322 | name = "phf_generator"
323 | version = "0.10.0"
324 | source = "registry+https://github.com/rust-lang/crates.io-index"
325 | checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
326 | dependencies = [
327 | "phf_shared",
328 | "rand",
329 | ]
330 |
331 | [[package]]
332 | name = "phf_shared"
333 | version = "0.10.0"
334 | source = "registry+https://github.com/rust-lang/crates.io-index"
335 | checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
336 | dependencies = [
337 | "siphasher",
338 | "uncased",
339 | ]
340 |
341 | [[package]]
342 | name = "ppv-lite86"
343 | version = "0.2.15"
344 | source = "registry+https://github.com/rust-lang/crates.io-index"
345 | checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
346 |
347 | [[package]]
348 | name = "proc-macro2"
349 | version = "1.0.33"
350 | source = "registry+https://github.com/rust-lang/crates.io-index"
351 | checksum = "fb37d2df5df740e582f28f8560cf425f52bb267d872fe58358eadb554909f07a"
352 | dependencies = [
353 | "unicode-xid",
354 | ]
355 |
356 | [[package]]
357 | name = "quote"
358 | version = "1.0.10"
359 | source = "registry+https://github.com/rust-lang/crates.io-index"
360 | checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
361 | dependencies = [
362 | "proc-macro2",
363 | ]
364 |
365 | [[package]]
366 | name = "rand"
367 | version = "0.8.4"
368 | source = "registry+https://github.com/rust-lang/crates.io-index"
369 | checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
370 | dependencies = [
371 | "libc",
372 | "rand_chacha",
373 | "rand_core",
374 | "rand_hc",
375 | ]
376 |
377 | [[package]]
378 | name = "rand_chacha"
379 | version = "0.3.1"
380 | source = "registry+https://github.com/rust-lang/crates.io-index"
381 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
382 | dependencies = [
383 | "ppv-lite86",
384 | "rand_core",
385 | ]
386 |
387 | [[package]]
388 | name = "rand_core"
389 | version = "0.6.3"
390 | source = "registry+https://github.com/rust-lang/crates.io-index"
391 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
392 | dependencies = [
393 | "getrandom",
394 | ]
395 |
396 | [[package]]
397 | name = "rand_hc"
398 | version = "0.3.1"
399 | source = "registry+https://github.com/rust-lang/crates.io-index"
400 | checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
401 | dependencies = [
402 | "rand_core",
403 | ]
404 |
405 | [[package]]
406 | name = "regex"
407 | version = "1.5.4"
408 | source = "registry+https://github.com/rust-lang/crates.io-index"
409 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
410 | dependencies = [
411 | "regex-syntax",
412 | ]
413 |
414 | [[package]]
415 | name = "regex-syntax"
416 | version = "0.6.25"
417 | source = "registry+https://github.com/rust-lang/crates.io-index"
418 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
419 |
420 | [[package]]
421 | name = "ryu"
422 | version = "1.0.6"
423 | source = "registry+https://github.com/rust-lang/crates.io-index"
424 | checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568"
425 |
426 | [[package]]
427 | name = "scoped-tls"
428 | version = "1.0.0"
429 | source = "registry+https://github.com/rust-lang/crates.io-index"
430 | checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
431 |
432 | [[package]]
433 | name = "serde"
434 | version = "1.0.131"
435 | source = "registry+https://github.com/rust-lang/crates.io-index"
436 | checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1"
437 | dependencies = [
438 | "serde_derive",
439 | ]
440 |
441 | [[package]]
442 | name = "serde_derive"
443 | version = "1.0.131"
444 | source = "registry+https://github.com/rust-lang/crates.io-index"
445 | checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2"
446 | dependencies = [
447 | "proc-macro2",
448 | "quote",
449 | "syn",
450 | ]
451 |
452 | [[package]]
453 | name = "serde_json"
454 | version = "1.0.72"
455 | source = "registry+https://github.com/rust-lang/crates.io-index"
456 | checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527"
457 | dependencies = [
458 | "itoa",
459 | "ryu",
460 | "serde",
461 | ]
462 |
463 | [[package]]
464 | name = "sha-1"
465 | version = "0.8.2"
466 | source = "registry+https://github.com/rust-lang/crates.io-index"
467 | checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
468 | dependencies = [
469 | "block-buffer",
470 | "digest",
471 | "fake-simd",
472 | "opaque-debug",
473 | ]
474 |
475 | [[package]]
476 | name = "siphasher"
477 | version = "0.3.7"
478 | source = "registry+https://github.com/rust-lang/crates.io-index"
479 | checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
480 |
481 | [[package]]
482 | name = "syn"
483 | version = "1.0.82"
484 | source = "registry+https://github.com/rust-lang/crates.io-index"
485 | checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
486 | dependencies = [
487 | "proc-macro2",
488 | "quote",
489 | "unicode-xid",
490 | ]
491 |
492 | [[package]]
493 | name = "time"
494 | version = "0.1.44"
495 | source = "registry+https://github.com/rust-lang/crates.io-index"
496 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
497 | dependencies = [
498 | "libc",
499 | "wasi",
500 | "winapi",
501 | ]
502 |
503 | [[package]]
504 | name = "typenum"
505 | version = "1.14.0"
506 | source = "registry+https://github.com/rust-lang/crates.io-index"
507 | checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
508 |
509 | [[package]]
510 | name = "ucd-trie"
511 | version = "0.1.3"
512 | source = "registry+https://github.com/rust-lang/crates.io-index"
513 | checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
514 |
515 | [[package]]
516 | name = "uncased"
517 | version = "0.9.6"
518 | source = "registry+https://github.com/rust-lang/crates.io-index"
519 | checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0"
520 | dependencies = [
521 | "version_check",
522 | ]
523 |
524 | [[package]]
525 | name = "unicode-xid"
526 | version = "0.2.2"
527 | source = "registry+https://github.com/rust-lang/crates.io-index"
528 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
529 |
530 | [[package]]
531 | name = "version_check"
532 | version = "0.9.3"
533 | source = "registry+https://github.com/rust-lang/crates.io-index"
534 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
535 |
536 | [[package]]
537 | name = "wasi"
538 | version = "0.10.0+wasi-snapshot-preview1"
539 | source = "registry+https://github.com/rust-lang/crates.io-index"
540 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
541 |
542 | [[package]]
543 | name = "wasm-bindgen"
544 | version = "0.2.78"
545 | source = "registry+https://github.com/rust-lang/crates.io-index"
546 | checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
547 | dependencies = [
548 | "cfg-if 1.0.0",
549 | "wasm-bindgen-macro",
550 | ]
551 |
552 | [[package]]
553 | name = "wasm-bindgen-backend"
554 | version = "0.2.78"
555 | source = "registry+https://github.com/rust-lang/crates.io-index"
556 | checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
557 | dependencies = [
558 | "bumpalo",
559 | "lazy_static",
560 | "log",
561 | "proc-macro2",
562 | "quote",
563 | "syn",
564 | "wasm-bindgen-shared",
565 | ]
566 |
567 | [[package]]
568 | name = "wasm-bindgen-futures"
569 | version = "0.4.28"
570 | source = "registry+https://github.com/rust-lang/crates.io-index"
571 | checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39"
572 | dependencies = [
573 | "cfg-if 1.0.0",
574 | "js-sys",
575 | "wasm-bindgen",
576 | "web-sys",
577 | ]
578 |
579 | [[package]]
580 | name = "wasm-bindgen-macro"
581 | version = "0.2.78"
582 | source = "registry+https://github.com/rust-lang/crates.io-index"
583 | checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
584 | dependencies = [
585 | "quote",
586 | "wasm-bindgen-macro-support",
587 | ]
588 |
589 | [[package]]
590 | name = "wasm-bindgen-macro-support"
591 | version = "0.2.78"
592 | source = "registry+https://github.com/rust-lang/crates.io-index"
593 | checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
594 | dependencies = [
595 | "proc-macro2",
596 | "quote",
597 | "syn",
598 | "wasm-bindgen-backend",
599 | "wasm-bindgen-shared",
600 | ]
601 |
602 | [[package]]
603 | name = "wasm-bindgen-shared"
604 | version = "0.2.78"
605 | source = "registry+https://github.com/rust-lang/crates.io-index"
606 | checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
607 |
608 | [[package]]
609 | name = "wasm-bindgen-test"
610 | version = "0.3.28"
611 | source = "registry+https://github.com/rust-lang/crates.io-index"
612 | checksum = "96f1aa7971fdf61ef0f353602102dbea75a56e225ed036c1e3740564b91e6b7e"
613 | dependencies = [
614 | "console_error_panic_hook",
615 | "js-sys",
616 | "scoped-tls",
617 | "wasm-bindgen",
618 | "wasm-bindgen-futures",
619 | "wasm-bindgen-test-macro",
620 | ]
621 |
622 | [[package]]
623 | name = "wasm-bindgen-test-macro"
624 | version = "0.3.28"
625 | source = "registry+https://github.com/rust-lang/crates.io-index"
626 | checksum = "6006f79628dfeb96a86d4db51fbf1344cd7fd8408f06fc9aa3c84913a4789688"
627 | dependencies = [
628 | "proc-macro2",
629 | "quote",
630 | ]
631 |
632 | [[package]]
633 | name = "web-sys"
634 | version = "0.3.55"
635 | source = "registry+https://github.com/rust-lang/crates.io-index"
636 | checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb"
637 | dependencies = [
638 | "js-sys",
639 | "wasm-bindgen",
640 | ]
641 |
642 | [[package]]
643 | name = "wee_alloc"
644 | version = "0.4.5"
645 | source = "registry+https://github.com/rust-lang/crates.io-index"
646 | checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e"
647 | dependencies = [
648 | "cfg-if 0.1.10",
649 | "libc",
650 | "memory_units",
651 | "winapi",
652 | ]
653 |
654 | [[package]]
655 | name = "when-web"
656 | version = "0.1.0"
657 | dependencies = [
658 | "chrono",
659 | "console_error_panic_hook",
660 | "libwhen",
661 | "serde",
662 | "serde_json",
663 | "wasm-bindgen",
664 | "wasm-bindgen-test",
665 | "wee_alloc",
666 | ]
667 |
668 | [[package]]
669 | name = "winapi"
670 | version = "0.3.9"
671 | source = "registry+https://github.com/rust-lang/crates.io-index"
672 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
673 | dependencies = [
674 | "winapi-i686-pc-windows-gnu",
675 | "winapi-x86_64-pc-windows-gnu",
676 | ]
677 |
678 | [[package]]
679 | name = "winapi-i686-pc-windows-gnu"
680 | version = "0.4.0"
681 | source = "registry+https://github.com/rust-lang/crates.io-index"
682 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
683 |
684 | [[package]]
685 | name = "winapi-x86_64-pc-windows-gnu"
686 | version = "0.4.0"
687 | source = "registry+https://github.com/rust-lang/crates.io-index"
688 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
689 |
690 | [[package]]
691 | name = "windows"
692 | version = "0.28.0"
693 | source = "registry+https://github.com/rust-lang/crates.io-index"
694 | checksum = "054d31561409bbf7e1ee4a4f0a1233ac2bb79cfadf2a398438a04d8dda69225f"
695 | dependencies = [
696 | "windows-sys",
697 | ]
698 |
699 | [[package]]
700 | name = "windows-sys"
701 | version = "0.28.0"
702 | source = "registry+https://github.com/rust-lang/crates.io-index"
703 | checksum = "82ca39602d5cbfa692c4b67e3bcbb2751477355141c1ed434c94da4186836ff6"
704 | dependencies = [
705 | "windows_aarch64_msvc",
706 | "windows_i686_gnu",
707 | "windows_i686_msvc",
708 | "windows_x86_64_gnu",
709 | "windows_x86_64_msvc",
710 | ]
711 |
712 | [[package]]
713 | name = "windows_aarch64_msvc"
714 | version = "0.28.0"
715 | source = "registry+https://github.com/rust-lang/crates.io-index"
716 | checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2"
717 |
718 | [[package]]
719 | name = "windows_i686_gnu"
720 | version = "0.28.0"
721 | source = "registry+https://github.com/rust-lang/crates.io-index"
722 | checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a"
723 |
724 | [[package]]
725 | name = "windows_i686_msvc"
726 | version = "0.28.0"
727 | source = "registry+https://github.com/rust-lang/crates.io-index"
728 | checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64"
729 |
730 | [[package]]
731 | name = "windows_x86_64_gnu"
732 | version = "0.28.0"
733 | source = "registry+https://github.com/rust-lang/crates.io-index"
734 | checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954"
735 |
736 | [[package]]
737 | name = "windows_x86_64_msvc"
738 | version = "0.28.0"
739 | source = "registry+https://github.com/rust-lang/crates.io-index"
740 | checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f"
741 |
--------------------------------------------------------------------------------
/web/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "when-web"
3 | version = "0.1.0"
4 | authors = ["Armin Ronacher "]
5 | edition = "2021"
6 |
7 | [lib]
8 | crate-type = ["cdylib", "rlib"]
9 |
10 | [features]
11 | default = ["console_error_panic_hook"]
12 |
13 | [dependencies]
14 | wasm-bindgen = "0.2.63"
15 | console_error_panic_hook = { version = "0.1.6", optional = true }
16 | wee_alloc = { version = "0.4.5", optional = true }
17 | libwhen = { path = "../libwhen" }
18 | chrono = { version = "0.4.19", features = ["wasmbind", "js-sys"] }
19 | serde_json = "1.0.72"
20 | serde = { version = "1.0.131", features = ["derive"] }
21 |
22 | [dev-dependencies]
23 | wasm-bindgen-test = "0.3.13"
24 |
25 | [profile.release]
26 | # Tell `rustc` to optimize for small code size.
27 | opt-level = "s"
28 |
29 | [workspace]
30 |
31 | [package.metadata.wasm-pack.profile.release]
32 | wasm-opt = false
33 |
--------------------------------------------------------------------------------
/web/src/lib.rs:
--------------------------------------------------------------------------------
1 | use serde::Serialize;
2 | use wasm_bindgen::prelude::*;
3 |
4 | use libwhen::TimeAtLocation;
5 |
6 | // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
7 | // allocator.
8 | #[cfg(feature = "wee_alloc")]
9 | #[global_allocator]
10 | static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
11 |
12 | #[derive(Serialize)]
13 | pub struct ParseResult {
14 | is_relative: bool,
15 | locations: Vec,
16 | error: Option,
17 | }
18 |
19 | fn handle_expr(input: &str) -> Result<(Vec, bool), String> {
20 | let expr = libwhen::InputExpr::parse(&input).map_err(|x| x.to_string())?;
21 | Ok(expr
22 | .process()
23 | .map(|x| (x, expr.is_relative()))
24 | .map_err(|x| x.to_string())?)
25 | }
26 |
27 | #[wasm_bindgen]
28 | pub fn parse_expr(input: String) -> String {
29 | let (locations, is_relative, error) = match handle_expr(&input) {
30 | Ok((locations, is_relative)) => (locations, is_relative, None),
31 | Err(err) => (Vec::new(), false, Some(err.to_string())),
32 | };
33 | serde_json::to_string(&ParseResult {
34 | is_relative,
35 | locations,
36 | error,
37 | })
38 | .unwrap()
39 | }
40 |
41 | #[wasm_bindgen]
42 | pub fn set_panic_hook() {
43 | // When the `console_error_panic_hook` feature is enabled, we can call the
44 | // `set_panic_hook` function at least once during initialization, and then
45 | // we will get better error messages if our code ever panics.
46 | //
47 | // For more details see
48 | // https://github.com/rustwasm/console_error_panic_hook#readme
49 | #[cfg(feature = "console_error_panic_hook")]
50 | console_error_panic_hook::set_once();
51 | }
52 |
--------------------------------------------------------------------------------
/web/tests/web.rs:
--------------------------------------------------------------------------------
1 | //! Test suite for the Web and headless browsers.
2 |
3 | #![cfg(target_arch = "wasm32")]
4 |
5 | extern crate wasm_bindgen_test;
6 | use wasm_bindgen_test::*;
7 |
8 | wasm_bindgen_test_configure!(run_in_browser);
9 |
10 | #[wasm_bindgen_test]
11 | fn pass() {
12 | assert_eq!(1 + 1, 2);
13 | }
14 |
--------------------------------------------------------------------------------
/web/www/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 |
--------------------------------------------------------------------------------
/web/www/bootstrap.js:
--------------------------------------------------------------------------------
1 | // A dependency graph that contains any wasm must all be imported
2 | // asynchronously. This `bootstrap.js` file does the single async import, so
3 | // that no one else needs to worry about it again.
4 | import("./index.jsx")
5 | .catch(e => console.error("Error importing `index.js`:", e));
6 |
--------------------------------------------------------------------------------
/web/www/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= htmlWebpackPlugin.options.title %>
7 | when?
8 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/web/www/index.jsx:
--------------------------------------------------------------------------------
1 | import * as wasm from "when";
2 | import React, { useEffect, useState, useRef } from "react";
3 | import ReactDOM from "react-dom";
4 | import "./style.css";
5 |
6 | wasm.set_panic_hook();
7 |
8 | const EXAMPLES = [
9 | "now",
10 | "2 hours ago in yyz",
11 | "5pm in yyz -> sfo",
12 | "5pm in Vienna -> London",
13 | "4pm on 17.05.2021 in Vienna -> Tokyo",
14 | "in 4 hours in San Francisco",
15 | "2pm in 2 days in New Delhi",
16 | "now in yyz -> sfo -> vie -> lhr",
17 | "unix 1639067620 in Tokyo",
18 | ];
19 |
20 | function evaluateDateExpr(input) {
21 | return JSON.parse(wasm.parse_expr(input || "now"));
22 | }
23 |
24 | function parseDate(datetime) {
25 | const match = datetime.match(/^([^T]+)T([^.]+)/);
26 | return {
27 | time: match[2],
28 | date: match[1],
29 | };
30 | }
31 |
32 | function Location({ location: loc }) {
33 | const dt = parseDate(loc.datetime);
34 | return (
35 |
36 |
37 |
38 | Time |
39 |
40 | {dt.time} (
41 | {loc.relative_to_now_human}{' — '}
42 | {loc.time_of_day.replace(/_/g, " ")})
43 | |
44 |
45 |
46 | Date |
47 |
48 | {dt.date}
49 | |
50 |
51 |
52 | Zone |
53 |
54 | {loc.timezone.name} (
55 | {loc.timezone.abbrev}; {loc.timezone.utc_offset})
56 | |
57 |
58 | {loc.location && (
59 |
60 | Location |
61 |
62 | {loc.location.name}
63 | {loc.location.admin_code
64 | ? ` (${loc.location.admin_code}; ${loc.location.country})`
65 | : loc.location.country
66 | ? ` (${loc.location.country})`
67 | : null}
68 | |
69 |
70 | )}
71 |
72 |
73 | );
74 | }
75 |
76 | function Examples({setExpr}) {
77 | return (
78 |
79 |
Need inspiration? Try some of these
80 |
95 |
96 | );
97 | }
98 |
99 | function Results({locations}) {
100 | return (
101 |
102 | {locations.map((loc, idx) => (
103 | -
104 |
105 |
106 | ))}
107 |
108 | );
109 | }
110 |
111 | function getTextResults(locations) {
112 | return locations
113 | .map((loc) => {
114 | const dt = parseDate(loc.datetime);
115 | const lines = [
116 | `time: ${dt.time} (${loc.relative_to_now_human}; ${loc.time_of_day.replace(/_/g, " ")})`,
117 | `date: ${dt.date}`,
118 | `zone: ${loc.timezone.name} (${loc.timezone.abbrev}; ${loc.timezone.utc_offset})`,
119 | ];
120 | if (loc.location) {
121 | let location = `location: ${loc.location.name}`;
122 | if (loc.location.admin_code) {
123 | location += ` (${loc.location.admin_code}; ${loc.location.country})`;
124 | } else if (loc.location.country) {
125 | location += ` (${loc.location.country})`;
126 | }
127 | lines.push(location);
128 | }
129 | return lines.join("\n");
130 | })
131 | .join("\n\n");
132 | }
133 |
134 | function PlainTextResults({locations}) {
135 | const ref = useRef();
136 | return {
137 | let range = document.createRange();
138 | range.selectNodeContents(ref.current);
139 | let sel = window.getSelection();
140 | sel.removeAllRanges();
141 | sel.addRange(range);
142 | }}>{getTextResults(locations)}
;
143 | }
144 |
145 | function App() {
146 | const url = new URL(window.location);
147 | const [inc, setInc] = useState(0);
148 | const [asText, setAsText] = useState(url.searchParams.get("format") == "text");
149 | const [expr, setExpr] = useState(url.searchParams.get("input") || "");
150 | const inputRef = useRef();
151 | const rv = evaluateDateExpr(expr);
152 |
153 | useEffect(() => {
154 | const url = new URL(window.location);
155 | url.searchParams.set("input", expr);
156 | if (asText) {
157 | url.searchParams.set("format", "text");
158 | } else {
159 | url.searchParams.delete("format");
160 | }
161 | window.history.replaceState({}, "", url);
162 |
163 | if (rv.is_relative && !asText) {
164 | const timer = setTimeout(() => {
165 | setInc(inc + 1);
166 | }, 1000);
167 | return () => clearTimeout(timer);
168 | }
169 | }, [inc, rv.is_relative, asText, location.search]);
170 |
171 | function setExprAndFocus(value) {
172 | setExpr(value);
173 | if (inputRef.current) {
174 | inputRef.current.focus();
175 | }
176 | }
177 |
178 | const showResults = expr && rv.locations.length > 0;
179 |
180 | return (
181 |
182 |
183 | when?
184 | huh?
185 | {" | "}
186 | who?
187 |
188 |
setExprAndFocus("")}
190 | title="Clear input (ESC)"
191 | className="clear"
192 | >
193 | x
194 |
195 |
{
200 | if (evt.key === "Escape") {
201 | setExprAndFocus("");
202 | }
203 | }}
204 | onChange={(evt) => {
205 | setExpr(evt.target.value);
206 | }}
207 | size="40"
208 | autoFocus
209 | />
210 | {!expr &&
}
211 | {showResults ? (
212 |
221 | ) : null}
222 | {showResults ? (asText
223 | ?
224 | : ) : null}
225 | {rv.error && (
226 |
227 | Ugh:
228 | {" " + rv.error + " :-("}
229 |
230 | )}
231 |
232 | );
233 | }
234 |
235 | ReactDOM.render(, document.getElementById("root"));
236 |
--------------------------------------------------------------------------------
/web/www/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "when",
3 | "version": "0.1.0",
4 | "main": "bootstrap.js",
5 | "scripts": {
6 | "build": "webpack --config webpack.prod.js",
7 | "start": "webpack-dev-server"
8 | },
9 | "license": "(MIT OR Apache-2.0)",
10 | "devDependencies": {
11 | "@babel/core": "^7.16.0",
12 | "@babel/preset-env": "^7.16.4",
13 | "@babel/preset-react": "^7.16.0",
14 | "babel-loader": "^8.2.3",
15 | "css-loader": "^6.5.1",
16 | "html-webpack-plugin": "^5.5.0",
17 | "style-loader": "^3.3.1",
18 | "webpack": "^5.56.0",
19 | "webpack-cli": "^4.9.0",
20 | "webpack-dev-server": "^4.6.0",
21 | "webpack-merge": "^5.8.0",
22 | "when": "file:../pkg"
23 | },
24 | "dependencies": {
25 | "react": "^17.0.2",
26 | "react-dom": "^17.0.2"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/web/www/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: rgb(41, 41, 40);
3 | margin: 0;
4 | padding: 20px;
5 | }
6 | a {
7 | color: #888;
8 | }
9 | a:hover {
10 | color: rgb(208, 145, 28);
11 | cursor: pointer;
12 | text-decoration: underline;
13 | }
14 | body, input {
15 | color: white;
16 | font-family: Verdana, sans-serif;
17 | font-size: 19px;
18 | }
19 | h1 {
20 | font-size: 28px;
21 | font-weight: normal;
22 | margin: 0;
23 | padding: 0 10px 0 0;
24 | display: inline;
25 | }
26 | #root {
27 | max-width: 900px;
28 | margin: 30px auto 10px auto;
29 | position: relative;
30 | }
31 | input {
32 | width: calc(100% - 16px);
33 | background: rgb(57, 57, 53);
34 | border: none;
35 | border-bottom: 2px solid #888;
36 | padding: 4px 8px;
37 | color: #aaa;
38 | font-weight: bold;
39 | margin: 20px 0 20px 0;
40 | border-radius: 0;
41 | }
42 | input:focus {
43 | outline: none;
44 | color: white;
45 | }
46 | a.clear {
47 | position: absolute;
48 | right: 10px;
49 | margin-top: 22px;
50 | text-decoration: none;
51 | color: white;
52 | }
53 | #root ul {
54 | margin: 0;
55 | padding: 0;
56 | list-style: none;
57 | }
58 | #root table {
59 | margin-bottom: 20px;
60 | }
61 | #root table th {
62 | width: 100px;
63 | font-size: 14px;
64 | text-align: right;
65 | padding-right: 7px;
66 | color: #aaa;
67 | }
68 | span.time { color: rgb(208, 145, 28); }
69 | span.date { color: rgb(23, 143, 143); }
70 | span.zone { text-decoration: underline; }
71 | p.error { color: rgb(196, 138, 117); margin: 0; }
72 | div.examples {
73 | padding: 0 0 0 20px;
74 | }
75 | div.examples h3 {
76 | font-size: 18px;
77 | margin: 0 0 8px -20px;
78 | padding: 0;
79 | }
80 |
81 | div.actions {
82 | margin-top: -10px;
83 | text-align: right;
84 | font-size: 80%;
85 | }
86 |
87 | pre {
88 | font-size: 85%;
89 | }
90 |
91 | @media only screen and (max-width: 600px) {
92 | body {
93 | font-size: 15px;
94 | }
95 | a.clear {
96 | margin-top: 25px;
97 | }
98 | #root table th {
99 | width: 70px;
100 | }
101 | }
--------------------------------------------------------------------------------
/web/www/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require('html-webpack-plugin');
2 | const path = require('path');
3 |
4 | module.exports = {
5 | entry: "./bootstrap.js",
6 | output: {
7 | path: path.resolve(__dirname, "dist"),
8 | filename: "bootstrap.js",
9 | },
10 | module: {
11 | rules: [
12 | {
13 | test: /\.?jsx?$/,
14 | exclude: /node_modules/,
15 | use: {
16 | loader: "babel-loader",
17 | options: {
18 | presets: ['@babel/preset-env', '@babel/preset-react']
19 | }
20 | }
21 | },
22 | {
23 | test: /\.css$/i,
24 | use: ["style-loader", "css-loader"],
25 | }
26 | ]
27 | },
28 | mode: "development",
29 | experiments: {
30 | asyncWebAssembly: true,
31 | },
32 | plugins: [
33 | new HtmlWebpackPlugin({
34 | title: "when?",
35 | template: "index.html"
36 | }),
37 | ],
38 | };
39 |
--------------------------------------------------------------------------------
/web/www/webpack.prod.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const common = require('./webpack.config.js');
3 |
4 | module.exports = merge(common, {
5 | mode: 'production',
6 | });
7 |
--------------------------------------------------------------------------------