Структура данных для хранения повторяющихся событий?

Я ищу шаблон структуры данных для хранения повторяющихся событий, но все, что я придумал, приведет к большому количеству обработки особых случаев или ввода пользователя и поиска данных. (Я получаю отчетливое чувство, что я недостаточно понимаю проблему, чтобы сделать это.)

Как сохранить повторяющиеся события в стиле Outlook?

  • Каждый день в 8 утра
  • Каждый первый вторник в месяц
  • Каждый 1 декабря в течение трех лет
  • Каждые два часа в неделю
  • ...

Ответ 1

Существуют различные документы, описывающие структуры данных и алгоритмы для этого варианта использования. Кроме того, вы можете увидеть код или описания реализации с открытым исходным кодом crontab и Quartz (Java) или Quartz.NET (.NET).

Это одна из таких статей

http://portal.acm.org/citation.cfm?id=359763.359801&coll=ACM&dl=ACM&CFID=63647367&CFTOKEN=55814330

Например, cron хранит такую ​​информацию (* означает каждый, поэтому месяц * означает месяц)

.---------------- minute (0 - 59) 
|  .------------- hour (0 - 23)
|  |  .---------- day of month (1 - 31)
|  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ... 
|  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7)  OR sun,mon,tue,wed,thu,fri,sat 
|  |  |  |  |
*  *  *  *  * 

There are several special entries, most of which are just shortcuts, 
that can be used instead of specifying the full cron entry:

Entry      Description                 Equivalent To
@reboot    Run once, at startup.       None
@yearly    Run once a year             0 0 1 1 *
@annually  (same as @yearly)           0 0 1 1 *
@monthly   Run once a month            0 0 1 * *
@weekly    Run once a week             0 0 * * 0
@daily     Run once a day              0 0 * * *
@midnight  (same as @daily)            0 0 * * *
@hourly    Run once an hour            0 * * * *

Ответ 2

Поддержка стандартных типов событий iCalendar

IETF задумался над этим, когда они создали спецификацию базового объекта Интернета для календаря и планирования, более известную как iCalendar.

Спецификация включает повторение событий.

В качестве дополнительного бонуса ваша база данных будет поддаваться обмену данными с другими источниками данных, совместимыми с iCalendar, такими как календари Google и Apple.

http://tools.ietf.org/html/rfc5545

Ответ 3

Event:

StartDate
EndDate (calculated on change of NumberOfOccurances)
NumberOfOccurances (calculated on change of EndDate )
Frequency e.g. 1/2hrs, 1/month, 1/day, ....
CorrectionFunction e.g. first Tuesday, last Sunday, ...

bool OccuresOn(day)
Date NextOccurance(date)

Ответ 4

Вот мой прием - пожалуйста, дайте мне знать, если мне что-то не хватает:

На основе параметров повторения Outlook у вас есть таблица с регулярными необходимыми полями:

FieldName       DataType      Sample Data
ID              int           primary key
EventID         int           foreign key (to EventID from Event Table)
StartTime       DateTime      8:00 AM
EndTime         DateTime      8:30 AM
Duration        int           30 (minutes)
StartDate       DateTime      01/25/2014
EndBy           DateTime      01/25/2024
NoEndDate       bit           False
NumOccurrences  int           10
RecurrenceType  int           ****See below for instructions on how to use these last 6 fields  
Int1            int           
Int2            int
Int3            int
String1         nvarchar(50)
IntYears        int

Здесь происходит волшебство. для этой логики требуется только 4 целых числа и одна строка.

The month of year (1 = Jan, 12 = Dec), 
The day of the month (1 = the 1st, 31 = 31st), 
Day of the week (0 = Sunday, 1=Monday, 6= Saturday), 
Week of the month (1 = first, 4 = forth, 5 = last),
Yearly reocurrence ( 1=1,2=2)   
When multiple days can be selected I use a comma delimited string (1,3,5 = Monday, Wed, Friday)

Я ввожу 3 целых числа в том порядке, в котором они появляются в планировщике Outlook Recurrence, это экономит дополнительные feilds, логику, раздражение. * Если вы откроете планировщик перспективы, это будет немного проще:

The RecurrenceType field can be any of the 7 following choices 

(Существует два варианта ежедневного, ежемесячного и годового и один вариант для еженедельного):

10 = Daily (Every `Int1` day(s) )    
            Every     4  day(s)
11 = Daily (Every Weekday)  -- no variables needed 
            Every Weekday (MTWTF)
20 = Weekly (Recur every `Int1` week(s) on: `String1`
             Recur every     3  week(s) on  Monday, Wednesday, Friday   
(`String1` will be a list of days selected (0=Sunday, 1=Monday, 2=Tuesday... 7=Saturday) so for (Mon, Wed, Fri) String1 would hold "1,3,5". You would parse this on the code side to pull the actual days.)
30 = Monthly (Day `Int1` of every `int2' month(s) 
              Day    28  of every     2  month(s)
31 = Monthly (The `Int1`  `Int2` of every `Int3` month(s) 
              The forth Tuesday  of every     1  month(s)
40 = Yearly (Recur every `intYears` year(s) On `Int1` `Int2`) -- 
             Recur every         1  year(s) on   Jan   28th
41 = Yearly (Recur every `intYears` year(s) on the `Int1` `Int2` of `Int3`) -- 
             Recur every         1  year(s) on the forth Tuesday of January

Код для вытягивания или сохранения повторного появления становится довольно простым.

if (RecurrenceType = 10 )
    Every `int1` days
if (RecurrenceType = 11)
    Every Weekday
if (RecurrenceType = 20)
    Every `int1 weeks on 
    parse `string1` and populate checkboxes for Mon, Tues, ...
if (RecurrenceType = 30)
    `int1 day of every `int2` month

etc...

Надеюсь, я достаточно подробно объясню это. Дайте мне знать, если что-то неясно или это не сработает. Я создаю это для текущего приложения. Спасибо всем.