Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Format #5

Merged
merged 4 commits into from Jan 16, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
123 changes: 123 additions & 0 deletions test/format.js
@@ -0,0 +1,123 @@
time = require('../time');

describe('Time', function() {
describe('#format', function() {
it('should work with no params', function() {
var t = time('1:23p');
t.format().should.equal('1:23 pm');
});

it('should always require hours', function() {
var t = time('1.23');
t.format(':mm').should.equal('invalid format');
});

it('should not require a separator', function() {
var t = time('7:23');
t.format('hmm').should.equal('723');
t.format('hhmm').should.equal('0723');
});

it('should respect h vs hh', function() {
var t = time('3:20');
t.format('h:mm').should.equal('3:20');
t.format('hh:mm').should.equal('03:20');
});

it('should still show hh when hour is > 9', function() {
var t = time('12:20');
t.format('h:mm').should.equal('12:20');
});

it('should not be case sensitive for hours and minutes', function() {
var t = time('03:23 pm');
var expected = '03:23';
t.format('hh:mm').should.equal(expected);
t.format('HH:mm').should.equal(expected);
t.format('Hh:Mm').should.equal(expected);
t.format('hH:MM').should.equal(expected);
t.format('H:MM').should.equal('3:23');
});

it('should not care if a or p is used for period', function() {
var t = time('9p');
t.format('h:mm p.m.').should.equal('9:00 p.m.');
t.format('h:mm a.m.').should.equal('9:00 p.m.');
});

it('should respect case for period', function() {
var t = time('9p');
t.format('h p').should.equal('9 p');
t.format('h P').should.equal('9 P');
t.format('h Pm').should.equal('9 Pm');
t.format('h a.m.').should.equal('9 p.m.');
});

it('should respect space between period', function() {
var t = time('12am');
t.format('h:mm pm').should.equal('12:00 am');
t.format('h:mmam').should.equal('12:00am');
t.format('h a.').should.equal('12 a.');
t.format('hP').should.equal('12A');
});

it('should ignore period if not originally parsed', function() {
var t = time('1');
t.format('hpm').should.equal('1');
t.format('h:mm PM').should.equal('1:00');
});

it('should hide period if not requested', function() {
var t = time('1p');
t.format('h').should.equal('1');
t.format('h:mm').should.equal('1:00');
});

it('should hide minutes if just an hour is given', function() {
var t = time('12');
t.format('h').should.equal('12');
t = time('12:23');
t.format('h').should.equal('12');
});

it('should show auto-hide minutes if they are omitted but a middlebit \
is supplied', function() {
var t = time('12 pm');
t.format('h:').should.equal('12');
t.format('h:A').should.equal('12P');
t = time('12:23 pm');
t.format('h:').should.equal('12:23');
t.format('h.').should.equal('12.23');
t.format('h.P').should.equal('12.23P');
});

it('should respect periods on period e.g. a.m. vs am', function() {
var t = time('7p');
t.format('h:mm a').should.equal('7:00 p');
t.format('h:mm a.').should.equal('7:00 p.');
t.format('h:mm a.m.').should.equal('7:00 p.m.');
t.format('h:mm am').should.equal('7:00 pm');
t.format('h:mm am.').should.equal('7:00 pm.');
t.format('h:mm a.m').should.equal('7:00 p.m');
});

it('should allow for only first letter of period', function() {
var t = time('10:55 PM');
t.format('hh:mm A').should.equal('10:55 P');
t.format('hh:mm AM').should.equal('10:55 PM');
});

it('should only allow blank, period, or colon for seperator', function() {
var t = time('323');
t.format('hmm').should.equal('323');
t.format('h:mm').should.equal('3:23');
t.format('h.mm').should.equal('3.23');
t.format('h,mm').should.equal('invalid format');
});

it('should not allow a single minute', function() {
var t = time('12:30');
t.format('h:m').should.equal('invalid format');
});
});
});
File renamed without changes.
127 changes: 99 additions & 28 deletions time.js
@@ -1,10 +1,12 @@
(function() {
(function () {

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');
var AM = 'am'
, PM = 'pm'
, periodRegex = new RegExp('([ap](\\.?)(m\\.?)?)', 'i')
, timeRegex = new RegExp('^(10|11|12|0?[1-9])(?::|\\.)?([0-5][0-9])?'
+ 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;
Expand Down Expand Up @@ -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();
Expand All @@ -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);
}
};
}

/*
Expand All @@ -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());
Expand All @@ -82,39 +84,108 @@
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
* This can be safely changed if so desired.
*/
Time.prototype.toString = function() {
if (!this.isValid()) return 'invalid time'
return toString(this);
}
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) {
format = format || Time.DEFAULT_TIME_FORMAT;
if (!this.isValid()) {
return 'invalid time';
} else if (!formatRegex.test(format)) {
return 'invalid format';
}
return toString(this, format);
};

/*
* Alias for `format`.
*/
Time.prototype.toString = Time.prototype.format;

/*
* (private) Format Time into hh:mm for validation test.
* (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) {
format = format || Time.DEFAULT_TIME_FORMAT;
var bits = formatRegex.exec(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();

// show if in the format or if non-zero and middlebit is provided
var minutes = (fMinutes || (fMiddlebit && time.minutes() !== 0)) ?
padTime(time.minutes()) : '';

// show middlebit if we have minutes
var middlebit = (minutes && fMiddlebit) ? fMiddlebit : '';

// show period if available and requested
var period = '';
if (fPeriod && time.period()) {
var firstPeriod = time.period().charAt(0);
if (fPeriod.charAt(0) === fPeriod.charAt(0).toUpperCase()) {
firstPeriod = firstPeriod.toUpperCase();
}
period = firstPeriod + fPeriod.slice(1);
}

// only show space if it was requested by format and there's a period
var space = (period && fPeriodSpace) ? fPeriodSpace : '';

return '' + hours + middlebit + minutes + space + period;
}

return (time.period() == undefined) ? result : result + ' ' + time.period();
function padTime(time) {
return time < 10 ? '0' + time : time;
}

/*
Expand All @@ -132,7 +203,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;
}
})();