diff --git a/.travis.yml b/.travis.yml index 483397b..b9dbfe6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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())' diff --git a/LICENSE.md b/LICENSE.md index b3f03a7..f973541 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -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: diff --git a/README.md b/README.md index 02b15d2..6e8f42e 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,6 @@ 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`) @@ -14,8 +11,6 @@ Humanize numbers, including 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`. diff --git a/REQUIRE b/REQUIRE index d5d6467..1b734fd 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1 +1,2 @@ -julia 0.4 +julia 0.7-beta +Dates \ No newline at end of file diff --git a/src/Humanize.jl b/src/Humanize.jl index 0248950..cbb77e1 100644 --- a/src/Humanize.jl +++ b/src/Humanize.jl @@ -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 @@ -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. diff --git a/test/runtests.jl b/test/runtests.jl index 2a413d1..80a9dae 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -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. \ No newline at end of file