diff --git a/twisted/internet/base.py b/twisted/internet/base.py
index 46e9217..5f10c6c 100644
--- a/twisted/internet/base.py
+++ b/twisted/internet/base.py
@@ -176,16 +176,7 @@ class DelayedCall:
         if self._str is not None:
             return self._str
         if hasattr(self, 'func'):
-            # This code should be replaced by a utility function in reflect;
-            # see ticket #6066:
-            if hasattr(self.func, '__qualname__'):
-                func = self.func.__qualname__
-            elif hasattr(self.func, '__name__'):
-                func = self.func.func_name
-                if hasattr(self.func, 'im_class'):
-                    func = self.func.im_class.__name__ + '.' + func
-            else:
-                func = reflect.safe_repr(self.func)
+            func = reflect.getFunctionName(self.func)
         else:
             func = None
 
diff --git a/twisted/internet/task.py b/twisted/internet/task.py
index 6e7b908..11afa17 100644
--- a/twisted/internet/task.py
+++ b/twisted/internet/task.py
@@ -244,17 +244,8 @@ class LoopingCall:
 
 
     def __repr__(self):
-        if hasattr(self.f, '__qualname__'):
-            func = self.f.__qualname__
-        elif hasattr(self.f, '__name__'):
-            func = self.f.__name__
-            if hasattr(self.f, 'im_class'):
-                func = self.f.im_class.__name__ + '.' + func
-        else:
-            func = reflect.safe_repr(self.f)
-
         return 'LoopingCall<%r>(%s, *%s, **%s)' % (
-            self.interval, func, reflect.safe_repr(self.a),
+            self.interval, reflect.getFunctionName(self.f), reflect.safe_repr(self.a),
             reflect.safe_repr(self.kw))
 
 
diff --git a/twisted/python/_reflectpy3.py b/twisted/python/_reflectpy3.py
index c0451e9..04c6856 100644
--- a/twisted/python/_reflectpy3.py
+++ b/twisted/python/_reflectpy3.py
@@ -286,6 +286,25 @@ def _determineClassName(x):
             return '<BROKEN CLASS AT 0x%x>' % unsignedID(c)
 
 
+def getFunctionName(function):
+    """
+    Return name of the function passed.
+
+    @param funptr: Searched function.
+
+    @return: Function name
+    """
+    if hasattr( function , '__qualname__'):
+        func = function.__qualname__
+    elif hasattr( function, '__name__'):
+        func = function.func_name
+        if hasattr(function, 'im_class'):
+            func = function.im_class.__name__ + '.' + func
+    else:
+        func = safe_repr(function)
+    return func
+
+
 def _safeFormat(formatter, o):
     """
     Helper function for L{safe_repr} and L{safe_str}.
diff --git a/twisted/python/reflect.py b/twisted/python/reflect.py
index 541d99f..9c87b1d 100644
--- a/twisted/python/reflect.py
+++ b/twisted/python/reflect.py
@@ -37,7 +37,7 @@ from twisted.python._reflectpy3 import (
     addMethodNamesToDict)
 from twisted.python._reflectpy3 import namedModule, namedObject, namedClass
 from twisted.python._reflectpy3 import InvalidName, ModuleNotFound
-from twisted.python._reflectpy3 import ObjectNotFound, namedAny
+from twisted.python._reflectpy3 import ObjectNotFound, namedAny, getFunctionName
 from twisted.python._reflectpy3 import filenameToModuleName
 from twisted.python._reflectpy3 import qual, safe_str, safe_repr
 
@@ -249,7 +249,7 @@ class Summer(Accessor):
 
     deprecatedModuleAttribute(
         Version("Twisted", 12, 1, 0),
-        "Summer is a child class of twisted.python.reflect.Accessor which is " 
+        "Summer is a child class of twisted.python.reflect.Accessor which is "
         "deprecated.", "twisted.python.reflect", "Summer")
 
     def reallySet(self, k,v):
@@ -412,20 +412,20 @@ def accumulateClassDict(classObj, attr, adict, baseClass=None):
 
       class Soy:
         properties = {\"taste\": \"bland\"}
-    
+
       class Plant:
         properties = {\"colour\": \"green\"}
-    
+
       class Seaweed(Plant):
         pass
-    
+
       class Lunch(Soy, Seaweed):
         properties = {\"vegan\": 1 }
-    
+
       dct = {}
-    
+
       accumulateClassDict(Lunch, \"properties\", dct)
-    
+
       print dct
 
     {\"taste\": \"bland\", \"colour\": \"green\", \"vegan\": 1}
@@ -534,4 +534,4 @@ __all__ = [
     'accumulateMethods',
     'accumulateClassDict', 'accumulateClassList', 'isSame', 'isLike',
     'modgrep', 'isOfType', 'findInstances', 'objgrep', 'filenameToModuleName',
-    'fullyQualifiedName']
+    'fullyQualifiedName', 'getFunctionName']
diff --git a/twisted/python/test/test_reflectpy3.py b/twisted/python/test/test_reflectpy3.py
index acd8f8d..e942dc4 100644
--- a/twisted/python/test/test_reflectpy3.py
+++ b/twisted/python/test/test_reflectpy3.py
@@ -350,6 +350,40 @@ class LookupsTestCase(TestCase):
 
 
 
+class GetFunctionNameTestCase(TestCase):
+    """
+    Tests for L{getFunctionName}.
+    """
+
+    def test_function(self):
+        """
+        L{getFunctionName} should return function name passed in the argument.
+        """
+        self.assertEqual(
+            reflect.getFunctionName(reflect.getFunctionName),
+            "getFunctionName")
+
+
+    def test_memberFunction(self):
+        """
+        L{getFunctionName} should return fully qualified function name passed in the argument.
+        """
+        self.assertEqual(
+            reflect.getFunctionName(self.test_memberFunction),
+            "GetFunctionNameTestCase.test_memberFunction")
+
+
+    def test_safeRepr(self):
+        """
+        L{getFunctionName} should return string representation of non-function object passed.
+        """
+        i = 45
+        self.assertEqual(reflect.getFunctionName(i), "45")
+        string = "string"
+        self.assertEqual(reflect.getFunctionName(string), "'string'")
+
+
+
 class Breakable(object):
 
     breakRepr = False
