Hello, githubs! :)
A small utility to easily add/subtract time intervals from JavaScript Date object.
offsetDate(interval, date)
`interval` - {String} - time interval in special format
`date` - starting date
Outputs JS Date object. Original `date` value will be left intact (returns modified copy)
I struggled a lot to make this syntax clean, predictable and easy to remember (some features were lost during the amputation process though -_-)
Typical format is as follows:
<sign><value><modifier>
Typical examples of the format are:
+1y // add 1 year
-2M // subtract 2 months
Easy, huh? :)
Supported modifiers should look familiar if you ever used any other JS date-related library:
y - years
M - months
d - days
h - hours
m - minutes
s - seconds
ms - milliseconds
w - weeks
MM - months (with side effect*)
So let's get down to the examples and use cases:
var date = new Date(2011, 0, 30, 13, 30, 25); // 2011-01-30 13:30:25
// add 1 day
offsetDate('+1d', date); // 2011-01-30 11:30:25
// subtract 2 hours
offsetDate('-2h', date); // 2011-01-30 11:30:25
// add 1 week (notice, +w is the same as +1w)
offsetDate('+w', date); // 2011-02-06 11:30:25
Two or more intervals can be separated by one or more spaces. Operations will be executed one after another
var date = new Date(2011, 0, 30, 13, 30, 25); // 2011-01-30 13:30:25
// add 1 hour 30 minutes
offsetDate('+1h +30m', date); // 2011-01-03 15:00:25
// this stuff does nothing
offsetDate('+1h -1h', date);
// add 2 days
offsetDate('+d +d', date);
// WARNING! Be careful with months as they may cause date changes
offsetDate('+M +M', date); // 2011-03-28 ...
offsetDate('+2M', date); // 2011-03-30 ...
While hours, minutes, seconds and milliseconds are pretty much predictable, Date is all about struggling with dates and months and leap years.
// Jan 30 2011
var date = new Date(2011, 0, 30);
// change month to February?
date.setMonth(1);
// WTF?
date.toString(); // "Wed Mar 02 2011 00:00:00 GMT-0800 (Pacific Standard Time)"
date.getMonth(); // 2
offsetDate
takes that into consideration:
var date = new Date(2011, 0, 30); // 2011-01-30
// adding 1 month guarantees month value will be increased by 1
offsetDate('+1M', date); // 2011-02-28
// and it also takes leap years into consideration
offsetDate('+1M', new Date(2012, 0, 30)); // 2012-02-29
// if you really REALLY want that native weird behavior, here's the gun,
// you know where to find your foot
offsetDate('+1MM', date); // 2011-03-02
But what if you would like to add some arbitrary number of days but prevent Date from jumping to the next month?
You can do this by adding "!" at the end of the interval. "!" guarantees that given parameter will remain withing an applicable range. Month changes may affect dates. For everyone else, "!" means other parameters will be left intact.
var date = new Date(2011, 0, 30);
// "!" guarantees that date will not exceed last day of the month
offsetDate('+5d!', date); // 2011-01-31
// works pretty much for everything, except years (centuries, epochs are not supported)
offsetDate('-35d!', date); // 2011-01-01
// weeks are capped by current month (subject to change though..., so use "7d" for this cases)
offsetDate('+w!', date); // 2011-01-31
Most of the time, however, just adding interval would not be enough.
var date = new Date(2011, 0, 30, 13, 30, 25);
// adding 1 day adds EXACTLY 24 hours
offsetDate('+d', date); // 2011-01-31 13:30:25
Sometimes we need to truncate time or even date information. For example if we want to know whether some date is in some date range.
Let's say, I need to check whether Alice's birthday is this month
var alice = new Date(2011, 1, 1); // Alice's birthday is Feb 1st
// Alice?
function aliceHasBirthdayThisMonth(today) {
return alice >= offsetDate('-99d!', today) && alice <= offsetDate('+99d!', today)
}
var date = new Date(2011, 1, 2, 13, 30, 25); // now is 2011-02-02 13:30:25
// surprise!
aliceHasBirthdayThisMonth(date); // false;
This happens because offset('-99d!', today)
results in 2011-02-01 13:30:25
because as we said
before "d!" does not affect time.
So you have to keep "!"-ing for hours and minutes and seconds and milliseconds (no pun intended) :)
offsetDate('-99d! -24h! -60m! -60s! -1000ms!', date) // 2011-02-01 00:00:00.000
So I decided to add trimming to spare you some time. To do it, just add "~" at the end of the interval. It would trim everything after that modifier.
var date = new Date(2011, 1, 2, 13, 30, 25); // now is 2011-02-02 13:30:25
// add month, then keep month and year, trim everything else
offsetDate('+1M~', date); // 2011-03-01 00:00:00.000
// but you can also write the same as above like this:
offsetDate('+M~', date);
// to specify beginning of this month, you can do
offsetDate('+0M~', date); // 2011-02-01 00:00:00.000
// but you can ommit "+0" for this case and get a cleaner syntax:
offsetDate('M~', date); // 2011-02-01 00:00:00.000
Trimming date is like rounding a number to a certain precision.
`y~` will keep year and trim month, date, hours, minutes, seconds and milliseconds
`M~` will keep year and month and trim date, etc.
`d~` will keep year, month and date.
`w~` will round date to the beginning of the current week. (I think it requires localization, tho)
You can think of "~" as equivalent of "go to the beginning of ...";
var date = new Date(2011, 1, 2, 13, 30, 25); // now is 2011-02-02 13:30:25
// beginning of this week
offsetDate('w~', date); // Sun, 2011-01-30 00:00:00.000
// beginning of next week:
offsetDate('+w~', date); // Sun, 2011-02-06 00:00:00.000
// beginning of the month before the previous month
offsetDate('-2M~', date); // 2010-11-01 00:00:00.000
// there are many ways you can go crazy
// last moment before Christmas
offsetDate('y~ +11M +24d -1ms', date); // 2011-12-24 23:59:59.999
offsetDate('+y~ -7d -1ms', date);
The final listing for the example with Alice's birthday would be
var alice = new Date(2011, 1, 1); // Alice's birthday is Feb 1st
// Alice?
function aliceHasBirthdayThisMonth(today) {
// from beginning of this month to the beginning of next month
return alice >= offsetDate('M~', today) && alice < offsetDate('+M~', today)
}
var date = new Date(2011, 1, 2, 13, 30, 25); // now is 2011-02-02 13:30:25
// no surprise!
aliceHasBirthdayThisMonth(date); // true;
Oh, yeah... keep "!" before "~".
-
There's no way to set a specific year with current syntax... May be "2008y" or something will be the way to do it, but all my previous attempts made syntax too complicated to remember.
-
Weeks are bound by month in strict mode ("w!"), not sure if anyone needs that
If you would like to contribute to this small project, feel free to send pull requests!
Please, make sure that tests in /test/SpecRunner.htm
are not broken. (Just open it in any
browser and make sure it's green :)). And submit test cases along with your pull requests.
It's Open-source and thus free and open to use for whatever you want.
Cheerz,
TEHEK =]