dthrasher
dthrasher

Reputation: 41802

How to convert a Date to UTC?

Suppose a user of your website enters a date range.

2009-1-1 to 2009-1-3

You need to send this date to a server for some processing, but the server expects all dates and times to be in UTC.

Now suppose the user is in Alaska. Since they are in a timezone quite different from UTC, the date range needs to be converted to something like this:

2009-1-1T8:00:00 to 2009-1-4T7:59:59

Using the JavaScript Date object, how would you convert the first "localized" date range into something the server will understand?

Upvotes: 881

Views: 1592182

Answers (30)

Matt Johnson-Pint
Matt Johnson-Pint

Reputation: 241525

I'll add a new answer to this very old question, because it's referenced often and all the other answers are demonstrating incorrect approaches such as epoch shifting or re-parsing, as described here. Additionally, they're only addressing the title of the question. I'll instead focus on the question body, and ignore the title.

Suppose a user of your website enters a date range.

2009-1-1 to 2009-1-3

You need to send this date to a server for some processing, but the server expects all dates and times to be in UTC.

First, note that this format is nonstandard. It should be using two digits for both the month and day components. Leading zeros are required for ISO 8601 compatibility, which is typically expected from date parsers. The dates should be expressed as 2009-01-01 to 2009-01-03.

The next error is in the design that "... all dates and times to be in UTC". The user didn't select a date and time, they only selected a date. Whole dates cannot be converted to UTC, because they're not in any particular time zone to begin with. Assuming otherwise is akin to me handing you a paper calendar such as below and asking what time zone it's in.

Jan 2009 calendar

Of course, none of those days has a time zone, because time is not represented on this device. Each square represents a specific day within the given month and year. If one were to associate time with each day, it wouldn't be a single time, but rather the range of times from the start of the day to the end of the day. Sure, each of those points could be converted to UTC if the calendar were to be read by a person in a particular time zone. But generally we don't go around conveying ranges of timestamps to each-other. Instead we just use a date - so that's what your program should do as well.

This is indeed what happens with the standard HTML5 <input type="date"> tag. Pick a date from below, and notice that value given is a string in ISO 8601 yyyy-mm-dd format.

<input type="date" id="dt" onchange="console.log(this.value, typeof(this.value))">

It's not a JS Date object, because the Date object is actually not a date, but rather a timestamp. It's name was poorly chosen. It contains only a single value, which is the number of milliseconds since 1970-01-01 00:00:00.000 UTC. Thus, a Date object can't accurately represent a date (a square on the calendar, or the user's selection from the date input).

So instead of assigning an arbitrary time (such as midnight) and converting between time zones, the simpler solution is to just send the year-month-day string directly to your server. The server should then store it in a way that preserves the year month and day. For example, most relational databases offer a date data type that is distinct from datetime or timestamp. If the user passes a date, then store a date, using the date type. When you later retrieve it, also just pass back the date. Don't assign midnight. In the example use case, you might pass data back and forth in JSON such as this:

{
  "startDate": "2009-01-01",
  "endDate": "2009-01-03"
}

The same applies to passing data in a form post or querystring or any other manner. Just pass the date, and don't add any time or time zone. "2009-01-01" is not the same as any of "2009-01-01T00:00:00.000" "2009-01-01T00:00:00.000Z", "2009-01-01T00:00:00.000-8:00", etc.

Going back to the remainder of the question:

Now suppose the user is in Alaska. Since they are in a timezone quite different from UTC, the date range needs to be converted to something like this:

2009-1-1T8:00:00 to 2009-1-4T7:59:59

Using the JavaScript Date object, how would you convert the first "localized" date range into something the server will understand?

I hope by now you realize that the premise of this question is flawed. One doesn't use the Date object for this purpose. Nor does one consider that the user is Alaska if they're just selecting a range of dates. For all we know, those dates might be intended to later used to book a hotel stay in Japan. The user's local time zone is thus irrelevant.

That said, what if we did need additional time zone information for some scenario? Perhaps instead of picking a range of dates, we're actually picking the date and time for a meeting. Well, then we would also need to know the time zone for that meeting. It might be the same as the user's time zone, or perhaps the meeting is to be held in some other location.

If it's the user's time zone we need, we can gather that from the browser, using the EcmaScript Intl API.

const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
console.log("Your time zone is:", tz);

If it's some other time zone, we'll have to provide a drop-down list or some other mechanism to select the target time zone.

Either way, we should store for the meeting the selected date, the selected time, and the selected time zone. We should not convert that to a UTC timestamp, because we cannot predict the future. There's no way to know if the government responsible for that time zone may change its standard time offset or daylight saving time dates or times, etc.

For example, if the meeting is to be held at 2:00 PM in New York on July 1 2030, then we should send to our backend something like:

{
  "datetime": "2030-07-01T14:00",
  "timezone": "America/New_York"
}

If I were, for example, storing that in a MySQL database, I'd use the datetime and varchar data types. If using PostgreSQL, it would be timestamp without time zone and varchar. For this purpose I wouldn't use timestamp in MySQL or timestamp with time zone in PostgreSQL, because those types are designed to convert values to/from UTC.

Summary

The original question was an XY problem with a misleading title. The presumption that one should convert to/from UTC when working with date-only values is incorrect. Instead, pass date-only values as strings, in yyyy-mm-dd format, without conversion.

Upvotes: 2

I am L
I am L

Reputation: 4632

For other people whos goal is to get it as a "Date Object" and not as a string, and you only want to display the date/time without the TZ (probably hardcoded), what you can do is:

const now = new Date();
const year = now.getUTCFullYear();
const month = now.getUTCMonth();
const day = now.getUTCDate();
const hour = now.getUTCHours();

const tomorrowUTC= new Date();
tomorrowUTC.setYear(year);
tomorrowUTC.setMonth(month);
tomorrowUTC.setDate(day + 1); // +1 because my logic is to get "tomorrow"
tomorrowUTC.Hours(hour);

// then use the tomorrowUTC for to display/format it
// tomorrowUTC is a "Date" and not a string.

You can then do stuff like:

We will delete your account at ${format(tomorrowUTC, 'EEEE do MMMM hh:mmaaa')} UTC

(format is a date-fns function, you can use other lib if you want);

This is kinda "hacky" as this is still using your local timezone, but if you just wanna display the date and not the timezone, then this works.

Upvotes: 7

Sandip Patel - SM
Sandip Patel - SM

Reputation: 3394

For Node Js timestamp conversion

process.env.TZ = "UTC"
var moment = require('moment')

module.exports = {
    mysqlTimeStamp: {
        CreateAt: moment(new Date()).format("YYYY-MM-DD HH:mm:ss"),
        UpdateAt: moment(new Date()).format("YYYY-MM-DD HH:mm:ss")
    },
    mysqlUpdateTimeStamp: {
        UpdateAt: moment(new Date()).format("YYYY-MM-DD HH:mm:ss")
    },
}

Upvotes: -1

CMcClymont
CMcClymont

Reputation: 614

Another solution to convert to UTC and keep it as a date object: (It works by removing the ' GMT' part from the end of the formatted string, then putting it back into the Date constructor)

const now = new Date();
const now_utc = new Date(now.toUTCString().slice(0, -4));
console.log(now_utc.toString()); // ignore the timezone

I needed to do this to interface with a datetime picker library. But in general it's a bad idea to work with dates this way.

Users generally want to work with datetimes in their local time, so you either update the server side code to parse datetime strings with offsets correctly, then convert to UTC (best option) or you convert to a UTC string client-side before sending to the server (like in Will Stern's answer)

Upvotes: 31

Jay
Jay

Reputation: 806

Extension function:

if (!Date.prototype.toUTC){
    Date.prototype.toUTC = function(){
        var utcOffset = new Date().getTimezoneOffset();
        var utcNow    = new Date().addMinutes(utcOffset);
        return utcNow;
    };
}

Usage:

new Date().toUTC();

Upvotes: -2

DrunkCoder
DrunkCoder

Reputation: 8523

Simple and stupid

var date = new Date();
var now_utc = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(),
                date.getUTCDate(), date.getUTCHours(),
                date.getUTCMinutes(), date.getUTCSeconds());

console.log(new Date(now_utc));
console.log(date.toISOString());

Upvotes: 743

Gras Double
Gras Double

Reputation: 16373

Here's my method:

var now = new Date();
var utc = new Date(now.getTime() + now.getTimezoneOffset() * 60000);

The resulting utc object isn't really a UTC date, but a local date shifted to match the UTC time (see comments). However, in practice it does the job.


Update: This answer is a quick-and-dirty way to get the UTC date when calling utc.toString(), utc.toLocaleString(), etc. Though, there are better solutions, in particular nowadays with modern browsers, and I should work on an improved answer. Basically, now.toISOString() (IE 9+) is what you want to use.

Upvotes: 269

Saurabh Joshi
Saurabh Joshi

Reputation: 81

As per what I observe,

var timeUtc = new Date();

timeUtc will always have UTC time but if you debug or console the browser will always show time as per the current timezone.

If you send timeUtc as a parameter to any post request then in the network tab of chrome you can check the post data and you will see timeUtc will have UTC time.

Upvotes: -2

Lucas Andrade
Lucas Andrade

Reputation: 4580

If your date has the timezone on it you can use date-fns-tz:

import { zonedTimeToUtc } from 'date-fns-tz';

const dateBrazil = new Date() // I'm in Brazil, you should have or get the user timezone.
const dateUtc = zonedTimeToUtc(dateBrazil, 'America/Sao_Paulo')

Upvotes: 3

Sumit Khanduri
Sumit Khanduri

Reputation: 3798

const event = new Date();

console.log(event.toUTCString());

Upvotes: -5

CryptoManiac
CryptoManiac

Reputation: 111

By far the best way I found to get the GMT time is first get your local date time. Then convert in to GMT String. Then use the string to build new time by removing the timezone.

let dtLocal = new Date()
let dt = new Date(dtLocal.toISOString().split('Z')[0])

Note: - it will create the new datetime in GMT. But it will be local date time as timezone will be attached to it.

Upvotes: 1

Will Stern
Will Stern

Reputation: 17579

The toISOString() method returns a string in simplified extended ISO format (ISO 8601), which is always 24 or 27 characters long (YYYY-MM-DDTHH:mm:ss.sssZ or ±YYYYYY-MM-DDTHH:mm:ss.sssZ, respectively). The timezone is always zero UTC offset, as denoted by the suffix "Z".

Source: MDN web docs

The format you need is created with the .toISOString() method. For older browsers (ie8 and under), which don't natively support this method, the shim can be found here:

This will give you the ability to do what you need:

var isoDateString = new Date().toISOString();
console.log(isoDateString);

For Timezone work, moment.js and moment.js timezone are really invaluable tools...especially for navigating timezones between client and server javascript.

Upvotes: 606

Ali Ataf
Ali Ataf

Reputation: 689

The getTimezoneOffset() method returns the time zone difference, in minutes, from current locale (host system settings) to UTC.

Source: MDN web docs

This means that the offset is positive if the local timezone is behind UTC, and negative if it is ahead. For example, for time zone UTC+02:00, -120 will be returned.

let d = new Date();
console.log(d);
d.setTime(d.getTime() + (d.getTimezoneOffset() * 60000));
console.log(d);

NOTE: This will shift the date object time to UTC±00:00 and not convert its timezone so the date object timezone will still the same but the value will be in UTC±00:00.

Upvotes: 5

Nitin Manocha
Nitin Manocha

Reputation: 140

You can use the following method to convert any js date to UTC:

let date = new Date(YOUR_DATE).toISOString()

// It would give the date in format "2020-06-16T12:30:00.000Z" where Part before T is date in YYYY-MM-DD format, part after T is time in format HH:MM:SS  and Z stands for UTC - Zero hour offset

Upvotes: 0

Adam Pietrasiak
Adam Pietrasiak

Reputation: 13194

My solution keeps the date the same no matter what timezone is set on the client-side. Maybe someone will find it useful.

My use case:

I'm creating a todo app, where you set date of your task. This date should remain constant no matter what timezone you're in.

Example. You want to call your friend at 8 am on June 25th.

You create this task 5 days before (June 20th) while you're in China.

Then, on the same day, you fly to New York for a few days.

Then on June 25th, while you're still in New York, you wake up at 7:30 am (which means you should receive task notification in 30 mins (even tho it's 1:30 pm already in China where you were when creating the task)

So the task is ignoring the timezone. It means 'I want to do it at 8 am in whatever timezone I'll be in'.

What I do is let's say 'I assume you're always in London Timezone - UTC'.

What it means is - when the user picks some date in her/his Timezone - I convert this date to the same date in UTC. ie. You pick 8 am in China, but I convert it to 8 am in UTC.

Then - next time you open the app - I read the date saved in UTC and convert it to the same date in your current timezone - eg. I convert 8 am in UTC to 8 am in the New York timezone.

This solution means that the date can mean something else depending on where you are when setting it and where you're reading it, but it remains constant in a way that it 'feels' like you're always in the same timezone.

Let's write some code:

First - we have 2 main functions for converting from/to UTC ignoring timezone:

export function convertLocalDateToUTCIgnoringTimezone(date: Date) {
  const timestamp = Date.UTC(
    date.getFullYear(),
    date.getMonth(),
    date.getDate(),
    date.getHours(),
    date.getMinutes(),
    date.getSeconds(),
    date.getMilliseconds(),
  );

  return new Date(timestamp);
}

export function convertUTCToLocalDateIgnoringTimezone(utcDate: Date) {
  return new Date(
    utcDate.getUTCFullYear(),
    utcDate.getUTCMonth(),
    utcDate.getUTCDate(),
    utcDate.getUTCHours(),
    utcDate.getUTCMinutes(),
    utcDate.getUTCSeconds(),
    utcDate.getUTCMilliseconds(),
  );
}

Then, I save/read this date like:

function saveTaskDate(localDate: Date) {
  // I convert your local calendar date so it looks like you've picked it being in UTC somewhere around London
  const utcDate = convertLocalDateToUTCIgnoringTimezone(localDate);
  api.saveTaskDate(utcDate);
}

function readTaskDate(taskUtcDate: Date) {
  // I convert this UTC date to 'look in your local timezone' as if you were now in UTC somewhere around london
  const localDateWithSameDayAsUTC = convertUTCToLocalDateIgnoringTimezone(taskUtcDate);

  // this date will have the same calendar day as the one you've picked previously
  // no matter where you were saving it and where you are now
}

Upvotes: 26

kjohnsonthecoder
kjohnsonthecoder

Reputation: 181

So this is the way I had to do it because i still wanted a JavaScript date object to manipulate as a date and unfortunantly alot of these answers require you to go to a string.

//First i had a string called stringDateVar that i needed to convert to Date
var newDate = new Date(stringDateVar)

//output: 2019-01-07T04:00:00.000Z
//I needed it 2019-01-07T00:00:00.000Z because i had other logic that was dependent on that 

var correctDate = new Date(newDate.setUTCHours(0))

//This will output 2019-01-07T00:00:00.000Z on everything which allows scalability 

Upvotes: -1

Bimal Jha
Bimal Jha

Reputation: 409

Using moment package, you can easily convert a date string of UTC to a new Date object:

const moment = require('moment');
let b = new Date(moment.utc('2014-02-20 00:00:00.000000'));
let utc = b.toUTCString();
b.getTime();

This specially helps when your server do not support timezone and you want to store UTC date always in server and get it back as a new Date object. Above code worked for my requirement of similar issue that this thread is for. Sharing here so that it can help others. I do not see exactly above solution in any answer. Thanks.

Upvotes: 2

keemor
keemor

Reputation: 1279

If you need Date Object

Passing only date string Date assumes time to be 00:00 shifted by time zone:

new Date('2019-03-11')
Sun Mar 10 2019 18:00:00 GMT-0600 (Central Standard Time)

If you add current hours and minutes you get proper date:

new Date('2019-03-11 ' + new Date().getHours() + ':' + new Date().getMinutes())
Mon Mar 11 2019 04:36:00 GMT-0600 (Central Standard Time)

Upvotes: 4

Yaki Klein
Yaki Klein

Reputation: 4376

Using moment.js UTC method;

const moment = require('moment');
const utc = moment.utc(new Date(string));

Upvotes: 4

AmirHossein Manian
AmirHossein Manian

Reputation: 617

This method will give you : 2017-08-04T11:15:00.000+04:30 and you can ignore zone variable to simply get 2017-08-04T11:15:00.000.

function getLocalIsoDateTime(dtString) {
    if(dtString == "")
        return "";
    var offset = new Date().getTimezoneOffset();
    var localISOTime = (new Date(new Date(dtString) - offset * 60000 /*offset in milliseconds*/)).toISOString().slice(0,-1);
    //Next two lines can be removed if zone isn't needed.
    var absO = Math.abs(offset);
    var zone = (offset < 0 ? "+" : "-") + ("00" + Math.floor(absO / 60)).slice(-2) + ":" + ("00" + (absO % 60)).slice(-2);
    return localISOTime + zone;
}

Upvotes: 4

Pradyumna Das
Pradyumna Das

Reputation: 85

This is what I have done in the past:

var utcDateString = new Date(new Date().toUTCString()).toISOString();

Upvotes: 3

Aaron Hoffman
Aaron Hoffman

Reputation: 6962

Browsers may differ, and you should also remember to not trust any info generated by the client, that being said, the below statement works for me (Google Chrome v24 on Mac OS X 10.8.2)

var utcDate = new Date(new Date().getTime());


edit: "How is this different than just new Date()?" see here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date

  • If no arguments are provided, the constructor creates a JavaScript Date object for the current date and time according to system settings.
  • Note: Where Date is called as a constructor with more than one argument, the specifed arguments represent local time. If UTC is desired, use new Date(Date.UTC(...)) with the same arguments. (note: Date.UTC() returns the number of millisecond since 1970-01-01 00:00:00 UTC)

Adding the 60000 * Date.getTimezoneOffset() as previous answers have stated is incorrect. First, you must think of all Dates/Times as already being UTC with a timezone modifier for display purposes.

Again, browsers may differ, however, Date.getTime() returns the number of milliseconds since 1970-01-01 UTC/GMT. If you create a new Date using this number as I do above, it will be UTC/GMT. However, if you display it by calling .toString() it will appear to be in your local timezone because .toString() uses your local timezone, not the timezone of the Date object it is called on.

I have also found that if you call .getTimezoneOffset() on a date, it will return your local timezone, not the timezone of the date object you called it on (I can't verify this to be standard however).

In my browser, adding 60000 * Date.getTimezoneOffset() creates a DateTime that is not UTC. However when displayed within my browser (ex: .toString() ), it displays a DateTime in my local timezone that would be correct UTC time if timezone info is ignored.

Upvotes: 23

Sanket Patel
Sanket Patel

Reputation: 931

var userdate = new Date("2009-1-1T8:00:00Z");
var timezone = userdate.getTimezoneOffset();
var serverdate = new Date(userdate.setMinutes(userdate.getMinutes()+parseInt(timezone)));

This will give you the proper UTC Date and Time.
It's because the getTimezoneOffset() will give you the timezone difference in minutes. I recommend you that not to use toISOString() because the output will be in the string Hence in future you will not able to manipulate the date

Upvotes: 4

Adam Boostani
Adam Boostani

Reputation: 6247

If you are dealing with dates a lot, it's worth using moment.js (http://momentjs.com). The method to convert to UTC would be:

moment(yourTime).utc()

You can use format to change your date to any format you want:

moment(yourTime).utc().format("YYYY-MM-DD")

There is offset options in moment as well but there is an additional complementary library for dealing with timezone (http://momentjs.com/timezone/). The time conversion would be as simple as this:

moment.tz(yourUTCTime, "America/New_York")

Upvotes: 7

Reeth
Reeth

Reputation: 72

Looking at your question its clear that you just want to send the date range to your backend for further post processing.

I am assuming you are conforming to the standard data guidelines which expect the data to be in a particular format. For example, I use ODATA which is a RESTfull API which expects date time objects to be in the format:-

YYYY-MM-DDT00:00:00.

That can be easily achieved via the snippet posted below(Please change the format as per your requirement).

var mydate;//assuming this is my date object which I want to expose var UTCDateStr = mydate.getUTCFullYear() + "-" + mydate.getUTCMonth() + "-" + mydate.getUTCDate() + "T00:00:00";

If on the other hand, you are in my situation wherein you have received a date from your backend, and the browser converts that to your local date. You on the other hand are interested in the UTC date then you can perform the following:-

var mydate;//assuming this is my date object which I want to expose var UTCDate = new Date(mydate);/*create a copy of your date object. Only needed if you for some reason need the original local date*/ UTCDate.setTime(UTCDate.getTime() + UTCDate.getTimezoneOffset() * 60 * 1000);

The code snippet above basically adds/subtracts the time added/subtracted by the browser based on the timezone.

For example if I am in EST(GMT-5) and my Service returns a date time object = Wed Aug 17 2016 00:00:00 GMT-0500 my browser automatically subtracts the timezone offset(5hrs) to get my local time. So if I try to fetch the time I get Wed Aug 16 2016 19:00:00 GMT-0500. This causes a lot of problems. There are a lot of libraries out there which will definitely make this easier but I wanted to share the pure JS approach.

For more info please have a look at: http://praveenlobo.com/blog/how-to-convert-javascript-local-date-to-utc-and-utc-to-local-date/ where in I got my inspiration.

Hope this helps!

Upvotes: 2

weagle08
weagle08

Reputation: 1964

I know this question is old, but was looking at this same issue, and one option would be to send date.valueOf() to the server instead. the valueOf() function of the javascript Date sends the number of milliseconds since midnight January 1, 1970 UTC.

valueOf()

Upvotes: 0

Timothy Gonzalez
Timothy Gonzalez

Reputation: 1908

My recommendation when working with dates is to parse the date into individual fields from user input. You can use it as a full string, but you are playing with fire.

JavaScript can treat two equal dates in different formats differently.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse

Never do anything like:

new Date('date as text');

Once you have your date parsed into its individual fields from user input, create a date object. Once the date object is created convert it to UTC by adding the time zone offset. I can't stress how important it is to use the offset from the date object due to DST (that's another discussion however to show why).

var year = getFullYear('date as text');
var month = getMonth('date as text');
var dayOfMonth = getDate('date as text');

var date = new Date(year, month, dayOfMonth);

var offsetInMs = ((date.getTimezoneOffset() * 60)  // Seconds
                 * 1000);                          //  Milliseconds

var utcDate = new Date(date.getTime + offsetInMs);

Now you can pass the date to the server in UTC time. Again I would highly recommend against using any date strings. Either pass it to the server broken down to the lowest granularity you need e.g. year, month, day, minute or as a value like milliseconds from the unix epoch.

Upvotes: 8

RollerCosta
RollerCosta

Reputation: 5186

Convert to ISO without changing date/time

var now = new Date(); // Fri Feb 20 2015 19:29:31 GMT+0530 (India Standard Time) 
var isoDate = new Date(now.getTime() - now.getTimezoneOffset() * 60000).toISOString();
//OUTPUT : 2015-02-20T19:29:31.238Z

Convert to ISO with change in date/time(date/time will be changed)

isoDate = new Date(now).toISOString();
//OUTPUT : 2015-02-20T13:59:31.238Z 

Fiddle link

Upvotes: 42

Will Strohl
Will Strohl

Reputation: 1680

This function works beautifully for me.

function ParseDateForSave(dateValue) {
    // create a new date object
    var newDate = new Date(parseInt(dateValue.substr(6)));

    // return the UTC version of the date
    return newDate.toISOString();
}

Upvotes: 3

someone
someone

Reputation: 1

Even simpler

myvar.setTime(myvar.getTime() + myvar.getTimezoneOffset() * 60000);

Upvotes: -8

Related Questions