node.js require() 奇怪的行为
node.js require() weird behavior
更新 - 我添加了更多信息。该问题似乎取决于 require 语句的顺序。不幸的是,修复一个会破坏另一个,请帮忙!!!
我不知道这里发生了什么。我有 5 个文件如下所示,我使用 index.js 只是为了演示这个问题。如果首先需要 HDate,则 HDate.make 在 HDateTime class 内失败。如果首先需要 HDateTime,则 HDateTime.make 在 HDate class 中失败。看来这可能是一个循环引用问题,但我一直无法弄清楚如何解决这个问题。
index.js - 在 DATETIME
失败
var HDate = require('./HDate');
var HDateTime = require('./HDateTime');
var HTimeZone = require('./HTimeZone');
var utc = HTimeZone.UTC;
console.log('DATE: ' + HDate.make(2011, 1, 2));
console.log('MIDNIGHT: ' + HDate.make(2011, 1, 2).midnight(utc));
console.log('DATETIME: ' + HDateTime.make(2011, 1, 2, 3, 4, 5, utc, 0));
ERROR OUTPUT:
C:\Users\Shawn\nodehaystack>node index.js
DATE: 2011-01-02
MIDNIGHT: 2011-01-02T00:00:00Z UTC
C:\Users\Shawn\nodehaystack\HDateTime.js:95
return HDateTime.make(HDate.make(arg1, arg2, arg3), HTime.make(arg4, arg5,
^
TypeError: undefined is not a function
at Function.HDateTime.make (C:\Users\Shawn\nodehaystack\HDateTime.js:95:33)
at Object.<anonymous> (C:\Users\Shawn\nodehaystack\index.js:8:38)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Function.Module.runMain (module.js:501:10)
at startup (node.js:129:16)
at node.js:814:3
index.js - 在午夜失败
var HDateTime = require('./HDateTime');
var HDate = require('./HDate');
var HTimeZone = require('./HTimeZone');
var utc = HTimeZone.UTC;
console.log('DATE: ' + HDate.make(2011, 1, 2));
console.log('DATETIME: ' + HDateTime.make(2011, 1, 2, 3, 4, 5, utc, 0));
console.log('MIDNIGHT: ' + HDate.make(2011, 1, 2).midnight(utc));
ERROR OUTPUT:
C:\Users\Shawn\nodehaystack>node index.js
DATE: 2011-01-02
DATETIME: 2011-01-02T03:04:05Z UTC
C:\Users\Shawn\nodehaystack\HDate.js:46
HDate.prototype.midnight = function(tz) { return HDateTime.make(this, HTime.MI
^
TypeError: undefined is not a function
at HVal.HDate.midnight (C:\Users\Shawn\nodehaystack\HDate.js:46:60)
at Object.<anonymous> (C:\Users\Shawn\nodehaystack\index.js:8:51)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Function.Module.runMain (module.js:501:10)
at startup (node.js:129:16)
at node.js:814:3
HDate.js
var HVal = require('./HVal');
var HDateTime = require('./HDateTime');
var HTime = require('./HTime');
/** Private constructor */
function HDate(year, month, day) {
/** Four digit year such as 2011 */
this.year = year;
/** Month as 1-12 (Jan is 1, Dec is 12) */
this.month = month;
/** Day of month as 1-31 */
this.day = day;
};
HDate.prototype = Object.create(HVal.prototype);
/** int - Hash is based on year, month, day */
HDate.prototype.hashCode = function() { return (this.year << 16) ^ (this.month << 8) ^ this.day; };
/** String - Encode as "YYYY-MM-DD" */
HDate.prototype.toZinc = function() {
var s = this.year + "-";
if (this.month < 10) s += "0"; s += this.month + "-";
if (this.day < 10) s += "0"; s += this.day;
return s;
};
/** boolean - Equals is based on year, month, day */
HDate.prototype.equals = function(that) { return that instanceof HDate && this.year === that.year && this.month === that.month && this.day === that.day; };
/** int - Return sort order as negative, 0, or positive */
HDate.prototype.compareTo = function(that) {
if (this.year < that.year) return -1; else if (this.year > that.year) return 1;
if (this.month < that.month) return -1; else if (this.month > that.month) return 1;
if (this.day < that.day) return -1; else if (this.day > that.day) return 1;
return 0;
};
/** Convert this date into HDateTime for midnight in given timezone. */
HDate.prototype.midnight = function(tz) { return HDateTime.make(this, HTime.MIDNIGHT, tz); };
/** Return date in future given number of days */
HDate.prototype.plusDays = function(numDays) {
if (numDays == 0) return this;
if (numDays < 0) return this.minusDays(-numDays);
var year = this.year;
var month = this.month;
var day = this.day;
for (; numDays > 0; --numDays) {
day++;
if (day > HDate.daysInMonth(year, month)) {
day = 1;
month++;
if (month > 12) { month = 1; year++; }
}
}
return HDate.make(year, month, day);
};
/** Return date in past given number of days */
HDate.prototype.minusDays = function(numDays) {
if (numDays == 0) return this;
if (numDays < 0) return this.plusDays(-numDays);
var year = this.year;
var month = this.month;
var day = this.day;
for (; numDays > 0; --numDays) {
day--;
if (day <= 0) {
month--;
if (month < 1) { month = 12; year--; }
day = HDate.daysInMonth(year, month);
}
}
return HDate.make(year, month, day);
};
/** Return day of week: Sunday is 1, Saturday is 7 */
HDate.prototype.weekday = function() { return new Date(this.year, this.month-1, this.day).getDay() + 1; };
/** Export for use in testing */
module.exports = HDate;
/** Construct from basic fields, javascript Date instance, or String in format "YYYY-MM-DD" */
HDate.make = function(arg, month, day) {
if (arg instanceof Date) {
return new HDate(arg.getFullYear(), arg.getMonth() + 1, arg.getDate());
} else if (HVal.typeis(arg, 'string', String)) {
try {
var s = arg.split('-');
return new HDate(parseInt(s[0]), parseInt(s[1]), parseInt(s[2]));
} catch(err) {
throw err;
}
} else {
if (arg < 1900) throw new Error("Invalid year");
if (month < 1 || month > 12) throw new Error("Invalid month");
if (day < 1 || day > 31) throw new Error("Invalid day");
return new HDate(arg, month, day);
}
};
/** Get HDate for current time in default timezone */
HDate.today = function() { return HDateTime.now().date; };
/** Return if given year a leap year */
HDate.isLeapYear = function(year) {
if ((year & 3) != 0) return false;
return (year % 100 != 0) || (year % 400 == 0);
};
/** Return number of days in given year (xxxx) and month (1-12) */
var daysInMon = [ -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
var daysInMonLeap = [ -1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
HDate.daysInMonth = function(year, mon) { return HDate.isLeapYear(year) ? daysInMonLeap[mon] : daysInMon[mon]; };
HDateTime.js
var moment = require('moment-timezone');
var HVal = require('./HVal');
var HDate = require('./HDate');
var HTime = require('./HTime');
var HTimeZone = require('./HTimeZone');
// require('./io/HZincReader');
/** Private constructor */
function HDateTime(date, time, tz, tzOffset) {
/** HDate - Date component of the timestamp */
this.date = date;
/** HTime - Time component of the timestamp */
this.time = time;
/** int - Offset in seconds from UTC including DST offset */
this.tz = tz;
/** HTimeZone - Timezone as Olson database city name */
this.tzOffset = (typeof(tzOffset) == 'undefined' ? 0 : tzOffset);
/** long - millis since Epoch */
this.millis;
}
HDateTime.prototype = Object.create(HVal.prototype);
/** long - Get this date time as Java milliseconds since epoch */
HDateTime.prototype.millis = function() {
if (this.millis <= 0) {
var d = new Date(this.date.year, this.date.month-1, this.date.day, this.time.hour, this.time.min, this.time.sec, this.time.ms);
//TODO: implement UTC timezone
this.millis = d.getTime();
}
return this.millis;
};
/** int - Hash code is based on date, time, tzOffset, and tz */
HDateTime.prototype.hashCode = function() { return this.date.hashCode() ^ this.time.hashCode() ^ tzOffset ^ this.tz.hashCode(); };
/** String - Encode as "YYYY-MM-DD'T'hh:mm:ss.FFFz zzzz" */
HDateTime.prototype.toZinc = function() {
var s = this.date.toZinc() + "T" + this.time.toZinc();
if (this.tzOffset == 0) s += "Z";
else {
var offset = this.tzOffset;
if (offset < 0) { s += "-"; offset = -offset; }
else { s += "+"; }
var zh = offset / 3600;
var zm = (offset % 3600) / 60;
if (zh < 10) s += "0"; s += zh + ":";
if (zm < 10) s += "0"; s += zm;
}
s += " " + this.tz;
return s;
};
/** boolean - Equals is based on date, time, tzOffset, and tz */
HDateTime.prototype.equals = function(that) {
return that instanceof HDateTime && this.date === that.date
&& this.time === that.time && this.tzOffset === that.tzOffset && this.tz === that.tz;
};
/** int - Comparison based on millis. */
HDateTime.prototype.compareTo = function(that) {
var thisMillis = this.millis();
var thatMillis = that.millis();
if (thisMillis < thatMillis) return -1;
else if (thisMillis > thatMillis) return 1;
return 0;
};
/** Export for use in testing */
module.exports = HDateTime;
function utcDate(year, month, day, hour, min, sec, ms) {
var d = new Date();
d.setUTCFullYear(year);
d.setUTCMonth(month-1);
d.setUTCDate(day);
d.setUTCHours(hour);
d.setUTCMinutes(min);
d.setUTCSeconds(sec);
d.setUTCMilliseconds(ms);
return d;
}
/** Construct from various values */
HDateTime.make = function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) {
if (arg7 instanceof HTimeZone) {
return HDateTime.make(HDate.make(arg1, arg2, arg3), HTime.make(arg4, arg5, arg6), arg7, arg8);
} else if (arg6 instanceof HTimeZone) {
return HDateTime.make(HDate.make(arg1, arg2, arg3), HTime.make(arg4, arg5, 0), arg6, arg7);
} else if (arg3 instanceof HTimeZone) {
// use Date to decode millis to fields
var d = utcDate(arg1.year, arg1.month, arg1.day, arg2.hour, arg2.min, arg2.sec, arg2.ms);
var tzOffset = arg4;
if (typeof(tzOffset) == 'undefined') {
// convert to designated timezone
d = moment(d).tz(arg3.js.name);
tzOffset = d.utcOffset() * 60;
}
var ts = new HDateTime(arg1, arg2, arg3, tzOffset);
ts.millis = d.valueOf() + (tzOffset * -1000);
return ts;
} else if (this.typeis(arg1, 'string', String)) {
// TODO: Implemet parsing
// var val = new HZincReader(arg1).readScalar();
// if (typeof(val) == HDateTime) return val;
// throw new Error("Parse Error: " + arg1);
} else {
var tz = arg2;
if (typeof(tz) == 'undefined') tz = HTimeZone.DEFAULT;
var d = new Date(arg1);
// convert to designated timezone
d = moment(d).tz(tz.js.name);
tzOffset = d.utcOffset() * 60;
var ts = new HDateTime(HDate.make(d), HTime.make(d), tz, tz.getOffset());
ts.millis = arg1;
}
};
/** HDateTime - Get HDateTime for given timezone */
HDateTime.now = function(tz) { return make(new Date().getTime(), tz); };
HTime.js
var HVal = require('./HVal');
// require('./io/HZincReader');
/** Private constructor */
function HTime(hour, min, sec, ms) {
/** int - Hour of day as 0-23 */
this.hour = hour;
/** int - Minute of hour as 0-59 */
this.min = min;
/** int - Second of minute as 0-59 */
this.sec = (typeof(sec) == 'undefined' ? 0 : sec);
/** int - Fractional seconds in milliseconds 0-999 */
this.ms = (typeof(ms) == 'undefined' ? 0 : ms);
};
HTime.prototype = Object.create(HVal.prototype);
/** int - Hash code is based on hour, min, sec, ms */
HTime.prototype.hashCode = function() { return (this.hour << 24) ^ (this.min << 20) ^ (this.sec << 16) ^ this.ms; };
/** boolean - Equals is based on hour, min, sec, ms */
HTime.prototype.equals = function(that) {
return that instanceof HTime && this.hour === that.hour &&
this.min === that.min && this.sec === that.sec && this.ms === that.ms;
};
/** int - Return sort order as negative, 0, or positive */
HTime.prototype.compareTo = function(that) {
if (this.hour < that.hour) return -1; else if (this.hour > that.hour) return 1;
if (this.min < that.min) return -1; else if (this.min > that.min) return 1;
if (this.sec < that.sec) return -1; else if (this.sec > that.sec) return 1;
if (this.ms < that.ms) return -1; else if (this.ms > that.ms) return 1;
return 0;
};
/** String - Encode as "hh:mm:ss.FFF" */
HTime.prototype.toZinc = function() {
var s = "";
if (this.hour < 10) s += "0"; s += this.hour + ":";
if (this.min < 10) s += "0"; s += this.min + ":";
if (this.sec < 10) s += "0"; s += this.sec;
if (this.ms != 0) {
s += ".";
if (this.ms < 10) s += "0";
if (this.ms < 100) s += "0";
s += this.ms;
}
return s;
};
/** Export for use in testing */
module.exports = HTime;
/** Singleton for midnight 00:00 */
HTime.MIDNIGHT = new HTime(0, 0, 0, 0);
/** Construct with all fields, with Javascript Date object, or Parse from string fomat "hh:mm:ss.FF" */
HTime.make = function(arg1, min, sec, ms) {
if (HVal.typeis(arg1, 'string', String)) {
// TODO: Implemet parsing
// var val = new HZincReader(arg1).readScalar();
// if (val instanceof HTime) return val;
// throw new Error("Parse Error: " + arg1);
} else if (arg1 instanceof Date) {
return new HTime(arg1.getHours(), arg1.getMinutes(), arg1.getSeconds(), arg1.getMilliseconds());
} else {
return new HTime(arg1, min, sec, ms);
}
};
HTimeZone.js
var moment = require('moment-timezone');
var HVal = require('./HVal');
/** Package private constructor */
function HTimeZone(name, js) {
/** Haystack timezone name */
this.name = name;
/** Javascript (moment) representation of this timezone. */
this.js = js;
};
/** String - Return Haystack timezone name */
HTimeZone.prototype.toString = function() { return this.name; };
module.exports = HTimeZone;
HTimeZone.make = function(arg1, checked) {
if (typeof(checked) == 'undefined') return HTimeZone.make(arg1, true);
if (HVal.typeis(arg1, 'string', String)) {
/**
* Construct with Haystack timezone name, raise exception or
* return null on error based on check flag.
*/
// lookup in cache
var tz = cache[arg1];
if (typeof(tz) != 'undefined') return tz;
// map haystack id to Javascript full id
var jsId = toJS[arg1];
if (typeof(jsId) == 'undefined') {
if (checked) throw new Error("Unknown tz: " + arg1);
return undefined;
}
// resolve full id to HTimeZone and cache
var js = moment.tz.zone(jsId)
tz = new HTimeZone(arg1, js);
cache[arg1] = tz;
return tz;
} else {
/**
* Construct from Javascript timezone. Throw exception or return
* null based on checked flag.
*/
var jsId = arg1.name;
if (jsId.startsWith("GMT")) fixGMT(jsId);
var name = fromJS[jsId];
if (typeof(name) != 'undefined') return HTimeZone.make(name);
if (checked) throw new Error("Invalid Java timezone: " + arg1.name);
return;
}
};
function fixGMT(jsId) {
// Javscript (moment) IDs can be in the form "GMT[+,-]h" as well as
// "GMT", and "GMT0". In that case, convert the ID to "Etc/GMT[+,-]h".
// V8 uses the format "GMT[+,-]hh00 (used for default timezone), this also
// needs converted.
if (jsId.indexOf("+")<0 && jsId.indexOf("-")<0) return "Etc/" + jsId; // must be "GMT" or "GMT0" which are fine
// get the prefix
var pre = jsId.substring(0, 4);
var num = parseInt(jsId.substring(4, 6));
// ensure we have a valid value
if ((pre.substring(3)=="+" && num<13) || (pre.substring(3)=="-" & num<15)) return "Etc/" + pre + num;
// nothing we could do, return what was passed
return jsId;
}
var cache = {};
var toJS = {};
var fromJS = {};
{
try {
// only time zones which start with these
// regions are considered valid timezones
var regions = {};
regions["Africa"] = "ok";
regions["America"] = "ok";
regions["Antarctica"] = "ok";
regions["Asia"] = "ok";
regions["Atlantic"] = "ok";
regions["Australia"] = "ok";
regions["Etc"] = "ok";
regions["Europe"] = "ok";
regions["Indian"] = "ok";
regions["Pacific"] = "ok";
// iterate Javascript timezone IDs available
var ids = moment.tz.names();
for (var i=0; i<ids.length; ++i) {
var js = ids[i];
// skip ids not formatted as Region/City
var slash = js.indexOf('/');
if (slash < 0) continue;
var region = js.substring(0, slash);
if (typeof(regions[region]) == 'undefined') continue;
// get city name as haystack id
slash = js.lastIndexOf('/');
var haystack = js.substring(slash+1);
// store mapping b/w Javascript <-> Haystack
toJS[haystack] = js;
fromJS[js] = haystack;
}
} catch (err) {
console.log(err.stack);
}
var utc;
try {
utc = HTimeZone.make(moment.tz.zone("Etc/UTC"));
} catch (err) {
console.log(err.stack);
}
var def;
try {
// check if configured with system property
var defName = process.env["haystack.tz"];
if (defName != null) {
def = make(defName, false);
if (typeof(def) == 'undefined') console.log("WARN: invalid haystack.tz system property: " + defName);
}
// if we still don't have a default, try to use Javascript's
if (typeof(def) == 'undefined') {
var date = new Date().toString();
var gmtStart = date.indexOf("GMT");
var gmtEnd = date.indexOf(" ", gmtStart);
def = fromJS[fixGMT(date.substring(gmtStart, gmtEnd))];
}
} catch (err) {
console.log(err.stack);
def = utc;
}
/** UTC timezone */
HTimeZone.UTC = utc;
/** Default timezone for VM */
HTimeZone.DEFAULT = def;
}
问题在于如何导出函数。
你说
module.exports = HDate
这很好
但后来你做了
module.exports.make = function(){}
这也很好。
但是两者一起工作不会像您刚刚体验的那样可靠。
要修复该问题,可以将 class 导出为 module.exports.hdate
或将 make
函数添加到 HDate
:
Hdate.make = myFunction() {}
这显然适用于您像这样导出的所有方法。
如果这能解决您的问题,请告诉我:)
我发现这个问题的最佳解决方案是移动所有可能导致循环依赖的 require 语句,以确保它们在我的 class 的构造函数之后和 module.exports 之后陈述。例如...
var HVal = require('./HVal');
/**
* HDate models a date (day in year) tag value.
* @see {@link http://project-haystack.org/doc/TagModel#tagKinds|Project Haystack}
*
* @constructor
* @private
* @extends {HVal}
* @param {int} year - Four digit year such as 2011
* @param {int} month - Month as 1-12 (Jan is 1, Dec is 12)
* @param {int} day - Day of month as 1-31
*/
function HDate(year, month, day) {
this.year = year;
this.month = month;
this.day = day;
}
HDate.prototype = Object.create(HVal.prototype);
module.exports = HDate;
var HDateTime = require('./HDateTime'),
HTime = require('./HTime');
在我的所有 classes 中始终如一地执行此方法可以防止任何周期性问题。
更新 - 我添加了更多信息。该问题似乎取决于 require 语句的顺序。不幸的是,修复一个会破坏另一个,请帮忙!!!
我不知道这里发生了什么。我有 5 个文件如下所示,我使用 index.js 只是为了演示这个问题。如果首先需要 HDate,则 HDate.make 在 HDateTime class 内失败。如果首先需要 HDateTime,则 HDateTime.make 在 HDate class 中失败。看来这可能是一个循环引用问题,但我一直无法弄清楚如何解决这个问题。
index.js - 在 DATETIME
失败var HDate = require('./HDate');
var HDateTime = require('./HDateTime');
var HTimeZone = require('./HTimeZone');
var utc = HTimeZone.UTC;
console.log('DATE: ' + HDate.make(2011, 1, 2));
console.log('MIDNIGHT: ' + HDate.make(2011, 1, 2).midnight(utc));
console.log('DATETIME: ' + HDateTime.make(2011, 1, 2, 3, 4, 5, utc, 0));
ERROR OUTPUT:
C:\Users\Shawn\nodehaystack>node index.js
DATE: 2011-01-02
MIDNIGHT: 2011-01-02T00:00:00Z UTC
C:\Users\Shawn\nodehaystack\HDateTime.js:95
return HDateTime.make(HDate.make(arg1, arg2, arg3), HTime.make(arg4, arg5,
^
TypeError: undefined is not a function
at Function.HDateTime.make (C:\Users\Shawn\nodehaystack\HDateTime.js:95:33)
at Object.<anonymous> (C:\Users\Shawn\nodehaystack\index.js:8:38)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Function.Module.runMain (module.js:501:10)
at startup (node.js:129:16)
at node.js:814:3
index.js - 在午夜失败
var HDateTime = require('./HDateTime');
var HDate = require('./HDate');
var HTimeZone = require('./HTimeZone');
var utc = HTimeZone.UTC;
console.log('DATE: ' + HDate.make(2011, 1, 2));
console.log('DATETIME: ' + HDateTime.make(2011, 1, 2, 3, 4, 5, utc, 0));
console.log('MIDNIGHT: ' + HDate.make(2011, 1, 2).midnight(utc));
ERROR OUTPUT:
C:\Users\Shawn\nodehaystack>node index.js
DATE: 2011-01-02
DATETIME: 2011-01-02T03:04:05Z UTC
C:\Users\Shawn\nodehaystack\HDate.js:46
HDate.prototype.midnight = function(tz) { return HDateTime.make(this, HTime.MI
^
TypeError: undefined is not a function
at HVal.HDate.midnight (C:\Users\Shawn\nodehaystack\HDate.js:46:60)
at Object.<anonymous> (C:\Users\Shawn\nodehaystack\index.js:8:51)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Function.Module.runMain (module.js:501:10)
at startup (node.js:129:16)
at node.js:814:3
HDate.js
var HVal = require('./HVal');
var HDateTime = require('./HDateTime');
var HTime = require('./HTime');
/** Private constructor */
function HDate(year, month, day) {
/** Four digit year such as 2011 */
this.year = year;
/** Month as 1-12 (Jan is 1, Dec is 12) */
this.month = month;
/** Day of month as 1-31 */
this.day = day;
};
HDate.prototype = Object.create(HVal.prototype);
/** int - Hash is based on year, month, day */
HDate.prototype.hashCode = function() { return (this.year << 16) ^ (this.month << 8) ^ this.day; };
/** String - Encode as "YYYY-MM-DD" */
HDate.prototype.toZinc = function() {
var s = this.year + "-";
if (this.month < 10) s += "0"; s += this.month + "-";
if (this.day < 10) s += "0"; s += this.day;
return s;
};
/** boolean - Equals is based on year, month, day */
HDate.prototype.equals = function(that) { return that instanceof HDate && this.year === that.year && this.month === that.month && this.day === that.day; };
/** int - Return sort order as negative, 0, or positive */
HDate.prototype.compareTo = function(that) {
if (this.year < that.year) return -1; else if (this.year > that.year) return 1;
if (this.month < that.month) return -1; else if (this.month > that.month) return 1;
if (this.day < that.day) return -1; else if (this.day > that.day) return 1;
return 0;
};
/** Convert this date into HDateTime for midnight in given timezone. */
HDate.prototype.midnight = function(tz) { return HDateTime.make(this, HTime.MIDNIGHT, tz); };
/** Return date in future given number of days */
HDate.prototype.plusDays = function(numDays) {
if (numDays == 0) return this;
if (numDays < 0) return this.minusDays(-numDays);
var year = this.year;
var month = this.month;
var day = this.day;
for (; numDays > 0; --numDays) {
day++;
if (day > HDate.daysInMonth(year, month)) {
day = 1;
month++;
if (month > 12) { month = 1; year++; }
}
}
return HDate.make(year, month, day);
};
/** Return date in past given number of days */
HDate.prototype.minusDays = function(numDays) {
if (numDays == 0) return this;
if (numDays < 0) return this.plusDays(-numDays);
var year = this.year;
var month = this.month;
var day = this.day;
for (; numDays > 0; --numDays) {
day--;
if (day <= 0) {
month--;
if (month < 1) { month = 12; year--; }
day = HDate.daysInMonth(year, month);
}
}
return HDate.make(year, month, day);
};
/** Return day of week: Sunday is 1, Saturday is 7 */
HDate.prototype.weekday = function() { return new Date(this.year, this.month-1, this.day).getDay() + 1; };
/** Export for use in testing */
module.exports = HDate;
/** Construct from basic fields, javascript Date instance, or String in format "YYYY-MM-DD" */
HDate.make = function(arg, month, day) {
if (arg instanceof Date) {
return new HDate(arg.getFullYear(), arg.getMonth() + 1, arg.getDate());
} else if (HVal.typeis(arg, 'string', String)) {
try {
var s = arg.split('-');
return new HDate(parseInt(s[0]), parseInt(s[1]), parseInt(s[2]));
} catch(err) {
throw err;
}
} else {
if (arg < 1900) throw new Error("Invalid year");
if (month < 1 || month > 12) throw new Error("Invalid month");
if (day < 1 || day > 31) throw new Error("Invalid day");
return new HDate(arg, month, day);
}
};
/** Get HDate for current time in default timezone */
HDate.today = function() { return HDateTime.now().date; };
/** Return if given year a leap year */
HDate.isLeapYear = function(year) {
if ((year & 3) != 0) return false;
return (year % 100 != 0) || (year % 400 == 0);
};
/** Return number of days in given year (xxxx) and month (1-12) */
var daysInMon = [ -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
var daysInMonLeap = [ -1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
HDate.daysInMonth = function(year, mon) { return HDate.isLeapYear(year) ? daysInMonLeap[mon] : daysInMon[mon]; };
HDateTime.js
var moment = require('moment-timezone');
var HVal = require('./HVal');
var HDate = require('./HDate');
var HTime = require('./HTime');
var HTimeZone = require('./HTimeZone');
// require('./io/HZincReader');
/** Private constructor */
function HDateTime(date, time, tz, tzOffset) {
/** HDate - Date component of the timestamp */
this.date = date;
/** HTime - Time component of the timestamp */
this.time = time;
/** int - Offset in seconds from UTC including DST offset */
this.tz = tz;
/** HTimeZone - Timezone as Olson database city name */
this.tzOffset = (typeof(tzOffset) == 'undefined' ? 0 : tzOffset);
/** long - millis since Epoch */
this.millis;
}
HDateTime.prototype = Object.create(HVal.prototype);
/** long - Get this date time as Java milliseconds since epoch */
HDateTime.prototype.millis = function() {
if (this.millis <= 0) {
var d = new Date(this.date.year, this.date.month-1, this.date.day, this.time.hour, this.time.min, this.time.sec, this.time.ms);
//TODO: implement UTC timezone
this.millis = d.getTime();
}
return this.millis;
};
/** int - Hash code is based on date, time, tzOffset, and tz */
HDateTime.prototype.hashCode = function() { return this.date.hashCode() ^ this.time.hashCode() ^ tzOffset ^ this.tz.hashCode(); };
/** String - Encode as "YYYY-MM-DD'T'hh:mm:ss.FFFz zzzz" */
HDateTime.prototype.toZinc = function() {
var s = this.date.toZinc() + "T" + this.time.toZinc();
if (this.tzOffset == 0) s += "Z";
else {
var offset = this.tzOffset;
if (offset < 0) { s += "-"; offset = -offset; }
else { s += "+"; }
var zh = offset / 3600;
var zm = (offset % 3600) / 60;
if (zh < 10) s += "0"; s += zh + ":";
if (zm < 10) s += "0"; s += zm;
}
s += " " + this.tz;
return s;
};
/** boolean - Equals is based on date, time, tzOffset, and tz */
HDateTime.prototype.equals = function(that) {
return that instanceof HDateTime && this.date === that.date
&& this.time === that.time && this.tzOffset === that.tzOffset && this.tz === that.tz;
};
/** int - Comparison based on millis. */
HDateTime.prototype.compareTo = function(that) {
var thisMillis = this.millis();
var thatMillis = that.millis();
if (thisMillis < thatMillis) return -1;
else if (thisMillis > thatMillis) return 1;
return 0;
};
/** Export for use in testing */
module.exports = HDateTime;
function utcDate(year, month, day, hour, min, sec, ms) {
var d = new Date();
d.setUTCFullYear(year);
d.setUTCMonth(month-1);
d.setUTCDate(day);
d.setUTCHours(hour);
d.setUTCMinutes(min);
d.setUTCSeconds(sec);
d.setUTCMilliseconds(ms);
return d;
}
/** Construct from various values */
HDateTime.make = function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) {
if (arg7 instanceof HTimeZone) {
return HDateTime.make(HDate.make(arg1, arg2, arg3), HTime.make(arg4, arg5, arg6), arg7, arg8);
} else if (arg6 instanceof HTimeZone) {
return HDateTime.make(HDate.make(arg1, arg2, arg3), HTime.make(arg4, arg5, 0), arg6, arg7);
} else if (arg3 instanceof HTimeZone) {
// use Date to decode millis to fields
var d = utcDate(arg1.year, arg1.month, arg1.day, arg2.hour, arg2.min, arg2.sec, arg2.ms);
var tzOffset = arg4;
if (typeof(tzOffset) == 'undefined') {
// convert to designated timezone
d = moment(d).tz(arg3.js.name);
tzOffset = d.utcOffset() * 60;
}
var ts = new HDateTime(arg1, arg2, arg3, tzOffset);
ts.millis = d.valueOf() + (tzOffset * -1000);
return ts;
} else if (this.typeis(arg1, 'string', String)) {
// TODO: Implemet parsing
// var val = new HZincReader(arg1).readScalar();
// if (typeof(val) == HDateTime) return val;
// throw new Error("Parse Error: " + arg1);
} else {
var tz = arg2;
if (typeof(tz) == 'undefined') tz = HTimeZone.DEFAULT;
var d = new Date(arg1);
// convert to designated timezone
d = moment(d).tz(tz.js.name);
tzOffset = d.utcOffset() * 60;
var ts = new HDateTime(HDate.make(d), HTime.make(d), tz, tz.getOffset());
ts.millis = arg1;
}
};
/** HDateTime - Get HDateTime for given timezone */
HDateTime.now = function(tz) { return make(new Date().getTime(), tz); };
HTime.js
var HVal = require('./HVal');
// require('./io/HZincReader');
/** Private constructor */
function HTime(hour, min, sec, ms) {
/** int - Hour of day as 0-23 */
this.hour = hour;
/** int - Minute of hour as 0-59 */
this.min = min;
/** int - Second of minute as 0-59 */
this.sec = (typeof(sec) == 'undefined' ? 0 : sec);
/** int - Fractional seconds in milliseconds 0-999 */
this.ms = (typeof(ms) == 'undefined' ? 0 : ms);
};
HTime.prototype = Object.create(HVal.prototype);
/** int - Hash code is based on hour, min, sec, ms */
HTime.prototype.hashCode = function() { return (this.hour << 24) ^ (this.min << 20) ^ (this.sec << 16) ^ this.ms; };
/** boolean - Equals is based on hour, min, sec, ms */
HTime.prototype.equals = function(that) {
return that instanceof HTime && this.hour === that.hour &&
this.min === that.min && this.sec === that.sec && this.ms === that.ms;
};
/** int - Return sort order as negative, 0, or positive */
HTime.prototype.compareTo = function(that) {
if (this.hour < that.hour) return -1; else if (this.hour > that.hour) return 1;
if (this.min < that.min) return -1; else if (this.min > that.min) return 1;
if (this.sec < that.sec) return -1; else if (this.sec > that.sec) return 1;
if (this.ms < that.ms) return -1; else if (this.ms > that.ms) return 1;
return 0;
};
/** String - Encode as "hh:mm:ss.FFF" */
HTime.prototype.toZinc = function() {
var s = "";
if (this.hour < 10) s += "0"; s += this.hour + ":";
if (this.min < 10) s += "0"; s += this.min + ":";
if (this.sec < 10) s += "0"; s += this.sec;
if (this.ms != 0) {
s += ".";
if (this.ms < 10) s += "0";
if (this.ms < 100) s += "0";
s += this.ms;
}
return s;
};
/** Export for use in testing */
module.exports = HTime;
/** Singleton for midnight 00:00 */
HTime.MIDNIGHT = new HTime(0, 0, 0, 0);
/** Construct with all fields, with Javascript Date object, or Parse from string fomat "hh:mm:ss.FF" */
HTime.make = function(arg1, min, sec, ms) {
if (HVal.typeis(arg1, 'string', String)) {
// TODO: Implemet parsing
// var val = new HZincReader(arg1).readScalar();
// if (val instanceof HTime) return val;
// throw new Error("Parse Error: " + arg1);
} else if (arg1 instanceof Date) {
return new HTime(arg1.getHours(), arg1.getMinutes(), arg1.getSeconds(), arg1.getMilliseconds());
} else {
return new HTime(arg1, min, sec, ms);
}
};
HTimeZone.js
var moment = require('moment-timezone');
var HVal = require('./HVal');
/** Package private constructor */
function HTimeZone(name, js) {
/** Haystack timezone name */
this.name = name;
/** Javascript (moment) representation of this timezone. */
this.js = js;
};
/** String - Return Haystack timezone name */
HTimeZone.prototype.toString = function() { return this.name; };
module.exports = HTimeZone;
HTimeZone.make = function(arg1, checked) {
if (typeof(checked) == 'undefined') return HTimeZone.make(arg1, true);
if (HVal.typeis(arg1, 'string', String)) {
/**
* Construct with Haystack timezone name, raise exception or
* return null on error based on check flag.
*/
// lookup in cache
var tz = cache[arg1];
if (typeof(tz) != 'undefined') return tz;
// map haystack id to Javascript full id
var jsId = toJS[arg1];
if (typeof(jsId) == 'undefined') {
if (checked) throw new Error("Unknown tz: " + arg1);
return undefined;
}
// resolve full id to HTimeZone and cache
var js = moment.tz.zone(jsId)
tz = new HTimeZone(arg1, js);
cache[arg1] = tz;
return tz;
} else {
/**
* Construct from Javascript timezone. Throw exception or return
* null based on checked flag.
*/
var jsId = arg1.name;
if (jsId.startsWith("GMT")) fixGMT(jsId);
var name = fromJS[jsId];
if (typeof(name) != 'undefined') return HTimeZone.make(name);
if (checked) throw new Error("Invalid Java timezone: " + arg1.name);
return;
}
};
function fixGMT(jsId) {
// Javscript (moment) IDs can be in the form "GMT[+,-]h" as well as
// "GMT", and "GMT0". In that case, convert the ID to "Etc/GMT[+,-]h".
// V8 uses the format "GMT[+,-]hh00 (used for default timezone), this also
// needs converted.
if (jsId.indexOf("+")<0 && jsId.indexOf("-")<0) return "Etc/" + jsId; // must be "GMT" or "GMT0" which are fine
// get the prefix
var pre = jsId.substring(0, 4);
var num = parseInt(jsId.substring(4, 6));
// ensure we have a valid value
if ((pre.substring(3)=="+" && num<13) || (pre.substring(3)=="-" & num<15)) return "Etc/" + pre + num;
// nothing we could do, return what was passed
return jsId;
}
var cache = {};
var toJS = {};
var fromJS = {};
{
try {
// only time zones which start with these
// regions are considered valid timezones
var regions = {};
regions["Africa"] = "ok";
regions["America"] = "ok";
regions["Antarctica"] = "ok";
regions["Asia"] = "ok";
regions["Atlantic"] = "ok";
regions["Australia"] = "ok";
regions["Etc"] = "ok";
regions["Europe"] = "ok";
regions["Indian"] = "ok";
regions["Pacific"] = "ok";
// iterate Javascript timezone IDs available
var ids = moment.tz.names();
for (var i=0; i<ids.length; ++i) {
var js = ids[i];
// skip ids not formatted as Region/City
var slash = js.indexOf('/');
if (slash < 0) continue;
var region = js.substring(0, slash);
if (typeof(regions[region]) == 'undefined') continue;
// get city name as haystack id
slash = js.lastIndexOf('/');
var haystack = js.substring(slash+1);
// store mapping b/w Javascript <-> Haystack
toJS[haystack] = js;
fromJS[js] = haystack;
}
} catch (err) {
console.log(err.stack);
}
var utc;
try {
utc = HTimeZone.make(moment.tz.zone("Etc/UTC"));
} catch (err) {
console.log(err.stack);
}
var def;
try {
// check if configured with system property
var defName = process.env["haystack.tz"];
if (defName != null) {
def = make(defName, false);
if (typeof(def) == 'undefined') console.log("WARN: invalid haystack.tz system property: " + defName);
}
// if we still don't have a default, try to use Javascript's
if (typeof(def) == 'undefined') {
var date = new Date().toString();
var gmtStart = date.indexOf("GMT");
var gmtEnd = date.indexOf(" ", gmtStart);
def = fromJS[fixGMT(date.substring(gmtStart, gmtEnd))];
}
} catch (err) {
console.log(err.stack);
def = utc;
}
/** UTC timezone */
HTimeZone.UTC = utc;
/** Default timezone for VM */
HTimeZone.DEFAULT = def;
}
问题在于如何导出函数。
你说
module.exports = HDate
这很好
但后来你做了
module.exports.make = function(){}
这也很好。
但是两者一起工作不会像您刚刚体验的那样可靠。
要修复该问题,可以将 class 导出为 module.exports.hdate
或将 make
函数添加到 HDate
:
Hdate.make = myFunction() {}
这显然适用于您像这样导出的所有方法。
如果这能解决您的问题,请告诉我:)
我发现这个问题的最佳解决方案是移动所有可能导致循环依赖的 require 语句,以确保它们在我的 class 的构造函数之后和 module.exports 之后陈述。例如...
var HVal = require('./HVal');
/**
* HDate models a date (day in year) tag value.
* @see {@link http://project-haystack.org/doc/TagModel#tagKinds|Project Haystack}
*
* @constructor
* @private
* @extends {HVal}
* @param {int} year - Four digit year such as 2011
* @param {int} month - Month as 1-12 (Jan is 1, Dec is 12)
* @param {int} day - Day of month as 1-31
*/
function HDate(year, month, day) {
this.year = year;
this.month = month;
this.day = day;
}
HDate.prototype = Object.create(HVal.prototype);
module.exports = HDate;
var HDateTime = require('./HDateTime'),
HTime = require('./HTime');
在我的所有 classes 中始终如一地执行此方法可以防止任何周期性问题。