C# PostgreSQL date format exception - c#

I have format exception when i trying to add '9/30/2019 5:15:54 PM'(DD-MM-YYYY) to my database.
I'm already SET datestyle = 'ISO, DMY'. So now i can use it like:
INSERT INTO products(name, createdat) values ('test', '9/30/2019 5:15:54 PM')
I have the same SQL command in C# and PostgreSQL, but it works only in Postgre-pgAdmin(nice joke C#).
How can fix this error in C#?

Well if you insert a valid Postgres timestamp literal, it should work everywhere:
INSERT INTO products (name, createdat)
VALUES
('test', '2019-09-30 17:15:54'::timestamp);
Perhaps the setting you configured were only valid from the session originating from pgAdmin, but not with the Postgres driver which C# is using. In any case, the default Postgres timestamp literal is ISO compliant (your version is not), which is always a good thing.

Related

C#- "DateTime.UtcNow" outputs date in a format which is treated as out of range for Postgres DB

I have shifted to a new laptop (windows 10 i5) and trying to set up my .NET app. I am encountering a weird problem and it has taken a whole lot of my time. In my app, I am storing the current date-time in UTC format in a Postgres DB table. The c# command DateTime.UtcNow outputs the date format as
14-12-2021 13:50:57
When the above date is passed via an insert query to be stored in the Postgres Db, it throws the error as
ERROR: date/time field value out of range: "14-12-2021 13:50:57"
What seems even stranger is, that on my old laptop (same OS - windows 10 i3), the same code works fine and gives the output of the date as
12/14/2021 07:24:24 AM
and this format is accepted by the Postgres DB.
The table design is as follows
CREATE TABLE IF NOT EXISTS public."Log"
(
"Id" bigint NOT NULL DEFAULT nextval('"Log_Id_seq"'::regclass),
"QueueId" uuid NOT NULL,
"MessageId" character varying(100) COLLATE pg_catalog."default",
"LogTime" timestamp without time zone,
CONSTRAINT "Log_pkey" PRIMARY KEY ("Id")
)
The Value resulting from the DateTime.UtcNow is being passed into the LogTime column.
Here is the, c# code that forms the Postgres query.
string query = $#"INSERT INTO public.""Log""(
""QueueId"",
""LogTime"",
""Message"",
)
VALUES(
'{req.QueueId}',
'{req.LogTime}',
'{(item.Message.Length > 100 ? item.Message.Substring(0, 100) : item.Message)}'
RETURNING ""Id"";";
The same code behaves differently on different machines with the same OS. Any clues would be extremely appreciated. Thanks for any help.
I repeat my comment above because you are transforming a datetime in a string when you write '{req.LogTime}', and with that the code creates a parsing problem for your database because it needs to translate that string back to a timespan to insert it in the column. Not only this is prone to different result when converting the strings (one pc thinks that the right format for a date is "MM-dd", the other one thinks of "dd-MM") but it is also creates a well known vulnerability called Sql Injection.
You should simply use parameters to pass values to your database.
With a proper type defined parameter there is no more ambiguity in the resolution of the value and the database is not affected by script kids trying the easiest hack in the world.
string query = $#"INSERT INTO public.""Log""(
""QueueId"", LogTime"", ""Message"")
VALUES(#qid, #ltime, #msg)
RETURNING ""Id"";";
using (var cmd = new NpgsqlCommand(query, conn))
{
cmd.Parameters.Add("#qid", NpgsqlDbType.Uuid).Value = req.QueueId;
cmd.Parameters.Add("#ltime", NpgsqlDbType.Timestamp).Value = req.LogTime;
cmd.Parameters.Add("#msg", NpgsqlDbType.Varchar).Value =
(item.Message.Length > 100 ? item.Message.Substring(0, 100) : item.Message);
var id = cmd.ExecuteScalar();
}

DateTime sent in wrong format to Sql Server 2014

I'd like to execute a stored procedure on an sql server 2014. The sql server is set up in German, the user used for connecting to the sql server has also configured German as language. If I try to execute the sql procedure or raw sql, I always get the error
varchar cannot be converted to datetime
even if I provide german datetime values. I've found out that it works if I prepend the sql text with the command SET DATEFORMAT dmy.
The problem is the same for ADO .NET as well as Entity framework. Setting the thread and ui culture to German also didn't help.
It seems that C# SQL Connection sets the culture to default (English) independently of thread culture, date format or sql server language.
Any ideas highly appreciated how to set the culture correctly - such that I don't need to send always SET DATEFORMAT dmy before the real sql text.
UPDATE
This is my code to call the sql stored procedure and pass the dates using the c# sql parameter.
SqlConnection sqlConnection = null;
try
{
// open connection to the database
sqlConnection = new SqlConnection(Convert.ToString(ConfigurationManager.ConnectionStrings[ProductivityAnalyzerDatabase.ConnectionStringName]));
sqlConnection.Open();
// setup command
var sqlCommand = new SqlCommand("UpdateEmployeeBalances", sqlConnection);
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.Parameters.Add(new SqlParameter("#employeeId", employeeId));
sqlCommand.Parameters.Add(new SqlParameter("#startDate", startDate));
sqlCommand.Parameters.Add(new SqlParameter("#endDate", endDate));
sqlCommand.ExecuteNonQuery();
}
finally
{
if (sqlConnection != null && sqlConnection.State == ConnectionState.Open)
{
sqlConnection.Close();
}
}
Date values are not stored with their display format.
The problem is that you send your dates to Sql Server as strings, thus forcing sql server to cast the strings to date values. unless you send your dates in ANSI-SQL format (yyyy-mm-dd) this casting might fail or yield unexpected results (is 04/02/2015 April 2nd or February 4th?)
The correct solution, as Steve mentioned in his comment, is to use c#'s DateTime structure as the value of the parameter for the stored procedure. (don't use ToString or anything like that.)
Note that the parameter should be declared as a date type (datetime, datetime2, or date) in the stored procedure itself.
Good day,
You can read more about this issue in this clog:
http://ariely.info/Blog/tabid/83/EntryId/161/Date-displaying-format-vs-Date-storing-format.aspx
in short (from the link above):
Implicit conversion of ambiguous date formats are interpreted according to the language of the connection or the collate of the query. Always keep and following rules, in order to make your work more compatible.
Using .Net you should use a type that is mapped correctly to the SQL Server types. Check this link: https://msdn.microsoft.com/en-us/library/cc716729%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
When you specify dates in DML queries, always use constants way that
are interpreted the same way for all language settings!
for example use format yyyymmdd as 20160227, Use explicit CONVERT statement with an explicit style parameter, Use escape clauses while using ADO, OLE DB, or ODBC.
Remember that those are only display formats. In the database the
data stored in the same way, no matter what is your language!
*datetime is stored as 4 bytes for the date + 4 bytes for the time, Datetime2 is a bit more complex since it is flexible. you can read more on the undocumented internal stored format here.
I hope this is useful :-)

SQL Server Timestamp using with C#

In my database I have used Timestamp in each table to see when data was inserted.
It stores data in byte[] of 8 byte.
Now I want to read that time using C#.
How can I get DateTime object from Timestamp which is byte[]?
SQL Server's TIMESTAMP datatype has nothing to do with a date and time!
It's just a binary representation of a consecutive number - it's only good for making sure a row hasn't change since it's been read.
In never versions of SQL Server, it's being called RowVersion - since that's really what it is. See the MSDN docs on ROWVERSION:
Is a data type that exposes automatically generated, unique binary numbers within a database. rowversion is generally used as a mechanism
for version-stamping table rows. The
rowversion data type is just an incrementing number and does not
preserve a date or a time. To record a date or time, use a datetime2
data type.
So you cannot convert a SQL Server TIMESTAMP to a date/time - it's just not a date/time.
But if you're saying timestamp but really you mean a DATETIME column - then you can use any of those valid date formats described in the CAST and CONVERT topic in the MSDN help. Those are defined and supported "out of the box" by SQL Server. Anything else is not supported, e.g. you have to do a lot of manual casting and concatenating (not recommended).
The format you're looking for looks a bit like the ODBC canonical (style = 121):
DECLARE #today DATETIME = SYSDATETIME()
SELECT CONVERT(VARCHAR(50), #today, 121)
gives:
2011-11-14 10:29:00.470
SQL Server 2012 will finally have a FORMAT function to do custom formatting......

Cannot use Turkish characters with Entity Framework

We have a large SaaS ASP .NET 4.0 application used internationally. We are slowly migrating our classic ADO dataproviders toward Entity Framework.
We use MS Sql server 2008. For Turkish customers we use a Turkish collation "Turkish_CI_AS" for character columns. I.e. 8 bit varchar fields (we don't use 16bit nvarchar columns)
Now I encountered a problem when I add new objects with EF. Special characters like 'ş' are changed:
using (TestEntities myEntity = new TestEntities())
{
MyObject test = new MyObject()
{
TestString = "baş"
};
myEntity.MyObjects.AddObject(test);
myEntity.SaveChanges();
}
When I step through with the debugger, "test.TestString" is still "baş" in this line of code:
myEntity.MyObjects.AddObject(test);
However in the database the field "TestString" has the value "bas". The 'ş' is saved as a 's' This does not happen with my old dataprovider methods.
How can I add Turkish characters to my database with EF? Anyone has some good suggestions? I tried quite some things but can't figure it out :)
tnx, Frank
edit
running the profiler on a little test setup shows this:
exec sp_executesql N'insert [dbo].[TestObjects]([TestChar])
values (#0)
select [TestId]
from [dbo].[TestObjects]
where ##ROWCOUNT > 0 and [TestId] = scope_identity()',N'#0 varchar(255)',#0='Bas'
apparently sql receives the wrong value, it's really .NET who's responsible.
Ok I figured it out. In MySql it is possible to add encoding info in the connection string, Microsoft sql doesn't allow this.
EF seems to determine the encoding by looking at the default database collation. In our setup the database collation was Latin1, while the tables with customer specific data have collation "Turkish_CI_AS".
So I have two options:
1) change the default collation for the whole database (impact on
system and config tables etc)
2) change my columns to nvarchar
Changing to unicode will be done in the end, for now we'll use some good old ADO.NET which just works! :)
tnx for thinking with me

Safe DateTime in a T-SQL INSERT statement

Been running in problem lately with using a DateTime in a T-SQL INSERT INTO statement. Work fine on one machine but might not work on another and I guess this has to do with locale settings.
So if I have DateTime variable what is the safe way of using that in a SqlStatement string that it will always work regardless on local system settings?
Thanks
Use parameterized INSERT query.
Most likely, your code is assembling the SQL command string. That also makes your code vulnerable to SQL Injection.
You should use parameterized query as Adrian suggested.
Another possibility is to use an ISO 8601 string representation like described here which is independent of locale settings.
That would look like:
20110921 15:20:00
Either you use parametrized command/stored procedures where you create a parameter of type DateTime in the stored and assign it from .net code when you call the stored (so .NET and SQL will know they are working with a datetime and will never confuse/swap day and month), or you include a specific command on top of your insert commands and then format all dataetime strings with this pattern, for example:
SET DATEFORMAT dmy;
SET DATEFORMAT (Transact-SQL)

Categories

Resources