Relative formatting for timestamps - krl

I wrote the CS 462 Office Hours app over the past two days. The most recent iteration tells the user when the next office hour time block will be. Right now, it just formats it as "Thursday (February 3) at 3 PM." I'd like it to be a little smarter, though, and say something like "this afternoon at 3 PM" or "tomorrow at 10 AM".
This is similar to Twitter's relative timestamps on tweets (it says "3 minutes ago" or "23 hours ago;" beyond that it lists the date). In my case, though, it will be the opposite, since we're dealing with future times.
Basically, it needs to be smart enough to know that an event is today or tomorrow. Beyond that, I just want to display the date and day of the week.
Is there a way to do this with KRL? Do I just need to use logic like this and write a function (or module)?

These are the functions I have:
// First define some constants to measuring relative time
now = time:now({"tz": "America/Denver"});
midnight = time:new("23:59:59.000-07:00");
tomorrow_midnight = time:add(midnight, {"days": 1});
yesterday_midnight = time:add(midnight, {"days": -1});
// Functions for calculating relative time
relativefuturedate = function(d){
ispast(d) => "today (past) at " + justtime(d)
| istoday(d) => "today at " + justtime(d)
| istomorrow(d) => "tomorrow at " + justtime(d)
| datetime(d);
};
istoday = function(d){
d > now && d <= midnight;
};
istomorrow = function(d){
not istoday(d) && d <= tomorrow_midnight;
};
ispast = function(d){
d < now;
};
isfuture = function(d){
d > now;
};
justtime = function(d){
time:strftime(d, "%l:%M %p");
};
datetime = function(d){
time:strftime(d, "%A (%B %e) at %l:%M %p");
};
This should do the trick. Right now I'm testing it with this rule:
rule first_rule {
select when pageview ".*"
pre {
today_9 = time:new("2011-02-09T09:00:00.000-07:00");
today_12 = time:new("2011-02-09T12:00:00.000-07:00");
today_4 = time:new("2011-02-09T16:00:00.000-07:00");
tomorrow_9 = time:new("2011-02-10T09:00:00.000-07:00");
tomorrow_4 = time:new("2011-02-10T16:00:00.000-07:00");
nextday_9 = time:new("2011-02-11T09:00:00.000-07:00");
relative_now = relativefuturedate(now);
relative_today_9 = relativefuturedate(today_9);
relative_today_12 = relativefuturedate(today_12);
relative_today_4 = relativefuturedate(today_4);
relative_tomorrow_9 = relativefuturedate(tomorrow_9);
relative_tomorrow_4 = relativefuturedate(tomorrow_4);
relative_nextday_9 = relativefuturedate(nextday_9);
message = <<
Now: #{relative_now}<br />
Today at 9: #{relative_today_9}<br />
Today at 12: #{relative_today_12}<br />
Today at 4: #{relative_today_4}<br />
Tomorrow at 9: #{relative_tomorrow_9}<br />
Tomorrow at 4: #{relative_tomorrow_4}<br />
Day after tomorrow at 9: #{relative_nextday_9}<br />
>>;
}
notify("Time calculations:", message) with sticky=true;
}
However, that doesn't seem to be working yet. I get this output:
Can anyone see what's wrong?

There currently isn't built in functionality to do this in KRL. You will probably need to write a function or a module to do this and I would love to see it if/when you do.

Related

Scraping Calendar data problem with next day date

Good morning, I have a problem that I do not know how to solve, through web-scraping I take data from the website where I work and create a ics calendar file with my shifts, it works almost always well, only when I have a shift that ends at midnight or later I get an error, this is because in the page I find only the start date and not the end date, so if I finish at 00.00 the correct date should be the next day, but it is added the same as the beginning so I get an error, do you have any idea how to correct this error?
"python nameerror free variable referenced before assignment in enclosing scope"
# CLEANING DATETIME DATUM
datclnd = soup_datum.replace('.', ' ')
dated = tempo.datetime.strptime(datclnd, "%d %m %Y")
yearclnd = dated.year
monthclnd = dated.month
dayclnd = dated.day
# CLEANING DATETIME DATUM
# CLEANING DATETIME HOUR AND MINUTES
time_split = soup_dienstbegin.split(":")
dienstBH = int(time_split[0])
dienstBM = int(time_split[1])
#dienstende
time_split2 = soup_dienstende.split(":")
dienstEH = int(time_split2[0])
try:
dienstEM = int(time_split2[1])
except:
NOTHING
def ics_working():
event = Event()
event['uid'] = f'19970610T172345Z-AF23B2#{dayclnd}{monthclnd}{yearclnd}'
event.add('summary', f'{string1}{tagesinfo2} {soup_kommentar2} {kursinfo}')
event.add('description', f'{schiffinfo}Schichtdauer: {soup_schichtdauer}, Bezahlte Zeit: {soup_bezahltezeit}\n, Infos: {schiffinfo}')
event.add('dtstart', datetime(yearclnd,monthclnd,dayclnd,dienstBH,dienstBM,0))
event.add('dtend', datetime(yearclnd,monthclnd,dayclnd,dienstEH,dienstEM,0))
event.add('dtstamp', datetime(yearclnd,monthclnd,dayclnd,dienstBH,dienstBM,0))
organizer = vCalAddress(f'MAILTO:{receiver_email}')
organizer.params['cn'] = vText(f'{username} Monatsplan')
organizer.params['role'] = vText(f'{username} Monatsplan')
event['organizer'] = organizer
event['location'] = vText('Werftestrasse 5, 6002 Luzern')
# Adding events to calendar
cal.add_component(event)
# Printing working day info
print(f'Datum: {soup_datum} Dienst: {string1}{tagesinfo2} --> Mannschaft: {crew_list2} --> OK')
ics_working()

How to make custom time and date?

I have looked at this code and they just seem to simply update theirs every day with the HTML, but I know there's a way to make a custom date by altering Monday to be Morndas, Sunday to be Sundas, January as Morning Star, etc. How would I go about coding this for my own website, as it's a cool little feature to have on an Elder Scrolls website, and I am not always available to update it every night at midnight.
Thanks
I think this is best done using server side scripting, like PHP. But below I've given a Javascript implementation which you can easily embed. Once you learn about server side scripting, it should be trivial to translate this code to another language.
window.addEventListener('DOMContentLoaded', function() {
// Function to determine the postfix of the month number
function numberPostFix(nr) {
nr = nr % 100;
if (nr >= 10 && nr < 20) return 'th';
nr = nr % 10;
if (nr == 1) return 'st';
if (nr == 2) return 'nd';
if (nr == 3) return 'rd';
return 'th';
}
// Arrays of custom month names.
var monthNames = [
"Morning Star",
"Sun's Dawn",
"First Seed",
"Rain's Hand",
"Second Seed",
"Mid Year",
"Sun's Height",
"Last Seed",
"Hearthfire",
"Frostfall",
"Sun's Dusk",
"Evening Star"
];
// Array of custom weekday names.
var dayNames = [
"Sundas",
"Morndas",
"Tirdas",
"Middas",
"Turdas",
"Fredas",
"Loredas"
];
// Get all relevant parts.
var date = new Date();
var month = date.getMonth();
var monthday = date.getDate();
var weekday = date.getDay();
// Construct the day text.
var dateHtml = "Today is " + dayNames[weekday] + ", " + monthday + "<sup>" + numberPostFix(monthday) + "</sup> of " + monthNames[month];
// Write to document.
document.getElementById('date').innerHTML = dateHtml;
});
<header>
<h2>Welcome, this is your page header.</h2>
<span id="date"></span>
</header>

Google Sheets: Session.getEffectiveUser() returns wrong values... twice

So what I'm trying to do is to log the edits of a spreadsheet on a corresponding column.
http://i.imgur.com/P8qrJOe.gif
The last column is the autogenerated text from the embedded script.
Here's the code:
function triggeredEdit () {
// set sheet variables
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var activeRange = activeSheet.getActiveRange();
// last updated
// LAST EDITED FOR SCRIPTS (check M3 if "Latest Row Modifications")
if (activeSheet.getRange("M3").getValue() == "Latest Row Modifications") {
for (var i = 1; i <= activeRange.getHeight(); i++) {
// modified TL column
if (activeRange.getCell(i, 1).getRow() > 4 && activeRange.getColumn() >= 6 && activeRange.getLastColumn() <= 6) {
activeSheet.getRange(activeRange.getCell(i, 1).getRow(), 13)
.setValue(Session.getEffectiveUser() + "\n" + Utilities.formatDate(new Date(), "GMT", "dd MMM YY, HH:mm:ss"));
}
// modified TLC column
else if (activeRange.getCell(i, 1).getRow() > 4 && activeRange.getColumn() >= 8 && activeRange.getLastColumn() <= 8) {
activeSheet.getRange(activeRange.getCell(i, 1).getRow(), 14)
.setValue(Session.getEffectiveUser() + "\n" + Utilities.formatDate(new Date(), "GMT", "dd MMM YY, HH:mm:ss"));
}
// modified edit column
else if (activeRange.getCell(i, 1).getRow() > 4 && activeRange.getColumn() >= 10 && activeRange.getLastColumn() <= 10) {
activeSheet.getRange(activeRange.getCell(i, 1).getRow(), 15)
.setValue(Session.getEffectiveUser() + "\n" + Utilities.formatDate(new Date(), "GMT", "dd MMM YY, HH:mm:ss"));
}
// modified QC column
else if (activeRange.getCell(i, 1).getRow() > 4 && activeRange.getColumn() >= 11 && activeRange.getLastColumn() <= 11) {
activeSheet.getRange(activeRange.getCell(i, 1).getRow(), 16)
.setValue(Session.getEffectiveUser() + "\n" + Utilities.formatDate(new Date(), "GMT", "dd MMM YY, HH:mm:ss"));
}
}
}
}`
The function triggeredEdit is run through a project trigger, which is also dynamically created in another function. As you can see in the gif, it seems like the triggeredEdit is called twice (since it changes twice). What's worse is that the correct email to be displayed is the first one, but then it gets replaced with the wrong one.
How do I go around fixing this? I'm quite stuck with this problem.
From what you explained in the comments It seems that you have 2 triggers on that spreadsheet that work simultaneously.
When you call Session.getEffectiveUser in a function triggered by onEdit, it returns the email of the user that installed the trigger. If you want to get the user at the keyboard then use Session.getActiveUser. Note that this will work only if you are in a domain, nothing is returned in normal gmail accounts.
To test that I used this small code :
function triggeredEdit () {
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var activeRange = activeSheet.getActiveRange();
Logger.log('xx');
activeSheet.getRange(1, 1).setValue('effective = '+Session.getEffectiveUser() + "\n" + Utilities.formatDate(new Date(), "GMT", "dd MMM YY, HH:mm:ss"));
activeSheet.getRange(1, 3).setValue('active = '+Session.getActiveUser() + "\n" + Utilities.formatDate(new Date(), "GMT", "dd MMM YY, HH:mm:ss"));
}
and here is what I get when I edit a cell (Serge created the trigger and Lambda was the user at the keyboard.
:
If I use the spreadsheet with my gmail account then I get this result below : no value as activeUser
and, finally, if I create a second trigger with my gmail account I have a double instance like yours.(I didn't capture the gif, sorry ;)
All this seems quite logical after all when I refer to the documentation about script authorization : triggers run under the authority of the user that creates it.
The soluion in your case will probably to change the way the triggers are created to avoid that anyone can do it... or try to filter execution in some way but I'm not sure that can be done.

Bootstrap Datepicker defaults to 1900's when entered with 2 digit year

I am using a datepicker in a modal window in my Bootstrap application. I am using the original Datepicker from Stefan Petre. I built it where it works in a desktop and mobile using a mouse and it works fine.
Recently I had a user request to allow it to also work with a keyboard. I removed the readonly property to allow the user to enter a date in the input field. The date-format is set to 'mm/dd/yyyy'.
When I enter a date like today for example like this "12/11/13" then it will default to 1913. This isn't a huge deal as I could just train the users to use 4 digits, but I would rather just have it default to this century.
Note: This only seems to happen for the date-format with a 4 digit year. This also seems to happen in the same manner in the newer forks of Stefan's code.
Note: I am using Bootstrap 2.0.4. I am testing with Firefox.
Here is what it looks like:
In JavaScript, set the datepicker's assumeNearbyYear attribute to true, like this:
$("#dp").datepicker({
assumeNearbyYear: true
});
This happens because the Bootstrap datepicker is using JavaScript Date objects. When you create a new Date object and pass in a two digit year it will output 1900+year (see Why does Javascript evaluate a 2-digit year of 00 as 1900 instead of 2000?)
You could try to tweak the datepicker source code, but that might be too complicated.
From what I can see on http://www.eyecon.ro/bootstrap-datepicker/ there is no option to set a range for the selectable dates, but you could change the format to use two digit years.
On your screenshot I can see, that you are using the datepicker for "Arrival Date" which I assume is in the future. On the website there is an example on how to disable dates in the past.
I hope that helps.
UPDATE
I have written an event handler for your problem which should do the trick.
Javascript on http://jsfiddle.net/pCYbd/1/
$("#dp").datepicker();
$("#dp").on("keyup", function(e) {
var date, day, month, newYear, value, year;
value = e.target.value;
if (value.search(/(.*)\/(.*)\/(.*)/) !== -1) {
date = e.target.value.split("/");
month = date[0];
day = date[1];
year = date[2];
if (year === "") {
year = "0";
}
if (year.length < 4) {
newYear = String(2000 + parseInt(year));
$(this).datepicker("setValue", "" + month + "/" + day + "/" + newYear);
if (year === "0") {
year = "";
}
return $(this).val("" + month + "/" + day + "/" + year);
}
}
});
CoffeeScript on http://jsfiddle.net/pCYbd/2/
$("#dp").datepicker()
$("#dp").on "keyup", (e) ->
value = e.target.value
if value.search(/(.*)\/(.*)\/(.*)/) != -1
date = value.split("/")
month = date[0]
day = date[1]
year = date[2]
year = "0" if year == ""
if year.length < 4
newYear = String(2000 + parseInt(year))
$(#).datepicker("setValue", "#{month}/#{day}/#{newYear}")
year = "" if year == "0"
$(#).val("#{month}/#{day}/#{year}")
My JavaScript skills are not the best, but this should work.
Updating bootstrap-datepicker.js as shown in this post solved it for me https://github.com/eternicode/bootstrap-datepicker/pull/1461/commits/2ea16adad27cbc4d4dfa20b924addfb480e5b036
yyyy: function(d,v){
if (format.parts.indexOf('yyyy') > -1 && v < 1000) v = 2000+v; // this line ***
return d.setUTCFullYear(v);
},
I'm using bootstrap-datepicker v1.5.1 and if you look around line 1741 where it does the year mapping, you will notice this:
yyyy: function (d, v) {
return d.setUTCFullYear(v);
},
yy: function (d, v) {
return d.setUTCFullYear(2000 + v);
},
When you specify that the control uses a four year date "yyyy", it will only do the return d.setUTCFullYear(v);, which will get you the previous century that JavaScript gives you. When you specify that it use the two year date "yy", it will do the correct 2000+ that you need for the current century.
So if you want the correct two year date to be 2016, 2017, etc., you need to set your datepicker to use the "yy" like so:
$('#tbPurchaseDate').datepicker({
format: 'mm/dd/yy',
autoclose: true,
todayBtn: 'linked',
todayHighlight: true,
orientation: 'bottom auto'
});
Or you can change the "yyyy" setting in the bootstrap-datepicker.js to match the "yy" version, but then you'd have to remember to do that every time you update the datepicker js file via nuget. It's much easier to just change your format setting.
Of course, if you want the full 4 digit year to display in the control, then you might want to try one of the elaborate fixes listed here or just set the "yyyy" to what the "yy" is in the js file.
Or just update your code to the latest version (1.6.4 right now) and "yyyy" and "yy" are the same and you use assumeNearbyYear: true as noted in another answer here.
For me, the best solution was to customize parseDate function in bootstrap-datepicker.js file directly. Inside a function, there is variable setters_map with yyyy property which I modified a bit. Here is my solution:
yyyy: function(d,v) {
if (v.toString().length == 2 && v <= 30) {
v = 2000 + parseInt(v);
}
return d.setUTCFullYear(v);
},
In my case it was needed to convert only years that are less or equals 30.
In the update function of bootstrap-datepicker.js I added this block of code:
var str = this.element.prop('value');
var defaulted = false;
if (str.lastIndexOf("/") >= 0 && (str.match(/\//g) || []).length == 2)
{
var yr = str.substring(str.lastIndexOf("/") + 1);
if (yr.length <= 2)
defaulted = true;
}
if (this.date.getFullYear() < 2000 && defaulted) {
this.date.setFullYear(this.date.getFullYear() + 100);
}
right before the viewdate is set on this line:
this.viewDate = new Date(this.date.getFullYear(), this.date.getMonth(), 1, 0, 0, 0, 0);
It will work 100% if you update below two line in UTCDate() of bootstrap-datepicker.js core file:
function UTCDate(){
/* Date defaulted date from 2000 if entered date year less than 4 degit*/
if(arguments!=null && arguments[0].toString().length<4)
arguments[0] = 2000 + arguments[0];
return new Date(Date.UTC.apply(Date, arguments));
}

Working with Dates in Google Apps Script

What I am trying to do here is this - I want to give index to only the workdays in each week.
So, if in a week, Monday and Wednesday are holidays, then Tuesday should get 1, Thursday should get 2, Friday should get the index 3. Otherwise, in a normal week without any holidays, Monday should get 1, Tuesday 2, Wednesday 3, and so on ...
Here is the code I have written (I haven't coded in years now, so please pardon the crude approach)
Sheet 'Holidays' contains a list of holidays in the column B starting from row 2
Variable date is the date for which I want to find out the index for
Variable dayOfTheWeek is the number of day of 'date' counted from last Sunday, so if date is a Monday, dayOfTheWeek is 1; if date is Tuesday, dayOfTheWeek is 2, and so on ...
function indexOfWorkdayOfTheWeek (date, dayOfTheWeek, lastSundayDate)
{
var activeSheet = SpreadsheetApp.getActiveSpreadsheet();
var activeCell = activeSheet.getActiveRange();
var activeRow = activeCell.getRowIndex();
var activeColumn = activeCell.getColumn();
var count = 1;
for (var j = 1; j < dayOfTheWeek; j++)
{
var date2 = lastSundayDate.valueOf() + j*86400;
Logger.log('Date ' + j + ' is:' + date2);
Logger.log('Last Sunday is:' + lastSundayDate);
if (holidayOrNot(date2) == true)
{
}
else
{
count = count + 1;
}
}
return count;
}
function holidayOrNot(date2)
{
var holidaysSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Holidays');
var listOfHolidays = holidaysSheet.getSheetValues(2, 2, 95, 1);
var isDateMatch = false;
for (var k = 0; k < 90; k++)
{
if (date2 == listOfHolidays[k].valueOf())
{
isDateMatch = true;
break;
}
else
{
continue;
}
}
return isDateMatch;
}
I think the problem is two-fold here:
The date2 calculation isn't working for some reason (var date2 = lastSundayDate.valueOf() + j*86400;)
The function holidayOrNot is returning false, no matter what, even if it encounters a holiday ... the condition date2 == listOfHolidays[k] isn't working for some reason...
Help would be appreciated!
maybe this method below could help you in your calculations, it returns an integer corresponding to the day of the year so if you apply this to your holidays days and compare to the days of interest it could be a good way to find matches.
here it is, just add these lines outside of any function in your script (so you can use it anywhere) then use it like this :
var d = new Date().getDOY();
Logger.log(d)
Here the method :
Date.prototype.getDOY = function() {
var onejan = new Date(this.getFullYear(),0,1);
return Math.ceil((this - onejan) / 86400000);
}
Assuming that lastSundayDate is being passed around correctly, I see a glaring problem:
lastSundayDate.valueOf().
valueOf() on Date objects returns the primitive value... it looks like you're going for adding a day to the date (86400 seconds * j)? I can't tell what the logic is supposed to be here. But the valueOf() date2 is definitely giving you an integer something like: 1384628769399 (see here).
What you really want to accomplish is something like Date.getDay(), or something similar so that you can add hours, days, etc. to the original Date. This is likely the source of all your problems.
What you can do is read the Mozilla Developer Network documentation on Date objects to see all of the functions on Dates and their uses. You can greatly simplify what you're trying to do by using these functions, instead of doing abstract operations like j * 86400.
It should also be noted that you can do simple operations such as the following, to add 4 hours to the current Date (time):
var myDate = new Date();
Logger.log(myDate); // ~ console.write
var laterDate = new Date(myDate.setHours(myDate.getHours() + 4));
Logger.log(laterDate); // ~ console.write
which gives the following:
[13-11-16 14:13:38:947 EST] Sat Nov 16 14:13:38 GMT-05:00 2013
[13-11-16 14:13:38:954 EST] Sat Nov 16 18:13:38 GMT-05:00 2013
Working with dates can be tricky - but it's always best to use the simplest methods that are available, which are built into the Date objects themselves. There are also numerous other libraries that provide extended functionality for Dates such as Date js.
If you're still running into your problem after attempting to try using methods I displayed above, please run your script and post both the Execution Transcript and the content of the Logger so that I can help you narrow down the issue :)