I'm using this plugin called timeago found here: timeago.yarp.com
It works great, except that it operates in what appears to be a different timezone. I live in Eastern US (Philadelphia timezone) and when I put the exact time EST into the timeago plugin (say 2011-05-28, 13:47:18), it reprints as four hours later on my html page. When I write 2011-05-28, 17:47:18 (four hours later than my actual time from where I live), THEN it reprints as "less than a minute ago"
Here's the jquery plugin code:
(function($) {
$.timeago = function(timestamp) {
if (timestamp instanceof Date) {
return inWords(timestamp);
} else if (typeof timestamp === "string") {
return inWords($.timeago.parse(timestamp));
} else {
return inWords($.timeago.datetime(timestamp));
}
};
var $t = $.timeago;
$.extend($.timeago, {
settings: {
refreshMillis: 60000,
allowFuture: false,
strings: {
prefixAgo: "added",
prefixFromNow: "added",
suffixAgo: "ago",
suffixFromNow: "from now",
seconds: "less than a minute",
minute: "about a minute",
minutes: "%d minutes",
hour: "about an hour",
hours: "about %d hours",
day: "a day",
days: "%d days",
month: "about a month",
months: "%d months",
year: "about a year",
years: "%d years",
numbers: []
}
},
inWords: function(distanceMillis) {
var $l = this.settings.strings;
var prefix = $l.prefixAgo;
var suffix = $l.suffixAgo;
if (this.settings.allowFuture) {
if (distanceMillis < 0) {
prefix = $l.prefixFromNow;
suffix = $l.suffixFromNow;
}
distanceMillis = Math.abs(distanceMillis);
}
var seconds = distanceMillis / 1000;
var minutes = seconds / 60;
var hours = minutes / 60;
var days = hours / 24;
var years = days / 365;
function substitute(stringOrFunction, number) {
var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
var value = ($l.numbers && $l.numbers[number]) || number;
return string.replace(/%d/i, value);
}
var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
seconds < 90 && substitute($l.minute, 1) ||
minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
minutes < 90 && substitute($l.hour, 1) ||
hours < 24 && substitute($l.hours, Math.round(hours)) ||
hours < 48 && substitute($l.day, 1) ||
days < 30 && substitute($l.days, Math.floor(days)) ||
days < 60 && substitute($l.month, 1) ||
days < 365 && substitute($l.months, Math.floor(days / 30)) ||
years < 2 && substitute($l.year, 1) ||
substitute($l.years, Math.floor(years));
return $.trim([prefix, words, suffix].join(" "));
},
parse: function(iso8601) {
var s = $.trim(iso8601);
s = s.replace(/\.\d\d\d+/,""); // remove milliseconds
s = s.replace(/-/,"/").replace(/-/,"/");
s = s.replace(/T/," ").replace(/Z/," UTC");
s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
return new Date(s);
},
datetime: function(elem) {
// jQuery's `is()` doesn't play well with HTML5 in IE
var isTime = $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
var iso8601 = isTime ? $(elem).attr("datetime") : $(elem).attr("title");
return $t.parse(iso8601);
}
});
$.fn.timeago = function() {
var self = this;
self.each(refresh);
var $s = $t.settings;
if ($s.refreshMillis > 0) {
setInterval(function() { self.each(refresh); }, $s.refreshMillis);
}
return self;
};
function refresh() {
var data = prepareData(this);
if (!isNaN(data.datetime)) {
$(this).text(inWords(data.datetime));
}
return this;
}
function prepareData(element) {
element = $(element);
if (!element.data("timeago")) {
element.data("timeago", { datetime: $t.datetime(element) });
var text = $.trim(element.text());
if (text.length > 0) {
element.attr("title", text);
}
}
return element.data("timeago");
}
function inWords(date) {
return $t.inWords(distance(date));
}
function distance(date) {
return (new Date().getTime() - date.getTime());
}
// fix for IE6 suckage
document.createElement("abbr");
document.createElement("time");
}(jQuery));
I realize this problem is something very minor and can be easily fixed if I were to just remember the plugin works on a 4 hour delay, but I'd still like to know the answer if possible to provide.
Thanks!
Updated
I'm going to give this a shot but I must add the disclaimer that I'm a little fuzzy on this myself so I might be quite wrong!
The plugin expects the time in the ISO 8601 format which can also include offset information. I've now tried using the plugin and this is what I see (at 14:42 EDT, about 3 minutes from the test time string):
When a trailing Z is used, indicating Zulu time or an offset of 0 from UTC, the plugin interprets it as UTC (obviously) and when printing the relative time string, it takes into consideration your actual timezone. This causes the extra 4 hours to be added (EST is UTC-5 (UTC-4 when following DST, like now)).
2011-05-28T14:39:33Z prints as about 4 hours ago
When a trailing Z is not used, the plugin interprets the time specified according to your timezone and it seems to work just fine (as long as the timezone for the timestamp and the timezone you're viewing this timestamp in are the same). This is in line with what the Wikipedia article has to say:
If no UTC relation information is given with a time representation, the time is assumed to be in local time. While it may be safe to assume local time when communicating in the same time zone, it is ambiguous when used in communicating across different time zones. It is usually preferable to indicate a time zone (zone designator) using the standard’s notation.
This would not be a recommended way since it's going to mess up the times when viewed from elsewhere since the timestamp will be interpreted as being the timestamp for that timezone which is incorrect.
2011-05-28T14:39:33 prints as 3 minutes ago
When a trailing Z is specified along with the timezone offset (in the format of hh:mm, only hh seems to be ignored), it still seems to work just fine.
2011-05-28T14:39:33Z-04:00 prints as 3 minutes ago
You can see a working example here: http://jsfiddle.net/nogoodatcoding/SVgck/
You shouldn't be changing the timezone/offset of the plugin itself since that will cause visitors from other timezones to see incorrect values.
One fix is to also specify the timezone offset in your date-time strings: 2011-05-28, 13:47:18Z-04:00 - in a sense, a more complete description of the time since it also includes the UTC offset information.
Depending on how you're generating this page (if it's not just static HTML), the other option is to fix your server-side code so that the date-time string it outputs is in the UTC format - if not built in, you should be able to find a library that does the conversion from a timestamp in your local timezone into UTC. This is how sites I've seen do it - for example, the timestamps here on StackOverflow (or on Twitter and Facebook) are in UTC time - they are then formatted differently based on the user's timezone.
Hey, I use this plugin. It's not the javascript you have to edit. Look for the php file, in wordpress it's called 'wpTimeAgo.php' it should be something similar to that.
In that file look for this:
var $_gmtOffset = '';
Add a number there, I believe that should do the trick.
If you're using timeago in the .NET world, you'll find it's very fussy about the offset it gets. z and zz won't work, it needs to be zzz and should include the delimiting big "Z" and "T".
For example:
string.Format("{0}Z{1:%zzz}", DateTime.Now.ToString("s"), DateTime.Now);
This displays as:
2013-01-06T12:46:28Z-08:00
Related
I have date with format '2021-05-01T23:59:59.999-05:00'
Need to fetch utc offset value like 300, 330 etc.
Can someone help here.Any Answer without using moment.js is appreciated.
So extracting value -05:00 from '2021-05-01T23:59:59.999-05:00'
The function Date.getTimezoneOffset() will only ever give you the offset between the client machine timezone and UTC, it won't give you the UTC offset as specified in the ISO date string.
Given that the dates are in ISO-8601 format, we can parse the UTC offset from the data, it will be in the format ±[hh]:[mm], ±[hh][mm], ±[hh] or 'Z' for UTC / Zulu time.
Negative UTC offsets describe a time zone west of UTC±00:00, where the civil time is behind (or earlier) than UTC so the zone designator will look like "−03:00","−0300", or "−03".
Positive UTC offsets describe a time zone at or east of UTC±00:00, where the civil time is the same as or ahead (or later) than UTC so the zone designator will look like "+02:00","+0200", or "+02".
We'll use regular expressions to parse the timezone offsets, this will work on IE 11 too.
function getUTCOffsetMinutes(isoDate) {
// The pattern will be ±[hh]:[mm], ±[hh][mm], or ±[hh], or 'Z'
const offsetPattern = /([+-]\d{2}|Z):?(\d{2})?\s*$/;
if (!offsetPattern.test(isoDate)) {
throw new Error("Cannot parse UTC offset.")
}
const result = offsetPattern.exec(isoDate);
return (+result[1] || 0) * 60 + (+result[2] || 0);
}
const inputs = [
'2021-05-01T23:59:59.999+12:00',
'2021-05-01T23:59:59.999+10',
'2021-05-01T23:59:59.999+0530',
'2021-05-01T23:59:59.999+0300',
'2021-05-01T23:59:59.999Z',
'2021-05-01T23:59:59.999-05:00',
'2021-05-01T23:59:59.999-07:00',
];
for(var i = 0; i < inputs.length; i++) {
console.log('input:', inputs[i], 'offsetMinutes:', getUTCOffsetMinutes(inputs[i]));
}
Pretty much the same as #TerryLennox but deals with:
Seconds in the offset—fairly common before 1900, added as decimal part of minutes
Missing offset—local, returns undefined
function parseOffset(ts) {
let [all,hr,min,sec] = (''+ts).match(/([+-]\d{2}|Z):?(\d{2})?:?(\d{2})?$/) || [];
let sign = hr < 0 ? -1 : 1;
return !all ? all : // No match, return undefined
hr == 'Z' ? 0 :
hr * 60 + (min? sign * min : 0) + (sec? sign * sec / 60 : 0);
}
['2021-05-01T23:59:59.999-05:30',
'2021-05-01T23:59:59.999+05:30',
'2021-05-01T23:59:59.999-0530',
'2021-05-01T23:59:59.999+0530',
'2021-05-01T23:59:59.999-05',
'2021-05-01T23:59:59.999+05',
'2021-05-01T23:59:59.999+053012',
'2021-05-01T23:59:59.999+05:30:12',
'2021-05-01T23:59:59.999Z',
'2021-05-01T23:59:59.999',
'blah'
].forEach(ts => console.log(ts + ' -> ' + parseOffset(ts)));
I'm trying to display a chat div that displays between the hours of 8am-6pm Monday to Friday "Online" or show nothing if offline, based on the Eastern Time Zone (NYC), so that customers from Beijing will see Online or Offline based on these hours.
Simply need to show() or hide() the div. So far I have the hours, but I'm not sure how to get them to be in relation to the user time-zone.
$(document).ready(function () {
var start = new Date();
var end = new Date();
var time = new Date().getTime();
if (time > start.setHours(8,00) && time < end.setHours(18,00)) {
$('.online').show();
}
else {
$('.offline').hide();
}
});
The previous answer (seen in edit history) was to use the offset from UTC, however that isn't going to be an option if you want to support Daylight Savings; which is an important thing to do.
As such, the modification to the previous suggestion completely removes the use of UTC. To support daylight savings, the only proper way to get the time from EST is going to be to set the locale to that location, read the time, set up a new date object (which will technically be set up in the client local, but all we really want from it are the day and hour response from the Date object so we will ignore that technicality).
This is done by passing an object with the toLocaleString call which specifies the timezone, and then constructing a new date with the result of that.
var NYDate = new Date(new Date().toLocaleString("en-US", {timeZone: "America/New_York"}));
var NYHour = NYDate.getHours();
var NYDay = NYDate.getDay()
if (NYHour >= 8 && NYHour <= 18 &&
NYDay > 0 && NYDay < 6) {
$('.online').show();
}else {
$('.online').hide();
}
.online {
display: none;
color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="online">Online</div>
No JavaScript needed. You should do this from the server side. (The customer doesn’t tell the store when it’s open, the store tells the customer!)
Assuming your HTML is being generated by some server-side language (PHP, Ruby, etc), set the program to use New York time, and simply calculate if you’re within the “open” times. If you’re open, generate the Chat div. if you’re closed... don’t.
(Or alternately, show/hide it via CSS and classes)
Turns out that this is not a completely trivial task using JavaScript (as noted in the answer from #StephenR, this may be easier to deal with server side). And as noted in some of the comments, using a library may be the better js approach.
That said, after thinking a bit about the comments from #RobG regarding varying browser support for options like timeZone in toLocaleString, I was curious what it would take to solve this another way (makes me grateful for the various js date libraries). Snippet below...
const getOffset = (month, date, day, hour) => {
// assume EST offset
let offset = 5;
// adjust to EST offset as needed
if ((month > 2 && month < 10) || (month === 2 && date > 14)) {
offset = 4;
} else if (month === 2 && date > 7 && date < 15) {
if ((day && date - day > 7) || (day === 0 && hour - offset >= 2)) {
offset = 4;
}
} else if (month === 10 && date < 8) {
if ((day && date - day < 0) || (day === 0 && hour - offset < 1)) {
offset = 4;
}
}
return offset;
};
const isOnline = () => {
const dt = new Date(); // current datetime
let year = dt.getUTCFullYear(); // utc year
let month = dt.getUTCMonth(); // utc month (jan is 0)
let date = dt.getUTCDate(); // utc date
let hour = dt.getUTCHours(); // utc hours (midnight is 0)
let day = dt.getUTCDay(); // utc weekday (sunday is 0)
let offset = getOffset(month, date, day, hour);
if (hour - offset < 0) {
hour = 24 + hour - offset;
day = day ? day - 1 : 6;
if (date === 1) {
if (!month) {
year -= 1;
month = 11;
} else {
month -= 1;
}
date = new Date(year, month + 1, 0).getDate();
} else {
date -= 1;
}
} else {
hour -= offset;
}
if (day > 0 && day < 6 && hour > 7 && hour < 19) {
return true;
}
return false;
};
if (isOnline()) {
console.log('online'); // handle online
} else {
console.log('offline'); // handle offline
}
I have four fields in my HTML. Two are datepicker fields (one for from-date the other for the to-date). Similarly, the other two are timepicker fields (one for the from-time and other for the to-time) .
<input type="text" id="from-datepicker"/>
<input class="timepicker" name="timepicker" id="from-timepicker"/>
<input type="text" id="to-datepicker"/>
<input class="timepicker" name="timepicker" id="to-timepicker"/>
The dates are displayed and used in yyyy-mm-dd format, while the time format is 24-hour clock like 23:45:52.
Now, I just want to know how can I calculate the time difference between two timestamps such that the difference between
20 Oct 2015 11:00:00 and 28 Oct 2015 13:15:00shall return 7 days 2 hours and 15 minutes
I know how can I get the difference between two dates in JavaScript, but is there any library that provides differences using the timestamp?
Here is a function I use. Just modify the output to remove the "ago" part. Maybe change client_time and server_time to be more descriptive of your particular use.
And actually maybe add another if/else for your particular need following the general format.
Pub.prettyTime = function (server_time) {
var MINUTE = 60, // 000060 seconds in a minute
HOUR = 3600, // 003600 seconds in an hour
DAY = 43200, // 43,200 seconds in a day
NORMALIZE = 1000, // 00.001 seconds in a millisecond, flipped due to the .1 inaccuracy rule
// Date.now() is in milliseconds so divide by 1000, to get client_time in seconds
// this way client time and server time have the same units for comparison
// this is UTC time
client_time = Math.round(Date.now() / NORMALIZE),
rounded_time,
elapsed_time,
string = '';
// here we ensure we never get a negative elapsed time
// because clients are not synched to the server
// in the case of negative elapsed time, the server is ahead of the client
// and we will jus say "just a second ago"
if (client_time < server_time) {
client_time = server_time;
}
elapsed_time = (client_time - server_time);
// create the output string
if (elapsed_time === 0) {
string = ' just a second ago';
// 0 to 1 minute ago
} else if ((elapsed_time > 0) && (elapsed_time < MINUTE)) {
string = (elapsed_time === 1) ? 'one second ago' :
(elapsed_time + ' seconds ago');
// 1 minute to 1 hour ago
} else if ((elapsed_time >= MINUTE) && (elapsed_time < HOUR)) {
rounded_time = Math.floor(elapsed_time / MINUTE);
string = (rounded_time === 1) ? 'one minute ago' :
(rounded_time + ' minutes ago');
// 1 hour to to 1 day ago
} else if ((elapsed_time >= HOUR) && (elapsed_time < DAY)) {
rounded_time = Math.floor(elapsed_time / HOUR);
string = (rounded_time === 1) ? 'one hour ago' :
(rounded_time + ' hours ago');
// more than 1 day ago
} else if ((elapsed_time >= DAY)) {
rounded_time = new Date(server_time * NORMALIZE);
string = 'on ' + rounded_time.toLocaleDateString();
}
return string;
};
Furthermore you can plug in your format above in your Question into the constructor to obtain the normalized timestamp - var d2 = new Date("28 Oct 2015 13:15:00") and finally apply d2.valueOf() to get the unix timestamp. You can also take the difference of dates ( d2 - d1 ).
Using this info. you should be able to achieve what you need.
You can use momentjs.
Check substract part in docs.
I'm using this angular filter in my application to take a time stamp and convert it into 'time ago' time such as '3 hours ago'. I found the filter as a github gist and forked it to make it do conversions from UTC to the end user's local time. Now I'm can figure out why it only seems to work in chrome (note: I don't know if the original ever worked outside of chrome either). Safari and Firefox output 'in a long time' for all the times available from my application which span from a few minutes ago to a couple weeks ago. Any ideas?
app.filter('timeago', function () {
//time: the time
//local: compared to what time? default: now
//raw: whether you want in a format of "5 minutes ago", or "5 minutes"
return function (time, local, raw) {
var timeZoneOffset = (new Date().getTimezoneOffset()) * 60000;
if (!time) return "never";
if (!local) {
(local = Date.now());
}
if (angular.isDate(time)) {
time = time.getTime();
} else if (typeof time === "string") {
// convert string time to milliseconds
time = new Date(time).getTime();
}
// convert UTC to local
time = time - timeZoneOffset;
if (angular.isDate(local)) {
local = local.getTime();
}else if (typeof local === "string") {
local = new Date(local).getTime();
}
if (typeof time !== 'number' || typeof local !== 'number') {
return;
}
var span = [],
MINUTE = 60,
HOUR = 3600,
DAY = 86400,
WEEK = 604800,
MONTH = 2629744,
YEAR = 31556926,
DECADE = 315569260;
var offset = Math.abs((local - time) / 1000);
if (offset <= MINUTE) span = [ '', raw ? 'now' : 'a minute' ];
else if (offset < (MINUTE * 60)) span = [ Math.round(Math.abs(offset / MINUTE)), 'min' ];
else if (offset < (HOUR * 24)) span = [ Math.round(Math.abs(offset / HOUR)), 'hr' ];
else if (offset < (DAY * 7)) span = [ Math.round(Math.abs(offset / DAY)), 'day' ];
else if (offset < (WEEK * 52)) span = [ Math.round(Math.abs(offset / WEEK)), 'week' ];
else if (offset < (YEAR * 10)) span = [ Math.round(Math.abs(offset / YEAR)), 'year' ];
else if (offset < (DECADE * 100)) span = [ Math.round(Math.abs(offset / DECADE)), 'decade' ];
else span = [ '', 'a long time' ];
span[1] += (span[0] === 0 || span[0] > 1) ? 's' : '';
span = span.join(' ');
if (raw === true) {
return span;
}
return (time <= local) ? span + ' ago' : 'in ' + span;
};
});
EDIT:
Here is an condensed example of how i'm using it in html and the format that my api is returning date strings in. If other's are not having issues it could be related to how date strings are converted to millisecond time.
Time from within an angular controller
$scope.time = "2014-07-04 23:04:12";
From the html
<span>{{ time | timeago }}</span>
Display from firefox
'in a long time'
It appears that Date.parse might be implementation dependent. Based on this SO post and recommendation, I modified the custom parseDate function and verified that it works on IE, Chrome, and Firefox.
app.controller('ctrl', function($scope) {
// parse a date in yyyy-mm-dd format
function parseDate(input) {
var parts = input.split('-');
var timeBits = parts[2].split(' ');
var hms = timeBits[1].split(':');
// new Date(year, month [, day [, hours[, minutes[, seconds[, ms]]]]])
return new Date(parts[0], parts[1]-1, timeBits[0], hms[0], hms[1], hms[2]); // Note: months are 0-based
}
$scope.time = parseDate("2014-07-04 23:04:12");
alert($scope.time);
});
I was having the same issue. For me it was the date format with time zone that wasn't working.
"2014-07-04 23:04:12 GMT" This wasn't working
"2014/07/04 23:04:12 GMT" This worked
So basically I have done a calculation that increments a clock time:
function Clock(year,month,day,hours,minutes,seconds){
if(seconds !== null){
this.seconds = seconds;
}
if(minutes !== null){
this.minutes = minutes;
}
if(hours !== null){
this.hours = hours;
}
if(day !== null){
this.day = day;
}
if(month !== null){
this.month = month;
}
if(year !== null){
this.year = year;
}
}
function incrementClock(){
clock.seconds++;
if (clock.seconds >=60) {
clock.seconds = 0;
clock.minutes++;
if (clock.minutes >=60) {
clock.minutes = 0;
clock.hours++;
if (clock.hours >=24) {
clock.hours = 0;
clock.days++;
}
}
}
}
function showClock(){
//prints clock in format yyyy/mm/dd hh:mm:ss
}
This would increment the seconds, minutes, hours and days accordingly...
So if I call print the clock each second it would look like this:
var c = new Clock(2014,04,01,12,13,01);
showClock();
2014/4/1 12:13:1
I get stuck on the month part...
My question is how would I go about checking if a month has passed as there are different amount of days each month?
EDIT
I am creating my own minified Date function... so please don't recommend using Date objects as I am trying to implement my own
Is this what you're looking for?
function incrementClock(){
clock.seconds++;
if (clock.seconds >=60) {
clock.seconds = 0;
clock.minutes++;
if (clock.minutes >=60) {
clock.minutes = 0;
clock.hours++;
if (clock.hours >=24) {
clock.hours = 0;
clock.days++;
var months = [31,((clock.year%4==0)&&((clock.year%100!=0)||(clock.year%400==0)))?29:28,31,30,31,30,31,31,30,31,30,31];
if (clock.days>months[clock.month-1]){
clock.days = 0;
clock.months++;
}
}
}
}
}
This line:
var months = [31,((clock.year%4==0)&&((clock.year%100!=0)||(clock.year%400==0)))?29:28,31,30,31,30,31,31,30,31,30,31];
creates an array of the months and the days in each month. Thus it is easy to determine the amount of days per month like so:
days_in_jan = months[0]
days_in_feb = months[1]
...
days_in_dec = months[11]
You don't. You don't increment anything actually. If you want your own clock, that's fine, but you still need to access the current time using a Date object, so when you want to know how much time has elapsed, get a new Date object and figure it out:
pseudo structure:
Class Clock
- start date
- function timeElapsed
- elapsed = now - start date
In that function, compare the month, day, etc.
And honestly, if you don't like that answer you're on your own, as the answer to "how do I do this random thing as purely a thought exercise" is exactly that -- a thought exercise. There's no real point to it, so it shouldn't be asked or answered on SO.
As others have done, I would suggest using the Date object, or a date library like MomentJS, but it seems like you want to do the implementation yourself as an exercise. In that case, your code will need to know how many days are in each month if it is going to do the calculation correctly. Consider including in your code an array of the months' lengths:
var monthLengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
You'll also need a function to determine whether or not it's a leap year:
function isLeapYear(year) {
return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
}
Armed with those, you should be able to tell if a date has exceeded its month by checking the array to know how many days are in that month, and (if it's February) using the leap year function to determine if you should count 29 days for February.