how to keep the datetimeformat of excel.cell into dataset cell - c#

Having some hard time understanding the conversion between excel file to .net object type such as DataSet or DataTable.
One issue I see in this 3rd party library (and the issue still lingers in the latest I believe) called ExcelDataReader is its conversion of datetime format.
For example the excel file where user set custom format of dd/mm/yyyy gets converted in the result of dataSet as mm/dd/yyyy after importing the excel file to the object done.
I was thinking maybe to fix this, the library code needs to passively receive the excel cell format settings, not trying to convert with its own settings.
Wonder how in the code it can be done such that
if (excelfile.row[somenumber].cell.TypeSetting == typesetting.dateTime)
{
dataset.cell[somenumber].dateTimeFormatSetting = excelfile.row[somenumber].cell.dateTimeFormatSetting
}
probably this code isn't close to be realistic, just a wild guess of what needs to be done.

ExcelDataReader loses Excel's formatting information when using AsDataSet(), which basically gives you only the raw values.
F.ex a raw date object alone does not know whether it should be displayed as 'mm-dd-yy' or 'dd/mm/yy'. For this you need the "number format string", which is ignored by AsDataSet().
To work around this, instead of calling AsDataSet(), you need to manually loop over the rows/columns using ExcelDataReader's lower level reader APIs, and using the third party library ExcelNumberFormat to format the values like Excel.
This relevant snippet from their readme shows how to format a single value from the reader object:
string GetFormattedValue(IExcelDataReader reader, int columnIndex, CultureInfo culture)
{
var value = reader.GetValue(columnIndex);
var formatString = reader.GetNumberFormatString(columnIndex);
if (formatString != null)
{
var format = new NumberFormat(formatString);
return format.Format(value, culture);
}
return Convert.ToString(value, culture);
}

Related

Setting cell using excel introp changes date

I'm trying to enter a date value into an excel file.
For e.g.
"05/07/19" (July 5th of 2019) and the date formate of the cell in the excel is set to "dd-MM-yy" (regional setting of the computer is also the same). But when my code enters the value into excel, value is getting changed to "07-05-19" (May 7th of 2019).
I want it to stay as "05-07-19" and also excel should consider it as a date.
I have tried different ways to set the value into an excel,
const string DATE = "05/07/19";
Application app = new Application();
Workbook workBook = app.Application.Workbooks.Open(FILE_PATH);
Worksheet sheet = workBook.Sheets[1];
Range range = sheet.get_Range("A1");
range.set_Value(XlRangeValueDataType.xlRangeValueDefault, DATE);
Range range2 = app.get_Range("A1", "A2");
range2.Cells[2, 1] = DATE;
Range range3 = app.get_Range("A3", "A3");
range3.Value = DATE;
Range range4 = app.get_Range("A4", "A4");
//Of course, It is not setting the value as a Date but text.
range4.Value = new string[] { DATE };
I want my code to behave as per the formate of the cell. If the input can be fit as a date (based on the cell formate for date), it should be considered as a date, else just a text. (The way excel behaves when the user manually input the data)
UPDATE:
So far I got to know that runtime version of excel interop (File version: 15.0.4420.1017) is v2.0 and If I run my code with target framework .Net 3.5, it is working as expected.
But the same code is having the problem if I change the target framework to .Net 4.6
Do we know, if there may any compatible interop available for .Net 4.6?
Please set the number format for the relevant cells.
As an example, do like this:
range2.NumberFormat = "DD/MM/YYYY";
Ideally excel reacts on values based on default settings and if we wish to retain our own formatting while passing values we have to prepare our destination first then put value.
I would say the code which should work as:
range2.Numberformat="dd-mm-yy";
range2.Value=DATE;

How can I import CSV file with fraction values avoid fraction as date format

I need to write code for import CSV file I read file using stream reader. There is no problem in coding it's working good. But I have problem in CSV file where I need to import CSV from web in that lot of fraction numbers are there if no is like 3/4 after I read file value is 3/4/19 as date format. What could I do to avoid this issue?
Due to directly importing file from web I can't open and format each and every cell.
Note : there is multiple combination of values like decimal fraction and also string for names
I recommend you to use a library to achieve this. CsvHelper is a .NET library for reading and writing CSV files. Extremely fast, flexible, and easy to use. It's also RFC 4180 Compliant Adheres to the RFC 4180 standard to ensure compatibility across systems.
One of the good features of this library, that you can map a CSV file to C# class.
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}
void Main()
{
using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader))
{
var records = csv.GetRecords<Foo>();
}
}
Sample CSV:
Id,Name
1,one
It supports many other ways to read, I recommend to read it's website and decimations.
You can use CSVHelper as mention in another answer as well. As I read your question I understand the problem is multiple format is passed for datetime.
If this is the issue, I believe you should try parsing it using multiple format, for example one mentioned in this answer https://stackoverflow.com/a/17859959/713789
You might need to write a mapper to do this or just simply write your code to read the date as in format you want. This must fix your issue.

how to convert exponential string to normal string in c#

I am reading a column number from csv where the number is 123456791234567 however due to format it gets converted to
number="1.23457E+14" in the csv file.
is there any way where we can change it to original string using c# ?
I am trying with below code :
decimal number = Decimal.Parse(number,System.Globalization.NumberStyles.Any);
but the number i am getting is 123457000000000M and the actual number is 123456791234567
any idea on this?
If I understand correctly, excel is converting 123456791234567 to 1.23457E+14. In that case, you just need to format the excel cell (or potentially the column) containing the value to string, before you set the value.
If the C# program opens the csv to find the value to be 1.23457E+14, then there is no way for you to convert it back to 123456791234567, since the precision is already lost - unless of course the same value exists (or can be recreated) in other cells (columns)

C# EPPLUS excel array formula workarounds

The current version of EPPLUS support the creation of excel formulas but NOT excel array formulas, despite having the CreateFormulaArray() method.
When using the CreateFormulaArray() method, the correct formula string will appear on the excel formula editor. However, the formula does not actually execute on the sheet.
I was wondering if anyone knew of any clever workaround to this without having to use Microsoft.Office.Interop
My code is:
using (ExcelPackage pck = new ExcelPackage(newFile))
{
pck.Workbook.Worksheets.Add("Summary");
pck.Workbook.Worksheets.MoveToStart("Summary");
var summaryWS = pck.Workbook.Worksheets[1];
summaryWS.Cells["C2"].Value = 2;
summaryWS.Cells["C3"].Value = 3;
summaryWS.Cells["C4"].Value = 8;
summaryWS.Cells["A1"].CreateArrayFormula("STDEV.P($C$2:$C$4)*SQRT(8*260)");
}
my output in excel would be #NAME?
The formula editor would show {=STDEV.P($C$2:$C$4)*SQRT(8*260)}
Seems Excel is misinterpreting the function name STDEV.P which is the newer version of STDEVP. If you look at the XML output AFTER opening and saving with excel the wb EPPlus generates you will see it says _xludf.STDEV.P which means it thinks it is user-defined.
You can do one of two things. You could use the old version of the function:
summaryWS.Cells["A1"].CreateArrayFormula("STDEVP($C$2:$C$4)*SQRT(8*260)");
which is probably less then ideal since you always want to stick with the latest version. In that case, force excel to recognize the function like this:
summaryWS.Cells["A1"].CreateArrayFormula("_xlfn.STDEV.P($C$2:$C$4)*SQRT(8*260)");

Paste from Excel into C# app, retaining full precision

I have data in an Excel spreadsheet with values like this:
0.69491375
0.31220394
The cells are formatted as Percentage, and set to display two decimal places. So they appear in Excel as:
69.49%
31.22%
I have a C# program that parses this data off the Clipboard.
var dataObj = Clipboard.GetDataObject();
var format = DataFormats.CommaSeparatedValue;
if (dataObj != null && dataObj.GetDataPresent(format))
{
var csvData = dataObj.GetData(format);
// do something
}
The problem is that csvData contains the display values from Excel, i.e. '69.49%' and '31.22%'. It does not contain the full precision of the extra decimal places.
I have tried using the various different DataFormats values, but the data only ever contains the display value from Excel, e.g.:
DataFormats.Dif
DataFormats.Rtf
DataFormats.UnicodeText
etc.
As a test, I installed LibreOffice Calc and copy/pasted the same cells from Excel into Calc. Calc retains the full precision of the raw data.
So clearly Excel puts this data somewhere that other programs can access. How can I access it from my C# application?
Edit - Next steps.
I've downloaded the LibreOffice Calc source code and will have a poke around to see if I can find out how they get the full context of the copied data from Excel.
I also did a GetFormats() call on the data object returned from the clipboard and got a list of 24 different data formats, some of which are not in the DataFormats enum. These include formats like Biff12, Biff8, Biff5, Format129 among other formats that are unfamiliar to me, so I'll investigate these and respond if I make any discoveries...
Also not a complete answer either, but some further insights into the problem:
When you copy a single Excel cell then what will end up in the clipboard is a complete Excel workbook which contains a single spreadsheet which in turn contains a single cell:
var dataObject = Clipboard.GetDataObject();
var mstream = (MemoryStream)dataObject.GetData("XML Spreadsheet");
// Note: For some reason we need to ignore the last byte otherwise
// an exception will occur...
mstream.SetLength(mstream.Length - 1);
var xml = XElement.Load(mstream);
Now, when you dump the content of the XElement to the console you can see that you indeed get a complete Excel Workbook. Also the "XML Spreadsheet" format contains the internal representation of the numbers stored in the cell. So I guess you could use Linq-To-Xml or similar to fetch the data you need:
XNamespace ssNs = "urn:schemas-microsoft-com:office:spreadsheet";
var numbers = xml.Descendants(ssNs + "Data").
Where(e => (string)e.Attribute(ssNs + "Type") == "Number").
Select(e => (double)e);
I've also tried to read the Biff formats using the Excel Data Reader however the resulting DataSets always came out empty...
The BIFF formats are an open specification by Microsoft. (Note, that I say specification not standard). Give a read to this to get an idea of what is going on.
Then those BIFF you see correspond to the some Excel formats. BIFF5 is XLS from Excel 5.0 and 95, BIFF8 is XLS from Excel 97 to 2003, BIFF12 is XLSB from Excel 2003, note that Excel 2007 can also produce them (I guess Excel 2010 too). There is some documentation here and also here (From OpenOffice) that may help you make sense of the binary there...
Anyways, there is some work has been done in past to parse this documents in C++, Java, VB and for your taste in C#. For example this BIFF12 Reader, the project NExcel, and ExcelLibrary to cite a few.
In particular NExcel will let you pass an stream which you can create from the clipboard data and then query NExcel to get the data. If you are going to take the source code then I think ExcelLibrary is much more readable.
You can get the stream like this:
var dataobject = System.Windows.Forms.Clipboard.GetDataObject();
var stream = (System.IO.Stream)dataobject.GetData(format);
And read form the stream with NExcel would be something like this:
var wb = getWorkbook(stream);
var sheet = wb.Sheets[0];
var somedata = sheet.getCell(0, 0).Contents;
I guess the actual Office libraries from Microsoft would work too.
I know this is not the whole tale, please share how is it going. Will try it if I get a chance.

Categories