prettyunits/0000755000176200001440000000000013612511637012671 5ustar liggesusersprettyunits/NAMESPACE0000644000176200001440000000026213605314356014111 0ustar liggesusers# Generated by roxygen2: do not edit by hand export(compute_bytes) export(pretty_bytes) export(pretty_dt) export(pretty_ms) export(pretty_sec) export(time_ago) export(vague_dt) prettyunits/LICENSE0000644000176200001440000000005213214725500013665 0ustar liggesusersYEAR: 2014 COPYRIGHT HOLDER: Gabor Csardi prettyunits/README.md0000644000176200001440000000762313612450222014150 0ustar liggesusers [![Linux Build Status](https://travis-ci.org/r-lib/prettyunits.svg?branch=master)](https://travis-ci.org/r-lib/prettyunits) [![Windows Build status](https://ci.appveyor.com/api/projects/status/github/r-lib/prettyunits?svg=true)](https://ci.appveyor.com/project/gaborcsardi/prettyunits) [![CRAN RStudio mirror downloads](http://cranlogs.r-pkg.org/badges/prettyunits)](https://CRAN.R-project.org/package=prettyunits) # prettyunits The `prettyunits` package formats quantities in human readable form. Currently time units and information (i.e. bytes) are supported. ## Installation You can install the package from CRAN: ```r install.packages("prettyunits") ``` ```r library(prettyunits) library(magrittr) ``` ## Bytes `pretty_bytes` formats number of bytes in a human readable way: ```r pretty_bytes(1337) ``` ``` ##> [1] "1.34 kB" ``` ```r pretty_bytes(133337) ``` ``` ##> [1] "133.34 kB" ``` ```r pretty_bytes(13333337) ``` ``` ##> [1] "13.33 MB" ``` ```r pretty_bytes(1333333337) ``` ``` ##> [1] "1.33 GB" ``` ```r pretty_bytes(133333333337) ``` ``` ##> [1] "133.33 GB" ``` Here is a simple function that emulates the Unix `ls` command, with nicely formatted file sizes: ```r uls <- function(path = ".") { files <- dir(path) info <- files %>% lapply(file.info) %>% do.call(what = rbind) info$size <- pretty_bytes(info$size) df <- data.frame(d = ifelse(info$isdir, "d", " "), mode = as.character(info$mode), user = info$uname, group = info$grname, size = ifelse(info$isdir, "", info$size), modified = info$mtime, name = files) print(df, row.names = FALSE) } uls() ``` ``` ##> d mode user group size modified name ##> 644 gaborcsardi staff 440.00 B 2019-03-25 10:25:08 NEWS.md ##> 644 gaborcsardi staff 4.65 kB 2017-12-15 11:00:16 README.md ##> 644 gaborcsardi staff 2.95 kB 2019-03-27 09:58:43 README.Rmd ``` ## Time intervals `pretty_ms` formats a time interval given in milliseconds. `pretty_sec` does the same for seconds, and `pretty_dt` for `difftime` objects. The optional `compact` argument turns on a compact, approximate format. ```r pretty_ms(c(1337, 13370, 133700, 1337000, 1337000000)) ``` ``` ##> [1] "1.3s" "13.4s" "2m 13.7s" "22m 17s" ##> [5] "15d 11h 23m 20s" ``` ```r pretty_ms(c(1337, 13370, 133700, 1337000, 1337000000), compact = TRUE) ``` ``` ##> [1] "~1.3s" "~13.4s" "~2m" "~22m" "~15d" ``` ```r pretty_sec(c(1337, 13370, 133700, 1337000, 13370000)) ``` ``` ##> [1] "22m 17s" "3h 42m 50s" "1d 13h 8m 20s" ##> [4] "15d 11h 23m 20s" "154d 17h 53m 20s" ``` ```r pretty_sec(c(1337, 13370, 133700, 1337000, 13370000), compact = TRUE) ``` ``` ##> [1] "~22m" "~3h" "~1d" "~15d" "~154d" ``` ## Vague time intervals `vague_dt` and `time_ago` formats time intervals using a vague format, omitting smaller units. They both have three formats: `default`, `short` and `terse`. `vague_dt` takes a `difftime` object, and `time_ago` works relatively to the specified date. ```r vague_dt(format = "short", as.difftime(30, units = "secs")) ``` ``` ##> [1] "<1 min" ``` ```r vague_dt(format = "short", as.difftime(14, units = "mins")) ``` ``` ##> [1] "14 min" ``` ```r vague_dt(format = "short", as.difftime(5, units = "hours")) ``` ``` ##> [1] "5 hours" ``` ```r vague_dt(format = "short", as.difftime(25, units = "hours")) ``` ``` ##> [1] "1 day" ``` ```r vague_dt(format = "short", as.difftime(5, units = "days")) ``` ``` ##> [1] "5 day" ``` ```r now <- Sys.time() time_ago(now) ``` ``` ##> [1] "moments ago" ``` ```r time_ago(now - as.difftime(30, units = "secs")) ``` ``` ##> [1] "less than a minute ago" ``` ```r time_ago(now - as.difftime(14, units = "mins")) ``` ``` ##> [1] "14 minutes ago" ``` ```r time_ago(now - as.difftime(5, units = "hours")) ``` ``` ##> [1] "5 hours ago" ``` ```r time_ago(now - as.difftime(25, units = "hours")) ``` ``` ##> [1] "a day ago" ``` prettyunits/man/0000755000176200001440000000000013605174772013453 5ustar liggesusersprettyunits/man/time_ago.Rd0000644000176200001440000000432613601077733015526 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/time-ago.R \name{time_ago} \alias{time_ago} \title{Human readable format of the time interval since a time point} \usage{ time_ago(date, format = c("default", "short", "terse")) } \arguments{ \item{date}{Date(s), \code{as.POSIXct} will be called on them.} \item{format}{Format, currently available formats are: \sQuote{default}, \sQuote{short}, \sQuote{terse}. See examples below.} } \value{ Character vector of the formatted time intervals. } \description{ It calls \code{\link{vague_dt}} to do the actual formatting. } \examples{ now <- Sys.time() time_ago(now) time_ago(now - as.difftime(30, units = "secs")) time_ago(now - as.difftime(14, units = "mins")) time_ago(now - as.difftime(5, units = "hours")) time_ago(now - as.difftime(25, units = "hours")) time_ago(now - as.difftime(5, units = "days")) time_ago(now - as.difftime(30, units = "days")) time_ago(now - as.difftime(365, units = "days")) time_ago(now - as.difftime(365 * 10, units = "days")) ## Short format time_ago(format = "short", now) time_ago(format = "short", now - as.difftime(30, units = "secs")) time_ago(format = "short", now - as.difftime(14, units = "mins")) time_ago(format = "short", now - as.difftime(5, units = "hours")) time_ago(format = "short", now - as.difftime(25, units = "hours")) time_ago(format = "short", now - as.difftime(5, units = "days")) time_ago(format = "short", now - as.difftime(30, units = "days")) time_ago(format = "short", now - as.difftime(365, units = "days")) time_ago(format = "short", now - as.difftime(365 * 10, units = "days")) ## Even shorter, terse format, (almost always) exactly 3 characters wide time_ago(format = "terse", now) time_ago(format = "terse", now - as.difftime(30, units = "secs")) time_ago(format = "terse", now - as.difftime(14, units = "mins")) time_ago(format = "terse", now - as.difftime(5, units = "hours")) time_ago(format = "terse", now - as.difftime(25, units = "hours")) time_ago(format = "terse", now - as.difftime(5, units = "days")) time_ago(format = "terse", now - as.difftime(30, units = "days")) time_ago(format = "terse", now - as.difftime(365, units = "days")) time_ago(format = "terse", now - as.difftime(365 * 10, units = "days")) } prettyunits/man/vague_dt.Rd0000644000176200001440000000366513601077733015545 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/time-ago.R \name{vague_dt} \alias{vague_dt} \title{Human readable format of a time interval} \usage{ vague_dt(dt, format = c("default", "short", "terse")) } \arguments{ \item{dt}{A \code{difftime} object, the time interval(s).} \item{format}{Format, currently available formats are: \sQuote{default}, \sQuote{short}, \sQuote{terse}. See examples below.} } \value{ Character vector of the formatted time intervals. } \description{ Human readable format of a time interval } \examples{ vague_dt(as.difftime(30, units = "secs")) vague_dt(as.difftime(14, units = "mins")) vague_dt(as.difftime(5, units = "hours")) vague_dt(as.difftime(25, units = "hours")) vague_dt(as.difftime(5, units = "days")) vague_dt(as.difftime(30, units = "days")) vague_dt(as.difftime(365, units = "days")) vague_dt(as.difftime(365 * 10, units = "days")) ## Short format vague_dt(format = "short", as.difftime(30, units = "secs")) vague_dt(format = "short", as.difftime(14, units = "mins")) vague_dt(format = "short", as.difftime(5, units = "hours")) vague_dt(format = "short", as.difftime(25, units = "hours")) vague_dt(format = "short", as.difftime(5, units = "days")) vague_dt(format = "short", as.difftime(30, units = "days")) vague_dt(format = "short", as.difftime(365, units = "days")) vague_dt(format = "short", as.difftime(365 * 10, units = "days")) ## Even shorter, terse format, (almost always) exactly 3 characters wide vague_dt(format = "terse", as.difftime(30, units = "secs")) vague_dt(format = "terse", as.difftime(14, units = "mins")) vague_dt(format = "terse", as.difftime(5, units = "hours")) vague_dt(format = "terse", as.difftime(25, units = "hours")) vague_dt(format = "terse", as.difftime(5, units = "days")) vague_dt(format = "terse", as.difftime(30, units = "days")) vague_dt(format = "terse", as.difftime(365, units = "days")) vague_dt(format = "terse", as.difftime(365 * 10, units = "days")) } prettyunits/man/pretty_dt.Rd0000644000176200001440000000131313601366670015752 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/time.R \name{pretty_dt} \alias{pretty_dt} \title{Pretty formatting of time intervals (difftime objects)} \usage{ pretty_dt(dt, compact = FALSE) } \arguments{ \item{dt}{A \code{difftime} object, a vector of time differences.} \item{compact}{If true, then only the first non-zero unit is used. See examples below.} } \value{ Character vector of formatted time intervals. } \description{ Pretty formatting of time intervals (difftime objects) } \examples{ pretty_dt(as.difftime(1000, units = "secs")) pretty_dt(as.difftime(0, units = "secs")) } \seealso{ Other time: \code{\link{pretty_ms}()}, \code{\link{pretty_sec}()} } \concept{time} prettyunits/man/pretty_ms.Rd0000644000176200001440000000127213601366670015766 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/time.R \name{pretty_ms} \alias{pretty_ms} \title{Pretty formatting of milliseconds} \usage{ pretty_ms(ms, compact = FALSE) } \arguments{ \item{ms}{Numeric vector of milliseconds} \item{compact}{If true, then only the first non-zero unit is used. See examples below.} } \value{ Character vector of formatted time intervals. } \description{ Pretty formatting of milliseconds } \examples{ pretty_ms(c(1337, 13370, 133700, 1337000, 1337000000)) pretty_ms(c(1337, 13370, 133700, 1337000, 1337000000), compact = TRUE) } \seealso{ Other time: \code{\link{pretty_dt}()}, \code{\link{pretty_sec}()} } \concept{time} prettyunits/man/pretty_bytes.Rd0000644000176200001440000000252113605314357016472 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/xsizes-docs.R \name{pretty_bytes} \alias{pretty_bytes} \alias{compute_bytes} \title{Bytes in a human readable string} \usage{ pretty_bytes(bytes, style = c("default", "nopad", "6")) compute_bytes(bytes, smallest_unit = "B") } \arguments{ \item{bytes}{Numeric vector, number of bytes.} \item{style}{Formatting style: \itemize{ \item \code{"default"} is the original \code{pretty_bytes} formatting, and it always pads the output, so that all vector elements are of the same width, \item \code{"nopad"} is similar, but does not pad the output, \item \code{"6"} always uses 6 characters, The \code{"6"} style is useful if it is important that the output always has the same width (number of characters), e.g. in progress bars. See some examples below. }} \item{smallest_unit}{A character scalar, the smallest unit to use.} } \value{ Character vector, the formatted sizes. For \code{compute_bytes}, a data frame with columns \code{amount}, \code{unit}, \code{negative}. } \description{ Use \code{pretty_bytes()} to format bytes. \code{compute_bytes()} is the underlying engine that may be useful for custom formatting. } \examples{ bytes <- c(1337, 133337, 13333337, 1333333337, 133333333337) pretty_bytes(bytes) pretty_bytes(bytes, style = "nopad") pretty_bytes(bytes, style = "6") } prettyunits/man/pretty_sec.Rd0000644000176200001440000000125713601366670016124 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/time.R \name{pretty_sec} \alias{pretty_sec} \title{Pretty formatting of seconds} \usage{ pretty_sec(sec, compact = FALSE) } \arguments{ \item{sec}{Numeric vector of seconds.} \item{compact}{If true, then only the first non-zero unit is used. See examples below.} } \value{ Character vector of formatted time intervals. } \description{ Pretty formatting of seconds } \examples{ pretty_sec(c(1337, 13370, 133700, 1337000, 13370000)) pretty_sec(c(1337, 13370, 133700, 1337000, 13370000), compact = TRUE) } \seealso{ Other time: \code{\link{pretty_dt}()}, \code{\link{pretty_ms}()} } \concept{time} prettyunits/man/prettyunits.Rd0000644000176200001440000000036413601366670016353 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/pretty-package.R \docType{package} \name{prettyunits} \alias{prettyunits} \title{Prettier formatting of quantities} \description{ Prettier formatting of quantities } prettyunits/DESCRIPTION0000644000176200001440000000131713612511637014401 0ustar liggesusersPackage: prettyunits Title: Pretty, Human Readable Formatting of Quantities Version: 1.1.1 Author: Gabor Csardi Maintainer: Gabor Csardi Description: Pretty, human readable formatting of quantities. Time intervals: '1337000' -> '15d 11h 23m 20s'. Vague time intervals: '2674000' -> 'about a month ago'. Bytes: '1337' -> '1.34 kB'. License: MIT + file LICENSE LazyData: true URL: https://github.com/gaborcsardi/prettyunits BugReports: https://github.com/gaborcsardi/prettyunits/issues Suggests: codetools, covr, testthat RoxygenNote: 7.0.2 Encoding: UTF-8 NeedsCompilation: no Packaged: 2020-01-24 02:16:46 UTC; gaborcsardi Repository: CRAN Date/Publication: 2020-01-24 06:50:07 UTC prettyunits/tests/0000755000176200001440000000000013214725500014025 5ustar liggesusersprettyunits/tests/testthat/0000755000176200001440000000000013612511637015673 5ustar liggesusersprettyunits/tests/testthat/test-vague-dt.r0000644000176200001440000000366513214725500020553 0ustar liggesusers context("Vague time intervals") ## Amount, unit, default result, short, terse all_tests <- list( list( 1, "secs", "moments ago", "<1 min", " 1s"), list( 30, "secs", "less than a minute ago", "<1 min", "30s"), list( 50, "secs", "about a minute ago", "1 min", " 1m"), list( 14, "mins", "14 minutes ago", "14 min", "14m"), list( 70, "mins", "about an hour ago", "1 hour", " 1h"), list( 5, "hours", "5 hours ago", "5 hours", " 5h"), list( 25, "hours", "a day ago", "1 day", " 1d"), list( 5, "days", "5 days ago", "5 day", " 5d"), list( 30, "days", "about a month ago", "1 mon", " 1M"), list( 30 * 3, "days", "3 months ago", "3 mon", " 3M"), list(365, "days", "about a year ago", "1 year", " 1y"), list(365 * 10, "days", "10 years ago", "10 years", "10y") ) test_that("vague_dt works", { sapply(all_tests, function(case) { dt <- as.difftime(case[[1]], units = case[[2]]) default <- vague_dt(dt) short <- vague_dt(dt, format = "short") terse <- vague_dt(dt, format = "terse") expect_equal(default, case[[3]], info = paste(case[[1]], case[[2]], "default")) expect_equal(short, case[[4]], info = paste(case[[1]], case[[2]], "short")) expect_equal(terse, case[[5]], info = paste(case[[1]], case[[2]], "terse")) }) }) test_that("time_ago works", { sapply(all_tests, function(case) { t <- Sys.time() - as.difftime(case[[1]], units = case[[2]]) default <- time_ago(t) short <- time_ago(t, format = "short") terse <- time_ago(t, format = "terse") expect_equal(default, case[[3]], info = paste(case[[1]], case[[2]], "default")) expect_equal(short, case[[4]], info = paste(case[[1]], case[[2]], "short")) expect_equal(terse, case[[5]], info = paste(case[[1]], case[[2]], "terse")) }) }) prettyunits/tests/testthat/test-bytes.r0000644000176200001440000000740213612447567020176 0ustar liggesusers context("Pretty bytes") test_that("sizes.R is standalone", { stenv <- environment(format_bytes$pretty_bytes) objs <- ls(stenv, all.names = TRUE) funs <- Filter(function(x) is.function(stenv[[x]]), objs) funobjs <- mget(funs, stenv) for (f in funobjs) expect_identical(environmentName(topenv(f)), "base") expect_message( mapply(codetools::checkUsage, funobjs, funs, MoreArgs = list(report = message)), NA) }) test_that("pretty_bytes gives errors on invalid input", { expect_error(pretty_bytes(''), 'is.numeric.*is not TRUE') expect_error(pretty_bytes('1'), 'is.numeric.*is not TRUE') expect_error(pretty_bytes(TRUE), 'is.numeric.*is not TRUE') expect_error(pretty_bytes(list(1,2,3)), 'is.numeric.*is not TRUE') }) test_that("pretty_bytes converts properly", { expect_equal(pretty_bytes(0), '0 B') expect_equal(pretty_bytes(10), '10 B') expect_equal(pretty_bytes(999), '999 B') expect_equal(pretty_bytes(1001), '1.00 kB') expect_equal(pretty_bytes(1000 * 1000 - 1), '1.00 MB') expect_equal(pretty_bytes(1e16), '10 PB') expect_equal(pretty_bytes(1e30), '1000000 YB') }) test_that("pretty_bytes handles NA and NaN", { expect_equal(pretty_bytes(NA_real_), "NA B") expect_equal(pretty_bytes(NA_integer_), "NA B") expect_error(pretty_bytes(NA_character_), 'is.numeric.*is not TRUE') expect_error(pretty_bytes(NA), 'is.numeric.*is not TRUE') expect_equal(pretty_bytes(NaN), "NaN B") }) test_that("pretty_bytes handles vectors", { expect_equal(pretty_bytes(1:10), paste(format(1:10), "B")) v <- c(NA, 1, 1e4, 1e6, NaN, 1e5) expect_equal(pretty_bytes(v), c(" NA B", " 1 B", " 10 kB", " 1 MB", " NaN B", "100 kB")) expect_equal(pretty_bytes(numeric()), character()) }) test_that("pretty_bytes nopad style", { v <- c(NA, 1, 1e4, 1e6, NaN, 1e5) expect_equal(pretty_bytes(v, style = "nopad"), c("NA B", "1 B", "10 kB", "1 MB", "NaN B", "100 kB")) expect_equal(pretty_bytes(numeric(), style = "nopad"), character()) }) test_that("pretty_bytes handles negative values", { v <- c(NA, -1, 1e4, 1e6, NaN, -1e5) expect_equal(pretty_bytes(v), c(" NA B", " -1 B", " 10 kB", " 1 MB", " NaN B", "-100 kB")) }) test_that("always two fraction digits", { expect_equal( pretty_bytes(c(5.6, 5, NA) * 1000 * 1000), c("5.60 MB", " 5 MB", " NA B") ) }) test_that("6 width style", { cases <- c( "< 0 kB" = -1e4, # 1 "< 0 kB" = -100, # 2 "< 0 kB" = -1, # 3 "0.0 kB" = 0, # 4 "0.0 kB" = 1, # 5 "0.0 kB" = 9, # 6 "0.0 kB" = 9.99999, # 7 "0.0 kB" = 10.33333, # 8 "0.1 kB" = 100, # 9 "0.1 kB" = 111.33333, # 10 "1.0 kB" = 1e3, # 11 "1.0 kB" = 1049, # 12 "1.1 kB" = 1051, # 13 "1.1 kB" = 1100, # 14 " 10 kB" = 1e4, # 15 "100 kB" = 1e5, # 16 "1.0 MB" = 1e6, # 17 "NaN kB" = NaN, # 18 " NA kB" = NA # 19 ) expect_equal(pretty_bytes(unname(cases), style = "6"), names(cases)) }) test_that("No fractional bytes (#23)", { cases <- c( " -1 B" = -1, # 1 " 1 B" = 1, # 2 " 16 B" = 16, # 3 " 128 B" = 128, # 4 " 1.02 kB" = 1024, # 5 "16.38 kB" = 16384, # 6 " 1.05 MB" = 1048576, # 7 "-1.05 MB" = -1048576, # 8 " NA B" = NA # 9 ) expect_equal(pretty_bytes(unname(cases)), names(cases)) }) prettyunits/tests/testthat/test-ms.r0000644000176200001440000001060613601366670017460 0ustar liggesusers context("Pretty milliseconds") test_that("pretty_ms works", { expect_equal(pretty_ms(0), '0ms') expect_equal(pretty_ms(0.1), '1ms') expect_equal(pretty_ms(1), '1ms') expect_equal(pretty_ms(1000 + 400), '1.4s') expect_equal(pretty_ms(1000 * 2 + 400), '2.4s') expect_equal(pretty_ms(1000 * 55), '55s') expect_equal(pretty_ms(1000 * 67), '1m 7s') expect_equal(pretty_ms(1000 * 60 * 5), '5m') expect_equal(pretty_ms(1000 * 60 * 67), '1h 7m') expect_equal(pretty_ms(1000 * 60 * 60 * 12), '12h') expect_equal(pretty_ms(1000 * 60 * 60 * 40), '1d 16h') expect_equal(pretty_ms(1000 * 60 * 60 * 999), '41d 15h') }) test_that("compact pretty_ms works", { expect_equal(pretty_ms(1000 + 4, compact = TRUE), '~1s') expect_equal(pretty_ms(1000 * 60 * 60 * 999, compact = TRUE), '~41d') }) test_that("pretty_ms handles vectors", { v <- c(0, 0.1, 1, 1400, 2400, 1000 * 55, 1000 * 67, 1000 * 60 * 5, 1000 * 60 * 67, 1000 * 60 * 60 * 12, 1000 * 60 * 60 * 40, 1000 * 60 * 60 * 999) v2 <- c("0ms", "1ms", "1ms", "1.4s", "2.4s", "55s", "1m 7s", "5m", "1h 7m", "12h", "1d 16h", "41d 15h") expect_equal(pretty_ms(v), v2) }) context("Pretty seconds") test_that("pretty_sec works", { expect_equal(pretty_sec(0 / 1000), '0ms') expect_equal(pretty_sec(0.1 / 1000), '1ms') expect_equal(pretty_sec(1 / 1000), '1ms') expect_equal(pretty_sec((1000 + 400) / 1000), '1.4s') expect_equal(pretty_sec((1000 * 2 + 400) / 1000), '2.4s') expect_equal(pretty_sec(1000 * 55 / 1000), '55s') expect_equal(pretty_sec(1000 * 67 / 1000), '1m 7s') expect_equal(pretty_sec(1000 * 60 * 5 / 1000), '5m') expect_equal(pretty_sec(1000 * 60 * 67 / 1000), '1h 7m') expect_equal(pretty_sec(1000 * 60 * 60 * 12 / 1000), '12h') expect_equal(pretty_sec(1000 * 60 * 60 * 40 / 1000), '1d 16h') expect_equal(pretty_sec(1000 * 60 * 60 * 999 / 1000), '41d 15h') }) test_that("compact pretty_sec works", { expect_equal(pretty_sec((1000 + 4) / 1000, compact = TRUE), '~1s') expect_equal(pretty_sec(1000 * 60 * 60 * 999 / 1000, compact = TRUE), '~41d') }) test_that("pretty_sec handles vectors", { v <- c(0, 0.1, 1, 1400, 2400, 1000 * 55, 1000 * 67, 1000 * 60 * 5, 1000 * 60 * 67, 1000 * 60 * 60 * 12, 1000 * 60 * 60 * 40, 1000 * 60 * 60 * 999) / 1000 v2 <- c("0ms", "1ms", "1ms", "1.4s", "2.4s", "55s", "1m 7s", "5m", "1h 7m", "12h", "1d 16h", "41d 15h") expect_equal(pretty_sec(v), v2) }) context("Pretty dt") test_that("pretty_dt works", { expect_equal(pretty_dt(as.difftime(units = "secs", 0 / 1000)), '0ms') expect_equal(pretty_dt(as.difftime(units = "secs", 0.1 / 1000)), '1ms') expect_equal(pretty_dt(as.difftime(units = "secs", 1 / 1000)), '1ms') expect_equal(pretty_dt(as.difftime(units = "secs", (1000 + 400) / 1000)), '1.4s') expect_equal(pretty_dt(as.difftime(units = "secs", (1000 * 2 + 400) / 1000)), '2.4s') expect_equal(pretty_dt(as.difftime(units = "secs", 1000 * 55 / 1000)), '55s') expect_equal(pretty_dt(as.difftime(units = "secs", 1000 * 67 / 1000)), '1m 7s') expect_equal(pretty_dt(as.difftime(units = "secs", 1000 * 60 * 5 / 1000)), '5m') expect_equal(pretty_dt(as.difftime(units = "secs", 1000 * 60 * 67 / 1000)), '1h 7m') expect_equal(pretty_dt(as.difftime(units = "secs", 1000 * 60 * 60 * 12 / 1000)), '12h') expect_equal(pretty_dt(as.difftime(units = "secs", 1000 * 60 * 60 * 40 / 1000)), '1d 16h') expect_equal(pretty_dt(as.difftime(units = "secs", 1000 * 60 * 60 * 999 / 1000)), '41d 15h') }) test_that("compact pretty_dt works", { expect_equal(pretty_dt(as.difftime(units = "secs", (1000 + 4) / 1000), compact = TRUE), '~1s') expect_equal(pretty_dt(as.difftime(units = "secs", 1000 * 60 * 60 * 999 / 1000), compact = TRUE), '~41d') }) test_that("pretty_dt handles vectors", { v <- c(0, 0.1, 1, 1400, 2400, 1000 * 55, 1000 * 67, 1000 * 60 * 5, 1000 * 60 * 67, 1000 * 60 * 60 * 12, 1000 * 60 * 60 * 40, 1000 * 60 * 60 * 999) / 1000 v2 <- c("0ms", "1ms", "1ms", "1.4s", "2.4s", "55s", "1m 7s", "5m", "1h 7m", "12h", "1d 16h", "41d 15h") expect_equal(pretty_dt(as.difftime(units = "secs", v)), v2) }) test_that("pretty_dt works with NAs", { stime <- Sys.time() v <- .difftime(c(difftime(NA, NA), difftime(stime + 1, stime)), "secs") v2 <- c(NA_character_, "~1s") v3 <- c(NA_character_, "1s") expect_equal(pretty_dt(v, compact = TRUE), v2) expect_equal(pretty_dt(v, compact = FALSE), v3) }) prettyunits/tests/testthat.R0000644000176200001440000000010213214725500016001 0ustar liggesuserslibrary(testthat) library(prettyunits) test_check("prettyunits") prettyunits/R/0000755000176200001440000000000013612446753013100 5ustar liggesusersprettyunits/R/utils.R0000644000176200001440000000047613601345743014365 0ustar liggesusers`%s%` <- function(lhs, rhs) { assert_string(lhs) do.call( sprintf, c(list(lhs), as.list(rhs)) ) } `%+%` <- function(lhs, rhs) { paste0(lhs, rhs) } assert_diff_time <- function(x) { stopifnot(inherits(x, "difftime")) } assert_string <- function(x) { stopifnot(is.character(x), length(x) == 1L) } prettyunits/R/xsizes-docs.R0000644000176200001440000000225713605314344015474 0ustar liggesusers #' Bytes in a human readable string #' #' Use `pretty_bytes()` to format bytes. `compute_bytes()` is the underlying #' engine that may be useful for custom formatting. #' #' @param bytes Numeric vector, number of bytes. #' @param style Formatting style: #' * `"default"` is the original `pretty_bytes` formatting, and it always #' pads the output, so that all vector elements are of the same width, #' * `"nopad"` is similar, but does not pad the output, #' * `"6"` always uses 6 characters, #' The `"6"` style is useful if it is important that the output always #' has the same width (number of characters), e.g. in progress bars. #' See some examples below. #' @return Character vector, the formatted sizes. #' For `compute_bytes`, a data frame with columns `amount`, `unit`, #' `negative`. #' #' @export #' @examples #' bytes <- c(1337, 133337, 13333337, 1333333337, 133333333337) #' pretty_bytes(bytes) #' pretty_bytes(bytes, style = "nopad") #' pretty_bytes(bytes, style = "6") pretty_bytes <- format_bytes$pretty_bytes #' @rdname pretty_bytes #' @param smallest_unit A character scalar, the smallest unit to use. #' @export compute_bytes <- format_bytes$compute_bytes prettyunits/R/time-ago.R0000644000176200001440000001474213214726762014734 0ustar liggesusers e <- expression vague_dt_default <- list( list(c = e(seconds < 10), s = "moments ago"), list(c = e(seconds < 45), s = "less than a minute ago"), list(c = e(seconds < 90), s = "about a minute ago"), list(c = e(minutes < 45), s = e("%d minutes ago" %s% round(minutes))), list(c = e(minutes < 90), s = "about an hour ago"), list(c = e(hours < 24), s = e("%d hours ago" %s% round(hours))), list(c = e(hours < 42), s = "a day ago"), list(c = e(days < 30), s = e("%d days ago" %s% round(days))), list(c = e(days < 45), s = "about a month ago"), list(c = e(days < 335), s = e("%d months ago" %s% round(days / 30))), list(c = e(years < 1.5), s = "about a year ago"), list(c = TRUE, s = e("%d years ago" %s% round(years))) ) vague_dt_short <- list( list(c = e(seconds < 50), s = "<1 min"), list(c = e(minutes < 50), s = e("%d min" %s% round(minutes))), list(c = e(hours < 1.5), s = "1 hour"), list(c = e(hours < 18), s = e("%d hours" %s% round(hours))), list(c = e(hours < 42), s = "1 day"), list(c = e(days < 30), s = e("%d day" %s% round(days))), list(c = e(days < 45), s = "1 mon"), list(c = e(days < 335), s = e("%d mon" %s% round(days / 30))), list(c = e(years < 1.5), s = "1 year"), list(c = TRUE, s = e("%d years" %s% round(years))) ) vague_dt_terse <- list( list(c = e(seconds < 50), s = e("%2ds" %s% round(seconds))), list(c = e(minutes < 50), s = e("%2dm" %s% round(minutes))), list(c = e(hours < 18), s = e("%2dh" %s% round(hours))), list(c = e(days < 30), s = e("%2dd" %s% round(days))), list(c = e(days < 335), s = e("%2dM" %s% round(days / 30))), list(c = TRUE, s = e("%2dy" %s% round(years))) ) vague_dt_formats <- list( "default" = vague_dt_default, "short" = vague_dt_short, "terse" = vague_dt_terse ) #' Human readable format of the time interval since a time point #' #' It calls \code{\link{vague_dt}} to do the actual formatting. #' #' @param date Date(s), \code{as.POSIXct} will be called on them. #' @param format Format, currently available formats are: #' \sQuote{default}, \sQuote{short}, \sQuote{terse}. See examples below. #' @return Character vector of the formatted time intervals. #' #' @export #' @examples #' now <- Sys.time() #' #' time_ago(now) #' time_ago(now - as.difftime(30, units = "secs")) #' time_ago(now - as.difftime(14, units = "mins")) #' time_ago(now - as.difftime(5, units = "hours")) #' time_ago(now - as.difftime(25, units = "hours")) #' time_ago(now - as.difftime(5, units = "days")) #' time_ago(now - as.difftime(30, units = "days")) #' time_ago(now - as.difftime(365, units = "days")) #' time_ago(now - as.difftime(365 * 10, units = "days")) #' #' ## Short format #' time_ago(format = "short", now) #' time_ago(format = "short", now - as.difftime(30, units = "secs")) #' time_ago(format = "short", now - as.difftime(14, units = "mins")) #' time_ago(format = "short", now - as.difftime(5, units = "hours")) #' time_ago(format = "short", now - as.difftime(25, units = "hours")) #' time_ago(format = "short", now - as.difftime(5, units = "days")) #' time_ago(format = "short", now - as.difftime(30, units = "days")) #' time_ago(format = "short", now - as.difftime(365, units = "days")) #' time_ago(format = "short", now - as.difftime(365 * 10, units = "days")) #' #' ## Even shorter, terse format, (almost always) exactly 3 characters wide #' time_ago(format = "terse", now) #' time_ago(format = "terse", now - as.difftime(30, units = "secs")) #' time_ago(format = "terse", now - as.difftime(14, units = "mins")) #' time_ago(format = "terse", now - as.difftime(5, units = "hours")) #' time_ago(format = "terse", now - as.difftime(25, units = "hours")) #' time_ago(format = "terse", now - as.difftime(5, units = "days")) #' time_ago(format = "terse", now - as.difftime(30, units = "days")) #' time_ago(format = "terse", now - as.difftime(365, units = "days")) #' time_ago(format = "terse", now - as.difftime(365 * 10, units = "days")) time_ago <- function(date, format = c("default", "short", "terse")) { date <- as.POSIXct(date) if (length(date) > 1) return(sapply(date, time_ago, format = format)) seconds <- difftime(Sys.time(), date, units = "secs") vague_dt(seconds, format = format) } #' Human readable format of a time interval #' #' @param dt A \code{difftime} object, the time interval(s). #' @param format Format, currently available formats are: #' \sQuote{default}, \sQuote{short}, \sQuote{terse}. See examples below. #' @return Character vector of the formatted time intervals. #' #' @export #' @examples #' vague_dt(as.difftime(30, units = "secs")) #' vague_dt(as.difftime(14, units = "mins")) #' vague_dt(as.difftime(5, units = "hours")) #' vague_dt(as.difftime(25, units = "hours")) #' vague_dt(as.difftime(5, units = "days")) #' vague_dt(as.difftime(30, units = "days")) #' vague_dt(as.difftime(365, units = "days")) #' vague_dt(as.difftime(365 * 10, units = "days")) #' #' ## Short format #' vague_dt(format = "short", as.difftime(30, units = "secs")) #' vague_dt(format = "short", as.difftime(14, units = "mins")) #' vague_dt(format = "short", as.difftime(5, units = "hours")) #' vague_dt(format = "short", as.difftime(25, units = "hours")) #' vague_dt(format = "short", as.difftime(5, units = "days")) #' vague_dt(format = "short", as.difftime(30, units = "days")) #' vague_dt(format = "short", as.difftime(365, units = "days")) #' vague_dt(format = "short", as.difftime(365 * 10, units = "days")) #' #' ## Even shorter, terse format, (almost always) exactly 3 characters wide #' vague_dt(format = "terse", as.difftime(30, units = "secs")) #' vague_dt(format = "terse", as.difftime(14, units = "mins")) #' vague_dt(format = "terse", as.difftime(5, units = "hours")) #' vague_dt(format = "terse", as.difftime(25, units = "hours")) #' vague_dt(format = "terse", as.difftime(5, units = "days")) #' vague_dt(format = "terse", as.difftime(30, units = "days")) #' vague_dt(format = "terse", as.difftime(365, units = "days")) #' vague_dt(format = "terse", as.difftime(365 * 10, units = "days")) vague_dt <- function(dt, format = c("default", "short", "terse")) { assert_diff_time(dt) units(dt) <- "secs" seconds <- as.vector(dt) ## Simplest to quit here for empty input if (!length(seconds)) return(character()) pieces <- list( minutes = seconds / 60, hours = seconds / 60 / 60, days = seconds / 60 / 60 / 24, years = seconds / 60 / 60 / 24 / 365.25 ) format <- match.arg(format) for (p in vague_dt_formats[[format]]) { if (eval(p$c, pieces)) return(eval(p$s, pieces)) } } prettyunits/R/pretty-package.R0000644000176200001440000000012713214727707016142 0ustar liggesusers #' Prettier formatting of quantities #' #' @docType package #' @name prettyunits NULL prettyunits/R/time.R0000644000176200001440000000533713601077733014165 0ustar liggesusers parse_ms <- function(ms) { stopifnot(is.numeric(ms)) data.frame( days = floor(ms / 86400000), hours = floor((ms / 3600000) %% 24), minutes = floor((ms / 60000) %% 60), seconds = round((ms / 1000) %% 60, 1) ) } first_positive <- function(x) which(x > 0)[1] trim <- function (x) gsub("^\\s+|\\s+$", "", x) #' Pretty formatting of milliseconds #' #' @param ms Numeric vector of milliseconds #' @param compact If true, then only the first non-zero #' unit is used. See examples below. #' @return Character vector of formatted time intervals. #' #' @family time #' @export #' @examples #' pretty_ms(c(1337, 13370, 133700, 1337000, 1337000000)) #' #' pretty_ms(c(1337, 13370, 133700, 1337000, 1337000000), #' compact = TRUE) pretty_ms <- function(ms, compact = FALSE) { stopifnot(is.numeric(ms)) parsed <- t(parse_ms(ms)) if (compact) { units <- c("d", "h", "m", "s") parsed2 <- parsed parsed2[] <- paste0(parsed, units) idx <- cbind( apply(parsed, 2, first_positive), seq_len(length(ms)) ) tmp <- paste0("~", parsed2[idx]) # handle NAs tmp[is.na(parsed2[idx])] <- NA_character_ tmp } else { ## Exact for small ones exact <- paste0(ceiling(ms), "ms") exact[is.na(ms)] <- NA_character_ ## Approximate for others, in seconds merge_pieces <- function(pieces) { ## handle NAs if (all(is.na(pieces))) { return(NA_character_) } ## handle non-NAs ( (if (pieces[1]) pieces[1] %+% "d " else "") %+% (if (pieces[2]) pieces[2] %+% "h " else "") %+% (if (pieces[3]) pieces[3] %+% "m " else "") %+% (if (pieces[4]) pieces[4] %+% "s " else "") ) } approx <- trim(apply(parsed, 2, merge_pieces)) ifelse(ms < 1000, exact, approx) } } #' Pretty formatting of seconds #' #' @param sec Numeric vector of seconds. #' @return Character vector of formatted time intervals. #' #' @inheritParams pretty_ms #' @family time #' @export #' @examples #' pretty_sec(c(1337, 13370, 133700, 1337000, 13370000)) #' #' pretty_sec(c(1337, 13370, 133700, 1337000, 13370000), #' compact = TRUE) pretty_sec <- function(sec, compact = FALSE) { pretty_ms(sec * 1000, compact = compact) } #' Pretty formatting of time intervals (difftime objects) #' #' @param dt A \code{difftime} object, a vector of time #' differences. #' @return Character vector of formatted time intervals. #' #' @inheritParams pretty_ms #' @family time #' @export #' @examples #' pretty_dt(as.difftime(1000, units = "secs")) #' pretty_dt(as.difftime(0, units = "secs")) pretty_dt <- function(dt, compact = FALSE) { assert_diff_time(dt) units(dt) <- "secs" pretty_sec(as.vector(dt), compact = compact) } prettyunits/R/sizes.R0000644000176200001440000000557613612446753014375 0ustar liggesusers format_bytes <- local({ pretty_bytes <- function(bytes, style = c("default", "nopad", "6")) { style <- switch( match.arg(style), "default" = pretty_bytes_default, "nopad" = pretty_bytes_nopad, "6" = pretty_bytes_6 ) style(bytes) } compute_bytes <- function(bytes, smallest_unit = "B") { units0 <- c("B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB") stopifnot( is.numeric(bytes), is.character(smallest_unit), length(smallest_unit) == 1, !is.na(smallest_unit), smallest_unit %in% units0 ) limits <- c(1000, 999950 * 1000 ^ (seq_len(length(units0) - 2) - 1)) low <- match(smallest_unit, units0) units <- units0[low:length(units0)] limits <- limits[low:length(limits)] neg <- bytes < 0 & !is.na(bytes) bytes <- abs(bytes) mat <- matrix( rep(bytes, each = length(limits)), nrow = length(limits), ncol = length(bytes) ) mat2 <- matrix(mat < limits, nrow = length(limits), ncol = length(bytes)) exponent <- length(limits) - colSums(mat2) + low - 1L res <- bytes / 1000 ^ exponent unit <- units[exponent - low + 2L] ## Zero bytes res[bytes == 0] <- 0 unit[bytes == 0] <- units[1] ## NA and NaN bytes res[is.na(bytes)] <- NA_real_ res[is.nan(bytes)] <- NaN unit[is.na(bytes)] <- units0[low] # Includes NaN as well data.frame( stringsAsFactors = FALSE, amount = res, unit = unit, negative = neg ) } pretty_bytes_default <- function(bytes) { szs <- compute_bytes(bytes) amt <- szs$amount ## String. For fractions we always show two fraction digits res <- character(length(amt)) int <- is.na(amt) | amt == as.integer(amt) res[int] <- format( ifelse(szs$negative[int], -1, 1) * amt[int], scientific = FALSE ) res[!int] <- sprintf("%.2f", ifelse(szs$negative[!int], -1, 1) * amt[!int]) format(paste(res, szs$unit), justify = "right") } pretty_bytes_nopad <- function(bytes) { sub("^\\s+", "", pretty_bytes_default(bytes)) } pretty_bytes_6 <- function(bytes) { szs <- compute_bytes(bytes, smallest_unit = "kB") amt <- szs$amount na <- is.na(amt) nan <- is.nan(amt) neg <- !na & !nan & szs$negative l10 <- !na & !nan & !neg & amt < 10 l100 <- !na & !nan & !neg & amt >= 10 & amt < 100 b100 <- !na & !nan & !neg & amt >= 100 szs$unit[neg] <- "kB" famt <- character(length(amt)) famt[na] <- " NA" famt[nan] <- "NaN" famt[neg] <- "< 0" famt[l10] <- sprintf("%.1f", amt[l10]) famt[l100] <- sprintf(" %.0f", amt[l100]) famt[b100] <- sprintf("%.0f", amt[b100]) paste0(famt, " ", szs$unit) } structure( list( .internal = environment(), pretty_bytes = pretty_bytes, compute_bytes = compute_bytes ), class = c("standalone_bytes", "standalone") ) }) prettyunits/NEWS.md0000644000176200001440000000167113612451565013777 0ustar liggesusers # 1.1.1 * Fix spurious zero fractions in `pretty_bytes()` when formatting vectors of sizes (#23). # 1.1.0 * `pretty_dt()`, `pretty_ms()` and `pretty_sec()` now handle `NA` values properly, and return `NA_character_` for them (#10, @petermeissner). * `pretty_bytes()` now formats quantities just below the units better. E.g. 1MB - 1B is formatted as `"1 MB"` instead of `""1000 kB"` (#18). * `pretty_bytes()` now has multiple styles. In particular, a fixed width style is useful for progress bars. Another style avoids the left-padding with spaces. * The new low level `compute_bytes()` function can be used to create custom formatters for bytes. # 1.0.2 * `pretty_bytes()` always uses two fraction digits for non-integers. This looks nicer in a progress bar, as the width of string does not change so much. # 1.0.1 First version with a NEWS file. * Get rid of `R CMD check` notes. # 1.0.0 Last version without a NEWS file. prettyunits/MD50000644000176200001440000000211113612511637013174 0ustar liggesusersc7e0b31e9c47de40c1a9c11764717ab5 *DESCRIPTION 3d55ef9a10c04c95a867277c02d32bca *LICENSE e3b6961fd55e9711afe7e30b1a78e5a2 *NAMESPACE a7471e7edf5e4b9dbce093b473ccb6e4 *NEWS.md cd24536daf03c962b517de85b6cf1016 *R/pretty-package.R fe5d0e7464286dd1e07746d3da593ad9 *R/sizes.R 8b1e1abfd6f4de3301cb1ff98532e85e *R/time-ago.R b622e47328c70c5378d307c885bf26a3 *R/time.R 241f1dd00186fecb510f552fd310a1ee *R/utils.R 3846445cefe55584e4a70d4c2e2a938d *R/xsizes-docs.R 727c95ce9b6795c96f09aff7e6fe152c *README.md a76805eb8e488f0daffe1c81c3c39c53 *man/pretty_bytes.Rd ac05527084a6a160e1c53a924f9811cd *man/pretty_dt.Rd 086cf9b0d216fef2df8e55dfbdae0538 *man/pretty_ms.Rd 3b7c933e692b0c8dd6ba6683d1ceb4a5 *man/pretty_sec.Rd 713ae7eee4399182c576443e5e0260e7 *man/prettyunits.Rd 1eac8d1f7322001d8a9d4c28e2b32ead *man/time_ago.Rd ab8646217bb905043d5da7b6d96bc3af *man/vague_dt.Rd d4df5b35b4ce6e070b4980c38847cad3 *tests/testthat.R 675e661fec46a342af7362d194bb6765 *tests/testthat/test-bytes.r c7ce99540a9d95b591172192553b4511 *tests/testthat/test-ms.r 50c54d52ed56e60a64709770a7d38412 *tests/testthat/test-vague-dt.r