How to show same date for all time zone in javascript - javascript

We have a lot of data where dates are saved with the time component and these dates are saved from different time zones. For example, if we save "1st July 2022" in IST, in the database it is saved as "2022-06-30T18:30:00.000Z". But if we save the same date in EST, it is saved as "2022-07-01T04:00:00.000Z". Like this, we have data from a couple more time zones like the Central and Pacific timezone. Now the task is to show 1st July for all these time zones. Naturally, I browsed the internet for a solid foolproof answer, but couldn't get any. This one is the best I could come up with. But this is not foolproof. So need one logic that will work across any timezone.
const offset = yourDate.getTimezoneOffset()
yourDate = new Date(yourDate.getTime() - (offset*60*1000))
return yourDate.toISOString().split('T')[0]

You can use Date.toLocaleDateString() to show the date in the user agent's timezone.
Using a dateStyle of 'long' will show the date using a d MMM yyyy format, e.g. 1 July 2022.
function formatAsLocalDate(timestamp) {
const dt = new Date(timestamp);
return dt.toLocaleDateString('default', { dateStyle: "long" });
}
// This would be the date stored to the database...
const utcDate = new Date(2022, 06, 01).toISOString();
console.log('UTC date:', utcDate);
console.log('Local date:', formatAsLocalDate(utcDate))
.as-console-wrapper { max-height: 100% !important; }

Working with time zones can be tricky. It is best to use a JavaScript date library that can help you with date parsing, formatting and time zone translation.
It can be subjective as to which library to choose. I could recommend Day.js. Luxon is also very good (but has Monday as the first day-of-the-week which can't be changed).
An example with Day.js looks like the code sample below. The dayjs.tz.guess() function can be used to try to get the time zone from the user's browser.
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
dayjs.extend( utc );
dayjs.extend( timezone );
const YOUR_ISO8601_DATE = '2022-06-30T00:20:00.000Z';
const DESIRED_TIME_ZONE_LOCATION = dayjs.tz.guess(); // e.g. 'Indian/Mauritius';
let localDate = dayjs( YOUR_ISO8601_DATE ).tz( DESIRED_TIME_ZONE_LOCATION ).format( 'YYYY-MM-DD' );
As an aside: An UTC offset does not provide enough information for you to know how to translate a time from GMT to a desired time zone in a specific location. Time zone rules (e.g. when Daylight Savings Time/Summer Time begins and ends) are determined by governments rather than operating systems. A JavaScript date library along with use of IANA time zone database time zones (e.g. "America/New_York") could allow you to reflect time zone offset rules in your dates for specific locations.

Related

Using Mongoose and Luxon to try and display date of event, but I'm getting the day before

I am using Mongoose and Luxon to display a date, selected by the user from a form. But the date is console.logging one date, but displaying on the page as the day before.
This is my model:
const mongoose = require("mongoose");
const { DateTime, Settings } = require("luxon");
// Configure time zone
console.log(Settings);
const Schema = mongoose.Schema;
let AccomplishmentSchema = new Schema({
dateInput: {
type: Date,
required: true,
},
textInput: {
type: String,
required: true,
},
});
AccomplishmentSchema.virtual("dateInput_formatted").get(function () {
return DateTime.fromJSDate(this.dateInput).toLocaleString(DateTime.DATE_FULL); // format 'YYYY-MM-DD
});
module.exports = mongoose.model("Accomplishment", AccomplishmentSchema);
And this is my console log vs what is showing up on the page
dateInput: 2023-01-01T00:00:00.000Z,
textInput: 'etst',
December 31, 2022
etst
I can only assume this has to do with some kind of time conversion, but I've tried changing the time zone, and the settings, although I could've read the docs wrong. I cannot find a way to fix this issue.
When working with JS Date, the most important thing to understand is that Date doesn't store or know about a time zone. It simply stores the number of milliseconds since midnight on Jan 1, 1970 (aka UNIX epoch) in the UTC time zone. Then, when you call Date methods, the results are calculated for the current user's time zone (for methods like getMinutes()) or UTC (for methods like getUTCMinutes()). But the underlying data inside the Date is just a timezone-less number of milliseconds.
With that in mind, let's look at a simple example: new Date(0), which is equivalent to new Date('1970-01-01T00:00Z').
If your computer's time zone set to the Europe/Paris time zone, then when you call new Date(0).toLocaleDateString('en-US') then you'll get a result of '1/1/1970'. But if you change your computer's time zone to America/Los_Angeles and run the same code, you'll get a result of '12/31/1969'.
This happens because the Date's stored value corresponds to midnight on Jan 1, 1970 in the UTC time zone. In Paris that instant was 1:00AM on Jan 1, 1970, while in California it was 4:00PM on December 31, 1969. The same exact Date will print a different date depending on the time zone that's active at the time that date is printed.
Where this becomes particularly problematic is in cases where you're trying to express a date without a time. For example, assume you store a Date value of 2023-01-01T00:00:00.000Z in MongoDB.
When you display that Date in a date picker in a browser in California, the date shown will be Dec 31, 2022, because California is 8 hours ahead of UTC.
One way to solve this problem is to tell Date that the date you're using is a UTC date. Like this:
utcDateStoredInMongoDB = new Date ('2023-01-01T00:00:00.000Z');
// Get the year, month, and day in that Date, from the perspective
// of the UTC time zone.
year = utcDateStoredInMongoDB.getUTCFullYear();
month = utcDateStoredInMongoDB.getUTCMonth();
day = utcDateStoredInMongoDB.getUTCDate();
// Use that year, month, day to initialize a new Date in
// the user's time zone.
dateForDatePicker = new Date(year, month, day);
// Use that for your date picker.
dateForDatePicker.toLocaleDateString();
// => '1/1/2023'
// To go in the other direction, pull out the year/month/day
// in the current time zone, and turn that into a UTC date.
dateReturnedByDatePicker = new Date(2023, 02, 15);
year = dateReturnedByDatePicker.getFullYear();
month = dateReturnedByDatePicker.getMonth();
day = dateReturnedByDatePicker.getDate();
dateToStoreInMongoDB = new Date(Date.UTC(year, month, day));
dateToStoreInMongoDB.toISOString();
// => '2023-03-15T00:00:00.000Z'
Note that this all will get a lot easier in a year or so when the new JavaScript built-in date/time API, called Temporal starts shipping in browsers and Node.js. With Temporal, these kinds of problems are much easier because there's a dedicated API, Temporal.PlainDate for dealing with date-only values.
Luxon is working as intended and the display is correct. What is wrong is the data in your MongoDB.
Most libraries consider current time zone when you parse a date. I live in Switzerland.
const moment = require("moment");
console.log( moment('2023-01-01').toDate() )
> 2022-12-31T23:00:00.000Z
const { DateTime } = require("luxon");
console.log( DateTime.fromISO('2023-01-01').toJSDate() )
> 2022-12-31T23:00:00.000Z
const dayjs = require('dayjs')
console.log( dayjs('2023-01-01').toDate() )
> 2022-12-31T23:00:00.000Z
However, the native JavaScript new Date() constructor does not!
console.log( new Date('2023-01-01') )
> 2023-01-01T00:00:00.000Z
Maybe this behavior depends on your environment and scripting engine, I don't know.
You should also use Luxon when you parse the input data - don't forget to use .toJSDate() to convert the DateTime object back to Javascript native Date object.
Luxon uses the system time zone by default. You can change it:
const { DateTime, Settings } = require("luxon");
Settings.defaultZone = 'America/New_York';
If you don't care about the time, you can also use startOf('day')
Settings.defaultZone = 'America/New_York';
DateTime.now().startOf('day').toJSDate()
> 2023-01-13T05:00:00.000Z
DateTime.now().startOf('day').toISO()
> '2023-01-13T00:00:00.000-05:00'

convert date based on timezone user is in

I have a date I want to convert based on their timezone.
For example, I want to set it in EST time (America/New_York)
2019-04-24 12:00:00
and if the user comes across the site from America/Los_Angeles, it will appear:
2019-04-24 09:00:00
I need to be able to return the hour, so in that example: 9.
I tried using https://github.com/iansinnott/jstz to determine their timezone and https://moment.github.io/luxon in hopes of handling the conversion w/o any luck.
I was testing by changing the timezone on my computer w/o any luck.
It sounds like you're asking to convert from a specific time zone to the user's local time zone (whatever it may be). You do not need time zone detection for that, but at present you do need a library. (Answers that suggest using toLocaleString with a time zone parameter are incorrect, as that function converts to a specific time zone, but cannot go the other direction.)
Since you mentioned Luxon, I'll provide a Luxon specific answer:
luxon.DateTime.fromFormat('2019-04-24 12:00:00', // the input string
'yyyy-MM-dd HH:mm:ss', // the format of the input string
{ zone: 'America/New_York'}) // the time zone of the input
.toLocal() // convert to the user's local time
.toFormat('yyyy-MM-dd HH:mm:ss') // return a string in the same format
//=> "2019-04-24 09:00:00"
This capability is also provided by other libraries, such as date-fns-timezone, js-Joda, or Moment-Timezone, but it is not yet something built in to JavaScript.
Converting date based on the time can be done like this. reference convert date to another time zone example snippet is under.
var usaTime = new Date().toLocaleString("en-US", {timeZone: "America/New_York"});
usaTime = new Date(usaTime);
console.log('USA time: '+usaTime.toLocaleString())
var usaTime = new Date().toLocaleString("en-US", {timeZone: "America/Los_Angeles"});
usaTime = new Date(usaTime);
console.log('USA time: '+usaTime.toLocaleString())
You could keep a list of timzeone identifiers and a list of their corresponding +/- number of hours with respect to your local time (which is returned by your time function).
Once you have a user's time zone, and you have extracted the current hour from the local timestamp simply look up the timezone in your list and use it's index to access the second list to find how many hours to add or subtract from the users time.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString
var date = new Date(Date.UTC(2012, 11, 12, 3, 0, 0));
// toLocaleString() without arguments depends on the implementation,
// the default locale, and the default time zone
console.log(date.toLocaleString());
// → "12/11/2012, 7:00:00 PM" if run in en-US locale with time zone America/Los_Angeles
Or you can use getYear, getMonth, getDate, getHours, getMinutes, getSeconds to format your own representation of the date. These methods all return values according to the user's local timezone.
I think the question may need more clarification - my first impression was you refer to a date-time that you already have and serve from the server. Doesn't this problem boil down to the Date object being "user-timezone-aware"? or not? But it is (some methods are, to be exact)
Your date/time is 2019-04-24 12:00:00 EDT (i assume P.M.)
This means the Unix timestamp of this in milliseconds is 1556121600000
(i assume daylight is on for April so not pure EST but EDT and an offset of UTC-4:00)
When you call
console.log(new Date(1556121600000).getHours())
doesn't this return 9 as you suggest, for Javascript executed on a browser from America/Los_Angeles with PDT timezone?
As suggested at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getHours :
The getHours() method returns the hour for the specified date,
according to local time.

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.

Get Parseable Timezone

I am using Momentjs and Momentjs Timezone to handle dates / timezones.
I'm attempting to have a date that is input from a user in a specific time zone and convert it to the local time in their own time zone. It doesn't look like Moment's Timezone library supports the new Date().getTimezoneOffset() format for setting the time zone.
function calculateLocalTime(e) {
var currentTz = new Date().getTimezoneOffset(),
fromDate = moment.tz(this.value, 'GMT'),
toDate = fromDate.tz(currentTz);
$('to-time').val(toDate.format(dateFormat));
}
I've also attempted to extract the three-letter time zone from the normal Date object as well, but that doesn't seem to be supported either.
function calculateLocalTime(e) {
var currentTz = new Date().toString().split(' ').pop().replace(/\(/gi, '').replace(/\)/gi, ''),
fromDate = moment.tz(this.value, 'GMT'),
toDate = fromDate.tz(currentTz);
$('to-time').val(toDate.format(dateFormat));
}
Any idea on how I should be doing this using Moment?
Moment-timezone is for working with standard identifiers from the IANA TZ Database, such as America/Los_Angeles.
Moment.js supports fixed-offset zones, independent of moment-timezone, using the zone function.
var m = moment();
// All of the following are equivalent
m.zone(480); // minutes east of UTC, just like Date.getTimezoneOffset()
m.zone(8); // hours east of UTC
m.zone("-08:00"); // hh:mm west of UTC (ISO 8601)
However, since you said you wanted to convert to the user's local time zone, there's no need to manipulate it explicitly. Just use the local function.
Here is a complete example, converting from an explicit IANA time zone to the user's local time zone:
// Start at noon, Christmas Day, on Christmas Island (in the Indian Ocean)
var m = moment.tz('2014-12-25 12:00:00', 'Indian/Christmas');
// Convert to whatever the user's local time zone may be
m.local();
// Format it as a localized string for display
var s = m.format('llll');
For me, running this in the US Pacific time zone, I get "Wed, Dec 24, 2014 9:00 PM". The result will vary depending on where the code is run.

How to convert a UTC time to local time javascript

I have a requirement to convert a UTC time local time based on user timezone
I have two parameters utc time and users timezone as a string
ie
0,1,2,3 ...12 (timezone)
0,-1,-2,-3 ...-12 (timezone)
var utc = "2014-10-18T06:14:41.512Z"
tz = 5.5(Indian Standard Time)
Expected result Sat Oct 18 2014 11:44:28 GMT+0530
I have tried moment js
moment("2014-10-18T06:14:41.512Z").zone('+05:30').format('YYYY-MM-DD HH:mm')
and the result is correct.
But when i change the timezone to other it is not showing as expected result
tried
moment("2014-10-18T06:14:41.512Z").zone('+12:00').format('YYYY-MM-DD HH:mm')
result "2014-10-18 18:14" Expected 2014-10-18 19:18
12 is NewZeland timezone. Please help me to solve this issue. Thank you
Check this
var date = new Date('2014-10-19 17:00:34 UTC');
date.toString();
var timezone = "America/New_York";
var utcDate = "2014-10-19T10:31:59.0537721Z";
var localDate = moment.utc(utcDate).tz(timezone).format()
Also check
http://www.digitoffee.com/programming/get-local-time-utc-using-moment-js/94/
To adhere to international standards, you need to format your UTC date to include the time delimiter T, and the zone designator Z.
Z is the timezone designator for the zero UTC offset aka Zulu time.
You can read more about the International Date Standard ISO8601 format specifics here.
Once you've conformed to the international standard, the cross browser friendly approach is simple:
new Date('2014-10-19T17:00:34Z');
// Sun Oct 19 2014 12:00:34 GMT-0500 (Central Daylight Time)
A time zone is not an offset. An offset is only part of a time zone. Many time zones alternate between two different offsets to account for daylight saving time. The time zone has to account for this, including the specific dates and times that daylight saving time begins and ends, as well as any history of changes that the time zone may have had.
The New Zealand case you gave is a perfect example. You said "12 is New Zealand timezone", and thus expected since New Zealand is in DST for that date that the conversion from 6:14 UTC to New Zealand local time would be 19:14. - 13 hours later.
But 12 doesn't fully represent New Zealand. It is just a 12 hour offset from UTC. There are plenty of other time zones that use the same offset in different ways. For example, the Marshal Islands use UTC+12 year round, without daylight saving time.
You should really read the timezone tag wiki - especially the section titled "Time Zone != Offset".
Instead of offsets, you should represent time zones with their full IANA identifier from the tz database. For example US Eastern Time is "America/New_York", Indian Time is "Asia/Kolkata", and New Zealand Time is "Pacific/Auckland". You can find more in the list on Wikipedia.
You can use moment-timezone to work with these in JavaScript.
moment("2014-10-18T06:14:41.512Z").tz('Pacific/Auckland').format('YYYY-MM-DD HH:mm')
// Output: "2014-10-18 19:14"
I also cover these topics in great detail in my Date and Time Fundamentals course on Pluralsight.com.
Please Check this link
http://www.digitoffee.com/programming/get-local-time-utc-using-moment-js/94/
var timezone = "UTC+5.30";
var utcDate = "2014-10-19T10:31:59.0537721Z";
var localDate = moment.utc(utcDate).tz(timezone).format()

Categories