Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ with the option to convert to the next immediate corresponding Date.
Built for [Promt](http://promtapp.com), to solve [this problem](http://stackoverflow.com/q/141348/962091).

**Browser**

```
$ bower install time-js # or just manually download time.js
```
Expand All @@ -17,6 +17,7 @@ $ bower install time-js # or just manually download time.js
```js
var t = Time('2p');
t.hours(); // 2
t.militaryHours(); // 14
t.minutes(); // 0
t.period(); // 'pm'
t.toString(); // '2:00 pm'
Expand Down Expand Up @@ -45,6 +46,7 @@ Parses strings such as "8:20" into a Date-less Time.

```js
new Time('1') // 1:00
new Time('13') // 1:00 pm
new Time('1:23') // 1:23
```

Expand Down Expand Up @@ -77,7 +79,7 @@ Does validation statically...
```js
Time.isValid('8:00') // true
Time.isValid('12:60') // false
Time.isValid('13:23') // false
Time.isValid('13:23') // true
```

... or after construction.
Expand All @@ -92,6 +94,7 @@ There's basic formatting

```js
Time('2:30p').format('hh:mm A'); // '02:30 P'
Time('2:30p').format('HH:mm'); // '14:30'
Time('12 am').format('h: p'); // '12 a'
Time('220 a').format('h: p'); // '2:20 a'
Time('7').format('h: p'); // '7'
Expand All @@ -103,7 +106,23 @@ Accepts numbers too.
Time(1).isValid() // true
```

*Military time is not supported, but may be in the future (or not).*
Military (24-hour) time support

```js
Time('13').format('hh:mm AM'); // '01:00 PM'
Time('2:30p').format('HH:mm'); // '14:30'
Time('14:30').format('h:mm AM'); // '2:30 PM'
Time('0000').format('h:mm AM'); // '12:00 AM'
Time('2400').isValid(); // false (contrary to ISO8601)
```

ISO8601 time component (without seconds) support

```js
Time('12:00 am').toISOString(); // '00:00'
Time('12:00 pm').toISOString(); // '12:00'
Time('11:00 pm').toISOString(); // '23:00'
```

Test
----
Expand Down
43 changes: 39 additions & 4 deletions test/format.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ describe('Time', 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');
// 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() {
Expand Down Expand Up @@ -119,5 +119,40 @@ describe('Time', function() {
var t = time('12:30');
t.format('h:m').should.equal('invalid format');
});

it('should format military time: 0', function() {
var t = time('0');
t.format('hmm').should.equal('1200');
t.format('hhmm').should.equal('1200');
t.format('hh:mm AM').should.equal('12:00 AM');
t.format('H').should.equal('0');
t.format('HH').should.equal('00');
t.format('HHmm').should.equal('0000');
t.format('HH:mm').should.equal('00:00');
});

it('should format military time: 13', function() {
var t = time('13');
t.format('hmm').should.equal('100');
t.format('hhmm').should.equal('0100');
t.format('hh:mm AM').should.equal('01:00 PM');
t.format('H').should.equal('13');
t.format('HH').should.equal('13');
t.format('HHmm').should.equal('1300');
t.format('HH:mm').should.equal('13:00');
t.format('HH:mm AM').should.equal('13:00');
});

it('should format military time: 9:55 PM', function() {
var t = time('9:55 PM');
t.format('hmm').should.equal('955');
t.format('hhmm').should.equal('0955');
t.format('hh:mm AM').should.equal('09:55 PM');
t.format('H').should.equal('21');
t.format('HH').should.equal('21');
t.format('HHmm').should.equal('2155');
t.format('HH:mm').should.equal('21:55');
t.format('HH:mm AM').should.equal('21:55');
});
});
});
40 changes: 33 additions & 7 deletions test/parsing.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,19 +103,23 @@ describe('Time', function() {
result = time(hour);
result.isValid().should.be.ok;
result.hours().should.equal(parseInt(hour));
result.militaryHours().should.equal(parseInt(hour));
result.minutes().should.equal(0);
}
});

it('should fail made up hours e.g. 0, 13, 50', function() {
time('0').isValid().should.not.be.ok;
time('13').isValid().should.not.be.ok;
it('should pass military time hours', function() {
time('0').isValid().should.be.ok;
time('13').isValid().should.be.ok;
time('0:20').isValid().should.be.ok;
time('13:12').isValid().should.be.ok;
});

it('should fail made up hours e.g. 50', function() {
time('50').isValid().should.not.be.ok;
});

it('should fail made up hours e.g. 0:20, 13:12, 50:00', function() {
time('0:20').isValid().should.not.be.ok;
time('13:12').isValid().should.not.be.ok;
it('should fail made up hours e.g. 50:00', function() {
time('50:00').isValid().should.not.be.ok;
});

Expand Down Expand Up @@ -168,11 +172,33 @@ describe('Time', function() {
});

it('should fail made up minutes without the colon e.g. 13, 160', function() {
time('14').isValid().should.not.be.ok;
// time('14').isValid().should.not.be.ok;
time('160').isValid().should.not.be.ok;
time('1299').isValid().should.not.be.ok;
time('12021').isValid().should.not.be.ok;
time('12218').isValid().should.not.be.ok;
});

it('should not allow twenty four hundred hours', function() {
time('2400').isValid().should.not.be.ok;
});
});

describe('#toISOString', function() {
it('should output the time component specified by ISO8601', function() {
time('12:00 am').toISOString().should.equal('00:00');
time('1:00 am').toISOString().should.equal('01:00');
time('11:00 am').toISOString().should.equal('11:00');
time('12:00 pm').toISOString().should.equal('12:00');
time('1:00 pm').toISOString().should.equal('13:00');
time('11:00 pm').toISOString().should.equal('23:00');
time('0').toISOString().should.equal('00:00');
time('1').toISOString().should.equal('01:00');
time('11').toISOString().should.equal('11:00');
time('12').toISOString().should.equal('12:00');
time('13').toISOString().should.equal('13:00');
time('23').toISOString().should.equal('23:00');
time('24').toISOString().should.equal('invalid time');
});
});
});
65 changes: 58 additions & 7 deletions time.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
, 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)?( ?)'
, militaryTimeRegex = new RegExp('^([01]?[0-9]|2[0-3])(?::|\\.)?([0-5][0-9])?$', 'i')
, formatRegex = new RegExp('^(h|hh|H|HH)([:|\.])?(mm)?( ?)'
+ periodRegex.source + '?$', 'i');

// play nice with both node.js and browser
Expand All @@ -15,8 +16,8 @@
/*
* Time constructor works with(out) 'new'
*
* @time (optional) string or number representing a time.
* e.g. 7, 1234, '7', '7:00', '12.14'
* @time (optional) string or number representing a 12-hour or 24-hour military time.
* e.g. 7, 1234, '7', '7:00', '12.14', '13', '15:30'
*
* If not provided, current time is used.
*/
Expand All @@ -26,11 +27,23 @@
var hours, minutes, period = null;

if (time) {
var result = timeRegex.exec(sanitize(time));
var sanitizedTime = sanitize(time);
// parse 12-hour time
var result = timeRegex.exec(sanitizedTime);
if (result) {
hours = parseInt(result[1]);
minutes = result[2] ? parseInt(result[2]) : 0;
period = parsePeriod(result[3]);
} else {
// parse 24-hour military time
result = militaryTimeRegex.exec(sanitizedTime);
if (result) {
hours = parseInt(result[1]);
period = hours > 11 ? PM : AM;
if (hours > 12) hours -= 12;
if (hours === 0) hours = 12;
minutes = result[2] ? parseInt(result[2]) : 0;
}
}
} else {
// set to current time
Expand All @@ -48,6 +61,27 @@
hours = parseInt(newHours);
};

this.militaryHours = function(newHours) {
if (!newHours) {
if (period === AM || !period) {
if (hours === 12 && period)
return 0;
else
return hours;
} else {
if (hours === 12)
return 12;
else {
return parseInt(hours) + 12;
}
}
}
hours = parseInt(newHours);
period = hours > 11 ? PM : AM;
if (hours > 12) hours -= 12;
if (hours === 0) hours = 12;
};

// gets or sets minutes
this.minutes = function(newMinutes) {
if (!newMinutes) return minutes;
Expand Down Expand Up @@ -91,7 +125,8 @@
};

Time.isValid = function(time) {
return timeRegex.test(sanitize(time));
var sanitizedTime = sanitize(time);
return timeRegex.test(sanitizedTime) || militaryTimeRegex.test(sanitizedTime);
};

Time.prototype.isValid = function() {
Expand Down Expand Up @@ -125,6 +160,8 @@
* hh:mm a.m. 01:55 a.m.
* h:mma 1:55a
* h.mm 1.55
* H:mm 13:55
* HH:mm 01:00
*/
Time.prototype.format = function(format) {
format = format || Time.DEFAULT_TIME_FORMAT;
Expand All @@ -141,6 +178,13 @@
*/
Time.prototype.toString = Time.prototype.format;

/*
* Alias for `format('HH:mm')` returns ISO8601 time component (without seconds)
*/
Time.prototype.toISOString = function() {
return this.format('HH:mm');
};

/*
* (private) Format Time in the given format.
*
Expand All @@ -159,7 +203,14 @@
var fPeriodM = bits[7];

// always show hour
var hours = fHour.length == 2 ? padTime(time.hours()) : time.hours();
var hours;
// check for military format H and HH
var militaryFormat = fHour.toLowerCase() !== fHour;

if (!militaryFormat)
hours = fHour.length == 2 ? padTime(time.hours()) : time.hours();
else
hours = fHour.length == 2 ? padTime(time.militaryHours()) : time.militaryHours();

// show if in the format or if non-zero and middlebit is provided
var minutes = (fMinutes || (fMiddlebit && time.minutes() !== 0)) ?
Expand All @@ -170,7 +221,7 @@

// show period if available and requested
var period = '';
if (fPeriod && time.period()) {
if (!militaryFormat && fPeriod && time.period()) {
var firstPeriod = time.period().charAt(0);
if (fPeriod.charAt(0) === fPeriod.charAt(0).toUpperCase()) {
firstPeriod = firstPeriod.toUpperCase();
Expand Down