【问题标题】:Reoccurring events without year using DateTime使用 DateTime 的没有年份的重复事件
【发布时间】:2016-09-06 13:01:24
【问题描述】:

我是一名初级程序员,刚刚起步。我被赋予了一项任务,以确定事件(每年都会一遍又一遍地发生)当前是否正在发生。这些事件以月/日开始日期到月/日结束日期的格式存储在数据库中,但没有年份。更复杂的是,这些事件不跨越通常的日历年,它们发生在一年的 7 月 1 日至次年的 6 月 30 日之间的财政年度。似乎没有年份就没有 DateTime 对象,我真的不想使用任意年份的方法,因为我认为从长远来看这不会奏效。据我了解,这是我们项目将越来越多的新事物,根据一年中的月份和日期显示信息(或不显示信息)。给定当前日期,以及我以月/日格式知道开始日期和结束日期的事件,我如何判断该事件当前是否正在发生?请记住,这可能跨越日历年的变化。如果我可以将此功能构建到一个 linq 查询中,这样我什至不用费心从数据库中提取我不关心的事件,那将是非常棒的,但我会尽我所能让它工作。我们使用的语言是 C#。谢谢

对不起,我的帖子结构,我没有太多使用堆栈溢出 数据库中的数据结构有点像这样:

StartDate    |  EndDate
-------------|----------
3/21         |  4/15
4/25         |  5/6
11/30        |  3/7

是的,它存储为 varchar。

【问题讨论】:

  • 您是否需要了解某个事件当前是否正在发生?数据库中的类型只是 varchar 吗? (如果你可以有四个int 字段,StartMonth、StartDay、EndMonth、EndDay 那就更好了。)
  • 另外,您当前的问题真的很难阅读,因为只是一堵文字墙。如果您对其进行结构化以使其更易于阅读以及提供示例数据等,那将非常有帮助。
  • 抱歉我的帖子的结构,我没有太多使用堆栈溢出数据库中的数据结构有点像这样:StartDate |结束日期 3/21 | 4/15 4/25 | 5/6 11/30 | 3/7 是的,它存储为 varchar。
  • 请编辑问题以反映这一点,并使其更易于理解。你能改变数据库模式吗?很难使用作为文本卡在一起的数据......
  • 我想我可以更改数据库架构,因为我是新手,我不会做太多这类事情,但如果我真的明白为什么,我也许可以做到。

标签: c# datetime


【解决方案1】:

我强烈建议您重构数据库,使其更易于查询 - 您不希望在每次查询时都解析每一行的字符串值。此外,根据您的最后一个示例,您可以添加一个额外的字段来指示该事件是跨年的,即“开始”在“结束”之后。所以你会有类似的东西:

StartDay   StartMonth   EndDay  EndMonth  CrossYear
      21            3       15         4      False
      25            4        6         5      False
      30           11        7         3      True

此时您可以轻松地合理地编写查询。例如:

DateTime date = ...; // Wherever you get this from

var activeEvents = db.Events.Where
    (e =>
     // Regular case
     (e.CrossYear == false &&
      (date.Month > e.StartMonth || (date.Month == e.StartMonth && date.Day >= e.StartDay)) &&
      (date.Month < e.EndMonth || (date.Month == e.EndMonth && date.Day <= e.EndDay))) ||
     // Cross-year case, basically, just invert start and end... conditions
     (e.CrossYear == true &&
      (date.Month < e.StartMonth || (date.Month == e.StartMonth && date.Day <= e.StartDay)) &&
      (date.Month > e.EndMonth || (date.Month == e.EndMonth && date.Day >= e.EndDay))));

您可以通过使用乘法将月/日转换为单个数字来更简单地表达相同的结果,例如

int pseudoDayOfYear = date.Month * 100 + date.Day;
var activeEvents = db.Events.Where
    (e =>
     // Regular case
     (e.CrossYear == false &&
      pseudoDayOfYear >= e.StartMonth * 100 + e.StartDay &&
      pseudoDayOfYear <= e.EndMonth * 100 + e.EndDay) ||
     // Cross-year case
     (e.CrossYear == true &&
      pseudoDayOfYear <= e.StartMonth * 100 + e.StartDay &&
      pseudoDayOfYear >= e.EndMonth * 100 + e.EndDay));

这是可行的,因为从来没有任何月份超过 100 天,但它有点丑 IMO。

【讨论】:

    【解决方案2】:

    我想你可以修复数据库结构,正如许多其他人所说的那样。话虽这么说,你一个初级程序员,在这件事上你可能没有太多发言权。

    好消息:您还有一些选择。

    我从你的问题中注意到了一些事情:

    1. 您显然有一个包含部分日期片段的事件表,以 varchar 格式存储。没有可用的年份信息。

    2. 您有一个会计年度的概念,从 7 月 1 日到 6 月 30 日 - 例如2016 年 7 月 1 日 - 2017 年 6 月 30 日。

    3. 基于以上 2 点,我相信您指的是包含每年重复事件的表(即每个列出的行项目每年出现一次)。

    4. 所以现在,重新表述您的问题:鉴于当前日期上述参数,您如何判断事件正在接近,正在发生是现在,还是已经过去了?

    我认为你需要做的是:

    1. 您需要一些程序结构来保存表中的事件数据。因此,创建一个具有开始日期、结束日期和枚举指示符的类,以表示事件是否已经过去、正在发生或正在接近(在未来)。您可能还想存储某种 ID 或描述。将这些日期片段存储到事件对象中也可能是有意义的。
    2. 我认为事件表行不会是一个庞大的集合,因此使用一些 ORM,如 PetaPoco、Dapper、EF 等,您应该将表行下拉到您设计的类中。您可能需要建立一些 ORM 映射 - 您必须研究如何做到这一点。
    3. 使用DateTime.TryParse 方法从事件表中的日期片段重建日期。只需获取片段,然后将当前年份添加到片段中。因此,对于您的示例3/214/15,您可以将这些字符串扩展为3/21/20164/15/2016
    4. 将日期时间值解析到事件对象后,编写一些代码来验证日期时间信息,以使其符合会计年度信息。您知道财年从 7 月持续到 6 月。所以看看你开始和结束的月份部分,看看是否都在七月之前登陆。如果他们这样做,这意味着年份部分是明年的(在开始和结束时执行AddYears(1))。如果他们都在之后登陆,那么你知道他们是今年的。现在时髦的部分是如果开始和结束跨越 FY 边界。示例:5 月 1 日开始,7 月 1 日结束。根据 FY 规则,一年从 7 月开始,所以我们谈论的是 2017 年 5 月 1 日开始和 2017 年 7 月 1 日结束。你只需要弄清楚逻辑,我希望你明白我的意思。
    5. 一旦您建立了日期时间验证,剩下的就是将开始日期与当前日期进行比较 - 如果开始日期小于或等于 DateTime.Today,则意味着该事件是 a ) 开始或已完成; b) 如果不是,那么事件就在未来的某个地方。如果 case b) 为真,那么您必须根据DateTime.Today 评估结束日期。如果等于或小于,则事件已完成(或将在今天完成)。如果更大,则事件仍在进行中。

    很抱歉这篇超长的帖子,但我只是想为您提供一个高级概述,说明如何仍然可以合理有效地满足您的要求,而无需触发重新设计表格。

    出于几个原因,我经常对重新设计现有表持怀疑态度。其中一个主要问题是,我可能不知道该表的项目依赖关系的完整映射。例如,一些生产报告可能会针对该表运行。或者 Excel 插件可能正在查询该表并为某处的用户生成一些输出。底线是,如果某些东西看起来很可疑,在你要求重新设计之前,试着弄清楚它为什么会这样设计。有时,可疑设计的基本原理令人信服,有时它只是不良思维的孤立产物(因此可以以最小的风险重新设计)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-12-17
      • 1970-01-01
      • 2015-05-11
      • 1970-01-01
      • 2015-09-10
      • 2020-04-21
      • 1970-01-01
      相关资源
      最近更新 更多