{"id":329,"date":"2018-10-17T09:31:15","date_gmt":"2018-10-17T09:31:15","guid":{"rendered":"https:\/\/www.appservgrid.com\/paw92\/index.php\/2018\/10\/17\/doing-date-math-on-the-command-line-part-i\/"},"modified":"2018-10-17T09:31:15","modified_gmt":"2018-10-17T09:31:15","slug":"doing-date-math-on-the-command-line-part-i","status":"publish","type":"post","link":"https:\/\/www.appservgrid.com\/paw92\/index.php\/2018\/10\/17\/doing-date-math-on-the-command-line-part-i\/","title":{"rendered":"Doing Date Math on the Command Line, Part I"},"content":{"rendered":"<p>If you&#8217;ve ever used a spreadsheet, you&#8217;ve probably used or seen<br \/>\nfunctions for doing date math\u2014in other words, taking one date and adding some number<br \/>\nof days or months to it to get a new date, or taking two<br \/>\ndates and finding the number days between them.<br \/>\nThe same thing can be done from the command line using<br \/>\nthe lowly date command, possibly with a little<br \/>\nhelp from Bash&#8217;s arithmetic.<\/p>\n<p>The two most important features of the date command to<br \/>\nunderstand for doing date math are the +FORMAT option<br \/>\nand the &#8211;date option.<br \/>\nThe formatting option allows you to format the date in different ways<br \/>\nor get just certain parts of the date, such as the year,<br \/>\nthe month, the day and so on:<\/p>\n<p><b>$ date &#8216;+%Y&#8217;<\/b><br \/>\n2018<br \/>\n<b>$ date &#8216;+%m&#8217;<\/b><br \/>\n09<br \/>\n<b>$ date &#8216;+%d&#8217;<\/b><br \/>\n16<\/p>\n<p>There are a few dozen formatting options; see the <a href=\"http:\/\/man7.org\/linux\/man-pages\/man1\/date.1.html\">date(1)<\/a><br \/>\nman page for more.<\/p>\n<p>The second option, the &#8211;date (or -d) option, allows you to specify<br \/>\nthe date to use, rather than the current date:<\/p>\n<p><b>$ date &#8211;date &#8216;March 1, 2015&#8217; &#8220;+%Y&#8221;<\/b><br \/>\n2015<br \/>\n<b>$ date &#8211;date=&#8217;March 1, 2015&#8242; &#8220;+%m&#8221;<\/b><br \/>\n03<br \/>\n<b>$ date -d &#8216;March 1, 2015&#8217; &#8220;+%d&#8221;<\/b><br \/>\n01<\/p>\n<p>The interesting part of the &#8211;date option is that it allows you to<br \/>\nspecify date strings in a variety of ways. For example, given the<br \/>\nabove date of &#8220;March 1, 2015&#8221;, you can calculate dates relative to it:<\/p>\n<p><b>$ date &#8211;date &#8216;March 1, 2015 +7 days&#8217;<\/b><br \/>\nSun Mar 8 00:00:00 MST 2015<br \/>\n<b>$ date &#8211;date &#8216;March 1, 2015 -1 year&#8217;<\/b><br \/>\nSat Mar 1 00:00:00 MST 2014<br \/>\n<b>$ date &#8211;date &#8216;March 1, 2015 +12 days +12 hours +15 minutes&#8217;<\/b><br \/>\nFri Mar 13 12:15:00 MST 2015<\/p>\n<p>You also can use strings that refer to previous or upcoming days<br \/>\nof the week (relative to the current date):<\/p>\n<p><b>$ date &#8211;date &#8216;last Monday&#8217;<\/b><br \/>\nMon Sep 10 00:00:00 MST 2018<br \/>\n<b>$ date &#8211;date &#8216;next Monday&#8217;<\/b><br \/>\nMon Sep 17 00:00:00 MST 2018<\/p>\n<p>Note, however, that combining a date and things like &#8220;next Monday&#8221; does not work.<br \/>\nThe <a href=\"http:\/\/man7.org\/linux\/man-pages\/man1\/date.1.html\">date(1)<\/a> man page has a bit more information about the types<br \/>\nof date strings you can specify, however, the <a href=\"https:\/\/www.gnu.org\/software\/coreutils\/manual\/html_node\/Examples-of-date.html\">GNU Info date<\/a> page<br \/>\nhas much more complete documentation.<\/p>\n<p>So now that you&#8217;ve seen the basics of doing date math, let&#8217;s use it<br \/>\nto do something.<br \/>\nThis example comes from some recent scripts I was working on<br \/>\nwhile organizing Linux Journal&#8217;s online archives.<br \/>\nEach month we publish a new issue.<br \/>\nEach issue has an issue number that&#8217;s one more than the previous issue.<br \/>\nIssue numbers are easy to deal with when specifying arguments<br \/>\nto a script, but readers tend to think more in terms of months and years.<br \/>\nSo in a couple places I needed to generate months and years from issue numbers.<\/p>\n<p>To not complicate the example, let&#8217;s ignore a couple breaks in<br \/>\n<em>LJ<\/em>&#8216;s publishing history.<br \/>\nSo, let&#8217;s use issue #284 from March 2018 as the starting point<br \/>\nand make all calculations relative to it.<br \/>\nThe script takes one or more issue numbers and prints out the month<br \/>\nand year corresponding to that issue number:<\/p>\n<p><b>$ cat date1.sh<\/b><br \/>\n1 for inum in $*<br \/>\n2 do<br \/>\n3 [[ &#8220;$inum&#8221; =~ ^[0-9]$ ]] || { echo &#8220;Invalid issue number: $inum&#8221;; continue; }<br \/>\n4<br \/>\n5 month=$(date &#8211;date=&#8221;March 1, 2018 +$((inum &#8211; 284)) month&#8221; +&#8217;%B&#8217;)<br \/>\n6 year=$(date &#8211;date=&#8221;March 1, 2018 +$((inum &#8211; 284)) month&#8221; +&#8217;%Y&#8217;)<br \/>\n7 printf &#8220;Issue %d is from %s, %sn&#8221; $inum $month $year<br \/>\n8 done<\/p>\n<p><b>$ bash date1.sh 284 285<\/b><br \/>\nIssue 284 is from March, 2018<br \/>\nIssue 285 is from April, 2018<\/p>\n<p>Lines 5 and 6 calculate the number of issues<br \/>\nthat have elapsed since issue 284 using Bash&#8217;s arithmetic<br \/>\nexpression syntax: $((inum &#8211; 284)).<br \/>\nThat result then is used to add that many months onto the<br \/>\ndate of the first issue, which gives the date<br \/>\nof the issue in question.<br \/>\nThen the date command&#8217;s format options provides the month and year<br \/>\n(the %B and %Y format specifiers, respectively).<br \/>\nThose values next are used to print out the month and year of the issue.<br \/>\nAnd just in case you were wondering, issue #1000 will be coming out<br \/>\nin November 2077.<\/p>\n<p>One thing that can you trip you up when doing date math is that<br \/>\nif you use dates near the end of the month when adding or<br \/>\nsubtracting months, you can end up with unexpected results.<br \/>\nThis is because the date command uses the same value for the<br \/>\nlength of each month when it adds months and ignores the fact<br \/>\nthat not all months are the same length.<br \/>\nSo, for example, look what happens if you change the reference<br \/>\ndate in the script above from the start of the month to the end of the month:<\/p>\n<p><b>$ cat date2.sh<\/b><br \/>\n5 month=$(date &#8211;date=&#8221;March 31, 2018 +$((inum &#8211; 284)) month&#8221; +&#8217;%B&#8217;)<br \/>\n6 year=$(date &#8211;date=&#8221;March 31, 2018 +$((inum &#8211; 284)) month&#8221; +&#8217;%Y&#8217;)<\/p>\n<p><b>$ bash date2.sh 284 285<\/b><br \/>\nIssue 284 is from March, 2018<br \/>\nIssue 285 is from May, 2018<\/p>\n<p>This incorrectly labels issue 285 as being in May rather<br \/>\nthan April.<br \/>\nYou can verify this if you run the effective date command<br \/>\nby itself:<\/p>\n<p><b>$ date &#8211;date=&#8221;March 31, 2018 +1 month&#8221;<\/b><br \/>\nTue May 1 00:00:00 MST 2018<\/p>\n<p>Since April has only 30 days, you end up skipping to the first of May.<\/p>\n<p>The other common type of date math that I mentioned above is calculating<br \/>\nthe difference between two dates.<br \/>\nUnfortunately, the date command doesn&#8217;t support this directly.<br \/>\nTo do that, you need to convert the dates in question into the number<br \/>\nof seconds since the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Unix_time\">epoch<\/a> (using the %s format specifier),<br \/>\nand then subtract the values and divide by the number of seconds<br \/>\nin a day (86,400) to get the days in between:<\/p>\n<p><b>$ cat date3.sh<\/b><br \/>\nstart_date=&#8221;March 1, 2018&#8243;<br \/>\nend_date=&#8221;March 31, 2018&#8243;<\/p>\n<p>sdate=$(date &#8211;date=&#8221;$start_date&#8221; &#8216;+%s&#8217;)<br \/>\nedate=$(date &#8211;date=&#8221;$end_date&#8221; &#8216;+%s&#8217;)<br \/>\ndays=$(( (edate &#8211; sdate) \/ 86400 ))<br \/>\necho &#8220;$days days between $start_date and $end_date&#8221;<\/p>\n<p><b>$ bash date3.sh<\/b><br \/>\n30 days between March 1, 2018 and March 31, 2018<\/p>\n<p>In part two of this article, I plan to tackle how to get a<br \/>\nspecific previous day of the week relative to a specific date<br \/>\n(which, as I noted above, is not directly doable with the date command).<br \/>\nIn other words, I want to be able to do something that effectively<br \/>\ndoes the following:<\/p>\n<p><b>$ date &#8211;date &#8216;March 1, 2015 previous Monday&#8217; # won&#8217;t work<\/b><br \/>\ndate: invalid date \u2018March 1 2015 previous Monday\u2019<br \/>\n <br \/> <a href=\"https:\/\/www.linuxjournal.com\/content\/doing-date-math-command-line-part-i\" target=\"_blank\">Source<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you&#8217;ve ever used a spreadsheet, you&#8217;ve probably used or seen functions for doing date math\u2014in other words, taking one date and adding some number of days or months to it to get a new date, or taking two dates and finding the number days between them. The same thing can be done from the &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.appservgrid.com\/paw92\/index.php\/2018\/10\/17\/doing-date-math-on-the-command-line-part-i\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Doing Date Math on the Command Line, Part I&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-329","post","type-post","status-publish","format-standard","hentry","category-linux"],"_links":{"self":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/posts\/329","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/comments?post=329"}],"version-history":[{"count":0,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/posts\/329\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/media?parent=329"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/categories?post=329"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/tags?post=329"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}