Dates manipulation with moment.js - javascript

I receive the following date from the server:
"2018-11-21 07:00:00 UTC"
Then, I convert it using userTimzone variable (since I wish the editor would use the user timezone):
dateOfAction: moment(dateOfLoss, 'YYYY-MM-DD HH:mm Z').tz(userTimzone).unix(), So dateOfAction is 1542776400. That is: Wednesday, November 21, 2018 5:00:00 AM - and so far so good. That is the dateOfAction in UTC with after right offset
I'm using react-datetime as the calendar the edit the date, using UTC.
When performing the save action, without touching the date, the calendar output is still 1542783600, but I wish to be 1542783600 - i.e the same value as in the beginning, reverting the offset at the other direction.
How can I achieve that?

A few things:
Moment doesn't map the Z token to the string "UTC". Since you're parsing in local mode, you are actually getting a moment based on the local computer's time zone rather than UTC. Thus, change the first part of your code to:
moment.utc(dateOfLoss, 'YYYY-MM-DD HH:mm [UTC]')
The brackets are to treat UTC as a literal string, which isn't strictly required so you can omit it if you like and the result will be the same.
moment.utc(dateOfLoss, 'YYYY-MM-DD HH:mm')
There's no need to call .tz(userTimezone) if you're just going to call .unix() subsequently. Unix timestamps are always UTC based. Though it's not clear why you're asking for a Unix timestamp, as react-datetime doesn't need one.
The two values you gave in the last paragraph of your question are identical, so I'm not sure specifically what you were looking for. The time you gave is indeed 1542783600, not 1542776400.
The readme file of react-datetime describes all the options you can use. You can simply pass the moment object obtained above to the value prop. You might need to use the utc or displayTimeZone props if you want to change the behavior. You might also need to call .local() or .tz(userTimezone) on the moment object before passing it in, but I'm not certain if that is required or not for this particular component.

Related

Moment.js - Anywhere in the world, use the same Timezone

I found a couple other posts but they didn't have something specific to what I was looking for.
Here's the scenario:
A user in China (although it could be anywhere in the world) inputs a date time into a field that represents a local time in the U.S. So even though their local machine might be 1:11 AM 02/15/2018 in Beijing, the user is entering a date in Austin, TX for 11:11 AM 02/14/2018 into the date time field.
This is the string I'm pulling from the input field:
'2018-02-14T11:11'
How can I use moment.js to make sure that when I convert '2018-02-14T11:11' to UTC, the UTC string always reflects Austin time, not Beijing time? At the end of the day, we won't know which timezone the user is from, but we will always know the entered timezone will be in Central Standard Time.
What seems to be happening with the below is that when I use these to convert to UTC, the dates are still not reflecting CST, or they are offset incorrectly by several hours.
moment('2018-02-14T11:11').zone("America/Chicago").format()
moment('2018-02-14T11:11').tz("America/Chicago").format()
moment('2018-02-14T11:11', 'CST').tz("America/Chicago").format()
moment('2018-02-14T11:11', "America/Chicago").tz("America/Chicago").format()
To UTC:
moment.utc('2018-02-14T11:11').tz("America/Chicago").toISOString()
I'm definitely missing something. Any advice would be appreciated!
Thanks in advance!
You're close - you will need the Moment-Timezone addon, and then it's like this:
moment.tz('2018-02-14T11:11', "America/Chicago").utc().format()
Let's break that down:
// create the moment with a specific time zone
var m = moment.tz('2018-02-14T11:11', "America/Chicago");
// convert to UTC
m.utc();
// format the output
var s = m.format();
Just add another scenario that I got:
Parse '2018-02-14Z', but ignore 'Z' (UTC), just use local time zone or a specified timezone.
You can simply remove 'Z' from the string or do these:
moment('2018-02-14Z', 'YYYY-MM-DD') will ignore 'Z' or any unmatched characters.
moment.tz('2018-02-14Z', 'YYYY-MM-DD', 'America/Chicago') will use the given timezone.
Of course, you should not do this in the first place. A correct timezone should be used.

How to change only timezone without modifying the time in momentz

I have date and time in 2016-06-21T10:00:00-07:00 format which represets 06/21/2016 5 PM in PST, I just want to change this to 06/21/2016 5 PM in EST and vice versa. How can I do it with momentz?
JSFiddle
debugger;
var dateTime = moment('2016-06-21T10:00:00-07:00');
var newDateTime = dateTime.clone();
newDateTime.tz('US/Eastern');
//dateTime = dateTime.utc();
console.log(dateTime.utcOffset());
console.log(newDateTime.utcOffset());
console.log(newDateTime.utcOffset() - dateTime.utcOffset());
//console.log(utc.format());
dateTime = dateTime.add(newDateTime.utcOffset(), 'minutes');
console.log(dateTime.format());
console.log(new Date(Date.parse(dateTime.format())).toJSON());
EDIT:
given input = 2016-06-21T08:00:00-07:00 (PST)
expected output = 2016-06-21T08:00:00-04:00 (EST)
So when I convert that to UTC then it should become
2016-06-22T15:00:00Z for PST
2016-06-22T12:00:00Z for EST
I think you are confused about how ISO8601 format works. This format always represents local time with a time zone offset. Thus 2016-06-21T10:00:00-07:00 represents June 21 2016 at 10 AM in a timezone that is currently UTC-7 (this could be US pacific, among many others).
It sounds like you want to take the local time, but put it in a new timezone. This opens up some interesting questions about why you are receiving the date in the format that you are. If the date is meant to be interpreted as an exact point on the global timeline, then the format you are receiving it in is good. If however, the date is meant to be interpreted as a local time (not relative to UTC), it might be worth considering the possibility that the format of the date needs to be changed at the source. For instance, if you are making an ajax request to an API, and it is returning a date in this format, but that date actually has no relationship to UTC, it would be good to try to change that API to only send the local time (without the offset). If you were able to do that, then the following code would work:
moment.tz('2016-06-21T10:00:00', 'America/New_York').format()
"2016-06-21T10:00:00-04:00"
If you are unable to do that, or if the date is meant to be interpreted as an exact point on the global timeline, but you wish to ignore that in your specific use case, that can be done. You will need to specify a parse format that ignores the timezone offset on your initial time stamp. The code would be as follows:
moment.tz('2016-06-21T10:00:00-07:00', 'YYYY-MM-DDTHH:mm:ss', 'America/New_York').format()
"2016-06-21T10:00:00-04:00"
You might benefit from the material in this blog post, as it covers how ISO8601 format works, and how all of moment's constructor functions work.
Checkout moment().utcOffset() You can pass in the offset as parameter to this function and the date would use that locale.
Assuming you know beforehand the utcOffsets required which in your case are -420 and -240 or -300(EST with DayLightSaving). Below can be done
var dateTime = moment('2016-06-21T10:00:00-07:00');
dateTime.utcOffset(-420).format();
"2016-06-21T10:00:00-07:00"
dateTime.utcOffset(-240).format()
"2016-06-21T13:00:00-04:00"
NOTE: With -04:00, it should 13:00:00 and not 07:00:00 - http://www.timeanddate.com/time/zones/est
EDIT: This answer was posted to the earlier version of question, where same time was needed in different timezones. If it is incorrect, kindly please elaborate on how it is.
Thanks!

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.

momentjs: how to get the date in a specific timezone

In a nutshell I want moment to respect server's timezone. I've set my machine's timezone to Alaska but I'm passing a Brisbane timezone string to moment. Now I need moment.toDate to return a date instance in the same timezone as the one I pass in the moment constructor; e.g.
m = moment("2016-11-20T08:00:00+10:00")
m.format() // "2016-11-20T08:00:00+10:00"
m.toDate() // Sat Nov 19 2016 13:00:00 GMT-0900 (AKST)
I want to get a Date instance from moment that's in the input timezone; e.g. somehow get toDate to return Sun Nov 20 2016 08:00:00 GMT+1000 (AEST).
FWIW I have tried the same code with and without moment.tz.setDefault and while it correctly changes the format result, toDate always uses the machine's timezone!
Update
The reason I need this behaviour is that some JavaScript libraries and controls don't understand moment and only work with Date and the time/date gets skewed when presented back by them. One example, the one I'm currently dealing with, is jQuery UI date picker control. I want the date picker to show the current date as it's on the server (or on a specific timezone).
Thanks in advance.
The Date object represents the time in UTC internally, and can only use the time zone of the machine its running on when projected.
There's absolutely no way to produce a Date object that uses an arbitrary time zone. Any examples you may come across that try to manipulate the Date object (such by adding or subtracting time zone offsets) are fundamentally flawed.
Moment itself has great time zone support, including the moment-timezone extension for working with named time zones instead of just time zone offsets. But once you go back to a Date object - you're back at the mercy of the behavior of that object.
Sorry, but there's no way to achieve what you are asking. Perhaps you could elaborate as to why you wanted to do this, and I could recommend a workaround.
Update: With regards to your update, usually there is a mechanism for getting the value from a date picker as text, rather than as a date object. With the example of the jQuery UI date picker control, the onSelect event gives it to you as text already, or you can simply call .val() instead of .datepicker('getDate') to get the text out of the field. Once you have a textual value, you can then parse it with moment however you like.
Similarly, when setting the value, you don't necessarily need a Date object. You could just set the value of the textbox as a string, or you can pass a string to the setDate function.
In most cases, you won't have to go through a Date object. But if for some reason you do, then you'll need to artificially construct one with something crazy like:
var d = new Date(m.format('YYYY/MM/DD'));
Normally, I'd be against that - but if it's just there to get the pass a value to a UI control, then it's probably ok.
This will get you a moment in the same timezone as the moment string, but toDate is always in the local timezone.
d = "2016-11-20T08:00:00+10:00"
m = moment(d).utcOffset(d)
m.format()
m.toDate()

Unit testing handling of date inputs in JavaScript regardless of time zone

I have a form where a user can enter a date, i.e. <input type="date"> the value is submitted in yyyy-MM-dd format. When I create a Date object with the string it assumes the time zone is the one the user's browser is set to – this is the behavior I want.
I'm then using the date value to make queries against a REST API that expects ISO date/time strings. That's no problem as the toISOString function on the Date object handles everything correctly.
However, when I'm unit testing this code – setting my input to a yyyy-MM-dd string then asserting that the output is an expected ISO timestamp string the tests can only work in a particular time zone. Is there a way I can force the time zone in the test?
I've tried using Jasmine spies to do something like:
var fixedTime = moment().zone(60).toDate()
spyOn(window, 'Date').andCallFake(function() {
return fixedTime;
});
But given there are so many variants of the constructor and so many ways it gets called by moment.js this is pretty impractical and is getting me into infinite loops.
A JavaScript Date cannot be set to a particular time zone. It only knows about UTC and the computer's local time from the environment it is running on.
There are time zone libraries for javascript, but I don't think that will help you here.
First, understand that "ISO" refers to ISO8601, which is a specification that defines a collection of related formats, such as YYYY-MM-DDTHH:MM:SS.
It is a separate concept from UTC, which refers to Universal Coordinated Time. UTC is the timekeeping system that we all synchronize our clocks to, which uses GMT as its basis - that is, the time in effect at the prime meridian not adjusted for daylight saving time.
Put together, the Date.toISOString() method will return the UTC form of an ISO8601 formatted timestamp, such as 2013-09-20T01:23:45Z. The Z at the end indicates that the time is in UTC.
But a value such as 2013-09-20 is still ISO formatted - it's just that it only has precision to the whole day, which means that it can't carry any time zone information.
When you use <input type="date">, the resulting value is not a Date class. It's a string containing the ISO formatted YYYY-MM-DD. You should just pass this directly to your application.
Now if what you are looking for is the full date and time, at midnight in the local time zone, of the date selected, and adjusted to UTC, well that's a different story. It is certainly doable but you have to understand that it is not the same as just passing the calendar date.
The easiest way to do that would be with moment.js as follows:
var s = "2013-09-20"; // from your input's value property
var m = moment(s);
var result = m.toISOString(); // "2013-09-20T07:00:00.000Z"
The value is adjusted because my time zone offset is -07:00.
You can do it without moment, but you have to replace dashes with slashes or the original value will be interpreted as if it is already in UTC.
new Date(s.replace('-','/')).toISOString()

Categories

Resources