Why is this function behaving weirdly - javascript

I have the following JavaScript as below
function getExpiryDate(contract) {
var expiryDay;
var contractType;
var today = new moment();
var today1 = new moment();
var x = myFunction(4, 3);
var abc =3;
var xyz= 4;
var c = myFunction(abc, xyz);
console.log("abc is: "+abc);
console.log("xyz is: "+xyz);
console.log(today1);
expiryDay = getlastDayofMonth('Thursday',today1);
console.log(today1); /* Why has the value of today changed? */
}
function getlastDayofMonth(dayName,date1) {
var endDate = date1.endOf('month');
var lastDayOfMonth = endDate.format('dddd');
var weekDayToFind = moment().day(dayName).weekday(); //change to searched day name
var searchDate = endDate; //now or change to any date
while (searchDate.weekday() !== weekDayToFind)
{
searchDate.subtract(1, 'days');
}
return searchDate;
}
function myFunction(a, b) {
return a * b; // Function returns the product of a and b
}
When I execute I get the following output.
expirydate.js:11 abc is: 3
expirydate.js:12 xyz is: 4
expirydate.js:13 Moment { _d: Wed Aug 31 2016 10:21:04 GMT+0530 }
expirydate.js:15 Moment { _d: Thu Aug 25 2016 23:59:59 GMT+0530 }
I am totally confused on why the value of today1 changes when it is used in the function.

Because in JS, objects are passing by reference and not by value. It means you are working on the same object.
To prevent this you have to clone date1 value in the function into a variable with a scope restricted to the function

This is happening because Moment is written in a mutable style of code. It's fairly surprising behavior. After reading through your code a few times I couldn't find any obvious problem until I looked at the Moment documentation.
The .endOf() method mutates the date, aka changes the date of the moment object you call it on:
Mutates the original moment by setting it to the end of a unit of time.
So when you call that method here:
var endDate = date1.endOf('month');
It's mutating date1, meaning it modifies date1 in place and changes its time. In fact, it looks like almost all of moment's methods mutate the moment object. There's a discussion on Github about why the API is designed poorly.
In terms of solving your specific problem, it's a personal style preference. I think it's undesirable to force the user to clone an object before passing it to a function. So I would clone the moment passed in inside the function:
function getlastDayofMonth(dayName,date1) {
var endDate = date1.clone().endOf('month');

Related

Why does two JS date Objects instantianted differently?

I'd like to enable/disable a button based on a datepicker, and I have a setup for a check like this:
public dateChanged = false;
public availableFromDate: Date;
public availableToDate: Date;
initDatepickers() {
const currentDay = new Date();
this.availableFromDate = currentDay;
this.availableToDate = currentDay;
}
private dateCheck() {
if ((this.availableFromDate > this.availableToDate) || (this.availableFromDate === this.availableToDate)) {
this.dateChanged = false;
} else {
this.dateChanged = true;
}
console.log(this.dateChanged);
console.log(`Available from - ${this.availableFromDate}`);
console.log(`Available to - ${this.availableToDate}`);
}
The check works good upwards, and enables the button when from date is lower, however!
If you log the values to the console be button is disabled because the init value is false, not because the check works.
The two date objects are initialized differently (console.log dump):
true
clinics-upload-documents.component.ts:73 Available from - Fri Feb 22 2019 00:00:00 GMT+0100 (Central European Standard Time)
clinics-upload-documents.component.ts:74 Available to - Fri Feb 22 2019 10:52:31 GMT+0100 (Central European Standard Time)
It's never going to be false because the first date obj is # 0:00:00 however the 2nd is tied to current local time.
these are used to manipulate the dates:
onFromChange(fromDate) {
const dateType = 'from';
this.setDateValues(fromDate, dateType);
}
onToChange(toDate) {
const dateType = 'to';
this.setDateValues(toDate, dateType);
}
private setDateValues(date: Date, dateType: string) {
dateType === 'from' ? this.availableFromDate = new Date(date) : this.availableToDate = new Date(date);
this.dateCheck();
}
What am I missing so badly?
Change this:
const currentDay = new Date();
this.availableFromDate = currentDay;
this.availableToDate = currentDay;
To this:
const currentDay = new Date();
currentDay.setHours(0, 0, 0, 0);
this.availableFromDate = new Date(currentDay);
this.availableToDate = new Date(currentDay);
This will zero out the time portion and make date comparison straight forward.
Next, change this:
if (
(this.availableFromDate > this.availableToDate) ||
(this.availableFromDate === this.availableToDate)
)
To this (assuming that you want to check greater than or equal to):
if (this.availableFromDate >= this.availableToDate)
You cannot compare two dates with === although you can compare them using < <= >= >.
It looks like the Date objects that come in from your date picker via onFromChange/onToChange are pure dates (they are all at midnight), while the date objects that you create with Date() will have the current time included. The js Date class should really have been called DateTime. The mismatched times will cause the === comparison to fail.
Try using something like this to set availableFromDate and availableToDate in your initDatepickers function:
private getCurrentDate() {
const date = new Date();
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
date.setMilliseconds(0);
}
EDIT: Nevermind, the === will still fail if you do this, because Date is an object, so === checks for reference equality. Two Date objects can hold the same underlying date/time values, but they are still considered to be separate objects. Things like numbers, however, are value types, so === will tell you if the two values are equal. E.g:
5 === 5; // True, because pure numbers are value types
const number1 = { number: 5 }; // This is an object, so it is a reference type. Dates are also objects.
const number2 = { number: 5 }; // Another reference type
number1 === number2; // False, because although number1 and number2 hold the same values, they are still distinct objects.
See Salman's answer for a proper solution.

Using alert with object date

I'm learning JavaScript and I'm trying to figure out object date. Specifically toLocaleDateString method.
I've come across two different basic examples of it. The first one is:
var d = new Date();
var n = d.toLocaleDateString();
alert(d);
And the second one is:
var myDate = new Date();
alert(myDate.toLocaleDateString());
Both work fine, but they give different date formats. First one gives: Mon Jul 16 2018 18:34:00 GMT+0200 (Central European Summer Time), while the second one is just 7/16/2018. And I can't see why? What am I missing here?
Thanks!
small error, in the first example you should be alerting n instead of d
var d = new Date();
var n = d.toLocaleDateString();
alert(n);
d is the date object while n is the date string. you are getting different date formats because you are alerting a date object in the first example and a date string in the second
This is simply because you are not alerting the modified date string (which is n) in the first alert:
var d = new Date();
var n = d.toLocaleDateString();
alert(n);
var myDate = new Date();
alert(myDate.toLocaleDateString());
You're printing the wrong variable in the first block. When you declare a variable with the reserved word var in the second line you're storing there the formatted date that you want to output, so you can just alert(n) or you can avoid declaring the var:
var d = new Date();
d.toLocaleDateString();
alert(d);

Passing value into methods by dot prefixes

What I want to do should be incredibly simple but I suppose I'm not understanding the basic concepts and causing me more confusion than I'd like to admit.
I want to pass a value into a function by "chaining" it instead of passing the value as an argument.
I want to be able to do this:
var formattedDate = myDate.convertTime()
Instead of:
var formattedDate = convertTime(myDate);
In my search I've come across many examples of how to use call functions inside objects, but they always start with declaring a new object and calling the function inside that object that simply updates predefined variables like age, name, or location as in new Obj(name, age) which doesn't seem to be what I am looking for when applied to my situation. I'd really appreciate some guidance in the right direction.
Are you looking for something like following
String.prototype.myFunction = function() {
return this.toUpperCase();
}
var text = "sample text";
console.log(text.myFunction()); // calling user defined function
Fiddle: https://jsfiddle.net/mna7jxrs/
Update 1:
For example, We're passing a date string to convertTime() and it is converting it to UTC String
Date.prototype.convertTime = function() {
return this.toUTCString();
}
var date = new Date();
console.log(date); // Mon Oct 31 2016 11:56:57 GMT+0530 (India Standard Time)
console.log(date.convertTime()); // Mon, 31 Oct 2016 06:26:57 GMT
Fiddle: https://jsfiddle.net/doc7gL2g/
What you want to do is add a method to your object myDate.
Lets assume myDate is a date created by var myDate = new Date();.
If you want to add it only to the current object, you can do this way:
var myDate=new Date();
myDate.convertTime = function() {
console.log("I’m working on", this);
};
myDate.convertTime(); // -> see console
Now to make it more generic, you want to do something we call monkey patching:
Date.prototype.convertTime = function() {
console.log("I’m working on", this);
};
var myDate = new Date();
myDate.convertTime(); // -> see console
var myOtherDate = new Date();
myOtherDate.convertTime(); // -> see console
A working pen for the monkey patch: http://codepen.io/BuonOmo/pen/RGzYmz?editors=0012

Add Number of months to a Date in a text field

function addDays(){
var myDate =document.getElementById('treatdate');
var numberOfDaysToAdd = document.getElementById('resultdays');
var tset = numberOfDaysToAdd.value;
var result1 = myDate.value.addMonths(parseInt(tset));
var pldate= moment(result1).format('YYYY-MM-DD');
return pldate; }
'treatdate' is an id for a treatment date which is pulled from my database. Thanks in advance.
You can always use moment.js library for Date manipulations. http://momentjs.com/
For your problem you can do the following.
function addDays(){
var datefrmDb = $('#treatdate').val();
var monthstoadd= $('#resultdays').val();
var new_date = (moment(datefrmDb, "YYYY-MM-DD").add('months', monthstoadd)).format("YYYY-MM-DD");
return new_date;
}
If your date format is yyyy-MM-dd
var myDate = new Date(document.getElementById('treatdate').value);
this will a date and time.
Example:
var dd = new Date("2014-02-02 11:11:11")
console log
Sun Feb 02 2014 11:11:11 GMT+0000 (GMT Standard Time)
See if this snippet can help you understand how to manipulate dates. To change/add the months to a Date() object you can use the setMonth() method.
numberOfMonthsToAdd = 5; // the number of months that you want to add
date = new Date(); // creating a Date object
date.setMonth(numberOfMonthsToAdd); // adding the number of months
Note that you may add numbers beyond 12 - the object handles the date, and jumps to the next year.

Google Form on Submit get values and format the time

I am using Google Apps Script with a Google form. When the user submits the Google Form I get a value from a question. I then take that value and make it a date object, from what I saw on this post about daylight savings I use that to determine the timezone. I run the date object through Utilities.formatDate and want to get the correctly formatted date.
example: 9:00 AM
But instead I am getting a completely different time than expected.
My question is: Can someone help me understand why the below code is outputting a time that is 3 hours different?
function onSubmit(e) {
var values = e.values;
Logger.log(values);
try {
var start1 = new Date(values[3]);
var startN = new Date(start1).toString().substr(25,6)+"00";
var startT = Utilities.formatDate(start1, startN, "h:mm a");
Logger.log(startT);
} catch(error) {
Logger.log(error);
}
}
The assumption that Utilities formatDate does not support GMT... parameter is not true.
The post you mentioned in reference is used to get calendar events and is a useful way to get the right value when you get events from another daylight saving period (getting the TZ info from the calendar event itself), for example events for next month will be in "summer time" while we are still in "winter time"...
Your issue might come from different sources depending on time zone settings of your script vs timezone of the source. Could you describe the exact configuration in which you use this script ?
In the mean time, here is a small code that demonstrates how the code is working + the logger results :
function testOnSubmit() {
var eventInfo = {};
var values = {};
values['3'] = new Date();
eventInfo['values'] = values;
Logger.log('eventInfo = '+JSON.stringify(eventInfo)+'\n\n');
onSubmit(eventInfo);
}
function onSubmit(e) {
var values = e.values;
try {
var start1 = new Date(values[3]);
Logger.log('onSubmit log results : \n');
Logger.log('start1 = '+start1)
var startN = new Date(start1).toString().substr(25,6)+"00";
Logger.log('startN = '+startN);
var startT = Utilities.formatDate(start1, startN, "h:mm a");
Logger.log('result in timeZone = '+startT);
} catch(error) {
Logger.log(error);
}
}
EDIT : additionally, about the 30 and 45' offset, this can easily be solved by changing the substring length like this :
var startN = new Date(start1).toString().substr(25,8);
the result is the same, I had to use the other version a couple of years ago because Google changed the Utilities.formatDate method at some moment (issue 2204) but this has been fixed.
EDIT 2 : on the same subject, both methods actually return the same result, the GMT string has only the advantage that you don't have to know the exact timezone name... there is also the Session.getScriptTimeZone() method. Below is a demo script that shows the resulst for 2 dates in January and July along with the log results :
function testOnSubmit() {
var eventInfo = {};
var values = {};
values['3'] = new Date(2014,0,1,8,0,0,0);
eventInfo['values'] = values;
Logger.log('eventInfo = '+JSON.stringify(eventInfo)+'\n\n');
onSubmit(eventInfo);
values['3'] = new Date(2014,6,1,8,0,0,0);
eventInfo['values'] = values;
Logger.log('eventInfo = '+JSON.stringify(eventInfo)+'\n');
onSubmit(eventInfo);
}
function onSubmit(e) {
var values = e.values;
var start1 = new Date(values[3]);
Logger.log('onSubmit log results : ');
Logger.log('start1 = '+start1)
var startN = new Date(start1).toString().substr(25,8);
Logger.log('startN = '+startN);
Logger.log('result in timeZone using GMT string = '+Utilities.formatDate(start1, startN, "MMM,d h:mm a"));
Logger.log('result in timeZone using Joda.org string = '+Utilities.formatDate(start1, 'Europe/Brussels', "MMM,d h:mm a"));
Logger.log('result in timeZone using Session.getScriptTimeZone() = '+Utilities.formatDate(start1, Session.getScriptTimeZone(), "MMM,d h:mm a")+'\n');
}
Note also that the Logger has its own way to show the date object value ! it uses ISO 8601 time format which is UTC value.
Try this instead:
var timeZone = Session.getScriptTimeZone();
var startT = Utilities.formatDate(start1, timeZone, "h:mm a");
The Utilities.formatDate function expects a time zone that is a valid IANA time zone (such as America/Los_Angeles), not a GMT offset like GMT+0700.
I am making the assumption that Session.getScriptTimeZone() returns the appropriate zone. If not, then you might need to hard-code a specific zone, or use a different function to determine it.
Additionally, the +"00" in the script you had was assuming that all time zones use whole-hour offsets. In reality, there are several that have 30-minute or 45-minute offsets.

Categories

Resources