Class change based on local time : local sunrise/sunset calculation? - javascript

I'm new to JavaScript and put together this code to change the background on my page based on visitors local time.
I can't really test it yet, so first question would be, is this actually working? :D
And the main question: is there a way to set the sunrise and sunset times (set to 6 and 20 now) to follow the actual local times of the visitor?
Any help very much appreciated!!
<script type="text/javascript">
setInterval(change_background, 1000 * 60 * 60);
function change_background() {
var d = new Date();
var n = d.getHours();
console.log(n);
if (n == 6 || n == 20) {
document.getElementById("intro").className = "inter";
} else {
if (n == 23 || n < 7) {
document.getElementById("intro").className = "night";
} else {
document.getElementById("intro").className = "day";
}
console.log("test");
}
console.log("test");
}
change_background();
</script>

I suggest this shorter and reordered code – main difference is we set the class each time the function is called, your code would not have done anything after hour 7, execpt in exactly the hours 6, 20 and 23.
function change_background() {
var nowHour = new Date().getHours();
document.querySelector("#intro").className = ( nowHour >= 6 && nowHour <= 20 ) ? "inter" : ( (nowHour >= 23 || nowHour <= 5) ? "night" : "day" );
}
setInterval(change_background, 1000 * 60 * 60); change_background(); // trigger interval and first check
To use the local timezone you can use getTimezoneOffset – but things can become nightmarish with timezones, if you want to be as near to accurate as you can get. Just think about a science station in the polar regions, sunrise and sunset lose their meaning in such extreme scenarios. But with the offset you can use that to link it to a known good sunrise time .. but then you need to edit your file every day (or at least couple of days) to match the progression throughout the year.

Related

Snowflake UDF in JavaScript does not calculate as expected

I am trying to calculate the number of minutes a logged job has been running.
Each job has a start time and an end time.
In this particular case, the working hours are between 01:00 and 10:00, and only business days (weekends excluded)
In order to calculate this, I tried and made a JavaScript based UDF like this:
CREATE OR REPLACE FUNCTION JobRuns(f datetime, t datetime)
RETURNS DOUBLE
LANGUAGE JAVASCRIPT
AS
$$
// Based on the Calculation of Business Hours in JavaScript
// https://www.c-sharpcorner.com/UploadFile/36985e/calculating-business-hours-in-javascript/
function workingMinutesBetweenDates(startDate, endDate) {
// Store minutes worked
var minutesWorked = 0;
// Validate input
if (endDate < startDate) {
return 0;
}
// Loop from your Start to End dates (by hour)
var current = startDate;
// Define work range
var workHoursStart = 1;
var workHoursEnd = 10;
var includeWeekends = false;
// Loop while currentDate is less than end Date (by minutes)
while (current <= endDate) {
// Is the current time within a work day (and if it occurs on a weekend or not)
if (current.getHours() >= workHoursStart && current.getHours() <= workHoursEnd && (includeWeekends ? current.getDay() !== 0 && current.getDay() !== 6 : true)) {
minutesWorked++;
}
// Increment current time
current.setTime(current.getTime() + 1000 * 60);
}
// Return the number of minutes
return minutesWorked;
}
return workingMinutesBetweenDates(F,T);
$$
;
But the result I am getting are in some cases rather off from what I had expected.
The JS logic is grabbed from here; https://www.c-sharpcorner.com/UploadFile/36985e/calculating-business-hours-in-javascript/ and when I look at the code, I cannot see any flaws, which might cause these discrepancies.
I am using these test data
CREATE OR REPLACE TABLE "SLA_Test" (
"DocumentID" VARCHAR(16777216),
"From" TIMESTAMP_NTZ(9),
"To" TIMESTAMP_NTZ(9),
"ExpectedTime" INT
);
INSERT INTO "SLA_Test"
VALUES
('ACD7EFC1-8D17-46E3-84DB-C08067466866','2021-03-03 07:12:34.567','2021-03-03 08:12:34.567',60),
('C41FB599-D1EC-4461-BBAF-1AFF67D2F3C2','2021-03-03 09:55:00.000','2021-03-04 01:05:00.000',10),
('B741C663-732B-4FD3-839D-E70330C58990','2021-03-03 09:55:00.000','2021-03-04 00:05:00.000',5),
('C5893C51-F5CE-40E4-85F7-775515BC3E3D','2021-03-03 19:55:00.000','2021-03-04 01:05:00.000',5),
('BAF4ED57-8184-4CDF-8875-DFDA6EAC2033','2021-03-03 09:55:00.000','2021-03-05 01:05:00.000',550),
('F325059E-E78F-4DCE-B675-CC1C59669B3C','2021-03-05 09:55:00.000','2021-03-08 01:05:00.000',10),
('F325059E-E78F-4DCE-B675-CC1C59669B3C','2021-03-05 09:55:00.000','2021-03-07 01:05:00.000',5);
SELECT "DocumentID","From","To",
DATEDIFF(second, "From", "To") AS "TotalElapsedTimeSecond",
DATEDIFF(second, "From", "To")/60 AS "TotalElapsedTimeMinut",
"ExpectedTime",
JobRuns("From","To") AS "ElapsedTimeMinut"
FROM "SLA_Test";
Any ideas why the UDF does not return the expected time?
If you create a working hours table, you can run the following query:
select
t.id
, sum(datediff(‘second’,
-- calculate the max of the two start time
(case when t.start <=
w.working_day_start_timestamp
then w.working_day_start_timestamp
else t.start
end),
-- calculate the min of the two end times
(case when t.end >=
w.working_day_end_timestamp
then w.working_day_end_timestamp
else t.end
end)
)) / 3600 -- convert to hourly
as working_hour_diff
from
working_days_times w,
cross join time_intervals t
where -- select all intersecting intervals
(
t.start <= w.working_day_end_timestamp
and
t.end >= w.working_day_start_timestamp
)
and -- select only working days
w.is_working_day
group by
t.id
This article also goes into more detail about implementing this as a Javascript UDF: https://medium.com/dandy-engineering-blog/how-to-calculate-the-number-of-working-hours-between-two-timestamps-in-sql-b5696de66e51
this can all be done in SQL,
with SLA_Test(DocumentID, FromTime, ToTime, ExpectedTime) AS (
SELECT column1, column2::timestamp_ntz, column3::timestamp_ntz, column4
FROM
VALUES
('ACD7EFC1-8D17-46E3-84DB-C08067466866','2021-03-03 07:12:34.567','2021-03-03 08:12:34.567',60),
('C41FB599-D1EC-4461-BBAF-1AFF67D2F3C2','2021-03-03 09:55:00.000','2021-03-04 01:05:00.000',10),
('B741C663-732B-4FD3-839D-E70330C58990','2021-03-03 09:55:00.000','2021-03-04 00:05:00.000',5),
('C5893C51-F5CE-40E4-85F7-775515BC3E3D','2021-03-03 19:55:00.000','2021-03-04 01:05:00.000',5),
('BAF4ED57-8184-4CDF-8875-DFDA6EAC2033','2021-03-03 09:55:00.000','2021-03-05 01:05:00.000',550),
('F325059E-E78F-4DCE-B675-CC1C59669B3C','2021-03-05 09:55:00.000','2021-03-08 01:05:00.000',10),
('F325059E-E78F-4DCE-B675-CC1C59669B3C','2021-03-05 09:55:00.000','2021-03-07 01:05:00.000',5)
), days as (
SELECT row_number() over(order by seq8())-1 as num
FROM table(GENERATOR(rowcount=>30))
), enriched as (
SELECT *,
datediff('day', s.fromtime, s.totime) as tot_days
from SLA_Test AS s
), day_sliced AS (
select s.*
,d.*
,date_trunc('day',fromtime) f_s
,dateadd('day', d.num, f_s) as clip_day
,dateadd('hour', 1, clip_day) as clip_start
,dateadd('hour', 10, clip_day) as clip_end
,dayofweekiso(clip_day) as dowi
,dowi >=1 AND dowi <= 5 as work_day
,least(greatest(s.fromtime, clip_start),clip_end) as slice_start
,greatest(least(s.totime, clip_end), clip_start) as slice_end
,DATEDIFF('second', slice_start, slice_end) as slice_sec
,DATEDIFF('minute', slice_start, slice_end) as slice_min
from enriched AS s
join days AS d on d.num <= s.tot_days
qualify work_day = true
)
SELECT
DocumentID
,FromTime
,ToTime
,ExpectedTime
,round(sum(slice_sec)/60,0) as elasped_time_minutes
FROM day_sliced
GROUP BY 1,2,3,4
ORDER BY 1,2;
which gives the results as noted in expected:
DOCUMENTID FROMTIME TOTIME EXPECTEDTIME ELASPED_TIME_MINUTES
ACD7EFC1-8D17-46E3-84DB-C08067466866 2021-03-03 07:12:34.567 2021-03-03 08:12:34.567 60 60
B741C663-732B-4FD3-839D-E70330C58990 2021-03-03 09:55:00.000 2021-03-04 00:05:00.000 5 5
BAF4ED57-8184-4CDF-8875-DFDA6EAC2033 2021-03-03 09:55:00.000 2021-03-05 01:05:00.000 550 550
C41FB599-D1EC-4461-BBAF-1AFF67D2F3C2 2021-03-03 09:55:00.000 2021-03-04 01:05:00.000 10 10
C5893C51-F5CE-40E4-85F7-775515BC3E3D 2021-03-03 19:55:00.000 2021-03-04 01:05:00.000 5 5
F325059E-E78F-4DCE-B675-CC1C59669B3C 2021-03-05 09:55:00.000 2021-03-07 01:05:00.000 5 5
F325059E-E78F-4DCE-B675-CC1C59669B3C 2021-03-05 09:55:00.000 2021-03-08 01:05:00.000 10 10
Did you test this outside of Snowflake? I just created the following file and running node /tmp/dates.js produces this output which matches up with Snowflake
// Col1: function return, Col2: Expected
61 60
71 10
65 5
6 5
671 550
1271 10
671 5
function workingMinutesBetweenDates(startDate, endDate) {
// Store minutes worked
var minutesWorked = 0;
// Validate input
if (endDate < startDate) {
return 0;
}
// Loop from your Start to End dates (by hour)
var current = startDate;
// Define work range
var workHoursStart = 1;
var workHoursEnd = 10;
var includeWeekends = false;
// Loop while currentDate is less than end Date (by minutes)
while (current <= endDate) {
// Is the current time within a work day (and if it occurs on a weekend or not)
if (current.getHours() >= workHoursStart && current.getHours() <= workHoursEnd && (includeWeekends ? current.getDay() !== 0 && current.getDay() !== 6 : true)) {
minutesWorked++;
}
// Increment current time
current.setTime(current.getTime() + 1000 * 60);
}
// Return the number of minutes
return minutesWorked;
}
console.log(workingMinutesBetweenDates((new Date(Date.parse('2021-03-03 07:12:34.567'))), (new Date(Date.parse('2021-03-03 08:12:34.567')))), 60);
console.log(workingMinutesBetweenDates((new Date(Date.parse('2021-03-03 09:55:00.000'))), (new Date(Date.parse('2021-03-04 01:05:00.000')))), 10);
console.log(workingMinutesBetweenDates((new Date(Date.parse('2021-03-03 09:55:00.000'))), (new Date(Date.parse('2021-03-04 00:05:00.000')))), 5);
console.log(workingMinutesBetweenDates((new Date(Date.parse('2021-03-03 19:55:00.000'))), (new Date(Date.parse('2021-03-04 01:05:00.000')))), 5);
console.log(workingMinutesBetweenDates((new Date(Date.parse('2021-03-03 09:55:00.000'))), (new Date(Date.parse('2021-03-05 01:05:00.000')))), 550);
console.log(workingMinutesBetweenDates((new Date(Date.parse('2021-03-05 09:55:00.000'))), (new Date(Date.parse('2021-03-08 01:05:00.000')))), 10);
console.log(workingMinutesBetweenDates((new Date(Date.parse('2021-03-05 09:55:00.000'))), (new Date(Date.parse('2021-03-07 01:05:00.000')))), 5);
I've found at least 2 issues with the code, I think:
It will always over-count by at least 1. On the first loop round the WHILE statement the minutesWorked is incremented but at that point no time has actually been worked - the first minute hasn't been worked until StartDate + 1 minute
Your working day ends at 10 but your logic includes any time where the hour portion <= 10, so it is incrementing minutesWorked up to 10:59:59. I think the logic should be just less than not less than or equals: ... && current.getHours() < workHoursEnd

Get times in 15 minute increments up to a specific time

I'm using moment.js and would like to create an array that contains all of the times in 15 minute intervals from the current time. So for example:
Current time is 1:35pm. The next time would be 1:45pm, then 2:00, 2:15, 2:30, 2:45, etc. up until a certain point.
I'm really not sure how to this. Would anyone be able to point me in the right direction?
Try this:
function calculate(endTime) {
var timeStops = [];
var startTime = moment().add('m', 15 - moment().minute() % 15);
while(startTime <= endTime){
timeStops.push(new moment(startTime));
startTime.add('m', 15);
}
return timeStops;
}
usage:
calculate(moment().add('h', 1));
This will return time intervals of every quarter of hour (like you said) h:15, h:30, h:45, h+1:00... It also contains seconds, so you might set seconds to 0, since I was not sure if you need them or not.
You also can see working example on FIDDLE
I'm not as familiar with momentjs but this is relatively easy to do in pure Javascript. To get the closest 15 minutes you can use this solution here. Then if you put that in a date variable you can just add 15 minutes as many times as you want! So the resulting Javascript is:
var d = new Date();
var result = "";
for (var idx = 0; idx < 3; idx++)
{
var m = (((d.getMinutes() + 7.5)/15 | 0) * 15) % 60;
var h = ((((d.getMinutes()/105) + .5) | 0) + d.getHours()) % 24;
d = new Date(d.getYear(), d.getMonth(), d.getDay(), h, m, 0, 0);
if (idx > 0) result += ", ";
result += ("0" + h).slice(-2) + ":" + ("0" + m).slice(-2);
d = addMinutes(d, 15);
}
SEE THIS IN A FIDDLE
Notes - I just added 15 minutes 3 times arbitrarily. You could calculate the difference between the time you want and now if you need a different number of intervals. Also note that I don't know exactly what this would do if it is almost midnight, though that would be easy enough to test and code around.
Best of luck!

Javascript Time Comparisons

I have two issues I need to solve using javascript or jQuery.
1) I need to test whether the current time falls between 7am and 7pm.
var now = new Date();
if (now >= *7am* && now < *7pm*){} else {}
2) On document load, I need a function to run everyday at 7am and 7pm.
Any help is appreciated.
Resolved part 2 by running a time check every 15 minutes.
var checkTime = setInterval(myFunction(), 900000);
You can use var currentHour = (new Date()).getHours(); to retrieve the specific hour as an integer between 0 and 23, according to the local timezone of your environment:
var currentHour = (new Date()).getHours();
if (currentHour >= 7 && currentHour < 19) { /* stuff */ } else { /* other stuff }
If you need to get UTC, there is the .getUTCHours() method.
The documentation has more information if you're interested: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getHours
Look at Date.getHours
$(function(){
var now = new Date();
var hours = now.getHours();
if (hours >= 7 && now < hours < 19){} else {}
});

How do I change the timezone/timestamp of this jquery plugin?

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

Will this JS time code work? Can I make it better?

I'm displaying a message between Saturday at 6pm and Sunday 4am. The last time I had to do this it didn't work because I didn't take into account UTC time going negative when changing it to NYC time.
I am doing the math right (displaying at the appropriate times)?Should I put the UTC conversion code into its own function? Is this the worst js you've ever seen?
-- jquery is called --
$(document).ready(function() {
var dayTime = new Date();
var day = dayTime.getUTCDay();
var hour = dayTime.getUTCHours();
//alert(day.toString()+" "+hour.toString());
if (hour >= 5){
hour = hour-5;
}
else{
hour = hour+19;
if(day > 0){
day--;
}
else{
day = 6;
}
}
//alert(day.toString()+" "+hour.toString());
if ((day == 6 && hour >= 18) || (day == 0 && hour < 4)){
}
else{
$('#warning').hide(); //Want this message to show if js is disabled as well
}
});
Why do you even need that UTC stuff? Just work with local time:
var day = dayTime.getDay();
var hour = dayTime.getHours();
And you can clean up that conditional a bit too:
if (!(day == 6 && hour >= 18) && !(day == 0 && hour < 4)) {
$('#warning').hide();
}
This should get you your server's time:
var dayTime = new Date();
localOffset = dayTime.getTimezoneOffset() * 60000;
serverOffset = 5 * 60 * 60000;
dayTime = new Date(dayTime.getTime() + (localOffset - serverOffset));
Play around with that "5" in the server offset; it's the hours. It may need to be a -5; I'm not really sure.
Also, that's going to break every daylight savings. You'll have to detect that somehow and modify serverOffset.

Categories