Adding documentation for what text would be shown when.
Fixing '1 minutes ago' bug.
Use Math.round instead of Math.floor for the nearest date.
Author: Tim Wood
-Version: 0.5.1
+Version: 0.5.2
**Note:** There are some api changes that will break your code when upgrading from 0.4.1 to 0.5.0. Read about the changes in the changelog at the bottom of the page.
The base strings for this function can be customized with `_date.relativeTime`.
+The breakdown of which string is displayed when is outlined in the table below.
+
+<table>
+ <tr>
+ <th>Range</th>
+ <th>Key</th>
+ <th>Sample Output</th>
+ </tr>
+ <tr>
+ <td>0 to 45 seconds</td>
+ <td>s</td>
+ <td>seconds ago</td>
+ </tr>
+ <tr>
+ <td>45 to 90 seconds</td>
+ <td>m</td>
+ <td>a minute ago</td>
+ </tr>
+ <tr>
+ <td>90 seconds to 45 minutes</td>
+ <td>mm</td>
+ <td>2 minutes ago ... 45 minutes ago</td>
+ </tr>
+ <tr>
+ <td>45 to 90 minutes</td>
+ <td>h</td>
+ <td>an hour ago</td>
+ </tr>
+ <tr>
+ <td>90 minutes to 22 hours </td>
+ <td>hh</td>
+ <td>2 hours ago ... 22 hours ago</td>
+ </tr>
+ <tr>
+ <td>22 to 36 hours</td>
+ <td>d</td>
+ <td>a day ago</td>
+ </tr>
+ <tr>
+ <td>36 hours to 25 days</td>
+ <td>dd</td>
+ <td>2 days ago ... 25 days ago</td>
+ </tr>
+ <tr>
+ <td>25 to 45 days</td>
+ <td>M</td>
+ <td>a month ago</td>
+ </tr>
+ <tr>
+ <td>45 to 345 days</td>
+ <td>MM</td>
+ <td>2 months ago ... 11 months ago</td>
+ </tr>
+ <tr>
+ <td>345 to 547 days (1.5 years)</td>
+ <td>y</td>
+ <td>a year ago</td>
+ </tr>
+ <tr>
+ <td>548 days+</td>
+ <td>yy</td>
+ <td>2 years ago ... 20 years ago</td>
+ </tr>
+</table>
_date.fromNow()
There are a bunch of tests in the test/ folder. Check them out. If you think some tests are missing, open an issue, and I'll add it.
+### Unit tests
+
+[Underscore.date unit tests](http://timrwood.github.com/underscore.date/test/test.html)
+
+[Underscore.date customization tests](http://timrwood.github.com/underscore.date/test/customize.html)
+
### Speed tests
[Floor vs bitwiseor vs bitwisenor vs parseint](http://jsperf.com/floor-vs-bitwise-or-vs-parseint/4)
Changelog
=========
+### 0.5.2
+
+Buxfix for [issue 8](https://github.com/timrwood/underscore.date/pull/8) and [issue 9](https://github.com/timrwood/underscore.date/pull/9).
+
### 0.5.1
Buxfix for [issue 5](https://github.com/timrwood/underscore.date/pull/5).
<p>Underscore.date is a javascript date library that helps create, manipulate, and format dates without extending the Date prototype.</p>
<p>Author: Tim Wood (washwithcare@gmail.com)</p>
- <p>Version: 0.5.1</p>
+ <p>Version: 0.5.2</p>
<p class="filesize">1.82 kb (min + gzip)</p>
<h2>Download</h2>
_date([2007, 0, 27]).from(_date([2007, 0, 28]), true , true) // 86400000);</code></pre>
<p>The base strings for this function can be customized with <code>_date.relativeTime</code>.</p>
+
+ <p>The breakdown of which string is displayed when is outlined in the table below.</p>
+
+ <table>
+ <tr>
+ <th>Range</th>
+ <th>Key</th>
+ <th>Sample Output</th>
+ </tr>
+ <tr>
+ <td>0 to 45 seconds</td>
+ <td>s</td>
+ <td>seconds ago</td>
+ </tr>
+ <tr>
+ <td>45 to 90 seconds</td>
+ <td>m</td>
+ <td>a minute ago</td>
+ </tr>
+ <tr>
+ <td>90 seconds to 45 minutes</td>
+ <td>mm</td>
+ <td>2 minutes ago ... 45 minutes ago</td>
+ </tr>
+ <tr>
+ <td>45 to 90 minutes</td>
+ <td>h</td>
+ <td>an hour ago</td>
+ </tr>
+ <tr>
+ <td>90 minutes to 22 hours </td>
+ <td>hh</td>
+ <td>2 hours ago ... 22 hours ago</td>
+ </tr>
+ <tr>
+ <td>22 to 36 hours</td>
+ <td>d</td>
+ <td>a day ago</td>
+ </tr>
+ <tr>
+ <td>36 hours to 25 days</td>
+ <td>dd</td>
+ <td>2 days ago ... 25 days ago</td>
+ </tr>
+ <tr>
+ <td>25 to 45 days</td>
+ <td>M</td>
+ <td>a month ago</td>
+ </tr>
+ <tr>
+ <td>45 to 345 days</td>
+ <td>MM</td>
+ <td>2 months ago ... 11 months ago</td>
+ </tr>
+ <tr>
+ <td>345 to 547 days (1.5 years)</td>
+ <td>y</td>
+ <td>a year ago</td>
+ </tr>
+ <tr>
+ <td>548 days+</td>
+ <td>yy</td>
+ <td>2 years ago ... 20 years ago</td>
+ </tr>
+ </table>
+
<h2>_date.fromNow()</h2>
<pre><code>_date.fromNow(withoutSuffix:boolean, asMilliseconds:boolean)</code></pre>
{
"name": "underscore.date",
- "version": "0.5.1",
+ "version": "0.5.2",
"description": "Underscore.date is a javascript date library that helps create, manipulate, and format dates without extending the `Date` prototype.",
"homepage": "https://github.com/timrwood/underscore.date",
"author": "Tim Wood <washwithcare@gmail.com> (http://timwoodcreates.com/)",
});
- test("_date().from() -- suffixless", 11, function() {
+ test("_date().from() -- suffixless", 30, function() {
var start = _date([2007, 1, 28]);
- equal(start.from(_date([2007, 1, 28]).add({s:30}), true), "seconds");
- equal(start.from(_date([2007, 1, 28]).add({s:60}), true), "a minute");
- equal(start.from(_date([2007, 1, 28]).add({m:5}), true), "5 minutes");
- equal(start.from(_date([2007, 1, 28]).add({h:1}), true), "an hour");
+ equal(start.from(_date([2007, 1, 28]).add({s:44}), true), "seconds");
+ equal(start.from(_date([2007, 1, 28]).add({s:45}), true), "a minute");
+ equal(start.from(_date([2007, 1, 28]).add({s:89}), true), "a minute");
+ equal(start.from(_date([2007, 1, 28]).add({s:90}), true), "2 minutes");
+ equal(start.from(_date([2007, 1, 28]).add({m:44}), true), "44 minutes");
+ equal(start.from(_date([2007, 1, 28]).add({m:45}), true), "an hour");
+ equal(start.from(_date([2007, 1, 28]).add({m:89}), true), "an hour");
+ equal(start.from(_date([2007, 1, 28]).add({m:90}), true), "2 hours");
equal(start.from(_date([2007, 1, 28]).add({h:5}), true), "5 hours");
+ equal(start.from(_date([2007, 1, 28]).add({h:21}), true), "21 hours");
+ equal(start.from(_date([2007, 1, 28]).add({h:22}), true), "a day");
+ equal(start.from(_date([2007, 1, 28]).add({h:35}), true), "a day");
+ equal(start.from(_date([2007, 1, 28]).add({h:36}), true), "2 days");
equal(start.from(_date([2007, 1, 28]).add({d:1}), true), "a day");
equal(start.from(_date([2007, 1, 28]).add({d:5}), true), "5 days");
+ equal(start.from(_date([2007, 1, 28]).add({d:25}), true), "25 days");
+ equal(start.from(_date([2007, 1, 28]).add({d:26}), true), "a month");
+ equal(start.from(_date([2007, 1, 28]).add({d:30}), true), "a month");
+ equal(start.from(_date([2007, 1, 28]).add({d:45}), true), "a month");
+ equal(start.from(_date([2007, 1, 28]).add({d:46}), true), "2 months");
+ equal(start.from(_date([2007, 1, 28]).add({d:75}), true), "2 months");
+ equal(start.from(_date([2007, 1, 28]).add({d:76}), true), "3 months");
equal(start.from(_date([2007, 1, 28]).add({M:1}), true), "a month");
equal(start.from(_date([2007, 1, 28]).add({M:5}), true), "5 months");
+ equal(start.from(_date([2007, 1, 28]).add({d:344}), true), "11 months");
+ equal(start.from(_date([2007, 1, 28]).add({d:345}), true), "a year");
+ equal(start.from(_date([2007, 1, 28]).add({d:547}), true), "a year");
+ equal(start.from(_date([2007, 1, 28]).add({d:548}), true), "2 years");
equal(start.from(_date([2007, 1, 28]).add({y:1}), true), "a year");
equal(start.from(_date([2007, 1, 28]).add({y:5}), true), "5 years");
});
<link rel="stylesheet" href="vendor/qunit.css" type="text/css" media="screen" />
<script type="text/javascript" src="vendor/jquery.js"></script>
<script type="text/javascript" src="vendor/underscore.js"></script>
- <script type="text/javascript" src="../underscore.date.js"></script>
+ <script type="text/javascript" src="../underscore.date.min.js"></script>
<script type="text/javascript" src="vendor/qunit.js"></script>
<script type="text/javascript" src="vendor/jslitmus.js"></script>
<script type="text/javascript" src="date.js"></script>
// (c) 2011 Tim Wood
// Underscore.date is freely distributable under the terms of the MIT license.
//
-// Version 0.5.1
+// Version 0.5.2
(function (undefined) {
- var _date;
+ var _date,
+ round = Math.round;
// left zero fill a number
// see http://jsperf.com/left-zero-filling for performance comparison
hours = minutes / 60,
days = hours / 24,
years = days / 365;
- return seconds < 45 && substituteTimeAgo('s', ~~ seconds) ||
- seconds < 90 && substituteTimeAgo('m') ||
- minutes < 45 && substituteTimeAgo('mm', ~~ minutes) ||
- minutes < 90 && substituteTimeAgo('h') ||
- hours < 24 && substituteTimeAgo('hh', ~~ hours) ||
- hours < 48 && substituteTimeAgo('d') ||
- days < 25 && substituteTimeAgo('dd', ~~ days) ||
+ return seconds < 45 && substituteTimeAgo('s', round(seconds)) ||
+ round(minutes) === 1 && substituteTimeAgo('m') ||
+ minutes < 45 && substituteTimeAgo('mm', round(minutes)) ||
+ round(hours) === 1 && substituteTimeAgo('h') ||
+ hours < 22 && substituteTimeAgo('hh', round(hours)) ||
+ round(days) === 1 && substituteTimeAgo('d') ||
+ days < 25 && substituteTimeAgo('dd', round(days)) ||
days < 45 && substituteTimeAgo('M') ||
- days < 350 && substituteTimeAgo('MM', ~~ ((days + 15) / 30)) ||
- years < 2 && substituteTimeAgo('y') ||
- substituteTimeAgo('yy', ~~ years);
+ days < 345 && substituteTimeAgo('MM', round(days / 30)) ||
+ round(years) === 1 && substituteTimeAgo('y') ||
+ substituteTimeAgo('yy', round(years));
}
UnderscoreDate.prototype = {
};
// CommonJS module is defined
- if (window === undefined && module !== undefined) {
+ if (typeof window === 'undefined' && typeof module !== 'undefined') {
// Export module
module.exports = _date;
// Integrate with Underscore.js
-(function(a){function l(a){var b=Math.abs(a)/1e3,c=b/60,d=c/60,e=d/24,f=e/365;return b<45&&j("s",~~b)||b<90&&j("m")||c<45&&j("mm",~~c)||c<90&&j("h")||d<24&&j("hh",~~d)||d<48&&j("d")||e<25&&j("dd",~~e)||e<45&&j("M")||e<350&&j("MM",~~((e+15)/30))||f<2&&j("y")||j("yy",~~f)}function k(a,b){return i(a)-i(b)}function j(a,c){return b.relativeTime[a].replace(/%d/i,c||1)}function i(a){return isNaN(a)?(new h(a)).date.getTime():a}function h(b,c){b&&b.date instanceof Date?this.date=b.date:c?this.date=g(b,c):this.date=b===a?new Date:b instanceof Date?b:e(b)?f(b):new Date(b)}function g(a,b){function j(a,b){switch(a){case"M":case"MM":c[1]=~~b-1;break;case"D":case"DD":case"DDD":case"DDDD":c[2]=~~b;break;case"YY":b=~~b,c[0]=b+(b>70?1900:2e3);break;case"YYYY":c[0]=~~b;break;case"a":case"A":i=b.toLowerCase()==="pm";break;case"H":case"HH":case"h":case"hh":c[3]=~~b;break;case"m":case"mm":c[4]=~~b;break;case"s":case"ss":c[5]=~~b}}var c=[0],d=/[0-9a-zA-Z]+/g,e=[],g=[],h,i;a.replace(d,function(a){e.push(a)}),b.replace(d,function(a){g.push(a)});for(h=0;h<g.length;h++)j(g[h],e[h]);i&&c[3]<12&&(c[3]+=12);return f(c)}function f(a){return new Date(a[0],a[1]||0,a[2]||1,a[3]||0,a[4]||0,a[5]||0,a[6]||0)}function e(a){return Object.prototype.toString.call(a)==="[object Array]"}function d(a,b,c){var d=(b.ms||0)+(b.s||0)*1e3+(b.m||0)*6e4+(b.h||0)*36e5+(b.d||0)*864e5+(b.w||0)*6048e5,e=(b.M||0)+(b.y||0)*12,f;d&&a.setMilliseconds(a.getMilliseconds()+d*c),e&&(f=a.getDate(),a.setDate(1),a.setMonth(a.getMonth()+e*c),a.setDate(Math.min((new Date(a.getFullYear(),a.getMonth()+1,0)).getDate(),f)));return a}function c(a,b){var c=a+"";while(c.length<b)c="0"+c;return c}var b;b=function(a,b){return new h(a,b)},b.months=["January","February","March","April","May","June","July","August","September","October","November","December"],b.monthsShort=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],b.weekdays=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],b.weekdaysShort=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],b.relativeTime={future:"in %s",past:"%s ago",s:"seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},b.ordinal=function(a){var b=a%10;return~~(a%100/10)===1?"th":b===1?"st":b===2?"nd":b===3?"rd":"th"},h.prototype={valueOf:function(){return this.date.getTime()},format:function(a){function o(a){var d,m;switch(a){case"M":return e+1;case"Mo":return e+1+b.ordinal(e+1);case"MM":return c(e+1,2);case"MMM":return b.monthsShort[e];case"MMMM":return b.months[e];case"D":return f;case"Do":return f+b.ordinal(f);case"DD":return c(f,2);case"DDD":d=new Date(g,e,f),m=new Date(g,0,1);return~~((d-m)/864e5+1.5);case"DDDo":d=o("DDD");return d+b.ordinal(d);case"DDDD":return c(o("DDD"),3);case"d":return h;case"do":return h+b.ordinal(h);case"ddd":return b.weekdaysShort[h];case"dddd":return b.weekdays[h];case"w":d=new Date(g,e,f-h+5),m=new Date(d.getFullYear(),0,4);return~~((d-m)/864e5/7+1.5);case"wo":d=o("w");return d+b.ordinal(d);case"ww":return c(o("w"),2);case"YY":return(g+"").slice(-2);case"YYYY":return g;case"a":return i>11?"pm":"am";case"A":return i>11?"PM":"AM";case"H":return i;case"HH":return c(i,2);case"h":return i%12||12;case"hh":return c(i%12||12,2);case"m":return j;case"mm":return c(j,2);case"s":return k;case"ss":return c(k,2);case"z":return o("zz").replace(n,"");case"zz":d=l.indexOf("(");if(d>-1)return l.slice(d+1,l.indexOf(")"));return l.slice(l.indexOf(":")).replace(n,"");default:return a.replace("\\","")}}var d=this.date,e=d.getMonth(),f=d.getDate(),g=d.getFullYear(),h=d.getDay(),i=d.getHours(),j=d.getMinutes(),k=d.getSeconds(),l=d.toString(),m=/(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|dddd?|do?|w[o|w]?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|zz?)/g,n=/[^A-Z]/g;return a.replace(m,o)},add:function(a){this.date=d(this.date,a,1);return this},subtract:function(a){this.date=d(this.date,a,-1);return this},from:function(a,c,d){var e=k(this.date,a),f=e<0?b.relativeTime.past:b.relativeTime.future;return d?e:c?l(e):f.replace(/%s/i,l(e))},fromNow:function(a,b){return this.from(new h,a,b)},isLeapYear:function(){var a=this.date.getFullYear();return a%4===0&&a%100!==0||a%400===0}},window===a&&module!==a?module.exports=b:(this._!==a&&this._.mixin!==a&&this._.mixin({date:b}),this._date=b)})()
\ No newline at end of file
+(function(a){function m(a){var b=Math.abs(a)/1e3,d=b/60,e=d/60,f=e/24,g=f/365;return b<45&&k("s",c(b))||c(d)===1&&k("m")||d<45&&k("mm",c(d))||c(e)===1&&k("h")||e<22&&k("hh",c(e))||c(f)===1&&k("d")||f<25&&k("dd",c(f))||f<45&&k("M")||f<345&&k("MM",c(f/30))||c(g)===1&&k("y")||k("yy",c(g))}function l(a,b){return j(a)-j(b)}function k(a,c){return b.relativeTime[a].replace(/%d/i,c||1)}function j(a){return isNaN(a)?(new i(a)).date.getTime():a}function i(b,c){b&&b.date instanceof Date?this.date=b.date:c?this.date=h(b,c):this.date=b===a?new Date:b instanceof Date?b:f(b)?g(b):new Date(b)}function h(a,b){function j(a,b){switch(a){case"M":case"MM":c[1]=~~b-1;break;case"D":case"DD":case"DDD":case"DDDD":c[2]=~~b;break;case"YY":b=~~b,c[0]=b+(b>70?1900:2e3);break;case"YYYY":c[0]=~~b;break;case"a":case"A":i=b.toLowerCase()==="pm";break;case"H":case"HH":case"h":case"hh":c[3]=~~b;break;case"m":case"mm":c[4]=~~b;break;case"s":case"ss":c[5]=~~b}}var c=[0],d=/[0-9a-zA-Z]+/g,e=[],f=[],h,i;a.replace(d,function(a){e.push(a)}),b.replace(d,function(a){f.push(a)});for(h=0;h<f.length;h++)j(f[h],e[h]);i&&c[3]<12&&(c[3]+=12);return g(c)}function g(a){return new Date(a[0],a[1]||0,a[2]||1,a[3]||0,a[4]||0,a[5]||0,a[6]||0)}function f(a){return Object.prototype.toString.call(a)==="[object Array]"}function e(a,b,c){var d=(b.ms||0)+(b.s||0)*1e3+(b.m||0)*6e4+(b.h||0)*36e5+(b.d||0)*864e5+(b.w||0)*6048e5,e=(b.M||0)+(b.y||0)*12,f;d&&a.setMilliseconds(a.getMilliseconds()+d*c),e&&(f=a.getDate(),a.setDate(1),a.setMonth(a.getMonth()+e*c),a.setDate(Math.min((new Date(a.getFullYear(),a.getMonth()+1,0)).getDate(),f)));return a}function d(a,b){var c=a+"";while(c.length<b)c="0"+c;return c}var b,c=Math.round;b=function(a,b){return new i(a,b)},b.months=["January","February","March","April","May","June","July","August","September","October","November","December"],b.monthsShort=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],b.weekdays=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],b.weekdaysShort=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],b.relativeTime={future:"in %s",past:"%s ago",s:"seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},b.ordinal=function(a){var b=a%10;return~~(a%100/10)===1?"th":b===1?"st":b===2?"nd":b===3?"rd":"th"},i.prototype={valueOf:function(){return this.date.getTime()},format:function(a){function o(a){var c,m;switch(a){case"M":return e+1;case"Mo":return e+1+b.ordinal(e+1);case"MM":return d(e+1,2);case"MMM":return b.monthsShort[e];case"MMMM":return b.months[e];case"D":return f;case"Do":return f+b.ordinal(f);case"DD":return d(f,2);case"DDD":c=new Date(g,e,f),m=new Date(g,0,1);return~~((c-m)/864e5+1.5);case"DDDo":c=o("DDD");return c+b.ordinal(c);case"DDDD":return d(o("DDD"),3);case"d":return h;case"do":return h+b.ordinal(h);case"ddd":return b.weekdaysShort[h];case"dddd":return b.weekdays[h];case"w":c=new Date(g,e,f-h+5),m=new Date(c.getFullYear(),0,4);return~~((c-m)/864e5/7+1.5);case"wo":c=o("w");return c+b.ordinal(c);case"ww":return d(o("w"),2);case"YY":return(g+"").slice(-2);case"YYYY":return g;case"a":return i>11?"pm":"am";case"A":return i>11?"PM":"AM";case"H":return i;case"HH":return d(i,2);case"h":return i%12||12;case"hh":return d(i%12||12,2);case"m":return j;case"mm":return d(j,2);case"s":return k;case"ss":return d(k,2);case"z":return o("zz").replace(n,"");case"zz":c=l.indexOf("(");if(c>-1)return l.slice(c+1,l.indexOf(")"));return l.slice(l.indexOf(":")).replace(n,"");default:return a.replace("\\","")}}var c=this.date,e=c.getMonth(),f=c.getDate(),g=c.getFullYear(),h=c.getDay(),i=c.getHours(),j=c.getMinutes(),k=c.getSeconds(),l=c.toString(),m=/(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|dddd?|do?|w[o|w]?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|zz?)/g,n=/[^A-Z]/g;return a.replace(m,o)},add:function(a){this.date=e(this.date,a,1);return this},subtract:function(a){this.date=e(this.date,a,-1);return this},from:function(a,c,d){var e=l(this.date,a),f=e<0?b.relativeTime.past:b.relativeTime.future;return d?e:c?m(e):f.replace(/%s/i,m(e))},fromNow:function(a,b){return this.from(new i,a,b)},isLeapYear:function(){var a=this.date.getFullYear();return a%4===0&&a%100!==0||a%400===0}},typeof window=="undefined"&&typeof module!="undefined"?module.exports=b:(this._!==a&&this._.mixin!==a&&this._.mixin({date:b}),this._date=b)})()
\ No newline at end of file