diff --git a/readme.md b/readme.md index 621ad4f86..0a1168c0d 100644 --- a/readme.md +++ b/readme.md @@ -754,6 +754,24 @@ 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 + +text = size.Per(measurementInterval).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 0e20cf130..66374a14c 100644 --- a/release_notes.md +++ b/release_notes.md @@ -8,6 +8,7 @@ - [#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 - [#173](https://github.com/MehdiK/Humanizer/pull/173): Added support for Window Phone 8.1 + - [#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...v1.28.0) 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 new file mode 100644 index 000000000..16ba5277b --- /dev/null +++ b/src/Humanizer.Tests/Bytes/ByteRateTests.cs @@ -0,0 +1,67 @@ +using System; +using Humanizer.Bytes; +using Humanizer.Localisation; +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).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); + + Assert.Equal(expectedValue, text); + } + + [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)); + + Assert.Throws(() => + { + dummyRate.Humanize(units); + }); + } + } +} 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..8e8dcc519 --- /dev/null +++ b/src/Humanizer/Bytes/ByteRate.cs @@ -0,0 +1,66 @@ +using System; +using Humanizer.Localisation; + +namespace Humanizer.Bytes +{ + + /// + /// Class to hold a ByteSize and a measurement interval, for the purpose of calculating the rate of transfer + /// + public class ByteRate + { + /// + /// Quantity of bytes + /// + /// + public ByteSize Size { 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; + } + + /// + /// 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 Humanize(TimeUnit timeUnit = TimeUnit.Second) + { + TimeSpan displayInterval; + string displayUnit; + + 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) + { + 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/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 c74acba60..0af4bf994 100644 --- a/src/Humanizer/Humanizer.csproj +++ b/src/Humanizer/Humanizer.csproj @@ -49,6 +49,7 @@ Humanizer.snk +