当心时区
使用日期对象立即表示刚刚的日期会使您陷入巨大的超精度问题。您需要管理时间和时区以将其保留,它们可以在任何步骤潜入。这个问题的公认答案就落入了陷阱。
javascript日期没有时区的概念。这是一个时刻(自历元开始滴答作响),带有方便的(静态)函数,用于在字符串之间进行转换,默认情况下使用设备的“本地”时区,如果使用,则使用UTC或其他时区。为了用日期对象表示just-a-date™,您希望您的日期在相关日期的开始时代表UTC午夜。这是一项常见且必要的约定,可让您使用日期,而不考虑其创建的季节或时区。因此,在创建午夜UTC日期对象以及对其进行序列化时,都需要非常警惕地管理时区的概念。
控制台的默认行为使很多人感到困惑。如果您在控制台上喷一个日期,您看到的输出将包括您的时区。这仅仅是因为控制台会要求toString()
您约会,并toString()
为您提供本地代表。基础日期没有时区!(只要时间与时区偏移量匹配,您仍然会有一个午夜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>
看到它正在运行...
更新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(/((\+|-)\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())
date1 === date2
似乎并不能提供一致的行为。这是好做date1.valueOf() === b.valueOf()
,甚至date1.getTime() === date2.getTime()
。奇怪