Comparing date part only without comparing time in JavaScript

后端 未结 22 1856
春和景丽
春和景丽 2020-11-22 10:59

What is wrong with the code below?

Maybe it would be simpler to just compare date and not time. I am not sure how to do this either, and I searched, but I couldn\'t

22条回答
  •  天涯浪人
    2020-11-22 11:33

    BEWARE THE TIMEZONE

    Using the date object to represent just-a-date straight away gets you into a huge excess precision problem. You need to manage time and timezone to keep them out, and they can sneak back in at any step. The accepted answer to this question falls into the trap.

    A javascript date has no notion of timezone. It's a moment in time (ticks since the epoch) with handy (static) functions for translating to and from strings, using by default the "local" timezone of the device, or, if specified, UTC or another timezone. To represent just-a-date™ with a date object, you want your dates to represent UTC midnight at the start of the date in question. This is a common and necessary convention that lets you work with dates regardless of the season or timezone of their creation. So you need to be very vigilant to manage the notion of timezone, both when you create your midnight UTC Date object, and when you serialize it.

    Lots of folks are confused by the default behaviour of the console. If you spray a date to the console, the output you see will include your timezone. This is just because the console calls toString() on your date, and toString() gives you a local represenation. The underlying date has no timezone! (So long as the time matches the timezone offset, you still have a midnight UTC date object)

    Deserializing (or creating midnight UTC Date objects)

    This is the rounding step, with the trick that there are two "right" answers. Most of the time, you will want your date to reflect the local timezone of the user. What's the date here where I am.. Users in NZ and US can click at the same time and usually get different dates. In that case, do this...

    // 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()));
    

    Sometimes, international comparability trumps local accuracy. In that case, do this...

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

    Deserialize a date

    Often dates on the wire will be in the format YYYY-MM-DD. To deserialize them, do this...

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

    Serializing

    Having taken care to manage timezone when you create, you now need to be sure to keep timezone out when you convert back to a string representation. So you can safely use...

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

    And totally avoid everything else, especially...

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

    So to answer your question, 7 years too late...

    
    
    

    See it running...

    Update 2019... free stuff...

    Given the popularity of this answer, I've put it all in code. The following function returns a wrapped date object, and only exposes those functions that are safe to use with just-a-date™.

    Call it with a Date object and it will resolve to JustADate reflecting the timezone of the user. Call it with a string: if the string is an ISO 8601 with timezone specified, we'll just round off the time part. If timezone is not specified, we'll convert it to a date reflecting the local timezone, just as for date objects.

    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(/((\+|-)\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())

提交回复
热议问题