Incorrect Date/Time Formatting Nan/Nan/Nan Nan:Nan:Nan

Asked

Viewed 1,946 times

6

The Field DateTime that returns from Database is formatted as dd/MM/yyyy hh:mm:ss, by now when you start looping ($each) the code below the format changes to: "/Date(1566322265000)/".

I created this function to format the Date/Time in dd/MM/yyyy hh:mm:ss, but it returns me wrong, like this: Nan/Nan/Nan Nan:Nan:Nan

Example: Note: I only put the relevant code.

var data = "/Date(1566322265000)/";

$.each(response.listarLoginLogout, function (index, item) {

var _Data = FormatarDataddMMyyyyhrmmss(item.DATA);
console.log("Resultado: " + _Data);



});

function FormatarDataddMMyyyyhrmmss(data) {
    var dataFormatada = "";
    if (data != null) {
        var _dtAux = /\/Date\((\d+).*\)\//.exec(data);
        var _dtIni = new Date(parseInt(_dtAux[1]));
        var dtMilissegundos = new Date(_dtIni.toLocaleString());
        var dd = dtMilissegundos.getDate();
        var MM = dtMilissegundos.getMonth() + 1;
        var yyyy = dtMilissegundos.getFullYear();
        var hh = dtMilissegundos.getHours();
        var mm = dtMilissegundos.getMinutes();
        var ss = dtMilissegundos.getSeconds();
        if (dd < 10) { dd = '0' + dd; };
        if (MM < 10) { MM = '0' + MM; };
        if (hh < 10) { hh = '0' + hh; };
        if (mm < 10) { mm = '0' + mm; };
        if (ss < 10) { ss = '0' + ss; };
        dataFormatada = dd + "/" + MM + "/" + yyyy + " " + hh + ":" + mm + ":" + ss;
    }
    return dataFormatada;
};
  • Things like new Date("xxxx").getMonth() produce NaN as a result. Therefore, what is in your variable response.listarLoginLogout, especially in the field DATA thereof?

  • Use /\/Date\((\d+).*\)\//.exec(data) does not seem a good idea from a security point of view.

  • 1

    Nan/Nan/Nan Nan:Nan:Nan batmaaaan

  • 1

    @Andersoncarloswoss Whenever I see a lot of Nan I remember this: https://www.destroyallsoftware.com/talks/wat

  • Good morning! Thanks @Victor Stafusa for the comments I solved the problem as suggested below and I will mark as answered post, however answering your question within the field DATA has the following string: "/Date(1566322265000)/".

4 answers

4

The other answers have already provided solutions to get the date format, but I think it is worth an explanation about what was the error of your code.

First you extract the value of timestamp using regex, and creates the date. So far so good:

var data = "/Date(1566322265000)/";
var _dtAux = /\/Date\((\d+).*\)\//.exec(data);
var _dtIni = new Date(parseInt(_dtAux[1]));

In this case, the timestamp 1566322265000 corresponds to "August 20, 2019, at 14:31:05, at Brasilia Time". That’s what _dtIni contains: a date corresponding to this specific instant.

But then you use toLocaleString(), which returns the date in a specific format (such as a string), and creates another date using this string:

var dtMilissegundos = new Date(_dtIni.toLocaleString());

This is the point that is wrong.

First that, when calling toLocaleString() no parameters, a format corresponding to the locale which is configured in the browser (which, if I’m not mistaken, depends on the browser’s language settings).

So the first point is that the string that will be generated by this method may not be the same for all customers, since some locales use "day/month/year", others (such as English en-US) use "month/day/year", others use "year/month/day", etc. And as already explained here, the only standardized format that the Date recognizes and works the same way in all browsers is the ISO 8601 ("year-month-dayThour:minute:second" - note the letter T between date and time), and any format other than this may have a different behavior, which varies according to the browser.

For example, in my browser (Chrome, configured with locale pt-BR - brazilian Portuguese), toLocaleString() returns the string "20/08/2019 14:31:05". And in doing new Date("20/08/2019 14:31:05"), the result is Invalid Date (that the snippet below can show how null). This is because when this format is used, the Chrome implementation assumes that the format is "month/day/year hour:minute:second":

// o Chrome entende que esse formato é mês/dia/ano hora:minuto:segundo
console.log(new Date("11/08/2019 14:31:05")); // 8 de novembro
console.log(new Date("20/08/2019 14:31:05")); // data inválida, pois o mês é 20

And as in the second case, the month is 20, the date is invalid. And when trying to call any method of this invalid date (such as getDate(), getMonth(), etc), the result is NaN.

Probably your browser is configured with a locale which produces a different format than ISO 8601, and which also causes a similar error.


That said, you could have stopped at the creation of _dtIni. It is already a date corresponding to the timestamp you got with regex (that is, it is already what you need), there is no reason to create another date derived from this. Then just use _dtIni.getDate(), _dtIni.getMonth(), etc, to get the right values.

As an alternative to other responses, for this specific format you can use toLocaleString('pt-BR'):

let data = "/Date(1566322265000)/";
let m = data.match(/\/Date\((\d+)\)\//);
if (m) {
    let d = new Date(parseInt(m[1]));
    console.log(d.toLocaleString('pt-BR')); // 20/08/2019 14:31:05
}

Note also that I removed the .* of the regex: the \d+ already takes all the numbers, and then there’s the \) for closing the parentheses, then .* will not pick up any character, and makes no difference in this case (it would only do if the original string had more things between the number and closing parentheses).

One detail is that the methods getMonth(), getDate(), etc, and the very toLocaleString() return the numerical values of the date taking into account the browser’s time zone (which in turn, usually uses what is configured in the operating system). This can be changed by passing Timezone as parameter:

let data = "/Date(1566322265000)/";
let m = data.match(/\/Date\((\d+)\)\//);
if (m) {
    let d = new Date(parseInt(m[1]));
    console.log(d.toLocaleString('pt-BR', { timeZone: 'UTC' })); // 20/08/2019 17:31:05
    console.log(d.toLocaleString('pt-BR', { timeZone: 'America/Sao_Paulo' })); // 20/08/2019 14:31:05
    console.log(d.toLocaleString('pt-BR', { timeZone: 'Asia/Tokyo' })); // 21/08/2019 02:31:05
}

Note that both the day and time can change according to the chosen time zone.

Already using the getters, cannot convert to any Timezone. It is only possible to get the corresponding values in UTC using the methods getUTCMonth(), getUTCDate(), etc..


Another detail is that the documentation of toLocaleString says the following:

Most of the time, the formatting returned by toLocaleString() is consistent. However, this Might change in the Future and isn’t Guaranteed for all Languages - output Variations are by design and allowed by the Specification. Most notably, the IE and Edge browsers Insert bidirectional control characters Around Dates, so the output text will flow properly when Concatenated with other text.

In free translation (emphasis mine):

Most of the time, the format returned is toLocaleString() is consistent. However, it can change in the future and is not guaranteed for all languages - variations in the result are allowed by the specification. The most notable are the IE and Edge browsers, which insert bidirectional control characters around dates, so the text can follow the correct flow when concatenated with another text.

In this answer there’s also a quote about:

... browsers are allowed a large amount of leeway with what formats they support and what characters Compose the format.
... you cannot expect to be Able to compare the Results of toLocaleString Across browsers or Even expect the same browser to continue Giving the same result from release to release.

In free translation:

... browsers are allowed a wide range of formats supported and which characters can compose the format.
... you cannot expect the comparison of the results of toLocaleString between different browsers work, or that the same browser keeps giving the same results from one version to another.

That is, there is no guarantee that toLocaleString() always return the same format 100% of the time (although many locales are apparently stable in this sense). So if you always want the same format, regardless of the settings of the locale or browser, prefer the reply from Luiz Felipe.

  • Where did you read this: Mas suponha que um cliente está acessando seu sistema em um computador em outro país, que não necessariamente terá suporte ao idioma português. because that’s not what the documentation says?

  • @Virgilionovic I already did tests on a machine that only had a locale installed and the browser did not recognize the others (when using pt-BR, for example, it ignored and used the default machine) and only after installing the locales started to work. So it is not guaranteed that all machines will always have all locales, and so using a locale-dependent format does not guarantee that the format will always be the same

  • I saw in the documentation that this was solved, because it was formerly implementation that I solved and now it’s the API that plays the role. If it were so as you say the miscellaneous format would not work I have tested others and it works as it should ... Well I’m here informing is nothing against and yes a finding despite that Javascript is crazy language.

  • 1

    Another factor that the numerical value generated it also has a local factor ... of time mainly. It’s not guaranteed then if it goes away or doing the way you said about Felipe’s answer. It’s only guaranteed if it’s all on the same site then.

  • 1

    @Virgilionovic Hmm, I think I need to study the specification further (is the machine I tested a rare exception?). Anyway, I removed this section for now. As for the time zone question, I added this in the reply, thank you! :-)

  • 1

    Actually I got curious and I’m really also looking for more documents is nothing against your answer is even a question.

  • 1

    @Virgilionovic Tranquil, your comment was helpful for me to review this issue of locales. A while ago I read the documentation and understood one thing, but hj after you commented I reread and understood another, so I think I’ll have to study more calmly. If that’s the case I’ll update the reply later, thanks again!

  • 1

    Good morning ! @hkotsubo your explanations have been enriching ! I see that it is an apmplo subject ! I liked it very much ! Thank you !

  • 1

    @hard123 Yeah, dates are more complicated than they seem, and language implementations can complicate even more :-)

Show 4 more comments

4

You can simplify your script. You can do it like this:

const test = '/Date(1566322265000)/';

function formatDate(dateString) {
  const [, ms] = dateString.match(/\((\d+)\)\/$/);
  const instance = new Date(parseInt(ms, 10));

  // Uma pequena função que formata a parte da data automaticamente, somando
  // `1` ao valor retornado, caso necessário.
  const get = (method, sum = false) =>
    (instance[method]() + (sum ? 1 : 0)).toString().padStart(2, '0');

  const DD = get('getDate');
  const MM = get('getMonth', true);
  const YY = get('getFullYear');
  const hh = get('getHours');
  const mm = get('getMinutes');
  const ss = get('getSeconds');

  return `${DD}/${MM}/${YY} ${hh}:${mm}:${ss}`;
}

console.log(formatDate(test));

To facilitate the padding of the date parts, I created an auxiliary function called get, that makes use of the method String.prototype.padStart.

  • Good morning ! @Felipe Light helped a lot to my knowledge. Thank you! It works too !

4


A very simple solution would be like this, creating an object Date with the command val:

var data = eval("/Date(1566322265000)/".replace('/','new ').replace('/',''));
//Somente a data
console.log(data.toLocaleDateString('pt-BR'));
//Somente a hora
console.log(data.toLocaleTimeString('pt-BR'));
//A data e a hora
console.log(data.toLocaleString('pt-BR'));

could even be created a function to facilitate this:

// example: '/Date(1566322265000)/'

function data_parse_br(value) {
  const data = eval(value.replace('/', 'new ').replace('/', ''));
  //return data.toLocaleDateString('pt-BR') + ' ' +  data.toLocaleTimeString('pt-BR')
  //ou
  return data.toLocaleString('pt-BR');
}

console.log(data_parse_br('/Date(1566322265000)/'));

Maybe one option I particularly like is with Moment.js as follows:

function date_parse_br(value, format = 'DD/MM/YYYY HH:mm:ss') {
  moment.locale('pt-br');
  return moment(value).format(format);
}

console.log(date_parse_br('/Date(1566322265000)/'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment-with-locales.min.js"></script>

by the practicality factor, the Moment.js makes the necessary conversions.

  • If I’m not mistaken, toLocaleString('pt-BR') already formats the date and time with a space separating them, so do not need to use toLocaleDateString concatenated with toLocaleTimeString

  • @hkotsubo this factor would not need to make the answer somewhat poor, I agree that an additional factor by putting a method that solves the date and time formatting at once is cool, but reporting that having the option of having the information separate is also a good deal. Thank you and have been added the two forms. I also think that it does not deserve a negative vote.

  • 1

    I didn’t give the negative

  • 1

    Good morning! @Vigilio Novic really liked the suggestion of using Moment.js, I’ve already implemented and the Reports are already in production (10 Reports) and everyone uses this Moment.js. Thanks !

4

You can do it this way too:

var data = "/Date(1566322265000)/";


console.log("Data: " + (new Date(parseInt(data.replace(/\D/g,'')))).toLocaleDateString('pt-BR'));

  • Thanks ! @Augusto Vasques is an option ! Thanks !!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.