basic/qa/basic_coverage/test_format_function.bas |    6 +++
 basic/qa/basic_coverage/test_str_method.bas      |   42 +++++++++++++++++++++++
 basic/qa/basic_coverage/test_typename_method.bas |    2 +
 basic/source/classes/image.cxx                   |    3 +
 basic/source/comp/scanner.cxx                    |    2 -
 basic/source/comp/symtbl.cxx                     |    5 ++
 basic/source/inc/filefmt.hxx                     |    2 +
 basic/source/runtime/methods.cxx                 |   19 ++++++++--
 basic/source/runtime/runtime.cxx                 |    4 +-
 basic/source/sbx/sbxscan.cxx                     |   10 ++++-
 10 files changed, 88 insertions(+), 7 deletions(-)

New commits:
commit 93a53e06649965b0697476d03fa9184439171e53
Author:     Mike Kaganski <[email protected]>
AuthorDate: Sat Sep 27 00:42:27 2025 +0500
Commit:     Xisco Fauli <[email protected]>
CommitDate: Tue Sep 30 09:33:40 2025 +0200

    tdf#168569: support date values in string pool
    
    Commit f45463d8e2bb0771ec1837d159ff98108b0047cf (tdf#93727 Support date
    literals in basic, 2017-05-24) introduced correct parsing of literals
    like #2025-09-26#. However, it didn't retain the value type; there was
    a discussion about that in gerrit, but no solution was found, and type
    was set to double.
    
    Later, a similar problem (storing type of value in compiled image) was
    fixed in commit 5eedb3beeaeed88de0d1ebd041a9f15ceea7e78c (tdf#142460:
    properly handle boolean values in string pool, 2021-06-25).
    
    This change reuses the same method to store date type using 'd' char in
    the string pool.
    
    Change-Id: I32e8497ece1f30980ba6d4fca248687b817348f2
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191555
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>
    (cherry picked from commit 22d7827c6695358e11ee06a5599b72a92ff0b2ac)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191595
    Reviewed-by: Xisco Fauli <[email protected]>
    Signed-off-by: Xisco Fauli <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191638

diff --git a/basic/qa/basic_coverage/test_format_function.bas 
b/basic/qa/basic_coverage/test_format_function.bas
index 75b7c37ee3bf..3a8ba67aa921 100644
--- a/basic/qa/basic_coverage/test_format_function.bas
+++ b/basic/qa/basic_coverage/test_format_function.bas
@@ -26,6 +26,7 @@ Sub verify_testFormat
     TestUtil.AssertEqual(Format(" "), " ", "Format("" "")")
     TestUtil.AssertEqual(Format(" 00 "), "0", "Format("" 00 "")")
     TestUtil.AssertEqual(Format(CDate("2025-09-26")), "09/26/2025", 
"Format(CDate(""2025-09-26""))")
+    TestUtil.AssertEqual(Format(#2025-09-26#), "09/26/2025", 
"Format(#2025-09-26#)")
 
     Exit Sub
 errorHandler:
diff --git a/basic/qa/basic_coverage/test_str_method.bas 
b/basic/qa/basic_coverage/test_str_method.bas
index 2ce10952ee04..9ce67ff1dec3 100644
--- a/basic/qa/basic_coverage/test_str_method.bas
+++ b/basic/qa/basic_coverage/test_str_method.bas
@@ -32,6 +32,7 @@ Sub verify_testStr
 
     ' Dates are converted into locale-dependent strings (test uses en-US)
     TestUtil.AssertEqualStrict(Str(CDate("2025-09-26")), "09/26/2025", 
"Str(CDate(""2025-09-26""))")
+    TestUtil.AssertEqualStrict(Str(#2025-09-26#), "09/26/2025", 
"Str(#2025-09-26#)")
 
     TestUtil.AssertEqualStrict(Str(true), "True", "Str(true)")
 
diff --git a/basic/qa/basic_coverage/test_typename_method.bas 
b/basic/qa/basic_coverage/test_typename_method.bas
index 028f57f0e8db..f6fb90c2df79 100644
--- a/basic/qa/basic_coverage/test_typename_method.bas
+++ b/basic/qa/basic_coverage/test_typename_method.bas
@@ -43,6 +43,8 @@ Function doUnitTest ' TypeName()
     assert( TypeName(myUDF) = "Object"  , "TypeName(myUDF) is not ""Object""" )
     assert( TypeName(var)   = "Empty"   , "TypeName(var) is not ""Empty""" )
 
+    assert( TypeName(#2025-09-26#) = "Date" , "TypeName(#2025-09-26#) is not 
""Date""" )
+
     assert( TypeName(int_)      = "Integer" , "TypeName(int_) is not 
""Integer""" )
     assert( TypeName(long_)     = "Long"    , "TypeName(long_) is not 
""Long""" )
     assert( TypeName(single_)   = "Single"  , "TypeName(single_) is not 
""Single""" )
diff --git a/basic/source/classes/image.cxx b/basic/source/classes/image.cxx
index 336666300fd2..19c0d518417f 100644
--- a/basic/source/classes/image.cxx
+++ b/basic/source/classes/image.cxx
@@ -615,6 +615,7 @@ void SbiImage::AddEnum(SbxObject* pObject) // Register enum 
type
     rEnums->Insert(pObject, rEnums->Count());
 }
 
+// See also: SbiRuntime::StepLOADNC
 // Note: IDs start with 1
 OUString SbiImage::GetString( sal_uInt32 nId, SbxDataType *eType ) const
 {
@@ -654,6 +655,8 @@ OUString SbiImage::GetString( sal_uInt32 nId, SbxDataType 
*eType ) const
                         case '@': *eType = SbxCURRENCY; break;
                         // tdf#142460 - properly handle boolean values in 
string pool
                         case 'b': *eType = SbxBOOL; break;
+                        // tdf#168569 - support date values in string pool
+                        case 'd': *eType = SbxDATE; break; // Not in 
GetSuffixType
                     }
                 }
             }
diff --git a/basic/source/comp/scanner.cxx b/basic/source/comp/scanner.cxx
index 820951889ad7..5aac70b833fe 100644
--- a/basic/source/comp/scanner.cxx
+++ b/basic/source/comp/scanner.cxx
@@ -648,7 +648,7 @@ bool SbiScanner::NextSym()
                 GenError( ERRCODE_BASIC_CONVERSION );
 
             bNumber = true;
-            eScanType = SbxDOUBLE;
+            eScanType = SbxDATE;
         }
         else
         {
diff --git a/basic/source/comp/symtbl.cxx b/basic/source/comp/symtbl.cxx
index 6f8b53ed0d5a..d28d5fde5e7c 100644
--- a/basic/source/comp/symtbl.cxx
+++ b/basic/source/comp/symtbl.cxx
@@ -100,6 +100,11 @@ short SbiStringPool::Add(double n, SbxDataType t)
             size = snprintf(buf, sizeof(buf), "%.16g", n) + 1;
             buf[size++] = '@';
             break;
+        case SbxDATE:
+            // tdf#168569 - support date values in string pool
+            size = snprintf(buf, sizeof(buf), "%.16g", n) + 1;
+            buf[size++] = 'd'; // Not in GetSuffixType
+            break;
         default: assert(false); break; // should not happen
     }
 
diff --git a/basic/source/inc/filefmt.hxx b/basic/source/inc/filefmt.hxx
index 38dfa95754f3..5f29d6cdf56d 100644
--- a/basic/source/inc/filefmt.hxx
+++ b/basic/source/inc/filefmt.hxx
@@ -43,6 +43,8 @@
 //             tdf#142460: properly handle boolean values in string pool (no
 //                       version number bump for backward compatibility; 
relies on
 //                       new integer type suffix 'b')
+//             tdf#168569: support date values in string pool (no version 
number bump
+//                       for backward compatibility; relies on new integer 
type suffix 'd')
 //
 
 #define B_IMG_VERSION_12 0x00000012
diff --git a/basic/source/runtime/runtime.cxx b/basic/source/runtime/runtime.cxx
index 2fcbfb7372cb..833e3af6a5a9 100644
--- a/basic/source/runtime/runtime.cxx
+++ b/basic/source/runtime/runtime.cxx
@@ -2817,7 +2817,7 @@ void SbiRuntime::StepERROR()
 }
 
 // loading a numeric constant (+ID)
-
+// See also: SbiImage::GetString
 void SbiRuntime::StepLOADNC( sal_uInt32 nOp1 )
 {
     // tdf#143707 - check if the data type character was added after the 
string termination symbol
@@ -2849,6 +2849,8 @@ void SbiRuntime::StepLOADNC( sal_uInt32 nOp1 )
             case '@': eType = SbxCURRENCY; break;
             // tdf#142460 - properly handle boolean values in string pool
             case 'b': eType = SbxBOOL; break;
+            // tdf#168569 - support date values in string pool
+            case 'd': eType = SbxDATE; break; // Not in GetSuffixType
         }
     }
     // tdf#143707 - if the data type character is different from the default 
value, it was added
commit ec8227e2225f56a4d9517cc9d69d593db7302d90
Author:     Mike Kaganski <[email protected]>
AuthorDate: Fri Sep 26 21:14:33 2025 +0500
Commit:     Xisco Fauli <[email protected]>
CommitDate: Tue Sep 30 09:33:33 2025 +0200

    tdf#168561: fix Str function implementation
    
    1. If a string is passed as argument, it is returned without any changes.
    For that, SbRtl_Str checks argument type, skipping processing for string.
    
    2. Non-negative numbers are preceded by a blank space.
    That already was implemented.
    
    3. Negative numbers are preceded by a minus sign.
    That was done incorrectly - a space was added for any number.
    
    4. Dates are converted into locale-dependent strings.
    That wasn't done at all - dates were converted to strings representing
    the serial date. A check is implemented SbxValue::Format, to handle this.
    
    Additionally, Format function was improved to handle such input without
    format string (taking into account that it handles strings that can be
    converted to numbers differently).
    
    Change-Id: I5ac0429950e4ea8bf69b0091502b4e6dc1f4957d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191549
    Reviewed-by: Mike Kaganski <[email protected]>
    Tested-by: Jenkins
    (cherry picked from commit 92878d3216aeaf0e5131c0c3fa1f8dc9ce67b5b4)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191582
    Reviewed-by: Xisco Fauli <[email protected]>
    Signed-off-by: Xisco Fauli <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191637

diff --git a/basic/qa/basic_coverage/test_format_function.bas 
b/basic/qa/basic_coverage/test_format_function.bas
index d1c51fe791a5..75b7c37ee3bf 100644
--- a/basic/qa/basic_coverage/test_format_function.bas
+++ b/basic/qa/basic_coverage/test_format_function.bas
@@ -22,6 +22,11 @@ Sub verify_testFormat
     TestUtil.AssertEqual(Format(d, "YYYY-MM-DD"), "2024-09-16", "Format(d, 
""YYYY-MM-DD"")")
     TestUtil.AssertEqual(Format("2024-09-16 05:03:30 PM", "hh-mm-ss"), 
"17-03-30", "Format(""2024-09-16 05:03:30 PM"", ""hh-mm-ss"")")
 
+    TestUtil.AssertEqual(Format(""), "", "Format("""")")
+    TestUtil.AssertEqual(Format(" "), " ", "Format("" "")")
+    TestUtil.AssertEqual(Format(" 00 "), "0", "Format("" 00 "")")
+    TestUtil.AssertEqual(Format(CDate("2025-09-26")), "09/26/2025", 
"Format(CDate(""2025-09-26""))")
+
     Exit Sub
 errorHandler:
     TestUtil.ReportErrorHandler("verify_testFormat", Err, Error$, Erl)
diff --git a/basic/qa/basic_coverage/test_str_method.bas 
b/basic/qa/basic_coverage/test_str_method.bas
new file mode 100644
index 000000000000..2ce10952ee04
--- /dev/null
+++ b/basic/qa/basic_coverage/test_str_method.bas
@@ -0,0 +1,41 @@
+' 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/.
+'
+
+Option Explicit
+
+Function doUnitTest as String
+    TestUtil.TestInit
+    verify_testStr
+    doUnitTest = TestUtil.GetResult()
+End Function
+
+Dim failedAssertion As Boolean, messages As String
+
+Sub verify_testStr
+    On Error GoTo errorHandler
+
+    ' If a string is passed as argument, it is returned without any changes
+    TestUtil.AssertEqualStrict(Str(""), "", "Str("""")")
+    TestUtil.AssertEqualStrict(Str(" "), " ", "Str("" "")")
+    TestUtil.AssertEqualStrict(Str(" 00 "), " 00 ", "Str("" 00 "")")
+
+    ' Non-negative numbers are preceded by a blank space
+    TestUtil.AssertEqualStrict(Str(0), " 0", "Str(0)")
+    TestUtil.AssertEqualStrict(Str(1 / 10), " 0.1", "Str(1 / 10)")
+
+    ' Negative numbers are preceded by a minus sign
+    TestUtil.AssertEqualStrict(Str(-1 / 10), "-0.1", "Str(-1 / 10)")
+
+    ' Dates are converted into locale-dependent strings (test uses en-US)
+    TestUtil.AssertEqualStrict(Str(CDate("2025-09-26")), "09/26/2025", 
"Str(CDate(""2025-09-26""))")
+
+    TestUtil.AssertEqualStrict(Str(true), "True", "Str(true)")
+
+    Exit Sub
+errorHandler:
+    TestUtil.ReportErrorHandler("verify_testStr", Err, Error$, Erl)
+End Sub
diff --git a/basic/source/runtime/methods.cxx b/basic/source/runtime/methods.cxx
index b486f76a50ed..0291de17d31c 100644
--- a/basic/source/runtime/methods.cxx
+++ b/basic/source/runtime/methods.cxx
@@ -1353,12 +1353,21 @@ void SbRtl_Str(StarBASIC *, SbxArray & rPar, bool)
     else
     {
         OUString aStr;
-        OUString aStrNew(u""_ustr);
+        OUString aStrNew;
         SbxVariableRef pArg = rPar.Get(1);
-        pArg->Format( aStr );
+        const SbxDataType argType = pArg->GetType();
+        if (argType == SbxSTRING)
+        {
+            // From Help: "If a string is passed as argument, it is returned 
without any changes"
+            aStr = pArg->GetOUString();
+        }
+        else
+        {
+            pArg->Format(aStr);
+        }
 
         // Numbers start with a space
-        if (pArg->GetType() != SbxBOOL && pArg->IsNumericRTL())
+        if (argType != SbxBOOL && argType != SbxSTRING && pArg->IsNumericRTL())
         {
             // replace commas by points so that it's symmetric to Val!
             aStr = aStr.replaceFirst( ",", "." );
@@ -1394,7 +1403,9 @@ void SbRtl_Str(StarBASIC *, SbxArray & rPar, bool)
             }
             else
             {
-                aStrNew = " " + aStr;
+                if (!aStr.startsWith("-"))
+                    aStrNew = " ";
+                aStrNew += aStr;
             }
         }
         else
diff --git a/basic/source/sbx/sbxscan.cxx b/basic/source/sbx/sbxscan.cxx
index a2e80161b16a..3d71d7c14ace 100644
--- a/basic/source/sbx/sbxscan.cxx
+++ b/basic/source/sbx/sbxscan.cxx
@@ -476,7 +476,10 @@ std::optional<double> StrToNumberIntl(const OUString& s,
                                       std::shared_ptr<SvNumberFormatter>& 
rpFormatter)
 {
     double ret;
-    if (SbxValue::ScanNumIntnl(s, ret) == ERRCODE_NONE)
+    sal_uInt16 nLen = 0;
+    bool bHasNumber = false;
+    if (ImpScan(s, ret, o3tl::temporary(SbxDataType()), &nLen, &bHasNumber, 
true) == ERRCODE_NONE
+        && bHasNumber && nLen == s.getLength())
         return ret;
 
     // We couldn't detect a Basic-formatted number (including type characters 
& specific exponents).
@@ -551,6 +554,11 @@ void SbxValue::Format( OUString& rRes, const OUString* 
pFmt ) const
         rRes = SbxBasicFormater::BasicFormatNull(pFmt ? *pFmt : 
std::u16string_view{});
         return;
     }
+    if (eType == SbxDATE && !pFmt)
+    {
+        rRes = GetOUString();
+        return;
+    }
 
     std::shared_ptr<SvNumberFormatter> pFormatter;
     std::optional<double> number = GetNumberIntl(*this, rRes, pFormatter, pFmt 
!= nullptr);

Reply via email to