Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/svl/source/numbers/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 212 kB image not shown  

Quelle  zforlist.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <sal/config.h>

#include <sal/log.hxx>
#include <officecfg/Office/Common.hxx>
#include <svl/zforlist.hxx>
#include <svl/currencytable.hxx>

#include <comphelper/lok.hxx>
#include <comphelper/string.hxx>
#include <o3tl/string_view.hxx>
#include <tools/debug.hxx>
#include <unotools/charclass.hxx>
#include <unotools/configmgr.hxx>
#include <i18nlangtag/mslangid.hxx>
#include <unotools/localedatawrapper.hxx>
#include <com/sun/star/i18n/KNumberFormatUsage.hpp>
#include <com/sun/star/i18n/KNumberFormatType.hpp>
#include <com/sun/star/i18n/FormatElement.hpp>
#include <com/sun/star/i18n/Currency2.hpp>
#include <com/sun/star/i18n/NumberFormatCode.hpp>
#include <com/sun/star/i18n/XNumberFormatCode.hpp>
#include <com/sun/star/i18n/NumberFormatMapper.hpp>
#include <comphelper/processfactory.hxx>

#include <osl/mutex.hxx>

#include "zforscan.hxx"
#include "zforfind.hxx"
#include <svl/zformat.hxx>
#include <i18npool/reservedconstants.hxx>

#include <unotools/syslocaleoptions.hxx>
#include <unotools/digitgroupingiterator.hxx>
#include <rtl/strbuf.hxx>
#include <rtl/math.hxx>

#include <atomic>
#include <limits>
#include <memory>
#include <set>

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::i18n;
using namespace ::com::sun::star::lang;

// Constants for type offsets per Country/Language (CL)
#define ZF_STANDARD              0
#define ZF_STANDARD_PERCENT     10
#define ZF_STANDARD_CURRENCY    20
#define ZF_STANDARD_DATE        30
#define ZF_STANDARD_TIME        60
#define ZF_STANDARD_DURATION    (ZF_STANDARD_TIME + 4)
#define ZF_STANDARD_DATETIME    70
#define ZF_STANDARD_SCIENTIFIC  80
#define ZF_STANDARD_FRACTION    85

// Additional builtin formats, historically not fitting into the first 10 of a
// category. Make sure it doesn't spill over to the next category.
#define ZF_STANDARD_DATE_SYS_DMMMYYYY       (ZF_STANDARD_DATE + 10)
#define ZF_STANDARD_DATE_SYS_DMMMMYYYY      (ZF_STANDARD_DATE + 11)
#define ZF_STANDARD_DATE_SYS_NNDMMMYY       (ZF_STANDARD_DATE + 12)
#define ZF_STANDARD_DATE_SYS_NNDMMMMYYYY    (ZF_STANDARD_DATE + 13)
#define ZF_STANDARD_DATE_SYS_NNNNDMMMMYYYY  (ZF_STANDARD_DATE + 14)
#define ZF_STANDARD_DATE_DIN_DMMMYYYY       (ZF_STANDARD_DATE + 15)
#define ZF_STANDARD_DATE_DIN_DMMMMYYYY      (ZF_STANDARD_DATE + 16)
#define ZF_STANDARD_DATE_DIN_MMDD           (ZF_STANDARD_DATE + 17)
#define ZF_STANDARD_DATE_DIN_YYMMDD         (ZF_STANDARD_DATE + 18)
#define ZF_STANDARD_DATE_DIN_YYYYMMDD       (ZF_STANDARD_DATE + 19)
#define ZF_STANDARD_DATE_WW                 (ZF_STANDARD_DATE + 20)

#define ZF_STANDARD_LOGICAL     SV_MAX_COUNT_STANDARD_FORMATS-1 //  99
#define ZF_STANDARD_TEXT        SV_MAX_COUNT_STANDARD_FORMATS   // 100

static_assert( ZF_STANDARD_TEXT == NF_STANDARD_FORMAT_TEXT, "definition mismatch" );

static_assert( NF_INDEX_TABLE_RESERVED_START == i18npool::nStopPredefinedFormatIndex,
        "NfIndexTableOffset does not match i18npool's locale data predefined format code index bounds.");

static_assert( NF_INDEX_TABLE_ENTRIES <= i18npool::nFirstFreeFormatIndex,
        "NfIndexTableOffset crosses i18npool's locale data reserved format code index bounds.\n"
        "You will need to adapt all locale data files defining index values "
        "(formatIndex=\"...\") in that range and increment those and when done "
        "adjust nFirstFreeFormatIndex in include/i18npool/reservedconstants.hxx");

/* Locale that is set if an unknown locale (from another system) is loaded of
 * legacy documents. Can not be SYSTEM because else, for example, a German "DM"
 * (old currency) is recognized as a date (#53155#). */

#define UNKNOWN_SUBSTITUTE      LANGUAGE_ENGLISH_US

// Same order as in include/svl/zforlist.hxx enum NfIndexTableOffset
sal_uInt32 const indexTable[NF_INDEX_TABLE_ENTRIES] = {
    ZF_STANDARD, // NF_NUMBER_STANDARD
    ZF_STANDARD + 1, // NF_NUMBER_INT
    ZF_STANDARD + 2, // NF_NUMBER_DEC2
    ZF_STANDARD + 3, // NF_NUMBER_1000INT
    ZF_STANDARD + 4, // NF_NUMBER_1000DEC2
    ZF_STANDARD + 5, // NF_NUMBER_SYSTEM
    ZF_STANDARD_SCIENTIFIC, // NF_SCIENTIFIC_000E000
    ZF_STANDARD_SCIENTIFIC + 1, // NF_SCIENTIFIC_000E00
    ZF_STANDARD_PERCENT, // NF_PERCENT_INT
    ZF_STANDARD_PERCENT + 1, // NF_PERCENT_DEC2
    ZF_STANDARD_FRACTION, // NF_FRACTION_1D
    ZF_STANDARD_FRACTION + 1, // NF_FRACTION_2D
    ZF_STANDARD_CURRENCY, // NF_CURRENCY_1000INT
    ZF_STANDARD_CURRENCY + 1, // NF_CURRENCY_1000DEC2
    ZF_STANDARD_CURRENCY + 2, // NF_CURRENCY_1000INT_RED
    ZF_STANDARD_CURRENCY + 3, // NF_CURRENCY_1000DEC2_RED
    ZF_STANDARD_CURRENCY + 4, // NF_CURRENCY_1000DEC2_CCC
    ZF_STANDARD_CURRENCY + 5, // NF_CURRENCY_1000DEC2_DASHED
    ZF_STANDARD_DATE, // NF_DATE_SYSTEM_SHORT
    ZF_STANDARD_DATE + 8, // NF_DATE_SYSTEM_LONG
    ZF_STANDARD_DATE + 7, // NF_DATE_SYS_DDMMYY
    ZF_STANDARD_DATE + 6, // NF_DATE_SYS_DDMMYYYY
    ZF_STANDARD_DATE + 9, // NF_DATE_SYS_DMMMYY
    ZF_STANDARD_DATE_SYS_DMMMYYYY, // NF_DATE_SYS_DMMMYYYY
    ZF_STANDARD_DATE_DIN_DMMMYYYY, // NF_DATE_DIN_DMMMYYYY
    ZF_STANDARD_DATE_SYS_DMMMMYYYY, // NF_DATE_SYS_DMMMMYYYY
    ZF_STANDARD_DATE_DIN_DMMMMYYYY, // NF_DATE_DIN_DMMMMYYYY
    ZF_STANDARD_DATE_SYS_NNDMMMYY, // NF_DATE_SYS_NNDMMMYY
    ZF_STANDARD_DATE + 1, // NF_DATE_DEF_NNDDMMMYY
    ZF_STANDARD_DATE_SYS_NNDMMMMYYYY, // NF_DATE_SYS_NNDMMMMYYYY
    ZF_STANDARD_DATE_SYS_NNNNDMMMMYYYY, // NF_DATE_SYS_NNNNDMMMMYYYY
    ZF_STANDARD_DATE_DIN_MMDD, // NF_DATE_DIN_MMDD
    ZF_STANDARD_DATE_DIN_YYMMDD, // NF_DATE_DIN_YYMMDD
    ZF_STANDARD_DATE_DIN_YYYYMMDD, // NF_DATE_DIN_YYYYMMDD
    ZF_STANDARD_DATE + 2, // NF_DATE_SYS_MMYY
    ZF_STANDARD_DATE + 3, // NF_DATE_SYS_DDMMM
    ZF_STANDARD_DATE + 4, // NF_DATE_MMMM
    ZF_STANDARD_DATE + 5, // NF_DATE_QQJJ
    ZF_STANDARD_DATE_WW, // NF_DATE_WW
    ZF_STANDARD_TIME, // NF_TIME_HHMM
    ZF_STANDARD_TIME + 1, // NF_TIME_HHMMSS
    ZF_STANDARD_TIME + 2, // NF_TIME_HHMMAMPM
    ZF_STANDARD_TIME + 3, // NF_TIME_HHMMSSAMPM
    ZF_STANDARD_TIME + 4, // NF_TIME_HH_MMSS
    ZF_STANDARD_TIME + 5, // NF_TIME_MMSS00
    ZF_STANDARD_TIME + 6, // NF_TIME_HH_MMSS00
    ZF_STANDARD_DATETIME, // NF_DATETIME_SYSTEM_SHORT_HHMM
    ZF_STANDARD_DATETIME + 1, // NF_DATETIME_SYS_DDMMYYYY_HHMMSS
    ZF_STANDARD_LOGICAL, // NF_BOOLEAN
    ZF_STANDARD_TEXT, // NF_TEXT
    ZF_STANDARD_DATETIME + 2, // NF_DATETIME_SYS_DDMMYYYY_HHMM
    ZF_STANDARD_FRACTION + 2, // NF_FRACTION_3D
    ZF_STANDARD_FRACTION + 3, // NF_FRACTION_2
    ZF_STANDARD_FRACTION + 4, // NF_FRACTION_4
    ZF_STANDARD_FRACTION + 5, // NF_FRACTION_8
    ZF_STANDARD_FRACTION + 6, // NF_FRACTION_16
    ZF_STANDARD_FRACTION + 7, // NF_FRACTION_10
    ZF_STANDARD_FRACTION + 8, // NF_FRACTION_100
    ZF_STANDARD_DATETIME + 3, // NF_DATETIME_ISO_YYYYMMDD_HHMMSS
    ZF_STANDARD_DATETIME + 4, // NF_DATETIME_ISO_YYYYMMDD_HHMMSS000
    ZF_STANDARD_DATETIME + 5, // NF_DATETIME_ISO_YYYYMMDDTHHMMSS
    ZF_STANDARD_DATETIME + 6  // NF_DATETIME_ISO_YYYYMMDDTHHMMSS000
};

/**
    instead of every number formatter being a listener we have a registry which
    also handles one instance of the SysLocale options
 */


class SvNumberFormatterRegistry_Impl : public utl::ConfigurationListener
{
    std::vector< SvNumberFormatter* >
                                aFormatters;
    SvtSysLocaleOptions         aSysLocaleOptions;
    LanguageType                eSysLanguage;

public:
                            SvNumberFormatterRegistry_Impl();
    virtual                 ~SvNumberFormatterRegistry_Impl() override;

    void                    Insert( SvNumberFormatter* pThis )
                                { aFormatters.push_back( pThis ); }

    void                    Remove( SvNumberFormatter const * pThis );

    size_t                  Count() const
                                { return aFormatters.size(); }

    virtual void            ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints ) override;
};

SvNumberFormatterRegistry_Impl::SvNumberFormatterRegistry_Impl()
    : eSysLanguage(MsLangId::getRealLanguage( LANGUAGE_SYSTEM ))
{
    aSysLocaleOptions.AddListener( this );
}


SvNumberFormatterRegistry_Impl::~SvNumberFormatterRegistry_Impl()
{
    aSysLocaleOptions.RemoveListener( this );
}


void SvNumberFormatterRegistry_Impl::Remove( SvNumberFormatter const * pThis )
{
    auto it = std::find(aFormatters.begin(), aFormatters.end(), pThis);
    if (it != aFormatters.end())
        aFormatters.erase( it );
}

void SvNumberFormatterRegistry_Impl::ConfigurationChanged( utl::ConfigurationBroadcaster*,
                                                           ConfigurationHints nHint)
{
    ::osl::MutexGuard aGuard( SvNumberFormatter::GetGlobalMutex() );

    if ( nHint & ConfigurationHints::Locale )
    {
        for(SvNumberFormatter* pFormatter : aFormatters)
            pFormatter->ReplaceSystemCL( eSysLanguage );
        eSysLanguage = MsLangId::getRealLanguage( LANGUAGE_SYSTEM );
    }
    if ( nHint & ConfigurationHints::Currency )
    {
        for(SvNumberFormatter* pFormatter : aFormatters)
            pFormatter->ResetDefaultSystemCurrency();
    }
    if ( nHint & ConfigurationHints::DatePatterns )
    {
        for(SvNumberFormatter* pFormatter : aFormatters)
            pFormatter->InvalidateDateAcceptancePatterns();
    }
}

static std::atomic<bool> g_CurrencyTableInitialized;

SvNumberFormatterRegistry_Impl* SvNumberFormatter::pFormatterRegistry = nullptr;
namespace
{
    NfCurrencyTable& theCurrencyTable()
    {
        static NfCurrencyTable SINGLETON;
        return SINGLETON;
    }

    NfCurrencyTable& theLegacyOnlyCurrencyTable()
    {
        static NfCurrencyTable SINGLETON;
        return SINGLETON;
    }

    /** THE set of installed locales. */
    std::set< LanguageType > theInstalledLocales;

}
sal_uInt16 SvNumberFormatter::nSystemCurrencyPosition = 0;

// Whether BankSymbol (not CurrencySymbol!) is always at the end (1 $;-1 $) or
// language dependent.
#define NF_BANKSYMBOL_FIX_POSITION 1

const sal_uInt16 SvNumberFormatter::UNLIMITED_PRECISION   = ::std::numeric_limits<sal_uInt16>::max();
const sal_uInt16 SvNumberFormatter::INPUTSTRING_PRECISION = ::std::numeric_limits<sal_uInt16>::max()-1;

void SvNFEngine::ChangeIntl(SvNFLanguageData& rCurrentLanguage, LanguageType eLnge)
{
    rCurrentLanguage.ChangeIntl(eLnge);
}

void SvNFEngine::ChangeNullDate(const SvNFLanguageData& rCurrentLanguage, sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear)
{
    rCurrentLanguage.pFormatScanner->ChangeNullDate(nDay, nMonth, nYear);
    rCurrentLanguage.pStringScanner->ChangeNullDate(nDay, nMonth, nYear);
}

SvNFLanguageData::SvNFLanguageData(const Reference<XComponentContext>& rxContext, LanguageType eLang,
                                   const SvNumberFormatter& rColorCallback)
    : xContext(rxContext)
    , IniLnge(eLang)
    , ActLnge(eLang)
    , aLanguageTag(eLang)
    , eEvalDateFormat(NF_EVALDATEFORMAT_INTL)
{
    xCharClass.changeLocale(xContext, aLanguageTag);
    xLocaleData.init(aLanguageTag);
    xCalendar.init(xContext, aLanguageTag.getLocale());
    xTransliteration.init(xContext, ActLnge);

    // cached locale data items
    const LocaleDataWrapper* pLoc = GetLocaleData();
    aDecimalSep = pLoc->getNumDecimalSep();
    aDecimalSepAlt = pLoc->getNumDecimalSepAlt();
    aThousandSep = pLoc->getNumThousandSep();
    aDateSep = pLoc->getDateSep();

    pStringScanner.reset(new ImpSvNumberInputScan(*this));
    pFormatScanner.reset(new ImpSvNumberformatScan(*this, rColorCallback));
}

SvNFLanguageData::SvNFLanguageData(const SvNFLanguageData& rOther)
    : xContext(rOther.xContext)
    , IniLnge(rOther.IniLnge)
    , ActLnge(rOther.ActLnge)
    , aLanguageTag(rOther.aLanguageTag)
    , aDecimalSep(rOther.aDecimalSep)
    , aDecimalSepAlt(rOther.aDecimalSepAlt)
    , aThousandSep(rOther.aThousandSep)
    , aDateSep(rOther.aDateSep)
    , eEvalDateFormat(rOther.eEvalDateFormat)
{
    xCharClass.changeLocale(xContext, aLanguageTag);
    xLocaleData.init(aLanguageTag);
    xCalendar.init(xContext, aLanguageTag.getLocale());
    xTransliteration.init(xContext, ActLnge);

    pStringScanner.reset(new ImpSvNumberInputScan(*this));
    pFormatScanner.reset(new ImpSvNumberformatScan(*this, rOther.pFormatScanner->getColorCallback(),
                                                   rOther.pFormatScanner->GetNullDate()));
}

SvNFLanguageData::~SvNFLanguageData()
{
}

const LocaleDataWrapper* SvNFLanguageData::GetLocaleData() const { return xLocaleData.get(); }

const CharClass* SvNFLanguageData::GetCharClass() const { return xCharClass.get(); }

CalendarWrapper* SvNFLanguageData::GetCalendar() const { return xCalendar.get(); }

const ::utl::TransliterationWrapper* SvNFLanguageData::GetTransliteration() const
{
    return xTransliteration.get();
}

const LanguageTag& SvNFLanguageData::GetLanguageTag() const { return aLanguageTag; }

const ImpSvNumberformatScan* SvNFLanguageData::GetFormatScanner() const { return pFormatScanner.get(); }

const OUString& SvNFLanguageData::GetNumDecimalSep() const { return aDecimalSep; }

const OUString& SvNFLanguageData::GetNumDecimalSepAlt() const { return aDecimalSepAlt; }

const OUString& SvNFLanguageData::GetNumThousandSep() const { return aThousandSep; }

const OUString& SvNFLanguageData::GetDateSep() const { return aDateSep; }

bool SvNFLanguageData::IsDecimalSep( std::u16string_view rStr ) const
{
    if (rStr == GetNumDecimalSep())
        return true;
    if (GetNumDecimalSepAlt().isEmpty())
        return false;
    return rStr == GetNumDecimalSepAlt();
}

OUString SvNFLanguageData::GetLangDecimalSep( LanguageType nLang )
{
    if (nLang == ActLnge)
    {
        return GetNumDecimalSep();
    }
    OUString aRet;
    LanguageType eSaveLang = xLocaleData.getCurrentLanguage();
    if (nLang == eSaveLang)
    {
        aRet = xLocaleData->getNumDecimalSep();
    }
    else
    {
        LanguageTag aSaveLocale( xLocaleData->getLanguageTag() );
        xLocaleData.changeLocale( LanguageTag( nLang));
        aRet = xLocaleData->getNumDecimalSep();
        xLocaleData.changeLocale( aSaveLocale );
    }
    return aRet;
}

void SvNFLanguageData::ChangeIntl(LanguageType eLnge)
{
    if (ActLnge == eLnge)
        return;

    ActLnge = eLnge;

    aLanguageTag.reset( eLnge );
    xCharClass.changeLocale( xContext, aLanguageTag );
    xLocaleData.changeLocale( aLanguageTag );
    xCalendar.changeLocale( aLanguageTag.getLocale() );
    xTransliteration.changeLocale( eLnge );

    // cached locale data items, initialize BEFORE calling ChangeIntl below
    const LocaleDataWrapper* pLoc = GetLocaleData();
    aDecimalSep = pLoc->getNumDecimalSep();
    aDecimalSepAlt = pLoc->getNumDecimalSepAlt();
    aThousandSep = pLoc->getNumThousandSep();
    aDateSep = pLoc->getDateSep();

    pFormatScanner->ChangeIntl();
    pStringScanner->ChangeIntl();
}

SvNumberFormatter::SvNumberFormatter( const Reference< XComponentContext >& rxContext,
                                      LanguageType eLang )
    : m_xContext( rxContext )
    , IniLnge(eLang != LANGUAGE_DONTKNOW ? eLang : UNKNOWN_SUBSTITUTE)
    , m_aRWPolicy(SvNFEngine::GetRWPolicy(m_aFormatData))
    , m_aCurrentLanguage(rxContext, IniLnge, *this)
    , m_xNatNum(m_xContext)
{

    // 0 .. 999 for initialized language formats
    m_aFormatData.ImpGenerateFormats(m_aCurrentLanguage, GetNatNum(), 0, false);

    ::osl::MutexGuard aGuard( GetGlobalMutex() );
    GetFormatterRegistry().Insert( this );
}

SvNumberFormatter::~SvNumberFormatter()
{
    {
        ::osl::MutexGuard aGuard( GetGlobalMutex() );
        pFormatterRegistry->Remove( this );
        if ( !pFormatterRegistry->Count() )
        {
            delete pFormatterRegistry;
            pFormatterRegistry = nullptr;
        }
    }

    m_aFormatData.aFTable.clear();
    ClearMergeTable();
}

void SvNumberFormatter::ChangeIntl(LanguageType eLnge)
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    SvNFEngine::ChangeIntl(m_aCurrentLanguage, eLnge);
}

// static
::osl::Mutex& SvNumberFormatter::GetGlobalMutex()
{
    // #i77768# Due to a static reference in the toolkit lib
    // we need a mutex that lives longer than the svl library.
    // Otherwise the dtor would use a destructed mutex!!
    static osl::Mutex* persistentMutex(new osl::Mutex);

    return *persistentMutex;
}


// static
SvNumberFormatterRegistry_Impl& SvNumberFormatter::GetFormatterRegistry()
{
    ::osl::MutexGuard aGuard( GetGlobalMutex() );
    if ( !pFormatterRegistry )
    {
        pFormatterRegistry = new SvNumberFormatterRegistry_Impl;
    }
    return *pFormatterRegistry;
}

void SvNumberFormatter::SetColorLink( const Link<sal_uInt16,Color*>& rColorTableCallBack )
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    aColorLink = rColorTableCallBack;
}

Color* SvNumberFormatter::GetUserDefColor(sal_uInt16 nIndex) const
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    if( aColorLink.IsSet() )
    {
        return aColorLink.Call(nIndex);
    }
    else
    {
        return nullptr;
    }
}

void SvNumberFormatter::ChangeNullDate(sal_uInt16 nDay,
                                       sal_uInt16 nMonth,
                                       sal_Int16 nYear)
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    SvNFEngine::ChangeNullDate(m_aCurrentLanguage, nDay, nMonth, nYear);
}

const Date& SvNFLanguageData::GetNullDate() const
{
    return pFormatScanner->GetNullDate();
}

void SvNFLanguageData::ChangeStandardPrec(short nPrec)
{
    pFormatScanner->ChangeStandardPrec(nPrec);
}

const Date& SvNumberFormatter::GetNullDate() const
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    return m_aCurrentLanguage.GetNullDate();
}

void SvNumberFormatter::ChangeStandardPrec(short nPrec)
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    m_aCurrentLanguage.ChangeStandardPrec(nPrec);
}

void SvNumberFormatter::SetNoZero(bool bNZ)
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    m_aFormatData.SetNoZero(bNZ);
}

sal_uInt16 SvNumberFormatter::GetStandardPrec() const
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    return m_aCurrentLanguage.pFormatScanner->GetStandardPrec();
}

bool SvNumberFormatter::GetNoZero() const
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    return m_aFormatData.GetNoZero();
}

void SvNumberFormatter::ReplaceSystemCL( LanguageType eOldLanguage )
{
    sal_uInt32 nCLOffset = m_aFormatData.ImpGetCLOffset( LANGUAGE_SYSTEM );
    if ( nCLOffset > m_aFormatData.MaxCLOffset )
    {
        return ;    // no SYSTEM entries to replace
    }
    const sal_uInt32 nMaxBuiltin = nCLOffset + SV_MAX_COUNT_STANDARD_FORMATS;
    const sal_uInt32 nNextCL = nCLOffset + SV_COUNTRY_LANGUAGE_OFFSET;
    sal_uInt32 nKey;

    // remove old builtin formats
    auto it = m_aFormatData.aFTable.find( nCLOffset );
    while ( it != m_aFormatData.aFTable.end() && (nKey = it->first) >= nCLOffset && nKey <= nMaxBuiltin )
    {
        it = m_aFormatData.aFTable.erase(it);
    }

    // move additional and user defined to temporary table
    SvNumberFormatTable aOldTable;
    while ( it != m_aFormatData.aFTable.end() && (nKey = it->first) >= nCLOffset && nKey < nNextCL )
    {
        aOldTable[ nKey ] = it->second.release();
        it = m_aFormatData.aFTable.erase(it);
    }

    // generate new old builtin formats
    // reset m_aCurrentLanguage.ActLnge otherwise ChangeIntl() wouldn't switch if already LANGUAGE_SYSTEM
    m_aCurrentLanguage.ActLnge = LANGUAGE_DONTKNOW;
    ChangeIntl( LANGUAGE_SYSTEM );
    m_aFormatData.ImpGenerateFormats(m_aCurrentLanguage, GetNatNum(), nCLOffset, true);

    // convert additional and user defined from old system to new system
    SvNumberformat* pStdFormat = m_aFormatData.GetFormatEntry( nCLOffset + ZF_STANDARD );
    sal_uInt32 nLastKey = nMaxBuiltin;
    m_aCurrentLanguage.pFormatScanner->SetConvertMode( eOldLanguage, LANGUAGE_SYSTEM, true , true);
    while ( !aOldTable.empty() )
    {
        nKey = aOldTable.begin()->first;
        if ( nLastKey < nKey )
        {
            nLastKey = nKey;
        }
        std::unique_ptr<SvNumberformat> pOldEntry(aOldTable.begin()->second);
        aOldTable.erase( nKey );
        OUString aString( pOldEntry->GetFormatstring() );

        // Same as PutEntry() but assures key position even if format code is
        // a duplicate. Also won't mix up any LastInsertKey.
        ChangeIntl( eOldLanguage );
        LanguageType eLge = eOldLanguage;   // ConvertMode changes this
        bool bCheck = false;
        sal_Int32 nCheckPos = -1;
        std::unique_ptr<SvNumberformat> pNewEntry(new SvNumberformat( aString, m_aCurrentLanguage.pFormatScanner.get(),
                                                                      m_aCurrentLanguage.pStringScanner.get(), GetNatNum(), nCheckPos, eLge ));
        if ( nCheckPos == 0 )
        {
            SvNumFormatType eCheckType = pNewEntry->GetType();
            if ( eCheckType != SvNumFormatType::UNDEFINED )
            {
                pNewEntry->SetType( eCheckType | SvNumFormatType::DEFINED );
            }
            else
            {
                pNewEntry->SetType( SvNumFormatType::DEFINED );
            }

            if ( m_aFormatData.aFTable.emplace( nKey, std::move(pNewEntry) ).second )
            {
                bCheck = true;
            }
        }
        DBG_ASSERT( bCheck, "SvNumberFormatter::ReplaceSystemCL: couldn't convert" );
    }
    m_aCurrentLanguage.pFormatScanner->SetConvertMode(false);
    pStdFormat->SetLastInsertKey( sal_uInt16(nLastKey - nCLOffset), SvNumberformat::FormatterPrivateAccess() );

    // append new system additional formats
    css::uno::Reference< css::i18n::XNumberFormatCode > xNFC = i18n::NumberFormatMapper::create( m_xContext );
    ImpGenerateAdditionalFormats( nCLOffset, xNFC );
}

const NativeNumberWrapper& SvNumberFormatter::GetNatNum() const { return m_xNatNum.get(); }

bool SvNFFormatData::IsTextFormat(sal_uInt32 F_Index) const
{
    const SvNumberformat* pFormat = GetFormatEntry(F_Index);
    return pFormat && pFormat->IsTextFormat();
}

bool SvNumberFormatter::IsTextFormat(sal_uInt32 F_Index) const
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    return m_aFormatData.IsTextFormat(F_Index);
}

bool SvNFFormatData::PutEntry(SvNFLanguageData& rCurrentLanguage,
                                 const NativeNumberWrapper& rNatNum,
                                 OUString& rString,
                                 sal_Int32& nCheckPos,
                                 SvNumFormatType& nType,
                                 sal_uInt32& nKey,      // format key
                                 LanguageType eLnge,
                                 bool bReplaceBooleanEquivalent)
{
    nKey = 0;
    if (rString.isEmpty())                             // empty string
    {
        nCheckPos = 1;                                  // -> Error
        return false;
    }
    eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
    rCurrentLanguage.ChangeIntl(eLnge);                 // change locale if necessary
    LanguageType eLge = eLnge;                          // non-const for ConvertMode
    bool bCheck = false;
    std::unique_ptr<SvNumberformat> p_Entry(new SvNumberformat(rString,
                                                               rCurrentLanguage.pFormatScanner.get(),
                                                               rCurrentLanguage.pStringScanner.get(),
                                                               rNatNum,
                                                               nCheckPos,
                                                               eLge,
                                                               bReplaceBooleanEquivalent));

    if (nCheckPos == 0)                         // Format ok
    {                                           // Type comparison:
        SvNumFormatType eCheckType = p_Entry->GetType();
        if ( eCheckType != SvNumFormatType::UNDEFINED)
        {
            p_Entry->SetType(eCheckType | SvNumFormatType::DEFINED);
            nType = eCheckType;
        }
        else
        {
            p_Entry->SetType(SvNumFormatType::DEFINED);
            nType = SvNumFormatType::DEFINED;
        }

        sal_uInt32 CLOffset = ImpGenerateCL(rCurrentLanguage, rNatNum, eLge);  // create new standard formats if necessary

        nKey = ImpIsEntry(p_Entry->GetFormatstring(),CLOffset, eLge);
        if (nKey == NUMBERFORMAT_ENTRY_NOT_FOUND) // only in not yet present
        {
            SvNumberformat* pStdFormat = GetFormatEntry(CLOffset + ZF_STANDARD);
            sal_uInt32 nPos = CLOffset + pStdFormat->GetLastInsertKey( SvNumberformat::FormatterPrivateAccess() );
            if (nPos+1 - CLOffset >= SV_COUNTRY_LANGUAGE_OFFSET)
            {
                SAL_WARN( "svl.numbers""SvNumberFormatter::PutEntry: too many formats for CL");
            }
            else if (!aFTable.emplace( nPos+1, std::move(p_Entry)).second)
            {
                SAL_WARN( "svl.numbers""SvNumberFormatter::PutEntry: dup position");
            }
            else
            {
                bCheck = true;
                nKey = nPos+1;
                pStdFormat->SetLastInsertKey(static_cast<sal_uInt16>(nKey-CLOffset), SvNumberformat::FormatterPrivateAccess());
            }
        }
    }
    return bCheck;
}

bool SvNumberFormatter::PutEntry(OUString& rString,
                                 sal_Int32& nCheckPos,
                                 SvNumFormatType& nType,
                                 sal_uInt32& nKey,      // format key
                                 LanguageType eLnge,
                                 bool bReplaceBooleanEquivalent)
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    return m_aFormatData.PutEntry(m_aCurrentLanguage, GetNatNum(), rString, nCheckPos, nType, nKey, eLnge, bReplaceBooleanEquivalent);
}

bool SvNumberFormatter::PutandConvertEntry(OUString& rString,
                                           sal_Int32& nCheckPos,
                                           SvNumFormatType& nType,
                                           sal_uInt32& nKey,
                                           LanguageType eLnge,
                                           LanguageType eNewLnge,
                                           bool bConvertDateOrder,
                                           bool bReplaceBooleanEquivalent )
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    bool bRes;
    if (eNewLnge == LANGUAGE_DONTKNOW)
    {
        eNewLnge = IniLnge;
    }
    m_aCurrentLanguage.pFormatScanner->SetConvertMode(eLnge, eNewLnge, false, bConvertDateOrder);
    bRes = PutEntry(rString, nCheckPos, nType, nKey, eLnge, bReplaceBooleanEquivalent);
    m_aCurrentLanguage.pFormatScanner->SetConvertMode(false);

    if (bReplaceBooleanEquivalent && nCheckPos == 0 && nType == SvNumFormatType::DEFINED
            && nKey != NUMBERFORMAT_ENTRY_NOT_FOUND)
    {
        // The boolean string formats are always "user defined" without any
        // other type.
        const SvNumberformat* pEntry = m_aFormatData.GetFormatEntry(nKey);
        if (pEntry && pEntry->GetType() == SvNumFormatType::DEFINED)
        {
            // Replace boolean string format with Boolean in target locale, in
            // case the source strings are the target locale's.
            const OUString aSaveString = rString;
            ChangeIntl(eNewLnge);
            if (m_aCurrentLanguage.pFormatScanner->ReplaceBooleanEquivalent( rString))
            {
                const sal_Int32 nSaveCheckPos = nCheckPos;
                const SvNumFormatType nSaveType = nType;
                const sal_uInt32 nSaveKey = nKey;
                const bool bTargetRes = PutEntry(rString, nCheckPos, nType, nKey, eNewLnge, false);
                if (nCheckPos == 0 && nType == SvNumFormatType::LOGICAL && nKey != NUMBERFORMAT_ENTRY_NOT_FOUND)
                {
                    bRes = bTargetRes;
                }
                else
                {
                    SAL_WARN("svl.numbers""SvNumberFormatter::PutandConvertEntry: can't scan boolean replacement");
                    // Live with the source boolean string format.
                    rString = aSaveString;
                    nCheckPos = nSaveCheckPos;
                    nType = nSaveType;
                    nKey = nSaveKey;
                }
            }
        }
    }
    return bRes;
}

bool SvNumberFormatter::PutandConvertEntrySystem(OUString& rString,
                                                 sal_Int32& nCheckPos,
                                                 SvNumFormatType& nType,
                                                 sal_uInt32& nKey,
                                                 LanguageType eLnge,
                                                 LanguageType eNewLnge)
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    bool bRes;
    if (eNewLnge == LANGUAGE_DONTKNOW)
    {
        eNewLnge = IniLnge;
    }
    m_aCurrentLanguage.pFormatScanner->SetConvertMode(eLnge, eNewLnge, truetrue);
    bRes = PutEntry(rString, nCheckPos, nType, nKey, eLnge);
    m_aCurrentLanguage.pFormatScanner->SetConvertMode(false);
    return bRes;
}

sal_uInt32 SvNumberFormatter::GetIndexPuttingAndConverting( OUString & rString, LanguageType eLnge,
                                                            LanguageType eSysLnge, SvNumFormatType & rType,
                                                            bool & rNewInserted, sal_Int32 & rCheckPos )
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    sal_uInt32 nKey = NUMBERFORMAT_ENTRY_NOT_FOUND;
    rNewInserted = false;
    rCheckPos = 0;

    // #62389# empty format string (of Writer) => General standard format
    if (rString.isEmpty())
    {
        // nothing
    }
    else if (eLnge == LANGUAGE_SYSTEM && eSysLnge != SvtSysLocale().GetLanguageTag().getLanguageType())
    {
        sal_uInt32 nOrig = GetEntryKey( rString, eSysLnge );
        if (nOrig == NUMBERFORMAT_ENTRY_NOT_FOUND)
        {
            nKey = nOrig;   // none available, maybe user-defined
        }
        else
        {
            nKey = GetFormatForLanguageIfBuiltIn( nOrig, SvtSysLocale().GetLanguageTag().getLanguageType() );
        }
        if (nKey == nOrig)
        {
            // Not a builtin format, convert.
            // The format code string may get modified and adapted to the real
            // language and wouldn't match eSysLnge anymore, do that on a copy.
            OUString aTmp( rString);
            rNewInserted = PutandConvertEntrySystem( aTmp, rCheckPos, rType,
                                                     nKey, eLnge, SvtSysLocale().GetLanguageTag().getLanguageType());
            if (rCheckPos > 0)
            {
                SAL_WARN( "svl.numbers""SvNumberFormatter::GetIndexPuttingAndConverting: bad format code string for current locale");
                nKey = NUMBERFORMAT_ENTRY_NOT_FOUND;
            }
        }
    }
    else
    {
        nKey = GetEntryKey( rString, eLnge);
        if (nKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
        {
            rNewInserted = PutEntry( rString, rCheckPos, rType, nKey, eLnge);
            if (rCheckPos > 0)
            {
                SAL_WARN( "svl.numbers""SvNumberFormatter::GetIndexPuttingAndConverting: bad format code string for specified locale");
                nKey = NUMBERFORMAT_ENTRY_NOT_FOUND;
            }
        }
    }
    if (nKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
    {
        nKey = GetStandardIndex( eLnge);
    }
    rType = GetType( nKey);
    // Convert any (!) old "automatic" currency format to new fixed currency
    // default format.
    if (rType & SvNumFormatType::CURRENCY)
    {
        const SvNumberformat* pFormat = GetEntry( nKey);
        if (!pFormat->HasNewCurrency())
        {
            if (rNewInserted)
            {
                DeleteEntry( nKey);     // don't leave trails of rubbish
                rNewInserted = false;
            }
            nKey = GetStandardFormat( SvNumFormatType::CURRENCY, eLnge);
        }
    }
    return nKey;
}

void SvNumberFormatter::DeleteEntry(sal_uInt32 nKey)
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    m_aFormatData.aFTable.erase(nKey);
}

void SvNumberFormatter::GetUsedLanguages( std::vector<LanguageType>& rList )
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    rList.clear();

    sal_uInt32 nOffset = 0;
    while (nOffset <= m_aFormatData.MaxCLOffset)
    {
        SvNumberformat* pFormat = m_aFormatData.GetFormatEntry(nOffset);
        if (pFormat)
        {
            rList.push_back( pFormat->GetLanguage() );
        }
        nOffset += SV_COUNTRY_LANGUAGE_OFFSET;
    }
}


void SvNumberFormatter::FillKeywordTable( NfKeywordTable& rKeywords,
                                          LanguageType eLang )
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    ChangeIntl( eLang );
    const NfKeywordTable & rTable = m_aCurrentLanguage.pFormatScanner->GetKeywords();
    for ( sal_uInt16 i = 0; i < NF_KEYWORD_ENTRIES_COUNT; ++i )
    {
        rKeywords[i] = rTable[i];
    }
}


void SvNumberFormatter::FillKeywordTableForExcel( NfKeywordTable& rKeywords )
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    FillKeywordTable( rKeywords, LANGUAGE_ENGLISH_US );

    // Replace upper case "GENERAL" with proper case "General".
    rKeywords[ NF_KEY_GENERAL ] = GetStandardName( LANGUAGE_ENGLISH_US );

    // Excel or OOXML do not specify format code keywords case sensitivity,
    // but given and writes them lower case. Using upper case even lead to an
    // odd misrepresentation in iOS viewer and OSX Quicklook viewer that
    // strangely use "D" and "DD" for "days since beginning of year", which is
    // nowhere defined. See tdf#126773
    // Use lower case for all date and time keywords where known. See OOXML
    // ECMA-376-1:2016 18.8.31 numFmts (Number Formats)
    rKeywords[ NF_KEY_MI ]    = "m";
    rKeywords[ NF_KEY_MMI ]   = "mm";
    rKeywords[ NF_KEY_M ]     = "m";
    rKeywords[ NF_KEY_MM ]    = "mm";
    rKeywords[ NF_KEY_MMM ]   = "mmm";
    rKeywords[ NF_KEY_MMMM ]  = "mmmm";
    rKeywords[ NF_KEY_MMMMM ] = "mmmmm";
    rKeywords[ NF_KEY_H ]     = "h";
    rKeywords[ NF_KEY_HH ]    = "hh";
    rKeywords[ NF_KEY_S ]     = "s";
    rKeywords[ NF_KEY_SS ]    = "ss";
    /* XXX: not defined in OOXML: rKeywords[ NF_KEY_Q ]     = "q"; */
    /* XXX: not defined in OOXML: rKeywords[ NF_KEY_QQ ]    = "qq"; */
    rKeywords[ NF_KEY_D ]     = "d";
    rKeywords[ NF_KEY_DD ]    = "dd";
    rKeywords[ NF_KEY_DDD ]   = "ddd";
    rKeywords[ NF_KEY_DDDD ]  = "dddd";
    rKeywords[ NF_KEY_YY ]    = "yy";
    rKeywords[ NF_KEY_YYYY ]  = "yyyy";
    /* XXX: not defined in OOXML: rKeywords[ NF_KEY_AAA ]   = "aaa"; */
    /* XXX: not defined in OOXML: rKeywords[ NF_KEY_AAAA ]  = "aaaa"; */
    rKeywords[ NF_KEY_EC ]    = "e";
    rKeywords[ NF_KEY_EEC ]   = "ee";
    rKeywords[ NF_KEY_G ]     = "g";
    rKeywords[ NF_KEY_GG ]    = "gg";
    rKeywords[ NF_KEY_GGG ]   = "ggg";
    rKeywords[ NF_KEY_R ]     = "r";
    rKeywords[ NF_KEY_RR ]    = "rr";
    /* XXX: not defined in OOXML: rKeywords[ NF_KEY_WW ]    = "ww"; */

    // Remap codes unknown to Excel.
    rKeywords[ NF_KEY_NN ] = "ddd";
    rKeywords[ NF_KEY_NNN ] = "dddd";
    // NNNN gets a separator appended in SvNumberformat::GetMappedFormatString()
    rKeywords[ NF_KEY_NNNN ] = "dddd";
    // Export the Thai T NatNum modifier. This must be uppercase for internal reasons.
    rKeywords[ NF_KEY_THAI_T ] = "T";
}

static OUString lcl_buildBooleanStringFormat(const SvNumberformat* pEntry, const NativeNumberWrapper& rNatNum, const SvNFLanguageData& rCurrentLang)
{
    // Build Boolean number format, which needs non-zero and zero subformat
    // codes with TRUE and FALSE strings.
    const Color* pColor = nullptr;
    OUString aFormatStr, aTemp;
    pEntry->GetOutputString( 1.0, aTemp, &pColor, rNatNum, rCurrentLang );
    aFormatStr += "\"" + aTemp + "\";\"" + aTemp + "\";\"";
    pEntry->GetOutputString( 0.0, aTemp, &pColor, rNatNum, rCurrentLang );
    aFormatStr += aTemp + "\"";
    return aFormatStr;
}

OUString SvNumberFormatter::GetFormatStringForExcel( sal_uInt32 nKey, const NfKeywordTable& rKeywords,
        SvNumberFormatter& rTempFormatter ) const
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    OUString aFormatStr;
    if (const SvNumberformat* pEntry = GetEntry( nKey))
    {
        if (pEntry->GetType() == SvNumFormatType::LOGICAL)
        {
            // Build a source locale dependent string boolean. This is
            // expected when loading the document in the same locale or if
            // several locales are used, but not for other system/default
            // locales. You can't have both. We could force to English for all
            // locales like below, but Excel would display English strings then
            // even for the system locale matching this locale. YMMV.
            aFormatStr = lcl_buildBooleanStringFormat(pEntry, GetNatNum(), m_aCurrentLanguage);
        }
        else
        {
            bool bIsLOK = comphelper::LibreOfficeKit::isActive();
            bool bSystemLanguage = false;
            LanguageType nLang = pEntry->GetLanguage();
            if (nLang == LANGUAGE_SYSTEM)
            {
                bSystemLanguage = true;
                nLang = SvtSysLocale().GetLanguageTag().getLanguageType();
            }
            if (!bIsLOK && nLang != LANGUAGE_ENGLISH_US)
            {
                sal_Int32 nCheckPos;
                SvNumFormatType nType = SvNumFormatType::DEFINED;
                sal_uInt32 nTempKey;
                OUString aTemp( pEntry->GetFormatstring());
                /* TODO: do we want bReplaceBooleanEquivalent=true in any case
                 * to write it as English string boolean? */

                rTempFormatter.PutandConvertEntry( aTemp, nCheckPos, nType, nTempKey, nLang, LANGUAGE_ENGLISH_US,
                        false /*bConvertDateOrder*/, false /*bReplaceBooleanEquivalent*/);
                SAL_WARN_IF( nCheckPos != 0, "svl.numbers",
                        "SvNumberFormatter::GetFormatStringForExcel - format code not convertible");
                if (nTempKey != NUMBERFORMAT_ENTRY_NOT_FOUND)
                    pEntry = rTempFormatter.GetEntry( nTempKey);
            }

            if (pEntry)
            {
                if (pEntry->GetType() == SvNumFormatType::LOGICAL)
                {
                    // This would be reached if bReplaceBooleanEquivalent was
                    // true and the source format is a string boolean like
                    // >"VRAI";"VRAI";"FAUX"< recognized as real boolean and
                    // properly converted. Then written as
                    // >"TRUE";"TRUE";"FALSE"<
                    aFormatStr = lcl_buildBooleanStringFormat(pEntry, GetNatNum(), m_aCurrentLanguage);
                }
                else
                {
                    // m_aCurrentLanguage.GetLocaleData() returns the current locale's data, so switch
                    // before (which doesn't do anything if it was the same locale
                    // already).
                    rTempFormatter.ChangeIntl( LANGUAGE_ENGLISH_US);
                    aFormatStr = pEntry->GetMappedFormatstring( rKeywords, *rTempFormatter.m_aCurrentLanguage.GetLocaleData(), nLang,
                            bSystemLanguage);
                }
            }
        }
    }
    else
    {
        SAL_WARN("svl.numbers","SvNumberFormatter::GetFormatStringForExcel - format not found: " << nKey);
    }

    if (aFormatStr.isEmpty())
        aFormatStr = "General";
    return aFormatStr;
}


OUString SvNumberFormatter::GetKeyword( LanguageType eLnge, sal_uInt16 nIndex )
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    ChangeIntl(eLnge);
    const NfKeywordTable & rTable = m_aCurrentLanguage.pFormatScanner->GetKeywords();
    if ( nIndex < NF_KEYWORD_ENTRIES_COUNT )
    {
        return rTable[nIndex];
    }
    SAL_WARN( "svl.numbers""GetKeyword: invalid index");
    return OUString();
}


OUString SvNumberFormatter::GetStandardName( LanguageType eLnge )
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    ChangeIntl( eLnge );
    return m_aCurrentLanguage.pFormatScanner->GetStandardName();
}

SvNFFormatData::SvNFFormatData()
    : MaxCLOffset(0)
    , nDefaultSystemCurrencyFormat(NUMBERFORMAT_ENTRY_NOT_FOUND)
    , bNoZero(false)
{
}

SvNFFormatData::~SvNFFormatData() = default;

sal_uInt32 SvNFFormatData::ImpGetCLOffset(LanguageType eLnge) const
{
    sal_uInt32 nOffset = 0;
    while (nOffset <= MaxCLOffset)
    {
        const SvNumberformat* pFormat = GetFormatEntry(nOffset);
        if (pFormat && pFormat->GetLanguage() == eLnge)
        {
            return nOffset;
        }
        nOffset += SV_COUNTRY_LANGUAGE_OFFSET;
    }
    return nOffset;
}

sal_uInt32 SvNFFormatData::ImpIsEntry(std::u16string_view rString,
                                      sal_uInt32 nCLOffset,
                                      LanguageType eLnge) const
{
    sal_uInt32 res = NUMBERFORMAT_ENTRY_NOT_FOUND;
    auto it = aFTable.find( nCLOffset);
    while ( res == NUMBERFORMAT_ENTRY_NOT_FOUND &&
            it != aFTable.end() && it->second->GetLanguage() == eLnge )
    {
        if ( rString == it->second->GetFormatstring() )
        {
            res = it->first;
        }
        else
        {
            ++it;
        }
    }
    return res;
}

sal_uInt32 SvNumberFormatter::ImpIsEntry(std::u16string_view rString,
                                         sal_uInt32 nCLOffset,
                                         LanguageType eLnge) const
{
    return m_aFormatData.ImpIsEntry(rString, nCLOffset, eLnge);
}

SvNumberFormatTable& SvNumberFormatter::GetFirstEntryTable(
                                                      SvNumFormatType& eType,
                                                      sal_uInt32& FIndex,
                                                      LanguageType& rLnge)
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    SvNumFormatType eTypetmp = eType;
    if (eType == SvNumFormatType::ALL)                  // empty cell or don't care
    {
        rLnge = IniLnge;
    }
    else
    {
        SvNumberformat* pFormat = m_aFormatData.GetFormatEntry(FIndex);
        if (!pFormat)
        {
            rLnge = IniLnge;
            eType = SvNumFormatType::ALL;
            eTypetmp = eType;
        }
        else
        {
            rLnge = pFormat->GetLanguage();
            eType = pFormat->GetMaskedType();
            if (eType == SvNumFormatType::ALL)
            {
                eType = SvNumFormatType::DEFINED;
                eTypetmp = eType;
            }
            else if (eType == SvNumFormatType::DATETIME)
            {
                eTypetmp = eType;
                eType = SvNumFormatType::DATE;
            }
            else
            {
                eTypetmp = eType;
            }
        }
    }
    ChangeIntl(rLnge);
    return GetEntryTable(eTypetmp, FIndex, rLnge);
}

sal_uInt32 SvNFFormatData::ImpGenerateCL(SvNFLanguageData& rCurrentLanguage, const NativeNumberWrapper& rNatNum, LanguageType eLnge)
{
    rCurrentLanguage.ChangeIntl(eLnge);
    sal_uInt32 CLOffset = ImpGetCLOffset(rCurrentLanguage.ActLnge);
    if (CLOffset > MaxCLOffset)
    {
        // new CL combination
        if (LocaleDataWrapper::areChecksEnabled())
        {
            const LanguageTag aLoadedLocale = rCurrentLanguage.xLocaleData->getLoadedLanguageTag();
            if ( !aLoadedLocale.equals( rCurrentLanguage.aLanguageTag ) )
            {
                LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->appendLocaleInfo( u"SvNumberFormatter::ImpGenerateCL: locales don't match:" ));
            }
            // test XML locale data FormatElement entries
            {
                uno::Sequence< i18n::FormatElement > xSeq = rCurrentLanguage.xLocaleData->getAllFormats();
                // A test for completeness of formatindex="0" ...
                // formatindex="47" is not needed here since it is done in
                // ImpGenerateFormats().

                // Test for dupes of formatindex="..."
                for ( sal_Int32 j = 0; j < xSeq.getLength(); j++ )
                {
                    sal_Int16 nIdx = xSeq[j].formatIndex;
                    OUStringBuffer aDupes;
                    for ( sal_Int32 i = 0; i < xSeq.getLength(); i++ )
                    {
                        if ( i != j && xSeq[i].formatIndex == nIdx )
                        {
                            aDupes.append( OUString::number(i) + "(" + xSeq[i].formatKey + ") ");
                        }
                    }
                    if ( !aDupes.isEmpty() )
                    {
                        OUString aMsg = "XML locale data FormatElement formatindex dupe: "
                                      + OUString::number(nIdx)
                                      + "\nFormatElements: "
                                      + OUString::number( j )
                                      + "("
                                      + xSeq[j].formatKey
                                      + ") "
                                      + aDupes;
                        LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->appendLocaleInfo( aMsg ));
                    }
                }
            }
        }

        MaxCLOffset += SV_COUNTRY_LANGUAGE_OFFSET;
        ImpGenerateFormats(rCurrentLanguage, rNatNum, MaxCLOffset, false/*bNoAdditionalFormats*/ );
        CLOffset = MaxCLOffset;
    }
    return CLOffset;
}

SvNumberFormatTable& SvNumberFormatter::ChangeCL(SvNumFormatType eType,
                                                 sal_uInt32& FIndex,
                                                 LanguageType eLnge)
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    m_aFormatData.ImpGenerateCL(m_aCurrentLanguage, GetNatNum(), eLnge);
    return GetEntryTable(eType, FIndex, m_aCurrentLanguage.ActLnge);
}

SvNumberFormatTable& SvNumberFormatter::GetEntryTable(
                                                    SvNumFormatType eType,
                                                    sal_uInt32& FIndex,
                                                    LanguageType eLnge)
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );
    if ( pFormatTable )
    {
        pFormatTable->clear();
    }
    else
    {
        pFormatTable.reset( new SvNumberFormatTable );
    }
    ChangeIntl(eLnge);
    sal_uInt32 CLOffset = m_aFormatData.ImpGetCLOffset(m_aCurrentLanguage.ActLnge);

    // Might generate and insert a default format for the given type
    // (e.g. currency) => has to be done before collecting formats.
    sal_uInt32 nDefaultIndex = GetStandardFormat( eType, m_aCurrentLanguage.ActLnge );

    auto it = m_aFormatData.aFTable.find( CLOffset);

    if (eType == SvNumFormatType::ALL)
    {
        while (it != m_aFormatData.aFTable.end() && it->second->GetLanguage() == m_aCurrentLanguage.ActLnge)
        {   // copy all entries to output table
            (*pFormatTable)[ it->first ] = it->second.get();
            ++it;
        }
    }
    else
    {
        while (it != m_aFormatData.aFTable.end() && it->second->GetLanguage() == m_aCurrentLanguage.ActLnge)
        {   // copy entries of queried type to output table
            if ((it->second->GetType()) & eType)
                (*pFormatTable)[ it->first ] = it->second.get();
            ++it;
        }
    }
    if ( !pFormatTable->empty() )
    {   // select default if queried format doesn't exist or queried type or
        // language differ from existing format
        SvNumberformat* pEntry = m_aFormatData.GetFormatEntry(FIndex);
        if ( !pEntry || !(pEntry->GetType() & eType) || pEntry->GetLanguage() != m_aCurrentLanguage.ActLnge )
        {
            FIndex = nDefaultIndex;
        }
    }
    return *pFormatTable;
}

namespace {

const SvNumberformat* ImpSubstituteEntry(SvNFLanguageData& rCurrentLanguage, const SvNFFormatData& rFormatData,
                                         const NativeNumberWrapper& rNatNum, const SvNFEngine::Accessor& rFuncs,
                                         const SvNumberformat* pFormat, sal_uInt32* o_pRealKey)
{
    if (!pFormat || !pFormat->IsSubstituted())
        return pFormat;

    // XXX NOTE: substitution can not be done in GetFormatEntry() as otherwise
    // to be substituted formats would "vanish", i.e. from the number formatter
    // dialog or when exporting to Excel.

    sal_uInt32 nKey;
    if (pFormat->IsSystemTimeFormat())
    {
        /* TODO: should we have NF_TIME_SYSTEM for consistency? */
        nKey = SvNFEngine::GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs,
                                             SvNumFormatType::TIME, LANGUAGE_SYSTEM);
    }
    else if (pFormat->IsSystemLongDateFormat())
        /* TODO: either that above, or have a long option for GetStandardFormat() */
    {
        nKey = SvNFEngine::GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum,
                                          NF_DATE_SYSTEM_LONG, LANGUAGE_SYSTEM);
    }
    else
        return pFormat;

    if (o_pRealKey)
        *o_pRealKey = nKey;
    return rFormatData.GetFormatEntry(nKey);
}

}

bool SvNFEngine::IsNumberFormat(SvNFLanguageData& rCurrentLanguage,
                                const SvNFFormatData& rFormatData,
                                const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
                                const OUString& sString,
                                sal_uInt32& F_Index,
                                double& fOutNumber,
                                SvNumInputOptions eInputOptions)
{
    SvNumFormatType FType;
    // For the 0 General format directly use the init/system locale and avoid
    // all overhead that is associated with a format passed to the scanner.
    const SvNumberformat* pFormat = (F_Index == 0 ? nullptr :
            ::ImpSubstituteEntry(rCurrentLanguage, rFormatData, rNatNum, rFuncs, rFormatData.GetFormatEntry(F_Index), nullptr));
    if (!pFormat)
    {
        rCurrentLanguage.ChangeIntl(rCurrentLanguage.IniLnge);
        FType = SvNumFormatType::NUMBER;
    }
    else
    {
        FType = pFormat->GetMaskedType();
        if (FType == SvNumFormatType::ALL)
        {
            FType = SvNumFormatType::DEFINED;
        }
        rCurrentLanguage.ChangeIntl(pFormat->GetLanguage());
        // Avoid scanner overhead with the General format of any locale.
        // These are never substituted above so safe to ignore.
        if ((F_Index % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
        {
            assert(FType == SvNumFormatType::NUMBER);
            pFormat = nullptr;
        }
    }

    bool res;
    SvNumFormatType RType = FType;
    if (RType == SvNumFormatType::TEXT)
    {
        res = false;        // type text preset => no conversion to number
    }
    else
    {
        res = rCurrentLanguage.pStringScanner->IsNumberFormat(sString, RType, fOutNumber, pFormat, rNatNum, eInputOptions);
    }
    if (res && !SvNumberFormatter::IsCompatible(FType, RType))     // non-matching type
    {
        switch ( RType )
        {
        case SvNumFormatType::DATE :
            // Preserve ISO 8601 input.
            if (rCurrentLanguage.pStringScanner->CanForceToIso8601( DateOrder::Invalid))
            {
                F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATE_DIN_YYYYMMDD, rCurrentLanguage.ActLnge );
            }
            else
            {
                F_Index = GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, RType, rCurrentLanguage.ActLnge);
            }
            break;
        case SvNumFormatType::TIME :
            if ( rCurrentLanguage.pStringScanner->GetDecPos() )
            {
                // 100th seconds
                if ( rCurrentLanguage.pStringScanner->GetNumericsCount() > 3 || fOutNumber < 0.0 )
                {
                    F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_TIME_HH_MMSS00, rCurrentLanguage.ActLnge);
                }
                else
                {
                    F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_TIME_MMSS00, rCurrentLanguage.ActLnge);
                }
            }
            else if ( fOutNumber >= 1.0 || fOutNumber < 0.0 )
            {
                F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_TIME_HH_MMSS, rCurrentLanguage.ActLnge);
            }
            else
            {
                F_Index = GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, RType, rCurrentLanguage.ActLnge );
            }
            break;
        case SvNumFormatType::DATETIME :
            // Preserve ISO 8601 input.
            if (rCurrentLanguage.pStringScanner->HasIso8601Tsep())
            {
                if (rCurrentLanguage.pStringScanner->GetDecPos())
                    F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDDTHHMMSS000, rCurrentLanguage.ActLnge);
                else
                    F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDDTHHMMSS, rCurrentLanguage.ActLnge);
            }
            else if (rCurrentLanguage.pStringScanner->CanForceToIso8601( DateOrder::Invalid))
            {
                if (rCurrentLanguage.pStringScanner->GetDecPos())
                    F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDD_HHMMSS000, rCurrentLanguage.ActLnge);
                else
                    F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDD_HHMMSS, rCurrentLanguage.ActLnge);
            }
            else
            {
                F_Index = GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, RType, rCurrentLanguage.ActLnge);
            }
            break;
        default:
            F_Index = GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, RType, rCurrentLanguage.ActLnge);
        }
    }
    return res;
}

bool SvNumberFormatter::IsNumberFormat(const OUString& sString,
                                       sal_uInt32& F_Index,
                                       double& fOutNumber,
                                       SvNumInputOptions eInputOptions)
{
    ::osl::MutexGuard aGuard( GetInstanceMutex() );

    return SvNFEngine::IsNumberFormat(m_aCurrentLanguage, m_aFormatData, GetNatNum(),
                                      m_aRWPolicy, sString, F_Index, fOutNumber,
                                      eInputOptions);
}

LanguageType SvNumberFormatter::GetLanguage() const
{
    return IniLnge; // immutable
}

// static
bool SvNumberFormatter::IsCompatible(SvNumFormatType eOldType, SvNumFormatType eNewType)
{
    if (eOldType == eNewType)
    {
        return true;
    }
    else if (eOldType == SvNumFormatType::DEFINED)
    {
        return true;
    }
    else
    {
        switch (eNewType)
        {
        case SvNumFormatType::NUMBER:
            switch (eOldType)
            {
            case SvNumFormatType::PERCENT:
            case SvNumFormatType::CURRENCY:
            case SvNumFormatType::SCIENTIFIC:
            case SvNumFormatType::FRACTION:
            case SvNumFormatType::DEFINED:
                return true;
            case SvNumFormatType::LOGICAL:
            default:
                return false;
            }
            break;
        case SvNumFormatType::DATE:
            switch (eOldType)
            {
            case SvNumFormatType::DATETIME:
                return true;
            default:
                return false;
            }
            break;
        case SvNumFormatType::TIME:
            switch (eOldType)
            {
            case SvNumFormatType::DATETIME:
                return true;
            default:
                return false;
            }
            break;
        case SvNumFormatType::DATETIME:
            switch (eOldType)
            {
            case SvNumFormatType::TIME:
            case SvNumFormatType::DATE:
                return true;
            default:
                return false;
            }
            break;
        case SvNumFormatType::DURATION:
            return false;
        default:
            return false;
        }
    }
}

static sal_uInt32 ImpGetSearchOffset(SvNumFormatType nType, sal_uInt32 CLOffset)
{
    sal_uInt32 nSearch;
    switch( nType )
    {
    case SvNumFormatType::DATE:
        nSearch = CLOffset + ZF_STANDARD_DATE;
        break;
    case SvNumFormatType::TIME:
        nSearch = CLOffset + ZF_STANDARD_TIME;
        break;
    case SvNumFormatType::DATETIME:
        nSearch = CLOffset + ZF_STANDARD_DATETIME;
        break;
    case SvNumFormatType::DURATION:
        nSearch = CLOffset + ZF_STANDARD_DURATION;
        break;
    case SvNumFormatType::PERCENT:
        nSearch = CLOffset + ZF_STANDARD_PERCENT;
        break;
    case SvNumFormatType::SCIENTIFIC:
        nSearch = CLOffset + ZF_STANDARD_SCIENTIFIC;
        break;
    default:
        nSearch = CLOffset + ZF_STANDARD;
    }
    return nSearch;
}

// static
sal_uInt32 SvNFEngine::ImpGetDefaultFormat(const SvNFFormatData& rFormatData,
                                           const SvNFEngine::Accessor& rFuncs,
                                           SvNumFormatType nType, sal_uInt32 CLOffset)
{
    sal_uInt32 nSearch = ImpGetSearchOffset(nType, CLOffset);

    sal_uInt32 nDefaultFormat = rFuncs.mFindFormat(nSearch);
    if (nDefaultFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
        return nDefaultFormat;

    nDefaultFormat = ImpGetDefaultFormat(rFormatData, nType, CLOffset);
    rFuncs.mCacheFormat(nSearch, nDefaultFormat);
    return nDefaultFormat;
}

namespace {

    sal_uInt32 ImpGetFormatIndex(NfIndexTableOffset nTabOff, sal_uInt32 nCLOffset)
    {
        if (nTabOff >= NF_INDEX_TABLE_ENTRIES)
            return NUMBERFORMAT_ENTRY_NOT_FOUND;

        if (indexTable[nTabOff] == NUMBERFORMAT_ENTRY_NOT_FOUND)
            return NUMBERFORMAT_ENTRY_NOT_FOUND;

        return nCLOffset + indexTable[nTabOff];
    }

    bool ImpIsSpecialStandardFormat(sal_uInt32 nFIndex, sal_uInt32 nCLOffset)
    {
        return
            nFIndex == ImpGetFormatIndex( NF_TIME_MMSS00, nCLOffset ) ||
            nFIndex == ImpGetFormatIndex( NF_TIME_HH_MMSS00, nCLOffset ) ||
            nFIndex == ImpGetFormatIndex( NF_TIME_HH_MMSS, nCLOffset )
            ;
    }

    bool ImpIsSpecialStandardFormat(SvNFLanguageData& rCurrentLanguage,
                                    const NativeNumberWrapper& rNatNum, const SvNFEngine::Accessor& rFuncs,
                                    sal_uInt32 nFIndex, LanguageType eLnge)
    {
        eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
        sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary (and allowed)
        return ::ImpIsSpecialStandardFormat(nFIndex, nCLOffset);
    }
}

sal_uInt32 SvNFEngine::ImpGetStandardFormat(SvNFLanguageData& rCurrentLanguage,
                                            const SvNFFormatData& rFormatData,
                                            const NativeNumberWrapper& rNatNum,
                                            const SvNFEngine::Accessor& rFuncs,
                                            SvNumFormatType eType,
                                            sal_uInt32 CLOffset,
                                            LanguageType eLnge)
{
    switch(eType)
    {
    case SvNumFormatType::CURRENCY:
        return rFuncs.mGetDefaultCurrency(rCurrentLanguage, rNatNum, CLOffset, eLnge);
    case SvNumFormatType::DURATION :
        return ImpGetFormatIndex(NF_TIME_HH_MMSS, CLOffset);
    case SvNumFormatType::DATE:
    case SvNumFormatType::TIME:
    case SvNumFormatType::DATETIME:
    case SvNumFormatType::PERCENT:
    case SvNumFormatType::SCIENTIFIC:
        return ImpGetDefaultFormat(rFormatData, rFuncs, eType, CLOffset);
    case SvNumFormatType::FRACTION:
        return CLOffset + ZF_STANDARD_FRACTION;
    case SvNumFormatType::LOGICAL:
        return CLOffset + ZF_STANDARD_LOGICAL;
    case SvNumFormatType::TEXT:
        return CLOffset + ZF_STANDARD_TEXT;
    case SvNumFormatType::ALL:
    case SvNumFormatType::DEFINED:
    case SvNumFormatType::NUMBER:
    case SvNumFormatType::UNDEFINED:
    default:
        return CLOffset + ZF_STANDARD;
    }
}

sal_uInt32 SvNFEngine::GetStandardFormat(SvNFLanguageData& rCurrentLanguage,
                                         const SvNFFormatData& rFormatData,
                                         const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
                                         SvNumFormatType eType,
                                         LanguageType eLnge)
{
    eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);

    sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary (and allowed)

    return ImpGetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, eType, nCLOffset, eLnge);
}

sal_uInt32 SvNumberFormatter::GetStandardFormat( SvNumFormatType eType, LanguageType eLnge )
{
    ::osl::MutexGuard aGuard(GetInstanceMutex());
    return SvNFEngine::GetStandardFormat(m_aCurrentLanguage, m_aFormatData, GetNatNum(), m_aRWPolicy, eType, eLnge);
}

bool SvNumberFormatter::ImpIsSpecialStandardFormat(sal_uInt32 nFIndex, LanguageType eLnge)
{
    ::osl::MutexGuard aGuard(GetInstanceMutex());
    return ::ImpIsSpecialStandardFormat(m_aCurrentLanguage, GetNatNum(), m_aRWPolicy, nFIndex, eLnge);
}

sal_uInt32 SvNFEngine::GetStandardFormat(SvNFLanguageData& rCurrentLanguage,
                                         const SvNFFormatData& rFormatData,
                                         const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
                                         sal_uInt32 nFIndex, SvNumFormatType eType, LanguageType eLnge)
{
    eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);

    sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary (and allowed)

    if (::ImpIsSpecialStandardFormat(nFIndex, nCLOffset))
        return nFIndex;

    return ImpGetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, eType, nCLOffset, eLnge);
}

namespace
{
    sal_uInt32 FindCachedDefaultFormat(const SvNFFormatData::DefaultFormatKeysMap& rDefaultFormatKeys, sal_uInt32 nSearch)
    {
        auto it = rDefaultFormatKeys.find(nSearch);
        sal_uInt32 nDefaultFormat = (it != rDefaultFormatKeys.end() ?
                                     it->second : NUMBERFORMAT_ENTRY_NOT_FOUND);
        return nDefaultFormat;
    }
}

sal_uInt32 SvNFFormatData::FindCachedDefaultFormat(sal_uInt32 nSearch) const
{
    return ::FindCachedDefaultFormat(aDefaultFormatKeys, nSearch);
}

// static
sal_uInt32 SvNFEngine::ImpGetDefaultFormat(const SvNFFormatData& rFormatData, SvNumFormatType nType, sal_uInt32 CLOffset)
{
    sal_uInt32 nDefaultFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;

    // look for a defined standard
    sal_uInt32 nStopKey = CLOffset + SV_COUNTRY_LANGUAGE_OFFSET;
    sal_uInt32 nKey(0);
    auto it2 = rFormatData.aFTable.find( CLOffset );
    while ( it2 != rFormatData.aFTable.end() && (nKey = it2->first ) >= CLOffset && nKey < nStopKey )
    {
        const SvNumberformat* pEntry = it2->second.get();
        if ( pEntry->IsStandard() && (pEntry->GetMaskedType() == nType) )
        {
            nDefaultFormat = nKey;
            break;  // while
        }
        ++it2;
    }

    if ( nDefaultFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
    {   // none found, use old fixed standards
        switch( nType )
        {
        case SvNumFormatType::DATE:
            nDefaultFormat = CLOffset + ZF_STANDARD_DATE;
            break;
        case SvNumFormatType::TIME:
            nDefaultFormat = CLOffset + ZF_STANDARD_TIME+1;
            break;
        case SvNumFormatType::DATETIME:
            nDefaultFormat = CLOffset + ZF_STANDARD_DATETIME;
            break;
        case SvNumFormatType::DURATION:
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=90 G=91

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.