tdf#132475 writerfilter: use proper date-field defaults

When a field doesn't specify the format for a date,
the system is free to implement it as it chooses.
However, the user tends to disagree with that,
so lets try to create a MS-similar default for
these dates - since LO defaults to only displaying
a date without any time component.

For example: PRINTDATE \* MERGEFORMAT
doesn't tell us how to display the date.

MS Word uses different defaults depending on
the language of the document. And that only make sense.
For example, I noticed that of course en-GB's format
is dd/MM/yyyy instead of en-US's M/d/yyyy.

As a documentation example: 17.16.5.47 PRINTDATE
Syntax: PRINTDATE [ switches ]
Description: Retrieves the date and time on which the
document was last printed, as recorded in the
LastPrinted element of the Core File Properties part.

Switches: Zero or one date-and-time-formatting-switch
and zero or one of the following field-specific-switches.

PRINTDATE (without any formatting switches)
   the results are:
1/6/2006 2:58:00 PM

Change-Id: I41f3bdc155bd8cdc74177e4626b31ece9b47e16b
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133208
Tested-by: Justin Luth <jluth@mail.com>
Tested-by: Jenkins
Reviewed-by: Justin Luth <jluth@mail.com>
diff --git a/sw/qa/extras/ooxmlexport/data/tdf148380_printField.docx b/sw/qa/extras/ooxmlexport/data/tdf148380_printField.docx
new file mode 100644
index 0000000..4414574
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf148380_printField.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
index 9347a8a..464c0b2 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
@@ -100,6 +100,23 @@ DECLARE_OOXMLEXPORT_TEST(testTdf148380_modifiedField, "tdf148380_modifiedField.d
                                 OUString("Charles Brown"), xField->getPresentation(false));
}

DECLARE_OOXMLEXPORT_TEST(testTdf148380_printField, "tdf148380_printField.docx")
{
    // Verify that these are fields, and not just plain text
    uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY);
    auto xFieldsAccess(xTextFieldsSupplier->getTextFields());
    uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration());
    uno::Reference<text::XTextField> xField(xFields->nextElement(), uno::UNO_QUERY);
    // unspecified SAVEDATE gets default GB formatting because stylele.xml has w:lang w:val="en-GB"
    //CPPUNIT_ASSERT_EQUAL(OUString("08/04/2022 07:10:00 AM"), xField->getPresentation(false));
    //CPPUNIT_ASSERT_EQUAL(OUString("DocInformation:Modified"), xField->getPresentation(true));
    //xField.set(xFields->nextElement(), uno::UNO_QUERY);
    // MS Word actually shows "8 o'clock-ish" until the document is reprinted,
    // but it seems best to actually show the real last-printed date since it can't be FIXEDFLD
    CPPUNIT_ASSERT_EQUAL(OUString("08/04/2022 06:47:00 AM"), xField->getPresentation(false));
    CPPUNIT_ASSERT_EQUAL(OUString("DocInformation:Last printed"), xField->getPresentation(true));
}

DECLARE_OOXMLEXPORT_TEST(testTdf132475_printField, "tdf132475_printField.docx")
{
    // The last printed date field: formatted two different ways
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index 6206a93..d8327fa 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -35,6 +35,8 @@
#include <com/sun/star/document/IndexedPropertyValues.hpp>
#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
#include <com/sun/star/embed/XEmbeddedObject.hpp>
#include <com/sun/star/i18n/NumberFormatMapper.hpp>
#include <com/sun/star/i18n/NumberFormatIndex.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
#include <com/sun/star/style/LineNumberPosition.hpp>
@@ -1187,6 +1189,15 @@ uno::Any DomainMapper_Impl::GetAnyProperty(PropertyIds eId, const PropertyMapPtr
            return aProperty->second;
    }

    // then look whether it was directly applied as a paragraph property
    PropertyMapPtr pParaContext = GetTopContextOfType(CONTEXT_PARAGRAPH);
    if (pParaContext && rContext != pParaContext)
    {
        std::optional<PropertyMap::Property> aProperty = pParaContext->getProperty(eId);
        if (aProperty)
            return aProperty->second;
    }

    // then look whether it was inherited from a directly applied character style
    if ( eId != PROP_CHAR_STYLE_NAME && isCharacterProperty(eId) )
    {
@@ -4429,23 +4440,6 @@ static OUString lcl_trim(std::u16string_view sValue)
    return OUString(o3tl::trim(sValue)).replaceAll("\"","").replaceAll(u"“", "").replaceAll(u"”", "");
}

void DomainMapper_Impl::GetCurrentLocale(lang::Locale& rLocale)
{
    PropertyMapPtr pTopContext = GetTopContext();
    std::optional<PropertyMap::Property> pLocale = pTopContext->getProperty(PROP_CHAR_LOCALE);
    if( pLocale )
        pLocale->second >>= rLocale;
    else
    {
        PropertyMapPtr pParaContext = GetTopContextOfType(CONTEXT_PARAGRAPH);
        pLocale = pParaContext->getProperty(PROP_CHAR_LOCALE);
        if( pLocale )
        {
            pLocale->second >>= rLocale;
        }
    }
}

/*-------------------------------------------------------------------------
    extract the number format from the command and apply the resulting number
    format to the XPropertySet
@@ -4461,9 +4455,39 @@ void DomainMapper_Impl::SetNumberFormat( const OUString& rCommand,
    aUSLocale.Language = "en";
    aUSLocale.Country = "US";

    //determine current locale - todo: is it necessary to initialize this locale?
    lang::Locale aCurrentLocale = aUSLocale;
    GetCurrentLocale( aCurrentLocale );
    lang::Locale aCurrentLocale;
    GetAnyProperty(PROP_CHAR_LOCALE, GetTopContext()) >>= aCurrentLocale;

    if (sFormatString.isEmpty())
    {
        // No format specified. MS Word uses different formats depending on w:lang,
        // "M/d/yyyy h:mm:ss AM/PM" for en-US, and "dd/MM/yyyy hh:mm:ss AM/PM" for en-GB.
        // ALSO SEE: ww8par5's GetWordDefaultDateStringAsUS.
        sal_Int32 nPos = rCommand.indexOf(" \\");
        OUString sCommand = nPos == -1 ? rCommand.trim()
                                       : OUString(o3tl::trim(rCommand.subView(0, nPos)));
        if (sCommand == "CREATEDATE" || sCommand == "PRINTDATE" || sCommand == "SAVEDATE")
        {
            try
            {
                css::uno::Reference<css::i18n::XNumberFormatCode> const& xNumberFormatCode =
                    i18n::NumberFormatMapper::create(m_xComponentContext);
                sFormatString = xNumberFormatCode->getFormatCode(
                    css::i18n::NumberFormatIndex::DATE_SYSTEM_SHORT, aCurrentLocale).Code;
                nPos = sFormatString.indexOf("YYYY");
                if (nPos == -1)
                    sFormatString = sFormatString.replaceFirst("YY", "YYYY");
                if (aCurrentLocale == aUSLocale)
                    sFormatString += " h:mm:ss AM/PM";
                else
                    sFormatString += " hh:mm:ss AM/PM";
            }
            catch(const uno::Exception&)
            {
                DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
            }
        }
    }
    OUString sFormat = ConversionHelper::ConvertMSFormatStringToSO( sFormatString, aCurrentLocale, bHijri);
    //get the number formatter and convert the string to a format value
    try
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
index a8355e1..32721e1 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -614,7 +614,6 @@ private:
    bool m_bAnnotationResolved = false;
    std::unordered_map< sal_Int32, AnnotationPosition > m_aAnnotationPositions;

    void GetCurrentLocale(css::lang::Locale& rLocale);
    void SetNumberFormat(const OUString& rCommand, css::uno::Reference<css::beans::XPropertySet> const& xPropertySet, bool bDetectFormat = false);
    /// @throws css::uno::Exception
    css::uno::Reference<css::beans::XPropertySet> FindOrCreateFieldMaster(const char* pFieldMasterService, const OUString& rFieldMasterName);