Comparing dates in Javascript for fullcalendar [duplicate] - javascript

This question already has answers here:
JavaScript Date Object Comparison
(7 answers)
Closed 2 years ago.
I'm trying to check dates in Javascript for a calendar (fullcalendar), essentially I just want it to not be able to choose past dates:
dateClick: function(info) {
var today = Date.now();
var check = new Date(info.dateStr)
if(check < today)
{
alert('You cannot request dates in the past');
return;
}
else
{
alert('this is the future');
}
},
I'm getting some odd results in that, it seems to calculate the past fine, but also calculates the current day as the past AS well as tomorrow. The day after tomorrow it calculates as the future. No sure what's going on.
info.dateStr gives the format YYYY-mm-dd.

You should coerce to number using +Date or use .getTime() to make sure you are comparing the numeric timestamp values. You're probably fine since you're using Date.now(), which returns a timestamp.
Parsing using the string parsing for Date is strongly discouraged, due to issues like the one in OP:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
Use Date(yyyy, mm, dd, ...) constructor (which uses local time zone) by parsing string manually instead of built-in Date string parsing (which uses UTC if timezone isn't provided).
Using end of the day by adding 24*60*60*1000 to the getTime() value, as that's most likely what you're expecting (same date as today being past is not what most people usually want).
eg: with date to check 05-29-2020, you actually want anything before 05-29-2020 23:59:999
ie: check=05-29-2020 23:59:999 < today=05-29-2020 22:00:000 === false (not past)
or to put it another way the actual intention when:
05-29-2020 => anything from 05-29-2020 00:00 to 05-29-2020 23:59 => actually same as checking 05-30-2020 00:00 - 1 millisecond
dateClick = function(info) {
var today = Date.now()
var check = (([y,m,d])=>new Date(+y,+m-1,+d))(info.dateStr.split(/\D/)).getTime()
+ 24*60*60*1000-1 // add full day so same date as today is not past
console.log(today,check)
if(check < today)
{
alert('You cannot request dates in the past');
return;
}
else
{
alert('this is the future');
}
}
dateClick({dateStr:'2020-05-28'})
dateClick({dateStr:'2020-05-29'})
dateClick({dateStr:'2020-05-30'})
dateClick({dateStr:'2020-05-31'})

you want to compare dates like this: date1.getTime() - date2.getTime()
also, keep in mind, if your goal is to make sure a user is inputing a date that is not in the past, you can use html5 date input with a min set:
<input type="date" min="2020-05-29">

Related

Moment js Date values comparison across time-zones

I am getting 4 dates as inputs mentioned below from an external source.
Dates with time element:
"InitialDate": "2019-02-19T12:03:22.129Z",
"updateDate": "2019-02-28T05:26:57.115Z",
Dates without time element:
"startDate": "2019-02-18",
"endDate": "2020-02-16",
I am coverting InitialDate and updateDate and creating actualInitDatE out of them using a moment format as below, as they are getting time element also in it.
I don't want time element and i only want date elements of all the 4 dates.
const actualInitDatE = moment(InitialDate).format('MM-DD-YYYY') ||
moment(updateDate).format('MM-DD-YYYY');
Now, I am converting the startDate and endDate which are having only date element in it (and no time element) and finally creating actualStartDate and actualEndDateW variables,
const actualStartDateW = moment(startDate).format('MM-DD-YYYY');
const actualEndDateW = moment(endDate).format('MM-DD-YYYY');
Now I am comparing them with the below logic and is working fine in IST,
if (actualInitDatE >= actualStartDateW && actualInitDatE <= actualEndDateW) {
console.log('Compared and True');
}
My Doubt is will this work correctly in UTC and other time zones as well? I am doubtful because some of the dates have time elements and some of them have only the date elements.
I have gone through this and implemented the approach. Is this approach is correct or do we need to use any offset?
javascript Date timezone issue
Can someone help me in this regard and let me know if this code works across timeZones?
I believe the core issue here is that you must specify a timezone for startDate and endDate. If you don't, moment.js will assume local time, for example IST or let's say you were in the US, Pacific time. The problem with this approach is that the code will give inconsistent results (depending on the machine).
You can demonstrate this by running the snippet below in your browser (Chrome is best) and changing your machine timezone. You'll see that parsing the startDate (and endDate) would result in different times depending on your timezone.
So the combination of a timestamp and a timezone give us a clear, unambiguous point in time for the most robust code. If we don't set a timezone when parsing the start and end date, the code could give a different result depending on the machine it is running on.
The best approach is to specify what timezone the startDate and endDate are in, e.g. are they in IST, or in UTC?
This way you can be sure your dates will parse consistently.
I would also suggest creating a function, say, parseDate that accepts a datestring, a format, and a timezone. This is makes all assumptions clear to anyone who reads the code.
There is no issue with InitialDate or updateDate, since they are specified as UTC times (the Z timezone specifier), so they are both clear and unambiguous.
const dates = {
startDate: "2019-02-18",
endDate: "2020-02-16"
}
const startDateNoTimezoneSpecified = moment(dates.startDate);
console.log("StartDate (No Timezone Specified):", startDateNoTimezoneSpecified.toISOString());
function parseDate(dateString, format, timezone) {
return moment.tz(dateString, format, timezone)
}
// Parse start date, assuming it is in IST (I'm assuimg IST refers to India Standard Time , if it's Israel Standard Time replace with Asia/Jerusalem!
console.log("Parse date result (IST):", parseDate(dates.startDate, "YYYY-MM-DD","Asia/Kolkata").toISOString());
console.log("Parse date result (UTC):", parseDate(dates.startDate, "YYYY-MM-DD","UTC").toISOString());
// You can also use moment.utc instead of moment.tz(date, "UTC").. it's simpler!
const startDateUTC = moment.utc(dates.startDate);
console.log("StartDate (UTC (moment.utc)):", startDateUTC.toISOString());
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://momentjs.com/downloads/moment-timezone-with-data-1970-2030.js"></script>
You seem to be over complicating things.
Your conversion of UTC timestamps to local dates is OK, but the format doesn't make sense. MM-DD-YYYY is pretty useless for anything, I'd suggest using ISO 8601 YYYY-MM-DD.
Date-only timestamps should be treated as local, so no conversion is necessary for the second two dates. Using ISO 8601 format, the strings can be compared directly:
let initialDate = '2019-02-19T12:03:22.129Z';
let updateDate = '2019-02-28T05:26:57.115Z';
// Get local date in required format
let actualInitDatE = moment(initialDate || updateDate).format('YYYY-MM-DD');
// Use these as they are
let startDate = '2019-02-18';
let endDate = '2020-02-16';
if (actualInitDatE >= startDate &&
actualInitDatE <= endDate) {
console.log('Compared and True');
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
You can also keep the values as moment objects after setting them to the start of the day and use various moment methods for comparison, but I think the string version is pretty simple so why make it harder than it has to be?
Whether "this code works across timeZones" is unknown as you haven't explained what you are actually trying to achieve.

Date restrictions on user input in JavaScript

I am trying to have a date entry box which has the following restrictions. Date must be today's date or earlier, but not more than 1 year previous. I have the following line:
if (myFDGDT - todayDate > 0 || (myFDGDT - todayDate)/86400000 < -365)
The first portion of that creates the appropriate alert when some enters a date after today's date. Not sure about the best way to cap the entry to a year previous. Attempted a few items, but the above was just an example of my last attempt. This is also written in a dependency and not in the Global JavaScript of our entry client.
Here is a snippet that will generate a Date object that is one year ago. You can compare against it as needed using greater than/less than operators.
var oneyear = new Date('01/01/1971'); // from unix epoch
var now = new Date();
var oneyearago = new Date(now - oneyear);
alert(oneyearago);
If you are manipulating dates a lot in your app you should consider using the momentjs library. For your problem the solution would be something like:
var momentdate = moment(date);
if (momentdate.isAfter(momentdate.add(1, 'year') ||
momentdate.isBefore(momentdate.startOf('day')) {
// Invalid date?
}
Hope this helps.

Javascript failing compare dates

im tryng to create a system that open and close rounds in some date and time for users to play.
The problem is that javascript is not beeing precise, some times its faling to make the change in the correct time, and just change 1 minute later.
Maybe the problem is becouse I am geting de original date in UTC and converting before compare this date with the actual date and time...
I try compare using >=, just <, using .getTime(), but the problem is the same, javascript dont detect when the times are equal, the comparing only works 1 minute later, when one date is minor or greater than other.
This is the last code:
round_ended = new Date(round.ended + 'Z');
var date = new Date();
if (date.getTime() >= round_ended.getTime()) {
round.phase = "closed";
}
As I say, i have tried, with no success, other variations like this:
round_ended = new Date(round.ended + 'Z');
var date = new Date();
if (!(date < round_ended)) {
round.phase = "closed";
}
Someone can help?
What if you compare date ISO Strings.
if (!(date.toISOString() < round_ended.toISOString())) {
round.phase = "closed";
}
The ISO format is fixed : YYYY-MM-DDTHH:mm:ss.sssZ
I still dont no exactly the problem, but i guess is the miliseconds.
So, i found a solution based on this guess.
I compare the difference of the two values and check if it is smaller than 1000:
var time = round_ended - date;
if (time < 1000) {
round.phase = "closed";
}
Now it work's fine.
Thanks for the help.

Is this a good way to check for a valid date in JavaScript?

Please correct or explain how my over-simplification is incorrect as I am not a JavaScript expert.
But I just need to know if an object is a valid date. This will only come from user input (ie, text box).
var is_valid_date = function(date) {
try {
var d = new Date(date);
return true;
}
catch(e) {
return false;
}
}
YOU have to decide what form of dates you want to accept.
Then, once you know what forms you want to accept, you can then check the spec for new Date(str) or date.parse() on MDN and see if it supports exactly what you want and if it does the right things on error conditions (it probably will not). If not, then you will have to do some manual parsing.
If you want further help from us, you will need to specify what forms of date you want to accept.
There are also some browser differences as javascript has moved to support additional date formats and earlier browsers had some inconstencies between them which all means you'll want to build yourself a simple test script with a bunch of legal and illegal date format strings and see if your validity detection does what you want in several browsers. This isn't rocket science to get it right, but it's not trivial either and requires some work unless you only want to accept what the original date object supported (which is unlikely).
If this were my code, I'd probably decide that it's far less work to do manual parsing of your desired input format that you know with 100% certainty will work in all browsers because it's your own manual parsing. I'd probably use a regex to parse the date and then convert each component to a number and check each component for validity. You can then feed those numeric components to the Date constructor to create the Date object.
If you can tell by now, the built-in date class isn't very useful for user entered input. If you're willing to use a library for this, the date.js library has a ton of useful functionality in this regard.
Here's an example of a manual parsing function that accepts these US formats:
mm-dd-yyyy
mm dd yyyy
mm/dd/yyyy
JS Code:
function checkDate(str) {
var matches = str.match(/(\d{1,2})[- \/](\d{1,2})[- \/](\d{4})/);
if (!matches) return;
// parse each piece and see if it makes a valid date object
var month = parseInt(matches[1], 10);
var day = parseInt(matches[2], 10);
var year = parseInt(matches[3], 10);
var date = new Date(year, month - 1, day);
if (!date || !date.getTime()) return;
// make sure we have no funny rollovers that the date object sometimes accepts
// month > 12, day > what's allowed for the month
if (date.getMonth() + 1 != month ||
date.getFullYear() != year ||
date.getDate() != day) {
return;
}
return(date);
}
And a demo with some test cases: http://jsfiddle.net/jfriend00/xZmBY/
If you want the Euro format, it's a trivial matter to switch the code to that. In either case, you have to decide which format you accept, code for it and then communicate to the user which format is required. If you think this is messy, then perhaps you will see why so many sites use a date calendar picker that doesn't have this complexity.
Please correct or explain how my over-simplification is incorrect as I am not a JavaScript expert.
But I just need to know if an object is a valid date. This will only come from user input (ie, text box).
Here's why it's an oversimplification.
First of all, it sounds like you really want to check the validity of a string representation of a Date object. This is not particularly useful by itself, because you are going to want to use the date for something in your script, send it to the server, etc.
If you want to use the date in your script, there are caveats.
new Date('2020-10-10') // Fri Oct 09 2020 20:00:00 GMT-0400 (EDT)
If you want to pass it to the server, you'll need to do more than just check validity– you'll need to use a format that your server side code can interpret.
If that's the case, you could consider normalizing the string into a format of your choice. You'd want to be able to create equivalent dates from the normalized strings in both your client and server side code. For simplicity, the format can be human-readable (not a timestamp), and you can replace the value of the text input with the normalized string.
Checking the validity of the string can simply be a part of normalization... have the function return false or an empty string if the input was bad, don't change the text input's value, and instead show a message indicating that the value is invalid:
// assume `birthday` is a text input.
birthday.onblur = function() {
var dateString = normalizeDate(birthday.value);
if (dateString) {
validator.style.display = 'none';
birthday.value = dateString;
} else {
validator.style.display = 'block';
}
};
Here's an example of what the normalizeDate function might look like. This example uses the format 'yyyy-mm-dd', you can change it to suit your needs.
function normalizeDate(dateString) {
// If it's not at least 6 characters long (8/8/88), give up.
if (dateString.length && dateString.length < 6) {
return '';
}
var date = new Date(dateString),
month, day;
// If input format was in UTC time, adjust it to local.
if (date.getHours() || date.getMinutes()) {
date.setMinutes(date.getTimezoneOffset());
}
month = date.getMonth() + 1;
day = date.getDate();
// Return empty string for invalid dates
if (!day) {
return '';
}
// Return the normalized string.
return date.getFullYear() + '-' +
(month > 9 ? '' : '0') + month + '-' +
(day > 9 ? '' : '0') + day;
}
Here's the obligatory live demo.
new Date() doesn't throw an exception if month>12 for example, you can use Date.parse() and test the returned value with isNaN()

JavaScript Addition Date Functions

I don't really know too much about core JavaScript, just a dot of jQuery. But I know jQuery is not necessary for what I need here:
I want to use the getdate function to find out the server's day of the week. Then add a bunch of clauses like:
if its Monday add 6 to the date and return the date in MM/DD/YYYY form.
if its Tuesday add 5 to the date and return the date in MM/DD/YYYY form.
if its Wednesday add 4 to the date and return the date in MM/DD/YYYY form.
and so on until Sunday when it will add 0.
So lets say todays Monday, it will return 1/8/2012
And in real dates today's Sunday so it will really return 1/1/2012
Then I just want to call a document.write function to write the MM/DD/YYYY it returns into my HTML document.
Can anybody help me? I can clarify if you need me to...
getDay() returns the day of the week, Sunday = 0, Monday = 1, etc, etc.
So say today was Monday getDay() would return 1, which means daysToAdd would be 5.
Once we know how many days we want to add we can create a new date and add those days. We do this by getting today in milliseconds and then adding the number of days (daysToAdd) in milliseconds.
We convert days to milliseconds by multiplying by 24*60*60*1000 which is the number of milliseconds in a day.
I add 1 to the month because JavaScript returns 0 based month, but for display purposes we want to format it so that January for example is 1 not zero.
function getEndOfWeek() {
var today = new Date();
var weekDay = today.getDay();
// if you want the week to start on Monday instead of Sunday uncomment the code below
//weekDay -= 1;
//if(weekDay < 0) {
// weekDay += 7;
//}
var daysToAdd = 6 - weekDay;
var newDate = new Date(today.getTime() + daysToAdd *24*60*60*1000);
var month = newDate.getMonth() + 1;
var day = newDate.getDate();
var year = newDate.getFullYear();
var formatedDate = month + "/" + day + "/" + year;
return formatedDate;
}
You could implement in your code like so, JavaScript:
$(function() {
$("#TheDate").html(getEndOfWeek());
});
Your HTML would be something like this:
The week ends on <span id="TheDate"></span>.
You can find the jsFiddle here: jsFiddle
If you want to adjust the weekday so that you consider Monday the start of the week instead of Sunday you can do the following after you get the weekDay:
weekDay -= 1;
if(weekDay < 0) {
weekDay += 7;
}
var day = 1000*60*60*24
, nextSunday = new Date(+new Date() + day*(7-((0|(+new Date()/day)%7-3)||7)));
alert(
(101+nextSunday.getMonth()).toString().substr(1) + '/' +
(100+nextSunday.getDate()).toString().substr(1) + '/' +
nextSunday.getFullYear()
)
As fas as adding dates in JavaScipt my "DateExtensions" library does this well enough, I think. You can get it here:
http://depressedpress.com/javascript-extensions/dp_dateextensions/
Once refenced you can call "add()" as a method for any valid date and pass it any of many date parts (second, minutes, days, hours, etc). So assuming "curDate" is a valid JavaScript date object you can add 5 days like this:
newDate = curDate.add(5, "days");
Using a negative value will subtract:
newDate = curDate.add(-5, "days");
Once you get the date you want you can the use the library's dateFormat() method to display it like so:
curDate.dateFormat("MM/DD/YYYY");
There's full documentation at the link.
Integer Values for Day of Week
As for getting the integer value you want, it's actually easier that it looks (and you don't need an "if" just some math). The getDay() method of date returns the day of week with Sunday as "0" and Saturday as "6". So the week, from Sunday, would normally be:
0,1,2,3,4,5,6
First, you want to reverse that scale. That's easily done via subtraction by taking 7 (to total number of members of the set) from the value. This gives you this scale:
-7,-6,-5,-4,-3,-2,-1
We're getting closer. You want the first value to be zero as well. The simplest way (I think) to do this is to get the modulus (remainder) of the value by the total number of members. All this basically does is make "-7" a zero and leave the rest alone giving us this:
0,-6,-5,-4,-3,-2,-1
Almost done. Finally you don't want negative numbers so you need to use the Math.abs() method to eliminate the sign (get the absolute value) leaving us with our desired result:
0,6,5,4,3,2,1
For all the talk the acutual code is pretty compact:
Math.abs((cnt-7)%7)
Wrapping this into the original example gives us:
newDate = curDate.add(Math.abs((curDate.getDay()-7)%7), "days");
Server Vs Client
However take nnnnnn's comment to heart: in JavaScript the getDate() function gets the current date/time of the machine that it's running on - in the case of a web page that's the client, not the server.
If you actually meant the client time them you're set and done. If you really need the server time however that's annoying-to-impossible. If you own the server then it's actually not to hard to set up a rule that includes the current server in a cookie withing each fufilled request (you could then use my cookie library, also at the site above, to access the information!)
It's messier but depending on the server you might also be able to create an old-school server-side include that adds a bit of JavaScript to each page (preferably as a marked replace in the header) that hard-codes the date as a global variable.
You might also create a web service that returns the current server time but the client-overhead for that is insane compared to the data being delivered.
If the server's NOT yours (and you can't get the owner to provide the above) then the only real potential option is to do a straight http call and examine the HTTP "Date" header. Again however the overhead on this is immense compared to the return but it's really the only way. Any system like this would have to be very flexible however as any particular server might not return the date header or might not return it correctly.
Even if it does work understand that you might still not be getting the "server" time - or at least not the server you want. In a tiered architecture, for example an application server might render then page and hand it to a web server to return - you'd be getting the web server time, not the app server. Any number of appliances might also rewrite the headers (for example it's common to use dedicated SSL appliances to offload all the encryption work - these often re-write the headers themselves).
Sorry to get overly technical - JavaScript is definately one area where there's unfortunately rarely a "simple question". ;^)
Good Luck!

Categories

Resources