【问题标题】:Declare a TDateTime as a Const in Delphi在 Delphi 中将 TDateTime 声明为常量
【发布时间】:2010-10-15 02:29:03
【问题描述】:

据我所知,没有办法做到这一点,但我会问一下,以防万一其他人知道如何做到这一点。如何在 Delphi 中将日期声明为 const?

我找到的唯一解决方案是使用数字等价物,因为它不是人类可读的,所以维护起来有点痛苦

const
  Expire : TDateTime = 39895; // Is actually 3/23/2009

我想做的是这样的:

const
  Expire : TDateTime = TDateTime ('3/23/2009');

const
  Expire : TDateTime = StrToDate('3/23/2009');

如果这是一个功能请求,或者我只是错过了如何执行此操作,请告诉我(是的,我知道这似乎是一件奇怪的事情......)

【问题讨论】:

  • 很好的问题 - 我经常想这样做(更多的是时间而不是日期,但原理大致相同 - 例如,我想将 6:45pm 放入 TDateTime 作为常量,等等)。我总是会用 cmets 做类似你的第一个例子的事情——当我以后需要改变它时,这很痛苦!
  • 这看起来像 GExperts 或 DLangExtensions 应该能够做的事情,或者作为专家输入日期和/或时间以创建正确注释的常量,或者作为将字符串转换为 TDateTime 的预处理器。只允许 ISO 8601 格式应该消除所有歧义。
  • @Mghie - 好点。我完全可以接受 GExperts 解决方案。当我不得不拿出计算器开始做除法时,我感到很沮丧。 :-)

标签: delphi date operator-overloading constants tdatetime


【解决方案1】:

System.DateUtils 具有每个时间部分的常量。

const cDT : TDateTime = (12 * OneHour)   + ( 15 * OneMinute) 
                      + (33 * OneSecond) + (123 * OneMillisecond);

【讨论】:

  • 这真的很有创意。我没想到。
【解决方案2】:

类型“TDateTime” = 类型“Double”。

算法:

  1. 使用StrToDateTime('01.01.1900 01:01:01') (或其他方式)计算需要的double_value。 ('01.01.1900 01:01:01' => 2.04237268518519)

2.常量 DTZiro:TDateTime = 2.04237268518519;

【讨论】:

  • OP 总是知道这一点。
【解决方案3】:

我倾向于用函数模拟 const 日期。从技术上讲,它们比“伪常数”可赋值类型 constconstant 多一点。

function Expire: TDateTime;
begin
  Result := EncodeDate(2009, 3, 23);
end;

注意使用EncodeDate 而不是StrToDateStrToDate 受区域设置影响,这意味着无法保证字符串会按预期进行解释。

例如,您是否知道有一群奇怪的人认为将日期部分“打乱”成不一致的重要性顺序是有意义的?他们使用中间,然后是最小,然后是最重要的部分(例如'3/23/2009')。只有当你年满 102 岁时,逻辑才有意义——那么你可以声称你的年龄是 021 岁。

对于不成熟的优化器,如果函数调用如此频繁以至于编码日期所需的纳秒成为问题 - 你有一个远更大的问题而不是以可读、可维护代码的名义出现的这种轻微的低效率。

【讨论】:

    【解决方案4】:

    我认为您可以使用的最佳解决方案是声明:

    ArmisticeDay: TDateTime = 6888.0 + (11.0/24.0); //Nov 11, 1918 11 AM
    

    然后接受它。


    我的第 1 次尝试

    Expire = EncodeDate(2009, 3, 23);
    

    [错误] 需要常量表达式

    我的第 2 次尝试

    Expire: TDateTime = EncodeDate(2009, 3, 23);
    

    [错误] 需要常量表达式

    因此,即使它们是恒定的且具有确定性(即不依赖于任何语言环境信息),它仍然不起作用。

    【讨论】:

      【解决方案5】:

      好吧,我的反应有点晚了,但这里有一个针对较新的 Delphi 的解决方案。

      它使用隐式类重载器,因此可以像使用 TDateTime 变量一样使用这种类型的记录。

        TDateRec = record
          year,month,day,hour,minute,second,millisecond:word;
          class operator implicit(aDateRec:TDateRec):TDateTime;
          class operator implicit(aDateTime:TDateTime):TDateRec; // not needed
          class operator implicit(aDateRec:TDateRec):String; // not needed
          class operator implicit(aDateRec:String):TDateRec; // not needed
        end;
      

      实施:

      uses DateUtils;
      
      class operator TDateRec.Implicit(aDateRec:TDateRec):TDateTime;
      begin
        with aDateRec do // Yeah that's right you wankers. I like "with" :)
          Result := encodeDateTime(Year,Month,Day,Hour,Minute,Second,Millisecond);
      end;
      
      class operator TDateRec.Implicit(aDateTime:TDateTime):TDateRec;
      begin
        with Result do
          DecodeDateTime(aDateTime,Year,Month,Day,Hour,Minute,Second,Millisecond);
      end;
      
      class operator TDateRec.Implicit(aDateRec:TDateRec):String;
      begin
        Result := DateTimeToStr(aDateRec)
      end;
      
      class operator TDateRec.Implicit(aDateRec:String):TDateRec;
      begin
        Result := StrToDateTime(aDateRec)
      end;
      

      现在您可以像这样声明您的日期:

      const
        Date1:TDateRec=(Year:2009;month:05;day:11);
        Date2:TDateRec=(Year:2009;month:05;day:11;hour:05);
        Date3:TDateRec=(Year:2009;month:05;day:11;hour:05;minute:00);
      

      要查看它是否有效,请执行以下操作:

      ShowMessage(Date1); // it can act like a string
      ShowMessage(DateToStr(Date1)); // it can act like a date
      

      如果您真的想用这个替换所有 TdateTime 变量,您可能还需要重载一些其他运算符(加、减、显式...)。

      【讨论】:

      • 解决方案 +1,with 再次 +1,虽然我鄙视它的使用 :-)
      • 我接受了这个并添加了我自己的添加...FriendlyDateFriendlyTimeFriendlyDateTime
      【解决方案6】:

      一种解决方案是为年份创建一个常量列表,另一个为月份偏移量创建一个列表,然后动态构建它。您必须自己处理闰年,将每个结果常数加 1。下面的一些内容可以帮助您入门... :)

      Const
        Leap_Day = 1;  // use for clarity for leap year dates beyond feb 29.
        Year_2009 = 39812;  // January 1, 2009
        Year_2010 = Year_2009 + 365; // January 1, 2010
        Year_2011 = Year_2010 + 365; // January 1, 2011
        Year_2012 = Year_2011 + 365; // January 1, 2012 (is leap year)
        Year_2013 = Year_2012 + Leap_Day + 365;  // January 1, 2013
      
      Const
        Month_Jan = -1; // because adding the day will make the offset 0. 
        Month_Feb = Month_Jan + 31; // 31 days more for the first day of Feb.
        Month_Mar = Month_Feb + 28; // 28 days more for the first day of Mar.  
        Month_Apr = Month_Mar + 30; // 30 days more for the first day of Apr.
      
      Const
        Expire_Jan1 : tDateTime = Year_2009 + Month_Jan + 1;
        Expire : tDateTime = Year_2009 + Month_Mar + 23;
      

      如果您有闰年,那么您必须在该年 2 月之后的任何内容上加 1。

      Const
        Expire : tDateTime = Year_2008 + Month_Mar + 23 + Leap_Day;
      

      编辑

      为了清楚起见,又增加了几年,并添加了 Leap_Day 常量。

      【讨论】:

      • 为什么不添加另一个常量:Leap_day = 1。这样就不会出现乍一看可能不直观的虚假“+1”...
      【解决方案7】:

      唯一的?可能的方式,但可能不是您想要的:

      const
      {$J+}
        Expire: TDateTime = 0;
      {$J-}
      
      initialization
        Expire := EncodeDate(2009, 3, 23);
      

      【讨论】:

      • 也可以将其声明为 var 而不是 const。
      • 是的,你是对的,但至少我的解决方案使用了 const 关键字;)
      • 吉姆,我的错字来自你的帖子:P
      • 哎呀,你是对的。第一个 Expire 拼写错误。我会修好它。当我看到它时我认为它可能,但后来我只看了第二个两个实例。
      • 请永远不要这样做。我讨厌{$J+}
      【解决方案8】:

      Delphi 日期是days since Dec 30, 1899 的#。因此,您可能会想出一个复杂的数学公式来将日期表示为 const。然后你可以非常奇怪地格式化它,以强调人类可读的部分。我最好的尝试如下,但它非常不完整;一方面,它假设所有月份都有 30 天。

      我的例子主要是为了好玩。实际上,这非常荒谬。

      const
        MyDate = ((
                 2009  //YEAR
                                                - 1900) * 365.25) + ((
                 3     //MONTH
                                                - 1) * 30) +
                 24    //DAY
                 ;
      

      【讨论】:

      • 小数部分也可能导致问题,因为您的日期从一年的午夜缓慢移动到早上 6 点、中午、下午 6 点,然后在闰年再次返回到午夜。
      • 是的,绝对正确。就像我说的那样,除非您花费大量时间设计一个非常复杂的论坛来处理所有可能性,否则这并不是真正可行的。获得价值并对其进行硬编码会更好。
      • 如果您不小心在“边距”中输入了错误的数字或运算符或括号,您可能会遇到一个非常难以发现的错误。不会投反对票,但请不要在实际代码中这样做。
      【解决方案9】:

      没有办法做到这一点,因为解释日期文字本身不是确定性的,它取决于您遵循的约定/语言环境。
      例如,对于任何法国人来说,'1/4/2009' 不在 1 月,而将编译器翻译为 1 月 4 日将使它成为傻瓜编译器;-)
      除非编译器实现一些(有据可查的)“神奇”双射函数来配对日期值和显示表示......而且无论如何,地球的一半都不喜欢它。
      我现在看到的唯一明确的方式是提供价值,即使它看起来很痛苦...... ...我的 0.02 美元

      【讨论】:

      • 如果语言被定义为对区域设置敏感,则仅取决于区域设置。浮点文字对语言环境不敏感;他们根本不接受逗号作为小数分隔符。没有理由日期时间文字不能对格式有类似的限制。
      • EncodeDate 和 EncodeTime 是确定性的,因此如果可以在编译时评估它们是可用的。
      【解决方案10】:

      Rob Kennedy 的回答表明 StrToDate 解决方案本质上是不可能的,因为如果在欧洲编译,您不希望代码中断!

      我同意应该有某种方法来执行 EncodeDate,但没有。

      就我而言,编译器应该简单地编译和运行它在常量赋值中找到的任何代码,并将结果存储到常量中。我会把它留给程序员来确保代码对其环境不敏感。

      【讨论】:

        【解决方案11】:

        不,Delphi 不支持。

        您的第一个想法是请求与普通浮点文字不同的日期时间文字。我找到了QC 72000,它是关于在调试器中将TDateTime 值显示为日期,但与您的特定请求无关。不过,这不像以前没有人提到过。这是新闻组中常年出现的话题。我只是在 QC 中找不到任何关于它的信息。

        您的第二个想法需要StrToDate 在编译时可评估。我在 QC 中也没有看到任何关于它的条目,但就其价值而言,C++ 正在为具有必要品质的函数获得这样的特性。不过,StrToDate 不符合这些要求,因为它对当前语言环境的日期设置很敏感。

        【讨论】:

        • EncodeDate、EncodeTime 不受影响,符合要求。
        • 不完全是,@Craig。 C++ 的要求之一是函数不能抛出异常(隐含在函数仅包含return 语句的要求中)。 Delphi 函数有复合语句并且可以抛出。
        • 有点误解,我的评论应该不那么含糊。请允许我详细说明:EncodeDate 和 EncodeTime 不受影响(受本地化 - 它们是确定性的)并且会满足要求(Jim 指定一个恒定的日期/时间,尽管在语法上与他的问题不同)。当然这个想法是一个有争议的点,因为 Delphi 目前没有这样的功能。 :( 但是,您对 C++ 无例外要求的评论很有趣。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-28
        • 1970-01-01
        • 2012-01-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多