Send date from PHP to Javascript - javascript

I have already tried out this question but it didn't solve my question.
I have a PHP server which sends a date via JSON to the user where it is then processed by Javascript:
PHP: 'date' => date('D M d Y H:i:s O', strtotime($array['Time']))
Javascript var time = new Date(data.date).toLocaleString()
But instead of getting 18. January 2015 ..., I get 3. March 5877521 -596:-31:-23 GMT+0:53:28. What is wrong there?
Some things you might need to know: The server has the central european timezone as well as the date sent. I am trying (above is only an example) to internationalize the date with javascript.

Why pass a string? JS's date constructor will accept a timestamp:
var time = new Date(<?php echo strtotime($array['Time']) ?>000);
Note the 000 in there. JS uses milliseconds, while strtotime returns in seconds, so effectively you'd be building:
var time = new Date(12345678000);
^^^^^^^^---seconds from php
^^^---instant conversion to milliseconds.

I had to parse the timestamp as in Marc B's answer into an int (why ever?):
I solved it now: new Date(parseInt(data.date)) works

First, note that this error is quite common on the web, common enough that it's not [just] a "you" problem.
The reason for the weird March date millions of years in the future seems to be a bug in specific JavaScript engines. I noticed the same date appeared when some tests of ours were run in Chutzpah, a Jasmine test runner for Windows, when our test had an invalid date cast to a locale string and we ran only on its embedded setup from the command line (aka, not in a separate browser).
For our case, it turns out it was PhantomJS that was causing the issue. (PhantomJS used WebKit as its engine. I'd imagine your issue was from an engine with a similar lineage.)
Here's a minimal example that causes the error:
in a file called phantonTest.js
console.log(new Date('').toLocaleString('en-us', { timeZoneName: 'short' }));
phantom.exit();
then execute it...
>phantomjs.exe phantomTest.js
March 3, 5877521 at -8:-31:-23 GMT-4:56:02
You get a slightly date, but are performing a similar operation. You likely have an invalid date coming from strtotime($array['Time']) and the user a similar browser engine. QED, etc.

Related

How do I tell momentjs/moment-tz that the string I'm giving it is already in the timezone I'm giving it? [duplicate]

This question already has answers here:
How to create time in a specific time zone with moment.js
(5 answers)
Closed 2 years ago.
Small problem that's driving me nuts. I'm getting a time as a string from an API call, and I'm getting the timezone the time is in, as two separate strings. I'm trying to create a moment that is using this given data and it's taking my local machine timezone as a starting point and adjusting into the given zone
let timeFromApi = "01 Jan 2021 15:00"; //That's 3pm, Eastern Standard Time
let timeZoneFromApi = "America/Indianapolis"; //Server says "time I gave is in EST"
If I do either of these, with my local machine set to UTC:
let x = moment.tz(timeFromApi, timeZoneFromApi).format("YYYY-MM-DDTHH:mm:ss zz");
let y = moment(timeFromApi).tz(timeZoneFromApi).format("YYYY-MM-DDTHH:mm:ss zz");
Then they both end up containing "2021-01-01 10:00 -05"..
Moment seems to assume the given string is in timezone the local machine is in: If I set the local machine to EST, then I get "2021-01-01 15:00 -05" like I expect...
... but I don't want moment reading the local machine time at all. I literally want to give it a time, and give it a timezone and have it create that time in that timezone - how do I do this?
Having gone through the same sort of trouble, I can tell you that
let x = moment.tz(timeFromApi, timeZoneFromApi);
is the correct method. The documentation also says that is the way to do. I suspect your trouble is the formatting not the parsing. The z and zz formatting options are deprecated as of 1.6 (see https://momentjs.com/docs/#/displaying/). So I'd play with the formatting a bit.

When converting date, I get previous day

I want to convert a string into a date "as it is".
const date = "8/16/2019"
console.log(new Date(date))
However, I get:
As you can see I get the prevous day. I was thinking that it might be a timezone issue, even though there is no timezone that I am converting it from.
Any suggestions how to convert is as it is?
I appreciate you replies!
If your format is consistent, you could split on / and use Date.UTC. Creating your new Date from that would ensure it's UTC.
const date = "8/16/2019"
const [month,day,year] = date.split("/");
const utcDate = Date.UTC(year,month-1,day);
console.log(new Date(utcDate));
const date = "8/16/2019"
console.log(new Date(date).toLocaleString("en-US", {timeZone: "Asia/kolkata"}))
Note:- You need to add timezone
You can use toLocaleDateString
console.log(new Date("8/16/2019").toLocaleDateString('en-us', {timeZone: "Asia/Kolkata"}))
new Date("8/16/2019") will create a date object using your current timezone. Add a "Z" at the end if you want your date to be in UTC.
console.log(new Date("8/16/2019Z"))
EDIT
It appears that Firefox is not implementing the parsing of standard date format. Unfortunately until recently how exactly was a date parsed was completeley based on heuristics and intrinsically non portable.
Looking at Firefox bug tracker seems the issue has been discussed but the problem is still present (some toolkit just works around by replacing "Z" with "+00:00" before calling the parser).
The only way to be sure on every browser is to parse the string yourself and build the date from the fields. I didn't notice because I'm using chrome instead (in both chrome and Node works as expected).
EDIT 2
After more investigation seems the standard requires that:
If you use yyyy-mm-ddThh:mm:ssz then you get what ISO format for datetime defines it to be. Also the syntax described in the standard is not very precise and for example is not clear to me if the time zone can be present when no time is present (Chrome says yes, Firefox says no).
If you use another format then anything goes (so for example there is no string that is guaranteed to issue an invalid date response).
In other words new Date("8/16/2019") is not portable Javascript (with the meaning that you don't know what date / time / timezone you will get, if any). Either you parse yourself the date or you just live with what that version of that Javascript engine in that moment decides to give you.

Using Moment.js like PHP's date and strtotime

I'm a typically server side developer feeling a bit like a fish out of water trying to display time values on the front end. How can I get behavior like PHP's date() and strtotime() functions out of moment.js? I just want a unix timestamp to appear in H:i:s format, and vice versa.
So far I've tried the following, from existing example code and the documentation:
moment(timestamp).format(H:i:s);
moment().duration(timestamp).format(H:i:s);
moment.unix(timestamp).format(h:mm:ss);
moment(formatted,'H:i:s');
Not a SINGLE one of which has worked properly. This may get flagged as duplicate since there are plenty of moment.js questions out there, but I don't know whether it's been updates to the library itself or slightly different context, I have not found one existing solution that has worked for me.
Anybody have any suggestions for these two simple tasks?
EDIT:
I've distilled two different problems out of this. One is that functions the moment docs say should work are giving weird values:
moment(1437462000).format('h:mm:ss')
for instance, which should return 7:00:00 utc, returns 10:17:42. This can be fixed in this case by using moment.unix(1437462000).utc().format('h:mm:ss') instead, but this leads into the second problem - the .utc() function seems to get ignored when converting back from a date into a timestamp:
timestamp = moment(formatted,'DD/MM/YYYY H:m:s').utc().unix();
will still return a timezone corrected value (in my case this is incorrect by several hours since the formatted time in question has nothing to do with the client computer) regardless of whether the .utc() function is included or not.
A few things you should realize:
Unix timestamps should always in terms of UTC. They are never adjusted for time zone in numerical form. If they're adjusted for time zone, that's done during the interpretation of the number, not in its representation.
While traditionally a "Unix Timestamp" is in terms of seconds, many environments use milliseconds instead. PHP's date timestamps are based on seconds, while moment and JavaScript's Date object both use milliseconds by default. Using the moment.unix function will let you pass seconds, and is identical to just multiplying the timestamp by 1000.
Moment has two built-in modes, local and UTC. The default mode is local. It doesn't matter what input you provide, if you don't specify UTC, the moment is adjusted to local. To specify UTC, you use the utc function. There are two forms of the function:
moment.utc(input) // parsing form
moment(input).utc() // conversion form
Both forms take some input and result in a moment in UTC mode. The difference is in how the input is interpreted. In either case, if the input value is unambiguous, the result is the same. For strings, that means the input would contain either a Z (from ISO8601), or a UTC-based offset. All other forms are ambiguous. For example, if I pass "2015-11-08 01:23:45", I will get different results depending on whether I interpret that string as local time or as UTC.
For numbers, they are always interpreted as milliseconds in UTC. However, if you use moment(number) without then calling .utc() then the moment is left in local mode, so any output will display as local time.
When you call moment.unix(input), the input is a number of seconds, but the moment is left in local mode. So to display the UTC time, you would use moment.unix(input).utc().
If your pre-recorded timestamps from your other system are in numeric form, but have been adjusted away from UTC, then they are incorrect. You have bad data, and Moment can't help you unless you know specifically how they have deviated and you write code to counteract that.
Moment's formatters are case sensitive. M is months, m is minutes. H is hours on a 24-hour clock, h is hours on a 12-hour clock. Use two consecutive letters when you want to include zero-padding. Example, HH:mm:ss for 13:02:03 vs. h:m:s for 1:2:3.
Moment's X formatter does not care which mode the moment is in. It will always emit seconds in UTC. Likewise, the x formatter returns milliseconds in UTC, as does moment.valueOf().
Also, your last example:
moment.unix(1437462000).utc().format()
Returns "2015-07-21T07:00:00+00:00" - which I believe is the value you expected.
You also get the same original timestamp regardless of which of these you try:
moment.unix(1437462000).utc().format("X") // "1437462000"
moment.unix(1437462000).format("X") // "1437462000"
moment.unix(1437462000).utc().unix() // 1437462000
moment.unix(1437462000).unix() // 1437462000
For anyone who comes in and is still looking for direct PHP equivalents for date() and strtotime(), here are the ones I ended up using. Matching up to php basically means just completely ignoring any kind of local time information by making sure everything is in UTC. That task is a little different between the timestamp->date and date->timestamp cases, though, so you have to be careful.
date()
Converting a timestamp to formatted date without any client timezone correction
var formatted = moment.unix(timestamp).utc().format('h:mm:ss');
strtotime()
Converting a UTC formatted date back to a timestamp without correcting it to local time:
var new_timestamp = moment.utc(formatted_utc,'DD/MM/YYYY H:m:s').format('X')
//where 'DD/MM/YYYY H:m:s' is the formatted date's format, and
//'X' outputs a unix timestamp without milliseconds.
Notes:
Do not use moment() with parenthesis in the calls:
moment().utc(date,format) will return local time values, not your
input.
Moment.js does not like the use of 'i' for minutes in the formatting,
unlike php.

Taking UTC time from database and displaying local time in browser

I have a table that contains DateTimes in UTC. I'm using PHP to return these date/time strings to an AJAX request in JSON. An example of one of these strings I am receiving on the front end is "2014-12-22 09:36:54". I would like to display this to the user in their local time. For instance I am 8 hours behind UTC time in California, so I would like to see something like "2014-12-22 01:36:54".
In javascript I tried new Date("2014-12-22T09:36:54").toLocaleString() and got "12/22/2014, 9:36:54 AM"---basically an unchanged date/time.
I tried new Date("2014-12-22T09:36:54").toUTCString() and I got "Mon, 22 Dec 2014 17:36:54 GMT", which is pretty much the opposite of what I wanted. But I guess that should have been obvious.
Last thoughts...:
Am I going to have to do some manipulation involving getTimezoneOffset()?
Would this be easier solved on the PHP backend?
One last note is that I included jstz.js thinking it would help but all it does is return the timezone name, and I don't know how that is particularly useful. Does any function take the name of a timezone as an argument which would be helpful in this situation?
To reliably convert a UTC string like "2014-12-22 09:36:54" to local, manually parse it, e.g.:
function parseISOAsLocal(s) {
var b = s.split(/\D/);
return new Date(Date.UTC(b[0], b[1]-1, b[2], b[3], b[4], b[5]));
}
An ISO 8601 compliant string without a timezone must be treated as UTC according to the current ECMA-262 standard (ed5). However, the draft version 6 changes that to treat them as local (per ISO 8601). Most browsers treat it as UTC, however Chrome treats it as local (and IE 8 as NaN). Much better to manually parse date strings and avoid browser vagaries.

Using timezones with moment.js fromNow() or from()

I want to show users how long has been elapsed since they performed an action.
The date+time of the action happening is stored on the server, in the server's timezone. That's what's causing the trouble, since if the user's computer's timezone is 12 hours ahead of the server's timezone, then if the user adds something right now, moment.js will show '12 hours ago' as the output of fromNow() rather than just now.
To try to solve this, I'm trying the following method:
var actionTime = moment( action.timeStamp);//time of when user performed action
var serverTime = moment().zone('-07:00'); //current server time
console.debug( serverTime);//outputs Wed Sep 11 2013 15:19:51 GMT-0700
var timeAgo = serverTime.from( actionTime);
But despite of all this, timeAgo still shows the difference between the client's timezone and the server's timezone (i.e showing '12 hours ago' instead of 'now');
Anyone know how to fix this or what I'm doing wrong?
Ideally, you would want to pass a UTC timestamp from your server to the client. That doesn't mean you have to switch your whole server over to UTC, it just means that you would convert from the time in your database to UTC on the server before sending it over the web. Sure, it would be even better if you actually stored times in UTC, but you said you aren't in a position to make that sort of change right now. But let's just work off the assumption that you can't change anything at all on the server.
We'll also assume that your server is fixed to the UTC-07:00 offset. In real life, this would only be true for places like Arizona that don't follow daylight saving time. So if you are in Los Angeles and are in Pacific Time, then some of your data is based on UTC-07:00, but some of it is based on UTC-08:00. That requires a lot more work if you want to do it in JavaScript.
Let's also assume that the input is already a string in ISO8601 format. (If it's not, then let me know and I will adjust this code.)
var s = "2013-09-11 18:00:00"; // from action.timeStamp
var actionTime = moment(s + "-07:00", "YYYY-MM-DD HH:mm:ssZ");
var timeAgo = actionTime.fromNow();
The reason your other code didn't work is because in the first line, you are affected by the time zone of the browser. The zone setter in the second line just changes the zone for formatting, not changing the actual moment in time.
Also, when you dump a moment to the console for debugging, make sure you format it for output. Otherwise you are just looking at its internal property values, which may or may not make sense directly.
I have solved it in a different way, maybe this option was not possible back when the question was asked, but might be easier now.
I used moment-timezone.js (which requires moment.js 2.6.0+).
I set the default timezone to my server's timezone like this:
moment.tz.setDefault("America/New_York"); // "America/New_York" in my case
and then just use it normally. fromNow() will use the timezone in the client to calculate the time that has passed since that moment.
moment(myDatetime).fromNow();
i had the same issue and used the above comments to modify my code. I did the following to get it resolved:
transform(value: string) {
var time = moment(value).utc();
return moment(time, "YYYYMMDD").fromNow();
}
I was missing the .utc() to convert it before I applied the .fromNow()
Things to note this is being used within a Pipe for Ionic 3 and the above code is from the pipe logic.

Categories

Resources