What is a preferred way to store recurring time windows?
For example. If I have a calendar system where I need to be able to accommodate daily, weekly or monthly recurring events, what sort of time management system is best?
How is this best represented in a database?
More Details
The Specific goal of this is to provide sets of open time windows. Once we have these time windows, the code needs to test if a message that arrives to the system falls within one of the time windows.
I would create a RecurrenceType table which would hold records for each type of recurrence that your calendar system supported. Each event in the CalendarEvents table would have a reference to the ID in the RecurrenceType table.
I would then have a RecurrenceRules object in the code which would hold the business logic of calculating when the next date will be given a current or starting date or retrieve the next N dates as an array.
Most of the recurrence logic would probably have to be coded off enumerated values which are based on the RecurrenceType table's ID. I would be surprised if you could make it all data driven unless it was a very simple calendar system.
Hope that helps. It sounds like an interesting project.
This is long delayed, but after much testing and many heartaches, we decided on a certain scheme to make this work. For the needs of our system, we are required only to recur on a weekly basis if at all. The final product will be designed as such.
Here is the DB schema
id : int(10)
window_name : varchar(100)
start_date : datetime
end_date : datetime
start_time : time
duration : int(10)
timezone: varchar(100)
monday : tinyint(1)
tuesday : tinyint(1)
...
sunday : tinyint(1)
Each "window" of time will have a set start date, end date, start time and a duration.
The window will open on the start date and start time and run for "duration" seconds
The window will only open on specified days of the week
This is the only system that would allow windows to open and close on a recurring weekly basis and also have windows that would span into the next morning. I am sure there are more complex systems out there that do recurring windows, but we were looking for something flexible and fast, and did not need monthly recurring events or yearly events.
Related
I having an column of UNIX time stamp in my database table, which comes from a system that is in the Kuwait time zone.
My database server's time zone is Eastern Time US & Canada. Now I need to convert the UNIX time stamp in to Kuwait time zone date value using an SQL query.
Can anyone tell me how I can convert this UNIX time stamp into a Kuwait time zone date value?
Unix timestamps are integer number of seconds since Jan 1st 1970 UTC.
Assuming you mean you have an integer column in your database with this number, then the time zone of your database server is irrelevant.
First convert the timestamp to a datetime type:
SELECT DATEADD(second, yourTimeStamp, '1970-01-01')
This will be the UTC datetime that corresponds to your timestamp.
Then you need to know how to adjust this value to your target time zone. In much of the world, a single zone can have multiple offsets, due to Daylight Saving Time.
Unfortunately, SQL Server has no ability to work work time zones directly. So if you were, for example, using US Pacific time, you would have no way of knowing if you should subtract 7 hours or 8 hours. Other databases (Oracle, Postgres, MySql, etc.) have built-in ways to handle this, but alas, SQL Server does not. So if you are looking for a general purpose solution, you will need to do one of the following:
Import time zone data into a table, and maintain that table as time zone rules change. Use that table with a bunch of custom logic to resolve the offset for a particular date.
Use xp_regread to get at the Windows registry keys that contain time zone data, and again use a bunch of custom logic to resolve the offset for a particular date. Of course, xp_regread is a bad thing to do, requires certain permissions granted, and is not supported or document.
Write a SQLCLR function that uses the TimeZoneInfo class in .Net. Unfortunately, this requires an "unsafe" SQLCLR assembly, and might cause bad things to happen.
IMHO, none of these approaches are very good, and there is no good solution to doing this directly in SQL. The best solution would be to return the UTC value (either the original integer, or the datetime at UTC) to your calling application code, and do the timezone conversion there instead (with, for example, TimeZoneInfo in .Net or similar mechanisms in other platforms).
HOWEVER - you have lucked out in that Kuwait is (and always has been) in a zone that does not change for Daylight Saving Time. It has always been UTC+03:00. So you can simply add three hours and return the result:
SELECT DATEADD(hour, 3, DATEADD(second, yourTimeStamp, '1970-01-01'))
But do recognize that this is not a general purpose solution that will work in any time zone.
If you wanted, you could return one of the other SQL data types, such as datetimeoffset, but this will only help you reflect that the value is three hours offset to whomever might look at it. It won't make the conversion process any different or better.
Updated Answer
I've created a project for supporting time zones in SQL Server. You can install it from here. Then you can simply convert like so:
SELECT Tzdb.UtcToLocal('2015-07-01 00:00:00', 'Asia/Kuwait')
You can use any time zone from the IANA tz database, including those that use daylight saving time.
You can still use the method I showed above to convert from a unix timestamp. Putting them both together:
SELECT Tzdb.UtcToLocal(DATEADD(second, yourTimeStamp, '1970-01-01'), 'Asia/Kuwait')
Updated Again
With SQL Server 2016, there is now built-in support for time zones with the AT TIME ZONE statement. This is also available in Azure SQL Database (v12).
SELECT DATEADD(second, yourTimeStamp, '1970-01-01') AT TIME ZONE 'Arab Standard Time'
More examples in this announcement.
I am storing timestamps in database as expressed in UTC, e.g. '2015-03-27 08:32:46.024 +00:00'
And I am using the current servers timezone to convert the timestamps to local time.
select
SWITCHOFFSET (CAST(CAST('2015-03-27 08:32:00.000 +00:00' AS datetime2(3)) as DateTimeOffset(3)), DATEPART(TZ, SYSDATETIMEOFFSET ()) )
This works fine as long as I look to wintertime timestamps in wintertime.
For last Fridays time wintertime timestamp get expressed in summer time: '2015-03-27 10:32:00.000 +02:00'.
I would like to see 2015-03-27 09:32:00.000 +01:00.
Anyone that has a solution without storing winter- and summertime in the database?
As I mentioned in the question's comments, this is usually best done in application code, rather than in the database. However, there are some scenarios where having it in the database can be quite useful, or necessary. For example, you may need to group by a date in a specific time zone, or you may need to convert many items in bulk, or generate data for a reporting system.
For these, you can use my SQL Server Time Zone Support project.
Follow the installation instructions, then you can do this:
SELECT Tzdb.UtcToLocal('2015-03-27 08:32:46.024', 'Europe/Paris')
I am guessing at your time zone. You can choose any from the list here.
I would love to insert a default value into a column with data type datetime2(7). However, because my website is hosted on a server in a different timezone, the getdate function doesn't work properly. I wonder if there is a solution to this. I have done some research and found two ways.
First is to use GetUTCDate() function. However, I would need to do the conversion when I display the information. I am sure my web application is used for only my timezone. So I would like to avoid this.
Second way, this is the closest I could get this done by using SwitchOffSet function:
CREATE TABLE [dbo].[Test_Date](
[test_id] [int] NOT NULL,
[test_date] [datetime2](7) NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Test_Date] ADD CONSTRAINT [DF_Test_Date_test_date] DEFAULT (switchoffset(CONVERT([datetimeoffset],getutcdate()),'+13:00')) FOR [test_date]
GO
However, my problem is the +13:00 cause in the next few months, it will be +12:00 cause of the day light saving time change. As a result, I would need to change it every time. Anybody has a solution to this?
Thanks.
SELECT GETDATE() AT TIME ZONE 'UTC' AT TIME ZONE 'Central Standard Time'
You need the first 'AT TIME ZONE UTC' in order to tell the DB what the value currently is in, so it knows how to get to the second given time zone, 'Central Standard Time' in my example.
You can use SYSDATETIMEOFFSET function
select SYSDATETIMEOFFSET()
MSDN description:
Returns a datetimeoffset(7) value that contains the date and time of the computer on which the instance of SQL Server is running. The time zone offset is included.
More on MSDN.
Based on clarification in the comment below:
Because you want to store the local time of the client, SQL Server has no way of knowing what is your local time. The best option that would work best would be to send the current time from the client each time.
Since Sql Server 2016 you can use AT TIME ZONE...
SELECT CONVERT(datetime2(0), '2015-03-29T01:01:00', 126)
AT TIME ZONE 'Central European Standard Time';
... as specified in the documentation
Just store the data in UTC, and use a calendar table to calculate offsets when you read the data (see these tips: part 1, part 2, part 3). Related Q & A:
GET UTC Date of a past date
Daylight saving time and time zone best practices
Best Practices working with Datetimeoffset
how to convert all datetime columns in a sql server 2005 express database with data to UTC
Where to set a UTC datetime value in n-tier application: Presentation Layer, Domain, or Database?
How do I handle the timezones for every Chat message
since the db timezone info is different with your web server, its best you explicitly pass your desired datetime value from your web app to the db, instead of using db server-side default function.
Getdate() function to get date for Japan Standard Time Zone[JST]
SELECT CAST( GETDATE() AT TIME ZONE 'Asia/Tokyo' AS Date )
I am creating a table for an application that handles scheduling and deals with recurring events. I need to indicate which days and times an event may recur on (eg. every Monday and Wednesday at 1pm). Is there a datatype that can handle only days of the week without inputting specific dates, or will I need to create another table containing days of the week and reference these with a FK? This is obviously undesirable because it will make handling events that start before midnight and end after midnight more complex.
Use a smallint for the Day of Week.
If you are using SQL Server 2008, there is a new time datatype for the time of day column, otherwise you will still need to use a datetime datatype.
Looks like you need something similar to this:
CREATE TABLE T (
DAY_OF_WEEK TINYINT CHECK (DAY_OF_WEEK BETWEEN 1 AND 7)
-- Other fields and constraints...
)
I'm creating an online Calendar app (like Google Calendar or MS Outlook), and not sure how I should store the data to make it fast to query for the basic views: Daily, Weekly, Monthly.
Before you mark this as a duplicate, please keep in mind I have read several threads here and in most cases they say "too hard to optimize for general purposes". In my case this is a very specific purpose that I haven't seen asked about yet -- a very specific type of data which many developers have [hopefully] had experience with.
I need to quickly get any rows that land inside my view (day, week, month), so something like:
[end date of row] >= [start date of query]
AND
[start date of row] <= [end date of query]
I don't see a normal b-tree index working well for this, but I also suspect someone has figured out something clever that will work with SQL Server 2005 (and probably older), since calendar apps have been around forever, and there are 100s of them.
I'm also curious about reoccurring events, how to store those, although my current plan is to just always read all of those (index by "Is Recurring") and optimize that in code, and not SQL. There shouldn't be a huge number of those, unlike normal events which could get very large over time.
Update: Also unique to this question, because it is for a calendar app, I need to store dates with timezone info, but my queries cannot be timezone specific. If you have experience with a Calendar app, you'll know what I mean (if not, you'll just say store as UTC).
I've had to work with something similar to this a while back. It was emergency room software and we had to do a lot of date range and shift calculations and couldn't have time zone issues affect us. What we ended up having to do is store 6 columns for each date. A set three columns (Date, Date as int, and time as int) once as entered and once as UTC. All calculations are done using UTC to avoid timezone issues. You could also add in a timezone column if needed.
Date as datetime -- in the time zone entered - Used for display
UDate as a datetime -- The UTC version of the date.
-- Used for display and some calculations
IntDate as int -- Date as an int YYYYMMDD so 20130417
IntUDate as int -- UTC date as an int.
IntTime as int -- Time as an int HHMMSS.
-- So for 1:12:40 PM it would be 131240 and for 1:12:40 AM
-- it would be 11240. Note only 5 places.
-- May need to be decimal if you need more precision)
IntUTime as int -- Sames as IntTime but for the UTC datetime
You may not need the time columns. We did because of the shift calculations. Create indexes on the columns as needed. At least the IntDate and IntUDate columns. Because these are integers the indexes will be blazing fast. Note that all calculations should be done using the UTC columns to avoid timezone issues. Displays are done typically with the Date column.
Next create a date table. What you have to realize here is that this table is fairly narrow and you can fill in hundreds of years worth of dates and still not have that large a table. Aprox 36525 rows per 100 years. Add in indexes and again it's very fast.
Ours looked something like this.
CREATE TABLE DateTable (
[Date] Int PRIMARY KEY,
[DayOfYear] smallint,
[Month] tinyint,
[Quarter] tinyint,
[Year] smallint,
[LeapYear] bit,
[DaylightSavings] bit
)
With indexes on (Year, DayOfYear), (Year, Month, Day) etc. Whatever you need. Also you can add any other columns you need. Say leap year, holidays, first day of the month, last day of the month etc.
if you need to pull say everything for a given year/quarter you add a join on the datetable and everything is nicely indexed.
Using the example you have above you could do something like this:
SELECT *
FROM MyTable
WHERE EXISTS
(SELECT 1 FROM DateTable
WHERE DateTable.[Date] BETWEEN MyTable.UTCStartDate AND MyTable.UTCEndDate
AND DateTable.[Date] BETWEEN #StartDate AND #EndDate)
Because I think the timezone issue is important to calendar applications, and several existing applications do not handle this well (even Outlook, prior to 2007), I'm adding this info as an answer, and as follow-up to the prior comments.
I hope the Google developers will also read this, because based on http://support.google.com/calendar/answer/2367918?hl=en, it seems they also have the "shift" issue. Here is what they say, which seems wrong/unacceptable to me:
However, this process doesn't always work in cases where a country
decides to change when they switch to DST or even their overall time
zone. If you had created an event before we knew about the change,
Calendar converted your time zone into UTC, using the information
available at the time of creation. Once the time zone change is known,
Calendar will use the new rule to display events in your time zone,
and it might cause events to shift in your calendar.
The last part in bold is what should NEVER happen. If I set a meeting for 8am PST, it will be at 8am PST, it will not "shift" just because some timezone rules change.
In a calendar applications, if a user enters an event for "April 26, 2020, 12:00pm, Arizona Time". If you convert this to UTC for storage, as most applications do, you will be saving that as (with the rules at the time of me typing ths) "April 26, 2020, 7:00pm, UTC".
Then, if you want to do a query to find out if there are any events happening at "April 26, 2020, 12:00pm, Arizona Time", you would query for "April 26, 2020, 7:00pm, UTC", because that is what the current conversion rules tell you to do.
At first you would find the item, correct, yeah.
Now, if the Timezone rules change, say in the year 2018 Arizona becomes -0800 UTC instead of -0700 UTC (maybe they decide to support DST, who knows). Then you do your query again, looking for any events happening at "April 26, 2020, 12:00pm, Arizona Time". This time, when you do the query, you are going to look for "April 26, 2020, 8:00pm, UTC". This is because you only know to use the current rules when you do your query, you don't know that some of your data used an older rule when it was saved. So you don't find the item, even though you should have, and the user misses the event.
Now, how you decide to display that item is different from app to app, but for a calendar/schedule app it should never change the time from what the user entered. It should still be displayed as "April 26, 2020, 12:00pm, Arizona Time" when the user views it. However, the UTC value you use to base your queries on, doesn't match that time, because of the rules change.
The way a good calendar app should handle this (from what I've learned after much research) is:
Store the following info for each time entered by the user:
The Time Zone (in Windows I use the Windows Time Zone ID, but this can come from other sources, so long as it is unique and is what you use to do your conversions with).
Date and Time as entered by the user
Date and Time converted to UTC using the rules at the time the user entered the info (problem area)
Anytime Time Zone rules change, make sure your conversion code is updated (i.e.-Windows Updates, library update, etc), AND within the same update process, update all UTC times in your database using the new rules.
The "update" process is something like this:
Query for all records with the Time Zone that changed. Can filter for records that have a date after the rules change if you want, since any before that have not changed (this would be based on the date as the user entered it, not the UTC value).
For each of those records (doesn't matter if you didn't filter exactly, or even if you just blinding do this for every record in the DB in every time zone)... run the same conversion code you did when the record was added/edited last, just take the value the user entered and convert it to UTC using the current rules, and save that new UTC value.
The proof that this mess is required is that the results will be that some of your UTC values have changed, and none of the values the user entered have changed (because we can't allow that, that would be silly for a calendar app, unless the event time was UTC based, in which case the user should have set the timezone to UTC when they added it).
Think about what happens if you don't do this update process. All of the queries you do based on UTC are incorrect. How could they not be?