Add timestamp offset to local timestamp in javascript - javascript

Suppose I am in Sri Lanka (offset +5.30). I want to schedule a Meeting at 8.00 AM in American local time(offset -10.00) while I am staying in Sri Lanka.
I want to create my timestamp by adding offset of America to my local timestamp.
Have any one has idea of how to do that in javascript without using moment timezone.
What I have done is,
var localTimestamp = new Date('2015-02-27 14:59').getTime();
var offset = parseInt('-10.00')*60*60;
var timestamp = (localTimestamp/1000) + offset;
Above gives wrong result after converting back to local time.

There are a couple of issues there.
You're relying on undocumented behavior here:
var localTimestamp = new Date('2015-02-27 14:59').getTime();
That string format is not defined in the specification. V8 will parse it (as of this writing), but you have no guarantees about whether it parses it in local time or UTC or what, because (again) it's undefined. To create a date/time in a defined way, you could use the date/time format in the spec, but sadly they got that format wrong in ES5 and are having to fix it in ES6: In ES5, the absense of the "Z" at the end was defined as meaning UTC, but that's at odds with the ISO-8601 standard it was based on, and means you don't have a way to say "local time." Since ES6 will fix this, some engines have already changed it; whether your version of V8 has depends on the version number. So you're probably better off using the multi-argument date constructor:
var localTimestamp = new Date(2015, 1, 27, 14, 59).getTime();
// Remember that months start at 0 -^
There's zero reason for parseInt('-10.00'); just use -10.
Here you're dividing by 1000:
var timestamp = (localTimestamp/1000) + offset;
But then you say
Above gives wrong result after converting back to local time.
You need to multiply again when going back:
var newDate = new Date(timestamp * 1000);
Then you need to be careful about how you use the resulting Date, because it still works in what it considers local time. But if you're converting it to a string, etc., you could use it.

Related

Working with different timezones in Javascript

I am working on a cloud based application which deals extensively with date and time values, for users across the world.
Consider a scenario, in JavaScript, where my machine is in India (GMT+05:30), and I have to display a clock running in California's timezone (GMT-08:00).
In this case I have to get a new date object,
let india_date = new Date()
add it's time zone offset value,
let uts_ms = india_date.getTime() + india_date.getTimezoneOffset()
add california's timezone offset value,
let california_ms = utc_ms + getCaliforniaTimezoneOffsetMS()
and finally the date object.
let california_date: Date = new Date(california_ms)
Is there any way to directly deal with these kinds of time zones without having to convert the values again and again?
First, let's talk about the code in your question.
let india_date = new Date()
You have named this variable india_date, but the Date object will only reflect India if the code is run on a computer set to India's time zone. If it is run on a computer with a different time zone, it will reflect that time zone instead. Keep in mind that internally, the Date object only tracks a UTC based timestamp. The local time zone is applied when functions and properties that need local time are called - not when the Date object is created.
add it's timezone offset value
let uts_ms = india_date.getTime() + india_date.getTimezoneOffset()
This approach is incorrect. getTime() already returns a UTC based timestamp. You don't need to add your local offset. (also, the abbreviation is UTC, not UTS.)
Now add california's timezone offset value
let california_ms = utc_ms + getCaliforniaTimezoneOffsetMS()
Again, adding an offset is incorrect. Also, unlike India, California observes daylight saving time, so part of the year the offset will be 480 (UTC-8), and part of the year the offset will be 420 (UTC-7). Any function such as your getCaliforniatimezoneOffsetMS would need to have the timestamp passed in as a parameter to be effective.
and finally the date object
let california_date: Date = new Date(california_ms)
When the Date constructor is passed a numeric timestamp, it must be in terms of UTC. Passing it this california_ms timestamp is actually just picking a different point in time. You can't change the Date object's behavior to get it to use a different time zone just by adding or subtracting an offset. It will still use the local time zone of where it runs, for any function that requires a local time, such as .toString() and others.
There is only one scenario where this sort of adjustment makes sense, which is a technique known as "epoch shifting". The timestamp is adjusted to shift the base epoch away from the normal 1970-01-01T00:00:00Z, thus allowing one to take advantage of the Date object's UTC functions (such as getUTCHours and others). The catch is: once shifted, you can't ever use any of the local time functions on that Date object, or pass it to anything else that expects the Date object to be a normal one. Epoch shifting done right is what powers libraries like Moment.js. Here is another example of epoch shifting done correctly.
But in your example, you are shifting (twice in error) and then using the Date object as if it were normal and not shifted. This can only lead to errors, evident by the time zone shown in toString output, and will arise mathematically near any DST transitions of the local time zone and the intended target time zone. In general, you don't want to take this approach.
Instead, read my answer on How to initialize a JavaScript Date to a particular time zone. Your options are listed there. Thanks.
JavaScript Date objects store date and time in UTC but the toString() method is automatically called when the date is represented as a text value which displays the date and time in the browser's local time zone. So, when you want to convert a datetime to a time zone other than your local time, you are really converting from UTC to that time zone (not from your local time zone to another time zone).
If your use case is limited to specific browsers and you are flexible on formatting (since browsers may differ in how they display date string formats), then you may be able to use toLocaleString(), but browsers like Edge, Android webview, etc do not fully support the locales and options parameters.
Following example sets both the locale and timezone to output the date in a local format that may vary from browser to browser.
const dt = new Date();
const kolkata = dt.toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' });
const la = dt.toLocaleString('en-US', { timeZone: 'America/Los_Angeles' });
console.log('Kolkata:', kolkata);
// example output: Kolkata: 19/3/2019, 7:36:26 pm
console.log('Los Angeles:', la);
// example output: Los Angeles: 3/19/2019, 7:06:26 AM
You could also use Moment.js and Moment Timezone to convert date and time to a time zone other than your local time zone. For example:
const dt = moment();
const kolkata = dt.tz('Asia/Kolkata').format();
const la = dt.tz('America/Los_Angeles').format();
console.log(kolkata);
// example output: 2019-03-19T19:37:11+05:30
console.log(la);
// example output: 2019-03-19T07:07:11-07:00
<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>
Well, you really do kind of have to convert anytime you want to change the display, but it's not as bad as you think.
First, store all time as UTC. Probably using the milliseconds format, e.g. Date.UTC().
Second, do all manipulation / comparison using that stored info.
Third, if your cloud-based application has an API that API should only talk in terms of UTC as well, though you could provide the ISO string if you prefer that to the MS, or if you expect clients to handle that better.
Fourth and finally, only in the UI should you do the final conversion to the local date/time string, either with the method you're describing or using a library such as momentjs
new Date creates a Date object with a time value that is UTC. If you can guarantee support for the timeZone option of toLocaleString (e.g. corporate environment with a controlled SOE), you can use it to construct a timestamp in any time zone and any format, but it can be a bit tedious. Support on the general web may be lacking. A library would be preferred in that case if you need it to work reliably.
E.g. to get values for California, you can use toLocaleString and "America/Los_Angeles" for the timeZone option:
var d = new Date();
// Use the default implementation format
console.log(d.toLocaleString(undefined, {timeZone:'America/Los_Angeles'}));
// Customised format
var weekday = d.toLocaleString(undefined, {weekday:'long', timeZone:'America/Los_Angeles'});
var day = d.toLocaleString(undefined, {day:'numeric', timeZone:'America/Los_Angeles'});
var month = d.toLocaleString(undefined, {month:'long', timeZone:'America/Los_Angeles'});
var year = d.toLocaleString(undefined, {year:'numeric', timeZone:'America/Los_Angeles'});
var hour = d.toLocaleString(undefined, {hour:'numeric',hour12: false, timeZone:'America/Los_Angeles'});
var minute = d.toLocaleString(undefined, {minute:'2-digit', timeZone:'America/Los_Angeles'});
var ap = hour > 11? 'pm' : 'am';
hour = ('0' + (hour % 12 || 12)).slice(-2);
console.log(`The time in Los Angeles is ${hour}:${minute} ${ap} on ${weekday}, ${day} ${month}, ${year}`);
Getting the timezone name is a little more difficult, it's difficult to get it without other information.

new Date() returns relative time and not UTC time

When I go to my browser development tools and write new Date() in the console, it gives me the following
Mon Dec 18 2017 17:11:29 GMT+0200
I thought it suppose to return the UTC time.
The issue is that I have a server on AWS which writes UTC time to a DB. it writes it as a string and on the client side I do the following
const updatedMilliAgo = new Date() - new Date(timeStrFromDb);
For some reason the diff is two hours even due I check it right after the write in the server.
What am I doing wrong here?
When you use new Date(), you are constructing a Date object using the current value of the system clock where the code is executing. The Date object internally stores only a number of milliseconds since 1970-01-01T00:00:00Z (without consideration of leap seconds). In other words, Date objects are always representing the UTC time.
However - there are many functions and parameters on the Date object that work in local time. For example, when you call .toString() on a Date object, the computer's local time zone is applied to the internal UTC-based value, in order to generate a string that reflects local time.
In the case of console.log - a standard object like Date cannot be directly logged. Instead, most implementations will log a string value. How that value is created is entirely implementation specific, and not defined by the ECMAScript specification. Many implementations will return the same local-time based value that .toString() returns. Some (FireFox, for example) will return the same UTC based value that .toISOString() returns. It would be reasonable for an implementation to return the actual number of milliseconds stored (.valueOf()), or some other representation. If you need consistency, don't just log the Date object. Instead, log the output of one of its functions that returns a string or a number.
You also asked about subtracting two date objects. That will implicitly call .valueOf() on each object, subtracting their UTC-based internal values and giving you the number of milliseconds between them. The most likely problem you are encountering is with how you construct the second Date object. You didn't give an example of what timeStrFromDb consists of, but understand that how that string is formatted directly relates to how the Date object is constructed. If you aren't using a standardized format, or you aren't clear on whether the value is based on UTC or a specific offset from UTC, your string may be parsed differently than you expect.
Try to use
var d = new Date();
var n = d.toUTCString();
console.log(n)
I am late, but I didn't find the answer and I had to fight for hours vs the code... finally my solution was as simple as that:
TimeZone timeZone = TimeZone.getTimeZone(ZoneId.of("UTC"));
timeZone.setDefault(timeZone);
When you set the default timezone, all dates you create will be in that ZoneId that you specify.
I hope to help to someone.

Extracting timezone number using moment.js

My web app client receive JSON data from rest api server.
Data contains timezone string value like as "Etc/GMT-1".
For the time calculating, I need to extract timezone number (just like -1).
If I use parseInt(timezone);, return value is NaN.
I am not sure if moment.js can handle this format and extract GMT number.
I think timezone data format maybe flexible.
So, I prefer to use moment.js.
Is there any way to extract number using moment.js?
The string "Etc/GMT-1" is a valid IANA time zone identifier. You can find it in the tzdb sources, and in the list on Wikipedia. As pointed out in the tzdb commentary, the sign is opposite from what you might expect. It means UTC+1, not UTC-1.
You also said: "I think timezone data format may be flexible." Indeed, if your data contains any tzdb zone identifiers, you should assume that all time zone identifiers are also valid, including the more conventional form like "America/Los_Angeles" or "Asia/Shanghai". Therefore, you should not try to extract anything from the string itself.
It's important to remember that while some zones have a single fixed offset, most represent a series of offsets and the transition points between them. Therefore, you should not try to get a single offset from a time zone id without also considering a point in time. This is also covered in the timezone tag wiki, under the topic "Time Zone != Offset".
To use these with moment.js, you will also need the moment-timezone add-on. For example:
var timeZone = 'Etc/GMT-1'; // your time zone
var m = moment.tz(timeZone); // get the *current* time in the time zone.
// or
var m = moment.tz(input, timeZone); // get a *specific* time in the time zone.
// or
var m = someOtherMomentObject.clone().tz(timeZone); // convert to the time zone.
var n = m.utcOffset(); // 60 (minutes West of GMT at the given point in time)
var s = m.format('Z'); // "+01:00" (offset as a string in ISO-8601 format)
Etc/GMT-1 is a standard format. Apple has documentation for it: Documentation
Just FYI, As this Stackoverflow answear stated the Etc/GMT-1 should translate to GMT+1.
If your input is always gonna be Etc/GMT# you can just extract it by writing you own one-line function as such:
function extract(input_string){
return input_string.substring(input_string.indexOf("Etc/GMT")+7);
}
If you also install moment-timezone, you can get the zone's offset:
var offsetHours = moment.tz.zone("Etc/GMT-1").offsets[0] / 60
The value of offsetHours variable will be -1. Note that I had to divide by 60, because the API returns the value in minutes.
As Matt reminded in the comments, usually timezones have more than one valid offset, depending on the date - due to Daylight Saving Time changes or even politicians deciding to change their country's offset - so it's better to get the offset based on a specific instant, as explained in the documentation:
moment.tz.zone('Etc/GMT-1').offset(1403465838805);
The parameter 1403465838805 is the number of milliseconds since 1970-01-01T00:00Z.
Specifically for Etc/GMT timezones, there's no difference since they have no offset variations, but for other zones, it's better to use an instant.
Had the same problem so created an array with [ label, value ] for autocomplete. Enjoy.
// npm install -S moment-timezone
import moment from 'moment-timezone'
import timezones from '<path-to-node_modules>/node_modules/moment-timezone/data/unpacked/latest.json'
// Build Timezone Array for autocomplete
const TIMEZONES = timezones.zones.map( i => {
let abbrs = i.abbrs.filter((el, index) => i.abbrs.indexOf(el) === index)
return { label : `${i.name.replace('_',' ')} ( ${abbrs} )`, value: i.name}
})

Is it Possible in javascript to restrict date to specific Timezone [duplicate]

I know I can get the local timezone offset via new Date().getTimeZoneOffset(). But where did Javascript get that information? Is there a way I can set it, so that all future Date objects have the offset I want? I tried searching the DOM in Firebug, but couldn't find anything.
What I am trying to accomplish is converting epoch times to readable format, but it needs to be in US/Central, no matter what the browser's OS setting. Because I am using US/Central, it's not a fixed difference from GMT. So instead of a bunch of super nasty conversion steps, why can't I just tell Javascript that I'm actually in US/Central?
Currently, Moment-Timezone enables us to set the "browser's" default timezone by using moment.tz.setDefault().
You'll have to use moment() instead of Date(), but this is still a nice upgrade over the weird JS Date object.
I know I can get the local timezone offset via new Date().getTimeZoneOffset(). But where did Javascript get that information?
An implementation of ECMAScript is expected to determine the local time zone adjustment.
Is there a way I can set it, so that all future Date objects have the offset I want?
No.
So instead of a bunch of super nasty conversion steps, why can't I just tell Javascript that I'm actually in US/Central?
Have you considered using a library?
I realize this is an old post, but momentJS is a powerful javascript library to manipulate date/time objects
Output format
If you are concerned about the output format, you always need to format you Date object prior to outputting it if you need it in a local timezone (e.g. using Intl) or you a library like dayjs or moment.
Create a new Date object from a date with a non-UTC timezone
You can set an offset in pure JS: new Date('2022-10-29T12:50:00.000+02:00') will contain 2022-10-29T10:50:00.000Z. You just have to always specify the timezone offset in /^[+-][0-2]\d:[0-5]\d$/ format.
console.log(new Date('2022-10-29T12:50:00.000+02:00').toISOString())
// Output
// 2022-10-29T10:50:00.000Z
Get timezone offset string from timezone offset number
Now, if you want to get an offset in that format from (new Date()).getTimezoneOffset() (e.g. -120), you need to
const tzOffsetNumber = (new Date()).getTimezoneOffset()
const tzOffsetString = `${tzOffsetNumber > 0 ? '+' : '-'}${Math.floor(Math.abs(tzOffsetNumber) / 60).toString().padStart(2, '0')}:${(Math.abs(tzOffsetNumber) % 60).toString().padStart(2, '0')}`
Get timezone offset string from IANA timezone
// Note: We need to specify a date in order to also consider DST settings.
const date = new Date()
const ianaTimezone = 'Europe/Bratislava'
const tzOffsetString = new Intl.DateTimeFormat('en', {timeZone: ianaTimezone, timeZoneName: 'longOffset'}).format(date).match(/[\d+:-]+$/)?.[0]

Convert DateTime string to timestap in javascript

if dd = "2012-08-20 01:16:00";
converting this date to time-stamp (as in the following code)
var t = new Date(dd).getTime();
http://jsfiddle.net/userdude/DHxwR/
the result t = NaN why ?
According to ECMA-262 (§15.9.1.15, Date Time String Format, page 169), the only date string format required to be accepted is:
[+YY]YYYY[-MM[-DD]][THH:mm[:ss[.sss]]]Z
where Z is either Z (for UTC) or an offset consisting of either a + or a - followed by HH:mm. Any other formats that happen to be supported by a particular browser should not be relied upon, as continued support is not guaranteed.
Therefore, replace the space with a T and append either a Z, or a fixed time zone offset before passing it to the Date constructor. For example, if the date and time are in the UTC+8 zone:
var dd = "2012-08-20 01:16:00";
var t = new Date(dd.replace(' ', 'T') + '+08:00').getTime();
This will return the number of milliseconds from January 1, 1970, midnight UTC, to the date you have specified, treated as either universal time (if you appended Z) or a time local to the fixed time zone offset that you specify.
Please note that this will act differently in that the date is not simply treated as time local to the user's system time zone as your question's example does. However, I can't think of a situation where doing that would be useful, because you'd get different results depending on the user's configuration — but in reality, the time difference between two dates is always the same no matter where you are.
Try to use a space or comma between the year, month, and day values.
It's simple:
+(new Date("2012-08-20 01:16:00"));

Categories