Add PathUtils.getBaseName(Path)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 6451d0d..cd8fdcd 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -107,6 +107,7 @@
       <action dev="ggregory" type="add"                due-to="Gary Gregory">Add FileTimes.toUnixTime(FileTime).</action>
       <action dev="ggregory" type="add"                due-to="Gary Gregory">Add BrokenInputStream.Builder.</action>
       <action dev="ggregory" type="add"                due-to="Gary Gregory">Add PathUtils.getExtension(Path).</action>
+      <action dev="ggregory" type="add"                due-to="Gary Gregory">Add PathUtils.getBaseName(Path).</action>
       <action dev="ggregory" type="add"                due-to="Gary Gregory">Add ThrottledInputStream.</action>
       <action dev="ggregory" type="add"                due-to="Gary Gregory">Add IORunnable.noop().</action>
       <action dev="ggregory" type="add"                due-to="Gary Gregory">Add ChecksumInputStream and test #548.</action>
diff --git a/src/main/java/org/apache/commons/io/file/PathUtils.java b/src/main/java/org/apache/commons/io/file/PathUtils.java
index 30d0add..5812cc7 100644
--- a/src/main/java/org/apache/commons/io/file/PathUtils.java
+++ b/src/main/java/org/apache/commons/io/file/PathUtils.java
@@ -883,8 +883,7 @@
      * </p>
      *
      * @param path the path to query.
-     * @return the extension of the file or an empty string if none exists or {@code null}
-     * if the fileName is {@code null}.
+     * @return the extension of the file or an empty string if none exists or {@code null} if the fileName is {@code null}.
      * @since 2.16.0
      */
     public static String getExtension(final Path path) {
@@ -1857,6 +1856,24 @@
     }
 
     /**
+     * Gets the base name (the part up to and not including the last ".") of the last path segment of a file name.
+     * <p>
+     * Will return the file name itself if it doesn't contain any dots. All leading directories of the {@code file name} parameter are skipped.
+     * </p>
+     *
+     * @return the base name of file name
+     * @param path the path of the file to obtain the base name of.
+     * @since 2.16.0
+     */
+    public static String getBaseName(final Path path) {
+        if (path == null) {
+            return null;
+        }
+        final Path fileName = path.getFileName();
+        return fileName != null ? FilenameUtils.removeExtension(fileName.toString()) : null;
+    }
+
+    /**
      * Prevents instantiation.
      */
     private PathUtils() {
diff --git a/src/test/java/org/apache/commons/io/file/PathUtilsTest.java b/src/test/java/org/apache/commons/io/file/PathUtilsTest.java
index 2251445..74c4540 100644
--- a/src/test/java/org/apache/commons/io/file/PathUtilsTest.java
+++ b/src/test/java/org/apache/commons/io/file/PathUtilsTest.java
@@ -247,6 +247,27 @@
     }
 
     @Test
+    public void testGetBaseNamePathBaseCases() {
+        assertEquals("bar", PathUtils.getBaseName(Paths.get("a/b/c/bar.foo")));
+        assertEquals("foo", PathUtils.getBaseName(Paths.get("foo")));
+        assertEquals("", PathUtils.getBaseName(Paths.get("")));
+        assertEquals("", PathUtils.getBaseName(Paths.get(".")));
+        for (final File f : File.listRoots()) {
+            assertNull(PathUtils.getBaseName(f.toPath()));
+        }
+        if (SystemUtils.IS_OS_WINDOWS) {
+            assertNull(PathUtils.getBaseName(Paths.get("C:\\")));
+        }
+    }
+
+    @Test
+    public void testGetBaseNamePathCornerCases() {
+        assertNull(PathUtils.getBaseName((Path) null));
+        assertEquals("foo", PathUtils.getBaseName(Paths.get("foo.")));
+        assertEquals("", PathUtils.getBaseName(Paths.get("bar/.foo")));
+    }
+
+    @Test
     public void testGetExtension() {
         assertNull(PathUtils.getExtension(null));
         assertEquals("ext", PathUtils.getExtension(Paths.get("file.ext")));