Permalink
Browse files

first stab at format support. no new tests, but existing ones pass.

  • Loading branch information...
1 parent b35a063 commit 8b5ba3f524c390564d0c6655a2fd2be931b9c89b @zackdever committed Jan 10, 2013
Showing with 94 additions and 27 deletions.
  1. +94 −27 time.js
View
121 time.js
@@ -1,10 +1,12 @@
-(function() {
+(function () {
- var am = 'am'
- , pm = 'pm'
- , periodRegex = new RegExp('([ap]\\.?(m\\.?)?)', 'i')
+ var AM = 'am'
+ , PM = 'pm'
+ , periodRegex = new RegExp('([ap](\\.?)(m\\.?)?)', 'i')
, timeRegex = new RegExp('^(10|11|12|[1-9])(?::|\\.)?([0-5][0-9])?'
- + periodRegex.source + '?$', 'i');
+ + periodRegex.source + '?$', 'i')
+ , formatRegex = new RegExp('^(h|hh)([:|\.])?(mm)?( ?)'
+ + periodRegex.source + '?$', 'i');
// play nice with both node.js and browser
if (typeof module !== 'undefined' && module.exports) module.exports = Time;
@@ -34,7 +36,7 @@
// set to current time
var d = new Date();
hours = d.getHours();
- period = hours > 11 ? pm : am;
+ period = hours > 11 ? PM : AM;
if (hours > 12) hours -= 12;
if (hours === 0) hours = 12;
minutes = d.getMinutes();
@@ -44,19 +46,19 @@
this.hours = function(newHours) {
if (!newHours) return hours;
hours = parseInt(newHours);
- }
+ };
// gets or sets minutes
this.minutes = function(newMinutes) {
if (!newMinutes) return minutes;
minutes = parseInt(newMinutes);
- }
+ };
// gets or sets period
this.period = function(newPeriod) {
if (!newPeriod) return period;
period = parsePeriod(newPeriod);
- }
+ };
}
/*
@@ -71,7 +73,7 @@
if (!this.isValid()) return null;
var hours = this.hours() === 12 ? 0 : this.hours(); // uniformly handle am/pm adjustments
- if (this.period() === pm) hours += 12;
+ if (this.period() === PM) hours += 12;
var d = new Date();
d.setHours(hours);
d.setMinutes(this.minutes());
@@ -82,41 +84,106 @@
while (new Date() > d) d.setHours(d.getHours() + 12);
// make sure we're in the correct period
- if (d.getHours() > 11 && this.period() === am) d.setHours(d.getHours() + 12)
- else if (d.getHours() < 12 && this.period() === pm) d.setHours(d.getHours() + 12)
+ if (d.getHours() > 11 && this.period() === AM) d.setHours(d.getHours() + 12)
+ else if (d.getHours() < 12 && this.period() === PM) d.setHours(d.getHours() + 12)
return d;
- }
+ };
Time.isValid = function(time) {
return timeRegex.test(sanitize(time));
- }
+ };
Time.prototype.isValid = function() {
return Time.isValid(toString(this));
- }
+ };
/*
- * h:mm
+ * Alias for `format`.
*/
- Time.prototype.toString = function() {
- if (!this.isValid()) return 'invalid time'
- return toString(this);
- }
+ Time.prototype.toString = Time.prototype.format;
/*
- * (private) Format Time into hh:mm for validation test.
+ * This can be safely changed if so desired.
+ */
+ Time.DEFAULT_TIME_FORMAT = 'h:mm am';
+
+ /*
+ * Formats the time to the given format, or h:mm if one is not provided.
+ *
+ * If periods are specified in the format, they are only printed if known.
+ *
+ * If the time isn't valid, return 'invalid time'.
+ * If the format isn't valid, return 'invalid format'.
+ *
+ * This isn't every combination, but hopefully you get the gist of things.
+ * h:mm 12:00 (default)
+ * hh:mm 01:00
+ * h 1
+ * h 1:55 (input specified minutes, so we show them)
+ * h. 1.55 (if minutes are shown, use . for separator)
+ * hpm 1am
+ * h:mm a 1:55 p
+ * h:mm a 1:55 (input didn't specify period)
+ * h.mm am 1.55 pm
+ * h.mm A 1.55 P
+ * hh:mm a.m. 01:55 a.m.
+ * h:mma 1:55a
+ * h.mm 1.55
+ */
+ Time.prototype.format = function(format) {
+ if (!this.isValid()) {
+ return 'invalid time';
+ } else if (!timeRegex.test(format)) {
+ return 'invalid format';
+ }
+ return toString(this, format);
+ };
+
+ /*
+ * (private) Format Time in the given format.
*
* @time Time instance
* @retun hh:mm e.g. 3:00, 12:23, undefined:undefined
*/
- function toString(time) {
- var minutes = time.minutes() < 10 ? '0' + time.minutes() : time.minutes()
- , result = time.hours() + ':' + minutes;
+ function toString(time, format) {
+ var bits = formatRegex.exec(format || Time.DEFAULT_TIME_FORMAT);
+ var fHour = bits[1];
+ var fMiddlebit = bits[2];
+ var fMinutes = bits[3];
+ var fPeriodSpace = bits[4];
+ var fPeriod = bits[5];
+ var fFirstPeriod = bits[6];
+ var fPeriodM = bits[7];
+
+ // always show hour
+ var hours = fHour.length == 2 ? padTime(time.hours()) : time.hours();
+
+ // only hide minutes if they aren't in the format, and not 0
+ var minutes = (fMinutes || time.minutes()) ? padTime(time.minutes()) : '';
+
+ // only show middlebit if we have minutes
+ var middlebit = minutes ? fMiddlebit : '';
+
+ // always show period if available
+ var period = (time.period() == undefined) ? '' : time.period();
+ if (period) {
+ var firstPeriod = period.charAt(0);
+ if (fPeriod.charAt(0) === fPeriod.charAt(0).toUpperCase()) {
+ firstPeriod = firstPeriod.toUpperCase();
+ }
+ period = firstPeriod + fPeriod.slice(1);
+ }
- return (time.period() == undefined) ? result : result + ' ' + time.period();
+ // only show space if it was requested by format and there's a period
+ var space = (period && fPeriodSpace) ? fPeriodSpace : '';
+
+ return '' + hours + middlebit + minutes + period;
}
+ function padTime(time) {
+ return time < 10 ? '0' + time : time;
+ }
/*
* (private) Force @time to a string and remove all whitespace.
*
@@ -132,7 +199,7 @@
*/
function parsePeriod(period) {
if (!period || !period.match(periodRegex)) return null;
- else if (period.match(/^p/i) != null) return pm;
- return (period.match(/^a/i) != null) ? am : null;
+ else if (period.match(/^p/i) != null) return PM;
+ return (period.match(/^a/i) != null) ? AM : null;
}
})();

0 comments on commit 8b5ba3f

Please sign in to comment.