Merging partial moments - javascript

Unlike a vanilla date, a moment does not have any required units that must be passed to its constructor. For instance, this is a perfectly valid way to instantiate a moment, with the unpassed units defaulting to current date and 00:00 time:
moment.utc('07:35', 'HH:mm').toISOString();
> "2013-10-24T07:35:00.000Z" //let's just ignore the timezones for now, ok?
So how can partial moments be merged into one? For instance, I could just .set() first moment's hours to values from the second:
var mergee = moment.utc('07:35', 'HH:mm');
moment.utc('2015-01-01').
set('hours', mergee.get('hours')).
set('minutes', mergee.get('minutes')).
toISOString();
> "2015-01-01T07:35:00.000Z" //yay!
But what if I don't know in advance the format of the "mergee", or even what units it has parsed?

While not entirely supported or recommended, something like this will work:
var m1 = moment.utc('07:35', 'HH:mm');
var m2 = moment.utc('2015-01-01');
var merged = moment.utc(m1._i + ' ' + m2._i, m1._f + ' ' + m2._f);
The space separator isn't necessarily required, but it might prevent conflicts with certain formats.
I could see that you might have troubles though if both moments contained the same elements with different data.

Related

How to convert a Postgresql timestamptz to a Javascript Date?

My database creates a bunch of timestamptz values using the now()::TEXT function. In several API calls I receive these values, but I'm having difficulty turning them into Javascript Date objects.
Here's an example of a Timestamptz string that Postgres returns: 2019-09-12 09:52:52.992823+00
The problem is that if I try to do new Date("2019-09-12 09:52:52.992823+00"), then there's finicky behavior in the browser. It looks like I have to append a "Z" first to the string to get the time correct (because of timezones?)
Also, Firefox doesn't seem to parse it at all (but Chrome does). If I remove the "+00", it seems to work.
So I came up with this "solution" which seems to work on both browsers, but I have to call it on all my dates before working with them, and it seems kinda brittle. Is this the proper way?
function fixDate(date) {
return date.replace("+00", "") + "Z";
}
The supported formats for the built–in parser are described in ECMA-262, essentially a specific subset of ISO 8601 and the format specified for Date.prototype.toString. Parsing of any other format (that is, anything that doesn't match exactly with one of the specified formats) is implementation dependent and should not be trusted.
You might consider parsing the string and constructing a new string that is compliant with ECMA-262, e.g. convert "2019-09-12 09:52:52.992823+00" to "2019-09-12T09:52:52.992+00:00".
// Convert 2019-09-12 09:52:52.992823+00 to
// 2019-09-12T09:52:52.992+00:00
// Assumes all strings are +00:00
function rectifyFormat(s) {
let b = s.split(/\D/);
return b[0] + '-' + b[1] + '-' + b[2] + 'T' +
b[3] + ':' + b[4] + ':' + b[5] + '.' +
b[6].substr(0,3) + '+00:00';
}
let s = '2019-09-12 09:52:52.992823+00';
console.log(new Date(rectifyFormat(s)))
However, also heed the general advice to avoid the built–in parser, even with supported formats. It also seems inefficient to parse a string to produce another string that is then parsed again to produce a Date. It's preferable to write your own parse function (a couple of lines of code) or use a library, e.g.
// Parse string like '2019-09-12 09:52:52.992823+00'
// to a date
// Assumes string is always +00
function myDateParse(s) {
let b = s.split(/\D/);
--b[1]; // Adjust month number
b[6] = b[6].substr(0,3); // Microseconds to milliseconds
return new Date(Date.UTC(...b));
}
let s = '2019-09-12 09:52:52.992823+00';
console.log(myDateParse(s).toISOString());

Getting ISO-8601 formatted date using Javascript in a Cosmos DB stored procedure

I'm writing a stored procedure for Cosmos DB. I am not very familiar with JavaScript.
I've been trying to get an ISO-8601 formatted Date, but have been unsuccessful so far. According to some docs I found, I should just be able to call toISOString on a Date. That does not work ("Object doesn't support property or method" error).
So I found this advice to define the prototype yourself:
function storedProcedure(arg) {
if (!Date.prototype.toISOString) {
// Here we rely on JSON serialization for dates because it matches
// the ISO standard. However, we check if JSON serializer is present
// on a page and define our own .toJSON method only if necessary
if (!Date.prototype.toJSON) {
var toISOString = function (date) {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
return date.getUTCFullYear() + '-' +
f(date.getUTCMonth() + 1) + '-' +
f(date.getUTCDate()) + 'T' +
f(date.getUTCHours()) + ':' +
f(date.getUTCMinutes()) + ':' +
f(date.getUTCSeconds()) + 'Z';
};
}
Date.prototype.toISOString = Date.prototype.toJSON;
}
var now = Date.now();
var iso8601 = now.toISOString();
console.log("ISO " + iso8601);
// other code
}
However, this still fails with:
Object doesn't support property or method 'toISOString'
I tried removing the prototype altogether and just using a function that takes a Date instead. However, then the same error occurred but for other members, like getUTCFullYear.
I tried also calling getSeconds and that failed for the same reason. Out of desperation, I tried listing out the object's properties using the getKeys function in this answer and it gives me an empty list.
What is going on here? How can I just get an ISO-8601 formatted Date representing the current time in UTC?
OK, it seems it was because I had used Date.now() per these docs instead of new Date() per this random google result.
I have no idea why this matters or how I could have known this (explanations welcome) but that was the underlying cause. I don't even need the prototype now.

Change date format on a template

I'm using the Foundry template on Squarespace and I need to change the date format on post pages from english to portuguese. Instead of "May 6" I need "6 Mai". In Brazil we use the pattern dd/mm/yyyy. In this case I just want the day and month, and also translate all the months (to: Jan, Fev, Mar, Abr, Mai, Jun, Jul, Ago, Set, Out, Nov, Dez).
I already saw people solving this for others languages there. But not to portuguese or on the Foundry template. It's possible to make a code-injection on Squarespace, on the head or footer. I just need a Javascript that can do that, overwriting the theme's default date format.
I would approach it via the following Javascript, inserted via code injection. Note that although some of the month abbreviations are the same, I've included them for clarity and so that it may be more reusable for others. Also, the abbreviations I've used for the keys (that is, the original month abbreviations) may not be what Squarespace actually uses, so they may need to be updated.
<script>
(function() {
var dates = document.getElementsByClassName("dt-published date-highlight");
var newDate;
var i,I;
// Create object with 'source' keys on the left, and 'output' values on the right.
var months = {
"Jan":"Jan",
"Feb":"Fev",
"Mar":"Mar",
"Apr":"Abr",
"May":"Mai",
"Jun":"Jun",
"Jul":"Jul",
"Aug":"Ago",
"Sep":"Set",
"Oct":"Out",
"Nov":"Nov",
"Dec":"Dez"
};
// Loop through all dates, replacing months and reordering display.
// - Trim extra white space from beginning and end of date.
// - Replace multiple consecutive spaces with a single space.
// - Split by space into an array.
// - Replace month text based on 'months' object key:value pairs.
// - Convert array to string, rearranging display order of elements.
// - Set new date HTML.
for (i=0, I=dates.length; i<I; i++) {
newDate = dates[i].innerHTML.trim();
newDate = newDate = newDate.replace(/ +/g, ' ');
newDate = newDate.split(" ");
newDate[0] = months[newDate[0]];
newDate = newDate[1] + " " + newDate[0];
dates[i].innerHTML = newDate;
}
})();
</script>

Ignore/don't convert timezone?

My page was working fine and displaying dates and times as expected until I had to add new Date() to avoid the momentjs deprecation warning. Now my date is 5 hours off what it should be.
How can I fix this? vd and v should both be 12:14:26 and, in this instance, fn should be "Seconds ago".
Here is the full code:
var k = key;
var v = obj[key];
var vd = Date.parse(obj[key]));
if (moment(vd).isValid()) {
var fn = moment(vd).fromNow();
v = fn;
}
return {
name: k,
value: v
};
I tried the following based on this post, but it brought back the momentjs deprecation warning:
var vd = new Date(Date.parse(obj[key])).toUTCString();
(and still didn't work)
__________________________________________________________________________________
Threaded comments would be cool
What happens if you just run moment(v)? Should work. – Maggie Pint
It does work, but I get the deprecation warning.
I just looked closer. The format of V is ISO8601 with offset, so you shouldn't be getting a deprecation if you just call moment(v). If you are, try moment(v, moment.ISO_8601) – Maggie Pint
Again, works, but I still get the deprecation warning.
instead of var vd = new Date(Date.parse(obj[key])).toUTCString(); did you try var vd = new Date(Date.parse(obj[key])).toISOString(); ? or even simplier var vd = Date.parse(obj[key]) – user3
With .toISOString() it outputs "Invalid Date". var vd = Date.parse(obj[key]) Is what I had originally, it works as expected but I get the warning.
Your v string appears to use ISO8601 format already? Maybe append the Z, or ask moment to recognise it automatically as UTC when timezone info is missing – Bergi
I just tried var vd = new Date(Date.parse(obj[key] + "Z")); and it works!
In your example, you're passing a value for v as an ISO8601 string without any offset specified. It is ambiguous as to what point in time you are actually talking about.
Because of changes in the ECMAScript specification, the value will be interpreted as UTC by some browsers, and as local time by others.
The way to avoid this ambiguity is to not use the date constructor. Anywhere you have new Date or Date.parse, you're introducing potential differences in behavior. Just use moment's own parser, and be specific as to your intent.
// your input
var v = "2016-09-14T12:14:26.149";
// pick only one of these:
var m = moment(v); // input is local time
var m = moment.utc(v) // input is UTC
// then proceed as normal
if (m.isValid()) {
var fn = m.fromNow();
// etc.
}
If the input is in some other time zone, or has some other specific fixed offset from UTC, then you have various other options for constructing the moment object. But really, don't rely on the Date object to do the parsing. That's the whole point of the deprecation message.
If the input is not always in the expected format (you're looking for invalid inputs, etc.), then you should specify the expected format such that you don't get the deprecation warning. For example:
var m = moment.utc(v, "YYYY-MM-DD[T]HH:mm:ss.SSS");
This particular format is also available as a constant:
var m = moment.utc(v, moment.ISO_8601);
This, and much more are spelled out in the Moment documentation.
It looks like the problem is when you create the date that recovered from the database... because it does not have the timezone... but you know it is "-0500", might be you can add that part hard coded... not ideal but it works
Look at this example:
function getMoment(v){
var vd = moment(v+"-0500");
var result = "?";
if (vd.isValid()){
result = vd.fromNow();
}
return result;
}
Check the getMoment() function, you can just copy and paste and use it in your code.

manipulating date object with javascript

i have been tinkering with the date object.
I want to add a dynamic amount of days to a day and then get the resulting date as a variable and post it to a form.
var startDate = $('#StartDate').datepicker("getDate");
var change = $('#numnights').val();
alert(change);
var endDate = new Date(startDate.getFullYear(), startDate.getMonth(),startDate.getDate() + change);
does everything correctly except the last part. it doesnt add the days onto the day
take this scenario:
startdate = 2011-03-01
change = 1
alert change = 1
endDate = 2011-03-11 *it should be 2011-03-02*
thank you to all the quick replies.
converting change variable to an integer did the trick. thank you.
parseInt(change)
just to extend on this: is there a way to assign a variable a type, such as var charge(int)?
You may have fallen victim to string concatenation.
Try changing your last parameter in the Date constructor to: startDate.getDate() + parseInt(change)
See this example for future reference.
convert change to a number before adding it. it looks like you're getting a string concatenation operation rather than the addition you're expectingin your code.
I believe you are concatenating instead of using the mathematical operator. Try this instead,
var endDate = new Date(startDate.getFullYear(), startDate.getMonth(),startDate.getDate() + (+change));
It looks like you are not adding the ending day, you are concatinating it so '1' + '1' = '11'
use parseInt() to make sure you are working with integers
example
var change = parseInt($('selector').val());
Also, with this solution, you could easily end up with a day out of range if you are say on a start date of the 29th of the month and get a change of 5

Categories