Convert time-formatted column in excel to C# DateTime - c#

I am currently using the Excel C# libraries (Microsoft.Office.Interop.Excel) to read an excel spreadsheet into my C# application.
I initially tried to read all the cells as their raw data, but found that Date-formatted cells were giving me a 5-digit integer, and time-formatted cells were returning a decimal. So I then found out that you can use a date-conversion method built into Excel's C# library, like so:
DateTime excelDate = (DateTime)ExcelCalcValue.ExcelDateToDateTime(workbook, Double.Parse(cell.Value.ToString()));
output = excelDate.ToString("yyyy-MM-dd HH:mm");
Through debugging my application with various test sheets, I have been able to record the various format strings that cells return when they are formatted in different ways. These are below:
(WorksheetCell.CellFormat.FormatString)
Times
[$-F400]h:mm:ss\\ AM/PM
hh:mm:ss;#
h:mm:ss;#
[$-409]hh:mm:ss\\ AM/PM;#
[$-409]h:mm:ss\\ AM/PM;#
Dates
m/d/yy
[$-F800]dddd\\,\\ mmmm\\ dd\\,\\ yyyy
dd/mm/yyyy;#
dd/mm/yy;#
d/m/yy;#
d\\.m\\.yy;#
yyyy\\-mm\\-dd;#
[$-809]dd\\ mmmm\\ yyyy;#
[$-809]d\\ mmmm\\ yyyy;#
Using these, I can now reliably determine the formatting style of a cell in excel. Using the earlier code, I can detect a date-formatted cell and return the proper data in DateTime format. However, I cannot see an equivalent function for converting time-formatted cells.
I get a result of 0.58368055555555554 when I read a cell time-formatted as [$-F400]h:mm:ss\\ AM/PM. I have absolutely no idea how to convert this into a DateTime, or indeed what this float represents.
Can anyone suggest a method of converting time-formatted excel cells (which are stored as a strange float) into the correct DateTime variable?

As FrankPI said, use DateTime.FromOADate(). You would use this function with the raw data from an Excel cell - there is no need to parse the format.
Excel encodes its dates and times in a double. The integral portion represents the days after January 1, 1900. The fraction part represents the time since midnight of the day referenced. For example:
1.5 is January 1, 1900 # Noon
and
41507.25 = August 21, 2013 # 6:00 am
Refer to the MSDN docs on this function for more information:
http://msdn.microsoft.com/en-us/library/system.datetime.fromoadate.aspx

The "strange float" can probably be converted too a DateTime via the DateTime.FromOADate() method. Actually, it is the number of days since January, 1, 1900 with the time as fractions, e. g. 0.04236 = 1/24 + 1/(24 * 60) for 1:01 am.

I wrote this function to handle a date input from Excel into C#. It handles a number of data type possibilities for a date cell:
/// <summary>
/// Returns DateTime?
/// Excel dates are double values, and sometimes, they're typical dd/mm/yyyy dates.
/// This function handles both possibilities, and the possibility of a blank date input.
/// ///
/// </summary>
/// <param name="inputDate"></param>
/// <returns></returns>
private static DateTime? ResolveExcelDateInput(string inputDate)
{
double incomingDate = 0;
DateTime incomingDateDate = new DateTime();
// If the incoming date is a double type, parse it into DateTime
if (Double.TryParse(inputDate, out incomingDate))
{
return DateTime.FromOADate(incomingDate);
}
// IF the incoming date value is a date type, parse it.
var parseDateResult = DateTime.TryParse(inputDate, out incomingDateDate);
if(parseDateResult)
{
// If the parse is successful return the date
return incomingDateDate;
}
else
{
// If the parse isn't successful; check if this a blank value and set to a default value.
if(string.IsNullOrWhiteSpace(inputDate))
{
return new DateTime(1901, 1, 1);
}
else
{
// Otherwise return null value so that is then handled by the validation logic.
// log a validation result because inputDate is likely an invalid string value that has nothing to do with dates.
return null;
}
}
}

If you want that either date value is in double format or in date format it converts it to date format then try to use following code. datestringvalue should be your input value.
DateTime dateNow = DateTime.Now;
DateTime formatedDate = DateTime.TryParse("datestringvalue", out dateNow) ? Convert.ToDateTime("datestringvalue") : DateTime.FromOADate(Convert.ToDouble("datestringvalue"));

Related

Formatting Date and Time with a function in C#

So I am getting the driver date from graphic card and display it into a TextBox but the value comes like this 20161216000000.000000-000 and I want to convert it into a real date.
I already got a function to convert this kind of dates, but it this case does does not work and after using it shows me like this 01-01-0001 00:00:00.
This is my function code:
private static string FormatDateTime(object ObjInstallDate)
{
object FinalDate = DBNull.Value;
string strDate = Convert.ToString(ObjInstallDate);
DateTime dtm;
DateTime.TryParseExact(strDate, new string[] { "yyyyMMdd", "yyyy-MM-dd", "dd-MM-yyyy" },
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.None, out dtm);
if (!String.IsNullOrEmpty(strDate))
{
FinalDate = dtm;
}
return FinalDate.ToString();
}
Do you have any idea how I can get in this case 20161216000000.000000-000 something like 2016-12-16?
Taking a substring does the job (if the format is always like shown):
DateTime.TryParseExact(strDate.Substring(0, 8), "yyyyMMdd",
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.None, out DateTime dtm);
To get the required format to present the result you can use
dtm.ToString("yyyy-MM-dd");
After looking at your code and question it looks like the date you are passing to the function is not in correct/expected format that c# supports, that's why it is giving you the default system's beginnning date which is 01-01-0001 00:00:00 here.
But, as a workl around, as I can observe first 8 digit of the input value is date part, so you can use that in following way:
DateTime.TryParseExact(strDate.Substring(0, 8), "yyyyMMdd",
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.None, out dtm);
return dtm.ToString("yyyy-MM-dd");
You only needed to make sure your format matched your input, which none of your provided input formats did.
See how the custom dateformat specifiers and literal characters line-up with your input.
Your input: 20161216000000.000000-000
format specifiers: yyyyMMddhhmmss.ffffff-000
Bringing that format to your method you'll get this:
// takes an object, returns a DateTime or null in case of failure
DateTime FormatDateTime(object ObjInstallDate)
{
DateTime dtm;
if (!DateTime.TryParseExact(
(string) ObjInstallDate, // notice that we don't hassle with the input here,
// only cast to a string
// we simply rely on the parser of DateTimer.TryParseExact
"yyyyMMddhhmmss.ffffff-000", // this matches the format
// if the -000 represents a timezone
// you can use z00 or zz0
// don't use zzz as that expects -0:00
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.None,
out dtm))
{
// an invalid date was supplied, maybe throw, at least log
Console.WriteLine("{0} is not valid", ObjInstallDate);
}
// we either return here null or a valid date
return dtm; // !! No ToString() here
}
I cherry picked the needed custom format value from Custom Date and Time Format Strings.
Notice that I simply return the DateTime instance that was created. You'll see next why I do that.
As you want to display the DateTime on a Winform (I assume in a textbox, but an label will work as well) you can now simply Databind the DateTime instance to the textbox and let the databinding plumbing do the formatting. Here is a code example that can be run in LinqPad:
// take a string and make a date of it
var str = "20161216000000.000000-000";
DateTime dtm = FormatDateTime(str);
// show it on a Databind TextBox
var f = new Form();
var txt = new TextBox();
txt.Width = 200;
// bind the Text property
txt.DataBindings.Add(
"Text", // property on the textbox
dtm, // our DateTime object
null, // use the DateTime instance, not a property
true, // use formatting
DataSourceUpdateMode.OnValidation,
null, // value to indicate null
"yyyy-MM-dd"); // our format
f.Controls.Add(txt);
f.Show();
I'm using the overload of Add on the DataBindingsCollection that takes an Format string. I can then use the same custom format specifier options to represent that DateTime instance however I want. From here it would be easy to add another TextBox which uses the same DateTime instance but shows the month in text for example.
When all of this comes together this will be your result:

Convert extracted date from excel to SQL date

I am extracting data from an Excel spreadsheet using interop in C# and I have a small problem that I cant think of an answer for.
When I extract the data for date cell using this code:
string _date = xlWorksheet.get_Range("B3", "B3").Value2.ToString().Trim();
I get a value of 40694 which wont go directly in to SQL using my insert statemwnt.
I have also tried:
DateTime _date = Convert.ToDateTime(xlWorksheet.get_Range("B3", "B3").Value2.ToString().Trim());
But that comes back with an error saying that it cant convert it.
Can anyone advise me on how to do it?
Excel's internal date values are "days since the epoch", which depends on if it's in PC or Mac mode (PC version uses 1/1/1900, Mac version uses 1/1/1904), and then there's an extra setting to be bug-compatible with Lotus 1-2-3 which has some leapyear issues. Converting this number realiably requires that you check if the spreadsheet is Windows- or Mac-based, and if the 1-2-3 compat flag is on.
You might be better of having Excel format the string into an unambiguous string (like 1-jan-1904) and then parse that back to a datetime value in SQL server, rather than trying to duplicate Excel's complicated date handling logic.
Use DateTime.FromOADate()
Using your example:
DateTime _date = DateTime.FromOADate(Double.Parse(xlWorksheet.get_Range("B3", "B3").Value2))
Use DateTime.FromOADate(double d):
DateTime.FromOADate((double)(xlWorksheet.get_Range("B3", "B3").Value2))
Ran into the same thing, here's the conversion
/// <summary>
/// Seriously? For the loss
/// <see cref="http://www.debugging.com/bug/19252"></see>
/// </summary>
/// <param name="excelDate">Number of days since 1900-01-01</param>
/// <returns>The converted days to date</returns>
public static DateTime ConvertXlsdtToDateTime(int excelDate)
{
DateTime dt = new DateTime(1899, 12, 31);
// adjust for 29 Feb 1900 which Excel considers a valid date
if (excelDate >= 60)
{
excelDate--;
}
return dt.AddDays(excelDate);
}
Excel stores dates as a floating point number counting the number of days since the day before 1900-01-01 (or 1904-01-01 for Mac). There is also a leap-year issue you have to take into account if the date is before 1900-03-01.
The following code will do the conversion:
DateTime ConvertToDateTime(Double date) {
if (date < 1)
throw new ArgumentException("Excel dates cannot be smaller than 1.");
var epoch = new DateTime(1900, 1, 1);
if (date > 60D)
date -= 2;
else
date -= 1;
return epoch.AddDays(date);
}

how can i assign only year say 2011 in a datetime variable in c#

I have a asp Text box as
where the user will fill only a year value, For this value I have Datetime type Property in c# application and Date type column in DB. So I want to convert that txtYear.Text to DateTime But it will only hold and/or show the year. Please help me in this situation.
A DateTime object will always hold a complete DateTime value, you can't use it to store a year only. (what use would that be anyway?) Besides, the datatype of a "year" is int, not DateTime.
So, I'd like to suggest changing your property to datatype int, both in your code and database.
To display just the year use the format "yyyy".
string s = "2011";
DateTime d = new DateTime(int.Parse(s), 1, 1);
Console.WriteLine(d.ToString("yyyy"));
Console.WriteLine(d);
You have to specify the format of the DateTime value you are manipulating:
String dateTimeFormat = "yyyy";
To show only a part of the DateTime value use the following:
dateTimeValue.ToString(dateTimeFormat);
To read a String value that represents a year into a DateTime use the following:
DateTime.ParseExact(stringValue, dateTimeFormat, CultureInfo.InvariantCulture);
DateTime.ParseExact Method (String, String, IFormatProvider) converts the specified string representation of a date and time to its DateTime equivalent using the specified format and culture-specific format information. The format of the string representation must match the specified format exactly.
DateTime.ToString Method converts the value of the current DateTime object to its equivalent string representation.
Rather than applying any string manipulating function make use of Year property. Check the documentation on the msdn by visiting below link.
DateTime.Year Property
A DateTime always has a full date component. When you create the DateTime instance, you'll need to assign a month and day, but you can ignore them in your usage.
DateTime d = new DateTime(int.Parse(txtYear.Text, 1, 1);
txtYear.Text = d.ToString("yyyy");
Even better would be not to use a DateTime but just use int. If you have only a year, you only need an int.
i assume the text box name is txYear
DateTime dt = new DateTime (Convert.ToInt32(txYear.text),1,1)
save this dt value in database
If you want only the year, why don't you make it of type smallint?
Anyway if you do really want to make it an year,
DateTime x = new DateTime(Convert.ToInt32(txtYear.text), 1, 1);
But make sure you validate that txtYear.text actually does have a valid year.
That is how I did and it worked.
string format = "yyyy";
var CurrentYear = DateTime.Now.ToString(format);

Is there a way to compare date "strings" in C# without converting the strings?

I have two fields:
string date1 = "04/26/10";
string date2 = "04/25/10";
How can I compare these two fields like so?:
if (date2 <= date1)
{
// perform some code here
}
Can this be done without first converting the fields to a separate date-type variable?
EDIT: I should mention that these values are coming from a database table where the date values are in a string format to begin with. Old legacy code...
No, but it is not difficult to convert to a date in C#.
if ( DateTime.Parse(date2,CultureInfo.InvariantCulture) <= DateTime.Parse(date1,CultureInfo.InvariantCulture))
{
// perform some code here
}
CultureInfo depends on the format the string dates have in the legacy DB. See: DateTime formats used in InvariantCulture
If your dates are actually stored as strings in the database, it seems like you can't be sure they'll be in a valid format before parsing. For that reason I'd suggest a small variation on jle's answer:
DateTime d1, d2;
if (DateTime.TryParse(date1, out d1) &&
DateTime.TryParse(date2, out d2) &&
d2 <= d1)
{
// perform some code here
}
else
{
// strings didn't parse, but hey,
//at least you didn't throw an exception!
}
At the very least you need to pick apart the strings in order to compare them in the right order.
If you want to leave them as strings, then you need to reorder them with LARGEST->SMALLEST units, so this:
yyyy/mm/dd
can be compared directly, but not the format you have. With your format, you need to split it, and either recombine it like above, or compare the individual pieces in the right order.
Having said that, it is rather easy to convert the strings to DateTime using DateTime.ParseExact.
Generally it is a bad idea to compare date as strings.
But if your strings are in the same format (e.g. yyyy/mm/dd means years, then monthes then days) then the comparison may be valid.
It could be done with string manipulation, but it would come down to effectively comparing three sets of integers, which as strings would induce more overhead than converting to datetimes. Why would you want to do that?
No. Let the .net framework sort that out for you. It will correctly identify the user date settings and format (using system settings, current thread) and determine which is the month, year and day - especially if that data comes from eg a sql server.
It's preferred to have the date formatted before doing the comparison. Depending in your cultureinfo, the safest way to compare dates is to format the date string to "yyyy-mm-dd".
DateTime d1, d2;
string date1 = "04/26/10";
string date2 = "04/25/10";
d1 = DateTime.Parse(date1.ToString("yyyy-MM-dd"));
d2 = DateTime.Parse(date2.ToString("yyyy-MM-dd"));
if (d1 > d2)
{
//do something
}
Best practice is to avoid comparing date as string types and compare with the official DateTime object of C#.
If your use-case requires the comparison using string objects then:
1st, make sure the date string format is yyyymmdd as recommend by Lasse V. Karlsen.
2nd, use the string method CompareTo to compare between the dates.
Here's how the method works in your case (Used C# Online Compiler to test this):
using System;
public class Program
{
public static void Main()
{
string fmt = "yyyymmdd";
string min = "20201206";
string max = "20210810";
Console.WriteLine(max.CompareTo(min)); // Output : 1
Console.WriteLine(min.CompareTo(max)); // Output : -1
string same1 = "20001212";
string same2 = "20001212";
Console.WriteLine(same1.CompareTo(same2)); // Output : 0
Console.WriteLine(same2.CompareTo(same1)); // Output : 0
// Summary:
// 1 = Greater than string param
// -1 = lesser than string param
// 0 = equals to string param
}
}

OWC11 Date Formatting Ignored

I am creating an Excel document using owc11. I am providing the dates in dd/mm/yyyy format. I am doing something like this in the code:
for (..looping through results..)
{
Range c = EmailStats.putCellValue(sheet, row, 1, val);
c.set_NumberFormat("dd/mm/yyyy");
}
private static Range putCellValue(Worksheet sheet, int row, int col, String val)
{
Range cell = (Range) sheet.Cells[row, col];
cell.set_Value(XlRangeValueType.xlRangeValueDefault, val);
return cell;
}
Now when for the val argument I set the date format as "dd/mm/yyyy" or not set it at all, the behaviour I get is mm/dd/yyyy from the 1st of a month up and till the 12th and then it swaps back to dd/mm/yyyy. So owc11 thinks that it knows better and swaps the days and month around (like in the US format) and when it is over the 12th date it sticks to the UK format.
Val is declared as String because it may not always be a date. It may be a date, a day, a user name, group name etc depending on how we group/sort our data. Also it may be a selection of days.
After experimenting a while I figured out that the only way to solve this is to use the universal date format of yyyy/mm/dd. However that may create other integration problems. So I was hoping that there may be a way to enforce the dd/mm/yyyy format, so please any suggestions are welcome.
Try to set val as not string. Try assign val as DateTime.
cell.set_Value(XlRangeValueType.xlRangeValueDefault, val);

Categories

Resources