Convert time of a known timezone to local timezone in moment js - javascript

I'm trying to convert the time ( time alone ) from a known timezone to my local timezone with Moment.js.
I wrote the following function and, I am getting invalidDate as the output.
const convertToLocalTime = (time, tz) => {
const t = moment.tz(time, tz)
const localTime = t.local()
}
time is just time; without any date eg: 10:06 am and,
tz is a timezone string for eg: Europe/Berlin
What am I doing wrong?

See Parsing in Zone:
The moment.tz constructor takes all the same arguments as the moment constructor, but uses the last argument as a time zone identifier.
Since your input (10:06 am) is not in ISO 8601/RFC 2822 recognized format (see moment(String) docs), you have to pass format parameter as shown in moment(String, String).
Here a live sample:
const convertToLocalTime = (time, tz) => {
const t = moment.tz(time, 'hh:mm a', tz)
const localTime = t.local()
return localTime;
}
const res = convertToLocalTime("10:06 am", 'Europe/Berlin');
console.log( res.format('hh:mm a') );
<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.14/moment-timezone-with-data-2012-2022.min.js"></script>

Related

Convert Epoch timestamp to ISO in javascript

I have a function in Java to convert an Epoch date to ISO 8601, but I've come across the need to do it in Javascript. I have it somewhat working in JS but it needs to be localized to the timezone.
Java version:
public static String epochToIso8601(long time, String Zone) {
String format = "yyyy-MM-dd'T'HH:mm:ssX";
TimeZone timeZone = TimeZone.getTimeZone(Zone);
SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.getDefault());
sdf.setTimeZone(timeZone);
return sdf.format(new Date(time));
}
Param1: -157737600000
Param2: PST
Output: 1965-01-01T00:00:00-08
My attempt in Javascript:
function epcov(epoch, timezone)
{
var someValueNum = Number(epoch);
var s = new Date(someValueNum);
return s.toISOString();
}
Essentially I want the same thing that's coming out of Java, but it's outputting: 1965-01-01T08:00:00.000Z
By the way, I'm already splitting the date and time up from something that looks like this, so if there is a better to just pass in the following as one string and let Javascript parse it, that would be amazing:
/Date(-157737600000-0800)/
We can convert the string /Date(-157737600000-0800)/ to a unix time (in ms), and a UTC offset in HHMM using String.match().
The HHMM UTC offset can then be converted to a UTC offset in milliseconds by multiplying the HH value by 3600000 and the MM value by 60000.
We then create a new Date object using the unix time and offset values, since we know the exact offset at that point in time.
We then format using Date.toISOString() and replace the 'Z' UTC offset timezone value with the actual UTC offset string (e.g. '-08:00').
function parseAndFormatDate(date) {
const [,millis, offset] = date.match(/([+\-]+\d+)([+\-]+\d+)/);
const offsetMillis = (offset.slice(0, 1) + '1') * (offset.slice(1, 3) * 3600000 + offset.slice(-2) * 60000);
const formattedOffset = offset.slice(0, 3) + ':' + offset.slice(-2);
return new Date(+millis + offsetMillis).toISOString().replace('Z', formattedOffset);
}
console.log(parseAndFormatDate('/Date(-157737600000-0800)/'));
console.log(parseAndFormatDate('/Date(+1664271413000+0100)/'));
.as-console-wrapper { max-height: 100% !important; }
I hope this helps you in some way.
JS doesn't have good (any?!) support for exporting an ISO 8601 string in a specified time zone. So you have to construct the string yourself, manually.
The use of the Swedish locale is to get an ISO 8601-like basis from which to pull the elements of the date time. Unfortunately there is no ISO 8601 formatting locale.
Note that the following will only reliably work with IANA timezone names because timezone abbreviations (PST, CST, BST) can be ambiguous. For example: V8 will accept 'PST' but SpiderMonkey will not.
// Get wall clock date at the specified tz in IS0 8601
const getISO8601Date = (d, timeZone) =>
d.toLocaleString('sv-SE', { timeZone, dateStyle: 'short'})
// Get wall clock time at the specified tz in IS0 8601
const getISO8601Time = (d, timeZone) =>
d.toLocaleString('sv-SE', { timeZone, timeStyle: 'medium'})
// Get time zone offset in specified tz in IS0 8601
const getISO8601TimeZoneOffset = (d, timeZone) => {
const s = d.toLocaleString('sv-SE', { timeZone, timeZoneName: 'longOffset' })
const result = /GMT(?<offset>[+−]\d\d:\d\d)/.exec(s) // Use regexp to pull out offset
return result ? result.groups.offset : 'Z'
}
// Put together an ISO 8601 string representing the wall clock at the specified date, in the specified timezone
const getISO8601Dtg = (instant, timeZone) => {
const d = new Date(instant)
return `${getISO8601Date(d, timeZone)}T${getISO8601Time(d, timeZone)}${getISO8601TimeZoneOffset(d, timeZone)}`
}
// Only reliably accepts IANA timezone names eg. 'America/Los_Angeles'
console.log(getISO8601Dtg(-157737600000, 'America/Los_Angeles')) // 1965-01-01T00:00:00−08:00
Relevant.

Converting time to another timezone JS

I'm obtaining data.value which is a time in the format: hh:mm a - for example 12:30 am.
I also know:
the local timezone of the user (userTimeZone)
the timezone of the venue (venueTimeZone)
I need to convert the time selected by the user (data.value) to the correct date in the venueTimeZone. For example, if the user is in Americas/New York and they selected 1:30PM on the 20/05/2022, and the venue is in Americas/Los Angeles - the value I am interested in obtaining is 20/05/2022 10:30AM.
This is my attempt, however the timezone itself doesn't change - I think this is because when I create the userDateTime with moment I don't specify a time offset, but I'm not sure how to obtain the offset from userTimeZone, whilst accounting for DST.
const userTimeZone = _.get(
Intl.DateTimeFormat().resolvedOptions(),
['timeZone']
);
const venueDateStr = new Date().toLocaleString('en-US', {
timeZone: venueTimeZone,
});
const Date = new Date(restaurantDateStr);
const venueYear = venueDate.getFullYear();
const venueMonth = `0${venueDate.getMonth() + 1}`.slice(-2);
const venueDateOfMonth = `0${venueDate.getDate()}`.slice(-2);
const userDateTime = createDateAsUTC(
moment(
`${venueDateOfMonth}/${venueMonth}/${venueYear} ${data.value}`,
'DD/MM/YYYY hh:mm a'
).toDate()
).toLocaleString('en-US', { timeZone: venueTimeZone });
EDIT - I do not have the city offset, I have the timezone name, therefore I cannot use any suggested answer which relies on city offset.
Consider using Luxon - the successor to Moment. (See Moment's project status.)
// Parse your input string using the user's local time zone
// (this assumes the current local date)
const local = luxon.DateTime.fromFormat('1:30 pm', 'h:mm a');
// Convert to the desired time zone
const converted = local.setZone('America/Los_Angeles');
// Format the output as desired
const formatted = converted.toFormat('dd/MM/yyyy h:mm a').toLowerCase();
console.log(formatted);
<script src="https://cdnjs.cloudflare.com/ajax/libs/luxon/2.4.0/luxon.min.js"></script>
You could also do this without a library, however you may find that not all browsers will parse the input string, and your output format is up to the browser as well.
// Get the current local date as a string
const date = new Date().toLocaleDateString();
// Parse the date and time in the local time zone
// Warning: This is non-standard and may fail in some environments
const dt = new Date(date + ' 1:30 pm');
// Format the output, converting to the desired time zone
const result = dt.toLocaleString(undefined, { timeZone: 'America/Los_Angeles' });
console.log(result);
There are, of course, manual ways to parse and format dates (using regex, etc.) but I'll leave that up to you or another person to complete.

Check if an iso string date is same or before an hour using moment

I've a string date in ISO format and a string in format HH:mm.
I want to know if the hour of the string ISO date is same or before the string in format HH:mm.
Example:
const isoDateString = '2021-09-28T07:30:00Z' // UTC
const hour = '07:30' // not UTC
-> result true
---
const isoDateString = '2021-09-28T07:30:00Z' // UTC
const hour = '08:30' // not UTC
-> result false
I'm using moment and this is my code:
const TIME_FORMAT = 'HH:mm'
const isoDateString = '2021-09-28T09:30:00Z'
const hour = '07:30'
const isHourSameOrBeforeIsoString = moment(
moment(isoDateString).format(TIME_FORMAT),
).isSameOrBefore(moment(hour, TIME_FORMAT));
console.log(isHourSameOrBeforeIsoString)
It doesn't work. It returns false in both cases. Why?
Use moment.utc() when constructing your iso date string because you should handle that as in UTC.
I also added TIME_FORMAT inside moment constructor of formatted iso date string.
const TIME_FORMAT = 'HH:mm'
const isoDateString = '2021-09-28T09:30:00Z'
const hour = '07:30'
const isHourSameOrBeforeIsoString = moment(
moment.utc(isoDateString).format(TIME_FORMAT), TIME_FORMAT
).isSameOrBefore(moment(hour, TIME_FORMAT));
console.log(isHourSameOrBeforeIsoString)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
Here is a more reasonable way to check in my opinion:
The logic you had was correct, but the reason it is returning false is because your isoDateString is returning 8:30 and the hour you are comparing it to is 7:30, like Krzysztof mentioned in their comment, it could be a time zone issue:
var format = 'hh:mm'
// var time = moment() gives you current time. no format required.
var time = moment('2021-09-28T09:30:00Z',format),
testTime = moment('07:30', format);
console.log(moment(time).format(format));
console.log(moment(testTime).format(format));
if (time.isSameOrBefore(testTime)) {
console.log('is before')
}
if(time.isSameOrAfter(testTime)){
console.log('is after')
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js" referrerpolicy="no-referrer"></script>

Reset timezone aware timestamp momentjs

I am using momentjs for doing my date operations and want to code a scenario where if timestamp is given, and timezone name is given, then I want to reset the time to midnight. For e.g.
let timestamp = 1493638245234;
expect(new Date(timestamp).toISOString()).toBe('2017-05-01T11:30:45.234Z'); // Time in UTC
let truncatedTimestamp = functionName(timestamp, 'America/Los_Angeles');
console.log(truncatedTimestamp);
expect(new Date(truncatedTimestamp)).toBe('2017-05-01T04:00:00.000Z');
const functionName = (timestamp, timezone) => {
return moment
.tz(timestamp, timezone)
.startOf('day')
.toDate()
.getTime();
};
I would like the function 'functionName' to return midnight of America/Los_Angeles time and not in UTC.
The timestamp I entered is 5th May 2017, 11:30 AM UTC.
I expect the function to return me timestamp for 5th May 2017, 00:00 America/Los_Angeles (Since 5th May 2017 11:30 AM UTC will be 11:30 AM -7 hours in America/Los_Angeles.) and convert it to milliseconds.
You have to remove toDate() that gives a JavaScript date object using local time. Then you can use valueOf() instead of getTime().
moment#valueOf simply outputs the number of milliseconds since the Unix Epoch, just like Date#valueOf.
Your code could be like the following:
const functionName = (timestamp, timezone) => {
return moment(timestamp)
.tz(timezone)
.startOf('day')
.valueOf();
};
let timestamp = 1493638245234;
let truncatedTimestamp = functionName(timestamp, 'America/Los_Angeles');
console.log(truncatedTimestamp);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.17/moment-timezone-with-data-2012-2022.min.js"></script>
Note that moment.tz and tz() are equivalent in your case (since you are passing millis).
I modified my function functionName to be like this and it worked.
const functionName = (timestamp, timezone) =>
moment
.tz(timestamp, timezone)
.startOf('day')
.valueOf();
NOTE: Some user posted this answer, but they deleted their answer.

Date in CET timezone to user's timezone

I have a list of list of string dates like this: '17/12/2017 19:34'. They are CET dates.
How can I transform it to the user's browser date?
I'm doing this:
const tzGuess = moment.tz.guess()
export const toTimeZone = (time) => {
const format = 'DD/MM/YYYY HH:mm'
return moment(time, format).tz(tzGuess).format(format)
}
console.log(toTimeZone('17/12/2017 19:34', tzGuess))
but how can I say to moment that the date I'm passing at first is a CET one?
Thanks!
You can use moment.tz function for parsing time string using a given timezone (e.g. 'Europe/Madrid').
The issue is: what do you mean with CET? If your input has fixed UTC+1 offset (like Central European Time), then you can use RobG's solution. If you have to consider both CET and CEST, I think that the best soution is to use moment.tz.
Here a live code sample:
const tzGuess = moment.tz.guess()
const toTimeZone = (time) => {
const format = 'DD/MM/YYYY HH:mm'
return moment.tz(time, format, 'Europe/Madrid').tz(tzGuess).format(format)
}
console.log(toTimeZone('17/12/2017 19:34', tzGuess))
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.4/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.13/moment-timezone-with-data-2012-2022.min.js"></script>
A great resource about timezone is the timezone tag info page.
Without moment.js, parse the string to a Date, treating it as UTC, then adjust for the CET offset (+0100). You can then format it using local time values for the client:
// Parse date in format DD/MM/YYYY HH:mm
// Adjust for CET timezone
function parseCET(s) {
var b = s.split(/\D/);
// Subtract 1 from month and hour
var d = new Date(Date.UTC(b[2], b[1]-1, b[0], b[3]-1, b[4]));
return d;
}
var s = '17/12/2017 19:34';
console.log(parseCET(s).toString());
However, if the time needs to observe daylight saving (CEST) for the source time stamp, you'll need to account for that.

Categories