I have a CloudCode function that is called from my iOS app. The function is supposed to create a "checkin" record and return a string to represent the last 30 days of check-ins and missed days.
The strange thing is that sometimes I get the expected results and sometimes I do not. It makes me think that there is some issue with the may I am using timezones - since that could result in a different set of "days in the past" depending on what time I run this function and what time of day I checked-in in the past. But I'm baffled and could use some help here.
It's also confusing me that I do not see all of my console.log() results appear in the parse log. Is that normal?? For example, in the for loop, I can uncomment the console.log entry and call the function but I will not see all of the days in the past listed - but they are included in the final array and text string.
Here is my complete function. Any help and suggestions are appreciated.
/* Function for recording a daily check in
*
* Calculates the number of days missed and updates the string used to display the check-in pattern.
* If no days missed then we increment the current count
*
* Input:
* "promiseId" : objectID,
* "timeZoneDifference" : String +07:00
*
* Output:
* JSON String eg. {"count":6,"string":"000000000000001111101010111111"}
*
*/
Parse.Cloud.define("dailyCheckIn", function(request, response) {
var promiseId = request.params.promiseId;
var timeZoneDifference = request.params.timeZoneDifference;
var currentUser = Parse.User.current();
if (currentUser === undefined) {
response.error("You must be logged in.");
}
if (timeZoneDifference === undefined || timeZoneDifference === "") {
//console.log("timeZoneDifference missing. Set to -07:00");
timeZoneDifference = '' + '-07:00'; // PacificTime as string
}
var moment = require('cloud/libs/moment.js');
// Query for the Promise
var Promise = Parse.Object.extend("Promise");
var queryforPromise = new Parse.Query(Promise);
queryforPromise.get(promiseId, {
success: function(promis) {
// Initialize
var dinarowString = "";
var dinarowCount = 0;
// Last Check In date from database (UTC)
var lastCheckInUTC = promis.get("lastCheckIn");
if (lastCheckInUTC === undefined) {
lastCheckInUTC = new Date(2015, 1, 1);
}
// Use moment() to convert lastCheckInUTC to local timezone
var lastCheckInLocalized = moment(lastCheckInUTC.toString()).utcOffset(timeZoneDifference);
//console.log('lastCheckIn: ' + lastCheckInUTC.toString());
//console.log('lastCheckInLocalized: ' + lastCheckInLocalized.format());
// Use moment() to get "now" in UTC timezone
var today = moment().utc(); // new Date();
//console.log('today: ' + today.format());
// Use moment() to get "now" in local timezone
var todayLocalized = today.utcOffset(timeZoneDifference);
//console.log('todayLocalized: ' + todayLocalized.format());
// 30 days in the past
var thirtydaysago = moment().utc().subtract(30, 'days');
//console.log("thirtydaysago = " + thirtydaysago.format());
// 30 days in the past in local timezone
var thirtydaysagoLocalized = thirtydaysago.utcOffset(timeZoneDifference);
//console.log('thirtydaysagoLocalized: ' + thirtydaysagoLocalized.format());
// Calculate the number of days since last time user checked in
var dayssincelastcheckin = todayLocalized.diff(lastCheckInLocalized, 'days');
//console.log("Last check-in was " + dayssincelastcheckin + " days ago");
// Function takes an array of Parse.Objects of type Checkin
// itterate over the array to get a an array of days in the past as numnber
// generate a string of 1 and 0 for the past 30 days where 1 is a day user checked in
function dinarowStringFromCheckins(checkins) {
var days_array = [];
var dinarowstring = "";
// Create an array entry for every day that we checked in (daysago)
for (var i = 0; i < checkins.length; i++) {
var checkinDaylocalized = moment(checkins[i].get("checkInDate")).utcOffset(timeZoneDifference);
var daysago = todayLocalized.diff(checkinDaylocalized, 'days');
// console.log("daysago = " + daysago);
days_array.push(daysago);
}
console.log("days_array = " + days_array);
// Build the string with 30 day of hits "1" and misses "0" with today on the right
for (var c = 29; c >= 0; c--) {
if (days_array.indexOf(c) != -1) {
//console.log("days ago (c) = " + c + "-> match found");
dinarowstring += "1";
} else {
dinarowstring += "0";
}
}
return dinarowstring;
}
// Define ACL for new Checkin object
var checkinACL = new Parse.ACL();
checkinACL.setPublicReadAccess(false);
checkinACL.setReadAccess(currentUser, true);
checkinACL.setWriteAccess(currentUser, true);
// Create a new entry in the Checkin table
var Checkin = Parse.Object.extend("Checkin");
var checkin = new Checkin();
checkin.set("User", currentUser);
checkin.set("refPromise", promis);
checkin.set("checkInDate", today.toDate());
checkin.setACL(checkinACL);
checkin.save().then(function() {
// Query Checkins
var Checkin = Parse.Object.extend("Checkin");
var queryforCheckin = new Parse.Query(Checkin);
queryforCheckin.equalTo("refPromise", promis);
queryforCheckin.greaterThanOrEqualTo("checkInDate", thirtydaysago.toDate());
queryforCheckin.descending("checkInDate");
queryforCheckin.find().then(function(results) {
var dinarowString = "000000000000000000000000000000";
var dinarowCount = 0;
if (results.length > 0) {
dinarowString = dinarowStringFromCheckins(results);
dinarowIndex = dinarowString.lastIndexOf("0");
if (dinarowIndex === -1) { // Checked in every day in the month!
// TODO
// If the user has checked in every day this month then we need to calculate the
// correct streak count in a different way
dinarowString = "111111111111111111111111111111";
dinarowCount = 999;
} else {
dinarowCount = 29 - dinarowIndex;
}
}
// Update the promise with new value and save
promis.set("dinarowString", dinarowString);
promis.set("dinarowCount", dinarowCount);
promis.set("lastCheckIn", today.toDate());
promis.save().then(function() {
response.success(JSON.stringify({
count: dinarowCount,
string: dinarowString
}));
});
}, function(reason) {
console.log("Checkin query unsuccessful:" + reason.code + " " + reason.message);
response.error("Something went wrong");
});
}); // save.then
},
error: function(object, error) {
console.error("dailyCheckIn failed: " + error);
response.error("Unable to check-in. Try again later.");
}
});
});
There's too much going on in your question to answer adequately, but I will be nice and at least point out a few errors that you should look into:
You take input in terms of a fixed offset, but then you are doing operations that subtract 30 days. It's entirely possible that you will cross a daylight saving time boundary, in which case the offset will have changed.
See "Time Zone != Offset" in the timezone tag wiki. In moment, you can use time zones names like "America/Los_Angeles" with the moment-timezone add-on.
From your example, I'm not even sure if time zone even matters or not for your use case.
You should not convert the Date to a string just to parse it again. Moment can accept a Date object, assuming the Date object was created correctly.
moment(lastCheckInUTC.toString()).utcOffset(timeZoneDifference)
becomes
moment(lastCheckInUTC).utcOffset(timeZoneDifference)
Since Date.toString() returns a locale-specific, implementation-specific format, you'll also see you have a warning in the debug console from moment.
As for the rest, we can't run your program and reproduce the results, so there's not much we can do to help. You need to start by debugging your own program, and then try to reproduce your error in a Minimal, Complete, and Verifiable example. Chances are, you'll solve your own problem along the way. If not, then you will have something in a better state to share with us.
I am answering my own question as I have found the solution.
I had two questions. The first was "why do I get unexpected (incorrect) results" and I suspected that it was related to the way I am using timezones. I would see different results from day to day depending on what time I check in.
The problem is actually related to the way that moment().diff() works. Diff does not calculate "days" the way I expected it to. If I compare 2am today with 11pm yesterday diff will say 0 days because it is less than 24 hours diff. If I compare 1am on Thursday with 8pm on the previous Monday, diff will report 2 days - not 3 as I expected. It's a precision issue. Diff thinks 2.4 days is 2 days ago. But it is more than 2 therefor it is 3 days ago.
We found that the easiest solution is to compare the two dates at midnight rather than at the actual time of day that is recorded in the database. This yields correct results for days. The rest of the code is working fine.
//Find start time of today's day
var todayLocalizedStart = todayLocalized.startOf('day');
for (var i = 0; i < checkins.length; i++) {
var checkinDaylocalized = moment(checkins[i].get("checkInDate")).utcOffset(timeZoneDifference);
//Find start time of checkIn day
var checkinDaylocalizedStart = checkinDaylocalized.startOf('day');
//Find number of days
var daysago = todayLocalizedStart.diff(checkinDaylocalizedStart, 'days');
// console.log("daysago = " + daysago);
days_array.push(daysago);
}
The second question I had was "is it normal to not see every console.log at runtime". I've talked with other Parse.com users and they report that Parse is inconsistent in logging. I was spending a lot of time debugging "problems" that were simply Parse not logging correctly.
Thanks to everyone that contributed to the answer.
I did make one other change - but it was not a bug. I replaced the query limit from 30 days in the past to simply "30". It's just a bit simpler with one less calculation.
Related
I am creating a Discord bot with node.js and discord.js, and there's a feature that allows users to vote thanks to a command, but I'd like them to vote only once a week.
The issue is that, on this Discord, weeks start on Friday and end on Thursday, therefore I can't simply write :
var weekNow = moment().week();
var weekLastVote = moment(dateLastVote).week();
if (weekNow == weekLastVote){
//Prevent from voting again
} else {
//Let the user vote
}
Therefore, I have written some code that seems to work, but I'd like your opinion on it as it seems very sloppy and I'm not sure if I have taken into account all of the possibilities (I don't know if I need to use my month variables for example):
module.exports = {
isSameWeek: function (dateLastVote) {
// moments for today's date
var dayNow = moment().weekday();
var weekNow = moment().week();
var monthNow = moment().month();
var yearNow = moment().year();
var dateNow = moment().format('MMDDYYYY'); // moment without hours/minutes/seconds
// moments for last vote's date
var dayLastVote = moment(dateLastVote).weekday();
var weekLastVote = moment(dateLastVote).week();
var monthLastVote = moment(dateLastVote).month();
var yearLastVote = moment(dateLastVote).year();
var dateLastVote = moment(dateLastVote).format('MMDDYYYY'); // moment without hours/minutes/seconds
if ((yearNow === yearLastVote && weekNow === weekLastVote && dayLastVote < 5) || // 5 = Friday, starting day of the week (a week = Friday to thursday)
(yearNow === yearLastVote && weekNow - 1 === weekLastVote && dayLastVote >= 5 && dayNow < 5) ||
(dateNow === dateLastVote)
){
return true;
} else {
return false;
}
}
};
As I said, this seems do to the trick but I would like someone else's opinion on it to be sure there isn't a simpler way or, if there isn't, if I haven't forgotten anything.
Thank you for reading :)
I do not know how our approaches compare to each other in matter of performance, but I still wanna show my approach on the problem:
function isSameWeek(firstDay, secondDay, offset) {
var firstMoment = moment(firstDay);
var secondMoment = moment(secondDay);
var startOfWeek = function (_moment, _offset) {
return _moment.add("days", _moment.weekday() * -1 + (_moment.weekday() >= 7 + _offset ? 7 + _offset : _offset));
}
return startOfWeek(firstMoment, offset).isSame(startOfWeek(secondMoment, offset), "day");
}
What the solution does is calculating the start of the week of each of the given dates in respect to the offset (for values >= -7 and <= 0) and returning whether both have the same start of the week. Same start of the week = same week.
All you have to do is call the function passing two date objects (or moment objects) and an offset between -7 and 0, depending on how the week is shifted in relation to a "regular" week.
I think that the best way to do want you need is to tell moment that your week starts on Friday. You can simply use updateLocale method customizing dow (day of week) key of the week object and then use your first code snippet. See Customize section of the docs to get more info about locale customization.
Here a live example of setting a custom day as first day of the week and then using your code to check if a given day is in the current week:
moment.updateLocale('en', {
week: {
dow : 5, // Friday is the first day of the week.
}
});
function checkWeek(dateLastVote){
var weekNow = moment().week();
var weekLastVote = moment(dateLastVote).week();
if (weekNow == weekLastVote){
//Prevent from voting again
console.log(moment(dateLastVote).format('YYYY-MM-DD') + ' is in the current week')
} else {
//Let the user vote
console.log(moment(dateLastVote).format('YYYY-MM-DD') + ' is NOT in the current week')
}
}
checkWeek('2017-05-30'); // same week mon-sun, but previous week fri-thu
checkWeek('2017-06-01'); // same week mon-sun, but previous week fri-thu
checkWeek('2017-06-08'); // next week mon-sun, but current week fri-thu
// First day of the current week
console.log(moment().startOf('week').format('YYYY-MM-DD'));
// Last day of the current week
console.log(moment().endOf('week').format('YYYY-MM-DD'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
EDIT An improved solution is to use moment isSame passing 'week' as second parameter. As the docs states:
Check if a moment is the same as another moment.
If you want to limit the granularity to a unit other than milliseconds, pass it as the second parameter.
Here a live sample:
moment.updateLocale('en', {
week: {
dow : 5, // Friday is the first day of the week.
}
});
function isSameWeek(dateLastVote){
var now = moment();
var lastVote = moment(dateLastVote);
if (now.isSame(lastVote, 'week')){
//Prevent from voting again
console.log(moment(dateLastVote).format('YYYY-MM-DD') + ' is in the current week')
} else {
//Let the user vote
console.log(moment(dateLastVote).format('YYYY-MM-DD') + ' is NOT in the current week')
}
}
isSameWeek('2017-06-10'); // same week mon-sun, but next week fri-thu
isSameWeek('2017-06-03'); // previous week mon-sun, but current week fri-thu
isSameWeek('2017-06-06'); // current week both mon-sun and fri-thu
// First day of the current week
console.log(moment().startOf('week').format('YYYY-MM-DD'));
// Last day of the current week
console.log(moment().endOf('week').format('YYYY-MM-DD'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
According to the Moment docs you can set the ISO start of the week:
moment().isoWeekday(1); // Monday
moment().isoWeekday(7); // Sunday
then you can use the same functionality to check if the days are in the same week of the year.
Take a look:
https://momentjs.com/docs/#/get-set/iso-weekday/
I am a systems guy, so code isn't really my forte. I'm building a new front-end for our in-house ticketing system. I pull the tickets from the database as objects and iterate over them. I have it set up so that each ticket object has an "updates" property which is an array of objects describing each time it was interacted with. I use the type of interaction to determine if the timer should be effected, and if so, if it should be started or stopped.
User creates a ticket -> timer starts
Tech1 responds to the ticket asking for more information -> timer stops
User responds -> timer starts again
Tech1 escalates ticket to tech2 -> no change in timer status
Tech2 resolves issue and closes ticket -> timer stops
I've been able to get it to work with a single start/stop event (open/close, nothing in-between) per ticket, including subtracting non-business hours (nights, weekends, and holidays), but I'm having trouble coming up with a clean way to deal with multiple start/stops per ticket.
The main thing throwing a wrench in things is validating the input. For example, a user might submit a response (triggering a start event), then realize they forgot to add something and submit a second response (triggering another start event) before a tech responds.
I'd appreciate any thoughts on how to go about this or about what I can do to improve my existing code, I'm sure it is far less than optimal.
Here is an example of a ticket object:
{
"status":"closed",
"id":13137,
"importance":4,
"type":1,
"owner":"User1",
"category":"User Accounts",
"cc":[
"User2",
"User3"
],
"updates":[
{
"time":1393264332000,
"updator":"Tech1",
"event":"techClose",
"detail":"Done. Let us know if you need anything else."
},
{
"time":1393261496000,
"updator":"Tech2",
"event":"assigned",
"detail":"Tech1"
},
{
"time":1393030776000,
"updator":"User1",
"event":"clientReply",
"detail":"Actually, nevermind, just remove them from the groups."
},
{
"time":1393030632000,
"updator":"User1",
"event":"clientReply",
"detail":"Can you give me a list and I'll let ya know what to do with each?"
},
{
"time":1393002994000,
"updator":"Tech2",
"event":"techResponse",
"detail":"These accounts are disabled, they are still in the lists because the accounts still exist, but they are not able to be logged in to or accessed in any other way. It is standard practice to leave the accounts exactly as they were, but in a disabled state just in case you need to access them at a later date because any other changes cannot be reversed. If you'd prefer, we can remove them from the groups associated with those distribution lists or delete the accounts entirely."
},
{
"time":1392991707000,
"updator":"User1",
"event":"userCreate",
"detail":"Hey. Can you revise the All employee and other staff email groups to delete the people that are no longer here? Thx."
}
]
}
Here is how the above might be represented when passed into the function:
start = [1392991707000,1393030632000,1393030776000];
stop = [1393002994000,1393264332000];
And here is the function I am currently using (I'm not passing in arrays, just single ints at the moment):
function timeCalc(start, end){
// Array of non-working days (eg: weekends) in numerical weekday format starting with Sunday=0 and ending with Saturday=6
var daysOff = new Array(0,6);
// Array of non-working dates (eg: holidays) in any valid date format, such as: "October 13, 1975" or "75,10,13"
var holidays = new Array("2014,02,17","2014,05,26","2014,07,04","2014,08,01","2014,11,11","2014,11,27","2014,11,28","2014,12,25");
// Two arrays defining business hours in 24hour time. busHoursStart contains all starting times, busHoursEnd contains all ending times. So if you work 9-5 with a lunch break from 12-1, put "9,13" in start and "12,17" in end.
/*var busHoursStart = new Array("9");
var busHoursEnd = new Array("17");*/
// Just using start and end time, no array for now
var busHoursStart = 9;
var busHoursEnd = 17;
// Define other variables (no need to configure)
var datesBetween = new Array();
var oneDay = 24*60*60*1000;
var busHoursPerDay = (busHoursEnd - busHoursStart);
var subtractDays = 0;
// Create array of JS timestamps (UNIX time in milliseconds) representing each full day between the start and end dates (including the end date)
var dateDiff = Math.round(Math.abs(start.getTime() - end.getTime())/(oneDay));
for (var i=0; i<dateDiff; i++){
datesBetween.push(new Date(start.getTime()+(i*oneDay)));
}
// Remove days off
var noWeekends=$.grep(datesBetween, function(wDate){
var wFail;
$.each(daysOff,function(wKey,wVal){
if(wDate.getDay() == wVal) wFail=wDate;
});
return (wDate != wFail);
});
// Remove holidays
var noHolidays=$.grep(noWeekends, function(hDate){
var hFail;
$.each(holidays,function(hKey,hVal){
if(hDate.toDateString() == new Date(hVal).toDateString()) hFail=hDate;
});
return (hDate != hFail);
});
var days=(noHolidays.length - 1);
// This should be made into a function
// If start event is before business hours, set start time to when business hours start
if(start.getHours() < busHoursStart){
var startTime = busHoursStart;
}
// If start event is after business hours, set start time to when the next day's business hours start by adding a day to the counter
else if(start.getHours() >= busHoursEnd){
var startTime = busHoursStart;
subtractDays++;
}
else var startTime = start.getHours();
// If end is before business hours, set end time to when business hours start
if(end.getHours() < busHoursStart){
var endTime = busHoursStart;
}
// If end is after business hours, set end time to to when the next day's business hours start by adding a day to the counter
else if(end.getHours() >= busHoursEnd){
var endTime = busHoursStart;
subtractDays++;
}
else var endTime = end.getHours();
// If the end time is a later hour than the start time, add up hours from full days then add hours between start and end
if(endTime >= startTime){
var hours = (days - subtractDays) * busHoursPerDay) + (endTime - startTime);
}
// If the end time is an earlier hour than the start time, add up hours from full days then get hours between start and close time and hours between open time and end time, add them together and subtract a full day worth of time
else {
var hours = ((days - subtractDays) * busHoursPerDay) + ((busHoursEnd - startTime) + (endTime - busHoursStart));
}
console.log("Days: "+days+" Hours: "+hours);
return hours;
}
This is for a system that essentially allows you to set the first date for a given event, then to set the recurrence period.
Eg. I set a date for a week from now, 19/07/2012, so I know that I have to put the cat out with the milk. I also set it to be a weekly notification, so in future weeks I want to be notified of the same.
That original date sits in my database, which is fine for week 1, but in week 2 I need to return the date as the original plus 1 week.
On the face of it, that may seem straightforward, but I need to make sure I can account for leap years and different recurrence frequencies (fortnightly, monthly, yearly, whatever).
I'd like to keep this as a javascript implementation - because it's quicker and I feel probably would require less code than updating dates in the database. Maybe it's not achievable, any pointers would be excellent.
I think these may be a starting point:
Given a start date , how to calculate number of years till current date in javascript
Given a date, how can I efficiently calculate the next date in a given sequence (weekly, monthly, annually)?
Update, I've written the below to return the amount of time to add in each different case, from there I can just use the answer below:
var strDate = $(this).find('.next').text();
var frequency = $(this).find('.occurs').text();
var frmDate = getDateObject(strDate);
var toDate = new Date();
var days = parseInt(Math.floor((frmDate - toDate) / 86400000));
if(days < 0) {
// find out how many WHOLE 'frequencies' have passed
var weeks = Math.ceil(0 - (days / 7));
var months = Math.ceil(0 - (monthDiff(toDate,frmDate)));
var years = Math.ceil(months / 12);
//alert(days + '/' + weeks + '/' + fortnights + '/' + months + '/' + quarters + '/' + years);
if(frequency == 'Weekly') { frmDate.add(weeks).weeks(); }
if(frequency == 'Fortnightly') { frmDate.add(weeks*2).weeks(); }
if(frequency == 'Monthly') { frmDate.add(months).months(); }
if(frequency == 'Quarterly') { frmDate.add(months*3).months(); }
if(frequency == 'Annually') { frmDate.add(years).years(); }
var newdate = frmDate.toString("dd/MM/yyyy");
//alert(newdate);
$(this).find('.next').text(newdate);
}
Also, the SQL implementation for this would be using DATEADD:
http://sql-plsql.blogspot.com/2010/07/dateadd.html
You don't have to worry about special dates like leap year and so forth, because most Date functions take care of that.
Alternatively, you can use the getDate(), getMonth() as the other user suggested.
var today = new Date();
today.setDate(today.getDate() + numberOfDaysToAdd);
What I would do (probably not the best solution, I'm just coming up with it right now) is to start from the initial date and use a loop: while the date you are observing is less than the current date, increment the observed date by a week (fortnight, month, year etc.). If you land on the current date, the event happens. Otherwise it's for another day.
You can use things like date.setDate(date.getDate()+1); to increment the date by a day, the same +7 for a week, using set/getMonth and set/getFullYear for months and years respectively. If you give a value out of bounds, JS will wrap it (so March 32nd becomes April 1st)
Please check out the following code for some raw idea
var someDate = new Date();
for(var i = 0 ; i < 7 ; i++)
{
someDate.setDate(someDate.getDate() + 1);
console.log(someDate)
}
You can test the same in the below fiddle
Consecutive 7 days from current day
I am trying to create a simple script that gives me the next recycling date based on a biweekly schedule starting on Wed Jul 6, 2011. So I've created this simple function...
function getNextDate(startDate) {
if (today <= startDate) {
return startDate;
}
// calculate the day since the start date.
var totalDays = Math.ceil((today.getTime()-startDate.getTime())/(one_day));
// check to see if this day falls on a recycle day
var bumpDays = totalDays%14; // mod 14 -- pickup up every 14 days...
// pickup is today
if (bumpDays == 0) {
return today;
}
// return the closest day which is in 14 days, less the # of days since the last
// pick up..
var ms = today.getTime() + ((14- bumpDays) * one_day);
return new Date(ms);
}
and can call it like...
var today=new Date();
var one_day=1000*60*60*24; // one day in milliseconds
var nextDate = getNextDate(new Date(2011,06,06));
so far so good... but when I project "today" to 10/27/2011, I get Tuesday 11/8/2011 as the next date instead of Wednesday 11/9/2011... In fact every day from now thru 10/26/2011 projects the correct pick-up... and every date from 10/27/2011 thru 2/28/2012 projects the Tuesday and not the Wednesday. And then every date from 2/29/2012 (leap year) thru 10/24/2012 (hmmm October again) projects the Wednesday correctly. What am I missing? Any help would be greatly appreciated..
V
The easiest way to do this is update the Date object using setDate. As the comments for this answer indicate this isn't officially part of the spec, but it is supported on all major browsers.
You should NEVER update a different Date object than the one you did the original getDate call on.
Sample implementation:
var incrementDate = function (date, amount) {
var tmpDate = new Date(date);
tmpDate.setDate(tmpDate.getDate() + amount)
return tmpDate;
};
If you're trying to increment a date, please use this function. It will accept both positive and negative values. It also guarantees that the used date objects isn't changed. This should prevent any error which can occur if you don't expect the update to change the value of the object.
Incorrect usage:
var startDate = new Date('2013-11-01T11:00:00');
var a = new Date();
a.setDate(startDate.getDate() + 14)
This will update the "date" value for startDate with 14 days based on the value of a. Because the value of a is not the same is the previously defined startDate it's possible to get a wrong value.
Expanding on Exellian's answer, if you want to calculate any period in the future (in my case, for the next pay date), you can do a simple loop:
var today = new Date();
var basePayDate = new Date(2012, 9, 23, 0, 0, 0, 0);
while (basePayDate < today) {
basePayDate.setDate(basePayDate.getDate()+14);
}
var nextPayDate = new Date(basePayDate.getTime());
basePayDate.setDate(nextPayDate.getDate()-14);
document.writeln("<p>Previous pay Date: " + basePayDate.toString());
document.writeln("<p>Current Date: " + today.toString());
document.writeln("<p>Next pay Date: " + nextPayDate.toString());
This won't hit odd problems, assuming the core date services work as expected. I have to admit, I didn't test it out to many years into the future...
Note: I had a similar issue; I wanted to create an array of dates on a weekly basis, ie., start date 10/23/2011 and go for 12 weeks. My code was more or less this:
var myDate = new Date(Date.parse(document.eventForm.startDate.value));
var toDate = new Date(myDate);
var week = 60 * 60 * 24 * 7 * 1000;
var milliseconds = toDate.getTime();
dateArray[0] = myDate.format('m/d/Y');
for (var count = 1; count < numberOccurrences; count++) {
milliseconds += week;
toDate.setTime(milliseconds);
dateArray[count] = toDate.format('m/d/Y');
}
Because I didn't specify the time and I live in the US, my default time was midnight, so when I crossed the daylight savings time border, I moved into the previous day. Yuck. I resolved it by setting my time of day to noon before I did my week calculation.
I have 2 questions about dates.
The first one is how can I get the "AM/PM" from a date in Javascript?
the second question is say I have this code
var convertedStartDate = new Date(dueDate);
var month = convertedStartDate.getMonth() + 1;
var day = convertedStartDate.getDate();
var year = convertedStartDate.getFullYear();
var shortDueDate = month + "/" + day + "/" + year;
Now as you can see I want always this format mm/dd/yyyy
So I am wondering if say dueDate is 1/9/2010 (mm/dd/yyyy) but the person entered it in as dd/mm/yyyy(some other format version of date).
would
month = 1
day = 9
year = 2010
Or do I have to tell it somehow to always convert into mm/dd/yyyy? Or does it do is own format so that it always would get the right order? Ie it does not matter what order they put the date in it would always get 9 as the day.
Here, give this a try:
now = new Date();
hour = now.getHours();
var tag = "";
if (hour >= 12) {
tag = "pm";
} else {
tag = "am";
}
As for the second part of your question, I'd just make those parts of the form separate fields, there really is no way otherwise. You're just going to have to write some hints into your form.
You need to always turn/convert whatever the user entered into a Javascript Date object. Remember - Javascript is local to the client's computer... a person in the USA will have different format settings than a person in the UK or China.
To keep things simple... suggest or present a hint near the input textbox the desired input format. Then, validate against that format using a Regex. This way you are almost guaranteed to get the desired date... well... unless the user has Javascript disabled. LOL... in that case... you need to convert on the server-side (you should always be doing this anyway).
To get the AM/PM of a time found some old code I wrote a long time ago. See the (remove am/pm) here you can replace it with a get using the substring.
function ValidateAdvancedTime(time, formatType){
time = time.replace(".", ":");
var newTime = time.substring(0, (time.indexOf(":") + 3)); // Strip out the seconds
var status = ValidateTime(newTime, formatType);
if(status == false)
return false;
var seconds = time.substring(time.indexOf(":") + 4, time.length);
if(seconds.length > 2)
**seconds = seconds.substring(0, 2); // Remove any AM/PM afterwards**
if(!isNaN(seconds)) { // Make sure its a number and it's between 0 and 59
if((seconds <= 59) && (seconds >= 0))
return true;
}
return false;
}
As far as the dates go I've never had any problems storing 1/9/2010 or 01/09/2010 in the database.