diff --git a/doc/lua-filters.md b/doc/lua-filters.md
index 4411e90a5..7c0e83ee8 100644
--- a/doc/lua-filters.md
+++ b/doc/lua-filters.md
@@ -1433,6 +1433,17 @@ Lua functions for pandoc scripts.
 This module exposes internal pandoc functions and utility
 functions.
 
+[`normalize_date (date_string)`]{#utils-normalize_date}
+
+:   Parse a date and convert (if possible) to "YYYY-MM-DD"
+    format. We limit years to the range 1601-9999 (ISO 8601
+    accepts greater than or equal to 1583, but MS Word only
+    accepts dates starting 1601).
+
+    Returns:
+
+    -   A date string, or nil when the conversion failed.
+
 [`sha1 (contents)`]{#utils-sha1}
 
 :   Returns the SHA1 has of the contents.
diff --git a/src/Text/Pandoc/Lua/Module/Utils.hs b/src/Text/Pandoc/Lua/Module/Utils.hs
index 3c830a4bd..458716a03 100644
--- a/src/Text/Pandoc/Lua/Module/Utils.hs
+++ b/src/Text/Pandoc/Lua/Module/Utils.hs
@@ -33,7 +33,7 @@ import Control.Applicative ((<|>))
 import Foreign.Lua (FromLuaStack, Lua, LuaInteger, NumResults)
 import Text.Pandoc.Definition (Pandoc, Meta, Block, Inline)
 import Text.Pandoc.Lua.StackInstances ()
-import Text.Pandoc.Lua.Util (addFunction)
+import Text.Pandoc.Lua.Util (OrNil (OrNil), addFunction)
 
 import qualified Data.Digest.Pure.SHA as SHA
 import qualified Data.ByteString.Lazy as BSL
@@ -44,9 +44,10 @@ import qualified Text.Pandoc.Shared as Shared
 pushModule :: Lua NumResults
 pushModule = do
   Lua.newtable
-  addFunction "to_roman_numeral" toRomanNumeral
+  addFunction "normalize_date" normalizeDate
   addFunction "sha1" sha1
   addFunction "stringify" stringify
+  addFunction "to_roman_numeral" toRomanNumeral
   return 1
 
 -- | Calculate the hash of the given contents.
@@ -85,3 +86,10 @@ instance FromLuaStack AstElement where
 -- | Convert a number < 4000 to uppercase roman numeral.
 toRomanNumeral :: LuaInteger -> Lua String
 toRomanNumeral = return . Shared.toRomanNumeral . fromIntegral
+
+-- | Parse a date and convert (if possible) to "YYYY-MM-DD" format. We
+-- limit years to the range 1601-9999 (ISO 8601 accepts greater than
+-- or equal to 1583, but MS Word only accepts dates starting 1601).
+-- Returns nil instead of a string if the conversion failed.
+normalizeDate :: String -> Lua (OrNil String)
+normalizeDate = return . OrNil . Shared.normalizeDate
diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs
index 28d09d339..1f7664fc0 100644
--- a/src/Text/Pandoc/Lua/Util.hs
+++ b/src/Text/Pandoc/Lua/Util.hs
@@ -125,6 +125,10 @@ instance FromLuaStack a => FromLuaStack (OrNil a) where
       then return (OrNil Nothing)
       else OrNil . Just <$> Lua.peek idx
 
+instance ToLuaStack a => ToLuaStack (OrNil a) where
+  push (OrNil Nothing)  = Lua.pushnil
+  push (OrNil (Just x)) = Lua.push x
+
 -- | Helper class for pushing a single value to the stack via a lua function.
 -- See @pushViaCall@.
 class PushViaCall a where
diff --git a/test/Tests/Lua.hs b/test/Tests/Lua.hs
index 956575911..7fb4309de 100644
--- a/test/Tests/Lua.hs
+++ b/test/Tests/Lua.hs
@@ -96,11 +96,12 @@ tests = map (localOption (QuickCheckTests 20))
     assertFilterConversion "pandoc.utils doesn't work as expected."
       "test-pandoc-utils.lua"
       (doc $ para "doesn't matter")
-      (doc $ mconcat [ plain (str "sha1: OK")
+      (doc $ mconcat [ plain (str "normalize_date: OK")
                      , plain (str "pipe: OK")
                      , plain (str "failing pipe: OK")
                      , plain (str "read: OK")
                      , plain (str "failing read: OK")
+                     , plain (str "sha1: OK")
                      , plain (str "stringify: OK")
                      , plain (str "to_roman_numeral: OK")
                      ])
diff --git a/test/lua/test-pandoc-utils.lua b/test/lua/test-pandoc-utils.lua
index 0a7aedbfd..b79f033f8 100644
--- a/test/lua/test-pandoc-utils.lua
+++ b/test/lua/test-pandoc-utils.lua
@@ -72,6 +72,13 @@ function test_to_roman_numeral ()
     and not pcall(utils.to_roman_numeral, 'not a number')
 end
 
+-- normalize_date
+------------------------------------------------------------------------
+function test_normalize_date ()
+  return utils.normalize_date("12/31/2017") == '2017-12-31'
+    and utils.normalize_date("pandoc") == nil
+end
+
 -- Return result
 ------------------------------------------------------------------------
 function run(fn)
@@ -80,11 +87,12 @@ end
 
 function Para (el)
   return {
-    pandoc.Plain{pandoc.Str("sha1: " .. run(test_sha1))},
+    pandoc.Plain{pandoc.Str("normalize_date: " .. run(test_normalize_date))},
     pandoc.Plain{pandoc.Str("pipe: " .. run(test_pipe))},
     pandoc.Plain{pandoc.Str("failing pipe: " .. run(test_failing_pipe))},
     pandoc.Plain{pandoc.Str("read: " .. run(test_read))},
     pandoc.Plain{pandoc.Str("failing read: " .. run(test_failing_read))},
+    pandoc.Plain{pandoc.Str("sha1: " .. run(test_sha1))},
     pandoc.Plain{pandoc.Str("stringify: " .. run(test_stringify))},
     pandoc.Plain{pandoc.Str("to_roman_numeral: " .. run(test_to_roman_numeral))},
   }