Why does moment.js UTC always show the wrong date. For example from chrome's developer console:
moment(('07-18-2013')).utc().format("YYYY-MM-DD").toString()
// or
moment.utc(new Date('07-18-2013')).format("YYYY-MM-DD").toString()
Both of them will return "2013-07-17" why is it returning 17th instead of 18th, that was passed in.
But if I use momentjs without the utc:
moment(new Date('07-18-2013')).format("YYYY-MM-DD").toString()
I get back "2013-07-18" which is what I also expect when using moment.js UTC.
Does this mean we cannot get the correct date when using moment.js UTC?
By default, MomentJS parses in local time. If only a date string (with no time) is provided, the time defaults to midnight.
In your code, you create a local date and then convert it to the UTC timezone (in fact, it makes the moment instance switch to UTC mode), so when it is formatted, it is shifted (depending on your local time) forward or backwards.
If the local timezone is UTC+N (N being a positive number), and you parse a date-only string, you will get the previous date.
Here are some examples to illustrate it (my local time offset is UTC+3 during DST):
>>> moment('07-18-2013', 'MM-DD-YYYY').utc().format("YYYY-MM-DD HH:mm")
"2013-07-17 21:00"
>>> moment('07-18-2013 12:00', 'MM-DD-YYYY HH:mm').utc().format("YYYY-MM-DD HH:mm")
"2013-07-18 09:00"
>>> Date()
"Thu Jul 25 2013 14:28:45 GMT+0300 (Jerusalem Daylight Time)"
If you want the date-time string interpreted as UTC, you should be explicit about it:
>>> moment(new Date('07-18-2013 UTC')).utc().format("YYYY-MM-DD HH:mm")
"2013-07-18 00:00"
or, as Matt Johnson mentions in his answer, you can (and probably should) parse it as a UTC date in the first place using moment.utc() and include the format string as a second argument to prevent ambiguity.
>>> moment.utc('07-18-2013', 'MM-DD-YYYY').format("YYYY-MM-DD HH:mm")
"2013-07-18 00:00"
To go the other way around and convert a UTC date to a local date, you can use the local() method, as follows:
>>> moment.utc('07-18-2013', 'MM-DD-YYYY').local().format("YYYY-MM-DD HH:mm")
"2013-07-18 03:00"
Both Date and moment will parse the input string in the local time zone of the browser by default. However Date is sometimes inconsistent with this regard. If the string is specifically YYYY-MM-DD, using hyphens, or if it is YYYY-MM-DD HH:mm:ss, it will interpret it as local time. Unlike Date, moment will always be consistent about how it parses.
The correct way to parse an input moment as UTC in the format you provided would be like this:
moment.utc('07-18-2013', 'MM-DD-YYYY')
Refer to this documentation.
If you want to then format it differently for output, you would do this:
moment.utc('07-18-2013', 'MM-DD-YYYY').format('YYYY-MM-DD')
You do not need to call toString explicitly.
Note that it is very important to provide the input format. Without it, a date like 01-04-2013 might get processed as either Jan 4th or Apr 1st, depending on the culture settings of the browser.
use this :
return moment.utc(new Date(oData.CreatedAtUtc), 'MM/DD/YYYY h:mm A').local().format("YYYY-MM-DD HH:mm") + ' (' + timezoneAbbr + ')';
Related
I have utc date 2017-04-22T21:03:54 (local date is 2017-04-23 00:03:54, offset +3)
I would like to get data from server for 1 day. For this example, local day is:
start: 2017-04-23 00:00:00
end: 2017-04-23 23:59:59
And UTC params for server:
start: 2017-04-22 21:00:00
end: 2017-04-23 20:59:59
How I can receive this dates with moment.js? Local offset may be different
I try:
moment.utc(startTime).format() // 2017-04-22T21:03:54Z, but I need 2017-04-22T21:00:00Z
// for example, if local timezone will be +6, I need 2017-04-22T18:00:00Z
(Putting my comments as answer)
The representation of datetimes in a text encoded transport/protocol (as HTTP) should follow ISO-8601. The time portion should include a timezone designator (for UTC time this is the simple Z, see link). These convention removes any ambiguity between client and server about what moment does the time literal represent.
Back ends should store times in UTC:
This removes any ambiguity when the database moves (failover to standby in different timezone, move of server to new location from east coast to west coats etc).
It removes any ambiguity for literals that fall into the DST time overlaps.
It removes any ambiguity from rushed legislative changes modifying timezones (of which several occur every few years)
Now that being said, if you need to convert an UTC time to local time in JavaScript/Node with MomentJS, use Moment's local().
I have utc date 2017-04-22T21:03:54
A date string without a time zone is treated as local (except for ISO 8601 date-only forms, which ECMAScript treats as UTC, contrary to ISO 8601). If you want to treat a string like "2017-04-22 21:00:00" as UTC then you have to tell the parser (in moment.js you can use the utc method).
You should also always tell the parser the format it's trying to parse, otherwise you're hoping it guesses correctly. If utc is used, moment uses offset +0000 by default (its "UTC mode" * ), not the local offset. If you want the host offset, you can use the local method or convert to a Date object and use built-in methods.
var s = '2017-04-22 21:00:00';
var format = 'YYYY-MM-DD HH:mm:ss';
// Tell parser to treat as UTC and the format
var m = moment.utc(s, format);
// Once the string has been parsed, you can output it in any format you like
console.log(
'Original format, offset +0000 : ' + m.format(format)
+'\nBuilt-in toISOString, offset Z: ' + m.toDate().toISOString()
+'\nBuilt-in toString, host offset: ' + m.toDate().toString()
+'\nOriginal format, host offset : ' + m.local().format(format)
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
* I'm not sure this is a good idea as it means the code using the object needs to know it's in UTC mode. If you get a moment.js object from elsewhere, it would be good practice to set it to the "mode" you want. Calling local or utc multiple times has no bad effects.
In doing some testing I've found inconsistant behavior between browsers with the following javascript
new Date("2013-09-10T08:00:00").toString()
In IE and Firefox the result is
"Tue Sep 10 2013 08:00:00 GMT-0400 (Eastern Daylight Time)"
In Chrome the result is
"Tue Sep 10 2013 04:00:00 GMT-0400 (Eastern Daylight Time)"
So according to my reading of the ECMA script of the format for Date strings it says...
All numbers must be base 10. If the MM or DD fields are absent "01" is
used as the value. If the HH, mm, or ss fields are absent "00" is
used as the value and the value of an absent sss field is "000".
The value of an absent time zone offset is "Z"
However in the documentation for the "new Date()" constructor it says
15.9.3.2 new Date (value)
Let v be ToPrimitive(value).
If Type(v) is String, then
a. Parse v as a date, in exactly the same manner as for the parse method (15.9.4.2); let V be the time
value for this date.
15.9.4.2 Date.parse (string)
The parse function applies the ToString operator to its argument and interprets the resulting String as a date
and time; it returns a Number, the UTC time value corresponding to the
date and time. The String may be interpreted as a local time, a UTC
time, or a time in some other time zone, depending on the contents of
the String.
Any ideas which implementation is correct?
Standards clash. ISO 8601 states that:
If no UTC relation information is given with a time representation, the time is assumed to be in local time.
ECMA says:
The value of an absent time zone offset is “Z”.
Mozilla devs think that ISO takes precedence, Chrome folks seem to disagree.
The current draft of ES6 says (under 20.3.1.15):
If the time zone offset is absent, the date-time is interpreted as a local time.
so Mozilla's implementation is (will be) correct.
There are several questions on stackoverflow.com that address this issue. I gave a rather thorough explanation here if anyone reading this is interested in the browser-to-browser details.
The bottom line though is, for now at least, you should either avoid the ISO 8601 format all together or ALWAYS include a timezone specifier when using it. And, never use the 'YYYY-MM-dd' format because it gets interpreted as a short version of ISO 8601 without a time zone specifier.
I'm trying to pass both date strings to new Date(t).
I expect both strings represent the same time, after all, if I omit the time, shouldn't it be midnight of that day?
But while,
new Date("2016-02-16 00:00")
returns 2016-02-16, midnight, local time as expected,
new Date("2016-02-16")
returns 2016-02-16, midnight UTC, which is wrong, or at least not what I expected given what the other string parses as.
I would understand it if they would both have the same behavior, whether it is to return the time as local time, or as UTC, but it seems very inconsistent why they return different things like this.
As a workaround, whenever I encounter a date that has no corresponding timestamp I can append " 00:00" to get consistent behavior, but it seems like this is rather fragile.
I am getting this value from an INPUT element, of type 'datetime-local' so it seems especially inconsistent that I have to work around a value returned by a page element.
Am I doing something wrong, or should I be doing something differently?
It's what the ES5.1 specification says to do:
The value of an absent time zone offset is “Z”.
It also says:
The function first attempts to parse the format of the String according to the rules called out in Date Time String Format (15.9.1.15). If the String does not conform to that format the function may fall back to any implementation-specific heuristics or implementation-specific date formats.
Since the format requires a T separator between date and time, the valid times go to UTC:
> new Date("2016-02-16T00:00:00")
Tue Feb 16 2016 01:00:00 GMT+0100 (CET)
> new Date("2016-02-16")
Tue Feb 16 2016 01:00:00 GMT+0100 (CET)
...while in node.js, an invalid time (without the T separator) seems to go to the implementation specific localtime:
> new Date("2016-02-16 00:00:00")
Tue Feb 16 2016 00:00:00 GMT+0100 (CET)
Note that ES6 changed this, in the same part of the documentation it changes to:
If the time zone offset is absent, the date-time is interpreted as a local time.
The joy of breaking changes.
Edit
According to TC39, the specification is meant to be interpreted as date and time strings without a time zone (e.g. "2016-02-16T00:00:00") are treated as local (per ISO 8601), but date only strings (e.g. "2016-02-16") as UTC (which is inconsistent with ISO 8601).
According to the specifications:
The function first attempts to parse the format of the String
according to the rules called out in Date Time String Format
(15.9.1.15). If the String does not conform to that format the
function may fall back to any implementation-specific heuristics or
implementation-specific date formats.
And Date Time String Formats accept 2016-02-16 as a valid date
This format includes date-only forms:
YYYY
YYYY-MM
YYYY-MM-DD
[...] If the HH, mm, or ss fields are absent “00” is used as the value
and the value of an absent sss field is “000”. The value of an absent
time zone offset is “Z”.
Thus 2016-02-16 translates to 2016-02-16T00:00:00.000Z.
The other date 2016-02-16 00:00 does not conform to the format and therefore its parsing is implementation specific. Apparently, such dates are treated as having local time zone and your example date will return different values depending on time zone:
/* tz = +05:00 */ new Date("2016-02-16 00:00").toISOString() // 2016-02-15T19:00:00.000Z
/* tz = -08:00 */ new Date("2016-02-16 00:00").toISOString() // 2016-02-16T08:00:00.000Z
Summary:
For conforming date time formats the behavior is well defined — in the absence of time zone offset the date string is treated as UTC (ES5) or local (ES6).
For non-conforming date time formats the behavior is implementation specific — in the absence of time zone offset the usual behavior is to treat the date as local.
As a matter of fact, the implementation could choose to return NaN instead of trying to parse non-conforming dates. Just test your code in Internet Explorer 11 ;)
You are perhaps running into a differences between ES5, ES6 implementations and your expected result. Per Date.parse at MDN, "especially across different ECMAScript implementations where strings like "2015-10-12 12:00:00" may be parsed to as NaN, UTC or local timezone" is significant.
Additional testing in Firefox 44 and IE 11 revealed they both return a date object for new Date("2016-02-16 00:00"), which object returns NaN when atttempting to get a date component value, and whose toString value is
"Invalid Date" (not "NaN"). Hence appending " 00:00 to get consistent behavior" can easily break in different browsers.
As noted in other answers new Date("2016-02-16") uses a timezone offset of zero by default, producing midnight UTC instead of local.
Per DateParser::Parse() of V8 source codes for Chrome.
ES5 ISO 8601 dates:
[('-'|'+')yy]yyyy[-MM[-DD]][THH:mm[:ss[.sss]][Z|(+|-)hh:mm]]
An unsigned number followed by ':' is a time value, and is added to the TimeComposer.
timezone defaults to Z if missing
> new Date("2016-02-16 00:00")
Tue Feb 16 2016 00:00:00 GMT+0800 (China Standard Time)
A string that matches both formats (e.g. 1970-01-01) will be parsed as an ES5 date-time string - which means it will default to UTC time-zone. That's unavoidable if following the ES5 specification.
> new Date("2016-02-16")
Tue Feb 16 2016 08:00:00 GMT+0800 (China Standard Time)
returns 2016-02-16, midnight UTC, which is wrong, or at least not what I expected given what the other string parses as.
It adds the timezone offset to the 00:00
new Date("2016-02-16") outputs Tue Feb 16 2016 05:30:00 GMT+0530 (India Standard Time)
My timezone being IST with an offset value (in minutes) +330, so it added 330 minutes to 00:00.
As per ecma-262, section 20.3.3.2 Date.parse ( string )
If ToString results in an abrupt completion the Completion Record is
immediately returned. Otherwise, parse interprets the resulting String
as a date and time; it returns a Number, the UTC time value
corresponding to the date and time. The String may be interpreted as a
local time, a UTC time, or a time in some other time zone, depending
on the contents of the String.
When you explicitly set the time-units new Date("2016-02-16 00:00") it wll use set that as hours and minutes,
Otherwise as stated here in 20.3.1.16
If the time zone offset is absent, the date-time is interpreted as a
local time.
I am trying to convert UTC time formate to local time format:
Here is the example:
I got the UTC format time from the server side, which looks like: 2014-09-17T11:09:58+1100. My requirement is to convert it to 17 Sep 2014.
However, I tried all the following method, but still get 03 Sep 2014. why this happened?
what I tried:
function dateFormatter(UTC) {
var dateFormat = 'd MMM YYYY';
console.log(moment(UTC).zone('+1100').format(dateFormat));
return moment(UTC).format(dateFormat).toLocaleString();
}
Those two method still get the same result: 03 Sep 2014
A few things:
UTC refers to Coordinated Universal Time, which is a system of timekeeping - not a format.
A value that is "in UTC" would have an offset of zero. Since you said the value has an offset of +1100, it doesn't make any sense to call it "UTC".
You should not call the zone function. That is for converting an input value to a particular time zone offset. Since your input value already contains an offset, calling it makes little sense.
If you're just trying to preserve the offset that you were given, then use moment.parseZone instead.
The format string characters are case sensitive. Lower-case d is for the day of the week as a number 0 through 6. You should use an upper-case D to specify the day of the month.
toLocaleString is not required. Note that you were calling that function from a string, not a date or a moment.
Putting it all together:
function dateFormatter(s) {
return moment.parseZone(s).format('D MMM YYYY');
}
Given the volume of Timezone questions, I would have thought to be able to find the answer to this issue, but haven't had any success.
Is there a way using moment.js to parse an ISO-8601 string but have it parsed in my local timzeone? Essentially I want to ignore the timezone information that is supplied in the ISO string.
For example, if I am in EDT timezone:
var x = moment( "2012-12-31T00:00:00+0000" );
will give me:
"2012-12-30T19:00:00-5000"
I'm looking to ignore the timezone info and just have it give me a moment equivalent of "2012-12-31T00:00:00-5000" local time (EDT).
I don't think you really want to ignore the offset. That would ultimately just be replacing the offset you provided with one from your local time zone - and that would result in a completely different moment in time.
Perhaps you are just looking for a way to have a moment retain the time zone it was given? If so, then use the moment.parseZone function. For example:
var m = moment.parseZone("2012-12-31T00:00:00+0000");
var s = m.format(); // "2012-12-31T00:00:00+00:00"
You could also achieve this with moment.utc. The difference is that moment.parseZone will retain whatever offset you give it, while moment.utc will adjust to UTC if you give it a non-zero offset.
I solved this by supplying a format as the second argument, and using Moment's method of escaping characters, and wrapped square brackets around the timezone.
moment("2016-01-01T05:00:00-05:00", "YYYY-MM-DDTHH:mm:ss[Z]").startOf("hour").format()
This will still create moment objects using your local time zone, but it won't do any sort of auto-timezone calculation. So the above example will give you 5am regardless of timezone supplied.
I know I'm late to the party, I had the same question and my searches didn't bring me any closer. I broke down and read the documentation and there is an option in moment for a String + Format:
String + Format docs
moment(String, String);
moment(String, String, String);
moment(String, String, Boolean);
moment(String, String, String, Boolean);
and more words, then this:
Unless you specify a time zone offset, parsing a string will create a date in the current time zone.
moment("2010-10-20 4:30", "YYYY-MM-DD HH:mm"); // parsed as 4:30 local time
moment("2010-10-20 4:30 +0000", "YYYY-MM-DD HH:mm Z"); // parsed as 4:30 UTC
The part that gave me pause was the example that was used to parse local time omitted the +0000, which lead me to think the input string needed to have that removed, but it doesn't.
example:
var time = "2012-12-31T00:00:00+0000";
var x = moment(time); // Sun Dec 30 2012 19:00:00 GMT-0500
var y = moment(time,'YYYY-MM-DD'); //Mon Dec 31 2012 00:00:00 GMT-0500
You can ignore the browser's timezone completely by creating a new moment using moment.utc() instead of moment().
For example, if you are trying to work purely with a UTC date/time of the browser's current time but want to discard its timezone data, you can recreate the browser's current time into a UTC format using the following:
let nowWithTimezone = moment();
let nowInUtc = moment.utc(nowWithTimezone.format('MM/DD/YYYY HH:mm'), 'MM/DD/YYYY HH:mm');
Further documentation on moment.utc(): https://momentjs.com/docs/#/parsing/utc/
If you know for sure your input string is in the ISO-8601 format, you could just strip off the last 5 digits and use that in the Moment constructor.
var input = "2012-12-31T00:00:00+0000"
input = input.substring(0, input.length-5)
moment(input).toString()
> "Mon Dec 31 2012 00:00:00 GMT-0600"
There are valid reasons to do what the OP is asking for. The easiest way to do this with Moment is using its parseZone(date) method. No futzing around with string manipulation or multiple calls. It effectively parses the date string as though it were in the browser's local time zone.
This is difficult task to do with MomentJS, it will basically depend as well on your current timezone.
Documentation as well is vague for this specific task, the way I solved the issue on my side was by adding hours to the date before converting it to JSON format.
var dt = moment("Sun Sep 13 2015 00:00:00 GMT-0400", "ddd MMM DD YYYY HH:mm:ss GMT-0400", false);
var date = dt.add(2, 'hour').toJSON();
console.log(date); //2015-09-13T00:00:00.000Z
Momentjs default logic will format the given time with local timezone. To format original date, I wrote a function:
https://github.com/moment/moment/issues/2788#issuecomment-321950638
Use moment.parseZone to convert without taking into account the timezone.
const moment = require('moment')
const dateStr = '2020-07-21T10:00:00-09'
const date = moment.parseZone(dateStr)
console.log(date.format('MM-DD-YY HH:mm A')) // 07-21-20 10:00 AM
Try here link to docs
The best way is to use:
dt = moment("Wed Sep 16 2015 18:31:00 GMT-0400", "ddd MMM DD YYYY HH:mm:ss GMT-0400",true);
And to display convert again to desired timezone:
dt.utcOffset("-04:00").toString()
output > Wed Sep 16 2015 18:31:00 GMT-0400