├── .editorconfig ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── shard.yml ├── spec ├── ms_spec.cr └── spec_helper.cr └── src ├── ms.cr └── ms └── version.cr /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | 10 | [*.cr] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.yml] 15 | indent_style = space 16 | indent_size = 2 17 | 18 | [*.md] 19 | indent_style = space 20 | indent_size = 2 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /doc/ 2 | /libs/ 3 | /lib/ 4 | /.crystal/ 5 | /.shards/ 6 | 7 | 8 | # Libraries don't need dependency lock 9 | # Dependencies will be locked in application that uses them 10 | /shard.lock 11 | 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: crystal 2 | 3 | script: crystal spec --verbose 4 | 5 | branches: 6 | except: 7 | - gh-pages 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.1.2 2 | 3 | - [**Renamed**] variable `type` -> `time_unit` 4 | 5 | 6 | -------------------------------------------------------------------------------- 7 | 8 | # 0.1.1 9 | 10 | - [**Added**] description into `shard.yml` 11 | 12 | 13 | -------------------------------------------------------------------------------- 14 | 15 | # 0.1.0 16 | 17 | - [**Added**] `MS.ms(String) : Int64` static method for convert string values 18 | to milliseconds. 19 | - [**Added**] `MS.ms(Int32 | Int64) : String` static method for convert 20 | milliseconds to short string format. 21 | - [**Added**] `MS.ms(Int32 | Int64, long : Bool) : String` static method for 22 | convert milliseconds to long static format. 23 | - [**Added**] specs for library. 24 | - [**Added**] meta files. 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Alexander Krivoshhekov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ms 2 | 3 | [![Linux Build][travis-image]][travis-url] 4 | [![Shards version][shards-image]][shards-url] 5 | 6 | 7 | Library to easily convert various time formats to milliseconds and 8 | milliseconds to human readable format. 9 | 10 | 11 | ## Installation 12 | 13 | Add this to your application's `shard.yml`: 14 | 15 | ```yaml 16 | dependencies: 17 | ms: 18 | github: SuperPaintman/ms 19 | ``` 20 | 21 | 22 | -------------------------------------------------------------------------------- 23 | 24 | ## Usage 25 | 26 | ```crystal 27 | require "ms" 28 | 29 | # == Strings to numbers === 30 | ms_in_5h = MS.ms("5h") 31 | # => 18000000 32 | 33 | ms_in_20sec = MS.ms("20 sec") 34 | # => 20000 35 | 36 | ms_in_an_hour_and_a_half = MS.ms("1.5 hours") 37 | # => 5400000 38 | 39 | ms_in_day = MS.ms("day") 40 | # => 86400000 41 | 42 | ms_in_2_years = MS.ms("2y") 43 | # => 63115200000 44 | 45 | 46 | # === Numbers to strings === 47 | two_ms = MS.ms(200) 48 | # => "200ms" 49 | 50 | five_sec = MS.ms(5000) 51 | # => "5s" 52 | 53 | six_hours = MS.ms(60 * 60 * 1000 * 6) 54 | # => "6h" 55 | 56 | 57 | # === Numbers to long strings === 58 | two_long_ms = MS.ms(200, long: true) 59 | # => "200 ms" 60 | 61 | five_long_sec = MS.ms(1000, long: true) 62 | # => "1 second" 63 | 64 | six_long_hours = MS.ms(60 * 60 * 1000 * 6, long: true) 65 | # => "6 hours" 66 | ``` 67 | 68 | 69 | or with including: 70 | 71 | ```crystal 72 | require "ms" 73 | include MS 74 | 75 | ms_in_5h = ms("5h") 76 | # => 18000000 77 | 78 | # ... 79 | ``` 80 | 81 | 82 | -------------------------------------------------------------------------------- 83 | 84 | ## Test 85 | 86 | ```sh 87 | crystal spec 88 | ``` 89 | 90 | 91 | -------------------------------------------------------------------------------- 92 | 93 | ## Contributing 94 | 95 | 1. Fork it () 96 | 2. Create your feature branch (`git checkout -b feature/`) 97 | 3. Commit your changes (`git commit -am 'Added some feature'`) 98 | 4. Push to the branch (`git push origin feature/`) 99 | 5. Create a new Pull Request 100 | 101 | 102 | -------------------------------------------------------------------------------- 103 | 104 | ## Contributors 105 | 106 | - [SuperPaintman](https://github.com/SuperPaintman) SuperPaintman - creator, maintainer 107 | 108 | 109 | -------------------------------------------------------------------------------- 110 | 111 | ## API 112 | [Docs][docs-url] 113 | 114 | 115 | -------------------------------------------------------------------------------- 116 | 117 | ## Changelog 118 | [Changelog][changelog-url] 119 | 120 | 121 | -------------------------------------------------------------------------------- 122 | 123 | ## License 124 | 125 | [MIT][license-url] 126 | 127 | 128 | [license-url]: LICENSE 129 | [changelog-url]: CHANGELOG.md 130 | [docs-url]: https://superpaintman.github.io/ms/ 131 | [travis-image]: https://img.shields.io/travis/SuperPaintman/ms/master.svg?label=linux 132 | [travis-url]: https://travis-ci.org/SuperPaintman/ms 133 | [shards-image]: https://img.shields.io/github/tag/superpaintman/ms.svg?label=shards 134 | [shards-url]: https://github.com/superpaintman/ms 135 | 136 | -------------------------------------------------------------------------------- /shard.yml: -------------------------------------------------------------------------------- 1 | name: ms 2 | version: 0.1.2 3 | 4 | authors: 5 | - SuperPaintman 6 | 7 | description: | 8 | Library to easily convert various time formats to milliseconds and 9 | milliseconds to human readable format. 10 | 11 | license: MIT 12 | -------------------------------------------------------------------------------- /spec/ms_spec.cr: -------------------------------------------------------------------------------- 1 | require "./spec_helper" 2 | 3 | describe MS do 4 | # String 5 | describe ".ms(String)" do 6 | it "should not raise an error" do 7 | begin 8 | MS.ms("1m") 9 | rescue err 10 | err.should be_nil 11 | end 12 | end 13 | 14 | it "should preserve \"ms\"" do 15 | MS.ms("100").should eq 100 16 | end 17 | 18 | it "should prepend time if it not exist" do 19 | MS.ms("day").should eq 86400000 20 | end 21 | 22 | it "should convert \"s\" to ms" do 23 | MS.ms("1s").should eq 1000 24 | end 25 | 26 | it "should convert from \"m\" to ms" do 27 | MS.ms("1m").should eq 60000 28 | end 29 | 30 | it "should convert from \"h\" to ms" do 31 | MS.ms("1h").should eq 3600000 32 | end 33 | 34 | it "should convert \"d\" to ms" do 35 | MS.ms("2d").should eq 172800000 36 | end 37 | 38 | it "should convert \"y\" to ms" do 39 | MS.ms("1y").should eq 31557600000 40 | end 41 | 42 | it "should convert \"ms\" to ms" do 43 | MS.ms("100ms").should eq 100 44 | end 45 | 46 | it "should work with decimals" do 47 | MS.ms("1.5h").should eq 5400000 48 | end 49 | 50 | it "should work with multiple spaces" do 51 | MS.ms("1 s").should eq 1000 52 | end 53 | 54 | it "should work with tabs" do 55 | MS.ms("1\ts").should eq 1000 56 | MS.ms("1\t\t\t\t\ts").should eq 1000 57 | end 58 | 59 | it "should work with mixed tabs and spaces" do 60 | MS.ms("1 \ts").should eq 1000 61 | MS.ms("1\t\t \t\ts").should eq 1000 62 | end 63 | 64 | it "should be case-insensitive" do 65 | MS.ms("1.5H").should eq 5400000 66 | end 67 | 68 | it "should work with numbers starting with \".\"" do 69 | MS.ms(".5s").should eq 500 70 | end 71 | 72 | it "should round" do 73 | MS.ms("0.2ms").should eq 0 74 | MS.ms("0.5ms").should eq 1 75 | MS.ms("0.9ms").should eq 1 76 | end 77 | end 78 | 79 | # Long Strings 80 | describe ".ms(String) # long" do 81 | it "should not raise an error" do 82 | begin 83 | MS.ms("53 milliseconds") 84 | rescue err 85 | err.should be_nil 86 | end 87 | end 88 | 89 | it "should convert \"milliseconds\", \"millisecond\", \"msecs\", \"msec\", \"ms\" to ms" do 90 | ["milliseconds", "millisecond", "msecs", "msec", "ms"].each do |time_unit| 91 | MS.ms("53 #{time_unit}").should eq 53 92 | end 93 | end 94 | 95 | it "should convert \"seconds\", \"second\", \"secs\", \"sec\", \"s\" to ms" do 96 | ["seconds", "second", "secs", "sec", "s"].each do |time_unit| 97 | MS.ms("1 #{time_unit}").should eq 1000 98 | end 99 | end 100 | 101 | it "should convert from \"minutes\", \"minute\", \"mins\", \"min\", \"m\" to ms" do 102 | ["minutes", "minute", "mins", "min", "m"].each do |time_unit| 103 | MS.ms("1 #{time_unit}").should eq 60000 104 | end 105 | end 106 | 107 | it "should convert from \"hours\", \"hour\", \"hrs\", \"hr\", \"h\" to ms" do 108 | ["hours", "hour", "hrs", "hr", "h"].each do |time_unit| 109 | MS.ms("1 #{time_unit}").should eq 3600000 110 | end 111 | end 112 | 113 | it "should convert \"days\", \"day\", \"d\" to ms" do 114 | ["days", "day", "d"].each do |time_unit| 115 | MS.ms("2 #{time_unit}").should eq 172800000 116 | end 117 | end 118 | 119 | it "should convert \"years\", \"year\", \"yrs\", \"yr\", \"y\" to ms" do 120 | ["years", "year", "yrs", "yr", "y"].each do |time_unit| 121 | MS.ms("2 #{time_unit}").should eq 63115200000 122 | end 123 | end 124 | 125 | it "should work with decimals" do 126 | MS.ms("1.5 hours").should eq 5400000 127 | end 128 | end 129 | 130 | # Int32 | Int64 131 | describe ".ms(Int32 | Int64)" do 132 | it "should not raise an error" do 133 | begin 134 | MS.ms(100) 135 | rescue err 136 | err.should be_nil 137 | end 138 | end 139 | 140 | it "should support milliseconds" do 141 | MS.ms(100).should eq "100ms" 142 | MS.ms(500).should eq "500ms" 143 | MS.ms(900).should eq "900ms" 144 | end 145 | 146 | it "should support seconds" do 147 | MS.ms(1000 * 1).should eq "1s" 148 | MS.ms(1000 * 5).should eq "5s" 149 | MS.ms(1000 * 10).should eq "10s" 150 | MS.ms(1000 * 29).should eq "29s" 151 | end 152 | 153 | it "should support minutes" do 154 | MS.ms(60 * 1000 * 1).should eq "1m" 155 | MS.ms(60 * 1000 * 5).should eq "5m" 156 | MS.ms(60 * 1000 * 10).should eq "10m" 157 | MS.ms(60 * 1000 * 29).should eq "29m" 158 | end 159 | 160 | it "should support hours" do 161 | MS.ms(60 * 60 * 1000 * 1).should eq "1h" 162 | MS.ms(60 * 60 * 1000 * 5).should eq "5h" 163 | MS.ms(60 * 60 * 1000 * 10).should eq "10h" 164 | MS.ms(60 * 60 * 1000 * 23).should eq "23h" 165 | end 166 | 167 | it "should support days" do 168 | MS.ms(24 * 60 * 60 * 1000 * 1).should eq "1d" 169 | MS.ms(24 * 60 * 60 * 1000 * 5).should eq "5d" 170 | MS.ms(24 * 60 * 60 * 1000 * 10).should eq "10d" 171 | MS.ms(24 * 60 * 60 * 1000 * 23).should eq "23d" 172 | end 173 | 174 | it "should support years" do 175 | MS.ms((365.25 * 24 * 60 * 60 * 1000 * 1).round.to_i64).should eq "1y" 176 | MS.ms((365.25 * 24 * 60 * 60 * 1000 * 5).round.to_i64).should eq "5y" 177 | MS.ms((365.25 * 24 * 60 * 60 * 1000 * 10).round.to_i64).should eq "10y" 178 | MS.ms((365.25 * 24 * 60 * 60 * 1000 * 23).round.to_i64).should eq "23y" 179 | end 180 | 181 | it "should round" do 182 | MS.ms(575675089).should eq "6d" 183 | end 184 | end 185 | 186 | # Int32 | Int64, long: true 187 | describe ".ms(Int32 | Int64, long: true)" do 188 | it "should not raise an error" do 189 | begin 190 | MS.ms(500, long: true) 191 | rescue err 192 | err.should be_nil 193 | end 194 | end 195 | 196 | it "should support milliseconds" do 197 | MS.ms(100, long: true).should eq "100 ms" 198 | MS.ms(500, long: true).should eq "500 ms" 199 | MS.ms(900, long: true).should eq "900 ms" 200 | end 201 | 202 | it "should support seconds" do 203 | MS.ms(1000 * 1, long: true).should eq "1 second" 204 | MS.ms((1000 * 1.2).round.to_i64, long: true).should eq "1 second" 205 | MS.ms(1000 * 10, long: true).should eq "10 seconds" 206 | end 207 | 208 | it "should support minutes" do 209 | MS.ms(60 * 1000 * 1, long: true).should eq "1 minute" 210 | MS.ms((60 * 1000 * 1.2).round.to_i64, long: true).should eq "1 minute" 211 | MS.ms(60 * 1000 * 10, long: true).should eq "10 minutes" 212 | end 213 | 214 | it "should support hours" do 215 | MS.ms(60 * 60 * 1000 * 1, long: true).should eq "1 hour" 216 | MS.ms((60 * 60 * 1000 * 1.2).round.to_i64, long: true).should eq "1 hour" 217 | MS.ms(60 * 60 * 1000 * 10, long: true).should eq "10 hours" 218 | end 219 | 220 | it "should support days" do 221 | MS.ms(24 * 60 * 60 * 1000 * 1, long: true).should eq "1 day" 222 | MS.ms((24 * 60 * 60 * 1000 * 1.2).round.to_i64, long: true).should eq "1 day" 223 | MS.ms(24 * 60 * 60 * 1000 * 10, long: true).should eq "10 days" 224 | end 225 | 226 | it "should support years" do 227 | MS.ms((365.25 * 24 * 60 * 60 * 1000 * 1).round.to_i64, long: true).should eq "1 year" 228 | MS.ms((365.25 * 24 * 60 * 60 * 1000 * 1.2).round.to_i64, long: true).should eq "1 year" 229 | MS.ms((365.25 * 24 * 60 * 60 * 1000 * 10).round.to_i64, long: true).should eq "10 years" 230 | end 231 | 232 | it "should round" do 233 | MS.ms(575675089, long: true).should eq "6 days" 234 | end 235 | end 236 | 237 | # Invalid vals 238 | describe ".ms(Invalid vals)" do 239 | it "should raise an error, when MS.ms(\"\")" do 240 | begin 241 | MS.ms("") 242 | rescue err 243 | err.message.should eq "Invalid time format" 244 | else 245 | raise "should raise an error" 246 | end 247 | end 248 | 249 | it "should raise an error, when MS.ms(\" \")" do 250 | begin 251 | MS.ms(" ") 252 | rescue err 253 | err.message.should eq "Invalid time format" 254 | else 255 | raise "should raise an error" 256 | end 257 | end 258 | 259 | it "should raise an error, when MS.ms(\"\\t\")" do 260 | begin 261 | MS.ms("\t") 262 | rescue err 263 | err.message.should eq "Invalid time format" 264 | else 265 | raise "should raise an error" 266 | end 267 | end 268 | 269 | it "should raise an error, when MS.ms(\"\\t \")" do 270 | begin 271 | MS.ms("\t ") 272 | MS.ms(" \t ") 273 | MS.ms("\t \t ") 274 | MS.ms("\t \t") 275 | rescue err 276 | err.message.should eq "Invalid time format" 277 | else 278 | raise "should raise an error" 279 | end 280 | end 281 | 282 | it "should raise an error, when MS.ms(\"10 waffles\")" do 283 | begin 284 | MS.ms("10 waffles") 285 | rescue err 286 | err.message.should eq "Invalid time format" 287 | else 288 | raise "should raise an error" 289 | end 290 | end 291 | 292 | it "should raise an error, when MS.ms(\"waffles\")" do 293 | begin 294 | MS.ms("10 waffles") 295 | rescue err 296 | err.message.should eq "Invalid time format" 297 | else 298 | raise "should raise an error" 299 | end 300 | end 301 | end 302 | end 303 | -------------------------------------------------------------------------------- /spec/spec_helper.cr: -------------------------------------------------------------------------------- 1 | require "spec" 2 | require "../src/ms" 3 | -------------------------------------------------------------------------------- /src/ms.cr: -------------------------------------------------------------------------------- 1 | require "./ms/*" 2 | 3 | # Constants 4 | private S = 1000 5 | private M = S * 60 6 | private H = M * 60 7 | private D = H * 24 8 | private Y = D * 365.25 9 | 10 | private REGEXP_STR = /^((?:\d+)?\.?\d+)?[\ \t]*(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i 11 | 12 | private def parse_str(str : String) : Int64 13 | res = REGEXP_STR.match(str) 14 | 15 | if res.nil? || (res[1]?.nil? && res[2]?.nil?) 16 | raise "Invalid time format" 17 | end 18 | 19 | begin 20 | n = res.try(&.[1]) 21 | n = n.to_s.to_f 22 | rescue err 23 | n = 1.0 24 | end 25 | n = n.as(Float64) 26 | 27 | begin 28 | time_unit = res.try(&.[2]) 29 | time_unit = time_unit.to_s.downcase 30 | rescue err 31 | time_unit = "ms" 32 | end 33 | 34 | case time_unit 35 | when "years", "year", "yrs", "yr", "y" 36 | return (n * Y).round.to_i64 37 | when "days", "day", "d" 38 | return (n * D).round.to_i64 39 | when "hours", "hour", "hrs", "hr", "h" 40 | return (n * H).round.to_i64 41 | when "minutes", "minute", "mins", "min", "m" 42 | return (n * M).round.to_i64 43 | when "seconds", "second", "secs", "sec", "s" 44 | return (n * S).round.to_i64 45 | when "milliseconds", "millisecond", "msecs", "msec", "ms" 46 | return (n).round.to_i64 47 | else 48 | raise "Unknown time unit: \"#{time_unit}\"" 49 | end 50 | end 51 | 52 | private def fmt_short(ms : Int64) : String 53 | if ms >= Y 54 | return (ms / Y).round.to_i64.to_s + "y" 55 | end 56 | 57 | if ms >= D 58 | return (ms / D).round.to_i64.to_s + "d" 59 | end 60 | 61 | if ms >= H 62 | return (ms / H).round.to_i64.to_s + "h" 63 | end 64 | 65 | if ms >= M 66 | return (ms / M).round.to_i64.to_s + "m" 67 | end 68 | 69 | if ms >= S 70 | return (ms / S).round.to_i64.to_s + "s" 71 | end 72 | 73 | ms.to_i64.to_s + "ms" 74 | end 75 | 76 | private def fmt_long(ms : Int64) : String 77 | if ms >= Y 78 | if ms < Y * 1.5 79 | return (ms / Y).floor.to_i64.to_s + " year" 80 | else 81 | return (ms / Y).ceil.to_i64.to_s + " years" 82 | end 83 | end 84 | 85 | if ms >= D 86 | if ms < D * 1.5 87 | return (ms / D).floor.to_i64.to_s + " day" 88 | else 89 | return (ms / D).ceil.to_i64.to_s + " days" 90 | end 91 | end 92 | 93 | if ms >= H 94 | if ms < H * 1.5 95 | return (ms / H).floor.to_i64.to_s + " hour" 96 | else 97 | return (ms / H).ceil.to_i64.to_s + " hours" 98 | end 99 | end 100 | 101 | if ms >= M 102 | if ms < M * 1.5 103 | return (ms / M).floor.to_i64.to_s + " minute" 104 | else 105 | return (ms / M).ceil.to_i64.to_s + " minutes" 106 | end 107 | end 108 | 109 | if ms >= S 110 | if ms < S * 1.5 111 | return (ms / S).floor.to_i64.to_s + " second" 112 | else 113 | return (ms / S).ceil.to_i64.to_s + " seconds" 114 | end 115 | end 116 | 117 | ms.to_i64.to_s + " ms" 118 | end 119 | 120 | module MS 121 | extend self 122 | 123 | # Convert string to an integer of the milliseconds. 124 | # 125 | # ``` 126 | # MS.ms("5h") # => 18000000 127 | # ``` 128 | def ms(val : String) : Int64 129 | parse_str(val) 130 | end 131 | 132 | # Convert integer of the milliseconds to an formatted string. 133 | # 134 | # ``` 135 | # # Short format 136 | # MS.ms(60 * 60 * 1000 * 6) # => "6h" 137 | # 138 | # # Long format 139 | # MS.ms(1000, long: true) # => "1 second" 140 | # ``` 141 | def ms(val : Int32 | Int64, long = false) : String 142 | if long 143 | fmt_long(val.to_i64) 144 | else 145 | fmt_short(val.to_i64) 146 | end 147 | end 148 | end 149 | -------------------------------------------------------------------------------- /src/ms/version.cr: -------------------------------------------------------------------------------- 1 | module MS 2 | VERSION = "0.1.2" 3 | end 4 | --------------------------------------------------------------------------------