Date conversion resulting into time zone issue - javascript

I am trying to get functionality - if the user entered date is less than the current date I need to show an error message on the screen, I implemented the following code which is working fine in my local system date but not working in other time zones. Can anyone please help in getting this.
I need to use only javascript or jquery. I was not supposed to use other libraries.
dateFormat = function(value, event) {
let newValue = value.replace(/[^0-9]/g, '').replace(/(\..*)\./g, '$1');
const dayOrMonth = (index) => index % 2 === 1 && index < 4;
// on delete key.
if (!event.data) {
return value;
}
return newValue.split('').map((v, i) => dayOrMonth(i) ? v + '/' : v).join('');
}
checkStart = function(value) {
var newDate = new Date().toISOString();
var inputDate = new Date(value).toISOString();
var today = new Date(newDate).setHours(0,0,0,0);
var userDate = new Date(inputDate).setHours(0,0,0,0);
if(userDate < today) {
$('#error-msg3').show();
$('#startDate').val('');
} else {
$('#error-msg3').hide();
}
}
<input type="tel" maxlength="10" id="startDate" name="startDate" placeholder="mm/dd/yyyy"
oninput="this.value = dateFormat(this.value, event)" onblur="checkStart(this.value)" required/>
<span id="error">Start date should not be lesser than the current date.</span>
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>

Server and Db May run on a different timezone, (UTC preferred ) and when you sen date as a string it doesn't have any timezone there instead it is just a string.
Try sending it as a timestamp or UTC date string
So that server and db will automatically convert it to their timzone and store it. and when any user fetch it from other time zone it will automatically converts to their timezone (but you store string it will just be treated as a string everywhere)
let d = new Date()
console.log(d.getTime())
//or get utc string
console.log(d.toUTCString())
Send this value to your server (API)

Your code runs entirely on the client so timezone is irrelevant.
In the OP there is:
var newDate = new Date().toISOString();
...
var today = new Date(newDate).setHours(0,0,0,0);
The conversion of Date to string to Date to number is inefficient and unnecessary. The following is equivalent:
let today = new Date().setHours(0,0,0,0);
Similarly for inputDate:
var inputDate = new Date(value).toISOString();
...
var userDate = new Date(inputDate).setHours(0,0,0,0);
is equivalent to:
let userDate = new Date(value).setHours(0,0,0,0);
All calculations are local so timezone is irrelevant. Also see Why does Date.parse give incorrect results?
Attempting to control user input using script is always fraught as there are many cases that are either impossible or impractical to code around. The use of a tel input for Dates is an example. The whole issue can be avoided by using a date input and setting a min value to today. Then users can't select a date before today and your issue is solved, e.g.
window.onload = function() {
let dateEl = document.getElementById('dateInput');
dateEl.min = new Date().toLocaleString('en-CA', {year:'numeric', month:'2-digit', day:'2-digit'});
}
<input id="dateInput" type="date">
If you are comparing the date sent by the user to a date on the server, then user system clock accuracy and timezone may be an issue, but that isn't explained in the OP.
If that is an issue, then you need to ask another question on that specific topic.
If you really want to manually control the input date and show an error message when invalid dates are selected, then parse the value from the date input and compare it to the start of today and go from there:
// Parse YYYY-MM-DD as local
function parseYMDLocal(s) {
let [Y, M, D] = s.split(/\D/);
return new Date(Y, M-1, D);
}
// Check if date in YYYY-MM-DD format is before today
function isBeforeToday(d) {
return parseYMDLocal(d) < new Date().setHours(0,0,0,0);
}
function checkValue() {
let errEl = document.getElementById('errEl');
errEl.textContent = '';
console.log(typeof this.value);
if (isBeforeToday(this.value)) {
errEl.textContent = 'Date must be today or later';
} else {
// do something else
}
}
window.onload = function() {
document.getElementById('dateInp').addEventListener('blur', checkValue, false);
}
#errEl {color: red}
<input id="dateInp" type="date"><span id="errEl"></span>

Related

How to compare dates to make sure Google Apps Script is only sending an alert once a day?

I have a script in a Google Sheet that is sending out an alert if a certain condition is met. I want to trigger the script to run hourly, however, if an alert was already sent out today, I don't want to send out another one (only the next day). What is the best way to achieve this?
I've tried formatting the date several ways, but somehow the only thing working for me so far is getting the year, month and day from the date object as int and comparing them separately.
function sendAlert{
var now = new Date();
var yearNow = now.getYear();
var monthNow = now.getMonth() + 1;
var dayNow = now.getDate();
var sheet = SpreadsheetApp.getActive().getSheetByName('CHANGE_ALERT');
var sentYear = sheet.getRange("R2").getValue();
var sentMonth = sheet.getRange("S2").getValue();
var sentDay = sheet.getRange("T2").getValue();
if (yearNow != sentYear || monthNow != sentMonth || dayNow != sentDay) {
sendEmail();
var sentYear = sheet.getRange("R2").setValue(yearNow);
var sentMonth = sheet.getRange("S2").setValue(monthNow);
var sentDay = sheet.getRange("T2").setValue(dayNow);
else {
Logger.log('Alert was already sent today.');
}
}
I think this solution is definitely not the best approach, but I cannot come up with another that merges the date into one. Only comparing the new Date() doesn't work, since the time of day will not necessarily be the same. If I format the date to YYYY-MM-dd, it should work, but then when I get the date again from the spreadsheet it gets it as a full date with the time again.
Requirement:
Compare dates and send an email if one hasn't been sent already today.
Modified Code:
function sendAlert() {
var sheet = SpreadsheetApp.getActive().getSheetByName('blank');
var cell = sheet.getRange(2,18); //cell R2
var date = new Date();
var alertDate = Utilities.formatDate(cell.getValue(), "GMT+0", "yyyy-MM-dd");
var currentDate = Utilities.formatDate(date, "GMT+0", "yyyy-MM-dd");
if (alertDate !== currentDate) {
sendEmail();
cell.setValue(date);
} else {
Logger.log('Alert was already sent today.');
}
}
As you can see, I've removed all of your year/month/day code and replaced it with Utilities.formatDate(), this allows you to compare the dates in the format you specified in your question. I've also changed the if statement to match this, so now we only need to compare alertDate and currentDate.
References:
Utilities.formatDate()
Class SimpleDateFormat

Date input type validation in javascript?

I can't quite figure out how to validate a date input type in javascript. I tried looking on the internet but I just couldnt find anything.
I have one field that ask the user to input its birthday. I want to validate it in javascript with the certain limits on days months and, especially years. For example if the user input more than 2016(or the current year) it would give an error.
I can't quite figure out how to "extract" the date input type and control every elements of it (day, month, year).
Here part of my html
<form method="POST" action="request.jsp" onsubmit="return validate()">
Date of birth: <input type="date" id="bday" name="bday" value="">
</form>
Javascript:
var birthday = document.getElementById('bday').value;
This is all i've got.. please help?
TLDR
You have to parse the string as a date (JavaScript provides the Date API for this very use case).
Full answer
You're on the right track. Here's a JSBin example I did. Try opening the console and changing the date, and you'll see it logged.
$('#birthday').on('change', function() {
console.log(new Date(this.value));
});
(I'm using jQuery in the above example just for convenience sake, but you can use whatever you want.)
The problem you have here is that the date is logged as a string. You can use the JavaScript Date object to parse the string.
Based on whatever validation you want to do, you can use various date object methods (like getFullYear, for example) and match those against the input.
I'll leave the full implementation up to you, but the inside of the change handler might look like:
var date = new Date(this.value);
if(date.getFullYear() > 2016) {
// do something (like show a message, for example)
}
If you are able to get the value of the input element with:
var birthday = document.getElementById('bday').value;
Then birthday will be available to you as a string (all input values are always returned to JavaScript as strings). From there, you'd need to convert that string to a date with:
var dob = Date.parse(birthday);
Then, once you've got the entire date, you can extract the pieces of it with the various JavaScript Date/Time methods:
var month = dob.getMonth(); // months start counting from zero!
var day = dob.getDate();
var year = dob.getFullYear(); // getYear() provides 3 digit year!
Here's a working example:
var birthday = null, btn = null, output = null;
// Wait until the document is ready for interaction:
window.addEventListener("DOMContentLoaded", function(){
// Get references to DOM elements needed:
birthday = document.getElementById('bDate');
btn = document.getElementById('btnGetDate');
output = document.getElementById('result');
// Set up an event callback for when the button gets clicked:
btn.addEventListener("click", function(){
// Create a new Date that converts the input date
var dob =new Date(birthday.value);
alert(dob);
// Extract pieces of the date:
var month = dob.getMonth(); // months start counting from zero!
var day = dob.getDate();
var year = dob.getFullYear();
// Now that you have the pieces of the date, you can validate as you wish:
// e.g. if(year > 2016) { . . . }
// Write out date:
output.innerHTML = ++month + "/" + ++day + "/" + year;
});
});
<input type="date" id="bDate">
<input type="button" id="btnGetDate" value="Get Date">
<p id="result"></p>
NOTE: Keep in mind that Daylight Savings Time will have an effect on
the result depending on what time of day it is. See:
How to check if the DST (Daylight Saving Time) is in effect and if it is what's the offset?
for more info. on that.
Input type date is not supported in all browsers, so you should detect that and replace the input with a suitable alternative that includes the format that is required.
Where supported, the input will return an ISO 8601 format date string without a time zone. According to ISO 8601, this should be treated as local, but TC39 in their wisdom decided that it should be treated as UTC, so that is what Date.parse (and the Date constructor) will do in most cases. In some it will be treated as local and in IE 8 as invalid. So for systems with a timezone that is west of Greenwich, Date.parse('2016-03-20') will return a Date object that, when displayed as a local date, will be '2016-03-19', i.e. one day early.
So you should manually parse the date string, validate the date using one of the many answers here, then check whether the year, month and day are within your constraints.
if you're simply trying to validate whether or not a string is a valid date, you can just check that it creates a valid date object.
function isValidDate(d){
return !isNaN((new Date(d)).getTime());
}
https://jsfiddle.net/46cztok6/
so your validate() function would look like this.
function validate(){
var birthday = document.getElementById('bday').value;
if(!isValidDate(birthday)){
alert("you did not enter a valid birthday");
return false;
}
}
Here is a bin so you can have an idea how to start validating this type of field: https://jsbin.com/lifacaxonu/edit?html,js,console,output
$('#birthday').on('change', function() {
var val = this.value.split('-');
if (val[0] > new Date().getFullYear()) {
console.log('invalid')
} else {
console.log('ok')
}
});
After looking out for 3 hours, i wrote this and achieved dd/mm/yyyy date input using plain Javascript.
<div class="container">
<div class="datetime-container">
<input type="text" placeholder="write your date" id="datetime" onblur="validateDate()">
<p id="error"></p><br>
<input type="tel" maxlength="10" placeholder="dd/mm/yyyy"
oninput="this.value = DDMMYYYY(this.value, event)" />
</div>
</div>
<script>
function DDMMYYYY(value, event) {
let newValue = value.replace(/[^0-9]/g, '').replace(/(\..*)\./g, '$1');
const dayOrMonth = (index) => index % 2 === 1 && index < 4;
// on delete key.
if (!event.data) {
return value;
}
let currentYear = new Date().getFullYear();
console.log(newValue.slice(2,4));
if(newValue.length>=2 && newValue.slice(0,2)>31){
tempValue = newValue;
newValue = tempValue.replace(tempValue.slice(0,2),31);
document.getElementById("error").style.display = "initial";
document.getElementById("error").innerHTML = "Invalid day!";
}else if(newValue.length>=4 &&newValue.slice(2,4)>12){
document.getElementById("error").style.display = "initial";
document.getElementById("error").innerHTML = "Invalid month!";
tempValue = newValue;
newValue = tempValue.replace(tempValue.slice(2,4),12);
}else if(newValue.length==8 && newValue.slice(4)>currentYear){
tempValue = newValue;
newValue = tempValue.replace(tempValue.slice(4),currentYear);
document.getElementById("error").style.display = "initial";
document.getElementById("error").innerHTML = "Invalid year!";
}
else{
document.getElementById("error").style.display="none";
}
return newValue.split('').map((v, i) => dayOrMonth(i) ? v + '/' : v).join('');;
}
</script>

How can I dynamically set an end date based on a given start date and term duration?

I want to dynamically set the Term End date based on the Term Type (annual, semiannual, quarter, month) and the Term Start date.
So if we have a term that is set to annual and the start date is set to 03/10/2016, the term end should be set to 03/10/2017.
Here's what I have so far, but not sure how to work in casing the Term Type (annual == 12 months, etc.). I'm also getting a console error for the format of the date. Any help or pointers would be greatly appreciated.
.field
= f.label :term_start_date, 'Term Start', class: 'label'
.input
= f.date_field :term_start_date, id: 'termStart'
.field
= f.label :term_end_date, 'Term End', class: 'label'
.input
= f.date_field :term_end_date, id: 'termEnd'
:javascript
$("#termStart").val();
$("#termEnd").val({
onSelect: function () {
var toDate = $(this).val('getDate');
toDate.setDate(toDate.getDate()+7)
$("#termStart").val("setDate", toDate);
}
});
The specified value "[object Object]" does not conform to the required format, "yyyy-MM-dd".
What you're attempting isn't difficult, but there's a bit of work to do. A date library can help, but it doesn't take a lot of effort to write your own methods. You need to do a few things:
Parse and validate the input start date
Get the term and add some months to the start date
Handle invalid input
Display the result
The following doesn't use any jQuery (you can use it if you like but it doesn't offer any benefits) and should give you some idea of what needs to be done.
var dLib = {
// Given a date string in format m/d/y, return a Date instance
parseMDY: function (s) {
var b = s.split(/\D+/);
var d = new Date(b[2], --b[0], b[1]);
return /\d{1,2}\/\d{1,2}\/\d{1,4}/.test(s) &&
d.getMonth() == b[0]? d : new Date(NaN);
},
// Given a Date, return date string formatted as mm/dd/yyyy
formatMDY: function (date) {
function z(n){return (n<10?'0':'')+n}
if (isNaN(date)) return date.toString();
return z(date.getMonth() + 1) + '/' + z(date.getDate()) + '/' + date.getFullYear();
},
// Add months to a date. If end date itn's equal to start date,
// must have gone past end of month so set to last day of month
addMonths: function (date, months) {
var startDate = date.getDate();
date = new Date(+date);
date.setMonth(date.getMonth() + Number(months))
if (date.getDate() != startDate) {
date.setDate(0);
}
return date;
},
};
function setTermEnd(el) {
var form = el.form;
var term = form.termInMonths.value;
var startDate = dLib.parseMDY(form.termStart.value);
// Check that a valid date was entered. If no date entered
// yet, do nothing. Else, put error message in termEnd
if (isNaN(startDate)) {
form.termEnd.value = form.termStart.value == ''? '' : 'Term start date is invalid';
// Add months and set term end
} else {
form.termEnd.value = dLib.formatMDY(dLib.addMonths(startDate, term));
}
}
// Attach listeners and run to initialise
window.onload = function() {
var form = document.forms[0];
form.termInMonths.onchange = function() {setTermEnd(this)};
form.termStart.onchange = function() {setTermEnd(this)};
form.termStart.onchange(form.termStart);
}
.hint {
color: #999999;
font-size: 75%;
}
<form>
<table>
<tr><td>Term:<td><select name="termInMonths">
<option value="12" selected>Annual
<option value="6">Semi-annual
<option value="3">Quarter
<option value="1">Month
</select>
<tr><td>Term start:<td><input name="termStart" value="1/31/2016"><span class="hint">m/d/y</span>
<tr><td>Term End:<td><input name="termEnd" readonly><span class="hint">m/d/y</span>
</table>
</form>
Javascript's built-in datetime manipulation is very limited.
Your best bet is to use a javascript datetime library. moment.js is a good one
Your second best bet is going to be to work with the time in seconds as that is how javascript does it. Use Date.parse(toDate) to convert it to seconds since the epoch, then add the number of seconds, then convert it back to a date.

Google Form on Submit get values and format the time

I am using Google Apps Script with a Google form. When the user submits the Google Form I get a value from a question. I then take that value and make it a date object, from what I saw on this post about daylight savings I use that to determine the timezone. I run the date object through Utilities.formatDate and want to get the correctly formatted date.
example: 9:00 AM
But instead I am getting a completely different time than expected.
My question is: Can someone help me understand why the below code is outputting a time that is 3 hours different?
function onSubmit(e) {
var values = e.values;
Logger.log(values);
try {
var start1 = new Date(values[3]);
var startN = new Date(start1).toString().substr(25,6)+"00";
var startT = Utilities.formatDate(start1, startN, "h:mm a");
Logger.log(startT);
} catch(error) {
Logger.log(error);
}
}
The assumption that Utilities formatDate does not support GMT... parameter is not true.
The post you mentioned in reference is used to get calendar events and is a useful way to get the right value when you get events from another daylight saving period (getting the TZ info from the calendar event itself), for example events for next month will be in "summer time" while we are still in "winter time"...
Your issue might come from different sources depending on time zone settings of your script vs timezone of the source. Could you describe the exact configuration in which you use this script ?
In the mean time, here is a small code that demonstrates how the code is working + the logger results :
function testOnSubmit() {
var eventInfo = {};
var values = {};
values['3'] = new Date();
eventInfo['values'] = values;
Logger.log('eventInfo = '+JSON.stringify(eventInfo)+'\n\n');
onSubmit(eventInfo);
}
function onSubmit(e) {
var values = e.values;
try {
var start1 = new Date(values[3]);
Logger.log('onSubmit log results : \n');
Logger.log('start1 = '+start1)
var startN = new Date(start1).toString().substr(25,6)+"00";
Logger.log('startN = '+startN);
var startT = Utilities.formatDate(start1, startN, "h:mm a");
Logger.log('result in timeZone = '+startT);
} catch(error) {
Logger.log(error);
}
}
EDIT : additionally, about the 30 and 45' offset, this can easily be solved by changing the substring length like this :
var startN = new Date(start1).toString().substr(25,8);
the result is the same, I had to use the other version a couple of years ago because Google changed the Utilities.formatDate method at some moment (issue 2204) but this has been fixed.
EDIT 2 : on the same subject, both methods actually return the same result, the GMT string has only the advantage that you don't have to know the exact timezone name... there is also the Session.getScriptTimeZone() method. Below is a demo script that shows the resulst for 2 dates in January and July along with the log results :
function testOnSubmit() {
var eventInfo = {};
var values = {};
values['3'] = new Date(2014,0,1,8,0,0,0);
eventInfo['values'] = values;
Logger.log('eventInfo = '+JSON.stringify(eventInfo)+'\n\n');
onSubmit(eventInfo);
values['3'] = new Date(2014,6,1,8,0,0,0);
eventInfo['values'] = values;
Logger.log('eventInfo = '+JSON.stringify(eventInfo)+'\n');
onSubmit(eventInfo);
}
function onSubmit(e) {
var values = e.values;
var start1 = new Date(values[3]);
Logger.log('onSubmit log results : ');
Logger.log('start1 = '+start1)
var startN = new Date(start1).toString().substr(25,8);
Logger.log('startN = '+startN);
Logger.log('result in timeZone using GMT string = '+Utilities.formatDate(start1, startN, "MMM,d h:mm a"));
Logger.log('result in timeZone using Joda.org string = '+Utilities.formatDate(start1, 'Europe/Brussels', "MMM,d h:mm a"));
Logger.log('result in timeZone using Session.getScriptTimeZone() = '+Utilities.formatDate(start1, Session.getScriptTimeZone(), "MMM,d h:mm a")+'\n');
}
Note also that the Logger has its own way to show the date object value ! it uses ISO 8601 time format which is UTC value.
Try this instead:
var timeZone = Session.getScriptTimeZone();
var startT = Utilities.formatDate(start1, timeZone, "h:mm a");
The Utilities.formatDate function expects a time zone that is a valid IANA time zone (such as America/Los_Angeles), not a GMT offset like GMT+0700.
I am making the assumption that Session.getScriptTimeZone() returns the appropriate zone. If not, then you might need to hard-code a specific zone, or use a different function to determine it.
Additionally, the +"00" in the script you had was assuming that all time zones use whole-hour offsets. In reality, there are several that have 30-minute or 45-minute offsets.

Validate date range in javascript

Date format is in "mm/dd/yyyy". It is not giving me proper result. It is only validating month.
<input type="text" name="durationstart" id="durationstart" value="">
<input type="text" name="durationend" id="durationend" value="" onclick="return chk_val12()" >
<script>
function chk_val12()
{
var durationstart = document.getElementById('durationstart').value;
var durationend = document.getElementById('durationend').value;
if(durationstart>durationend)
{
alert("Please enter proper duration range");
return false;
}
else
{
return true;
}
}
</script>
You cannot compare validate date like this.
Text box contains the value of string dataType. So what the below code does is
if(durationstart>durationend) // Invalid
Instead parse it to date object.
Updates:
Though it works, there are chances for browser compatibility so here I have written the full date parsing
function chk_val12() {
var durationstart = document.getElementById('durationstart').value;
var durationend = document.getElementById('durationend').value;
var st = durationstart.split("/");
var en = durationend.split("/");
var startDate = new Date(st[2], (+st[0] - 1), st[1]);
var endDate = new Date(en[2], (+en[0] - 1), en[1]);
if (startDate > endDate) {
alert("Please enter proper duration range");
return false;
} else {
return true;
}
}
JSFiddle
FYI: Make sure user enters the right date format.
What it does?
1) Split the dateString you have like
var st = durationstart.split("/");
2) Parse it like new Date(YYYY, MM, DD)
var startDate = new Date(st[2], (+st[0] - 1), st[1]);
Extracting and comparing formatted date strings from text inputs
You'll need to use the Date object's parse method to convert the input strings into milliseconds, then compare the 2:
var durationstart = Date.parse( document.getElementById('durationstart').value );
var durationend = Date.parse( document.getElementById('durationend').value );
This depends upon the input being entered in a way the Date.parse method expects though — in formats following either the Wed, 3 Feb 2014 or 2014-02-03 standards.
Converting arbitrary formats into date objects
If the user is likely to use another input format, you may want to use a plugin such as Sugar to parse a wider range of possible formats. Sugar adds a Date.create( inputString ) method which accepts the following formats.
Using HTML5 #type=date inputs
An alternative for modern browsers is to use inputs of type date instead of text, which would allow you to extract the date values directly without fear of the user entering an unparseable format. Using this method you would change the HTML to:
<input type="date" name="durationstart" id="durationstart" value="">
<input type="date" name="durationend" id="durationend" value="" onclick="return chk_val12()" >
…and use the native valueAsDate method to extract the values as follows:
var durationstart = document.getElementById('durationstart').valueAsDate;
var durationend = document.getElementById('durationend').valueAsDate;
Try this
function dateCheck() {
fDate = Date.parse(document.getElementById("durationstart").value);
lDate = Date.parse(document.getElementById("durationend").value);
if(fDate >lDate )
{
alert("Please enter proper duration range");
return false;
}
else
{
return true;
}
}

Categories

Resources