325

I have a Date object. How do I render the title portion of the following snippet?

<abbr title="2010-04-02T14:12:07">A couple days ago</abbr>

I have the "relative time in words" portion from another library.

I've tried the following:

function isoDate(msSinceEpoch) {

   var d = new Date(msSinceEpoch);
   return d.getUTCFullYear() + '-' + (d.getUTCMonth() + 1) + '-' + d.getUTCDate() + 'T' +
          d.getUTCHours() + ':' + d.getUTCMinutes() + ':' + d.getUTCSeconds();

}

But that gives me:

"2010-4-2T3:19"
Joshua Pinter
  • 45,245
  • 23
  • 243
  • 245
James A. Rosen
  • 64,193
  • 61
  • 179
  • 261

16 Answers16

581

There is already a function called toISOString():

var date = new Date();
date.toISOString(); //"2011-12-19T15:28:46.493Z"

If, somehow, you're on a browser that doesn't support it, I've got you covered:

if (!Date.prototype.toISOString) {
  (function() {

    function pad(number) {
      var r = String(number);
      if (r.length === 1) {
        r = '0' + r;
      }
      return r;
    }

    Date.prototype.toISOString = function() {
      return this.getUTCFullYear() +
        '-' + pad(this.getUTCMonth() + 1) +
        '-' + pad(this.getUTCDate()) +
        'T' + pad(this.getUTCHours()) +
        ':' + pad(this.getUTCMinutes()) +
        ':' + pad(this.getUTCSeconds()) +
        '.' + String((this.getUTCMilliseconds() / 1000).toFixed(3)).slice(2, 5) +
        'Z';
    };

  }());
}

console.log(new Date().toISOString())
mplungjan
  • 169,008
  • 28
  • 173
  • 236
Anatoly Mironov
  • 7,356
  • 1
  • 26
  • 27
68

Note: This answer is still getting upvotes as of 2022-03. The moment.js library is deprecated. These are the two main alternatives: Luxon and Day.js, others are mentioned in the deprecation link.

Luxon

Luxon can be thought of as the evolution of Moment. It is authored by Isaac Cambron, a long-time contributor to Moment. Please read Why does Luxon exist? and the For Moment users pages in the Luxon documentation.

Locales: Intl provided Time Zones: Intl provided

Day.js

Day.js is designed to be a minimalist replacement for Moment.js, using a similar API. It is not a drop-in replacement, but if you are used to using Moment's API and want to get moving quickly, consider using Day.js.

Locales: Custom data files that can be individually imported Time Zones: Intl provided, via a plugin

I use Day.js because of the size difference, but Luxon is easier to deal with.


Almost every to-ISO method on the web drops the timezone information by applying a convert to "Z"ulu time (UTC) before outputting the string. Browser's native .toISOString() also drops timezone information.

This discards valuable information, as the server, or recipient, can always convert a full ISO date to Zulu time or whichever timezone it requires, while still getting the timezone information of the sender.

The best solution I've come across is to use the Moment.js javascript library and use the following code:

To get the current ISO time with timezone information and milliseconds

now = moment().format("YYYY-MM-DDTHH:mm:ss.SSSZZ")
// "2013-03-08T20:11:11.234+0100"

now = moment().utc().format("YYYY-MM-DDTHH:mm:ss.SSSZZ")
// "2013-03-08T19:11:11.234+0000"

now = moment().utc().format("YYYY-MM-DDTHH:mm:ss") + "Z"
// "2013-03-08T19:11:11Z" <- better use the native .toISOString() 

To get the ISO time of a native JavaScript Date object with timezone information but without milliseconds

var current_time = Date.now();
moment(current_time).format("YYYY-MM-DDTHH:mm:ssZZ")

This can be combined with Date.js to get functions like Date.today() whose result can then be passed to moment.

A date string formatted like this is JSON compilant, and lends itself well to get stored into a database. Python and C# seem to like it.

Daniel F
  • 13,684
  • 11
  • 87
  • 116
  • 24
    dont stuff around with dates people. Just use moment.js and save your hair. – Valamas Mar 14 '13 at 22:36
  • 1
    actually, on python and db's it turned out to be a pain. db's use UTC (no prob, as you can easily convert to UTC server-side), so if you want to keep the offset info you need another field. And Python prefers the use of nanoseconds instead of javascript's milliseconds, which are usually enough and preferrable over plain seconds. On python, only *dateutil.parser.parse* parses it correctly, and to write millisecond ISO's one requires a "_when = when.strftime("%Y-%m-%dT%H:%M:%S.%f%z"); return _when[:-8] + _when[-5:]" to convert the nanos to millis. that's not nice. – Daniel F Jun 04 '13 at 09:58
  • 3
    You can actually just omit the format like so: `moment(new Date()).format()`. "As of version 1.5.0, calling moment#format without a format will default to ... the ISO8601 format `YYYY-MM-DDTHH:mm:ssZ`". Doc: Scroll up from http://momentjs.com/docs/#/displaying/fromnow/ – user193130 Feb 20 '14 at 16:50
  • 1
    Good point @user193130 but you really need to be carefull though because the output differs from native method. `moment().format()` `"2015-03-04T17:16:05+03:00"` `(new Date()).toISOString()` `"2015-03-04T14:16:24.555Z"` – Olga Mar 04 '15 at 14:18
  • 1
    Maybe being picky but these examples return the current offset from UTC, not the time zone. A time zone is a geographical region commonly expressed as e.g. "America/Toronto". Many time zones change their UTC offset twice a year and the time zone cannot (always) be determined from the current UTC offset... thus this answer is also dropping the time zone information :-) – Martin Fido Aug 02 '16 at 03:34
  • These examples are meant to illustrate how to output ISO 8601, which, other than having the ability to store the UTC-offset, serve no other purpose than to contain timing information. If you need to do timezone conversions, you can take a look at http://momentjs.com/timezone/ – Daniel F Aug 03 '16 at 07:58
  • 1
    Moment is a fairly big plugin to include though, at 50 KB minified, to just have some simple date processing that you could do with a couple of lines. – Juha Untinen Jul 24 '18 at 10:14
  • @JuhaUntinen Correct, but if you use a CDN it may already be cached in the browser. Minified without locale is 16.4k (= only english strings), with locales 66.4k – Daniel F Jul 24 '18 at 10:18
67

See the last example on page https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference:Global_Objects:Date:

/* Use a function for the exact format desired... */
function ISODateString(d) {
    function pad(n) {return n<10 ? '0'+n : n}
    return d.getUTCFullYear()+'-'
         + pad(d.getUTCMonth()+1)+'-'
         + pad(d.getUTCDate())+'T'
         + pad(d.getUTCHours())+':'
         + pad(d.getUTCMinutes())+':'
         + pad(d.getUTCSeconds())+'Z'
}

var d = new Date();
console.log(ISODateString(d)); // Prints something like 2009-09-28T19:03:12Z
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
dev-null-dweller
  • 29,274
  • 3
  • 65
  • 85
  • 2
    The sample provided by the OP (``) has no timezone. Perhaps they wanted the local time, which would make sense for a UI time string? – Dan Dascalescu Sep 14 '16 at 02:08
46

The question asked was ISO format with reduced precision. Voila:

 new Date().toISOString().slice(0, 19) + 'Z'
 // '2014-10-23T13:18:06Z'

Assuming the trailing Z is wanted, otherwise just omit.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
arcseldon
  • 35,523
  • 17
  • 121
  • 125
18

Shortest, but not supported by Internet Explorer 8 and earlier:

new Date().toJSON()
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
younes0
  • 2,288
  • 1
  • 25
  • 34
15

If you don't need to support IE7, the following is a great, concise hack:

console.log(
  JSON.parse(JSON.stringify(new Date()))
)
mplungjan
  • 169,008
  • 28
  • 173
  • 236
Russell Davis
  • 8,319
  • 4
  • 40
  • 41
  • for IE7 this decision is fit too if was included json3-library ( http://bestiejs.github.io/json3 ). Thanks :) – vladimir Oct 01 '13 at 22:39
  • Also fails in IE8. ("'JSON' is undefined") – Cees Timmerman Oct 03 '13 at 13:55
  • 1
    Round-tripping through JSON is ugly, especially if your stated goal is conciseness; use the date's `toJSON` method instead. `JSON.stringify` is using it under the covers anyway. – Mark Amery Mar 02 '15 at 17:26
  • @CeesTimmerman IE8 supports the `JSON` object, though not in some compatibility modes. See http://stackoverflow.com/questions/4715373/json-object-undefined-in-internet-explorer-8-dom – Mark Amery Mar 02 '15 at 17:28
  • 3
    In what way is this better than `.toISOString()` ? – xyhhx Mar 23 '19 at 20:22
10

I typically don't want to display a UTC date since customers don't like doing the conversion in their head. To display a local ISO date, I use the function:

function toLocalIsoString(date, includeSeconds) {
    function pad(n) { return n < 10 ? '0' + n : n }
    var localIsoString = date.getFullYear() + '-'
        + pad(date.getMonth() + 1) + '-'
        + pad(date.getDate()) + 'T'
        + pad(date.getHours()) + ':'
        + pad(date.getMinutes()) + ':'
        + pad(date.getSeconds());
    if(date.getTimezoneOffset() == 0) localIsoString += 'Z';
    return localIsoString;
};

The function above omits time zone offset information (except if local time happens to be UTC), so I use the function below to show the local offset in a single location. You can also append its output to results from the above function if you wish to show the offset in each and every time:

function getOffsetFromUTC() {
    var offset = new Date().getTimezoneOffset();
    return ((offset < 0 ? '+' : '-')
        + pad(Math.abs(offset / 60), 2)
        + ':'
        + pad(Math.abs(offset % 60), 2))
};

toLocalIsoString uses pad. If needed, it works like nearly any pad function, but for the sake of completeness this is what I use:

// Pad a number to length using padChar
function pad(number, length, padChar) {
    if (typeof length === 'undefined') length = 2;
    if (typeof padChar === 'undefined') padChar = '0';
    var str = "" + number;
    while (str.length < length) {
        str = padChar + str;
    }
    return str;
}
Wayne Maurer
  • 12,333
  • 4
  • 33
  • 43
Charles Burns
  • 10,310
  • 7
  • 64
  • 81
6

The problem with toISOString is that it gives datetime only as "Z".

ISO-8601 also defines datetime with timezone difference in hours and minutes, in the forms like 2016-07-16T19:20:30+5:30 (when timezone is ahead UTC) and 2016-07-16T19:20:30-01:00 (when timezone is behind UTC).

I don't think it is a good idea to use another plugin, moment.js for such a small task, especially when you can get it with a few lines of code.

Once you have the timezone offset in hours and minutes, you can append to a datetime string.

I wrote a blog post on it : http://usefulangle.com/post/30/javascript-get-date-time-with-offset-hours-minutes

var timezone_offset_min = new Date().getTimezoneOffset(),
  offset_hrs = parseInt(Math.abs(timezone_offset_min / 60)),
  offset_min = Math.abs(timezone_offset_min % 60),
  timezone_standard;

if (offset_hrs < 10)
  offset_hrs = '0' + offset_hrs;

if (offset_min > 10)
  offset_min = '0' + offset_min;

// getTimezoneOffset returns an offset which is positive if the local timezone is behind UTC and vice-versa.
// So add an opposite sign to the offset
// If offset is 0, it means timezone is UTC
if (timezone_offset_min < 0)
  timezone_standard = '+' + offset_hrs + ':' + offset_min;
else if (timezone_offset_min > 0)
  timezone_standard = '-' + offset_hrs + ':' + offset_min;
else if (timezone_offset_min == 0)
  timezone_standard = 'Z';

// Timezone difference in hours and minutes
// String such as +5:30 or -6:00 or Z
console.log(timezone_standard);
mplungjan
  • 169,008
  • 28
  • 173
  • 236
Useful Angle
  • 928
  • 9
  • 16
  • Typo: if (offset_min > 10) should be <.. – Gerard ONeill Jan 12 '22 at 03:44
  • Agreed with your post the most - having said that, 'Z' implied a purposeful "zulu" conversion, while 00:00 implies that it is what it is (the local is somewhere in the GMT zone). If this were a function I'd pass a 'convertToZulu' parameter to convert the date and attach z; otherwise no conversion and 00:00 instead of Z. – Gerard ONeill Jan 12 '22 at 03:50
3

There is a '+' missing after the 'T'

isoDate: function(msSinceEpoch) {
  var d = new Date(msSinceEpoch);
  return d.getUTCFullYear() + '-' + (d.getUTCMonth() + 1) + '-' + d.getUTCDate() + 'T'
         + d.getUTCHours() + ':' + d.getUTCMinutes() + ':' + d.getUTCSeconds();
}

should do it.

For the leading zeros you could use this from here:

function PadDigits(n, totalDigits) 
{ 
    n = n.toString(); 
    var pd = ''; 
    if (totalDigits > n.length) 
    { 
        for (i=0; i < (totalDigits-n.length); i++) 
        { 
            pd += '0'; 
        } 
    } 
    return pd + n.toString(); 
} 

Using it like this:

PadDigits(d.getUTCHours(),2)
kaiz.net
  • 1,984
  • 3
  • 23
  • 31
  • Great catch! It doesn't address the missing "0"s, though. – James A. Rosen Apr 04 '10 at 04:20
  • 1
    Write a function to convert an integer to a 2-character string (prepending a '0' if the argument is less than 10), and call it for each part of the date/time. – dan04 Apr 04 '10 at 04:46
3
function timeStr(d) { 
  return ''+
    d.getFullYear()+
    ('0'+(d.getMonth()+1)).slice(-2)+
    ('0'+d.getDate()).slice(-2)+
    ('0'+d.getHours()).slice(-2)+
    ('0'+d.getMinutes()).slice(-2)+
    ('0'+d.getSeconds()).slice(-2);
}
Sean
  • 31
  • 1
2

I was able to get below output with very less code.

var ps = new Date('2010-04-02T14:12:07')  ;
ps = ps.toDateString() + " " + ps.getHours() + ":"+ ps.getMinutes() + " hrs";

Output:

Fri Apr 02 2010 19:42 hrs
2

I think I have found an even better solution:

According to the wiki page Canada uses ISO 8601 as the official date format, therefore we can safely use this.

console.log(new Date("2022-12-19 00:43:00 GMT+0100").toISOString().split("T")[0]);
// results in '2022-12-18'

console.log(new Date("2022-12-19 00:43:00 GMT+0100").toLocaleDateString("en-CA"));
// results in '2022-12-19'
Attila Farago
  • 21
  • 1
  • 1
0

I would just use this small extension to Date - http://blog.stevenlevithan.com/archives/date-time-format

var date = new Date(msSinceEpoch);
date.format("isoDateTime"); // 2007-06-09T17:46:21
Anurag
  • 140,337
  • 36
  • 221
  • 257
0
function getdatetime() {
    d = new Date();
    return (1e3-~d.getUTCMonth()*10+d.toUTCString()+1e3+d/1)
        .replace(/1(..)..*?(\d+)\D+(\d+).(\S+).*(...)/,'$3-$1-$2T$4.$5Z')
        .replace(/-(\d)T/,'-0$1T');
}

I found the basics on Stack Overflow somewhere (I believe it was part of some other Stack Exchange code golfing), and I improved it so it works on Internet Explorer 10 or earlier as well. It's ugly, but it gets the job done.

Jonas Byström
  • 25,316
  • 23
  • 100
  • 147
0

To extend Sean's great and concise answer with some sugar and modern syntax:

// date.js

const getMonthName = (num) => {
  const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Oct', 'Nov', 'Dec'];
  return months[num];
};

const formatDate = (d) => {
  const date = new Date(d);
  const year = date.getFullYear();
  const month = getMonthName(date.getMonth());
  const day = ('0' + date.getDate()).slice(-2);
  const hour = ('0' + date.getHours()).slice(-2);
  const minutes = ('0' + date.getMinutes()).slice(-2);

  return `${year} ${month} ${day}, ${hour}:${minutes}`;
};

module.exports = formatDate;

Then eg.

import formatDate = require('./date');

const myDate = "2018-07-24T13:44:46.493Z"; // Actual value from wherever, eg. MongoDB date
console.log(formatDate(myDate)); // 2018 Jul 24, 13:44
Juha Untinen
  • 1,806
  • 1
  • 24
  • 40
0

A short one:

console.log(new Date().toISOString().slice(0,19).replace('T', ' '))
maxkuku
  • 188
  • 4
  • 7