Datetime to string: differents behavior depending on the converted datetime - c#

I use this C# code to convert a datetime to a string:
MyDatetime.ToString("dd/MM/yyyy HH:mm")
it works, but not every time. What do I mean?
If the input datetime is, for example, 2016-10-19 17:27:41.727, I get the string as expected, 19/10/2016 17:27. If the day in datetime (and/or the month) has only one digit, I get something weird.
If the input is 2016-01-07 14:58:13.560, I get 1/7/2016 and if it is 2016-10-26 17:14:16.000 I get 10/6/2016.
Do you know why? How can I always set a leading zero for days and months with only one digit? And, further, why I don't see the time part in the date I wrote as examples?
UPDATE
.
Some datetime fields from a SQL Server database (this is SQL Server Management Studio).

Most probably your DateTime already has invalid value (day and month are mixed)
new DateTime(2016, 01, 07,14,58,13,56).ToString("dd/MM/yyyy HH:mm")
returns 07-01-2016 14:58.
Take a look how do you read this DateTime value from the database. I believe the problem is there. (for example SqlDataReader.GetDateTime)

When you input a date as "XXXX-XX-XX" it assumes you're using the format "YYYY-MM-DD".
Just look at your input-output and you can see the pattern.
A lot of people in programming (that I work with) use this format because it is auto-filtering without any punctuation (20170102 will always go after 20161231, etc).

The DateTime method works in conjunction with the computer language. If your computer (or server) is in English (or vice versa) and you pass an American non-standard date format, you will have problems like this.
If you are using a web application, configure the correct language in web.config

Related

Convert user given date (January or 01. February 2017) to a timestamp

i'm trying to parse a user input of a date to a MySQL Timestamp format (YYYY-MM-DD).
User input could be something like:
input - wanted conversion
1) January - ThisYear-01-01, ThisYear-01-31
2) February 2017 - 2017-02-01, 2017-02-28
3) 01. April 2016 - 2016-04-01
4) 5.4.15 - 2015-04-05
P.S.:
The examples above are the suggested formats that we want to support (supporting only some of them would be also fine).
The users are not random or international, they will always write and understand dates in this format (Day Month Year).
Handling the missing year entry or the zero (like 5/4 in #4) isn't a problem but finding a proper way to handle the mentioned possible date input formats (DD MM YY, DD MM, DD Month, Month, Month YY, DD Month YY....etc.) with something that doesn't look very ugly and long in code is a little bit hard for me to imagine.
P.S.:
D,DD,MM,YY,YYYY are short for the numerical input.
Month is for the word input variant.
Could you please tell me if there is anything that could help me to make this process easier/more readable or at least point me to the right direction?
Thanks
Update #1:
By looking again to my question above i see that it's missing some background information, but i didn't want to write a long description to it, just only to the wanted function. Sorry.
So here are some general infos about the Program:
The Program is a Chatbot written in C# (UWP) which accepts a user request in natural language and give back the requested info (if recognized) from a mySQL DB (DB is based on a OSTicket support system).
Internally we send the user input to LUIS.ai to get it recognized and we get back the intents and entities from the service, which we then parse to a SQL Query and send it to the DB.
The results from the DB are then sent back to the user.
Many parsed queries work perfectly, that's why i want now to extend it by letting the user give a certain date in the request (e.g. give me all the support tickets from April).
What i only want is to take this new input "April" and convert it to a MySQL TimeStamp format, so that it would be also recognized from MySQL.
My current approach is to build a string like this:
string convertedTS = year + "-" + month + "-" + day;
And using 3 functions try to detect all three variables. But that's it for now.
E.g. "give me all the tickets from April":
April (recognized from LUIS AI as given date) will be converted to (04)
Year will be taken from
DateTime.Today.Year
and inserted too (2018-04)
The first day is always 01 and the last day of the month will be:
DateTime.Today.Year
Final Query from the example:
select ..........between '2018-04-01' AND '2017-04-30'
If you're worried about formatting the years, months, days, etc, you're doing it wrong. You have C#, and you have a date string from the user. Your concern is getting that string into a C# DateTime value. MySql has no part of this.
Once you have the DateTime value, let your connection provider worry about formatting via parameterized queries:
DateTime d = GetMyDateTimeValueFromUser();
string sql = "pretend SQL comnand with a datetime #variable";
using (var cn = new MySqlConnection("connection string here"))
using (var cmd = new MySqlCommand(sql, cn))
{
cmd.Parameters.Add("#variable", MySqlDbType.Timestamp).Value = d;
cn.Open();
//pick one.
cmd.ExecuteReader();
cmd.ExecuteNonQuery();
}
No special SQL format needed. If you're not using parameterized queries, you're not doing it right! This applies to much more than just DateTime values. All data used in an SQL statement should be handled this way. This has security and performance implications that go way beyond simple date values. Go parameters, or go home (I mean that: go home. Don't write bad code. We don't need any more.)
As it applies to the question, it means the problem is entirely about parsing a string to a C# DateTime value. The next step of moving this data to SQL is just not relevant. Thankfully, .Net has some options for you here. Specifically, take a look at the Parse family of functions, including:
DateTime.Parse()
DateTime.TryParse()
DateTime.ParseExact()
DateTime.TryParseExact()
The latter two allow you specify a set of allowed formats that can match the formats actually seen by your system. NuGet can be a further resource in this area.
What's that, you say? You want to allow the user to input anything? That just won't work. Period. Real humans can and will come up with far more ways to enter a date than you could possibly ever handle. Not to mention you need to know what to do when a British citizen inputs the value 1/2/2018 into your system, because that person almost certainly believes it means February 1, 2018, and not January 2. Cultural differences like this mean it is impossible to accept date inputs without some kind of contextual input filter on the front end. You must look to your user interface to help your users create values your code will understand... but we don't have enough info in the question to provide any guidance yet in that area.
I fixed that for the purpose of using it in my program.
Now i can detect and understand such inputs:
"09 11 2008"
"2017"
"today"
"yesterday"
"09/11/2002"
"march 2017"
"01.01.2010"
"01-01-2010"
"01 may 2019"
"31.October 2020"
"01 april"
"december"
If anyone is also interested in writing a similar function leave a comment, because the code is almost 300 lines.

How to convert an internal numeric date to Access Date/Time format?

I get some data from a PICK/UniVerse database that includes dates in a 4 or 5 character numeric format. Here are some examples .. I grabbed the date values from the database, and compared it to the date being shown in an application:
9832 12/1/1994
10027 6/14/1995
10594 1/1/1997
Is it possible to convert these into something that can be put into Access as a Date/Time value?
As A test, I put 9832 in Excel as a General format and then change it to Short Date, it comes up as 12/1/1926. So it's off by exactly 68 years. This was true for 10027 and 10594 as well.
In C# you can use DateTime.FromOADate
DateTime dt = DateTime.FromOADate(41481);
Returns a DateTime equivalent to the specified OLE Automation Date.
That will give you:
dt = {26/07/2013 12:00:00 AM}
Later on you can insert that Date in your Access database.
Access Date/Time values are actually double precision floats. The whole number portion represents the day and the integer portion represents the time of day.
It looks like those Pick date numbers correspond directly to the date portions of Access Date/Time values. So you can use CDate to transform them.
? CDate(41481)
7/26/2013
Experiment some more to get a feel for this:
? Date()
7/26/2013
? CDbl(Date())
41481
Note, although your question is tagged with c#, you don't need that to do these conversions. You can do them with an Access query and ask the db engine to apply those functions.
Since it turned out those date numbers are consistently offset by 68 years, you can still do the conversion in an Access query.
? DateAdd("yyyy", 68, CDate(9832))
12/1/1994
? DateAdd("yyyy", 68, CDate(10027))
6/14/1995
? DateAdd("yyyy", 68, CDate(10594))
1/1/1997
Or ...
? CDate(9832 + CLng(24837))
12/1/1994
? CDate(10027 + CLng(24837))
6/14/1995
? CDate(10594 + CLng(24837))
1/1/1997
A little late to this thread but I'll post some more detail: The Pick / MultiValue DBMS stores dates as an integer with date 0 = 12/31/1967. So as I write this on Jan 16, 2014 the internal Pick date is 16818. If you use the following you'll get that magic number 24837:
DateTime.Parse("12/31/1967").Subtract( DateTime.FromOADate(0)).Days
So add that to your Pick Date to get the OADate.
If you're using any of the common MV DBMS libraries for extracting data (UniObjects, U2.NET, mv.NET ...) you shouldn't need to convert the date like this. A typical function might look like:
string date = OConv( record["PurchaseDate"], "d2/" ); // "01/16/14"
Or rather than extracting the data in the internal DBMS format, you really should be getting it in external format to start. Ask the DBMS developer who provided the data to do this for you. It's real easy on their side to return " date'd2/' " rather than just "date".
Feel free to contact me directly if you need more info in this area.
All multivalue database dates (this includes UniVerse and UniData) are based on a base date of 31st December 1967. You can resolve this to an external data in a number of ways.
The favourite - e.g. if using SQL or one of the internal database tools is to create a data dictionary entry for the field concerned with a date conversion field, For example:
'D2' for a 2-digit year
'D4' for a 4-digit year
'D4/' for a 4-digit year with slash separators
'D4/E' for a 4-digit year with slash separators and explicitly in European format (DD/MM/YYYY) as compared to US format (MM/DD/YYYY).
If no explicit formatting is given then the format will default to environmental settings. There are other formatting options as well and many can be used in combination (as with the above).
As previously advised, the alternative is to adjust the raw date with a formula. The date is in days since 31st December 1967 - The base data for all multivalue databases.

Change date to Universal Time Format changes my date wrongly I can't get error

I am trying to simply change the date format from the datatable to universal time format but it formats it wrongly as if I have date for August 7 it changed it to August 8 after formatting it to universal date time. My code for formatting date is,
DateVar[runs] = DateTime.Parse(Convert.ToString(output.Tables[0].Rows[runs][0])).ToUniversalTime().ToString();
Don't get in to code its correct and its a part of loop so "run" is loop and output is data set having one table I have first data in table is "Sunday, August 07, 2011 10:52 PM" and it was converted to "8/8/2011 5:52:00 AM" after implementing universal time format.
Hopes for your suggestions
Universal time isn't a format - it's a time zone, effectively. It's not clear what you're trying to do, but converting a "local" DateTime to "universal" DateTime will usually change the time. If you don't want that to happen, don't call ToUniversalTime.
It's a pity that the .NET date/time API isn't as clear as it could be - the DateTime type itself has some horrible ambiguities about it. I'm trying to improve the situation with my Noda Time project, but you will need to understand what time zones are about etc.
Personally I would suggest not using simply DateTime.Parse or just calling ToString unless you're absolutely sure that the default format is what you want. I usually call DateTime.ParseExact and specify the expected format (and usually CultureInfo.InvariantCulture unless it's a user-entered string) - and likewise I provide a format string to the ToString call.
In your code you're simply converting a string to a string - what are you attempting to accomplish? If you're just trying to change the format (e.g. to dd/MM/yyyyTHH:mm:ss) then you don't need to call ToUniversalTime but you do need to provide the format string.
I suggest you split your code out into several statements to help you debug this (and for general code clarity):
Fetch the string from the DataTable, if you really need to (if it's already a DateTime, there's no point in converting it to a string and then back again)
Parse the string (again, assuming you need to)
Perform any conversions you need to
Format the DateTime with an explicit format string
Now if any single operation is causing a problem, you can isolate it more easily.
If I run ToUniversalTime() from Greenwich it will give same time but if i do it while I live some where else it will get an offset date time object of + or - hours depending on position.

How to format dates in format yyddd having no leading zeros

I am dealing with dates coming from the AS/400 that are a form of julian date. January 1st 2000 comes back as a string value of "1". If the date were in true julian form it would look like 2000001. The date 12/31/2049 is comes back from the AS/400 as "49365". Is there a way to format these dates in my C# code to look like standard short dates?
What does January 1, 2001 look like?
If it looks like "1001", you can pad on the left with zeros to 5 digits and then extract the 2-digit year as the first two digits and the day-of-year number as the last 3. It should then be a simple matter to convert the day-of-year number to a month and day; if nothing else you can do it with a bunch of if statements on day ranges.
If it looks like "11" because there are no leading zeros in the day number, you're just out of luck as there is no way to differentiate between many dates, such as January 1, 2001 and January 11, 2000.
P.S. These aren't Julian dates, they're a variation on ordinal dates.
IF your dates are always of the format 'yyddd':
If you can write your SQL statement directly, the following will work...
SELECT CAST('20' || julianDate as date)
FROM table
If you don't, consider writing a view that incorporates that behaviour (that's one of the reasons views exist...).
For obvious reasons, all dates will be considered year 2000 and later...
IF (for whatever reason) it's removing any leading zeros in each part (as pointed out in comments for #Anomie's answer), you are indeed simply toast. Frankly, the entire dataset is probably a loss, as I'm not sure how even RPG would be able to differentiate between certain dates properly at that point.
IBM defines *JUL date format as yy/ddd. It is not commonly used, but is is an available standard format supported on the AS/400. You say you have a string, so the assumption here is that it is stored as CHAR(5), or 5A in DDS.
If you column is called jdt, get the right number of digits in your string, in SQL, with:
RIGHT(('00000' || TRIM(jdt)),5)
Now put the slash in:
INSERT( RIGHT(('00000'||TRIM(jdt)),5) ,3,0,'/')
DB2/400 can cast this to a real date field, but it will only work properly if you can SET OPTION DATFMT=*JUL. How to do this from C# on Windows would depend on how you are connecting.
Let's suppose you can't find the way to set the date format on your connection.
Solution: Create a user defined function [UDF] in DB2.
First, choose an appropriate library to store the function in, and set that as your current library. In OS/400 CL, CHGCURLIB yourlib, or in SQL, SET SCHEMA = yourlib. Thus by default anything you create will go into this library.
I recommend storing the SQL definition for your UDF in a source member. (Ask if unfamiliar) You can execute the source with the RUNSQLSTM command.
Your function definition could look something like this:
CREATE FUNCTION CvtJul2Date( jdtin char(5) ) RETURNS DATE
LANGUAGE SQL
SPECIFIC CVTJUL2DT
DETERMINISTIC NO EXTERNAL ACTION
CONTAINS SQL
RETURNS NULL ON NULL INPUT
SET OPTION DATFMT = *JUL
BEGIN
RETURN(
date( insert( right(('00000'||trim(jdtin)),5) ,3,0,'/') )
);
END
The *JUL option is scoped to the UDF. Any SQL query that runs on the AS/400 should be able to do this conversion, regardless of the DATFMT of the job (assuming you have put this function in a library which is on that job's library list).
Oops... My bad. A method will still probably have to be written.
Based on your description, an increase of 1 is a new day? Looks like you will have to do some math to calculate the date. Maybe create a function like
public DateTime ConvertDate(int julianDate)
{
}
This is untested and may need some changes. But this would be my suggestion.

Representing date having 32 as maximum value for Day

I am building application in asp.net using Sql server 2005. In my application I have to represent many dates & dates are of Nepali(Bikram sambhat) in which the maximum day for some month can be 32.
So what is the best option to represent the date in sql server so that 32 can be placed for day value & that can be easily compared(manipulated) in sql server as well as in asp.net?
There is a guy that has implemented some classes for converting between Nepali dates and Gregorian dates. This way you can input dates in the Nepali format but store them in a format that SQL Server understands. Look here: http://rrajbhandari.blogspot.com/2010/06/bikram-sambat-classes-and-controls.html
Remember that a date in either calendar can be converted to another calendar - they point to the same day.
I would recommend using the built in Date type. It sounds like any date in your format can be easily converted into the standard sql format.
I would just write a utility to convert any date from sql server into your display format whenever you load or write to the database.
There isn't an easy conversion from Nepali to Gregorian. Think of it in two ways:
need to show Nepali date
need to manipulate (e.g. find difference between months, days etc)
The first one is easy - store as a varchar
The 2nd one is not. If you wanted to know the difference between 2 Nepali dates, you can store the date a 2nd date as the Gregorian equivalent and use datediff/dateadd(day) between them. However, dateadd(month) will be useless for you here unless you wanted to know the difference of 2 Nepali dates in Gregorian date months - not common.
Sounds like just storing in varchar and having a library for Nepali dates, either in the front end or as a CLR, would go down better.
It may help for conversions to have a fully materialized Nepali date table with the corresponding Gregorian date, so the layout would be
NepaliYear NepaliMonth NepaliDay Gregorian
x 4 2 2014-21-23
etc
But I am not sure it would help much for (Nepali) date maths beyond conversion - and only if you need such conversion within SQL Server.
EDIT
#pst's comment
There is a reason to prefer VARCHAR to datetime. I assumed that the frequency, most of the activity is reading/writing a Nepali date - which is stored and read as a VARCHAR - no conversion. If and only when date maths is required does it involve the library - in which case you invoke conversion, and only then if you need at any point in time the Gregorian calendar equivalent. If all you wanted is maths between NepaliDate/scalar or NepaliDate/NepaliDate - again, datetime offers no benefit whatsoever - it cannot handle day #32 in a month.
Check out Nepali calendar, it may help.

Categories

Resources