Skip to content

Commit

Permalink
Merge pull request #9 from IainNZ/v07
Browse files Browse the repository at this point in the history
Update Humanize.jl to Julia v0.7, refactor.
  • Loading branch information
IainNZ authored Jul 2, 2018
2 parents 9d60f1c + 19e4933 commit 0d4c676
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 163 deletions.
9 changes: 1 addition & 8 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
language: julia
os:
- linux
- osx
julia:
- 0.4
- 0.5
- 0.6
- 0.7
- nightly
notifications:
email: false
sudo: false
script:
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
- julia -e 'Pkg.clone(pwd()); Pkg.test("Humanize"; coverage=true)'
after_success:
- julia -e 'cd(Pkg.dir("Humanize")); Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())'
2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ This package is a derived work of the MIT-licensed humanize Python library:
https://github.com/jmoiron/humanize/
All original work in this package is also licensed under the MIT license:

Copyright (c) 2014 Iain Dunning, Julian Gehring
Copyright (c) 2018 Iain Dunning, Julian Gehring.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

Expand Down
5 changes: 0 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,13 @@ Humanize.jl
[![Build Status](https://travis-ci.org/IainNZ/Humanize.jl.svg?branch=master)](https://travis-ci.org/IainNZ/Humanize.jl)
[![codecov](https://codecov.io/gh/IainNZ/Humanize.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/IainNZ/Humanize.jl)

[![Humanize](http://pkg.julialang.org/badges/Humanize_0.4.svg)](http://pkg.julialang.org/?pkg=Humanize)
[![Humanize](http://pkg.julialang.org/badges/Humanize_0.5.svg)](http://pkg.julialang.org/?pkg=Humanize)

Humanize numbers, including
* data sizes (`3e6 -> 3.0 MB or 2.9 MiB`).
* Date/datetime differences (`Date(2014,2,3) - Date(2013,3,7) -> 1 year, 1 month`)
* Digit separator (`12345678 -> 12,345,678`)

This package is MIT licensed, and is based on [jmoiron's humanize Python library](https://github.com/jmoiron/humanize/).

**Installation:** `Pkg.add("Humanize")`

## Documentation

All functions are also documented using Julia's in-built help system, e.g. `?datasize`.
Expand Down
3 changes: 2 additions & 1 deletion REQUIRE
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
julia 0.4
julia 0.7-beta
Dates
125 changes: 60 additions & 65 deletions src/Humanize.jl
Original file line number Diff line number Diff line change
@@ -1,65 +1,65 @@
#----------------------------------------------------------------------
# Humanize.jl https://github.com/IainNZ/Humanize.jl
# Humanize.jl
# https://github.com/IainNZ/Humanize.jl
# Based on jmoiron's humanize Python library (MIT licensed):
# https://github.com/jmoiron/humanize/
# All original code is (c) Iain Dunning and MIT licensed.
#----------------------------------------------------------------------

isdefined(Base, :__precompile__) && __precompile__()
__precompile__()

module Humanize

export datasize, timedelta, digitsep
import Dates
import Printf: @sprintf

const SUFFIXES = Dict(
:dec => [" B", " kB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB"],
:bin => [" B", " KiB", " MiB", " GiB", " TiB", " PiB", " EiB", " ZiB", " YiB"],
:gnu => ["", "K", "M", "G", "T", "P", "E", "Z", "Y"]
)
const BASES = Dict(:dec => 1000, :bin => 1024, :gnu => 1024)

#---------------------------------------------------------------------
const dec_suf = [" B", " kB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB"]
const bin_suf = [" B", " KiB", " MiB", " GiB", " TiB", " PiB", " EiB", " ZiB", " YiB"]
const gnu_suf = ["", "K", "M", "G", "T", "P", "E", "Z", "Y"]
"""
datasize(value::Number; style=:dec, format="%.1f")
datasize(value::Number; style=:dec, format="%.1f")
Format a number of bytes in a human-friendly format (eg. 10 kB).
style=:dec - default, decimal suffixes (kB, MB), base 10^3
style=:bin - binary suffixes (KiB, MiB), base 2^10
style=:gnu - GNU-style (ls -sh style, K, M), base 2^10
style=:dec - default, decimal suffixes (kB, MB), base 10^3
style=:bin - binary suffixes (KiB, MiB), base 2^10
style=:gnu - GNU-style (ls -sh style, K, M), base 2^10
"""
function datasize(value::Number; style=:dec, format="%.1f")
suffix = style == :gnu ? gnu_suf : (style == :bin ? bin_suf : dec_suf)
base = style == :dec ? 1000.0 : 1024.0
bytes = float(value)
format = "$format%s"
unit = base
s = suffix[1]
for (i,s) in enumerate(suffix)
unit = base ^ (i)
function datasize(value::Number; style=:dec, format="%.1f")::String
suffix = SUFFIXES[style]
base = float(BASES[style])
bytes = float(value)
unit = base
biggest_suffix = suffix[1]
for power in 1:length(suffix)
unit = base ^ power
biggest_suffix = suffix[power]
bytes < unit && break
end
v = base * bytes / unit
return @eval @sprintf($format, $v, $s)
value = base * bytes / unit
format = "$format%s"
return @eval @sprintf($format, $value, $biggest_suffix)
end

#---------------------------------------------------------------------

"""
timedelta(secs::Integer)
timedelta{T<:Integer}(years::T,months::T,days::T,hours::T,mins::T,secs::T)
timedelta(dt_diff::Dates.Millisecond)
timedelta(d_diff::Dates.Day)
timedelta(secs::Integer)
timedelta(seconds::Dates.Second)
timedelta(Δdt::Dates.Millisecond)
timedelta(Δdate::Dates.Day)
Format a time length in a human friendly format.
timedelta(secs::Integer)
e.g. 3699 -> 'An hour'
timedelta{T<:Integer}(years::T,months::T,days::T,hours::T,mins::T,secs::T)
e.g. days=1,hours=4,... -> 'A day'
hours=4,mins=2,... -> '4 hours'
years=1,months=2,... -> '1 year, 2 months'
timedelta(dt_diff::Dates.Millisecond)
e.g. DateTime(2014,2,3) - DateTime(2013,3,7) -> '11 months'
timedelta(d_diff::Dates.Day)
e.g. Date(2014,3,7) - Date(2013,2,4) -> '1 year, 1 month'
timedelta(seconds::Integer) # 3699 -> "An hour".
timedelta(Δdt::Dates.Millisecond)
e.g. DateTime(2014,2,3) - DateTime(2013,3,7) -> "11 months".
timedelta(Δdate::Dates.Day)
e.g. Date(2014,3,7) - Date(2013,2,4) -> "1 year, 1 month".
"""
function timedelta(secs::Integer)
function timedelta(seconds::Integer)
secs = seconds
mins = div( secs, 60); secs -= 60*mins
hours = div( mins, 60); mins -= 60*hours
days = div( hours, 24); hours -= 24*days
Expand All @@ -83,34 +83,29 @@ function timedelta(secs::Integer)
return "$years years"
end
end
# Assume nothing about magnitudes of inputs, so cast to seconds first
timedelta{T<:Integer}(years::T,months::T,days::T,hours::T,mins::T,secs::T) =
timedelta(((((years*12+months)*30+days)*24+hours)*60+mins)*60+secs)
timedelta(dt_diff::Dates.Millisecond) = timedelta(div(Int(dt_diff),1000))
timedelta(d_diff::Dates.Day) = timedelta(Int(d_diff)*24*3600)
timedelta(seconds::Dates.Second) = timedelta(seconds.value)
timedelta(Δdt::Dates.Millisecond) = timedelta(convert(Dates.Second, Δdt))
timedelta(Δdate::Dates.Day) = timedelta(convert(Dates.Second, Δdate))


#---------------------------------------------------------------------
"""
digitsep(value::Integer, sep = ",", k = 3)
Convert an integer to a string, separating each 'k' digits by 'sep'. 'k'
defaults to 3, separating by thousands. The default "," for 'sep' matches the
commonly used digit separator in the US.
digitsep(value::Integer)
e.g. 12345678 -> "12,345,678"
digitsep(value::Integer, sep = "'")
e.g. 12345678 -> "12'345'678"
digitsep(value::Integer, sep = "'", k = 4)
e.g. 12345678 -> "1234'5678"
digitsep(value::Integer, separator=",", per_separator=3)
Convert an integer to a string, separating each `per_separator` digits by
`separator`.
digitsep(value) # 12345678 -> "12,345,678".
digitsep(value, separator="'") # 12345678 -> "12'345'678".
digitsep(value, separator="/", per_separator=4) # 12345678 -> "1234/5678".
"""
function digitsep(value::Integer, sep = ",", k = 3)
value = string(value)
n = length(value)
starts = reverse(collect(n:-k:1))
groups = [value[max(x-k+1, 1):x] for x in starts]
return join(groups, sep)
function digitsep(value::Integer, seperator=",", per_separator=3)
isnegative = value < zero(value)
value = string(abs(value)) # Stringify, no seperators.
# Figure out last character index of each group of digits.
group_ends = reverse(collect(length(value):-per_separator:1))
groups = [value[max(end_index - per_separator + 1, 1):end_index]
for end_index in group_ends]
return (isnegative ? "-" : "") * join(groups, seperator)
end

end
end # module Humanize.
151 changes: 68 additions & 83 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,101 +1,86 @@
using Humanize
using Base.Test
import Dates
using Test
import Humanize

function test_datasize()
println("test_datasize")
@testset "Humanize" begin

tests = [300, 3000, 3000000, 3000000000, 3000000000000, 1e26, 3141592]
results = Dict( :dec => ["450.000 B", "4.500 kB", "4.500 MB", "4.500 GB", "4.500 TB", "150.000 YB", "4.712 MB"],
:bin => ["630.000 B", "6.152 KiB", "6.008 MiB", "5.867 GiB", "5.730 TiB", "173.708 YiB", "6.292 MiB"],
:gnu => ["480.000", "4.688K", "4.578M", "4.470G", "4.366T", "132.349Y", "4.794M"])
for (style,mult) in [(:dec,1.5), (:bin,2.1), (:gnu, 1.6)]
println(" $style")
for i in 1:length(tests)
size = tests[i] * mult
@test datasize(size,style=style,format="%.3f") == results[style][i]
@testset "datasize" begin
VALUES = [300, 3000, 3000000, 3000000000, 3000000000000, 1e26, 3141592]
MULTIPLIERS = Dict(:dec => 1.5, :bin => 2.1, :gnu => 1.6)
RESULTS = Dict(
:dec => ["450.000 B", "4.500 kB", "4.500 MB", "4.500 GB", "4.500 TB", "150.000 YB", "4.712 MB"],
:bin => ["630.000 B", "6.152 KiB", "6.008 MiB", "5.867 GiB", "5.730 TiB", "173.708 YiB", "6.292 MiB"],
:gnu => ["480.000", "4.688K", "4.578M", "4.470G", "4.366T", "132.349Y", "4.794M"]
)
@testset "$style" for style in (:dec, :bin, :gnu)
for (value, result) in zip(VALUES, RESULTS[style])
size = value * MULTIPLIERS[style]
@test Humanize.datasize(size, style=style, format="%.3f") == result
end
end
end
end # testset datasize.

@testset "timedelta" begin
# Years, months, days, hours, minutes, seconds, result.
DATA = [((0, 0, 0, 0, 0, 0), "a moment"),
((0, 0, 0, 0, 0, 1), "a second"),
((0, 0, 0, 0, 0, 30), "30 seconds"),
((0, 0, 0, 0, 1, 30), "a minute"),
((0, 0, 0, 0, 2, 0), "2 minutes"),
((0, 0, 0, 1, 30, 30), "an hour"),
((0, 0, 0, 23, 50, 50), "23 hours"),
((0, 0, 1, 0, 0, 0), "a day"),
((0, 0, 500, 0, 0, 0), "1 year, 4 months"),
((0, 0, 365*2 + 35, 0, 0, 0), "2 years"),
((0, 0, 10000, 0, 0, 0), "27 years"),
((0, 0, 365 + 30, 0, 0, 0), "1 year, 1 month"),
((0, 0, 365 + 4, 0, 0, 0), "a year"),
((0, 0, 35, 0, 0, 0), "a month"),
((0, 0, 65, 0, 0, 0), "2 months"),
((0, 0, 9, 0, 0, 0), "9 days"),
((0, 0, 365, 0, 0, 0), "a year")]

function test_timedelta()
println("test_timedelta")

test = [(0,0,0,0,0,0), "a moment",
(0,0,0,0,0,1), "a second",
(0,0,0,0,0,30), "30 seconds",
(0,0,0,0,1,30), "a minute",
(0,0,0,0,2,0), "2 minutes",
(0,0,0,1,30,30), "an hour",
(0,0,0,23,50,50), "23 hours",
(0,0,1,0,0,0), "a day",
(0,0,500,0,0,0), "1 year, 4 months",
(0,0,365*2+35,0,0,0), "2 years",
(0,0,10000,0,0,0), "27 years",
(0,0,365+30,0,0,0), "1 year, 1 month",
(0,0,365+4,0,0,0), "a year",
(0,0,35,0,0,0), "a month",
(0,0,65,0,0,0), "2 months",
(0,0,9,0,0,0), "9 days",
(0,0,365,0,0,0), "a year"]
n_test = div(length(test),2)

#timedelta(years::Int,months::Int,days::Int,hours::Int,mins::Int,secs::Int)
println(" direct")
for i in 1:n_test
@test timedelta(test[2i-1]...) == test[2i]
end
#timedelta(dt_diff::Dates.Millisecond)
println(" DateTime diff")
for i in 1:n_test
offset = test[2i-1]
@testset "datetime diff $output" for (inputs, output) in DATA
base_datetime = Dates.DateTime(2014,1,1,0,0,0)
new_datetime = Dates.Year(offset[1]) + base_datetime
new_datetime += Dates.Month(offset[2])
new_datetime += Dates.Day(offset[3])
new_datetime += Dates.Hour(offset[4])
new_datetime += Dates.Minute(offset[5])
new_datetime += Dates.Second(offset[6])
@test timedelta(new_datetime-base_datetime) == test[2i]
end
#timedelta(d_diff::Dates.Day)
println(" Date diff")
for i in 1:n_test
offset = test[2i-1]
sum(offset[1:3]) == 0 && continue
base_date = Dates.Date(2014,1,1)
new_date = Dates.Year(offset[1]) + base_date
new_date += Dates.Month(offset[2])
new_date += Dates.Day(offset[3])
@test timedelta(new_date-base_date) == test[2i]
new_datetime = Dates.Year(inputs[1]) + base_datetime
new_datetime += Dates.Month(inputs[2])
new_datetime += Dates.Day(inputs[3])
new_datetime += Dates.Hour(inputs[4])
new_datetime += Dates.Minute(inputs[5])
new_datetime += Dates.Second(inputs[6])
@test Humanize.timedelta(new_datetime - base_datetime) == output
end
end

@testset "date diff $output" for (inputs, output) in DATA
sum(inputs[1:3]) == 0 && continue # Hour-scale or less.
base_date = Dates.Date(2014, 1, 1)
new_date = Dates.Year(inputs[1]) + base_date
new_date += Dates.Month(inputs[2])
new_date += Dates.Day(inputs[3])
@test Humanize.timedelta(new_date - base_date) == output
end
end # testset timedelta.

function test_digitsep()
println("test_digitsep")

test = (
(1, "1"),
@testset "digitsep" begin
DATA = ((1, "1"),
(12, "12"),
(123, "123"),
(1234, "1,234"),
(12345, "12,345"),
(123456, "123,456"),
(1234567, "1,234,567"),
(12345678, "12,345,678")
)

n_test = length(test)

#digitsep(value::Integer)
println(" direct")
for t in test
@test digitsep(t[1]) == t[2]
(12345678, "12,345,678"),
(-1, "-1"),
(-12, "-12"),
(-123, "-123"),
(-1234, "-1,234"),
(-12345, "-12,345"),
(-123456, "-123,456"),
(-1234567, "-1,234,567"),
(-12345678, "-12,345,678"))
@testset "digitsep $output" for (input, output) in DATA
@test Humanize.digitsep(input) == output
end

end
end # testset digitsep.

test_datasize()
test_timedelta()
test_digitsep()
end # testset Humanize.

0 comments on commit 0d4c676

Please sign in to comment.