I am currently using:
sample_start_time AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time' AS sample_start_time_est
from:
Convert Datetime column from UTC to local time in select statement
sample_start_time
2021-03-10 21:13:00.000
becomes
sample_start_time_est
2021-03-10 16:13:00.000 -05:00
Though EST is now displayed, I think the system still recognizes it as UTC based on the results of subsequent queries. How do I get the system to recognize the adjusted time as EST?
For instance:
sample_start_time_est = 2021-03-10 16:14:00.000 -05:00
end_time = 2021-03-10 18:14:00.000
WHERE sample_start_time_est < end_time
above WHERE clause currently evaluates to FALSE but I want it to be TRUE
I am using Microsoft SQL Server Management Studio.
CAST(sample_start_time AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time' AS DATETIME2) AS sample_start_time_est
The result you are seeing is happening because datetimeoffset has a higher precedence than datetime, and your comparison will force an implicit conversion. The implicit conversion will turn a datetime into a datetimeoffset with zero offset:
declare #dto_minus5 datetimeoffset = '2022-01-01 00:00:00 -05:00'
declare #later_dt datetime = '2022-01-01 01:00:00'; -- 1 hour later *if* we ignore the offset
declare #later_dt_casted datetimeoffset = cast(#later_dt as datetimeoffset);
select #dto_minus5, #later_dt_casted, iif(#dto_minus5 < #later_dt, 1, 0);
-- produces 2022-01-01 00:00:00 -05:00 2022-01-01 01:00:00 +00:00 0
fiddle;
What happens in the iif is that #later_dt gets promoted to datetimeoffset, with an offset of 0. When these are then compared, the offset is taken into account. That is to say, the two values are "brought into the same time zone". That means my #dto_minus5 value gets 5 hours added to it (or equivalently, the #later_dt gets 5 hours subtracted).
That is to say, the comparison of:
2022-01-01 00:00:00 -05:00 vs 2022-01-01 01:00:00
becomes a comparison of:
2022-01-01 00:00:00 -05:00 vs 2022-01-01 01:00:00 +00:00
Which in the same time zone is a comparison of:
2022-01-01 05:00:00 +00:00 vs 2022-01-01 01:00:00 +00:00
So the former is larger.
To get the semantics you want, you can use the todatetimeoffset() function:
declare #sample_start_time_est datetimeoffset = '2021-03-10 16:14:00.000 -05:00';
declare #end_time datetime = '2021-03-10 18:14:00.000';
select iif(#sample_start_time_est < todatetimeoffset(#end_time, '-05:00'), 1, 0);
-- prints 1
Related
I have a table1 which stores the data for multiple dates say (between 2022-01-01 to 2022-01-10) and I have another table2 which has time interval and has every 5mins information of dates available in table1 (i.e. 288 records for each day in above table).
Now, How I can write a query for table 2 to calculate time interval for each specific date in table 1. Say I need time intervals between
2022-01-01 12:00:00 to 2022-01-01 02:00:00
2022-01-02 00:05:00 to 2022-01-02 23:00:00
2022-01-04 00:05:00 to 2022-01-10 15:00:00
I tried using DATEDIFF function but that is not giving the results. let's say If I take date 2022-01-02 00:00:00 then my time interval should go back to 1 it should be 2 for 2022-01-02 00:05:00
Below is the example of data:
Table 1:
ID Start date End date
20030917.D0001 2003-09-17 14:10:00 2003-09-18 14:20:00
Table 2:
Date Time interval Amount
2003-09-17 1 150
2003-09-17 2 100
2003-09-17 3 200
2003-09-17 288 250
2003-09-18 1 250
2003-09-18 2 300
2003-09-18 3 1100
2003-09-18 288 150
The time interval in table 2 is every 5 mins of that particular date. Now I need to fetch the data from table 2 which matches with specific date and time in table1
i'm guessing here, if you are looking to return data from table2 with specific time and the table formatted like below
table1
tbl1id
Date
1
2022-01-01
2
2022-01-10
table2
tbl2id
tbl1id
Time
1
1
12:00:00
2
1
12:05:00
3
1
12:10:00
your query should be
select *
from table2
where Time between '12:00:00' and '02:00:00'
and tbl1id
in
(
select tbl1id
from table1
where Date = '2022-01-01'
)
however it you are looking for just looking for calculate time interval in Minute
DECLARE #startdate DATETIME2 = '2022-01-02 00:00:00';
DECLARE #enddate DATETIME2 = '2022-01-02 00:05:00';
SELECT DATEDIFF(Minute, #startdate, #enddate);
that should do it.
select (convert(datetime ,SWITCHOFFSET(CONVERT(datetimeoffset, '2022-02-11 11:15:00.000'),
(select current_utc_offset FROM sys.time_zone_info where name ='Singapore Standard Time')
))) CurrentTime
How can I achieve below expected result ?
Current Result:
CurrentTime
-----------------------
2022-02-11 19:15:00.000
Expecting Result:
CurrentTime
-----------------------
2022-02-11 03:15:00.000
PS: I can't use -8 or -08:00 in the script.
The offsets in sys.time_zone_info are those to switch from UTC+00:00 to another timezone.
So for "Singapore Standard Time" that's +08:00
But the sign can be changed if it needs to switch to UTC timezone.
SELECT (CONVERT(DATETIME,
SWITCHOFFSET(CONVERT(DATETIME, '2022-02-11 11:15:00.000'),
(SELECT TRANSLATE(CURRENT_UTC_OFFSET,'+-','-+') FROM sys.time_zone_info WHERE name ='Singapore Standard Time')
))) AS CurrentTime
Better is to use the AT TIME ZONE syntax :
SELECT
CONVERT(DATETIME, (CONVERT(DATETIME, '2022-02-11 11:15:00.000')
AT TIME ZONE 'Singapore Standard Time'
AT TIME ZONE 'UTC')) AS CurrentTime
Both methods will return the same :
CurrentTime
2022-02-11 03:15:00.000
I have a table called "EventLog" which has the column called nDateTime of type int.
This is the table "EventLog" with some values:
-----------------
| nDateTime |
-----------------
| 978307200 |
-----------------
| 978307219 |
-----------------
| 978513562 |
-----------------
| 978516233 |
-----------------
| 978544196 |
-----------------
| 1450379547 |
-----------------
| 1472299563 |
-----------------
| 1472299581 |
-----------------
| 1472300635 |
-----------------
| 1472300644 |
-----------------
| 1472300673 |
-----------------
I need to get the DateTime value, and I tried the following statements, but I receive these errors:
Test #1:
SELECT CONVERT(DATETIME, CONVERT(CHAR(8), nDateTime), 103) AS 'Formatted date'
FROM EventLog
The error says:
Conversion failed when converting date and/or time from character string.
Test #2: modified from here:
SELECT CONVERT(DATETIME, nDateTime, 103) AS 'Formatted date'
FROM EventLog
And Test #3 goes:
SELECT CAST(nDateTime AS datetime) AS 'Formatted date'
FROM EventLog
The duplicate question doesn't answer my question because (both, test #2 and test #3) generates this error:
Arithmetic overflow error converting expression to data type datetime.
I admit that I never saw such value as a Date, and for that, I'm kind of confused in how to proceed.
My question is: How can get the valid DateTime value from the sample data?
Almost every time you see a date/time represented as an integer, that number represents the passage of time since a known epoch. This is the basis of Unix time which is, put simply, the number of seconds which have elapsed since 1st January 1970 00:00:00
Using this, we can check with some values you have provided
declare #dt DATETIME = '1970-01-01' -- epoch start
print dateadd(second,978307200,#dt ) -- Jan 1 2001 12:00AM
print dateadd(second,1472300673,#dt ) -- Aug 27 2016 12:24PM
Seems possible, but who knows?!
You can check every date in your table simply using
declare #dt DATETIME = '1970-01-01' -- epoch start
SELECT
nDateTime AS OriginalData,
DATEADD(second, nDateTime,#dt) AS ActualDateTime
FROM EventLog
Just for giggles, I took a stab at having the base date of 1970-01-01, but without KNOWING the base, it is just a guess
Declare #Log table (DateInt int)
Insert Into #Log values
(978307200),
(978307219),
(978513562),
(978516233),
(978544196),
(1450379547),
(1472299563),
(1472299581),
(1472300635),
(1472300644),
(1472300673)
Select DateInt,Converted= DateAdd(SECOND,DateInt,'1970-01-01') From #Log
Returns
DateInt Converted
978307200 2001-01-01 00:00:00.000
978307219 2001-01-01 00:00:19.000
978513562 2001-01-03 09:19:22.000
978516233 2001-01-03 10:03:53.000
978544196 2001-01-03 17:49:56.000
1450379547 2015-12-17 19:12:27.000
1472299563 2016-08-27 12:06:03.000
1472299581 2016-08-27 12:06:21.000
1472300635 2016-08-27 12:23:55.000
1472300644 2016-08-27 12:24:04.000
1472300673 2016-08-27 12:24:33.000
The "2038" Problem with Unix Timestamps
There's a serious issue with writing code to convert UNIX Timestamps that are based on seconds... DATEADD can only handle INTs and that brings us to the "2038/Y2K38/Friday the 13th" problem (the day of the week when then "wraparound" to the most negative number an INT can have happens after the date below).
That means that the largest positive value it can handle is 2147483647. If we use DATEADD to add that number of seconds to the UNIX Epoch of the first instant of the year 1970, we end up with a DATETIME that clearly explains what they mean by the "2038" issue.
SELECT DATEADD(ss,2147483647,'1970');
The Standard Fix for the "2038" Problem
The standard way to get around that is to first store the UNIX Timestamp as a BIGINT and do two date adds... one for seconds and one for days.
There are 84600 seconds in a day. If we do Integer Division and ...
Use the Quotient to derive the number of days to add to
'1970'...
And use the Remainder to derive the number of
seconds to add to '1970'...
... we'll get the correct date not only for the MAX INT value...
DECLARE #SomeUnixTS BIGINT = 2147483647
,#SecsPerDay BIGINT = 86400
;
SELECT DATEADD(ss,#SomeUnixTS%#SecsPerDay,DATEADD(dd,#SomeUnixTS/#SecsPerDay,'1970'))
;
... but also for the last possible date in seconds for SQL Server. If we calculate the UNIX Timestamp (in seconds) for the last possible second that's available in SQL Server...
SELECT DATEDIFF_BIG(ss,'1970','9999-12-31 23:59:59');
... it still works with lots of room to spare and no "2038" problem.
DECLARE #SomeUnixTS BIGINT = 253402300799
,#SecsPerDay BIGINT = 86400
;
SELECT DATEADD(ss,#SomeUnixTS%#SecsPerDay,DATEADD(dd,#SomeUnixTS/#SecsPerDay,'1970'))
;
UNIX Timestamps Based on Milliseconds
Working with UNIX Timestamps that are based on Milliseconds are only slightly different but must be handled the same way...
DECLARE #SomeUnixTS BIGINT = DATEDIFF_BIG(ms,'1970','9999-12-31 23:59:59.999')
,#msUnixEpoch DATETIME2(3) = '1970'
,#msPerDay BIGINT = 86400000
;
SELECT SomeUnixTS = #SomeUnixTS
,msUnixEpoch = #msUnixEpoch
,Converted = DATEADD(ms,#SomeUnixTS%#msPerDay,DATEADD(dd,#SomeUnixTS/#msPerDay,#msUnixEpoch))
;
As a bit of a sidebar, you have to wonder what Microsoft was or was not thinking when they created DATEDIFF_BIG() but didn't create a DATEADD_BIG(). Amazing even more is the they have SQL Server that will work in a UNIX environment and still no CONVERT(ts) functionality.
Here's whats new in 2022 in the area I'm talking about...
https://learn.microsoft.com/en-us/sql/sql-server/what-s-new-in-sql-server-2022?view=sql-server-ver16#language
And, last but not least, do not convert UNIX Timestamps that are based on milliseconds directly to DATETIME because the rounding in DATETIME can take you to the next day, week, month, and even year. You must do a "units place" detection for "9" and "-1" and make the appropriate substitution of "7" and "-3" respectively.
Your input is > 8 digits hence it is throwing arithmentic overflow error.. If it is 8 digits you will get converted data:
For Example:
DECLARE #ndatetime int = 978307200
SELECT CONVERT(datetime, convert(varchar(10), #ndatetime, 112))
-- this throws arithmetic overflow error
DECLARE #ndatetime int = 97830720 -- with 8 digits only
SELECT CONVERT(datetime, convert(varchar(10), #ndatetime, 112))
This returns converted date
You can try try_convert which will return null if it is wrong date
DECLARE #ndatetime int = 978307200
SELECT TRY_CONVERT(datetime, convert(varchar(10), #ndatetime, 112))
I'm struggling with what I thought would be a simple SQL query. Running SQL Server 2014
I have an SQL table, "Visits":
Id | EntryTime | Duration
And I want to find the average entry TIME OF DAY between two dates, taking into account all records between those dates.
so if my EntryTime field between my dates is:
2016-04-28 12:00:00
2016-04-20 10:00:00
2016-04-19 08:00:00
2016-04-17 10:00:00
Then the average time returned should just be:
10:00:00
The date should not be taken into account at all, and it should be returned in string format, or a manner which returns ONLY 10:00:00.
create table mytimes(
id int identity,
mydatetime datetime
)
insert into mytimes (mydatetime) values ('2016-04-28 12:00:00')
insert into mytimes (mydatetime) values ('2016-04-20 10:00:00')
insert into mytimes (mydatetime) values ('2016-04-19 08:00:00')
insert into mytimes (mydatetime) values ('2016-04-17 10:00:00')
SELECT Cast(DateAdd(ms, AVG(CAST(DateDiff( ms, '00:00:00', cast(mydatetime as time)) AS BIGINT)), '00:00:00' ) as Time )
from mytimes
-- where mydatetime between XXX and YYY
SELECT convert(varchar(8), Cast(DateAdd(ms, AVG(CAST(DateDiff( ms, '00:00:00', cast(mydatetime as time)) AS BIGINT)), '00:00:00' ) as Time ))
from mytimes
-- where mydatetime between XXX and YYY
output-1 10:00:00.0000000 - this is an actual Time type that you can do more with if needed
output-2 10:00:00 - this is output as a varchar(8)
Add your where clause as you see fit
The steps include
Casting to a Time type from a DateTime.
Using the AVG on Time, this is not supported by type Time so you have to first convert Time to milliseconds.
Converting the milliseconds back to a Time type
To avoid Arithmetic overflow error converting expression to data type int you can cast the result of DateAdd to a BigInt. Alternatively you can use seconds instead of milliseconds in the DateDiff function which should work unless your result set is overly large.
SO Sources:
T-SQL calculating average time
How to get Time from DateTime format in SQL?
Operand data type time is invalid for avg operator…?
SELECT CONVERT(TIME, DATEADD(SECOND, AVG(DATEDIFF(SECOND, 0, CONVERT(TIME, EntryTime ))), 0))
FROM Visits
WHERE EntryTime >= #begin AND EntryTime <= #end
The idea came from here: T-SQL calculating average time
Yap, you can use the Time() to get this done.
Your query becomes like this (modify accordingly)
SELECT COUNT(*) FROM Visits WHERE TIME(EntryTime) > '06:00' AND EntryTime < '18:00';
I want to get last month's last date (whether 30 or 31) and time till last second, whenever the query is executed.
Eg. 11/30/2015 11:59:59 PM
So I have a query like
SELECT DATEADD(ss, (60*60*24)-1, DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), -1))
It solves my problem. But what is the difference between the query written above and the one below, when I change the DATEDIFF part and replace 0 with 1?
SELECT DATEADD(ss, (60*60*24)-1, DATEADD(MONTH, DATEDIFF(MONTH, 1, GETDATE()), -1))
Will both of these queries gives the same result whenever they are run, or which should I consider as the permanent solution?
Do NOT do this; attempt to get the "last second" of the last day of the previous month
I make this bold statement on the assumption you are attempting to use BETWEEN and you are concerned with the accuracy of something like this:
select sum(value) from Atable
where [Adate] BETWEEN '20151201' AND '21051231 23:59:59'
But the complexity of arriving at the last point in time on the last day of any month is solved so easily by using the first day of the next month instead. All that you also need to do is drop the use of BETWEEN. Like this:
select sum(value) from Atable
where [Adate] >= '20151201' and [Adate] < '21060101'
LESS THAN "the first day of the this month"
That is how you solve your conundrum.
& by the way: The precision (accuracy) of smalldatetime, datetime and datetime2 all differ, all the more reason not to use BETWEEN.
see "Be careful about rounding errors." at http://sqlmag.com/t-sql/t-sql-best-practices-part-2
Specifically, do this:
DateLogged < SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)
This will be 100% accurate for date, smalldatetime, datetime and datetime2 columns.
Here is another attempt to explain why LESS THAN [the_next_day_at_00:00:00+0000000] is accurate and using 22:59:59 is NOT accurate. Please take note of the sample data accuracy
SQL Fiddle
MS SQL Server 2014 Schema Setup:
Query 1:
DECLARE #Tbl TABLE
( [ID] int identity(1,1)
, [DT_a] datetime
, [DT_b] datetime
, [DT_c] datetime2
)
INSERT INTO #Tbl
([Dt_a], [Dt_b], [Dt_c])
VALUES
(
'20151231 23:59:59'
, '20151231 23:59:59.997'
, '20151231 23:59:59.9999999'
)
select
'where [DT_b] <= 20151231 23:59:59' as FilterString
, max([Dt_a]) as [Dt_a]
, max([Dt_b]) as [Dt_b]
, max([Dt_c]) as [Dt_c]
from #Tbl
where [DT_b] <= '20151231 23:59:59'
UNION ALL
select
'where [DT_b] < 20160101'
, max([Dt_a]) as [Dt_a]
, max([Dt_b]) as [Dt_b]
, max([Dt_c]) as [Dt_c]
from #Tbl
where [DT_b] < '20160101'
Results:
| FilterString | Dt_a | Dt_b | Dt_c |
|-----------------------------------|----------------------------|----------------------------|-----------------------------|
| where [DT_b] <= 20151231 23:59:59 | (null) | (null) | (null) |
| where [DT_b] < 20160101 | December, 31 2015 23:59:59 | December, 31 2015 23:59:59 | 2015-12-31 23:59:59.9999999 |
Data accuracy
smalldatetime: one minute
datetime: rounded to increments of .000, .003, or .007 seconds
datetime2: 100 nanoseconds
To avoid possible errors from rounding by time units DO NOT USE <= 23:59:59
Instead use LESS THAN [the_next_day]
AND, as a consequence AVOID USING BETWEEN for date ranges.
See this link for how DATEDIFF is used or should be used in SQL Server. The 2nd argument, the one which does not seem to make a difference in your case, is supposed to be the start date which is subtracted from the end date (getdate()) to get the difference and then converted to months. I would try and use this function the typical way and provide a proper start date.
Also below is an alternative way of getting the same result
SELECT DATEADD(ss, -1, '01/' + CONVERT(VARCHAR, DATEPART(MONTH, getdate())) + '/' + CONVERT(VARCHAR, DATEPART(YEAR, getdate())));
It is because of DATEDIFF( MONTH, 0, GETDATE()) Function
If you use an integer as the second argument, this is interpreted as the number of days since 1900-01-01 regardless of the Interval you are using in the datediff function.
For eg:
SELECT YEAR(0), MONTH(0), DAY(0);
year month day
1900 1 1
Now if I Increment 0 to 1 in year, month, day
SELECT YEAR(1), MONTH(1), DAY(1);
year month day
1900 1 2
Now if I Increment values to 365,
SELECT YEAR(365), MONTH(365), DAY(365);
year month day
1901 1 1
You can see the Year got incremented by 1.
there are many ways to find out the previous month's last date. Here is the one I am using.
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE()),0))
Well it would be expected that substracting one millisecond to the first day of the current month you would get the last millisecond of the previous month but it doesn't work that whay, with datediff millisecond,-1 you still get the first day of the month you have to do datediff millisecond,-2 to reach 997 milliseconds, no way to get 999 nor 998.(without using text).
select dateadd(MILLISECOND,-2,dateadd(month, datediff(month, 0, getdate()), 0))
And you get 2020-01-31 23:59:59.997
To get last second of current month use:
SELECT DATEADD(MILLISECOND, -10, CAST (EOMONTH(GETDATE()) AS DATETIME))
and you get:
2021-12-30 23:59:59.000
Explanation: takes begin of next month (2021-12-31) and convert to datetime (2021-12-31 00:00:00.000) then takes 1 second to get (2021-12-30 23:59:59.000)