util/abbreviate: little utility to abbreviate seconds and bytes
authorBrian Warner <warner@allmydata.com>
Wed, 19 Nov 2008 03:11:42 +0000 (20:11 -0700)
committerBrian Warner <warner@allmydata.com>
Wed, 19 Nov 2008 03:11:42 +0000 (20:11 -0700)
src/allmydata/test/test_util.py
src/allmydata/util/abbreviate.py [new file with mode: 0644]

index ba74c34f10f0888a478a761f71869f6c047209cb..f44fa09b880b37c7e42b6916c89dc96337478f9b 100644 (file)
@@ -7,7 +7,7 @@ from twisted.internet import defer, reactor
 from twisted.python import failure
 
 from allmydata.util import base32, idlib, humanreadable, mathutil, hashutil
-from allmydata.util import assertutil, fileutil, deferredutil
+from allmydata.util import assertutil, fileutil, deferredutil, abbreviate
 from allmydata.util import limiter, time_format, pollmixin, cachedir
 
 class Base32(unittest.TestCase):
@@ -445,6 +445,71 @@ class HashUtilTests(unittest.TestCase):
         h2.update("foo")
         self.failUnlessEqual(h1, h2.digest())
 
+class Abbreviate(unittest.TestCase):
+    def test_time(self):
+        a = abbreviate.abbreviate_time
+        self.failUnlessEqual(a(None), "unknown")
+        self.failUnlessEqual(a(0), "0 seconds")
+        self.failUnlessEqual(a(1), "1 second")
+        self.failUnlessEqual(a(2), "2 seconds")
+        self.failUnlessEqual(a(119), "119 seconds")
+        MIN = 60
+        self.failUnlessEqual(a(2*MIN), "2 minutes")
+        self.failUnlessEqual(a(60*MIN), "60 minutes")
+        self.failUnlessEqual(a(179*MIN), "179 minutes")
+        HOUR = 60*MIN
+        self.failUnlessEqual(a(180*MIN), "3 hours")
+        self.failUnlessEqual(a(4*HOUR), "4 hours")
+        DAY = 24*HOUR
+        MONTH = 30*DAY
+        self.failUnlessEqual(a(2*DAY), "2 days")
+        self.failUnlessEqual(a(2*MONTH), "2 months")
+        YEAR = 365*DAY
+        self.failUnlessEqual(a(5*YEAR), "5 years")
+
+    def test_space(self):
+        tests_si = [(None, "unknown"),
+                    (0, "0 B"),
+                    (1, "1 B"),
+                    (999, "999 B"),
+                    (1000, "1000 B"),
+                    (1023, "1023 B"),
+                    (1024, "1.02 kB"),
+                    (20*1000, "20.00 kB"),
+                    (1024*1024, "1.05 MB"),
+                    (1000*1000, "1.00 MB"),
+                    (1000*1000*1000, "1.00 GB"),
+                    (1000*1000*1000*1000, "1.00 TB"),
+                    (1000*1000*1000*1000*1000, "1.00 PB"),
+                    (1234567890123456, "1.23 PB"),
+                    ]
+        for (x, expected) in tests_si:
+            got = abbreviate.abbreviate_space(x, SI=True)
+            self.failUnlessEqual(got, expected)
+
+        tests_base1024 = [(None, "unknown"),
+                          (0, "0 B"),
+                          (1, "1 B"),
+                          (999, "999 B"),
+                          (1000, "1000 B"),
+                          (1023, "1023 B"),
+                          (1024, "1.00 kiB"),
+                          (20*1024, "20.00 kiB"),
+                          (1000*1000, "976.56 kiB"),
+                          (1024*1024, "1.00 MiB"),
+                          (1024*1024*1024, "1.00 GiB"),
+                          (1024*1024*1024*1024, "1.00 TiB"),
+                          (1000*1000*1000*1000*1000, "909.49 TiB"),
+                          (1024*1024*1024*1024*1024, "1.00 PiB"),
+                          (1234567890123456, "1.10 PiB"),
+                    ]
+        for (x, expected) in tests_base1024:
+            got = abbreviate.abbreviate_space(x, SI=False)
+            self.failUnlessEqual(got, expected)
+
+        self.failUnlessEqual(abbreviate.abbreviate_space_both(1234567),
+                             "(1.23 MB, 1.18 MiB)")
+
 class Limiter(unittest.TestCase):
     def job(self, i, foo):
         self.calls.append( (i, foo) )
diff --git a/src/allmydata/util/abbreviate.py b/src/allmydata/util/abbreviate.py
new file mode 100644 (file)
index 0000000..13c51c7
--- /dev/null
@@ -0,0 +1,55 @@
+
+HOUR = 3600
+DAY = 24*3600
+WEEK = 7*DAY
+MONTH = 30*DAY
+YEAR = 365*DAY
+
+def abbreviate_time(s):
+    def _plural(count, unit):
+        count = int(count)
+        if count == 1:
+            return "%d %s" % (count, unit)
+        return "%d %ss" % (count, unit)
+    if s is None:
+        return "unknown"
+    if s < 120:
+        return _plural(s, "second")
+    if s < 3*HOUR:
+        return _plural(s/60, "minute")
+    if s < 2*DAY:
+        return _plural(s/HOUR, "hour")
+    if s < 2*MONTH:
+        return _plural(s/DAY, "day")
+    if s < 4*YEAR:
+        return _plural(s/MONTH, "month")
+    return _plural(s/YEAR, "year")
+
+def abbreviate_space(s, SI=True):
+    if s is None:
+        return "unknown"
+    if SI:
+        U = 1000.0
+        isuffix = "B"
+    else:
+        U = 1024.0
+        isuffix = "iB"
+    def r(count, suffix):
+        return "%.2f %s%s" % (count, suffix, isuffix)
+
+    if s < 1024: # 1000-1023 get emitted as bytes, even in SI mode
+        return "%d B" % s
+    if s < U*U:
+        return r(s/U, "k")
+    if s < U*U*U:
+        return r(s/(U*U), "M")
+    if s < U*U*U*U:
+        return r(s/(U*U*U), "G")
+    if s < U*U*U*U*U:
+        return r(s/(U*U*U*U), "T")
+    return r(s/(U*U*U*U*U), "P")
+
+def abbreviate_space_both(s):
+    return "(%s, %s)" % (abbreviate_space(s, True),
+                         abbreviate_space(s, False))
+