
Date math in Linux shell script?Within a Linux shell script to be used as a cron job, how do I calculate the current date, the current date - n days, and the current date + n days ? This script is to be used to partition an oracle database, and automatically drop old partitions (n+1) and create a new partition (n+1). While I'm sure this will amaze some people, I am going to actually take the daring step of suggesting that date math is rather difficult to accomplish in many Unix and Linux shell script environments, and it's much easier to actually write a brief C program to accomplish your task. Yes, I've written one of the most popular shell script programming books ever written (Wicked Cool Shell Scripts), but even I don't think that you can solve every problem with a script! :-) There's a version of the date command (commonly known as GNU date) that allows some rudimentary mathematical calculations, but that's only useful if you have the more sophisticated version of this particular utility. Instead, the key to working with date math on a Unix system is to remember that Unix (and Linux) work on what's called epoch time, the number of seconds since a specific date back in 1970. As I write this entry, for example, the "epoch time" is 1131948327. Once you have that, and a utility that turns epoch time into a nice, readable date, it's easy enough to add or delete days: just add or subtract 60*60*24 seconds for each day in question. Here it is as a C utility: /** DATEMATH - Demonstration program that shows how to
do date mathematics by utilizing Unix procedures.
This is missing some error checking, etc., but will
show how to do "+n" and "-n" date math well enough.
(C) Copyright 2005 by Dave Taylor. Free to redistribute
if this copyright is left intact. Thanks.
***/
#include <stdio.h>
#include <time.h>
#define ONEDAY 60*60*24
time_t time(time_t *tloc);
char *ctime(const time_t *clock);
main(int argc, char **argv)
{
time_t theTime;
int offset = 1;
if (argc > 1) offset = atoi(argv[1]);
theTime = time((time_t *) NULL);
theTime += (long) (offset * ONEDAY);
printf("Offset by %d, the date is: %s\n",
offset, ctime(&theTime));
}
As you can see, the utility grabs the current epoch time by using the time() function, adds any days necessary by multiplying the requested offset by the seconds in a day ONEDAY, then uses the ctime() function to output a standard date string. Here's the utility at work: $ ./a.out +41 Offset by 41, the date is: Sun Dec 25 06:16:44 2005 Yes, that means there are only 41 shopping days until Christmas! $ ./a.out -41 Offset by -41, the date is: Tue Oct 4 06:16:50 2005 $ ./a.out -5000 Offset by -5000, the date is: Sat Mar 7 06:16:55 1992 $ ./a.out -999 Offset by -999, the date is: Wed Feb 19 06:17:13 2003 $ ./a.out 365 Offset by 365, the date is: Tue Nov 14 06:17:28 2006 I hope that helps you out!
Help others find this article at Del.icio.us, Digg, Netscape, Reddit, and Simpy.
Categorized:
Shell Script Programming
(Article 4275)
Tagged: Previous: Can I have AdSense and other Ads on my site simultaneously? Next: Can people hack my Google Gmail account? Subscribe!
Never miss another useful Q&A article again! Subscribe to AskDaveTaylor with Google Reader. In a former job I once wrote scripts that did date arithmetic entirely in Korn shell, and I expect the method would translate into other shell languages as well. I no longer have access to the scripts and no convenient access to a Unix/Linux system to reconstruct them, but I will describe the method and leave the details up to the reader. This requires an understanding of certain facts about Unix (about shells, actually) and the "date" command. First, the "date" command computes the human-readable date output based on the timezone specified in the environment (something like TZ= or TIMEZONE=). You can see which your system uses by looking in /etc/TIMEZONE or the like. Second, the TZ= (or whatever) variable supports different formats for the timezone, including the ability to specify positive or negative offsets from some timezone (something like EDT-1 or GMT+6). Third, you may already have realized that if the displacement is a multiple of 24 hours, then the date can be shifted by that number of days. Next, it is very easy to specify a change in a command's environment only for the duration of the command. When used with the "date" command, this allows you to change the timezone of the date returned; for example, "TZ=$TZ-2 date" (or the like; ignore the quotes) will show you the date and time two hours displaced. Finally, shells can do arithmetic. Dumb old shells use the "expr" command, newer ones do it directly. I like the Korn shell method. So to pull it all together, if you treat the following as metacode and not gospel, something like this should work for you: OFFSET_DATE=$(TZ=$TZ+$((TZ * 24)) date) Of course you can use the entire range of "date" command options to pull out any particular item of a date you wish. And if you really like a challenge, you can even control the date shift down to the minute or second, allowing you to force outputs for some particular time of day, say noon for example. Have fun and happy scripting! Posted by: Phillip Schearer at December 21, 2005 7:42 PMSorry, that example should have been: OFFSET_DATE=$(TZ=$TZ+$((DAYS * 24)) date) The offset can be positive or negative. The $(...) is command substitution and the $((...)) is arithmetic substitution. You may have to quote the asterisk. Posted by: Phillip Schearer at December 21, 2005 8:39 PMTry running these, see what you get: Thanks, Peter, but realize that what you're referencing isn't a flag that's available in all versions of Unix / Linux... Posted by: Dave Taylor at January 13, 2006 12:11 AMThe following code will convert any given date to any offset using only GNU compliant ksh (e.g. bash) script tools. Note: This is not 100% Unix portable since it only works if the date supports the non-RFC compliant “–d” option. GNU does (i.e. Linux) but I doubt it will work on older HP-UX or Solaris systems (but hey, try it and see -- ymmv) The basic algorithm uses the fact that the ‘date’ command can output the “seconds since epoch” as well as be provided a date other than “now” with the –d option (see above disclaimer). So, if we give it a specific date, and request the output in %s we get the “seconds since the epoch” for the date we’ve supplied. Now, we apply our offset (+ or -) and resubmit the date command with the modified epoch time. ----- Start of Script ----- #!/bin/bash # The date we want to convert # provide the original date instead of "now" and ask for "seconds since epoch" # Now apply our offset, in this example we subtract 1 day # We now have our target date in EPOCH format, we need to get it back to our # And there we have it! $NEW_DATE contains "20060331" # For those that like a tidyier script (like me) echo "ORIGINAL_DATE = $ORIGINAL_DATE" ----- End of Script ----- prints out: ORIGINAL_DATE = 20060401 There you have it. Enjoy! This site is very Good. Epoch time is the amount of time since a fixed point in the history of Unix itself, and that's what's displayed when you specify the '-r' flag to date followed by a number of seconds offset. For example: $ date -r 0 Zero seconds after the epoch time is the date and time shown above, basically 0:00:00 GMT on 31 December 1969. Why that time and date? Just ... because. :-) A few more, for fun: $ date -r 999999999 #!/bin/bash In the above example of date operation using EPOCH, the resultant date will be set as per GMT time zone. e.g. if we use epoch value, it will return seconds since 1970, Jan 01 GMT value. now if my machine time zone is different than GMT i.e. IST(+0530), then to set correct time I have to add addition seconds for that particular time zone. Is there any command which can convert or give extra seconds that I need to add or subtract to get actual time? Thanks... Posted by: viki at December 30, 2006 7:05 AMHey guys, what about this ;-) ? ---------------------------------------------- dateformat=$2 -------------------------------------------------
But why 1 hour late? Posted by: alex at February 13, 2007 3:44 PMRegarding all questions about why the time is off by hour(s): date assumes the input is in the local timezone, however epoch time is given in GMT. #First, we get our local timezone offset, i.e. +0130 A shell only possibility for calculating dates in the future or the past. date --date @$(($(date +%s)-(3600*24*2))) +%Y/%m/%d Tested with bash and zsh. Posted by: Marcus at April 10, 2007 8:08 PMThanx peter for the info ... the command date -d "-1 day" .. was really helpful Posted by: Prashant at April 29, 2007 11:54 PMHere is something I found that gets the job done. Using some shell commands in Linux or Unix to write a script that calculated the previous or future date or any date for that matter using the "date" and "cal" command. Taking in account that the previous date to the first of any month is the last day of the previous month. "cal" does a good enough job to give you the days of any month for any year since Jan 1 1970. I used the following command in my scripts to get the dates I need to use in variables: Get the current numerical day Get the last numerical date of the previous month Hugh: Your "cal" trick is nice but your math for the previous day and month breaks pretty badly around Jan 1st. I'd suggest using the TZ trick to set DAYLESS and YEAR properly for the target date and then you can use cal to count the number of days in that month. Posted by: Kevin at June 1, 2007 8:42 AMI think any algorithm that does arithmetic on the epoch time will have problems crossing daylight savings boundary as days on these boundaries are not 24 hours (in terms of number of seconds from 2am to 2am). The TZ trick might also have issues with daylight savings. So you might want to use Hugh's code, fixed up to handle month boundaries as suggested by Kevin. Posted by: Jay Goldman at October 1, 2007 9:15 PMOops! In my previous post I meant to say that the "Modification" and "Change" date time stamps change when new content is echoed to the end of the file. It is the "Access" date that remains unchanged. Nevertheless, if one wanted to measure "Access" time they could do it easily with the "stat" command. Posted by: brian at January 16, 2008 8:40 AMhttp://www.askdavetaylor.com/date_math_in_linux_shell_script.html Ok, I guess this really never got posted then... Though I would like to do "Date math in Linux shell script", my application is simply to test which file is newer than another based on "Access date". Note that there bash has the "-nt" newer than conditional test for files (e.g. file1 -nt file2), but the bash man page says that is according to the modification date. [del] --When I append a line to a file (e.g. cat "# my final comment" >> file2) and run the "stat" command, I see that only the "Access date" has changed - not the modification date.--[/del] correction: The "Modification" and "Change" dates are updated! Fortunately, there is an option in the "stat" command to return the MAC timestamps of a file in epoch time where Access/Modify/Change are given in format %X/%Y/%Z. Usage: Now you can easily perform mathematical operations in seconds and date time stamp comparisons (e.g. is file1 > 10 secs newer than file2?) without ANY parsing and extra computation! I'm not sure that the "stat" command is standard on every Unix/BSD/Linux platform - but it should be available as a package! Posted by: brian at January 16, 2008 8:44 AMThanks a lot! I try them, Peter's one is fine. Posted by: Yang at February 12, 2008 12:49 PMI have a lot to say, but ...
I do have a comment, now that you mention it!
|
Search
Find just the answers you seek from among our 1700+ free tech support articles by using our Lijit search engine.
Help!
Subscribe to
Ask Dave Taylor!
Free Updates!
Sign up and get free weekly updates and special offers on books, seminars, workshops and more.
Articles and Reviews
Auctions and Online Shopping Blogs and RSS Feeds Building Web site traffic Business and Management Cell Phones and Mobile Phones CGI Scripts and Web Site Programming Computer and Internet Basics d) None of the Above HTML and CSS Mac OS X Help MySpace, Facebook, Twitter and Social Network Help Pay Per Click (PPC) Search Engine Optimization Shell Script Programming Sony PSP, MP3 Players, Etc. The Writing Business Unix and Linux Help Video Game Tips and Help Windows Help
Recent Entries
Join the List!
Book Links
|