I'm stuck. Need help.
I store UTC dates in database.
Example rows:
GeneratedAt:
2011-06-08 23:30
2011-06-09 03:30
2011-06-09 15:30
Local time for my user is -2 hours (Central Europe). When I want rows from 09 then I have 3 rows.
Problem is with GROUP BY day for reporting purposes. I have 1 for 08 and 2 for 09 but this is not true for my local time.
Everywhere I see: "store data in UTC". How to do this properly?
UPDATE 1:
For data access I'm using NHibernate and I prefer solution independent of the database engine. So, I'm looking for solution with something like Date/Time Dimension table (or something like that).
My data table has columns like this:
GeneratedAt (datetime)
GeneratedAt_Year (int)
GeneratedAt_Month (int)
GeneratedAt_Day (int)
GeneratedAt_Hour (int)
Thanks to that I can easily grouping by: year, year+month, year+month+day, year+month+day+hour. Unfortunately this is UTC. :(
How to refactor this solution to deal with user timezones?
You could create a view of that table which provides the datetime value in the desired Central Europe timezone by applying a DATEADD function.
So if your table columns are: id, other_id, evt_time
then the select statement (for the view definition) would be:
SELECT id, other_id, evt_time, DATEADD( hh, -2, evt_time ) AS evt_time_ce
FROM MyTable
then you can use the view and apply the GROUP_BY to the evt_time_ce column
I have a similar issue in general, but my time difference is 8 hours.
I use dateadd(hour,8, [Timestamp]) to select the local time, and dateadd(hour,-8, #dateFrom) in WHERE clauses - which should work for GROUP BY as well.
For example:
DECLARE #dateFrom datetime, #dateUntil datetime
SET #dateFrom = '2011-06-20 00:00:02.000'
SET #dateUntil = '2011-06-22 10:00:00.000'
SELECT TOP 100
dateadd(hour, 8, [Timestamp]) LocalTime,
*
FROM [Log] L (nolock)
WHERE L.[Timestamp] BETWEEN dateadd(hour, -8, #dateFrom) AND dateadd(hour, -8, #dateUntil)
ORDER BY LogID DESC
SQL Server 2016 has introduced AT TIME ZONE that can help with this, including handling daylight savings time.
AT TIME ZONE lets you convert dates between time zones listed in the windows registry. You can see what timezones are available by running a simple SELECT:
select * from sys.time_zone_info
Here's the SQL that converts your UTC stored date into a date at another timezone
GeneratedAt
AT TIME ZONE 'UTC'
AT TIME ZONE 'Central Europe Standard Time'
So your SQL to group by day based on a local timezone might look something like this:
SELECT DATEADD(DAY,0, DATEDIFF(DAY, 0, GeneratedAt
AT TIME ZONE 'UTC'
AT TIME ZONE 'Central Europe Standard Time'))
FROM YourTable
GROUP BY DATEADD(DAY,0, DATEDIFF(DAY, 0, GeneratedAt
AT TIME ZONE 'UTC'
AT TIME ZONE 'Central Europe Standard Time'))
Here's a good article about the topic if you want to read more.
Related
I have user created records that I am storing the create date in UTC in a sql database.
I want to allow the user to select a date on the UI that will pull the records for that day.
My local time is -4:00 from the UTC time. So here is my issue:
If a user created a record at 9:00AM local time (1:00PM UTC), and then another record at 10:00PM local time (2:00AM UTC the next day), how do I query both those records out when they select that day?
The records are stored in UTC so they fall in 2 separate days, but they are actually in the same day for the local time.
How would I solve this? Should I even be storing the records in UTC?
Thanks
Is the -4 hours difference always there, or do you have multiple users from different time zones? If first one applies, consider dateadd. For example:
SELECT dateadd(hour, -4, yourTimeStamp) as TimeStamp
, CAST(dateadd(hour, -4, yourTimeStamp) as DATE) as DateOfTimeStamp
FROM YourTable
WHERE CAST(dateadd(hour, -4, yourTimeStamp) as DATE) = '20160510'
EDIT:
You can use:
SELECT DATEDIFF(MINUTE, SYSDATETIME(), GETDATE()) AS MinutesOffsetUTCtoLocal
to get the time difference in minutes between the client and the machine. Integrating this would look like this:
SELECT dateadd(minute, DATEDIFF(MINUTE, SYSDATETIME(), GETDATE()), yourTimeStamp) as TimeStamp
, CAST(dateadd(minute, DATEDIFF(MINUTE, SYSDATETIME(), GETDATE()), yourTimeStamp) as DATE) as DateOfTimeStamp
FROM YourTable
WHERE CAST(dateadd(minute, DATEDIFF(MINUTE, SYSDATETIME(), GETDATE()), yourTimeStamp) as DATE) = '20160510'
I suggest to build a calendar table with the following columns (will be handy for other things too) - in my examples it is called: local_calendar
local_date DATE -- This marks the day (without time)
date_starts_utc DATETIME -- Local midnight converted to UTC
date_ends_utc DATETIME -- Local midnight of the next day converted to UTC.
Fill this table from and to a reasonable date range.
From now, you can join this table to any UTC time to get the local date and any local dates to utc time boundaries.
To get the local date for a utc time
SELECT local_date
FROM local_calendar
WHERE #utcTime >= date_starts_utc AND #utcTime < date_ends_utc
To get the UTC boundaries for a local date
SELECT #lowerBoundary = date_starts_utc, #higherBoundary = date_ends_utc
FROM local_calendar
WHERE date = #localDate
I have one table in SQL Server where I want to add a column that will show this year's date for the exact transition dates for European Summer Time.
European Summer Time is observed across three time zones, beginning at 01:00 UTC/WET on the last Sunday in March and ending at 01:00 UTC on the last Sunday in October each year.
How do you change this into a SQL function?
On Wikipedia I was able to find this:
The formula used to calculate the beginning of European Summer Time is
Sunday (31 − ((((5 × y) ÷ 4) + 4) mod 7)) March at 01:00 UTC
https://en.wikipedia.org/wiki/Summer_Time_in_Europe
Be careful. DST is a non-technical, but political policy and can vary over time (countries change DST adjustments all the time) which requires an up-to-date timezone database.
SQL Server itself is not concerned with timezones: it does not perform any timezone conversion itself, only UTF-offset conversion (when using datetimeoffset when the instantaneous local-to-UTC conversion is known). It cannot tell you anything about zone locations or future offsets, as those are arguably business rules that belong outside the domain of the persistence layer (i.e. SQL Server). When storing values in a database always used either UTC datetime or datetimeoffset values. Use UTC datetime when zone information is irrelevant to the user (i.e. don't invent your own datetime+int system, use datetimeoffset).
Along those lines, SQL Server does not provide any API or functionality for accessing the system (the Windows OS') timezone database from T-SQL code.
You would need to do this from application code. In .NET you can use the TimeZoneInfo class which uses Windows' timezone database to get the DST dates:
TimeZoneInfo.AdjustmentRule.DateStart
TimeZoneInfo.AdjustmentRule.DateEnd
TimeZoneInfo.AdjustmentRule.DaylightTransitionStart
TimeZoneInfo.AdjustmentRule.DaylightTransitionEnd
https://msdn.microsoft.com/en-us/library/system.timezoneinfo.adjustmentrule(v=vs.110).aspx
Depending on what the criteria you're inputting into whatever function you want to produce here is, the specific code is going to be different.
The following, for example, produces a list of the dates/times for the next 100 years in the timezone this is run in.
; WITH C1(N) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) T(N))
, C2(N, RN) AS (SELECT 1, ROW_NUMBER() OVER (ORDER BY NEWID()) FROM C1 a CROSS JOIN C1 b)
, TYears AS (SELECT YEAR(GETDATE()) + RN - 1 [Years] FROM C2)
SELECT DATEADD(hh
, DATEDIFF(hh, GETUTCDATE(), GETDATE())
, DATEADD(dd
, 0 - (1 + DATEDIFF(dd, 0, CONVERT(DATE, CONVERT(NVARCHAR, [Years]) + '-03-31')) % 7) % 7
, CONVERT(DATETIME, CONVERT(NVARCHAR, [Years]) + '-03-31 01:00:00'))) [StartDateTime]
, DATEADD(hh
, DATEDIFF(hh, GETUTCDATE(), GETDATE())
, DATEADD(dd
, 0 - (1 + DATEDIFF(dd, 0, CONVERT(DATE, CONVERT(NVARCHAR, [Years]) + '-10-31')) % 7) % 7
, CONVERT(DATETIME, CONVERT(NVARCHAR, [Years]) + '-10-31 01:00:00'))) [EndDateTime]
FROM TYears
The basic premise being find the last Sunday of March for that year, find the UTC +/- value, add it to 1AM of the last Sunday of March.
I am new to SQL and Programming in general. I have a datetime field which I want to use to filter my results but only on the time portion.
For example I need to run a report from 2pm yesterday to 7am current day. I cannot hard code the dates because this report needs to run daily. This Query will run from a stored procedure automatically.
I have tried AND clm.createDtTm > DATEADD(d, -1, GETDATE()), which goes back one day but not the time range I need.
I tried this: AND (datepart(hh, '11:02:54.107') = 7)<--To be honest not even sure what I am doing here.
I am not sure if this is even possible, but if I can get results for one day back I am assuming there has to be a way to narrow that day between hours.
Any help would be appreciated.
Thanks,
Abdul
This should do the trick, it filters out rows that do not fall in the date interval, from 7pm yesterday to 7am today.
WHERE
createDtTm >= DATEADD(HOUR, 14,CAST(DATEADD(DAY,-1, CAST(GETDATE() AS DATE)) AS DATETIME))
AND createDtTm <= DATEADD(HOUR, 7, CAST(CAST(GETDATE() AS DATE) AS DATETIME))
I suggest something like this:
SELECT(DATEADD(d,-1,GETDATE()))
FROM table
WHERE (DATEADD(d,-1,GETDATE())) = rowloadtimestamp
HAVING DATEPART(HH,rowloadtimestamp) BETWEEN 13 AND 24
UNION
SELECT GETDATE()
FROM table
WHERE GETDATE() = rowloadtimestamp
HAVING DATEPART(HH,GETDATE()) BETWEEN 1 AND 7
Assuming you have a rowloadtimestamp field this should work for you.
What do you mean with „I can not hardcode the dates” ? Do you mean the time interval is variable, or is it fixed, or are you referring to the exact dates? I'm not sure I understand.
In case of MySQL you can do something like:
SELECT data, date FROM table WHERE date < DATE_SUB(NOW(), INTERVAL 2 DAY);
This Query will get you the last two days. You can vary the interval by replacing the „2” with a variable if that is possible in your case.
I am trying to add days to the current date and it's working fine but when I add 360 days to the current date it gives me wrong value.
eg: Current Date is 11/04/2014
And I am adding 360 Days to it, it should give me 11/04/2015, but it is showing the same date 11/04/2014. the year is not changing.
Here is my code:
select dateadd(dd,360,getdate())
Just do-
Select (Getdate()+360) As MyDate
There is no need to use dateadd function for adding or subtracting days from a given date. For adding years, months, hours you need the dateadd function.
select dateadd(dd,360,getdate()) will give you correct date as shown below:
2017-09-30 15:40:37.260
I just ran the query and checked:
Dateadd(datepart,number,date)
You should use it like this:
select DATEADD(day,360,getdate())
Then you will find the same date but different year.
From the SQL Server 2017 official documentation:
SELECT DATEADD(day, 360, GETDATE());
If you would like to remove the time part of the GETDATE function, you can do:
SELECT DATEADD(day, 360, CAST(GETDATE() AS DATE));
In SQL Server 2008 and above just do this:
SELECT DATEADD(day, 1, Getdate()) AS DateAdd;
can try this
select (CONVERT(VARCHAR(10),GETDATE()+360,110)) as Date_Result
Two or three ways (depends what you want), say we are at Current Date is
(in tsql code) -
DECLARE #myCurrentDate datetime = '11Apr2014 10:02:25 AM'
(BTW - did you mean 11April2014 or 04Nov2014 in your original post? hard to tell, as datetime is culture biased. In Israel 11/04/2015 means 11April2014. I know in the USA 11/04/2014 it means 04Nov2014. tommatoes tomatos I guess)
SELECT #myCurrentDate + 360 - by default datetime calculations followed by + (some integer), just add that in days. So you would get 2015-04-06 10:02:25.000 - not exactly what you wanted, but rather just a ball park figure for a close date next year.
SELECT DateADD(DAY, 365, #myCurrentDate) or DateADD(dd, 365, #myCurrentDate)
will give you '2015-04-11 10:02:25.000'. These two are syntatic sugar (exacly the same). This is what you wanted, I should think. But it's still wrong, because if the date was a "3 out of 4" year (say DECLARE #myCurrentDate datetime = '11Apr2011 10:02:25 AM') you would get '2012-04-10 10:02:25.000'. because 2012 had 366 days, remember? (29Feb2012 consumes an "extra" day. Almost every fourth year has 29Feb).
So what I think you meant was
SELECT DateADD(year, 1, #myCurrentDate)
which gives 2015-04-11 10:02:25.000.
or better yet
SELECT DateADD(year, 1, DateADD(day, DateDiff(day, 0, #myCurrentDate), 0))
which gives you 2015-04-11 00:00:00.000 (because datetime also has time, right?). Subtle, ah?
This will give total number of days including today in the current month.
select day(getDate())
Add Days in Date in SQL
DECLARE #NEWDOB DATE=null
SET #NEWDOB= (SELECT DOB, DATEADD(dd,45,DOB)AS NEWDOB FROM tbl_Employees)
SELECT DateAdd(5,day(getdate()) this is for adding 5 days to current days.
for eg:today date is 23/08/2018 it became 28/08/2018 by using the above query
The scenario is this:
select max date from some table, when the target table having no data, so the max date is null. when the date being null, I want to get the earliest date of system, so the epoch time seems perfect.
I have searched some ways including DATEADD functions, but that seems not elegant.
If I understand your question correctly, in SQL Server the epoch is given by cast(0 as datetime) :
select Max(ISNULL(MyDateCol, cast(0 as datetime)))
from someTable
group by SomeCol
The earliest date that can be stored in a SQL datetime field depends on the data type you use:
datetime:
1753-01-01 through 9999-12-31
smalldatetime:
1900-01-01 through 2079-06-06
date, datetime2 and datetimeoffset:
0001-01-01 through 9999-12-31
For more exact details see from https://msdn.microsoft.com/en-GB/library/ms186724.aspx#DateandTimeDataTypes
The epoch is useful if you are converting numbers to dates, but irrelevant if you are storing dates as dates.
If you don't want to deal with nulls properly the simple answer is to pick a date that you know will be before your data and hard-code it into your query. cast('1900-01-01' as datetime) will work in most cases.
While using something like cast(0 as datetime) produces the same result it obscures what you have done in your code. Someone maintaining it, wondering where these odd old dates come from, will be able to spot the hard coded date more quickly.
If you define the epoch as Jan 1, 1970: The better: to store it in a variable
DECLARE #epoch DATETIME
SET #epoch = CONVERT( DATETIME, '01 JAN 1970', 106 )
select
DATEPART(YEAR, DATEADD(day, 180, #epoch)) as year,
...