Overriding the Javascript Date constructor? - javascript

I am developing a browser application that is sensitive to the current date.
Throughout my application's code, I call new Date and perform calculations based on the current time and render the view accordingly.
In order to test my application for different potential calendar days, I would have to constantly change my system clock to the past or future, which is an annoyance and probably not healthy for my computer.
So purely for testing purposes (I would never use this code in production), I decided to override the built-in Date constructor by doing this in the console:
// create a date object for this Friday:
var d = new Date(2012, 0, 20)
//override Date constructor so all newly constructed dates return this Friday
Date = function(){return d}
With this assumption in mind, I tried this and got strange results:
var now = new Date
Sat Apr 07 2012 00:00:00 GMT-0400 (EDT)
now = new Date
Tue Jul 10 2012 00:00:00 GMT-0400 (EDT)
now = new Date
Wed Jul 09 2014 00:00:00 GMT-0400 (EDT)
now = new Date
Wed Jun 07 2023 00:00:00 GMT-0400 (EDT)
...and so on....
My question is, what exactly is going on here?
If I overrode the constructor to return a static date, why does it give unrelated and constantly incrementing dates?
Also, is there an effective way I can override the Date constructor to return a static date in the future without having to go through all date instantiation calls in my code and modifying the output?
Thanks in advance.
EDIT:
I tried my code in a fresh window and it worked as expected.
It seems the culprit was the jQuery UI datepicker plugin which was calling its "refresh" method. When I disable its call, the date overriding works normally, but as soon as I use the datepicker, the strange behavior above occurs.
No idea why this popular plugin would somehow affect something global like this. If anyone has any ideas, let me know.
Sorry for not figuring out the true culprit earlier.

I also faced this problem and ended up writing a module for that. Perhaps it's useful for somebody:
Github: https://github.com/schickling/timemachine
timemachine.config({
dateString: 'December 25, 1991 13:12:59'
});
console.log(new Date()); // December 25, 1991 13:12:59

I tested your code:
// create a date object for this Friday:
var d = new Date(2012, 0, 20);
//override Date constructor so all newly constructed dates return this Friday
Date = function(){return d;};
var now = new Date()
console.log(now);
now = new Date()
console.log(now);
now = new Date()
console.log(now);
now = new Date()
console.log(now);
And the result ???? Why so different?
Date {Fri Jan 20 2012 00:00:00 GMT+0700 (SE Asia Standard Time)}
Date {Fri Jan 20 2012 00:00:00 GMT+0700 (SE Asia Standard Time)}
Date {Fri Jan 20 2012 00:00:00 GMT+0700 (SE Asia Standard Time)}
Date {Fri Jan 20 2012 00:00:00 GMT+0700 (SE Asia Standard Time)}
EDIT:
I saw that whenever you interact with the Date Picker, the behavior goes different. Try another test, change the now is something like interact with Date Picker:
// create a date object for this Friday:
var d = new Date(2012, 0, 20);
//override Date constructor so all newly constructed dates return this Friday
Date = function(){return d;};
var now = new Date();
var another = new Date();
console.log(now);
another.setDate(13);
now = new Date()
console.log(now);
And the result is:
Date {Fri Jan 20 2012 00:00:00 GMT+0700 (SE Asia Standard Time)}
Date {Fri Jan 13 2012 00:00:00 GMT+0700 (SE Asia Standard Time)}
So, what goes wrong?
You already overridden core Date function by
Date = function(){return d;}; // after construction, all date will be d (2012-01-20)
var now = new Date(); // you instantiate a date, but actually now variable is d (2012-01-20)
var another = new Date(); // you instantiate a date, but another is still d (2012-01-20)
another.setDate(13); // change another date to 13 is to change now to 13 (because now and another is still one d)
now = new Date() // still d
console.log(now); // print out now (2012-01-13)
So, you overrides core Date function by a function that causes all date use the same (just one) instance, which is d (2012-01-20). Change any dates affect others.

This is working for me.
I.E how to return process time 1 minute back
Date = class extends Date{
constructor(options) {
if (options) {
super(options);
} else {
super(Date.now() - 1000 * 60);
}
}
};

Give this a shot.
var d = new Date(2012, 0, 20);
// undefine date so that it will only return what your function returns
Date = undefined;
Date = function(){return d;}
Modifying the prototype to point to your object should do the trick.
I believe the strange behavior you were experiencing earlier was that privately Date holds some notion of time, and since the prototype points to that internal clock, you were getting random times.

Alex Stanovsky's answer almost did it for me but this modification made the constructor work like before when given parameters.
Date = class extends Date {
constructor(...options) {
if (options.length) {
super(...options);
} else {
super(2019, 2, 11, 19, 19);
}
}
};

I used the below to enforce UTC dates
var _f = function(item) {
Date.prototype["get" + item] = Date.prototype["getUTC" + item];
Date.prototype["set" + item] = Date.prototype["setUTC" + item];
}
var _d = ['Milliseconds', 'Seconds', 'Minutes', 'Hours', 'Date', 'Month', 'FullYear', 'Year', 'Day'];
_d.forEach(_f);
Date = class extends Date {
constructor(...options) {
if (options.length == 1 && options[0].constructor == Date) {
super(options[0]);
} else if (options.length > 0) {
super(Date.UTC(...options));
} else {
super(Date.UTC());
}
}
};

Related

Javascript show next possible delivery date

I have a website, where the client can see the delivery date.
Here is the code
function getProductRecordHTML(Product, index, quantity, ProductType, blok)
{
var manufacturer = "", article_show = "", name = "";
var time_to_exe = Product.time_to_exe;
var displayDate;
if(time_to_exe == 6)
{
const date = new Date();
date.setDate(date.getDate() + parseInt(time_to_exe));
displayDate = date.toLocaleDateString();
}
if (displayDate) {
time_to_exe = displayDate;
} else {
time_to_exe = time_to_exe + "d";
}
For now, time_to_exe gives the delivery time in days
This code calculates the next delivery date just by adding these 6 days to the current date.
My main goal is to get the period from Monday to Wednesday at 12 pm, if it's true then time_to_exe shows the date of next Monday (for example 23/08/2021), but if it's false (for example it's period from Wednesday after 12 pm till Sunday 11:59 pm) then time_to_exe show Monday date 1-week after (for example 30/08/2021).
I hope explained clearly.
Already many thanks to the user #Christopher for helping before.
One way to work with dates much easier is to use a library like moment.js (which I have been using for years), or maybe even better a newer library like Luxon, since moment.js is going into maintenance mode.
Let's see how you would achieve your date calculation using moment.js:
var orderDateTime = moment('08/18/2021 8:15 am');
// Get Sunday (first day) of this week and add 3 days (to get to Wednesday) and set the time to 11:59am
var cutOffDate = moment().startOf('week').add(3,'days').set({'hour': 11, 'minute': 59, 'second': 59});
// Initialize delivery date from order date
var deliveryDate = orderDateTime.clone();
if (orderDateTime.isSameOrBefore(cutOffDate)) {
deliveryDate = deliveryDate.add(1,'week').startOf('week').add(1,'day'); // Monday next week
} else {
deliveryDate = deliveryDate.add(2,'week').startOf('week').add(1,'day'); // Monday the week after next
}
alert("Delivery Date is "+deliveryDate.format("MM/DD/YYYY"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
That's all you need for your calculation.
You can also find a fiddle of the code above at https://jsfiddle.net/yLpf3vxj/
The Javascript Date object includes a getDay() method that returns a numerical value for the day of the week. From this it's possible to work out the previous Monday's date, and then add 7 or 14 depending on the original date.
This function takes a JavaScript Date object and returns the relevant Monday as another Date.
Note that setDate() will update month and year as appropriate if the date being set is outside the current month.
function getMonday(orderDate) {
orderDate = orderDate || new Date();
if (!(orderDate instanceof Date)) {
throw "Invalid date";
}
// Get the date last Monday
let lastMonday = new Date(orderDate);
lastMonday.setDate(lastMonday.getDate()-lastMonday.getDay()+1);
// If order date is before Wednesday noon, deliver next Monday. Add 7 to last Monday date
if (orderDate.getDay()<3 || ((orderDate.getDay() === 3) && orderDate.getHours()<12)) {
lastMonday.setDate(lastMonday.getDate()+7);
} else {
// Otherwise. add 14 to last Monday date.
lastMonday.setDate(lastMonday.getDate()+14);
}
return lastMonday;
}
input:
let testDates = [
new Date(),
new Date(2021,7,18,11),
new Date(2021,7,18,13),
new Date(2021,9,1,11),
'bad date'
];
Output:
Wed Aug 18 2021 10:14:41 GMT+1200 (New Zealand Standard Time), Mon Aug 23 2021 10:14:41 GMT+1200 (New Zealand Standard Time)
Wed Aug 18 2021 11:00:00 GMT+1200 (New Zealand Standard Time), Mon Aug 23 2021 11:00:00 GMT+1200 (New Zealand Standard Time)
Wed Aug 18 2021 13:00:00 GMT+1200 (New Zealand Standard Time), Mon Aug 30 2021 13:00:00 GMT+1200 (New Zealand Standard Time)
Fri Oct 01 2021 11:00:00 GMT+1300 (New Zealand Daylight Time), Mon Oct 11 2021 11:00:00 GMT+1300 (New Zealand Daylight Time)
Invalid Date
Demo:https://jsfiddle.net/dzsf34ga/

Are references to Date object inside a map function carried over?

I am running into a weird issue where the dates in a function I have are being written to objects (and then stored) are having some side effects.
const normalizedEntries = allEntries.map((item) => {
const dateObj = new Date(item.timestamp * 1000);
const groupedDate = new Date(item.timestamp * 1000);
groupedDate.setHours(0, 0, 0, 0);
console.log(dateObj);
console.log(groupedDate);
const normalizedEntry: NormalizedEntry = {
sites: item.sites,
date: dateObj.toISOString(),
groupedDate: groupedDate.toISOString(),
};
console.log(normalizedEntry);
return normalizedEntry;
});
All I am trying to do in this function is take in an object that has a timestamp and a number (the sites field) and produce this new typed object NormalizedEntry which you can see has the original date, the sites (number) and a grouped date (which is just the day but with the time set to zero so I can group multiple objects later in redux).
Within the first two passes of the loop (for now I am working with a small set - 300, of test data) the data begins to get out of whack and I don't understand why. Is there something going on with the references of the dates? Or perhaps something about dates I do not understand yet? Below is the output of those console logs from above
// iteration one
Wed Jan 20 2021 17:16:49 GMT-0500 (Eastern Standard Time)
Wed Jan 20 2021 00:00:00 GMT-0500 (Eastern Standard Time)
{sites: 26, date: "2021-01-20T22:16:49.000Z", groupedDate: "2021-01-20T05:00:00.000Z"}
// iteration two
Wed Jan 20 2021 20:27:58 GMT-0500 (Eastern Standard Time)
Wed Jan 20 2021 00:00:00 GMT-0500 (Eastern Standard Time)
{sites: 41, date: "2021-01-21T01:27:58.000Z", groupedDate: "2021-01-20T05:00:00.000Z"}
As you can see the console.log(dadte) prints out the correct thing in the second iteration but for some reason when setting it to the object below it says it's on the 21st versus the 20th which it clearly is 5 lines above. This is in turn throwing all of my data out of whack.
EDIT:
Now trying to resolve it being a timezone issue as pointed out from below but this is still not resolving the issue:
const normalizedEntries = allEntries.map((item, idx) => {
let originDate = new Date(item.timestamp * 1000);
const timezoneDifference = originDate.getTimezoneOffset() / 60;
const correctedDate = new Date(
originDate.setHours(originDate.getHours() + timezoneDifference, 0, 0, 0)
);
let groupedDate = correctedDate;
groupedDate.setHours(0, 0, 0, 0);
console.log(originDate.toString());
console.log(groupedDate.toString());
const normalizedEntry: NormalizedAcquisition = {
sites: item.sites,
date: originDate.toISOString(),
groupedDate: groupedDate.toISOString(),
};
return normalizedEntry;
});
The issue is the timezone
The dates have gmt -5
You set hours to 0 in that timezone
When you format as iso it uses gmt
You need to get the timezone and take into account when setting the time to 00:00
See mdn date.getTimezoneOffset

Javascript : Increase a Date object by a year

I'm assign a new date object to my object attribute like that :
giftObject.purshasedDate = new Date()
which give a date format :
Date Thu Feb 20 2020 13:36:37 GMT+0100 (heure normale d’Europe
centrale)
I want to increase this date by one year, I tried :
new Date().setFullYear(giftObject.purshasedDate.getFullYear() + 1) but it give a number serial like this : 1613824899244
I do not understand what that number serial mean! it's a date or should a try some thing else ?
By default all dates object are timestamps.
JavaScript Date objects represent a single moment in time in a
platform-independent format. Date objects contain a Number that
represents milliseconds since 1 January 1970 UTC.
Source : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
I think the default new Date() object can display itself to string by in fact it's also a timestamp.
If you want to display a date as string, you have to use the toLocaleString() method on Date.
I tried by updating the original date and it return the string of the date, don't know why but it's work by updating the original date.
Example :
let giftObject = {};
giftObject.purshasedDate = new Date();
giftObject.purshasedDate.setFullYear(giftObject.purshasedDate.getFullYear() + 1);
console.log(giftObject.purshasedDate)
Result : "20/02/2021 à 13:55:49" for my French browser
const oldDate = new Date("Date Thu Feb 20 2020 13:36:37 GMT+0100")
const newDate = oldDate.setFullYear(oldDate.getFullYear() + 1)
const dateWithPlusOneYear = new Date(newDate)
console.log(new Date(dateWithPlusOneYear))
//Sat Feb 20 2021 13:36:37 GMT+0100 (Central European Standard Time)
Please use this one:
purshasedDate = new Date();
purshasedDate = new Date(purshasedDate.setFullYear(purshasedDate.getFullYear() + 1));

Unable to get the first day of the Month in Angular 2/4

I'm calculating the dates for my application like date,week,month.
First I am defining day,week,month,custom like:
this.reportTypes = [{TypeId: 1, Type: 'Day'}, {TypeId: 6, Type: 'Week'}, {TypeId: 30, Type: 'Month'}, {
TypeId: 10,
Type: 'Custom'
}]
Next I'm defining dates like:
var currdate = new Date();
if(reportType==1){
// this.reportDataFromDate=currdate;
// this.reportDataToDate=currdate;
//This is for setting the current date
this.reportDataFromDate= currdate;
this.reportDataToDate= currdate;
}
else if(reportType==30){
var First = new Date(currdate.getFullYear(),currdate.getMonth(),1);
this.reportDataFromDate=First;
this.reportDataToDate=currdate;
}
else if(reportType!=10){
var last = new Date(currdate.getTime() - (reportType * 24 * 60 * 60 * 1000));
this.reportDataFromDate=last;
this.reportDataToDate=currdate;
}
}
The problem is after selecting reportType == 30 then it has to get the first day of the month.
It is showing the date as 1-Dec-2017 but it is getting the data of till 30th November 2017?
This is screenshot of the SQL server. I'm sending the date as 1st Dec 2017 but it is getting 30-11-2017.
When the Date() constructor is invoked with integers, the result is a date object with that date assumed your systems (read browser/os) timezone.
Example:
let d = new Date(2017);
// returns Thu Jan 01 1970 01:00:02 GMT+0100 (W. Europe Standard Time)
// and with d.toUTCString(): Fri, 30 Dec 2016 23:00:00 GMT
Which may end up in an entire different year when sending to the server
Using the string constructor and specifying timezone will help you overcome this.
Example:
let d = new Date('2017z');
// returns Sun Jan 01 2017 01:00:00 GMT+0100 (W. Europe Standard Time)
// and with d.toUTCString(): Sun, 01 Jan 2017 00:00:00 GMT
The latter which is what you should pass to a server, and normally do calculations on.
However, note that calculations with dates are a complicated matter best left to a library like moment.js. To get a feel of what you are dealing with have a look at this great talk from the WebRebel conference.
So to actually give an answer to your title, try this example which creates the date in a simple string using UTC:
let d = new Date(currdate.getUTCFullYear() + ' ' +(currdate.getUTCMonth() + 1) + ' 1z');
d.getUTCDay(); // returns the day as an integer where Monday is 0.
Note that we add 1 month due to getUTCMonth() returns January as 0.
Why the difference?
The new Date(x,y,z) constructor treats the parameters as local date values.
See MDB Web Docs - Date
Note: Where Date is called as a constructor with more than one argument, the specifed arguments represent local time. If UTC is desired, use new Date(Date.UTC(...)) with the same arguments.
But, under the hood the date is stored as UTC (milliseconds since 1 Jan 1970).
const date = new Date(2017, 11, 29);
console.log('valueOf()', date.valueOf()) // 1514458800000
and the UTC date is different to your local date (see trailing 'Z' indicates UTC)
const date = new Date(2017, 11, 29);
console.log('date', date) // "2017-12-28T11:00:00.000Z" (trailing 'Z' means UTC)
// The difference in minutes between browser local and UTC
console.log('getTimezoneOffset()', date.getTimezoneOffset() )
and when you send it to the server, JSON sends it as UTC
const date = new Date(2017, 11, 29);
console.log('JSON', date.toJSON())
// JSON will yield string version of UTC === 2017-12-28T11:00:00.000Z
How to fix it
Well, you might decide that you actually want the date/time in local, and conclude it's not broken.
But if you want to send UTC to the server, wrap the parameters in Date.UTC()
const date = new Date(Date.UTC( 2017, 11, 29 ))
console.log('date.toJSON()', date.toJSON() ) // 2017-12-29T00:00:00.000Z
What about month parameter === 11?
From the MDB page referenced above,
Note: The argument month is 0-based. This means that January = 0 and December = 11.
If you are using .Net Web API as backend, you can config the timezone in Web API WebApiconfig.cs like below. It will serialize the time in UTC.
public static void Register(HttpConfiguration config)
{
config.Formatters.JsonFormatter.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
}
Or use
config.Formatters.JsonFormatter.SerializerSettings.DateTimeZ‌​oneHandling = Newtonsoft.Json.DateTimeZoneHandling.RoundtripKind; //Time zone information should be preserved when converting.

date string that will ignore timezone offset

I am at (UTS-05:00) Eastern Time (US & Canada)
i.e, new Date().getTimezoneOffset() == 300 seconds.
Now, I have a API endpoint (JSON) that returns a date string like this.
{
someDate: '2016-01-01T00:40:00.000+00:00'
}
Here, I pass it to Date constructor like this
var dateString = "2016-01-01T00:40:00.000+00:00";
var someDay = new Date(dateString);
console.log(someDay)
Mozilla Firefox console shows
Date {Fri Jan 01 2016 00:40:00 GMT-0500 (Eastern Summer Time)}
Google Chrome console shows
Thu Dec 31 2015 19:40:00 GMT-0500 (Eastern Standard Time)
Chrome is taking the TimezoneOffset into consideration and Firefox is not. What can I do to get a Date that doesn't take Offset into consideration like FireFox in Chrome?
You can do it by:
var dates = '2016-01-01T00:40:00.000+00:00'.split(/-|T|:/);
var newDate = new Date(dates[0], dates[1]-1, dates[2], dates[3], dates[4]);
This hack works (not very clean, but does the job)
var dateString = '2016-07-27T01:40:30';
var dateParts = dateString.split(/-|T|:/);
var saneDate = new Date(
+dateParts[0],
dateParts[1] - 1,
+dateParts[2],
+dateParts[3],
+dateParts[4],
+dateParts[5]);
console.log(saneDate);

Categories

Resources