函数迭代日期数组会产生意想不到的结果
Function to iterate over an array of dates produces unexpected results
我有一个从我的iOS应用程序调用的CloudCode函数。该函数应该创建一个"签入"记录,并返回一个字符串来表示最近30天的签入和错过的天数。
奇怪的是,有时我得到预期的结果,有时却没有。这让我认为我使用的时区可能有一些问题——因为这可能会导致一组不同的"过去的日子",这取决于我运行这个函数的时间和我过去签到的时间。但是我很困惑,需要一些帮助。
让我感到困惑的是,我没有看到所有的console.log()结果出现在解析日志中。这正常吗??例如,在For循环中,我可以取消console.log条目的注释并调用该函数,但我不会看到过去列出的所有日期-但它们包含在最终数组和文本字符串中。
这是我的完整函数。任何帮助和建议都是非常感谢的。
/* 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.");
}
});
});
你的问题涉及的内容太多了,无法充分回答,但我会很好,至少指出一些你应该注意的错误:
-
您以固定偏移量的形式输入,但是您所做的操作减去30天。你完全有可能会跨越夏令时边界,在这种情况下,偏移量将会改变。
请参阅时区标签wiki中的"Time Zone != Offset"。在moment,您可以使用像
"America/Los_Angeles"
这样的时区名称和moment-timezone附加组件。从你的例子中,我甚至不确定时区是否对你的用例很重要。
-
您不应该将
Date
转换为字符串只是为了再次解析它。Moment可以接受Date
对象,前提是Date
对象被正确创建。moment(lastCheckInUTC.toString()).utcOffset(timeZoneDifference)
是
moment(lastCheckInUTC).utcOffset(timeZoneDifference)
由于
Date.toString()
返回特定于语言环境、特定于实现的格式,因此您还将在调试控制台中看到一个警告。
至于其余的,我们不能运行你的程序并重现结果,所以我们无能为力。您需要从调试自己的程序开始,然后尝试在最小化、完整和可验证的示例中重现您的错误。很有可能,你会在这个过程中解决自己的问题。如果没有,那么你将有更好的状态与我们分享。
我在回答我自己的问题,因为我已经找到了解决方案。
我有两个问题。第一个问题是"为什么我会得到意想不到的(不正确的)结果",我怀疑这与我使用时区的方式有关。我每天都会看到不同的结果,这取决于我签到的时间。问题实际上与moment().diff()的工作方式有关。Diff并没有像我期望的那样计算"天数"。如果我比较今天凌晨2点和昨天晚上11点,diff会说0天,因为它不到24小时。如果我比较周四凌晨1点和前周一晚上8点,diff会报告2天,而不是我预期的3天。这是一个精度问题。Diff认为2.4天是2天前。
我们发现最简单的解决方案是在午夜比较两个日期,而不是在数据库中记录的实际时间。这将在数天内产生正确的结果。其余的代码运行良好。
//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);
}
我的第二个问题是"在运行时看不到每个console.log是否正常"。我与其他Parse.com用户交谈过,他们报告说Parse在日志记录方面不一致。我花了很多时间调试"问题",只是解析没有正确记录日志。
感谢每个为这个答案做出贡献的人。
我确实做了另一个更改-但它不是一个错误。我将查询限制从过去的30天改为简单的"30"。它只是简单了一点,少了一个计算。
- JavaScript 模块模式给出了意想不到的结果
- 使用 jQuery 从 HTML 获取 id,给出意想不到的结果
- Javascript for loop产生意想不到的结果
- Javascript计算产生意想不到的结果
- 比较 JavaScript 中的浮点数会产生意想不到的结果
- 弄乱jquery产生了意想不到的结果
- 当元素相等时,Array.sort() 会产生意想不到的结果
- 在JavaScript中循环获得意想不到的结果
- Math.round与Math.ceil相比会产生意想不到的结果
- CSS旋转和平移转换会产生意想不到的结果
- 在Javascript函数中传递参数产生意想不到的结果
- 函数迭代日期数组会产生意想不到的结果
- 重新启动函数会产生意想不到的结果
- CSS为jQuery页面生成与意想不到的结果
- json字符串中的数组长度在angular ng-repeat中返回意想不到的结果
- Javascript:从对象方法内部调用回调函数,产生意想不到的结果
- Javascript: Async嵌套函数在其他Async函数中会产生意想不到的结果
- Raphael-使用transform缩放js路径-意想不到的结果
- 使用窗口.反复打开会产生意想不到的结果
- Lodash isUndefined提供了意想不到的结果