From ec1b9318743471549e90a17d71706ea00f1ffb49 Mon Sep 17 00:00:00 2001 From: George Hahn Date: Sat, 5 Jul 2014 20:19:24 -0400 Subject: [PATCH 1/3] Added byte rate calculation functons, closes #296 --- src/Humanizer.Tests/Bytes/ByteRateTests.cs | 29 ++++++++++++++++++++++ src/Humanizer.Tests/Humanizer.Tests.csproj | 1 + src/Humanizer/Bytes/ByteRate.cs | 29 ++++++++++++++++++++++ src/Humanizer/Bytes/ByteSizeExtensions.cs | 14 ++++++++++- src/Humanizer/Humanizer.csproj | 1 + 5 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 src/Humanizer.Tests/Bytes/ByteRateTests.cs create mode 100644 src/Humanizer/Bytes/ByteRate.cs diff --git a/src/Humanizer.Tests/Bytes/ByteRateTests.cs b/src/Humanizer.Tests/Bytes/ByteRateTests.cs new file mode 100644 index 000000000..a3a3d5262 --- /dev/null +++ b/src/Humanizer.Tests/Bytes/ByteRateTests.cs @@ -0,0 +1,29 @@ +using System; +using Humanizer.Bytes; +using Xunit; +using Xunit.Extensions; + +namespace Humanizer.Tests.Bytes +{ + public class ByteRateTests : AmbientCulture + { + public ByteRateTests() : base("en") { } + + [Theory] + [InlineData(400, 1, "400 B/s")] + [InlineData(4 * 1024, 1, "4 KB/s")] + [InlineData(4 * 1024 * 1024, 1, "4 MB/s")] + [InlineData(4 * 2 * 1024 * 1024, 2, "4 MB/s")] + [InlineData(4 * 1024, 0.1, "40 KB/s")] + [InlineData(15 * 60 * 1024 * 1024, 60, "15 MB/s")] + public void HumanizesRates(long inputBytes, double perSeconds, string expectedValue) + { + var size = new ByteSize(inputBytes); + var interval = TimeSpan.FromSeconds(perSeconds); + + var rate = size.Per(interval).ToRatePerSecond(); + + Assert.Equal(expectedValue, rate); + } + } +} diff --git a/src/Humanizer.Tests/Humanizer.Tests.csproj b/src/Humanizer.Tests/Humanizer.Tests.csproj index b39cce5b1..68e40ad4c 100644 --- a/src/Humanizer.Tests/Humanizer.Tests.csproj +++ b/src/Humanizer.Tests/Humanizer.Tests.csproj @@ -56,6 +56,7 @@ + diff --git a/src/Humanizer/Bytes/ByteRate.cs b/src/Humanizer/Bytes/ByteRate.cs new file mode 100644 index 000000000..eba693514 --- /dev/null +++ b/src/Humanizer/Bytes/ByteRate.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Humanizer.Bytes +{ + public class ByteRate + { + public ByteSize size { get; private set;} + + public TimeSpan interval { get; private set; } + + public ByteRate(ByteSize size, TimeSpan interval) + { + this.size = size; + this.interval = interval; + } + + /// + /// Return this rate as a string, EG "2 MB/s" + /// + /// + public string ToRatePerSecond() + { + return (new ByteSize(size.Bytes / interval.TotalSeconds)).Humanize() + "/s"; + } + } +} diff --git a/src/Humanizer/Bytes/ByteSizeExtensions.cs b/src/Humanizer/Bytes/ByteSizeExtensions.cs index d057dc9e3..2fbd7a784 100644 --- a/src/Humanizer/Bytes/ByteSizeExtensions.cs +++ b/src/Humanizer/Bytes/ByteSizeExtensions.cs @@ -1,4 +1,5 @@ -using Humanizer.Bytes; +using System; +using Humanizer.Bytes; // ReSharper disable once CheckNamespace namespace Humanizer @@ -438,5 +439,16 @@ public static string Humanize(this ByteSize input, string format = null) { return string.IsNullOrWhiteSpace(format) ? input.ToString() : input.ToString(format); } + + /// + /// Turns a quantity of bytes in a given interval into a rate that can be manipulated + /// + /// Quantity of bytes + /// Interval to create rate for + /// + public static ByteRate Per(this ByteSize size, TimeSpan interval) + { + return new ByteRate(size, interval); + } } } diff --git a/src/Humanizer/Humanizer.csproj b/src/Humanizer/Humanizer.csproj index a3571b6bb..4e9a5c7ad 100644 --- a/src/Humanizer/Humanizer.csproj +++ b/src/Humanizer/Humanizer.csproj @@ -49,6 +49,7 @@ Humanizer.snk + From 75bc12be378b5c4273f4f7d35dbab2ade5cd265a Mon Sep 17 00:00:00 2001 From: George Hahn Date: Sun, 6 Jul 2014 12:27:42 -0400 Subject: [PATCH 2/3] Tweak byte rate API; finishes implementation for issue #296 --- readme.md | 19 +++++++++ release_notes.md | 1 + src/Humanizer.Tests/Bytes/ByteRateTests.cs | 29 +++++++++++++- src/Humanizer/Bytes/ByteRate.cs | 45 ++++++++++++++++++---- src/Humanizer/Bytes/TimeUnit.cs | 23 +++++++++++ src/Humanizer/Humanizer.csproj | 1 + 6 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 src/Humanizer/Bytes/TimeUnit.cs diff --git a/readme.md b/readme.md index 1e5aa7447..c8108a677 100644 --- a/readme.md +++ b/readme.md @@ -754,6 +754,25 @@ ByteSize.Parse("1.55 tB"); ByteSize.Parse("1.55 tb"); ``` +Finally, if you need to calculate the rate at which a quantity of bytes has been transferred, you can use the `Per` method of `ByteSize`. The `Per` method accepts one argument - the measurement interval for the bytes; this is the amount of time it took to transfer the bytes. + +The `Per` method returns a `ByteRate` class which has a `Humanize` method. By default, rates are given in seconds (eg, MB/s). However, if desired, a TimeUnit may be passed to `Humanize` for an alternate interval. Valid intervals are `TimeUnit.Second`, `TimeUnit.Minute`, and `TimeUnit.Hour`. Examples of each interval and example byte rate usage is below. + +``` +var size = ByteSize.FromMegabytes(10); +var measurementInterval = TimeSpan.FromSeconds(1); + +var text = size.Per(measurementInterval).Humanize(); +// 10 MB/s + +var rate = size.Per(measurementInterval); +text = rate.Humanize(TimeUnit.Minute); +// 600 MB/min + +text = size.Per(measurementInterval).Humanize(TimeUnit.Hour); +// 35.15625 GB/hour +``` + ##Mix this into your framework to simplify your life This is just a baseline and you can use this to simplify your day to day job. For example, in Asp.Net MVC we keep chucking `Display` attribute on ViewModel properties so `HtmlHelper` can generate correct labels for us; but, just like enums, in vast majority of cases we just need a space between the words in property name - so why not use `"string".Humanize` for that?! diff --git a/release_notes.md b/release_notes.md index f2bb3409a..6137a543b 100644 --- a/release_notes.md +++ b/release_notes.md @@ -3,6 +3,7 @@ - [#303](https://github.com/MehdiK/Humanizer/pull/303): Added support for all integer types in ByteSize extensions - [#307](https://github.com/MehdiK/Humanizer/pull/307): Added support to string.FormatWith for the explicit culture parameter. - [#312](https://github.com/MehdiK/Humanizer/pull/312): Added Turkish ToWord, ToOrdinalWord and Ordinalize implementation + - [#314](https://github.com/MehdiK/Humanizer/pull/314): Added ByteRate class and supporting members to facilitate calculation of byte transfer rates [Commits](https://github.com/MehdiK/Humanizer/compare/v1.27.0...master) diff --git a/src/Humanizer.Tests/Bytes/ByteRateTests.cs b/src/Humanizer.Tests/Bytes/ByteRateTests.cs index a3a3d5262..5d8bdd455 100644 --- a/src/Humanizer.Tests/Bytes/ByteRateTests.cs +++ b/src/Humanizer.Tests/Bytes/ByteRateTests.cs @@ -21,9 +21,36 @@ public void HumanizesRates(long inputBytes, double perSeconds, string expectedVa var size = new ByteSize(inputBytes); var interval = TimeSpan.FromSeconds(perSeconds); - var rate = size.Per(interval).ToRatePerSecond(); + var rate = size.Per(interval).Humanize(); Assert.Equal(expectedValue, rate); } + + [Theory] + [InlineData(1, 1, TimeUnit.Second, "1 MB/s")] + [InlineData(1, 60, TimeUnit.Minute, "1 MB/min")] + [InlineData(1, 60 * 60, TimeUnit.Hour, "1 MB/hour")] + [InlineData(10, 1, TimeUnit.Second, "10 MB/s")] + [InlineData(10, 60, TimeUnit.Minute, "10 MB/min")] + [InlineData(10, 60 * 60, TimeUnit.Hour, "10 MB/hour")] + [InlineData(1, 10 * 1, TimeUnit.Second, "102.4 KB/s")] + [InlineData(1, 10 * 60, TimeUnit.Minute, "102.4 KB/min")] + [InlineData(1, 10 * 60 * 60, TimeUnit.Hour, "102.4 KB/hour")] + public void TimeUnitTests(long megabytes, double measurementIntervalSeconds, TimeUnit displayInterval, string expectedValue) + { + var size = ByteSize.FromMegabytes(megabytes); + var measurementInterval = TimeSpan.FromSeconds(measurementIntervalSeconds); + + var rate = size.Per(measurementInterval); + var text = rate.Humanize(displayInterval); + + text = size.Per(measurementInterval).Humanize(displayInterval); + + + size = ByteSize.FromMegabytes(10); + measurementInterval = TimeSpan.FromSeconds(1); + + Assert.Equal(expectedValue, text); + } } } diff --git a/src/Humanizer/Bytes/ByteRate.cs b/src/Humanizer/Bytes/ByteRate.cs index eba693514..624f287aa 100644 --- a/src/Humanizer/Bytes/ByteRate.cs +++ b/src/Humanizer/Bytes/ByteRate.cs @@ -5,25 +5,56 @@ namespace Humanizer.Bytes { + + /// + /// Class to hold a ByteSize and a measurement interval, for the purpose of calculating the rate of transfer + /// public class ByteRate { - public ByteSize size { get; private set;} + /// + /// Quantity of bytes + /// + /// + public ByteSize Size { get; private set;} - public TimeSpan interval { get; private set; } + /// + /// Interval that bytes were transferred in + /// + /// + public TimeSpan Interval { get; private set; } + /// + /// Create a ByteRate with given quantity of bytes across an interval + /// + /// + /// public ByteRate(ByteSize size, TimeSpan interval) { - this.size = size; - this.interval = interval; + this.Size = size; + this.Interval = interval; } /// - /// Return this rate as a string, EG "2 MB/s" + /// Calculate rate for the quantity of bytes and interval defined by this instance /// + /// Unit of time to calculate rate for (defaults is per second) /// - public string ToRatePerSecond() + public string Humanize(TimeUnit timeUnit = TimeUnit.Second) { - return (new ByteSize(size.Bytes / interval.TotalSeconds)).Humanize() + "/s"; + var displayInterval = TimeSpan.FromSeconds(1); + var displayUnit = "s"; + + if (timeUnit == TimeUnit.Minute) + { + displayInterval = TimeSpan.FromMinutes(1); + displayUnit = "min"; + } else if(timeUnit == TimeUnit.Hour) + { + displayInterval = TimeSpan.FromHours(1); + displayUnit = "hour"; + } + + return (new ByteSize(Size.Bytes / Interval.TotalSeconds * displayInterval.TotalSeconds)).Humanize() + '/' + displayUnit; } } } diff --git a/src/Humanizer/Bytes/TimeUnit.cs b/src/Humanizer/Bytes/TimeUnit.cs new file mode 100644 index 000000000..6d8b4f3e7 --- /dev/null +++ b/src/Humanizer/Bytes/TimeUnit.cs @@ -0,0 +1,23 @@ +namespace Humanizer.Bytes +{ + /// + /// Units of time for which byte rates can be calculated + /// + public enum TimeUnit + { + /// + /// One hour + /// + Hour, + + /// + /// One minute + /// + Minute, + + /// + /// One second + /// + Second + } +} diff --git a/src/Humanizer/Humanizer.csproj b/src/Humanizer/Humanizer.csproj index 4e9a5c7ad..379b80b56 100644 --- a/src/Humanizer/Humanizer.csproj +++ b/src/Humanizer/Humanizer.csproj @@ -50,6 +50,7 @@ + From a797f97f321846a8d174100a242104bf6d251f36 Mon Sep 17 00:00:00 2001 From: George Hahn Date: Sun, 6 Jul 2014 23:30:39 -0400 Subject: [PATCH 3/3] Addressing issues raised in CR --- readme.md | 3 +-- ...provalTest.approve_public_api.approved.txt | 6 +++++ src/Humanizer.Tests/Bytes/ByteRateTests.cs | 23 ++++++++++++++----- src/Humanizer/Bytes/ByteRate.cs | 20 ++++++++++------ src/Humanizer/Bytes/TimeUnit.cs | 23 ------------------- src/Humanizer/Humanizer.csproj | 1 - 6 files changed, 37 insertions(+), 39 deletions(-) delete mode 100644 src/Humanizer/Bytes/TimeUnit.cs diff --git a/readme.md b/readme.md index c8108a677..403ebf57a 100644 --- a/readme.md +++ b/readme.md @@ -765,8 +765,7 @@ var measurementInterval = TimeSpan.FromSeconds(1); var text = size.Per(measurementInterval).Humanize(); // 10 MB/s -var rate = size.Per(measurementInterval); -text = rate.Humanize(TimeUnit.Minute); +text = size.Per(measurementInterval).Humanize(TimeUnit.Minute); // 600 MB/min text = size.Per(measurementInterval).Humanize(TimeUnit.Hour); diff --git a/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt b/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt index 145de1445..c17415171 100644 --- a/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt +++ b/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt @@ -92,6 +92,12 @@ public class ByteSizeExtensions public Humanizer.Bytes.ByteSize Terabytes(int input) { } public Humanizer.Bytes.ByteSize Terabytes(uint input) { } public Humanizer.Bytes.ByteSize Terabytes(double input) { } + public Humanizer.Bytes.ByteRate Per(Humanizer.Bytes.ByteSize size, System.TimeSpan interval) { } +} + +public class ByteRate +{ + public string Humanize(Humanizer.Bytes.TimeUnit timeUnit) { } } public class CasingExtensions diff --git a/src/Humanizer.Tests/Bytes/ByteRateTests.cs b/src/Humanizer.Tests/Bytes/ByteRateTests.cs index 5d8bdd455..16ba5277b 100644 --- a/src/Humanizer.Tests/Bytes/ByteRateTests.cs +++ b/src/Humanizer.Tests/Bytes/ByteRateTests.cs @@ -1,5 +1,6 @@ using System; using Humanizer.Bytes; +using Humanizer.Localisation; using Xunit; using Xunit.Extensions; @@ -43,14 +44,24 @@ public void TimeUnitTests(long megabytes, double measurementIntervalSeconds, Tim var rate = size.Per(measurementInterval); var text = rate.Humanize(displayInterval); + + Assert.Equal(expectedValue, text); + } - text = size.Per(measurementInterval).Humanize(displayInterval); - + [Theory] + [InlineData(TimeUnit.Millisecond)] + [InlineData(TimeUnit.Day)] + [InlineData(TimeUnit.Month)] + [InlineData(TimeUnit.Week)] + [InlineData(TimeUnit.Year)] + public void ThowsOnUnsupportedData(TimeUnit units) + { + var dummyRate = ByteSize.FromBits(1).Per(TimeSpan.FromSeconds(1)); - size = ByteSize.FromMegabytes(10); - measurementInterval = TimeSpan.FromSeconds(1); - - Assert.Equal(expectedValue, text); + Assert.Throws(() => + { + dummyRate.Humanize(units); + }); } } } diff --git a/src/Humanizer/Bytes/ByteRate.cs b/src/Humanizer/Bytes/ByteRate.cs index 624f287aa..8e8dcc519 100644 --- a/src/Humanizer/Bytes/ByteRate.cs +++ b/src/Humanizer/Bytes/ByteRate.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using Humanizer.Localisation; namespace Humanizer.Bytes { @@ -41,18 +39,26 @@ public ByteRate(ByteSize size, TimeSpan interval) /// public string Humanize(TimeUnit timeUnit = TimeUnit.Second) { - var displayInterval = TimeSpan.FromSeconds(1); - var displayUnit = "s"; + TimeSpan displayInterval; + string displayUnit; - if (timeUnit == TimeUnit.Minute) + if (timeUnit == TimeUnit.Second) + { + displayInterval = TimeSpan.FromSeconds(1); + displayUnit = "s"; + } + else if (timeUnit == TimeUnit.Minute) { displayInterval = TimeSpan.FromMinutes(1); displayUnit = "min"; - } else if(timeUnit == TimeUnit.Hour) + } + else if (timeUnit == TimeUnit.Hour) { displayInterval = TimeSpan.FromHours(1); displayUnit = "hour"; } + else + throw new NotSupportedException("timeUnit must be Second, Minute, or Hour"); return (new ByteSize(Size.Bytes / Interval.TotalSeconds * displayInterval.TotalSeconds)).Humanize() + '/' + displayUnit; } diff --git a/src/Humanizer/Bytes/TimeUnit.cs b/src/Humanizer/Bytes/TimeUnit.cs deleted file mode 100644 index 6d8b4f3e7..000000000 --- a/src/Humanizer/Bytes/TimeUnit.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Humanizer.Bytes -{ - /// - /// Units of time for which byte rates can be calculated - /// - public enum TimeUnit - { - /// - /// One hour - /// - Hour, - - /// - /// One minute - /// - Minute, - - /// - /// One second - /// - Second - } -} diff --git a/src/Humanizer/Humanizer.csproj b/src/Humanizer/Humanizer.csproj index 379b80b56..4e9a5c7ad 100644 --- a/src/Humanizer/Humanizer.csproj +++ b/src/Humanizer/Humanizer.csproj @@ -50,7 +50,6 @@ -