I'm looking to show dates relative to the users' timezones.
My hope is that Angular has way to globally config the Date filter to do this—having to do this manually on a case-by-case basis feels wrong.
My timestamps are already wrapped in a timestamp() function (simply to multiply by 1000), but I'd prefer not to modify that function if I don't have to.
Edit:
I'm doing this, and it works, but as stated above, I'd like to set this one level higher if possible
$scope.timestamp = function (unix_time) {
var epoch = (unix_time * 1000);
var date = new Date();
var localOffset = (-1) * date.getTimezoneOffset() * 60000;
var stamp = Math.round(new Date(epoch + localOffset).getTime());
return stamp;
};
So, thanks to changes in angular 1.4.x this is now trivial. The proper way to handle this would be to create a decorator that alters the built in date filter before it runs. This is trivially easy, and won't have an impact on performance.
This is one I use. It simply adds a DEFAULT_TIMEZONE if no timezone is specified. This has the effect of moving all dates in the app to GMT as long as no other timezone is given.
module.config(['$provide', function($provide) {
var DEFAULT_TIMEZONE = 'GMT';
$provide.decorator('dateFilter', ['$delegate', '$injector', function($delegate, $injector) {
var oldDelegate = $delegate;
var standardDateFilterInterceptor = function(date, format, timezone) {
if(angular.isUndefined(timezone)) {
timezone = DEFAULT_TIMEZONE;
}
return oldDelegate.apply(this, [date, format, timezone]);
};
return standardDateFilterInterceptor;
}]);
}]);
From official documentation:
Timezones
Keep in mind that Angular datetime filter uses the time zone settings
of the browser. So the same application will show different time
information depending on the time zone settings of the computer that
the application is running on. Neither Javascript nor Angular
currently supports displaying the date with a timezone specified by
the developer.
http://docs.angularjs.org/guide/i18n
A level 'higher' could be creating a wrapper type (AKA class, ...) with your function as constructor. This should be coded at the entry point of the application for being possible using it everywhere.
Have you checked out momentjs http://momentjs.com/ ?
There's also angular-timezones, but I cannot speak to that package (https://github.com/michaelahlers/angular-timezones).
Related
I know how to get the timezone offset, but what I need is the ability to detect something like "America/New York." Is that even possible from JavaScript or is that something I am going to have to guestimate based on the offset?
The Internationalization API supports getting the user timezone, and is supported in all current browsers.
console.log(Intl.DateTimeFormat().resolvedOptions().timeZone)
Keep in mind that on some older browser versions that support the Internationalization API, the timeZone property is set to undefined rather than the user’s timezone string. As best as I can tell, at the time of writing (July 2017) all current browsers except for IE11 will return the user timezone as a string.
Most upvoted answer is probably the best way to get the timezone, however, Intl.DateTimeFormat().resolvedOptions().timeZone returns IANA timezone name by definition, which is in English.
If you want the timezone's name in current user's language, you can parse it from Date's string representation like so:
function getTimezoneName() {
const today = new Date();
const short = today.toLocaleDateString(undefined);
const full = today.toLocaleDateString(undefined, { timeZoneName: 'long' });
// Trying to remove date from the string in a locale-agnostic way
const shortIndex = full.indexOf(short);
if (shortIndex >= 0) {
const trimmed = full.substring(0, shortIndex) + full.substring(shortIndex + short.length);
// by this time `trimmed` should be the timezone's name with some punctuation -
// trim it from both sides
return trimmed.replace(/^[\s,.\-:;]+|[\s,.\-:;]+$/g, '');
} else {
// in some magic case when short representation of date is not present in the long one, just return the long one as a fallback, since it should contain the timezone's name
return full;
}
}
console.log(getTimezoneName());
Tested in Chrome and Firefox.
Ofcourse, this will not work as intended in some of the environments. For example, node.js returns a GMT offset (e.g. GMT+07:00) instead of a name. But I think it's still readable as a fallback.
P.S. Won't work in IE11, just as the Intl... solution.
A short possibility for a result in current user's language:
console.log(new Date().toLocaleDateString(undefined, {day:'2-digit',timeZoneName: 'long' }).substring(4));
If you're already using Moment.js you can guess the timezone name:
moment.tz.guess(); // eg. "America/New York"
You can use this script.
http://pellepim.bitbucket.org/jstz/
Fork or clone repository here.
https://bitbucket.org/pellepim/jstimezonedetect
Once you include the script, you can get the list of timezones in - jstz.olson.timezones variable.
And following code is used to determine client browser's timezone.
var tz = jstz.determine();
tz.name();
Enjoy jstz!
You can simply write your own code by using the mapping table here:
http://www.timeanddate.com/time/zones/
or, use moment-timezone library:
http://momentjs.com/timezone/docs/
See zone.name; // America/Los_Angeles
or, this library:
https://github.com/Canop/tzdetect.js
console.log(new Date().toLocaleDateString(undefined, {day:'2-digit',timeZoneName: 'long' }).substring(4).match(/\b(\w)/g).join(''))
To detect something like "America/New York.", you can use the new LocalZone() from the Luxon library.
import { LocalZone } from 'luxon';
const zoneName = new LocalZone().name;
This gets the timezone code (e.g., GMT) in older javascript (I'm using google app script with old engine):
function getTimezoneName() {
return new Date().toString().get(/\((.+)\)/);
}
I'm just putting this here in case someone needs it.
In javascript , the Date.getTimezoneOffset() method returns the time-zone offset from UTC, in minutes, for the current locale.
var x = new Date();
var currentTimeZoneOffsetInHours = x.getTimezoneOffset() / 60;
Moment'timezone will be a useful tool.
http://momentjs.com/timezone/
Convert Dates Between Timezones
var newYork = moment.tz("2014-06-01 12:00", "America/New_York");
var losAngeles = newYork.clone().tz("America/Los_Angeles");
var london = newYork.clone().tz("Europe/London");
newYork.format(); // 2014-06-01T12:00:00-04:00
losAngeles.format(); // 2014-06-01T09:00:00-07:00
london.format(); // 2014-06-01T17:00:00+01:00
I'm using moment.js and I want to write a test that says, "If you do not pass in a date, it returns now()". The problem is, every time the test runs, now() will be a different result. In other libraries, you can set what now() will return for testing purposes. Does moment.js provide a way to do that? I've been googling and not finding any results that say whether or not you can do this.
Timekeeper (https://www.npmjs.com/package/timekeeper) fills this exact need.
It overrides the date constructors (which moment uses under the hood) so it will work for moment as well.
You can do the following:
const timekeeper = require('timekeeper');
const freezeDate = new Date();
timekeeper.freeze(freezeDate);
// in your test
expect(result).to.equal(freezeDate);
You can change the time source (which basically overrides the implementation with a new Date.now) before each test by doing the following from the official momentjs docs. This returns the number of milliseconds since unix epoch time (1/1/1970).
moment.now = function () {
return +new Date();
}
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}
})
I am working with an API that returns dates in UNIX format: i.e. 1441647410.
With the following moment.js helper, I am able to convert it to a more readable format (05/22/15, 09:33):
UI.registerHelper('formatUnix', function(context, options) {
if(context)
return moment.unix(context).format('MM/DD/YY, hh:mm');
});
Instead of displaying an absolute time, I'd like to display time elapsed from now (i.e. 5 hrs ago, or 11 weeks ago) in this example. How could I do that? I am trying to define now with moment(), but it doesn't seem to be working:
UI.registerHelper('fromNow', function(context, options) {
if(context)
var now = moment();
var diff = now - context;
return moment.unix(diff).format('hh');
});
How can I should an amount of time elapsed from now (11 weeks ago) instead of an absolute time (5/22/15)? Also, is there any way for the browser to automatically grab the client's local time?
Thanks !
What about parsing unix into moment time and then using from:
return moment(context).from(moment());
You were close, you just need to use the fromNow() method after constructing the moment instance using moment.unix():
moment.unix(context).fromNow()
I'm having what I hope/assume is a simple and silly issue getting Moment.js to return the local date.
I'm passing in this date/time string: 2015-02-19T06:32:00.001-05:00
Using this code:
var departureDateTime = "2015-02-19T06:32:00.001-05:00";
moment(departureDateTime).format("YYYY-MM-DD"); // returns 2015-02-19
That looks correct. However, if I inspect the time with the code below it will return the time in UTC, which will cause the date to be incorrect (a day in the future) at certain times during the day.
moment(departureDateTime).format("h:mma")); // 11:32am
What am I missing? I thought the default for Moment.js was to return the local time, so I'm confused as to why it wouldn't return 6:32am instead of 11:32am.
Thanks for any assistance you can offer.
A way to do it:
var dateStr = "2015-02-19T06:32:00.001-05:00";
moment(dateStr).utcOffset(dateStr).format("h:mma")
(From here: http://momentjs.com/docs/#/manipulating/utc-offset/)
Edit: The intended way to do it, according to the documentation identified by K_C, is to use parseZone:
var dateStr = "2015-02-19T06:32:00.001-05:00";
moment.parseZone(dateStr).format('h:mma');