Data corruption in PostgreSQL timestamp field - database

I have a PostgreSQL table with the following schema -
CREATE TABLE test (
id serial NOT NULL PRIMARY KEY,
username varchar(100) NOT NULL, -- The user name
dob timestamp with time zone NOT NULL -- The date of birth
);
I then inserted some data into the table with data like this -
INSERT INTO "test" ("username", "dob") VALUES (E'Scotty', E'2009-05-14 15:44:43');
And if I check the DB for the data, I get this -
mydb=> select username, dob from test where username='Scotty';
username | dob
----------+---------------------------
Scotty | 2009-05-14 15:44:43+05:30
(1 row)
Everything is fine and dandy until I try inserting some data with the date before 1946 -
INSERT INTO "test" ("username", "dob") VALUES (E'James T Kirk', E'1945-01-01 11:30:11');
mydb=> select username, dob from test where username='James T Kirk';
username | dob
-------------- +---------------------------
James T Kirk | 1945-01-01 11:30:11+06:30
(1 row)
Look at the above result. Notice how the Timezone value has changed from +05:30 to +06:30
It actually gets worse when I insert any date which is before 1942 -
INSERT INTO "test" ("username", "dob") VALUES (E'Spock', E'1941-01-01 11:30:11');
mydb=> select username, dob from test where username='Spock';
username | dob
----------+------------------------------
Spock | 1941-01-01 11:30:11+05:53:20
(1 row)
Now the Timezone value has got completely mangled and the date can't be parsed.
I would appreciate any help with this.
My Timezone is Asia/Kolkata (GMT+05:30).
Update: I tried entering the data by specifying the TZ explicitly like this -
INSERT INTO "test" ("username", "dob") VALUES (E'McCoy', E'1941-01-25 00:20:30+05:30');
Even then it didn't work.
mydb=> select username, dob from test where username='McCoy';
username | dob
----------+------------------------------
McCoy | 1941-01-25 00:43:50+05:53:20
(1 row)

What locale are you in? Probably PostgreSQL is assuming that the dates are for your current locale, and applying appropriate time zone and DST rules, which isn't the right thing to do if the dates and times are (say) UTC.
Do you really need the timezone functionality? A timestamp without time zone will exhibit saner behavior, since it doesn't have to implement weird rules. But if you need the time zone, then you definitely want to fix this rather than kludge it.
The best fix is just to specify the timezone explicitly: '04:05:06-08:00' for GMT–08:00, or perhaps '04:05:06z' for GMT/UTC/"Zulu" (hence the 'z').
Edit: Most of the real weirdness is coming from the Asia/Kolkata time zone. From tzdata2009g:
# India
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
Zone Asia/Kolkata 5:53:28 - LMT 1880 # Kolkata
5:53:20 - HMT 1941 Oct # Howrah Mean Time?
6:30 - BURT 1942 May 15 # Burma Time
5:30 - IST 1942 Sep
5:30 1:00 IST 1945 Oct 15
5:30 - IST
You don't describe what you expect the behavior to be, so it's hard to say where you want to go from here.

This looks like a Daylight Saving issue. (From what I get, the timezone is UTC+05.30, DST sets in around March/April, and adds one hour).
Have you tried with inserting the same date and just changing the year to rule out that possibility?
For that last one, It's strange. I have not managed to reproduce it, but it could be because of the half hour zones. Is it the same if you change the TZ environment variable?

I have one similar but different problem.
We have PostgreSQL8.0 installed on WinXP.
One of the columns is of type
TimeStamp with TimeZone. The Time
value of this column changes with
the change in system time zone. Can
we avoid this?
After every retrieval
from table, half hour is getting to
thid column for every tuple.
I haven'nt been able to trace origin to this problem.
rgds
nithin

Related

Snowflake: Converting a date + timestamp to timestamp + day of the week

Right now, I have a column which is in date + timestamp format. Like so:
2022-02-24 17:30:00.000
Does anyone know how to convert this into something like Thursday 17:30:00.000
I don't want to convert anything to a string and concat that way, as then it'll be difficult to graph in snowsight.
Snowflake has a 3 Letter Day, but does not appear to have full name days, for free in the TO_CHAR formatting.
SELECT '2022-02-24 17:30:00.000'::timestamp as my_timestamp,
TO_CHAR(my_timestamp, 'DY HH:MM:SS.FF');
MY_TIMESTAMP
TO_CHAR(MY_TIMESTAMP, 'DY HH:MM:SS.FF')
2022-02-24 17:30:00.000
Thu 17:02:00.000000000
but that is not the long form you requested.

Convert Wed Oct 14 08:00:00 CDT 2020 format to DateOnly and Time Only

We receive a csv file that has a column in this date format -- Wed Oct 14 08:00:00 CDT 2020, along with a column that has a count for each date/time
I am using an SSIS package to grab the file and import this data into a sql table, then I can format it the way I need to and then actually export the data in the format needed.
If there is a way to do this all within one SSIS package I am all ears but currently I am working on just getting the data into SQL and converted to the right format so that I can export it.
I need to get that file and convert that date format and split it up into two separate columns
One column will be just the date in this format 2020-10-14 00:00:00.000
One column will be just the time in this format 08:00:00.0000000
Updated to change the dates to match so it's not as confusing and also the error I am receiving when running the suggested code below.
Image of Error I'm recieving
Image of table with the data I am trying to convert
Image of table attributes
Screenshot of my screen when running a select * from the table I am pulling the data that I need converted
Screenshot of the error I receive when running the query by Aaron.
If this is the format it will always be in, and timezone is irrelevant, you can first try to convert it to a datetime, then you can extract the parts from that.
SET LANGUAGE us_english; -- important because not all languages understand "Oct"
;WITH src AS
(
SELECT dt = TRY_CONVERT(datetime, RIGHT(OpenedDateTime ,4)
+ SUBSTRING(OpenedDatetime, 4, 16))
--, other columns...
FROM [dbo].[VIRTUALROSTERIMPORT_Res_Import]
)
SELECT OpenedDateTime = CONVERT(datetime, CONVERT(date, dt)),
OnHour = CONVERT(time, dt)
--, other columns...
FROM src;
Results:
OpenedDateTime OnHour
-------------- ----------------
2020-10-14 08:00:00.0000000
If you need to shift from one timezone to another timezone, that's a different problem.
I was just showing the date formats, don't look so into the actual date examples I used. The time zone is irrelevant I just need the formats changed.
When I used The code Aaron suggested I got a conversion error: I'm assuming its because the columns are varchar in the table, but I cant get the dates to load as date formats bc SSIS keeps giving me truncated errors-- so I have to load it as varchar.
Below is the code I was running, I tweaked it to use the column and table names I am using.
SET LANGUAGE us_english; -- important because not all languages understand "Oct"
DECLARE #foo varchar(36) = 'Wed Oct 14 08:00:00 CDT 2020';
;WITH src(d) AS
(
SELECT TRY_CONVERT(datetime, RIGHT(#foo,4) + SUBSTRING(#foo, 4, 16))
)
SELECT OpenedDateTime = CONVERT(datetime, CONVERT(date, OpenedDateTime)),
onhour = CONVERT(time, OpenedDateTime)
FROM [dbo].[VIRTUALROSTERIMPORT_Res_Import];

How to set SSRS subscription time as an expression

I have a report and the report StartDate and EndDate parameters are using the expression as a default value below.
=DateAdd(DateInterval.Minute,0,DateAdd("h",7,DateAdd("h",-24,Today())))
=DateAdd(DateInterval.Minute,0,DateAdd("h",7,Today()))
When I execute the report, the report is starting from the day before at 7 AM to today 7 AM.
I would like to keep the report Start time and End time like this(07:00).
I also want to send the report to customer every day 7:30 AM but the report needs to be executed according to start date and end date paramaters.
Example: today 12.12.2019
Subscription time will be 07:30 AM
report needs to be running this time:
StartDate : 11/12/2019 07:00:00
EndDate : 12/12/2019 07:00:00
But when I schedule subscription every day and 7:30 AM, I received report from one day before 7:30 AM and today 7:30 AM.
I just want to see report from 7:00am to 7 am. Even if I change schedule time.
Could you please help me about this problem. How can I edit my subscription?
Is it possible to write an expression in "date/time from - date/time to" fields in subscription?
Btw, When I unclick “use Default” part, it always takes 11-12-2019 even 2 days after ☹
Time from needs to be one day before at 07:00 AM
Time to should be on that day at 07:00 AM
Do you have any suggestion for it?
Thanks
I resolved my issue. There are 2 solutions for it.
Option 1 :
In the report design If it is must to have those date parameters must be DATTEIME and to allow TIME factor as well then and if you want to run the report which is subscribed always for Yesterday 7:00 to today 7:00 am then I would not rely on sending any parameter values based on expressions …I would set up Date/Time Parameter in report design to allow null values and send null values as default from the subscription settings.
Then In report SP you can always add a clause at the TOP like
if #startDateTime is null AND #endDateTime is null
begin
set #startDateTime =CONVERT(VARCHAR(10), getdate(), 111);
set #startDateTime =dateadd(hh,7,( dateadd(d,-1,#startDateTime)))
set #endDateTime =dateadd(d,1,#startDateTime)
end
and let the rest SP be same
Option 2 :
If you can change the report parameters to be a type only DATE then its easy always send =Today() in your subscription parameter for both Start & End
Then In report SP you can always add a clause at the TOP like
if #startDateTime = #endDateTime
begin
set #endDateTime =CONVERT(VARCHAR(10), #endDateTime, 111);
set #endDateTime =dateadd(hh,7,#endDateTime)
set #startDateTime =dateadd(d,-1,#startDateTime)
end
and let the rest SP be same
Option 2 is better if they are ok to have Start & End date parameter as just DATE instead of DATETIME.
Any way Using any of these options do handle this in SP… you can always have control in future if they want to change time form 7:00 am to any other time …no need to change report design just update SP…2 minutes
You can schedule this report for any time of the day and it will always send them a report for Yesterday 7:00 to Today 7:00

TRY_CONVERT returns NULL on a value that looks like a date

I have been benefiting from TRY_CONVERT function thanks to a reply from my last question.
I am now facing a value in a key/value table (the value column of type NVARCHAR(50)) that cannot be converted to DATE even though it very much looks like a date.
I'd like to why TRY_CONVERT(DATE, [Claim issue complete]) should return a NULL.
Even though I use a substring of the value like this the result is the same
SELECT SUBSTRING([Claim issue complete], 1, 10) AS CompletionDate
UPDATE: A textual copy of the picture
Claim issue complete
2018-09-21 00:00:00+02
2017-10-12 00:00:00+02
2018-01-30 00:00:00+01
2017-07-19 00:00:00+02
2019-02-14 00:00:00+01
NULL
2017-08-06 00:00:00+02
2017-10-04 00:00:00+02
2018-05-18 00:00:00+02
2018-09-05 00:00:00+02
2019-01-14 00:00:00+01
2019-01-24 00:00:00+01
2018-03-21 00:00:00+01
2017-11-28 00:00:00+01
2018-02-26 00:00:00+01
2018-04-05 00:00:00+02
2018-04-27 00:00:00+02
Update 2: An Example
Here is a sample of the problem.
SELECT TRY_CONVERT(DATE, '2018-09-21 00:00:00+02')
The above returns NULL.
Ideally, you should change the database structure and store your values in the appropriate data type - if you need a date value store it as Date, if you need it with time store it as DateTime2 (Why not DateTime?) and if you need it to be time zone aware store it as DateTimeOffset.
For more information, read Aaron Bertrand's Bad habits to kick : choosing the wrong data type.
The string representation format you are using in your database does not fit any of the built in date time styles in SQL Server - that's why the Try_Convert fails (as will Try_Cast, for that matter).
If you are only interested in the date part of the value, you can use try_cast on the 10 left-most chars - since you are casting to Date and not DateTime the yyyy-mm-dd format is unambiguous and will always be converted correctly:
SELECT TRY_CAST(LEFT('2018-09-21 00:00:00+02', 10) As DATE)
If you want to use Try_convert instead, you need to specify the style parameter - 120 in this case:
SELECT TRY_CONVERT(DATE, LEFT('2018-09-21 00:00:00+02', 10), 120)
Please note that you don't need any code to handle null inputs - they will just come out as null
The reason that SELECT TRY_CONVERT(DATE, '2018-09-21 00:00:00+02') is'nt working is cause it is'nt a valid date format. To be specific, it is the +02 part in the end.
You could re-write it like this if you know the first 19 positions always will be YYYY-MM-DD hh:mm:ss:
SELECT TRY_CONVERT(DATE, LEFT('2018-09-21 00:00:00+02', 19))
I've just added the LEFT-function to sort out the +02 in the end
Good day,
SELECT TRY_CONVERT(DATETIME2 , '2018-09-21 00:00:00 +02', 20) -- null, since this '+02' is not a legit OFFSET
SELECT TRY_CONVERT(DATETIME2 , '2018-09-21 00:00:00 +02:00', 20) -- OK
--Solution for bad OFFSET style can be to add the missing string:
SELECT TRY_CONVERT(DATETIME2 , '2018-09-21 00:00:00 +02' + ':00', 20) -- OK
--Same using Date:
SELECT TRY_CONVERT(DATE , '2018-09-21 00:00:00 +02' + ':00', 20) -- OK

Update query to find and change portion of MS SQL DateTime

I have various datetime stamps on a expiration field (datetime). The datetime in that field for each record varies. For example,
2015-12-31 04:59:00:000, 2014-12-31 17:00:00:000, 2020-12-15 04:00:00:000
Trying to write a query to find all datetime that have an ENDING that is not equal != to 05:00:00:000.
Then an update query to update the end of the stamp to 05:00:00:000, leaving the front as is.
Some example changes, from - to:
From:
2015-12-31 04:59:00:000, 2014-12-31 17:00:00:000, 2020-12-15 04:00:00:000
To:
2015-12-31 05:00:00:000, 2014-12-31 05:00:00:000, 2020-12-15 05:00:00:000
Here is what I'd like to see:
UPDATE table_name
SET table_name.expire_field = keep_front_date_portion + '05:00:00.000'
WHERE table_name.expire_field = date_portion_ignore and time_portion != '05:00:00.000'
Not sure how this would be written correctly in MS SQL 2008 syntax?
I found this post but from what I can tell it needs a timestamp that does not vary.
We can use DATEADD/DATEDIFF to reset just the time portion of a datetime value
UPDATE table_name
SET expire_field =
DATEADD(day,DATEDIFF(day,'19000101',expire_field),'1900-01-01T05:00:00')
WHERE DATEPART(hour,expire_field) != 5 or
DATEPART(minute,expire_field) != 0 or
DATEPART(second,expire_field) != 0
But you may find it easier to just skip the WHERE clause and let it update the entire table - you're not going to be able to benefit from indexes here anyway.
In the DATEADD/DATEDIFF pair, the date is arbitrary. First, the inner DATEDIFF asks "how many whole days have passed since some date", and then the DATEADD adds that same number of days onto some date at 5am.

Resources