How to JSON stringify a javascript Date and preserve timezone - javascript

I have a date object that's created by the user, with the timezone filled in by the browser, like so:
var date = new Date(2011, 05, 07, 04, 0, 0);
> Tue Jun 07 2011 04:00:00 GMT+1000 (E. Australia Standard Time)
When I stringify it, though, the timezone goes bye-bye
JSON.stringify(date);
> "2011-06-06T18:00:00.000Z"
The best way I can get a ISO8601 string while preserving the browser's timezone is by using moment.js and using moment.format(), but of course that won't work if I'm serializing a whole command via something that uses JSON.stringify internally (in this case, AngularJS)
var command = { time: date, contents: 'foo' };
$http.post('/Notes/Add', command);
For completeness, my domain does need both the local time and the offset.

Assuming you have some kind of object that contains a Date:
var o = { d : new Date() };
You can override the toJSON function of the Date prototype. Here I use moment.js to create a moment object from the date, then use moment's format function without parameters, which emits the ISO8601 extended format including the offset.
Date.prototype.toJSON = function(){ return moment(this).format(); }
Now when you serialize the object, it will use the date format you asked for:
var json = JSON.stringify(o); // '{"d":"2015-06-28T13:51:13-07:00"}'
Of course, that will affect all Date objects. If you want to change the behavior of only the specific date object, you can override just that particular object's toJSON function, like this:
o.d.toJSON = function(){ return moment(this).format(); }

I'd always be inclined to not mess with functions in the prototype of system objects like the date, you never know when that's going to bite you in some unexpected way later on in your code.
Instead, the JSON.stringify method accepts a "replacer" function (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter) which you can supply, allowing you to override the innards of how JSON.stringify performs its "stringification"; so you could do something like this;
var replacer = function(key, value) {
if (this[key] instanceof Date) {
return this[key].toUTCString();
}
return value;
}
console.log(JSON.stringify(new Date(), replacer));
console.log(JSON.stringify({ myProperty: new Date()}, replacer));
console.log(JSON.stringify({ myProperty: new Date(), notADate: "I'm really not", trueOrFalse: true}, replacer));

Based on Matt Johnsons 's answer, I re-implemented toJSON without having to depend on moment (which I think is a splendid library, but a dependency in such a low level method like toJSON bothers me).
Date.prototype.toJSON = function () {
var timezoneOffsetInHours = -(this.getTimezoneOffset() / 60); //UTC minus local time
var sign = timezoneOffsetInHours >= 0 ? '+' : '-';
var leadingZero = (Math.abs(timezoneOffsetInHours) < 10) ? '0' : '';
//It's a bit unfortunate that we need to construct a new Date instance
//(we don't want _this_ Date instance to be modified)
var correctedDate = new Date(this.getFullYear(), this.getMonth(),
this.getDate(), this.getHours(), this.getMinutes(), this.getSeconds(),
this.getMilliseconds());
correctedDate.setHours(this.getHours() + timezoneOffsetInHours);
var iso = correctedDate.toISOString().replace('Z', '');
return iso + sign + leadingZero + Math.abs(timezoneOffsetInHours).toString() + ':00';
}
The setHours method will adjust other parts of the date object when the provided value would "overflow". From MDN:
If a parameter you specify is outside of the expected range, setHours() attempts to update the date information in the Date object accordingly. For example, if you use 100 for secondsValue, the minutes will be incremented by 1 (minutesValue + 1), and 40 will be used for seconds.

When I stringify it, though, the timezone goes bye-bye
That’s because Tue Jun 07 2011 04:00:00 GMT+1000 (E. Australia Standard Time) is actually the result of the toString method of the Date object, whereas stringify seems to call the toISOString method instead.
So if the toString format is what you want, then simply stringify that:
JSON.stringify(date.toString());
Or, since you want to stringify your “command” later on, put that value in there in the first place:
var command = { time: date.toString(), contents: 'foo' };

If you have a JS Date Object and want to stringify it to preserve the timezone, then you should definitely use toLocaleDateString().
It is a very powerful helper function that can help you format your Date object in every way possible.
For example, if you want to print "Friday, February 1, 2019, Pacific Standard Time",
const formatDate = (dateObject : Date) => {
const options: any = {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
timeZoneName: 'long'
};
return dateObject.toLocaleDateString('en-CA', options);
};
Thus, by modifying the options object, you can achieve different styles of formatting for your Date Object.
For more information regarding the ways of formatting, refer to this Medium article: https://medium.com/swlh/use-tolocaledatestring-to-format-javascript-dates-2959108ea020

let date = new Date(JSON.parse(JSON.stringify(new Date(2011, 05, 07, 04, 0, 0))));

I've created a small library that preserves the timezone with ISO8601 string after JSON.stringify. The library lets you easily alter the behavior of the native Date.prototype.toJSON method.
npm: https://www.npmjs.com/package/lbdate
Example:
lbDate().init();
const myObj = {
date: new Date(),
};
const myStringObj = JSON.stringify(myObj);
console.log(myStringObj);
// {"date":"2020-04-01T03:00:00.000+03:00"}
The library also gives you options to customize the serialization result if necessary.

Related

Troubleshooting a Date That Won't Parse in Javascript

Getting date properties back from a C# web API that seemed fine but ran into issues when plugging it into DevExtreme DateBox. It was throwing an error of 'getFullYear is not a function' so I checked the dates against this function I found here -
let r: any = http.post('/get', { Param1: 2, Param2: 1 });
console.log(r.StartDate);
console.log(this.isValidDate(r.StartDate));
r.StartDate = new Date(r.StartDate);
r.EndDate = moment(r.EndDate);
console.log('Start Date', this.isValidDate(r.StartDate));
console.log('End Date', this.isValidDate(r.EndDate));
isValidDate(d: any): void {
if (Object.prototype.toString.call(d) === "[object Date]") {
console.log('it is a date');
if (isNaN(d)) { // d.getTime() or d.valueOf() will also work
console.log('date object is not valid');
} else {
console.log('date object is valid');
}
} else {
console.log('not a date object');
}
}
StartDate: "/Date(1657512000000)/"
not a date object
undefined
it is a date
date object is not valid
Start Date undefined
not a date object
End Date undefined
Not sure why this hasn't come up before with this API but didn't want to look to DevExpress given that I can't produce a valid date.
I'm providing this answer to demonstrate one way to parse out the timestamp in the string you have of the following format, inferred by console.log(r.StartDate); ... /Date(TS)/:
// Provided the date has the following structure in a string
var anyStartDate = "/Date(1657512000000)/";
// Prepare to parse it out by getting the positions of the parentheses
var openParens = anyStartDate.indexOf("(");
var closeParens = anyStartDate.indexOf(")");
// Parse out the timestamp
var timeStampStr = anyStartDate.substring(openParens + 1, closeParens);
console.log( timeStampStr ); // 1657512000000
// Convert timestamp to an int. You can do this when you create the obj, but I am separating it here for explanation purposes.
var timeStampInt = parseInt( timeStampStr );
// Now create a date object
var dateObj = new Date( timeStampInt );
console.log( dateObj );
// (on the machine I'm on):
// Outputs: Mon Jul 11 2022 00:00:00 GMT-0400 (Eastern Daylight Time)
// Or outputs: 2022-07-11T04:00:00.000Z
Now I don't know which library(ies) you are using to handle dates so I just went with the native Date object. You can use this SOLUTION however on further insights to apply it to your code.
The point is once the timestamp is extracted, it can be then used to create a Date object, and thus utilize all the methods that are inherent to that class.
In terms of the "timezone", to get it to UTC, it's already in UTC but javascript formats it to your computer's locale. Internally it's still UTC. There's a way to display it as strictly UTC which is in the docs.
`

How to format a Date in a Specific Format in React.js?

I want to show the full date formatted from this 2020-11-09T17:50:00.000Z
to this 22/1/2020 14:20:22 format. I know how get the desired format via moment.js, but want to achieve this with JavaScript Date.
Here is what I have now, but this is not what I want.
let d = new Date("2020-11-09T17:50:00.000Z".toLocaleString("en-US"))
console.log(d);
Any help will be appreciated
You can always do it manually, the Date API only has a limited set of functions like .toLocaleDateString() which will give you "11/9/2020" and .toGMTString() will return "Mon, 09 Nov 2020 17:50:00 GMT".
Using your Date APIs, you can build the string yourself using what you have.
var timeString = d.toGMTString().split(" ")[4]; //This will return your 17:50:00
//For the date string part of it
var dateNumber = d.getDate();
var monthNumber = d.getMonth() + 1;
var yearNumber = d.getFullYear();
var dateString = `${dateNumber}/${monthNumber}/${yearNumber}`;
var finalDateString = [dateString, timeString].join(" ");
toLocaleString() can produce many formats, and you can choose the locale to get the format (or close to it) that you want.
The locale "en-GB" gives you almost what you want; you just need to remove the comma that it puts in...
let d = new Date(2020, 0, 22, 14, 20, 22);
let output = d.toLocaleString("en-GB")
.replace(',' ,'');
console.log(output);
You can actually control the output further by using the options parameter.
But also see the Intl object for its DateTimeFormat constructor.

How to convert short dates to this format: DDD MMM DD YYYY GMT+0800 (X country standard time)?

When I do new Date() I get:
Thu Dec 28 2017 10:17:58 GMT+0800 (台北標準時間)
If I apply .valueOf() to that date I get:
1514427724039
Which is what I want.
Now, I need to apply .valueOf() to a date like this: 2017/12/28. I tried using Luxon to convert the date (since applying .valueOf() to YYYY/MM/DD doesn't produce a number):
DateTime.fromISO(startDate.replace(/\//g, '-')).toRFC2822()
// => Thu, 28 Dec 2017 00:00:00 +0800
However, applying valueOf() that results returns the same string. Not a number like in the first example.
What should I do so I can produce a numeric value from YYYY/MM/DD? Just I did with DDD MMM DD YYYY GMT+0800 (X country standard time)?
I think you're losing track of the types.
fromISO() returns Luxon DateTime object, but toRFC2822 returns an RFC 2822 string representation of the date.
So your valueOf() was being called on the string, not the DateTime.
As others have pointed out, you need only call valueOf() on the result of fromISO().
To illustrate:
var dt = luxon.DateTime.fromISO('2017-12-05'); // returns a Luxon DateTime object
console.log('fromISO returns an', typeof dt);
var rfc = dt.toRFC2822(); // returns a string
console.log('toRFC2822 returns a', typeof rfc);
var valueOfRFC = rfc.valueOf(); // string.valueOf() is just that string
console.log('strings are their own values?', rfc === valueOfRFC);
var valueOfDT = dt.valueOf(); // this is what you want
console.log('value of datetime', valueOfDT);
<script src="https://moment.github.io/luxon/global/luxon.min.js"></script>
If you want to use Luxon you can:
use valueOf() instead of toRFC2822() (as Slai suggested in the comments)
use fromString removing replace
var startDate = '2017/12/28';
var DateTime = luxon.DateTime;
// fromISO with regex
console.log( DateTime.fromISO(startDate.replace(/\//g, '-')).valueOf() );
// fromString instead of regex
console.log( DateTime.fromString(startDate, 'yyyy/MM/dd').valueOf() );
<script src="https://moment.github.io/luxon/global/luxon.min.js"></script>
If you want to use momentjs (Luxon's "big brother"), you can simply use moment(String, String):
var startDate = '2017/12/28';
console.log( moment(startDate, 'YYYY/MM/DD').valueOf() );
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.js"></script>
By default, both luxon and moment parse string as local time.
You can also use vanilla JavaScript, see Date:
new Date(year, month, day) (The argument month is 0-based)
new Date(dateString) (IS0 8601 compliant format, e.g. 2017-12-28. Unsupported on Firefox <=3 and IE <=8)
var startDate = '2017/12/28';
var arr = startDate.split('/');
console.log( new Date(arr[0], arr[1]-1, arr[2]).valueOf() );
console.log( new Date(startDate.replace(/\//g, '-')).valueOf() );
Please note that:
Note: parsing of date strings with the Date constructor (and Date.parse, they are equivalent) is strongly discouraged due to browser differences and inconsistencies. Support for RFC 2822 format strings is by convention only. Support for ISO 8601 formats differs in that date-only strings (e.g. "1970-01-01") are treated as UTC, not local.
The Date object will accept a YYYY/MM/DD string as-is. From there, you can use .getTime() to get a timestamp:
var startDate='2017/12/28';
var dateStamp = new Date(startDate); // e.g. "Thu Dec 28 2017 00:00:00 GMT-0600 (Central Standard Time)"
var timeStamp = dateStamp.getTime(); // e.g. 1514440800000

Property date to String for parsing

I'm trying to insert creation date into table view in specific format.
Now it's like DD/MM/YYYY HH:MM:ss and I want it like DD/MM/YYYY.
YAHOO.Bubbling.fire("registerRenderer", {
propertyName: "test:date",
renderer: function functionPrice(record, label){
var jsNode = record.jsNode,
properties = jsNode.properties;
var rawDate = properties['test:date'];
var date= rawDate().toString().substring(0, 11);
return '<div id="attachments">' + date + '</div>';
}
});
In this case, column contains [Object obj.
I also tried convert it to toISOString but it returns Invalid Date.
Column is set like d:date but output is d:datetime and I don't know why.
Thank you.
If your date format is fixed, this is a safe way to create a Date instance:
var value = "31/12/2017 00:00:00";
var dd = value.substring(0,2);
var mm = value.substring(3,5);
var yyyy = value.substring(6,10);
var d = new Date(yyyy, mm - 1, dd); // Sun Dec 31 2017 00:00:00 GMT+0800 (+08)
To change dates the dates displayed in the date picker control, but this file may not exist in your environment. See if the following file exists:
<alfresco home>\tomcat\shared\classes\alfresco\web-extension\site-webscripts\org\alfresco\components\form\form.get_en.properties
If it doesn’t exist, copy it from here (create the form folder if necessary):
<alfresco home>\tomcat\webapps\share\WEB-INF\classes\alfresco\site-webscripts\org\alfresco\components\form\form.get_en.properties
Open the form.get_en.properties file for editing. Search for “form-control.date-picker” to find the proper properties to change (we found four values on a recent installation).
Restart Alfresco to make the changes take effect.

What is the difference between toGMTstring() and toUTCstring()?

I am saving data in MongoDB server from Node.js application (using Mongoose).
Consider following code:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var schemaObj = new Schema({
field1: String,
field2: String,
Datefield: Date//So on...
});
mongooseDB = mongoose.createConnection('mongodb://myserver:port/DBname');
mongooseDB.on('error', console.error.bind(console, 'error in connection'));
mongooseDB.once('open', function (err) {
var objmodel = db.model('myschema', schemaObj);
modelObj.field1 ='value1';
modelObj.Datefield = new Date().toGMTString(); //new Date().toUTCString();
//So on..
modelObj.save(function (err) {
if (err)
//Notify err
else
//DO some task after save
});
});
In the Datefield, Getting following value when I use 'toGMTstring()' or 'toUTCstring()'
'Thu, 24 Jan 2013 05:49:04 GMT'
I went through the following links:
toUTCstring()
toGMTstring()
toGMTString is deprecated and should no longer be used
Could anyone help me in understanding, whats the difference between toUTCstring() and toGMTstring() with respect to Node.js?
GMT and UTC are different timezones, they are Greenwich Mean Time and Coordinated Universal Time respectively. GMT is a 'solar' timezone, whereas UTC is 'atomic'. For most purposes they are essentially the same thing, however UTC is more 'universal'.
Interestingly the documentation you point to for toUTCString still show a GMT output:
var today = new Date();
var UTCstring = today.toUTCString();
// Mon, 03 Jul 2006 21:44:38 GMT
For interchange of data between application I would prefer to use something like ISO8601, which uses the 'Z' suffix for UTC:
2013-01-16T08:19Z
Where the 'Z' confusingly stands for 'Zulu time'!
From what I can see they are the same. And the documentation at MDN already states that toGMTString has been deprecated in favor of toUTCString:
toGMTString() is deprecated and should no longer be used. It remains implemented only for backward compatibility; please use toUTCString() instead.
Mostly use for formatting date and time (Human Readable).
You can also use toLocaleDateString()
var event = new Date(Date.UTC(2012, 11, 20, 3, 0, 0));
var options = { weekday: 'short', year: 'numeric', month: 'short', day: 'numeric' };
console.log(event.toLocaleDateString('en-US', options));
for ISO use toISOString()
var today = new Date();
var ISOstring = today.toISOString();
// 2020-08-03T23:59:58.123Z

Categories

Resources