Strip Time Zone from a string using JavaScripty, Moment.js, etc? - javascript

Suppose I have a string like "01/22/2014 2:07:00 PM -08:00".
I want to
a) Format it in ISO 8601 with time offsets from UTC http://goo.gl/JTfAZq, so it becomes "2014-01-22T14:07:00-08:00"
b) Strip out time offset part so it becomes "01/22/2014 2:07:00 PM" [and then format it in ISO 8601 so it becomes "2014-01-22T14:07:00"]
Sure I can use JavaScript string functions (and regular expressions), but it seems to be a better approach to use JavaScript Date() object facilities or Moment.js. Neither work, however. Both automatically convert dates to a current system timezone (-05:00 for me), so 2:07 PM becomes 5:07 PM. I found two ways of doing that "strip out time offset then format" task, but both looks ugly and brittle:
var mydateTime = "01/22/2014 2:07:00 PM -08:00";
// strip out time offset part using substring() so that Moment.js
// would think time is specified in a current zone
var myNewDateTime1 = moment(mydateTime.substring(0, mydateTime.length - 7)).format("YYYY-MM-DDTHH:mm:ss")
// or probably even worse trick - strip out time offset part using format
var myNewDateTime2 = moment(mydateTime, "MM/DD/YYYY h:mm:ss A").format("YYYY-MM-DDTHH:mm:ss")
I understand that JavaScript Date() object is not designed to preserve a time zone, but doesn't more elegant and stable solution exist for a) and b) ?

I think you are looking for moment.ParseZone. It parses the moment AND preserves the time zone offset that was in the string, instead of converting it to the browser's local time zone.
Also, your myDateTime variable doesn't match what you were asking about. If you do indeed already have a full ISO8601 extended with time zone offfset, then it is like this:
var m = moment.parseZone("2014-01-22T14:07:00-08:00");
Or if it's like you originally, showed, then like this:
var m = moment("01/22/2014 2:07:00 PM -08:00",
"MM/DD/YYYY h:mm:ss A Z").parseZone();
From there, you can format it however you like:
var s = m.format("YYYY-MM-DDTHH:mm:ss");

Related

How to make Moment.js ignore the user's timezone?

I've got a form where I input an event that starts at a certain time. Let's say 9am.
To assign a date/time object I'm using MomentJs. The issue comes when displaying it in different time-zones.
In London will show up 9am as intended - in Kiev will show 11am.
How can I make MomentJS and the browser ignore which timezone is relevant for the user, and just displaying the time I'm giving?
Here's my code:
<p>
Start time:
{moment(event.startDate).format("HH:mm")}
</p>
Assuming you have stored the date as utc (which in this case you probably should have), you could use the following:
moment.utc(event.startDate).format("HH:mm")
Let me provide an alternative answer in Vanilla JavaScript. If you want to make it timezone 'neutral', you can first convert it to UTC using toISOString().
const current = new Date();
const utcCurrent = current.toISOString();
console.log(utcCurrent);
If you want to convert it to a specific timezone, such as London, you can use toLocaleString(). Do take note of the browser support for the timezone though.
const londonTime = new Date().toLocaleString('en-US', { timeZone: 'Europe/London' })
console.log(londonTime);
What you want is a normalized Datetime. This can get a little confusing since the concept of timezones is a rather arbitrary construct.
I like to think of Datetime values as "absolute" and "relative". An "absolute" Datetime is one that is true regardless of which timezone you're in. The most common example of these are UTC(+000) and UNIX Time (also known as Unix epoch, POSIX Time or Unix Timestampe).
UTC is pretty obvious. Its the current time at +000 timezone. UNIX time is a bit more interesting. It represents the number of seconds that have elapsed since January 1, 1970.
You should always store data, in both client and backend, as an "absolute" time. My preference is UNIX time since its represented as a single integer (nice and clean).
moment.js does this for you. When you instantiate your moment object, you can use:
var date = moment.utc(utcString)
or for Unix Time
var date = moment.unix(unixInt)
You can then use this object to display the date in any form you wish:
console.log(date.tz.("America/Toronto"))
The only way I could solve this is by removing the timezone and milliseconds info from the string. I used date-fns lib but I imagine moment will work the same way.
import { format } from 'date-fns'
const myDateTimeString = '2022-02-22T19:55:00.000+01:00'
const dateTimeWithoutTimezone = myDateTimeString.slice(0, 16) // <- 2022-02-22T19:55
format(new Date(dateTimeWithoutTimezone), 'HH:mm')

Alternative to casting UTC Date in Javascript?

I wish to create a new Date in JS, but have it be cast as UTC time. For example, suppose castAsUTC() produces the following desired effect:
var x = new Date('2019-01-01T00:00:00') // In local time (PST)
castAsUTC(x).toISOString(); // => '2019-01-01T00:00:00Z'
// x.toISOString() gives us '2019-01-01T08:00:00Z', which is undesired
Currently, my function looks like this:
function castAsUTC(date) {
return new Date(x.toLocaleString() + '+00:00');
}
Is there a cleaner/nicer way of producing the same effect? Thanks in advance!
EDIT: To be more specific, I'm interested in transforming the date's timezone, without changing its actual value with as little arithmetic as possible. So calling .toISOString() will produce the same date as it is in local time.
I am currently using the moment-timezone library, but I can't seem to get the desired effect using that, either. I would definitely accept an answer that uses Moment.js
You can switch a Moment instance to UTC using the utc function. Then just use format to get whatever the specific output you want from it.
If indeed the string you have is like the one shown, then the easiest thing to do would be to append a Z to indicate UTC.
var input = '2019-01-01T00:00:00';
var date = new Date(input + 'Z');
var output = date.toISOString();
Or, if you would like to use Moment.js, then do this:
var input = '2019-01-01T00:00:00';
var m = moment.utc(input);
var output = m.format();
You do not need moment-timezone for this.
tl;dr;
You formatted the date wrong. Add the letter "Z" to the end of your date string and it will be treated as UTC.
var x = new Date('2019-01-01T00:00:00Z') // Jan 1, 2019 12 AM UTC
These formatting issues are easier to manage with a library like momentjs (utc and format functions) as described in other answers. If you want to use vanilla javascript, you'll need to subtract out the timezone offset before calling toISOString (see warnings in the longer answer below).
Details
Date in javascript deals with timezones in a somewhat counter intuitive way. Internally, the date is stored as the number of milliseconds since the Unix epoch (Jan 1, 1970). That's the number you get when you call getTime() and it's the number that's used for math and comparisons.
However - when you use the standard string formatting functions (toString, toTimeString, toDateString, etc) javascript automatically applies the timezone offset for the local computers timezone before formatting. In a browser, that means it will apply the offset for the end users computer, not the server. The toISOString and toUTCString functions will not apply the offset - they print the actual UTC value stored in the Date. This will probably still look "wrong" to you because it won't match the value you see in the console or when calling toString.
Here's where things really get interesting. You can create Date's in javascript by specifying the number of milliseconds since the Unix epoch using new Date(milliseconds) or by using a parser with either new Date(dateString). With the milliseconds method, there's no timezone to worry about - it's defined as UTC. The question is, with the parse method, how does javascript determine which timezone you intended? Before ES5 (released 2009) the answer was different depending on the browser! Post ES5, the answer depends on how you format the string! If you use a simplified version of ISO 8601 (with only the date, no time), javascript considers the date to be UTC. Otherwise, if you specify the time in ISO 8601 format, or you use a "human readable" format, it considers the date to be local timezone. Check out MDN for more.
Some examples. I've indicated for each if javascript treats it as a UTC or a local date. In UTC, the value would be Jan 1, 1970 at midnight. In local it depends on the timezone. For OP in pacfic time (UTC-8), the UTC value would be Jan 1, 1970 at 8 AM.
new Date(0) // UTC (milliseconds is always UTC)
new Date("1/1/1970"); // Local - (human readable string)
new Date("1970-1-1"); // Local (invalid ISO 8601 - missing leading zeros on the month and day)
new Date("1970-01-01"); // UTC (valid simplified ISO 8601)
new Date("1970-01-01T00:00"); // Local (valid ISO 8601 with time and no timezone)
new Date("1970-01-01T00:00Z"); // UTC (valid ISO 8601 with UTC specified)
You cannot change this behavior - but you can be pedantic about the formats you use to parse dates. In your case, the problem was you provided an ISO-8601 string with the time component but no timezone. Adding the letter "Z" to the end of your string, or removing the time would both work for you.
Or, always use a library like momentjs to avoid these complexities.
Vanilla JS Workaround
As discussed, the real issue here is knowing whether a date will be treated as local or UTC. You can't "cast" from local to UTC because all Date's are UTC already - it's just formatting. However, if you're sure a date was parsed as local and it should really be UTC, you can work around it by manually adjusting the timezone offset. This is referred to as "epoch shifting" (thanks #MattJohnson for the term!) and it's dangerous. You actually create a brand new Date that refers to a different point in time! If you use it in other parts of your code, you can end up with incorrect values!
Here's a sample epoch shift method (renamed from castAsUtc for clarity). First get the timezone offset from the object, then subtract it and create a new date with the new value. If you combine this with toISOString you'll get a date formatted as you wanted.
function epochShiftToUtc(date) {
var timezoneOffsetMinutes = date.getTimezoneOffset();
var timezoneOffsetMill = timezoneOffsetMinutes * 1000 * 60;
var buffer = new Date(date.getTime() - timezoneOffsetMill);
return buffer;
}
epochShiftToUtc(date).toUTCString();

Moment.js transform to date object

Using Moment.js I can't transform a correct moment object to a date object with timezones. I can't get the correct date.
Example:
var oldDate = new Date(),
momentObj = moment(oldDate).tz("MST7MDT"),
newDate = momentObj.toDate();
console.log("start date " + oldDate)
console.log("Format from moment with offset " + momentObj.format())
console.log("Format from moment without offset " + momentObj.utc().format())
console.log("(Date object) Time with offset " + newDate)
console.log("(Date object) Time without offset "+ moment.utc(newDate).toDate())
Use this to transform a moment object into a date object:
From http://momentjs.com/docs/#/displaying/as-javascript-date/
moment().toDate();
Yields:
Tue Nov 04 2014 14:04:01 GMT-0600 (CST)
As long as you have initialized moment-timezone with the data for the zones you want, your code works as expected.
You are correctly converting the moment to the time zone, which is reflected in the second line of output from momentObj.format().
Switching to UTC doesn't just drop the offset, it changes back to the UTC time zone. If you're going to do that, you don't need the original .tz() call at all. You could just do moment.utc().
Perhaps you are just trying to change the output format string? If so, just specify the parameters you want to the format method:
momentObj.format("YYYY-MM-DD HH:mm:ss")
Regarding the last to lines of your code - when you go back to a Date object using toDate(), you are giving up the behavior of moment.js and going back to JavaScript's behavior. A JavaScript Date object will always be printed in the local time zone of the computer it's running on. There's nothing moment.js can do about that.
A couple of other little things:
While the moment constructor can take a Date, it is usually best to not use one. For "now", don't use moment(new Date()). Instead, just use moment(). Both will work but it's unnecessarily redundant. If you are parsing from a string, pass that string directly into moment. Don't try to parse it to a Date first. You will find moment's parser to be much more reliable.
Time Zones like MST7MDT are there for backwards compatibility reasons. They stem from POSIX style time zones, and only a few of them are in the TZDB data. Unless absolutely necessary, you should use a key such as America/Denver.
.toDate did not really work for me, So, Here is what i did :
futureStartAtDate = new Date(moment().locale("en").add(1, 'd').format("MMM DD, YYYY HH:MM"))
hope this helps
Since momentjs has no control over javascript date object I found a work around to this.
const currentTime = new Date();
const convertTime = moment(currentTime).tz(timezone).format("YYYY-MM-DD HH:mm:ss");
const convertTimeObject = new Date(convertTime);
This will give you a javascript date object with the converted time
The question is a little obscure. I ll do my best to explain this. First you should understand how to use moment-timezone. According to this answer here TypeError: moment().tz is not a function, you have to import moment from moment-timezone instead of the default moment (ofcourse you will have to npm install moment-timezone first!). For the sake of clarity,
const moment=require('moment-timezone')//import from moment-timezone
Now in order to use the timezone feature, use moment.tz("date_string/moment()","time_zone") (visit https://momentjs.com/timezone/ for more details). This function will return a moment object with a particular time zone. For the sake of clarity,
var newYork= moment.tz("2014-06-01 12:00", "America/New_York");/*this code will consider NewYork as the timezone.*/
Now when you try to convert newYork (the moment object) with moment's toDate() (ISO 8601 format conversion) you will get the time of Greenwich,UK. For more details, go through this article https://www.nhc.noaa.gov/aboututc.shtml, about UTC. However if you just want your local time in this format (New York time, according to this example), just add the method .utc(true) ,with the arg true, to your moment object. For the sake of clarity,
newYork.toDate()//will give you the Greenwich ,UK, time.
newYork.utc(true).toDate()//will give you the local time. according to the moment.tz method arg we specified above, it is 12:00.you can ofcourse change this by using moment()
In short, moment.tz considers the time zone you specify and compares your local time with the time in Greenwich to give you a result. I hope this was useful.
To convert any date, for example utc:
moment( moment().utc().format( "YYYY-MM-DD HH:mm:ss" )).toDate()
let dateVar = moment('any date value');
let newDateVar = dateVar.utc().format();
nice and clean!!!!
I needed to have timezone information in my date string. I was originally using moment.tz(dateStr, 'America/New_York').toString(); but then I started getting errors about feeding that string back into moment.
I tried the moment.tz(dateStr, 'America/New_York').toDate(); but then I lost timezone information which I needed.
The only solution that returned a usable date string with timezone that could be fed back into moment was moment.tz(dateStr, 'America/New_York').format();
try (without format step)
new Date(moment())
var d = moment.tz("2019-04-15 12:00", "America/New_York");
console.log( new Date(d) );
console.log( new Date(moment()) );
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.23/moment-timezone-with-data.min.js"></script>
moment has updated the js lib as of 06/2018.
var newYork = moment.tz("2014-06-01 12:00", "America/New_York");
var losAngeles = newYork.clone().tz("America/Los_Angeles");
var london = newYork.clone().tz("Europe/London");
newYork.format(); // 2014-06-01T12:00:00-04:00
losAngeles.format(); // 2014-06-01T09:00:00-07:00
london.format(); // 2014-06-01T17:00:00+01:00
if you have freedom to use Angular5+, then better use datePipe feature there than the timezone function here. I have to use moment.js because my project limits to Angular2 only.
new Date(moment()) - could give error while exporting the data column in excel
use
moment.toDate() - doesn't give error or make exported file corrupt

How do you check whether a date is UTC in Javascript?

I've read this question:
How do you convert a JavaScript date to UTC?
and based on this I implemented this conversion in a dateTools module as follows:
[Update]
var dt, utcTime;
dt = new Date();
utcTime = new Date(Date.UTC(dt.getFullYear(),
dt.getMonth(),
dt.getDate(),
dt.getHours(),
dt.getMinutes(),
dt.getSeconds(),
dt.getMilliseconds()));
Now I'd like to write unit tests. My idea was to check whether the result is actually in UTC, but I don't know how.
All the toString, toUTCString and similar methods seem to be identical for the input (non UTC) and output (UTC) date.
Only the result of the getTime method differs.
Is there a way to check wheter a date is UTC in javascript? If not, is there a better idea to unit test this?
To give more context:
Only converting the it to a UTC string is not that helpful, because in the next step the date is sent to an Asp.net service and therefore converted to a string like:
'/Date([time])/'
with this code
var aspDate = '/Date(' + date.getTime() + ')/';
var aspDate = '/Date(' + date.getTime() + ')/';
This outputs the internal UNIX epoch value (UTC), which represents a timestamp. You can use the various toString methods to get a more verbose string representation of that timestamp:
.toString() uses the users timezone, result is something like "Fri Jan 25 2013 15:20:14 GMT+0100" (for me, at least, you might live in a different timezone)
.toUTCString() uses UTC, and the result will look like "Fri, 25 Jan 2013 14:20:15 GMT"
.toISOString() uses UTC, and formats the datestring according to ISO: "2013-01-25T14:20:20.061Z"
So how do we construct the time value that we want?
new Date() or Date.now() result in the current datetime. No matter what the user's timezone is, the timestamp is just the current moment.
new Date(year, month, …) uses the users timezone for constructing a timestamp from the single values. If you expect this to be the same across your user community, you are screwed. Even when not using time values but only dates it can lead to odd off-by-one errors.
You can use the setYear, setMonth, … and getYear, getMonth … methods to set/get single values on existing dates according to the users timezone. This is appropriate for input from/output to the user.
getTimezoneOffset() allows you to query the timezone that will be used for all these
new Date(timestring) and Date.parse cannot be trusted. If you feed them a string without explicit timezone denotation, the UA can act random. And if you want to feed a string with a proper format, you will be able to find a browser that does not accept it (old IEs, especially).
Date.UTC(year, month, …) allows you to construct a timestamp from values in the UTC timezone. This comes in handy for input/output of UTC strings.
Every get/set method has a UTC equivalent which you can also use for these things.
You can see now that your approach to get the user-timezone values and use them as if they were in UTC must be flawed. It means either dt or utcTime has the wrong value, although using the wrong output method may let it appear correct.
getTimezoneOffset
Syntax: object.getTimezoneOffset( ) This method
returns the difference in minutes between local time and Greenwich
Mean Time. This value is not a constant, as you might think, because
of the practice of using Daylight Saving Time.
i.e.
var myDate = new Date;
var myUTCDate = new Date(myDate - myDate.getTimezoneOffset() * 60000);
alert(myUTCDate);
note: 60000 is the number of milliseconds in a minute;

json dates and timezones

I have the following code:
$(function () {
var thedate = "/Date(1198908717056)/";
var thedate2 = ProcessDate(thedate)
alert(thedate2);
});
function ProcessDate(DateString) {
var TheDate = eval(DateString.replace(/\/Date\((\d+)\)\//gi, "new Date($1)"));
return TheDate;
}
When it runs, it returns an alert with December 29 and the time is showing as Eastern Time. When I change the timezone on my computer, it's still showing the date in the Eastern timezone.
My question is this: does the string "/Date(1198908717056)/" contain the timezone information or is the timezone displayed in the alert the result of the browser determining my timezone?
Thanks.
JSON doesn't have dates at all (it's one of JSON's flaws). Those strings are just strings.
Some frameworks, like ASP.Net, use that syntax to indicate dates. What timezone they're in will be dictated by the framework. I believe the dates are in UTC and so you can just use the new Date(Number) constructor to create them (more in this other answer). That creates the date by directly setting its internal "milliseconds since The Epoch UTC" value, more in section 15.9 of the specification. Mind you, that only works if, in fact, whatever it is creating these pseudo-date strings is using UTC.
Update: Looking at your code, although it works, this line:
var TheDate = eval(DateString.replace(/\/Date\((\d+)\)\//gi, "new Date($1)"));
...is an abuse of eval. eval should be avoided whenever possible. Instead, if you want to keep it as a one-liner:
var TheDate = new Date(Number(DateString.replace(/\/Date\((\d+)\)\//gi, "$1")));
...or somewhat more readably:
var Match = /\/Date\((\d+)\)\//gi.exec(DateString);
var TheDate;
if (Match) {
TheDate = new Date(Number(Match[1]));
}
In all of those cases, the Date will be initialized with the UTC time value embedded in the date string. But then when you ask JavaScript to format the date (for instance, via toString), it will use your local timezone to do that. You haven't shown how you're outputting the date, so I can't tell you why the timezone seems not to change if you change your timezone (perhaps the browser didn't pick up the change?). When I do it, if I output the date as a string, it shows it in British Summer Time (which is my current timezone) if I use toString, or UTC if I use toUTCString. Here's a live example using both your original date, and a date (today's date, as I write this) that's in daylight savings time so even in the UK you can see the difference between UTC and local time.
Off-topic: In JavaScript, the overwhelming convention is to use camelCased names starting with a lower-case letter for both local variables and function names. So, theDate rather than TheDate. Initial caps are reserved for constructor functions (like Date). You're free to ignore the convention, of course, but it will tend to make it difficult for others to read your code.
The timezone is taken from your current system setting. Have a look at the Date class.
The given value is in milliseconds and does not contain a timezone. The constructor of Date() expects the milliseconds to be given in UTC. If you have values with a known timezone, you should use the dateString constructor version.
However, as far as I know, there is no way convert between timezones in JavaScript, except for UTC and the local system timezone.

Categories

Resources