@@ -26,6 +26,7 @@ static ConversionResult Convert(Stream stream, IReadOnlyDictionary<string, objec
2626 static ConversionResult Convert ( SpreadsheetDocument document , IReadOnlyDictionary < string , object > settings )
2727 {
2828 var sheets = Convert ( document ) . ToList ( ) ;
29+
2930 var info = new Info
3031 {
3132 SheetNames = sheets . Select ( _ => _ . Name ! ) ,
@@ -44,20 +45,21 @@ static ConversionResult Convert(SpreadsheetDocument document, IReadOnlyDictionar
4445 static IEnumerable < ( StringBuilder Csv , string ? Name ) > Convert ( SpreadsheetDocument document )
4546 {
4647 var workbookPart = document . WorkbookPart ! ;
48+
4749 foreach ( var sheet in workbookPart . Workbook . Sheets ! . Elements < Sheet > ( ) )
4850 {
49- var worksheetPart = ( WorksheetPart ) workbookPart . GetPartById ( sheet . Id ! ) ;
50-
51- // Get shared string table for text values
52- var sharedStringPart = workbookPart . SharedStringTablePart ;
51+ var worksheetPart = ( WorksheetPart ) workbookPart . GetPartById ( sheet . Id ! ) ;
5352
53+ var sharedStringItems = workbookPart . SharedStringTablePart ? . SharedStringTable . Elements < SharedStringItem > ( ) . ToList ( ) ;
5454 var builder = new StringBuilder ( ) ;
5555
56- foreach ( var row in worksheetPart . Worksheet . Descendants < Row > ( ) . OrderBy ( r => r . RowIndex ) )
56+ foreach ( var row in worksheetPart . Worksheet
57+ . Descendants < Row > ( )
58+ . OrderBy ( r => r . RowIndex ) )
5759 {
5860 foreach ( var cell in row . Elements < Cell > ( ) )
5961 {
60- var cellValue = GetCellValue ( cell , sharedStringPart ) ;
62+ var cellValue = GetCellValue ( cell , workbookPart , sharedStringItems ) ;
6163 builder . Append ( EscapeCsvValue ( cellValue ) ) ;
6264 builder . Append ( ',' ) ;
6365 }
@@ -70,7 +72,7 @@ static ConversionResult Convert(SpreadsheetDocument document, IReadOnlyDictionar
7072 }
7173 }
7274
73- private static string GetCellValue ( Cell cell , SharedStringTablePart ? sharedStringPart )
75+ static string GetCellValue ( Cell cell , WorkbookPart workbookPart , List < SharedStringItem > ? sharedStringItems )
7476 {
7577 var value = cell . InnerText ;
7678
@@ -79,64 +81,103 @@ private static string GetCellValue(Cell cell, SharedStringTablePart? sharedStrin
7981 if ( cell . DataType . Value == CellValues . SharedString )
8082 {
8183 // Handle shared strings
82- if ( sharedStringPart != null && int . TryParse ( value , out var ssid ) )
84+ if ( sharedStringItems != null &&
85+ int . TryParse ( value , out var ssid ) )
8386 {
84- return sharedStringPart . SharedStringTable . Elements < SharedStringItem > ( ) . ElementAt ( ssid ) . InnerText ;
87+ return sharedStringItems . ElementAt ( ssid ) . InnerText ;
8588 }
8689 }
8790 else if ( cell . DataType . Value == CellValues . Boolean )
8891 {
89- return value == "1" ? "TRUE " : "FALSE " ;
92+ return value == "1" ? "true " : "false " ;
9093 }
9194 else if ( cell . DataType . Value == CellValues . Date )
9295 {
9396 if ( double . TryParse ( value , out var oaDate ) )
9497 {
95- return DateTime . FromOADate ( oaDate ) . ToString ( "yyyy-MM-dd" ) ;
98+ var date = DateTime . FromOADate ( oaDate ) ;
99+ return DateFormatter . Convert ( date ) ;
100+ }
101+ }
102+ }
103+ else if ( ! string . IsNullOrEmpty ( value ) )
104+ {
105+ // Check if this is a date based on number format
106+ if ( double . TryParse ( value , out var numericValue ) )
107+ {
108+ if ( IsCellDateFormatted ( cell , workbookPart ) )
109+ {
110+ try
111+ {
112+ var date = DateTime . FromOADate ( numericValue ) ;
113+ return DateFormatter . Convert ( date ) ;
114+ }
115+ catch ( ArgumentException )
116+ {
117+ // If conversion fails, return the original numeric value
118+ return value ;
119+ }
96120 }
97121 }
98122 }
99123
100124 return value ;
101125 }
102126
103- static uint GetColumnIndex ( string cellReference )
127+ static bool IsCellDateFormatted ( Cell cell , WorkbookPart workbookPart )
104128 {
105- if ( string . IsNullOrEmpty ( cellReference ) )
106- return 0 ;
129+ if ( cell . StyleIndex == null )
130+ {
131+ return false ;
132+ }
107133
108- // Extract column letters from cell reference (e.g., "A1" -> "A")
109- var columnName = new string ( cellReference . Where ( char . IsLetter ) . ToArray ( ) ) ;
110- return GetColumnIndex2 ( columnName ) ;
111- }
134+ var stylesPart = workbookPart . WorkbookStylesPart ;
135+ if ( stylesPart ? . Stylesheet . CellFormats == null )
136+ {
137+ return false ;
138+ }
112139
113- private static uint GetColumnIndex2 ( string columnName )
114- {
115- uint columnIndex = 0 ;
116- for ( var i = 0 ; i < columnName . Length ; i ++ )
140+ var cellFormats = stylesPart . Stylesheet . CellFormats ;
141+ var cellFormat = cellFormats . Elements < CellFormat > ( ) . ElementAtOrDefault ( ( int ) cell . StyleIndex . Value ) ;
142+
143+ if ( cellFormat ? . NumberFormatId == null )
117144 {
118- columnIndex = columnIndex * 26 + ( uint ) ( columnName [ i ] - 'A' + 1 ) ;
145+ return false ;
119146 }
120- return columnIndex ;
121- }
122147
123- private static string GetColumnName ( uint columnIndex )
124- {
125- var columnName = "" ;
126- while ( columnIndex > 0 )
148+ var numberFormatId = cellFormat . NumberFormatId . Value ;
149+
150+ // Built-in date formats (14-22, 176-180, 181-183)
151+ if ( numberFormatId is
152+ >= 14 and <= 22 or
153+ >= 176 and <= 180 or
154+ >= 181 and <= 183 )
127155 {
128- columnIndex -- ;
129- columnName = ( char ) ( 'A' + columnIndex % 26 ) + columnName ;
130- columnIndex /= 26 ;
156+ return true ;
131157 }
132- return columnName ;
158+
159+ // Check custom number formats
160+ var numberingFormats = stylesPart . Stylesheet . NumberingFormats ;
161+ if ( numberingFormats != null )
162+ {
163+ var numberFormat = numberingFormats . Elements < NumberingFormat > ( )
164+ . FirstOrDefault ( nf => nf . NumberFormatId != null && nf . NumberFormatId == numberFormatId ) ;
165+
166+ if ( numberFormat ? . FormatCode != null )
167+ {
168+ var formatCode = numberFormat . FormatCode . Value ! . ToLower ( ) ;
169+ // Look for common date format indicators
170+ return formatCode . Contains ( "yyyy" ) || formatCode . Contains ( "mm" ) ||
171+ formatCode . Contains ( "dd" ) || formatCode . Contains ( 'h' ) ||
172+ formatCode . Contains ( "m/d" ) || formatCode . Contains ( "d/m" ) ;
173+ }
174+ }
175+
176+ return false ;
133177 }
134178
135- private static string EscapeCsvValue ( string value )
179+ static string EscapeCsvValue ( string value )
136180 {
137- if ( string . IsNullOrEmpty ( value ) )
138- return "" ;
139-
140181 // Escape CSV special characters
141182 if ( value . Contains ( ',' ) ||
142183 value . Contains ( '"' ) ||
0 commit comments