diff --git a/CHANGELOG.md b/CHANGELOG.md
index e3e33cff12d72291654f91077bcfa15fbc6d7947..9f5e83f652c2ecc1e317922c8c6c88282c3cb9f2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,3 +2,4 @@
 
 * added kwargs to ThreadCollection.from_class
 * fixed a bug in CustomException
+* added `parse_time_string`
diff --git a/docs/time.rst b/docs/time.rst
index 17613b9671635b431daf1e4f7722b766d9cbc4fa..e4c802c6576717e1c11acfe4f2b29e9155b5fa82 100644
--- a/docs/time.rst
+++ b/docs/time.rst
@@ -2,6 +2,14 @@
 Time
 ====
 
+parse_time_string
+-----------------
+
+Parse a time string into amount of seconds
+
+.. autofunction:: satella.time.parse_time_string
+
+
 measure
 -------
 Sometimes you just need to measure how long does a routine call take.
diff --git a/satella/__init__.py b/satella/__init__.py
index badd328601d8b2d1a030cf8c0cfc9bd6963cab99..ffa161e81b0e7d22c1fdb82fa3d2a97e6e264ef9 100644
--- a/satella/__init__.py
+++ b/satella/__init__.py
@@ -1 +1 @@
-__version__ = '2.14.46a3'
+__version__ = '2.14.46'
diff --git a/satella/instrumentation/cpu_time/collector.py b/satella/instrumentation/cpu_time/collector.py
index 95e55f3208cc83483f913e8c135cf868a00fb05d..39f5ec805e026069b9f224d54892e081689ad620 100644
--- a/satella/instrumentation/cpu_time/collector.py
+++ b/satella/instrumentation/cpu_time/collector.py
@@ -5,8 +5,13 @@ import time
 
 import psutil
 
+from satella.coding import for_argument
 from satella.coding.structures import Singleton
 from satella.coding.transforms import percentile
+from satella.time import parse_time_string
+
+DEFAULT_REFRESH_EACH = '30m'
+DEFAULT_WINDOW_SECONDS = '5m'
 
 
 @Singleton
@@ -14,14 +19,18 @@ class CPUProfileBuilderThread(threading.Thread):
     """
     A CPU profile builder thread and a core singleton object to use.
 
-    :param window_seconds: the amount of seconds for which to collect data
-    :param refresh_each: time of seconds to sleep between rebuilding of profiles
+    :param window_seconds: the amount of seconds for which to collect data.
+        Generally, this should be the interval during which your system cycles through all of
+        it's load, eg. if it asks it's devices each 5 minutes, the interval should be 300 seconds.
+        Or a time string.
+    :param refresh_each: time of seconds to sleep between rebuilding of profiles, or a time string.
     """
-    def __init__(self, window_seconds: int = 300, refresh_each: int = 1800,
+    def __init__(self, window_seconds: tp.Union[str, int] = DEFAULT_WINDOW_SECONDS,
+                 refresh_each: tp.Union[str, int] = DEFAULT_REFRESH_EACH,
                  percentiles_requested: tp.Sequence[float] = (0.9, )):
         super().__init__(name='CPU profile builder', daemon=True)
-        self.window_size = window_seconds
-        self.refresh_each = refresh_each
+        self.window_size = parse_time_string(window_seconds)
+        self.refresh_each = parse_time_string(refresh_each)
         self.data = []
         self.percentiles_requested = list(percentiles_requested)
         self.percentile_values = []
@@ -72,8 +81,7 @@ class CPUTimeManager:
         :param percent: float between 0 and 1
         :return: the value of the percentile
         """
-        cp = CPUProfileBuilderThread()
-        return cp.percentile(percent)
+        return CPUProfileBuilderThread().percentile(percent)
 
     @staticmethod
     def set_window_size(window_size: float) -> None:
@@ -82,18 +90,18 @@ class CPUTimeManager:
 
         :param window_size: time, in seconds
         """
-        cp = CPUProfileBuilderThread()
-        cp.window_size = window_size
+        CPUProfileBuilderThread().window_size = window_size
 
 
-def sleep_cpu_aware(seconds: float, of_below: tp.Optional[float] = None,
+@for_argument(parse_time_string)
+def sleep_cpu_aware(seconds: tp.Union[str, float], of_below: tp.Optional[float] = None,
                     of_above: tp.Optional[float] = None,
                     check_each: float = 1) -> bool:
     """
     Sleep for specified number of seconds.
 
     Quit earlier if the occupancy factor goes below of_below or above of_above
-    :param seconds: time to sleep
+    :param seconds: time to sleep in seconds, or a time string
     :param of_below: occupancy factor below which the sleep will return
     :param of_above: occupancy factor above which the sleep will return
     :param check_each: amount of seconds to sleep at once
@@ -157,11 +165,13 @@ def _calculate_occupancy_factor() -> float:
 
 def calculate_occupancy_factor() -> float:
     """
-    IMPORTANT!
+    Get the average load between now and the time it was last called as a float,
+    where 0.0 is LA=0 and 1.0 is LA=max_cores.
 
     This will be the average between now and the time it was last called.
 
-    This in rare cases (being called the first or the second time) may block for up to 0.1 seconds
+    .. warning:: This in rare cases (being called the first or the second time) may block for
+                 up to 0.1 seconds
 
     :return: a float between 0 and 1 telling you how occupied CPU-wise is your system.
     """
diff --git a/satella/time.py b/satella/time.py
index 6df47795b088892b8bf6866427a85b042f1c4f3e..7d7783639ed80eafbeaf1903f6c1126967ba8146 100644
--- a/satella/time.py
+++ b/satella/time.py
@@ -1,13 +1,13 @@
 import copy
 import inspect
-import math
 import time
 import typing as tp
 import warnings
 from concurrent.futures import Future
 from functools import wraps  # import from functools to prevent circular import exception
 
-__all__ = ['measure', 'time_as_int', 'time_ms', 'sleep', 'time_us', 'ExponentialBackoff']
+__all__ = ['measure', 'time_as_int', 'time_ms', 'sleep', 'time_us', 'ExponentialBackoff',
+           'parse_time_string']
 
 from satella.exceptions import WouldWaitMore
 
@@ -364,3 +364,39 @@ class ExponentialBackoff:
         Called when something successes.
         """
         self.counter = self.start
+
+
+TIME_MODIFIERS = [
+    ('s', 1),
+    ('m', 60),
+    ('h', 60*60),
+    ('d', 24*60*60),
+    ('w', 7*24*60*60)
+]
+
+
+def parse_time_string(s: tp.Union[int, float, str]) -> float:
+    """
+    Parse a time string into seconds, so eg. '30m' will be equal to 1800, and so will
+    be '30 min'.
+
+    This will correctly parse:
+    - seconds
+    - minutes
+    - hours
+    - days
+    - weeks
+
+    .. warning:: This does not handle fractions of a second!
+
+    :param s: time string or time value in seconds
+    :return: value in seconds
+    """
+    if isinstance(s, (int, float)):
+        return s
+
+    for modifier, multiple in TIME_MODIFIERS:
+        if modifier in s:
+            return float(s[:s.index(modifier)]) * multiple
+
+    return float(s)
diff --git a/tests/test_time.py b/tests/test_time.py
index 0699f8f0b90905e1c06de1ffce3d27fb9c9012ff..1c323fdc2e7d9a7de2945b20beaf1835bf197da7 100644
--- a/tests/test_time.py
+++ b/tests/test_time.py
@@ -4,12 +4,20 @@ import time
 import multiprocessing
 import os
 import sys
-from satella.time import measure, time_as_int, time_ms, sleep, ExponentialBackoff
+from satella.time import measure, time_as_int, time_ms, sleep, ExponentialBackoff, \
+    parse_time_string
 from concurrent.futures import Future
 
 
 class TestTime(unittest.TestCase):
 
+    def test_parse_time_string(self):
+        self.assertEqual(parse_time_string('30m'), 30 * 60)
+        self.assertEqual(parse_time_string('30h'), 30*60*60)
+        self.assertEqual(parse_time_string('30w'), 30 * 7 * 24 * 60 * 60)
+        self.assertEqual(parse_time_string(2), 2)
+        self.assertEqual(parse_time_string(2.0), 2.0)
+
     def test_exponential_backoff(self):
         with measure() as measurement:
             eb = ExponentialBackoff()