仅比较日期部分而不比较 JavaScript 中的时间

下面的代码有什么问题?

也许只比较日期而不是时间会更简单。我也不知道该怎么做,我搜索了,但我找不到我的确切问题。

顺便说一句,当我在警报中显示两个日期时,它们显示的完全相同。

我的代码:

window.addEvent('domready', function() {
    var now = new Date();
    var input = $('datum').getValue();
    var dateArray = input.split('/');
    var userMonth = parseInt(dateArray[1])-1;
    var userDate = new Date();
    userDate.setFullYear(dateArray[2], userMonth, dateArray[0], now.getHours(), now.getMinutes(), now.getSeconds(), now.getMilliseconds());

    if (userDate > now)
    {
        alert(now + 'n' + userDate);
    }
});

有没有更简单的方法来比较日期而不包括时间?

stack overflow Comparing date part only without comparing time in JavaScript
原文答案
author avatar

接受的答案

我仍在学习 JavaScript,我发现唯一可以在没有时间的情况下比较两个日期的方法是使用 Date 对象的 setHours 方法并设置小时、分钟、秒和毫秒归零。然后比较两个日期。

例如,

date1 = new Date()
date2 = new Date(2011,8,20)

date2 将小时、分钟、秒和毫秒设置为零,但 date1 会将它们设置为创建 date1 的时间。要删除 date1 上的小时、分钟、秒和毫秒,请执行以下操作:

date1.setHours(0,0,0,0)

现在您可以仅将两个日期作为 DATES 进行比较,而不必担心时间元素。


答案:

作者头像

当心时区

立即使用日期对象来表示只是一个日期会让您陷入一个巨大的过度精度问题。您需要管理时间和时区以将它们拒之门外,并且它们可以随时潜入。 这个问题的公认答案落入了陷阱。

javascript 日期有 no notion of timezone 。这是一个时刻(从纪元开始滴答),带有方便的(静态)函数,用于与字符串进行相互转换,默认情况下使用设备的“本地”时区,或者,如果指定,UTC 或其他时区。要用日期对象表示 just-a-date™,您希望您的日期表示相关日期开始时的 UTC 午夜。这是一个常见且必要的约定,它使您可以使用日期而不考虑日期他们创作的季节或时区。因此,您需要非常警惕地管理时区的概念,无论是在创建午夜 UTC 日期对象时还是在序列化它时。

很多人对控制台的默认行为感到困惑。如果您向控制台喷一个日期,您看到的输出将包括您的时区。这只是因为控制台在您的日期调用 toString() ,而 toString() 为您提供本地表示。基础日期 has no timezone ! (只要时间与时区偏移相匹配,您仍然有一个午夜 UTC 日期对象)

反序列化(或创建午夜 UTC 日期对象)

这是舍入步骤,有两个“正确”答案。大多数时候,您会希望您的日期反映用户的本地时区。 我在这里的日期是几号。。新西兰和美国的用户可以同时点击,通常会得到不同的日期。在这种情况下,这样做...

// create a date (utc midnight) reflecting the value of myDate and the environment's timezone offset.
new Date(Date.UTC(myDate.getFullYear(),myDate.getMonth(), myDate.getDate()));

有时,国际可比性胜过当地的准确性。在这种情况下,这样做...

// the date in London of a moment in time. Device timezone is ignored.
new Date(Date.UTC(myDate.getUTCFullYear(), myDate.getUTCMonth(), myDate.getUTCDate()));

反序列化日期

电汇上的日期通常采用 YYYY-MM-DD 格式。要反序列化它们,请执行此操作...

var midnightUTCDate = new Date( dateString + 'T00:00:00Z');

序列化

在创建时注意管理时区后,现在需要确保在转换回字符串表示时将时区排除在外。 所以你可以安全地使用...

  • toISOString()
  • getUTCxxx()
  • getTime() //returns a number with no time or timezone.
  • .toLocaleDateString("fr",{timeZone:"UTC"}) // whatever locale you want, but ALWAYS UTC.

并完全避免其他一切,尤其是......

  • getYear() , getMonth() , getDate()

所以回答你的问题,晚了7年......

<input type="date" onchange="isInPast(event)">
<script>
var isInPast = function(event){
  var userEntered = new Date(event.target.valueAsNumber); // valueAsNumber has no time or timezone!
  var now = new Date();
  var today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() ));
  if(userEntered.getTime() < today.getTime())
    alert("date is past");
  else if(userEntered.getTime() == today.getTime())
    alert("date is today");
  else
    alert("date is future");

}
</script>

See it running...

2022 年更新...带测试的免费内容...

下面的代码现在是一个 npm 包, Epoq 。代码在 github 上。别客气 :-)

2019 年更新...免费内容...

鉴于此答案的受欢迎程度,我已将其全部放入代码中。以下函数返回一个包装的日期对象,并且只公开那些可以安全地与 just-a-date™ 一起使用的函数。

使用 Date 对象调用它,它将解析为反映用户时区的 JustADate。用字符串调用它:如果字符串是指定时区的 ISO 8601,我们将四舍五入时间部分。如果未指定时区,我们会将其转换为反映当地时区的日期,就像日期对象一样。

function JustADate(initDate){
  var utcMidnightDateObj = null
  // if no date supplied, use Now.
  if(!initDate)
    initDate = new Date();

  // if initDate specifies a timezone offset, or is already UTC, just keep the date part, reflecting the date _in that timezone_
  if(typeof initDate === "string" && initDate.match(/(-dd|(+|-)d{2}:d{2}|Z)$/gm)){  
     utcMidnightDateObj = new Date( initDate.substring(0,10) + 'T00:00:00Z');
  } else {
    // if init date is not already a date object, feed it to the date constructor.
    if(!(initDate instanceof Date))
      initDate = new Date(initDate);
      // Vital Step! Strip time part. Create UTC midnight dateObj according to local timezone.
      utcMidnightDateObj = new Date(Date.UTC(initDate.getFullYear(),initDate.getMonth(), initDate.getDate()));
  }

  return {
    toISOString:()=>utcMidnightDateObj.toISOString(),
    getUTCDate:()=>utcMidnightDateObj.getUTCDate(),
    getUTCDay:()=>utcMidnightDateObj.getUTCDay(),
    getUTCFullYear:()=>utcMidnightDateObj.getUTCFullYear(),
    getUTCMonth:()=>utcMidnightDateObj.getUTCMonth(),
    setUTCDate:(arg)=>utcMidnightDateObj.setUTCDate(arg),
    setUTCFullYear:(arg)=>utcMidnightDateObj.setUTCFullYear(arg),
    setUTCMonth:(arg)=>utcMidnightDateObj.setUTCMonth(arg),
    addDays:(days)=>{
      utcMidnightDateObj.setUTCDate(utcMidnightDateObj.getUTCDate + days)
    },
    toString:()=>utcMidnightDateObj.toString(),
    toLocaleDateString:(locale,options)=>{
      options = options || {};
      options.timeZone = "UTC";
      locale = locale || "en-EN";
      return utcMidnightDateObj.toLocaleDateString(locale,options)
    }
  }
}

// if initDate already has a timezone, we'll just use the date part directly
console.log(JustADate('1963-11-22T12:30:00-06:00').toLocaleDateString())
// Test case from @prototype's comment
console.log("@prototype's issue fixed... " + JustADate('1963-11-22').toLocaleDateString())
作者头像

这个怎么样?

Date.prototype.withoutTime = function () {
    var d = new Date(this);
    d.setHours(0, 0, 0, 0);
    return d;
}

它允许您像这样比较日期的日期部分而不影响变量的值:

var date1 = new Date(2014,1,1);
new Date().withoutTime() > date1.withoutTime(); // true
作者头像

使用 Moment.js

如果您可以选择包含第三方库,那么 Moment.js 绝对值得一看。它使使用 DateDateTime 变得非常非常容易。

例如,查看一个 Date 是否在另一个 Date 之后但不包括它们的时间,您可以这样做:

var date1 = new Date(2016,9,20,12,0,0); // October 20, 2016 12:00:00
var date2 = new Date(2016,9,20,12,1,0); // October 20, 2016 12:01:00

// Comparison including time.
moment(date2).isAfter(date1); // => true

// Comparison excluding time.
moment(date2).isAfter(date1, 'day'); // => false

您传递给 isAfter 的第二个参数是进行比较的精度,可以是 yearmonthweekdayhour 、{JXM= 中的任何一个} 或 minute

作者头像

简单地比较使用 .toDateString 如下:

new Date().toDateString();

这将只返回日期部分而不是时间或时区,如下所示:

“2017 年 2 月 3 日星期五”

因此,两个日期都可以用这种格式进行比较,同样没有时间部分。

作者头像

只需在两个日期上使用 toDateString() 即可。 toDateString 不包括时间,因此对于同一日期的 2 次,值将相等,如下所示。

var d1 = new Date(2019,01,01,1,20)
var d2 = new Date(2019,01,01,2,20)
console.log(d1==d2) // false
console.log(d1.toDateString() == d2.toDateString()) // true

显然,在这个问题的其他地方表达的一些时区问题是有效的,但在许多情况下,这些问题并不相关。

作者头像

如果你真的只比较没有时间成分的日期,另一种可能感觉不对但有效并避免所有 Date() 时间和时区问题的解决方案是直接使用字符串比较来比较 ISO 字符串日期:

> "2019-04-22" <= "2019-04-23"
true
> "2019-04-22" <= "2019-04-22"
true
> "2019-04-22" <= "2019-04-21"
false
> "2019-04-22" === "2019-04-22"
true

您可以使用以下方法获取当前日期(UTC 日期,不一定是用户的本地日期):

> new Date().toISOString().split("T")[0]
"2019-04-22"

我赞成它的论点是程序员的简单性——与尝试正确处理日期时间和偏移量相比,你搞砸的可能性要小得多,可能是以速度为代价的(我没有比较性能)

作者头像

这可能是一个更简洁的版本,还请注意,在使用 parseInt 时应始终使用基数。

window.addEvent('domready', function() {
    // Create a Date object set to midnight on today's date
    var today = new Date((new Date()).setHours(0, 0, 0, 0)),
    input = $('datum').getValue(),
    dateArray = input.split('/'),
    // Always specify a radix with parseInt(), setting the radix to 10 ensures that
    // the number is interpreted as a decimal.  It is particularly important with
    // dates, if the user had entered '09' for the month and you don't use a
    // radix '09' is interpreted as an octal number and parseInt would return 0, not 9!
    userMonth = parseInt(dateArray[1], 10) - 1,
    // Create a Date object set to midnight on the day the user specified
    userDate = new Date(dateArray[2], userMonth, dateArray[0], 0, 0, 0, 0);

    // Convert date objects to milliseconds and compare
    if(userDate.getTime() > today.getTime())
    {
            alert(today+'n'+userDate);
    }
});

查看 MDC parseInt 页面以获取有关基数的更多信息。

JSLint 是一个很好的工具,可以捕获缺少的基数和许多其他可能导致模糊且难以调试的错误。它迫使您使用更好的编码标准,以避免将来出现麻烦。我在我编写的每个 JavaScript 项目中都使用它。

作者头像

{JVM=} 库对这些东西很方便。它使所有与 JS 日期相关的脚本编写变得更加容易。

作者头像

比较日期的一种有效且正确的方法是:

Math.floor(date1.getTime() / 86400000) > Math.floor(date2.getTime() / 86400000);

它忽略了时间部分,适用于不同的时区,您也可以比较相等性 == 。 86400000 是一天中的毫秒数 ( = 24*60*60*1000 )。

请注意,相等运算符 == 应该永远用于比较 Date 对象,因为当您期望相等性测试起作用时它会失败,因为它正在比较两个 Date 对象(并且不比较两个日期),例如:

> date1;
outputs: Thu Mar 08 2018 00:00:00 GMT+1300

> date2;
outputs: Thu Mar 08 2018 00:00:00 GMT+1300

> date1 == date2;
outputs: false

> Math.floor(date1.getTime() / 86400000) == Math.floor(date2.getTime() / 86400000);
outputs: true

注意:如果您正在比较时间部分设置为零的 Date 对象,那么您可以使用 date1.getTime() == date2.getTime() 但这几乎不值得优化。直接比较 Date 对象时可以使用 <><=>= ,因为这些运算符首先通过调用 .valueOf() 转换 Date 对象,然后再进行比较。