{"id":15902841,"url":"https://github.com/erikh/speakable-time","last_synced_at":"2025-07-17T00:34:00.469Z","repository":{"id":216091134,"uuid":"740451994","full_name":"erikh/speakable-time","owner":"erikh","description":"Human-friendly time heavily abstracted","archived":false,"fork":false,"pushed_at":"2024-01-09T02:59:25.000Z","size":36,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-02T20:14:06.862Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/erikh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-08T11:26:25.000Z","updated_at":"2024-01-08T11:26:43.000Z","dependencies_parsed_at":"2024-01-08T13:18:49.189Z","dependency_job_id":"ee5d8b34-93ad-4aeb-baaf-2f816540b8ef","html_url":"https://github.com/erikh/speakable-time","commit_stats":{"total_commits":10,"total_committers":1,"mean_commits":10.0,"dds":0.0,"last_synced_commit":"81a65b94eeaf458244ca7c635da71ecf8b1173ba"},"previous_names":["erikh/speakable-time"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/erikh/speakable-time","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikh%2Fspeakable-time","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikh%2Fspeakable-time/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikh%2Fspeakable-time/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikh%2Fspeakable-time/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/erikh","download_url":"https://codeload.github.com/erikh/speakable-time/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikh%2Fspeakable-time/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265554790,"owners_count":23787280,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-06T12:00:22.317Z","updated_at":"2025-07-17T00:34:00.446Z","avatar_url":"https://github.com/erikh.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Human-friendly time heavily abstracted\n\nSpeakable time in this case, are time periods or intervals that are used informally, times like \"10 years ago\" or \"next week\". This library aims to deconstruct times in a way that they can be treated like a grammar for generating human-friendly times.\n\nTo accomplish this, we seed an Approximator with a list of selectors called ApproximateFilters, and a FormatGenerator, of which there are currently two styles: CoarseRoundFormat and FancyDurationFormat. The approximator computes durations into tokenized parts (called ApproximateState) which are picked out by the ApproximateFilters, which are then fed to the formatter of choice to generate a syntax. Then, a Translator is used to convert that final syntax into something you can read. It is fully localized and flexible.\n\nThere a few macros to make this easier. If the `translation` feature is enabled, it will allow you to provide translation maps as YAML files that live in your build directory. It will then select the right mapping to use for a given locale automatically on boot.\n\nThere are plenty of docs for writing your own formatter, too. Just see FormatGenerator.\n\n## Example\n\n```rust\nuse speakable_time::prelude::*;\n\nlet approx = approximator!(\n    // Use a format that lays heavily on the english\n    CoarseRoundFormat::default(),\n    // Round to the nearest day\n    ApproximateFilter::Round(TimeBoundary::Day),\n    // Indicate whether or not the compared time is in the future or past\n    ApproximateFilter::Relative\n);\n\nlet dt = chrono::Local::now() - chrono::Duration::days(2);\n\n// from_now! compares the dt against the current time and applies the approximator, and runs\n// it through the default translation.\nassert_eq!(\"2 days ago\", from_now!(dt, approx).unwrap());\n\n// Here we'll round to the nearest year and day, but not indicate future or past. Watch how\n// the output changes!\nlet approx = approximator!(\n    CoarseRoundFormat::default(),\n    ApproximateFilter::Round(TimeBoundary::Year),\n    ApproximateFilter::Round(TimeBoundary::Day)\n);\n\nlet dt2 = chrono::Local::now() + chrono::Duration::days(45 * 365 + 2); // account for leap\n                                                                       // years\nassert_eq!(\"45 years and 4 days\", time_diff!(dt2, dt, approx).unwrap());\n\nlet approx = approximator!(\n    // Here we use an abbreviated format that is also used by the `fancy-duration` crate.\n    FancyDurationFormat::default(),\n    // We're going to round to the two largest values\n    ApproximateFilter::TopRounds(2),\n    // Show past or present\n    ApproximateFilter::Relative\n);\n\nassert_eq!(\"in 45y4d\", time_diff!(dt2, dt, approx).unwrap());\n\n// Here's the same approximation applied to a date that is just 2 days from now\nassert_eq!(\n    \"in 2d\",\n    time_diff!(\n        chrono::Local::now() + chrono::Duration::days(2) + chrono::Duration::seconds(1),\n        chrono::Local::now(),\n        approx\n    ).unwrap()\n);\n```\n\n## Examples\n\nThere is an easily tweakable example in [relative-to-now](examples/relative-to-now.rs) that just accepts RFC 2822 formatted dates and converts them based on how far away they are from now. The [generate-random-times](examples/generate-random-times.rs) can be used to feed it so you can see a lot of conversions happen at once.\n\n## Tests\n\n```\ncargo test --all-features\n```\n\n## More to do\n\nFormatting can be a lot more intricate and there could be more options. I also want to bake in some translations too.\n\n## Author\n\nErik Hollensbe \u003cgit@hollensbe.org\u003e\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferikh%2Fspeakable-time","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ferikh%2Fspeakable-time","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferikh%2Fspeakable-time/lists"}