How to format number as time in Moment? - javascript

I am using bs-datepicker for save time as number in mongodb. In mongodb, it has time field with value i.e, 38700000 and 41400000 which are 16.15 and 17.00 respectively.
Now, i want to format the above time in readable form. I am using moment 2.16. Here i try but unable to do jsfiddle
console.log(moment.unix(38700000).format('HH:mm')) //03:30, expect:16:15
console.log(moment.unix(41400000).format('HH:mm')) //09:30, expect:17:00
And, how to convert from HH:mm to number. e.g 18:71 convert into number

The data you have is in terms of milliseconds since 1970-01-01 00:00:00.000 UTC.
38700000 == 1970-01-01T10:45:00Z
41400000 == 1970-01-01T11:30:00Z
If you convert those to the time zone offset you mentioned in comments (UTC+05:30) then the time portion of the values line up to what you expected.
38700000 == 1970-01-01T10:45:00Z == 1970-01-01T16:15:00+05:30
41400000 == 1970-01-01T11:30:00Z == 1970-01-01T17:00:00+05:30
Your values don't align at all, because you are using moment's unix function, which expects time in seconds, not milliseconds (because Unix Time is in whole seconds unless otherwise specified).
Also, by relying on the local time zone, you may get different values than expected if the user is in a different time zone.
The correct way to get what you asked for is:
moment(38700000).utcOffset("+05:30").format("HH:mm") // "16:15"
moment(41400000).utcOffset("+05:30").format("HH:mm") // "17:00"
Do recognize though that this means your original data is not stored with reference to UTC, but with reference to UTC+05:30. It would be better if you had stored UTC-based values instead, so you would do:
moment.utc(58500000).format("HH:mm") // "16:15"
moment.utc(61200000).format("HH:mm") // "17:00"
Of course, the real problem here is that you're storing a time-of-day into a date-time field. Probably the best thing to do would to not use the Date type at all in your MongoDB data, but rather just store the string "16:15" or the equivalent number of total minutes as an integer (60 * 16 + 15 == 975).

Related

Moment.js to convert to correct timezone

I'm trying to convert my date to the correct time zone with moment.js. However, I always get the whole thing without a time specification.
Here is my program code:
console.log(von);
console.log(bis);
var nVon = moment.tz(von, "Europe/Berlin");
var nBis = moment.tz(bis, "Europe/Berlin");
console.log(nVon.format());
console.log(nBis.format());
This is what I see in the console:
2022-10-31T00:00:00+01:00
And here the original German format, which I want to save in the correct time zone in MongoDb.:
The problem is that it is saved in MongoDB with an hour loss of time like this, without UTC etc.: 2022-10-31T19:44:39.000+00:00
Date values in MongoDB are stored as UTC times - always and only!
If you need to preserve the client input time zone, then you must store it in a separate field. Usually the client takes responsibility to display the MongoDB UTC times as local times.
NB, you should never store date values as string, it's a design flaw. Store always proper Date objects. Thus store
moment.tz(von, "Europe/Berlin").toDate()

Unknown date format /Date(1427982649000-0400)/

I need to display on the screen some date values but I'm receiving them in a format that I don't know. Does anybody know what format is this and how to convert them?
For example, I'm getting this:
/Date(1427982649000-0400)/
In the database is stored as
2015-04-02 09:50:49.000
I really don't know where to start looking at.
It's a unix timestamp in milliseconds, followed by a timezone (shift in hours differing from UTC).
So, it's UTC -4 hours, 1427982649 seconds after the 1st January of 1970.
Nice little tool for checking unix timestamps : http://www.unixtimestamp.com/index.php (don't forget to convert your milliseconds to seconds before posting them there)
/edit: To add some additional information - the "timezone shift" seems to be following RFC822 (and/or probably some other RFCs), that -0400 can be explained by the syntax "+/-HHMM" specified there, so to be exact it means -04 hours, 00 minutes.
The actual time and date gets converted into the milliseconds, and it follows the Unix time January 1st, 1970.
Because it is the date when the time for the Unix computer started.
But you can convert the milliseconds into the actual time by using some loops or conversions according to that time.
Does anybody know what format is this and how to convert them?
It seems that "/Date(1427982649000-0400)/" is a time value in milliseconds followed by an offset as ±HHmm. To convert that to a Date, use the time value adjusted by the offset.
Assuming the offset uses the typical sign convention, then a positive offset needs to be subtracted and negative offset added to get the correct UTC value, then something like the following should suit:
var s = '/Date(1427982649000-0400)/';
// Get the number parts
var b = s.match(/\d+/g);
// Get the sign of the offset
var sign = /-/.test(s)? -1 : +1;
// Adjust the time value by the offset converted to milliseconds
// and use to create a Date
var ms = +b[0] + sign * (b[1].slice(0,2)*3.6e6 + b[1].slice(-2)*6e4);
console.log(new Date(ms).toISOString()); // 2015-04-02T17:50:49.000Z
In your example, "2015-04-02 09:50:49.000" does not have a timezone, so it represents a different moment in time for each timezone with a different offset. If that is the actual value stored in the database, then I guess the missing timezone is UTC-0800. It is much better to store the values using UTC and to include the offset, then the host timezone is irrelevant.
Things are complicated here because ECMAScript timezone offsets are the opposite sign to the normal convention, i.e. positive for west of Greenwich and negative for east. If that convention is applied, then "/Date(1427982649000-0400)/" converts to 2015-04-02T09:50:49.000Z, which may be what you're after.
If that is the case, just change the sign in the line:
var sign = /-/.test(s)? -1 : +1;
to
var sign = /-/.test(s)? +1 : -1;

Using Moment.js like PHP's date and strtotime

I'm a typically server side developer feeling a bit like a fish out of water trying to display time values on the front end. How can I get behavior like PHP's date() and strtotime() functions out of moment.js? I just want a unix timestamp to appear in H:i:s format, and vice versa.
So far I've tried the following, from existing example code and the documentation:
moment(timestamp).format(H:i:s);
moment().duration(timestamp).format(H:i:s);
moment.unix(timestamp).format(h:mm:ss);
moment(formatted,'H:i:s');
Not a SINGLE one of which has worked properly. This may get flagged as duplicate since there are plenty of moment.js questions out there, but I don't know whether it's been updates to the library itself or slightly different context, I have not found one existing solution that has worked for me.
Anybody have any suggestions for these two simple tasks?
EDIT:
I've distilled two different problems out of this. One is that functions the moment docs say should work are giving weird values:
moment(1437462000).format('h:mm:ss')
for instance, which should return 7:00:00 utc, returns 10:17:42. This can be fixed in this case by using moment.unix(1437462000).utc().format('h:mm:ss') instead, but this leads into the second problem - the .utc() function seems to get ignored when converting back from a date into a timestamp:
timestamp = moment(formatted,'DD/MM/YYYY H:m:s').utc().unix();
will still return a timezone corrected value (in my case this is incorrect by several hours since the formatted time in question has nothing to do with the client computer) regardless of whether the .utc() function is included or not.
A few things you should realize:
Unix timestamps should always in terms of UTC. They are never adjusted for time zone in numerical form. If they're adjusted for time zone, that's done during the interpretation of the number, not in its representation.
While traditionally a "Unix Timestamp" is in terms of seconds, many environments use milliseconds instead. PHP's date timestamps are based on seconds, while moment and JavaScript's Date object both use milliseconds by default. Using the moment.unix function will let you pass seconds, and is identical to just multiplying the timestamp by 1000.
Moment has two built-in modes, local and UTC. The default mode is local. It doesn't matter what input you provide, if you don't specify UTC, the moment is adjusted to local. To specify UTC, you use the utc function. There are two forms of the function:
moment.utc(input) // parsing form
moment(input).utc() // conversion form
Both forms take some input and result in a moment in UTC mode. The difference is in how the input is interpreted. In either case, if the input value is unambiguous, the result is the same. For strings, that means the input would contain either a Z (from ISO8601), or a UTC-based offset. All other forms are ambiguous. For example, if I pass "2015-11-08 01:23:45", I will get different results depending on whether I interpret that string as local time or as UTC.
For numbers, they are always interpreted as milliseconds in UTC. However, if you use moment(number) without then calling .utc() then the moment is left in local mode, so any output will display as local time.
When you call moment.unix(input), the input is a number of seconds, but the moment is left in local mode. So to display the UTC time, you would use moment.unix(input).utc().
If your pre-recorded timestamps from your other system are in numeric form, but have been adjusted away from UTC, then they are incorrect. You have bad data, and Moment can't help you unless you know specifically how they have deviated and you write code to counteract that.
Moment's formatters are case sensitive. M is months, m is minutes. H is hours on a 24-hour clock, h is hours on a 12-hour clock. Use two consecutive letters when you want to include zero-padding. Example, HH:mm:ss for 13:02:03 vs. h:m:s for 1:2:3.
Moment's X formatter does not care which mode the moment is in. It will always emit seconds in UTC. Likewise, the x formatter returns milliseconds in UTC, as does moment.valueOf().
Also, your last example:
moment.unix(1437462000).utc().format()
Returns "2015-07-21T07:00:00+00:00" - which I believe is the value you expected.
You also get the same original timestamp regardless of which of these you try:
moment.unix(1437462000).utc().format("X") // "1437462000"
moment.unix(1437462000).format("X") // "1437462000"
moment.unix(1437462000).utc().unix() // 1437462000
moment.unix(1437462000).unix() // 1437462000
For anyone who comes in and is still looking for direct PHP equivalents for date() and strtotime(), here are the ones I ended up using. Matching up to php basically means just completely ignoring any kind of local time information by making sure everything is in UTC. That task is a little different between the timestamp->date and date->timestamp cases, though, so you have to be careful.
date()
Converting a timestamp to formatted date without any client timezone correction
var formatted = moment.unix(timestamp).utc().format('h:mm:ss');
strtotime()
Converting a UTC formatted date back to a timestamp without correcting it to local time:
var new_timestamp = moment.utc(formatted_utc,'DD/MM/YYYY H:m:s').format('X')
//where 'DD/MM/YYYY H:m:s' is the formatted date's format, and
//'X' outputs a unix timestamp without milliseconds.
Notes:
Do not use moment() with parenthesis in the calls:
moment().utc(date,format) will return local time values, not your
input.
Moment.js does not like the use of 'i' for minutes in the formatting,
unlike php.

Why moment.utc(...).diff(moment.utc(...)) gets affected by source date offsets

Check the following code sample:
moment.utc("2014-10-19T09:27:57.9417128+00:00")
.diff(moment.utc("2014-10-19T09:27:57.9417128+02:00"))
I would expect 0 since I'm converting both dates to UTC, but this gives 7200000 as result.
In fact, I'm looking to get moment.fromNow or moment.from to work with UTC in order to get a X seconds/minutes/hours... ago without an invalid result because of Date/moment translating date-times based on the date's offset.
What am I doing wrong here?
I'm not sure why you would think the source offsets should be ignored. They are especially relevant for converting to UTC, because they actually represent the difference between UTC and the time represented.
In the first timestamp, the +00:00 means the time is already at UTC. In the second timestamp, the +02:00 means the time is two hours ahead of UTC. 2 * 60 * 60 * 1000 = 7200000.
In other words:
2014-10-19T09:27:57.9417128+00:00 == 2014-10-19T09:27:57.9417128Z
- 2014-10-19T09:27:57.9417128+02:00 == 2014-10-19T07:27:57.9417128Z
=======================================================================
02:00:00
There is no way the result should be zero, because any way you look at it, the two timestamps represent two different moments in time that are separated by two hours.
Since moment's fromNow function already works with the current UTC time, and you have a full ISO timestamp with an offset, you can just use it directly without any conversion.
moment("2014-10-19T09:27:57.9417128+02:00").fromNow()
You don't even need to convert to UTC first. You could do it like this:
moment.utc("2014-10-19T09:27:57.9417128+02:00").fromNow()
But these will both return the same thing because you have already supplied the offset. They would only differ if you didn't include an offset, in which case the first example would interpret the input string in local time and the second case would interpret the input string in UTC. Neither of which change the behavior of the fromNow function.

Unit testing handling of date inputs in JavaScript regardless of time zone

I have a form where a user can enter a date, i.e. <input type="date"> the value is submitted in yyyy-MM-dd format. When I create a Date object with the string it assumes the time zone is the one the user's browser is set to – this is the behavior I want.
I'm then using the date value to make queries against a REST API that expects ISO date/time strings. That's no problem as the toISOString function on the Date object handles everything correctly.
However, when I'm unit testing this code – setting my input to a yyyy-MM-dd string then asserting that the output is an expected ISO timestamp string the tests can only work in a particular time zone. Is there a way I can force the time zone in the test?
I've tried using Jasmine spies to do something like:
var fixedTime = moment().zone(60).toDate()
spyOn(window, 'Date').andCallFake(function() {
return fixedTime;
});
But given there are so many variants of the constructor and so many ways it gets called by moment.js this is pretty impractical and is getting me into infinite loops.
A JavaScript Date cannot be set to a particular time zone. It only knows about UTC and the computer's local time from the environment it is running on.
There are time zone libraries for javascript, but I don't think that will help you here.
First, understand that "ISO" refers to ISO8601, which is a specification that defines a collection of related formats, such as YYYY-MM-DDTHH:MM:SS.
It is a separate concept from UTC, which refers to Universal Coordinated Time. UTC is the timekeeping system that we all synchronize our clocks to, which uses GMT as its basis - that is, the time in effect at the prime meridian not adjusted for daylight saving time.
Put together, the Date.toISOString() method will return the UTC form of an ISO8601 formatted timestamp, such as 2013-09-20T01:23:45Z. The Z at the end indicates that the time is in UTC.
But a value such as 2013-09-20 is still ISO formatted - it's just that it only has precision to the whole day, which means that it can't carry any time zone information.
When you use <input type="date">, the resulting value is not a Date class. It's a string containing the ISO formatted YYYY-MM-DD. You should just pass this directly to your application.
Now if what you are looking for is the full date and time, at midnight in the local time zone, of the date selected, and adjusted to UTC, well that's a different story. It is certainly doable but you have to understand that it is not the same as just passing the calendar date.
The easiest way to do that would be with moment.js as follows:
var s = "2013-09-20"; // from your input's value property
var m = moment(s);
var result = m.toISOString(); // "2013-09-20T07:00:00.000Z"
The value is adjusted because my time zone offset is -07:00.
You can do it without moment, but you have to replace dashes with slashes or the original value will be interpreted as if it is already in UTC.
new Date(s.replace('-','/')).toISOString()

Categories

Resources