和@TMS 一样,我也会使用日程表。我知道的火车表不仅在工作日不同,而且在节假日和银行假日(如 25.12. 或复活节星期一)和季节也不同。
所以我会将@TMS 的表扩展一列Holiday 和BankHoliday(或者更好的名字,我只是德国人)tinyint,0 = false,1=true,NULL = always。这意味着,如果火车只在节假日来,它在假期列中得到“1”,如果只是在上学时间,它得到“0”,如果节假日无关紧要,它得到 NULL。
然后您需要一个包含有趣年份的所有日期的 CalendarDay 表,您必须在其中标记假日日和银行假日日。如果幸运的话,这些不取决于您所在国家/地区的地区。在德国,您拥有按地区划分的所有内容(萨克森与巴伐利亚等不同)。
我确信夏季和冬季有不同的时间表,并且每年都会更改这些时间表(2014 年夏季!= 2013/2014 年冬季!= 2013 年夏季)。所以我会添加另一个带有 ID、名称、起始日期和截止日期的表 ScheduleTable,它的 ScheduleTableID 也是表 Schedule 中的一个额外字段。这意味着,您已经可以存储下一个夏季日程表的日程表。我会将“直到”排他性,因此 2014-04-01 表示“有效期至 2014-03-31”。必要时更容易加入连锁店。
该日历日表还可以标记 DST 噩梦,例如用 -1 表示较短的一天,+1 表示较长的一天,0 表示正常的日子。这也与时间表有关,我认为有些火车在 DST 切换日的行为不同,所以逻辑相同:在较短的一天中带有“-1”的 tinyint,在较长的一天中带有“+1”,在较长的一天中带有“0”正常天数,如果 DST 对该火车无关紧要,则为 NULL。
正如 TMS 所说,查询是 BETWEEN <usertime> - interval 15 minute and <usertime> + interval 15 minute,仅针对所有 calendar_day 值进行扩展。
所以,当用户给出“2014-04-25 16:30”时,你应该像这样查询:
select Sc.*
from Schedule sc
inner join CalendarDay cd on
(
cd.day = date(<userdate>)
and (cd.holiday = sc.holiday or sc.holiday is null)
and (cd.BankHoliday = sc.BankHoliday or sc.BankHoliday is null)
and (sc.DSTSwitch = cd.DSTSwitch or sc.DSTSwitch is null)
and sc.`weekday` = weekday(cd.day)
)
inner join ScheduleTable sct on(
sct.id = sc.ScheduleTableID
and sct.from <= cd.day and sct.until > cd.day
)
where sc.`Time` between
time(<userdate>) - interval 15 minute
and time(<userdate>) + interval 15 minute
;
这听起来可能有点复杂,但只要你没有 ScheduleTables 和 Holidays 之类的,这些字段就不会打扰你(将它们设置为 0,将 ScheduleTableID 设置为 1,日期范围从1000 年到 3000 年,NULL 表中的限制)。但是当它们已经在表结构中时,对这些“问题”做出反应会更容易。