Remove date from Time in Google App Script - datetime

I am new to script and found a code that I have been tweaking for my needs but I keep running into a problem since I separated the date and time. My date field display and selected value match but the time fields keep defaulting to MM/DD/YY HH:MM A/P when selected. en I click on it (it displays correctly when not selected). I need to have the field only display the time and nothing else. so it will stop causing issues to my formulas on other sheets. I know my problem lies in the last part but so far what I have tried has failed.
//DEFINE ALL ACTIVE SHEETS
var ss = SpreadsheetApp.getActiveSpreadsheet();
//DEFINE MAIN SHEET
var mainSheet = ss.getSheetByName("MAIN");
//LAST ROW ON MAIN SHEET
var lastRow = mainSheet.getLastRow();
for (var j = 5; j <= lastRow; j++)
{
// CHECK CLOCK IN
if(mainSheet.getRange('b1:b1').getValue() == mainSheet.getRange(j, 1).getValue() && mainSheet.getRange(j,3).getValue() == '')
{
Browser.msgBox('Need to Clock Out before Clocking IN');
return;
}
}
// ADD CLOCK IN RECORD
mainSheet.getRange(lastRow + 1, 1).setValue(mainSheet.getRange('b1:b1').getValue()).setFontSize(12);
mainSheet.getRange(lastRow + 1, 2).setValue(new Date(new Date().setHours(0, 0, 0, 0))).setNumberFormat('MM/DD/YY').setHorizontalAlignment("center").setFontSize(12);
mainSheet.getRange(lastRow + 1, 3).setValue(new Date()).setNumberFormat("hh:mm A/P").setHorizontalAlignment("center").setFontSize(12);//````

You get a whole bunch of formatting options if you look for Utilities.formatDate
var date = Utilities.formatDate(new Date(), "Europe/Berlin", "dd.MM.yyyy")
var time = Utilities.formatDate(new Date(), "Europe/Berlin", "HH:mm")
Change "Europe/Berlin" to your location to get the current time. Now use the variables wherever you need them. Simple as that.
Pro tip: Try to use variables (just like shown above) for readability instead of fitting everything in one line.

Related

AppSpreadsheet (GAS): avoid some problems with sistematic tested data

In my current job with spreadsheet, all inserted data passes through a test, checking if the same value is found on the same index in other sheets. Failing, a caution message is put in the current cell.
//mimimalist algorithm
function safeInsertion(data, row_, col_)
{
let rrow = row_ - 1; //range row
let rcol = col_ - 1; // range col
const active_sheet_name = getActiveSheetName(); // do as the its name suggest
const all_sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
//test to evaluate the value to be inserted in the sheet
for (let sh of all_sheets)
{
if (sh.getName() === active_sheet_name)
continue;
//getSheetValues do as its name suggest.
if( getSheetValues(sh)[rrow][rcol] === data )
return "prohibited insertion"
}
return data;
}
// usage (in cell): =safeInsertion("A scarce data", ROW(), COLUMN())
The problems are:
cached values confuse me sometimes. The script or data is changed but not perceived by the sheet itself until renewing manually the cell's content or refreshing all table. Is there any relevant configuration available to this issue?
Sometimes, at loading, a messing result appears. Almost all data are prohibited, for example (originally, all was fine!).
What can I do to obtain a stable sheet using this approach?
PS: The original function does more testing on each data insertion. Those tests consist on counting the frequency in the actual sheet and in all sheets.
EDIT:
In fact, I can't create a stable sheet. For test, a let you a copy of my code with minimal adaptations.
function safelyPut(data, max_onesheet, max_allsheet, row, col)
{
// general initialization
const data_regex = "\^\s*"+data+"\s*$"
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const activesheet = spreadsheet.getActiveSheet();
const active_text_finder = activesheet.createTextFinder(data_regex)
.useRegularExpression(true)
.matchEntireCell(true);
const all_text_finder = spreadsheet.createTextFinder(data_regex)
.useRegularExpression(true)
.matchEntireCell(true);
const all_occurrences = all_text_finder.findAll();
//test general data's environment
const active_freq = active_text_finder.findAll().length;
if (max_onesheet <= active_freq)
return "Too much in a sheet";
const all_freq = all_occurrences.length;
if (max_allsheet <= all_freq)
return "Too much in the work";
//test unicity in a position
const active_sname = activesheet.getName();
for (occurrence of all_occurrences)
{
const sname = occurrence.getSheet().getName();
//if (SYSTEM_SHEETS.includes(sname))
//continue;
if (sname != active_sname)
if (occurrence.getRow() == row && occurrence.getColumn() == col)
if (occurrence.getValue() == data)
{
return `${sname} contains same data with the same indexes.`;
};
}
return data;
}
Create two or three cells and put randomly in a short range short range a value following the usage
=safeInsertion("Scarce Data", 3; 5; ROW(), COLUMN())
Do it, probably you will get a unstable sheet.
About cached values confuse me sometimes. The script is changed but not perceived by the sheet until renewing manually the cell's content or refreshing all table. No relevant configuration available to this issue?, when you want to refresh your custom function of safeInsertion, I thought that this thread might be useful.
About Sometimes, at loading, a messing result appears. Almost all data are prohibited, for example (originally, all was fine!). and What can I do to obtain a stable sheet using this approach?, in this case, for example, how about reducing the process cost of your script? I thought that by reducing the process cost of the script, your situation might be a bit stable.
When your script is modified by reducing the process cost, how about the following modification?
Modified script:
function safeInsertion(data, row_, col_) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const range = ss.createTextFinder(data).matchEntireCell(true).findNext();
return range && range.getRow() == row_ && range.getColumn() == col_ && range.getSheet().getSheetName() != ss.getActiveSheet().getSheetName() ? "prohibited insertion" : data;
}
The usage of this is the same with your current script like =safeInsertion("A scarce data", ROW(), COLUMN()).
In this modification, TextFinder is used. Because I thought that when the value is searched from all sheets in a Google Spreadsheet, TextFinder is suitable for reducing the process cost.
References:
createTextFinder(findText) of Class Spreadsheet
findNext()

Google Sheet converting wrong the date

I vave a column that i only write numbers in the cells. Like for example:
I write 15022019 then i go to number formats and choose date. So the number is converted to 15/02/2019.
But i don't need everytime when i write a number make the change to date format. I need it automatically. So i found this script:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var column = sheet.getRange("D3:D31");
column.setNumberFormat("dd/mm/yyy");
It work. But is changing the numbers to date format incorrectly. If i write 14022019 its convert to 24/12/40290, and not in 14/02/2019(how i expected).
Why that?
Just in manually way it converts rightly. My location is Brazil.
Can someone say me what i'm doing wrong?
Edit 1:
I need it to convert to date automatically each time i fill a cell with the date. My range date will be always D3:D31. I tried modify the lines bellow:
function convertnumbertodate(crange){
// establish spreadsheet credentials
var ss1=SpreadsheetApp.getActive();
var sh1=ss1.getActiveSheet();
// get the range so that rows and columns can be calculated
var rg1=sh1.getRange(crange);
And in the place of (crange) i put D3:D31 to try to make the conversion to date automatically. Look bellow:
function convertnumbertodate(crange){
// establish spreadsheet credentials
var ss1=SpreadsheetApp.getActive();
var sh1=ss1.getActiveSheet();
// get the range so that rows and columns can be calculated
var rg1=sh1.getRange(D3:D31);
But when i run the function convertnumbertodate it reports error.
Can you help me how make it convert to date automatically?
Thank you
Edit 2:
Just made what you did:
function convertnumbertodate() {
// establish spreadsheet credentials
var editedCell;
var sh1=ss1.getActiveSheet();
// get the range so that rows and columns can be calculated
var rg1=sh1.getRange(D3:D31);
// get number of columns
var numColumns = rg1.getNumColumns();
// if more than one column chosen, stop the process.
if (numColumns !=1){
//Logger.log("DEBUG: Number of columns in range = "+numColumns); // DEBUG
var message = "Too Many Columns; one column only";
return message;
}
etc.
I deleted the crange and put my range D3:D31
Also made it run OnEdit: var editedCell;
But when i run, it says thats have an error in the line var rg1=sh1.getRange(D3:D31);
Problem
The OP enters 14022019 in an unformated cell. When the cell is formatted as a date, the value returned is 24 December 40290; the OP expected the date to be 14 February 2019.
Solution
- 1: format the cell as a date before data entry.
- 2: enter number with separators, such as 14/02/2019 or 14-02-2019
Explanation
When the OP types "14022019" into an unformatted cell, they intend that the input should be treated as a date (14 February 2019). However Google treats the contents at face value; there is no inference about date/time. So, when the cell is subsequently formatted as date, the raw value is converted to a date and the cell displays 24 December 40290.
The reason is that the Google Time Epoch began on 31 December 1899 00:00:00 (as opposed to the Unix Time Epoch, which is used by Javascript, which began on January 1, 1970 00:00:00). Secondly, Google measures date-time in days (as opposed to the Unix Epoch that measures elapsed seconds).
This is (roughly) how Google converts 14,022,019 to 24 December 40290.
14,022,019 "days", at a rough average of 365.245 days per year = approximately 38390.7 years.
Add on 1899 for the Google Epoch. Running total = 40289.7 years. (roughly mid September 40290)
Allow for adjustments for leap years 101.795 days = 0.3 (101.795/365.245); running total = 40290 years. (roughly 24 December 40290)
Note#1: there is a further complication.
The way that Sheets and Apps Script handle "dates" are very different.
Sheets: the "date" unit is 1 day; The base date is 1899-12-30 0:00:00, getting the timezone from the spreadsheet settings.
Apps Script (being based on JavaScript): the "date" unit is 1 millisecond. The base date is 1970-1-1 00:00:00 UTC.
Reference/Credit: Rubén
Note#2: My reference for the Google Epoch is (https://webapps.stackexchange.com/a/108119/196152)
Note#3: Broadly date/time conversions are based on 3,600 seconds per hour, 86,400 seconds per day, 31,556,926 second per year and 365.24 days per year.
UPDATE - 20 Feb 2019
The OP asks, quite rightly, "so how do I convert the existing cells?"
The code to make the conversion is straightforward:
- convert the number to a string
- slice the string into components for Day, Month and Year
- use the components to create a new date
- update the cell with the date
The range to be converted is an potential issue. What is the range, is the range always the the same size, etc? The following code enables an interface for the user to choose a range. The range can then be converted. Arguably this element wasn't essential, but does provide a more flexible, if not elegant, solution.
Code.gs
function onOpen(){
SpreadsheetApp.getUi()
.createMenu("Date Convert")
.addItem("Convert", "selRange")
.addToUi();
}
function selRange()//run this to get everything started. A dialog will be displayed that instructs you to select a range.
{
var output=HtmlService.createHtmlOutputFromFile('pickRange').setWidth(300).setHeight(200).setTitle('Convert to dates');
SpreadsheetApp.getUi().showModelessDialog(output, 'Convert Numbers to Dates');
}
function selCurRng()
{
var sso=SpreadsheetApp.getActive();
var sh0=sso.getActiveSheet();
var rg0=sh0.getActiveRange();
var rng0A1=rg0.getA1Notation();
rg0.setBackground('#FFC300');
return rng0A1;
}
function clrRange(range)
{
var sso=SpreadsheetApp.getActive();
var sh0=sso.getActiveSheet();
var rg0=sh0.getRange(range);
rg0.setBackground('#ffffff');
}
function convertnumbertodate(crange){
// establish spreadsheet credentials
var ss1=SpreadsheetApp.getActive();
var sh1=ss1.getActiveSheet();
// get the range so that rows and columns can be calculated
var rg1=sh1.getRange(crange);
// get number of columns
var numColumns = rg1.getNumColumns();
// if more than one column chosen, stop the process.
if (numColumns !=1){
//Logger.log("DEBUG: Number of columns in range = "+numColumns); // DEBUG
var message = "Too Many Columns; one column only";
return message;
}
// get the first row and the number of rows
var rowqty = 1;
var rownum = rg1.getRow();
// Logger.log("DEBUG: first row = "+rownum);//DEBUG
var rowqty = rg1.getNumRows();
// Logger.log("DEBUG: Number of rows = "+rowqty); //DEBUG
// get the values - different syntax for a single cell vs range
if (rowqty !=1){
// Multiple cells - uset GetValues
var rangevalues = rg1.getValues();
}
else {
// single cell, use getValue
var rangevalues = rg1.getValue();
}
//Logger.log("DEBUG: Values = "+rangevalues); //DEBUG
// create array for temporary storage
var newvalues = [];
// loop through the values
for (var i=0; i< rowqty; i++){
// different treatment for single cell value
if (i!=0 && rowqty !=1){
// multiple cells
var nstring = rangevalues[i].toString();
}
else {
// single value cell
var nstring = rangevalues.toString();
}
Logger.log("DEBUG: Value of the string is = "+nstring); //DEBUG
// slice the string in day, month and year
var daystring = nstring.slice(0, 2);
var monthstring = nstring.slice(2, 4);
var yearstring = nstring.slice(4, 8);
//calculate the date
var pubdate = new Date(yearstring, monthstring - 1, daystring);
//Logger.log("DEBUG: the date is "+pubdate); //DEBUG
// push the value onto the aray
newvalues.push([pubdate]);
}
// set the value(s)
if (rowqty !=1){
// Multiple cells - uset GetValues
rg1.setValues(newvalues)
}
else {
// single cell, use getValue
rg1.setValue(newvalues);
}
//rg1.setValues(newvalues);
var message = "Update complete";
rg1.setBackground('#ffffff');
return message;
}
pickRange.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
var grange='';
function selectRange()
{
$('#btn1').prop('disabled',true);
$('#btn2').prop('disabled',false);
google.script.run
.withSuccessHandler(setResponse)
.selCurRng();
}
function setResponse(r)
{
grange=r;
var msg='Selected range: ' + r+". Ready to convert";
$('#instr').css('display','none');
$('#rsp').text(msg);
}
function convert2date()
{
$('#btn1').prop('disabled',false);
$('#btn2').prop('disabled',false);
google.script.run
.withSuccessHandler(setResponse02)
.convertnumbertodate(grange);
}
function setResponse02(q)
{
qnumber=q;
var msg= q;
$('#instr').css('display','none');
$('#rsp').text(msg);
}
function clearAndClose()
{
google.script.run.clrRange(grange);
google.script.host.close();
}
console.log('My Code');
</script>
</head>
<body>
<div id="rsp"></div>
<div id="instr">Select range - <b>One column limit</b></div>
<br/>
<input type="button" id="btn1" value="1 - Select a range" onClick="selectRange();" />
<br />
<input type="button" id="btn3" value="2 - Convert numbers to dates" onClick="convert2date();" />
<br />
<input type="button" id="btn2" value="close" onClick="clearAndClose();"; disabled="true" />
</body>
</html>
Credit
//Prompt user for range in .gs function, pass array to html script and re-focus on the HTML dialog
//credit answer by Cooper - https://stackoverflow.com/a/45427670/1330560
ADDENDUM
If the range in which pseudo-dates are entered is know, and is non-changing, then the code to manage it is simplified
function onEdit(e) {
// establish spreadsheet credentials
var ss1 = SpreadsheetApp.getActive();
var sh1 = ss1.getActiveSheet();
// get the onEdit parameters
var debug_e = {
authMode: e.authMode,
range: e.range.getA1Notation(),
source: e.source.getId(),
user: e.user,
value: e.value,
oldValue: e.oldValue
};
//Logger.log("AuthMode: "+debug_e.authMode+"\n, Range: "+debug_e.range+"\n, source: "+debug_e.source+"\n, user: "+debug_e.user+"\n, value: "+debug_e.value+"\n, old value: "+debug_e.oldValue);
// Note the range for data entry is known and fixed.
// it is "D3:D31"
// Target range for converting numbers to dates
// set the column
var column = 4; // column D
// get the first row and the number of rows
var rowqty = 29;
var rowfirst = 3;
var rowlast = 31;
//Logger.log("DEBUG: first row = "+rowfirst+", last row = "+rowlast+", number of rows = "+rowqty);//DEBUG
// get detail of the edited cell
var editColumn = e.range.getColumn();
var editRow = e.range.getRow();
//Logger.log("DEBUG: edited column = "+editColumn+", edited row "+editRow);//DEBUG
//test if the edited cell falls into the target range
if (editColumn == 4 && editRow >= rowfirst && editRow <= rowlast) {
// the edit was in the target range
var nstring = e.value.toString();
//Logger.log("DEBUG: Value of the string is = "+nstring); //DEBUG
// slice the string in day, month and year
var daystring = nstring.slice(0, 2);
var monthstring = nstring.slice(2, 4);
var yearstring = nstring.slice(4, 8);
//calculate the date
var pubdate = new Date(yearstring, monthstring - 1, daystring);
//Logger.log("DEBUG: the date is "+pubdate); //DEBUG
e.range.setValue(pubdate)
} else {
//Logger.log("DEBUG: Nothing to see here; this cell not in the target range");//DEBUG
}
}

Google Sheets: delete rows containing specified data

I'm new to Java scripting and Google Apps Scripts so i am sorry if this has already been answered. I was not able to find what i was looking for over the last few months of working on this project.
I am working on a variant of the scripts here:
Delete row in Google Sheets if certain "word" is found in cell
AND
Google Sheet Script - Find Value in Col and Delete Row
I want to create a button, or menu, that will allow someone to enter specific data, and have each row in the spreadsheet containing that data deleted.
I have a test sheet here that illustrates the data i'm working with, formulas i'm using, and has the beginning of the script attached to it:
https://docs.google.com/spreadsheets/d/1e2ILQYf8MJD3mrmUeFQyET6lOLYEb-4coDTd52QBWtU/edit?usp=sharing
The first 4 sheets are pulling data from the "Form Responses 1" sheet via a formula in cell A:3 in each sheet so the data would only need to be deleted from the "Form Responses 1" sheet to clear it from the rest of the sheets.
I tried working this in but i do not think i am on the right track.
https://developers.google.com/apps-script/guides/dialogs
I also posted this on Google Docs Help Forum 60 days ago, but have not received any responses.
Any help would be greatly appreciated.
There's a few steps. For usability of UI this takes a little longer code. In concise form:
The user activates a dialog and enters a string.
Rows w/ the string are deleted (with error handling and confirmation)
(Hopefully this gets you started and you can tailor it to your needs)
Function that initiates the menu:
function onOpen(){
SpreadsheetApp.getUi()
.createMenu('My Menu')
.addItem('Delete Data', 'deleteFunction')
.addToUi();
}
The main workhorse:
function deleteFunction(){
//declarations
var sheetName = "Form Responses 1";
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName(sheetName);
var dataRange = sheet.getDataRange();
var numRows = dataRange.getNumRows();
var values = dataRange.getValues();
var delete_string = getUIstring();//open initial UI, save value
if (delete_string.length < 3) return shortStringError()//UI to protect your document from an accidental entry of a very short string.
//removing the rows (start with i=2, so don't delete header row.)
var rowsDeleted = 0;
for (var i = 2; i <= numRows; i++){
var rowValues = values[i-1].toString();//your sheet has various data types, script can be improved here to allow deleting dates, ect.
if (rowValues.indexOf(delete_string) > -1){
sheet.deleteRow(i - rowsDeleted);//keeps loop and sheet in sync
rowsDeleted++;
}
}
postUIconfirm(rowsDeleted);//Open confirmation UI
}
Isolated UI functions to help make above function more concise:
function getUIstring(){
var ui = SpreadsheetApp.getUi();
var response = ui.prompt("Enter the target data element for deletion")
return response.getResponseText()
}
function postUIconfirm(rowsDeleted){
var ui = SpreadsheetApp.getUi();
ui.alert("Operation complete. There were "+rowsDeleted+" rows deleted.")
}
function shortStringError(){
var ui = SpreadsheetApp.getUi();
ui.alert("The string is too short. Enter a longer string to prevent unexpected deletion")
}
I'll just show a way to delete the cell value if it matches your search criteria. It's up to you to connect it to buttons ,etc.
You'll loop through a Sheet Range. When you find the word match, delete it using clearContent()
function deleteSpecificData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range = sheet.getRange("Sheet1!A1:C4");
var values = range.getValues();
var numArray = [1,2,3,4,5,6,7,8,9];
var deleteItem = "Garen";
Logger.log(range);
for(var i=0; i< values.length; i++){
for(var j=0; j<values[i].length; j++){
if(values[i][j] == deleteItem){
var row = numArray[i];
var col = numArray[j];
var range = sheet.getRange(row,col).clearContent();
}
}
}
}
Before:
After:

momentjs - strange behave with startOf and endOf

I'm trying to get the start and the end a a specific day.
Here's the code:
var newDay, newDayEnd, newDayStart;
if (newDay === '') {
newDay = moment();
}
newDay.subtract('day', 1);
newDayStart = newDay.startOf('day');
newDayEnd = newDay.endOf('day');
I'm trying to debug it, and I noticed that when if goes trough the values are correct, but as soon as it reaches newDay.endOf('day') it sets all the variable to the end of the specified day (23.59.59)
I'm using the above function on button click. Every time I click a button, it goes back one day (newDay.subtract('day', 1)) and I need to be able to get start and end of the new day (newDay variable)
Any help?
What am I doing wrong here? I don't understand.
Thanks
Moment objects are mutable, so you have to clone() them before modifying them.
As you can read from the endOf docs:
Mutates the original moment by setting it to the end of a unit of time.
Working example:
var newDay, newDayEnd, newDayStart;
newDay = moment().subtract(1, 'day');
newDayStart = newDay.clone().startOf('day');
newDayEnd = newDay.clone().endOf('day');
console.log(newDayStart.format(), newDayEnd.format());
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.13.0/moment.min.js"></script>

Game Maker Studio: Top 10 Highscores (Seriously)

I feel really dumb for having to post this, but I've been trying to achieve this for an entire week now and I'm getting nowhere!
I'm trying to create a highscore board. Top 10 scores, saved to an INI file. I have searched every single thing on the entire internet ever. I just do not get it.
So what I have tried is this...
I have a "load_room" setup. When this room loads, it runs this code:
ini_open('score.ini')
ini_write_real("Save","highscore_value(1)",highscore_value(1));
ini_write_string("Save","highscore_name(1)",highscore_name(1));
ini_close();
room_goto(room0);
Then when my character dies:
myName = get_string("Enter your name for the highscore list: ","Player1"); //if they enter nothing, "Player1" will get put on the list
highscore_add(myName,score);
ini_open('score.ini')
value1=ini_write_real("Save","highscore_value(1)",0);
name1=ini_write_string("Save","highscore_name(1)","n/a");
ini_close();
highscore_clear();
highscore_add(myName,score);
score = 0;
game_restart();
I'm not worried about including the code to display the scores as I'm checking the score.ini that the game creates for the real values added.
With this, I seem to be able to save ONE score, and that's all. I need to save 10. Again, I'm sorry for asking the same age-old question, but I'm really in need of help and hoping someone out there can assist!
Thanks so much,
Lee.
Why you use save in load_room instead load?
Why you clear the table after die?
You need to use loop for save each result.
For example, loading:
highscore_clear();
ini_open("score.ini");
for (var i=1; i<=10; i++)
{
var name = ini_read_string("Save", "name" + string(i), "");
var value = ini_read_real("Save", "value" + string(i), 0);
if value != 0
highscore_add(name, value);
}
ini_close();
room_goto(room0);
Die:
myName = get_string("Enter your name for the highscore list: ","Player1");
highscore_add(myName, score);
ini_open("score.ini");
for (var i=1; i<=10; i++)
{
ini_write_string("Save", "name" + string(i), highscore_name(i));
ini_write_string("Save", "value" + string(i), string(highscore_value(i)));
}
ini_close();
score = 0;
game_restart();
And few more things...
score = 0;
You need do it when game starts, so here it is unnecessary.
get_string("Enter your name for the highscore list: ","Player1");
Did you read note in help?
NOTE: THIS FUNCTION IS FOR DEBUG USE ONLY. Should you require this functionality in your final game, please use get_string_async.
I used ini_write_string(..., ..., string(...)); instead ini_write_real() because second case will save something like 1000.000000000, and first case will save just 1000.

Resources