Days between dates
- Task
Calculate the number of days between two dates. Date input should be of the form YYYY-MM-DD.
- Motivation
To demonstrate one of the numerous ways this can be done.
Ada
<lang Ada>with Ada.Calendar; with Ada.Text_IO; with Ada.Integer_Text_IO;
with GNAT.Calendar.Time_IO;
procedure Days_Between_Dates is
function Days_Between (Lower_Date : in Ada.Calendar.Time; Higher_Date : in Ada.Calendar.Time) return Integer is use Ada.Calendar; Diff : constant Duration := Higher_Date - Lower_Date; begin return Integer (Diff / Day_Duration'Last); end Days_Between;
procedure Put_Days_Between (Lower_Date : in String; Higher_Date : in String; Comment : in String) is use Ada.Text_IO; use Ada.Integer_Text_IO; use GNAT.Calendar.Time_IO;
Diff : constant Integer := Days_Between (Lower_Date => Value (Lower_Date), Higher_Date => Value (Higher_Date)); begin Put ("Days between " & Lower_Date & " and " & Higher_Date & " is "); Put (Diff, Width => 5); Put (" days -- "); Put (Comment); New_Line; end Put_Days_Between;
begin
Put_Days_Between ("1995-11-21", "1995-11-21", "Identical dates"); Put_Days_Between ("2019-01-01", "2019-01-02", "Positive span"); Put_Days_Between ("2019-01-02", "2019-01-01", "Negative span"); Put_Days_Between ("2019-01-01", "2019-03-01", "Non-leap year"); Put_Days_Between ("2020-01-01", "2020-03-01", "Leap year"); Put_Days_Between ("1902-01-01", "1968-12-25", "Past"); Put_Days_Between ("2090-01-01", "2098-12-25", "Future"); Put_Days_Between ("1902-01-01", "2098-12-25", "Long span");
end Days_Between_Dates;</lang>
- Output:
Days between 1995-11-21 and 1995-11-21 is 0 days -- Identical dates Days between 2019-01-01 and 2019-01-02 is 1 days -- Positive span Days between 2019-01-02 and 2019-01-01 is -1 days -- Negative span Days between 2019-01-01 and 2019-03-01 is 59 days -- Non-leap year Days between 2020-01-01 and 2020-03-01 is 60 days -- Leap year Days between 1902-01-01 and 1968-12-25 is 24465 days -- Past Days between 2090-01-01 and 2098-12-25 is 3280 days -- Future Days between 1902-01-01 and 2098-12-25 is 71947 days -- Long span
AppleScript
<lang applescript>on daysBetweenDates(date1, date2)
considering numeric strings -- Allows for leading zeros having been omitted. if (date1 = date2) then return date1 & " and " & date2 & " are the same date" end considering -- Get the components of each date. set astid to AppleScript's text item delimiters set AppleScript's text item delimiters to "-" set {y1, m1, d1} to date1's text items set {y2, m2, d2} to date2's text items set AppleScript's text item delimiters to astid -- Derive AppleScript date objects. -- The best way to do this generally is to use the 'current date' function to obtain a date object -- and then to set the properties of the result (or of a copy) to the required values. -- The initial setting of the day values to 1 is to prevent overflow due to possibly -- incompatible day and month values during the sequential setting of the properties. -- The integer values set here are automatically coerced from the text values obtained above. tell (current date) to set {its day, its year, its month, its day, its time, firstDate} to {1, y1, m1, d1, 0, it} copy firstDate to secondDate tell secondDate to set {its day, its year, its month, its day} to {1, y2, m2, d2} -- Do the math. set daysDifference to (firstDate - secondDate) div days -- Format some output. if (daysDifference > 0) then return date1 & " comes " & daysDifference & " day" & item (((daysDifference is 1) as integer) + 1) of {"s", ""} & ¬ " after " & date2 else return date1 & " comes " & -daysDifference & " day" & item (((daysDifference is -1) as integer) + 1) of {"s", ""} & ¬ " before " & date2 end if
end daysBetweenDates
return daysBetweenDates("2020-04-11", "2001-01-01") & linefeed & ¬
daysBetweenDates("2020-04-11", "2020-04-12") & linefeed & ¬ daysBetweenDates("2020-04-11", "2020-04-11")</lang>
- Output:
"2020-04-11 comes 7040 days after 2001-01-01 2020-04-11 comes 1 day before 2020-04-12 2020-04-11 and 2020-04-11 are the same date"
Or, composing a function from reusable generics, and drawing on NSISO8601DateFormatter:
<lang applescript>use AppleScript version "2.4" use framework "Foundation" use scripting additions
-- daysBetween :: String -> String -> Int
on daysBetween(iso8601From, iso8601To)
set midnight to "T00:00:00+00:00" ((dateFromISO8601(iso8601To & midnight) - ¬ dateFromISO8601(iso8601From & midnight)) / 86400) as integer
end daysBetween
-- dateFromISO8601 :: String -> Date
on dateFromISO8601(isoDateString)
set ca to current application tell ca's NSISO8601DateFormatter's alloc's init() set its formatOptions to ¬ (ca's NSISO8601DateFormatWithInternetDateTime as integer) (its dateFromString:(isoDateString)) as date end tell
end dateFromISO8601
TEST----------------------------
on run
script test on |λ|(ab) set {a, b} to ab set delta to daysBetween(a, b) (a & " -> " & b & " -> " & delta as string) & pluralize(delta, " day") end |λ| end script unlines(map(test, {¬ {"2020-04-11", "2001-01-01"}, ¬ {"2020-04-11", "2020-04-12"}, ¬ {"2020-04-11", "2020-04-11"}, ¬ {"2019-01-01", "2019-09-30"}}))
end run
GENERIC FUNCTIONS ---------------------
-- Absolute value.
-- abs :: Num -> Num
on abs(x)
if 0 > x then -x else x end if
end abs
-- justifyRight :: Int -> Char -> String -> String
on justifyRight(n, cFiller, strText)
if n > length of strText then text -n thru -1 of ((replicate(n, cFiller) as text) & strText) else strText end if
end justifyRight
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
-- The list obtained by applying f -- to each element of xs. tell mReturn(f) set lng to length of xs set lst to {} repeat with i from 1 to lng set end of lst to |λ|(item i of xs, i, xs) end repeat return lst end tell
end map
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
-- 2nd class handler function lifted into 1st class script wrapper. if script is class of f then f else script property |λ| : f end script end if
end mReturn
-- pluralize :: Int -> String -> String
on pluralize(n, s)
set m to abs(n) if 0 = m or 2 ≤ m then s & "s" else s end if
end pluralize
-- replicate :: Int -> String -> String
on replicate(n, s)
set out to "" if n < 1 then return out set dbl to s repeat while (n > 1) if (n mod 2) > 0 then set out to out & dbl set n to (n div 2) set dbl to (dbl & dbl) end repeat return out & dbl
end replicate
-- unlines :: [String] -> String
on unlines(xs)
-- A single string formed by the intercalation -- of a list of strings with the newline character. set {dlm, my text item delimiters} to ¬ {my text item delimiters, linefeed} set str to xs as text set my text item delimiters to dlm str
end unlines</lang>
- Output:
2020-04-11 -> 2001-01-01 -> -7040 days 2020-04-11 -> 2020-04-12 -> 1 day 2020-04-11 -> 2020-04-11 -> 0 days 2019-01-01 -> 2019-09-30 -> 272 days
AWK
<lang AWK>
- syntax: GAWK -f DAYS_BETWEEN_DATES.AWK
BEGIN {
regexp = "^....-..-..$" # YYYY-MM-DD main("1969-12-31","1970-01-01","builtin has bad POSIX start date") main("1970-01-01","2038-01-19","builtin has bad POSIX stop date") main("1970-01-01","2019-10-02","format OK") main("1970-01-01","2019/10/02","format NG") main("1995-11-21","1995-11-21","identical dates") main("2019-01-01","2019-01-02","positive date") main("2019-01-02","2019-01-01","negative date") main("2019-01-01","2019-03-01","non-leap year") main("2020-01-01","2020-03-01","leap year") exit(0)
} function main(date1,date2,comment, d1,d2,diff) {
printf("\t%s\n",comment) d1 = days_builtin(date1) d2 = days_builtin(date2) diff = (d1 == "" || d2 == "") ? "error" : d2-d1 printf("builtin %10s to %10s = %s\n",date1,date2,diff) d1 = days_generic(date1) d2 = days_generic(date2) diff = (d1 == "" || d2 == "") ? "error" : d2-d1 printf("generic %10s to %10s = %s\n",date1,date2,diff)
} function days_builtin(ymd) { # use gawk builtin
if (ymd !~ regexp) { return("") } if (ymd < "1970-01-01" || ymd > "2038-01-18") { return("") } # outside POSIX range gsub(/-/," ",ymd) return(int(mktime(sprintf("%s 0 0 0",ymd)) / (60*60*24)))
} function days_generic(ymd, d,m,y,result) { # use Python formula
if (ymd !~ regexp) { return("") } y = substr(ymd,1,4) m = substr(ymd,6,2) d = substr(ymd,9,2) m = (m + 9) % 12 y = int(y - int(m/10)) result = 365*y + int(y/4) - int(y/100) + int(y/400) + int((m*306+5)/10) + (d-1) return(result)
} </lang>
- Output:
builtin has bad POSIX start date builtin 1969-12-31 to 1970-01-01 = error generic 1969-12-31 to 1970-01-01 = 1 builtin has bad POSIX stop date builtin 1970-01-01 to 2038-01-19 = error generic 1970-01-01 to 2038-01-19 = 24855 format OK builtin 1970-01-01 to 2019-10-02 = 18171 generic 1970-01-01 to 2019-10-02 = 18171 format NG builtin 1970-01-01 to 2019/10/02 = error generic 1970-01-01 to 2019/10/02 = error identical dates builtin 1995-11-21 to 1995-11-21 = 0 generic 1995-11-21 to 1995-11-21 = 0 positive date builtin 2019-01-01 to 2019-01-02 = 1 generic 2019-01-01 to 2019-01-02 = 1 negative date builtin 2019-01-02 to 2019-01-01 = -1 generic 2019-01-02 to 2019-01-01 = -1 non-leap year builtin 2019-01-01 to 2019-03-01 = 59 generic 2019-01-01 to 2019-03-01 = 59 leap year builtin 2020-01-01 to 2020-03-01 = 60 generic 2020-01-01 to 2020-03-01 = 60
C
<lang C>
- include<stdbool.h>
- include<string.h>
- include<stdio.h>
typedef struct{
int year, month, day;
}date;
date extractDate(char* str){
return (date){.year = 1000 * (str[0]-'0') + 100 * (str[1]-'0') + 10 * (str[2]-'0') + (str[3]-'0'), .month = 10*(str[5]-'0') + (str[6]-'0'), .day = 10*(str[8]-'0') + (str[9]-'0')};
}
bool isValidDate(char* str){
date newDate; if(strlen(str)!=10 && str[4]!='-' && str[7]!='-'){ return false; }
newDate = extractDate(str);
if(newDate.year<=0 || newDate.month<=0 || newDate.day<=0 || newDate.month>12 || (newDate.month==2 && newDate.day>29) || ((newDate.month==1 || newDate.month==3 || newDate.month==5 || newDate.month==7 || newDate.month==8 || newDate.month==10 || newDate.month==12) && newDate.day>31) || newDate.day>30 || (newDate.year%4==0 && newDate.month==2 && newDate.month>28)){ return false; }
return true;
}
int diffDays(date date1,date date2){
int days1, days2; date1.month = (date1.month + 9)%12; date1.year = date1.year - date1.month/10;
date2.month = (date2.month + 9)%12; date2.year = date2.year - date2.month/10;
days1 = 365*date1.year + date1.year/4 - date1.year/100 + date1.year/400 + (date1.month*306 + 5)/10 + ( date1.day - 1 ); days2 = 365*date2.year + date2.year/4 - date2.year/100 + date2.year/400 + (date2.month*306 + 5)/10 + ( date2.day - 1 );
return days2 - days1;
}
int main(int argc,char** argv) {
if(argc!=3){ return printf("Usage : %s <yyyy-mm-dd> <yyyy-mm-dd>",argv[0]); }
if(isValidDate(argv[1])&&isValidDate(argv[2]) == false){ return printf("Dates are invalid.\n"); }
printf("Days Difference : %d\n", diffDays(extractDate(argv[1]),extractDate(argv[2])));
return 0;
} </lang> Output :
abhishek_ghosh@Azure:~/doodles$ ./a.out 2019-01-01 2019-12-02 Days Difference : 335
C++
<lang cpp>#include <iomanip>
- include <iostream>
class Date { private:
int year, month, day;
public:
Date(std::string str) { if (isValidDate(str)) { year = atoi(&str[0]); month = atoi(&str[5]); day = atoi(&str[8]); } else { throw std::exception("Invalid date"); } }
int getYear() { return year; }
int getMonth() { return month; }
int getDay() { return day; }
// YYYY-MM-DD static bool isValidDate(std::string str) { if (str.length() != 10 || str[4] != '-' || str[7] != '-') { return false; }
if (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || !isdigit(str[3])) { return false; // the year is not valid } if (!isdigit(str[5]) || !isdigit(str[6])) { return false; // the month is not valid } if (!isdigit(str[8]) || !isdigit(str[9])) { return false; // the day is not valid }
int year = atoi(&str[0]); int month = atoi(&str[5]); int day = atoi(&str[8]);
// quick checks if (year <= 0 || month <= 0 || day <= 0) { return false; } if (month > 12) { return false; }
switch (month) { case 2: if (day > 29) { return false; } if (!isLeapYear(year) && day == 29) { return false; } break; case 1: case 3: case 5: case 7: case 8: case 10: case 12: if (day > 31) { return false; } break; default: if (day > 30) { return false; } break; }
return true; }
static bool isLeapYear(int year) { if (year > 1582) { return ((year % 4 == 0) && (year % 100 > 0)) || (year % 400 == 0); } if (year > 10) { return year % 4 == 0; } // not bothering with earlier leap years return false; }
friend std::ostream &operator<<(std::ostream &, Date &);
};
std::ostream &operator<<(std::ostream &os, Date &d) {
os << std::setfill('0') << std::setw(4) << d.year << '-'; os << std::setfill('0') << std::setw(2) << d.month << '-'; os << std::setfill('0') << std::setw(2) << d.day; return os;
}
int diffDays(Date date1, Date date2) {
int d1m = (date1.getMonth() + 9) % 12; int d1y = date1.getYear() - d1m / 10;
int d2m = (date2.getMonth() + 9) % 12; int d2y = date2.getYear() - d2m / 10;
int days1 = 365 * d1y + d1y / 4 - d1y / 100 + d1y / 400 + (d1m * 306 + 5) / 10 + (date1.getDay() - 1); int days2 = 365 * d2y + d2y / 4 - d2y / 100 + d2y / 400 + (d2m * 306 + 5) / 10 + (date2.getDay() - 1);
return days2 - days1;
}
int main() {
std::string ds1 = "2019-01-01"; std::string ds2 = "2019-12-02";
if (Date::isValidDate(ds1) && Date::isValidDate(ds2)) { Date d1(ds1); Date d2(ds2); std::cout << "Days difference : " << diffDays(d1, d2); } else { std::cout << "Dates are invalid.\n"; }
return 0;
}</lang>
- Output:
Days difference : 335
C#
<lang csharp>using System; using System.Globalization;
public class Program {
public static void Main() => WriteLine(DateDiff("1970-01-01", "2019-10-18"));
public static int DateDiff(string d1, string d2) { var a = DateTime.ParseExact(d1, "yyyy-MM-dd", CultureInfo.InvariantCulture); var b = DateTime.ParseExact(d2, "yyyy-MM-dd", CultureInfo.InvariantCulture); return (int)(b - a).TotalDays; }
}</lang>
- Output:
18187
COBOL
<lang COBOL>COBOL *> days-between
*> Tectonics: cobc -xj days-between.cob
identification division. program-id. days-between.
procedure division. compute tally = function integer-of-formatted-date('YYYY-MM-DD', '2019-11-24') - function integer-of-formatted-date('YYYY-MM-DD', '2000-01-01') display tally
compute tally = function integer-of-formatted-date('YYYYMMDD', '20191124') - function integer-of-formatted-date('YYYYMMDD', '20000101') display tally
goback. end program days-between.</lang>
- Output:
prompt$ cobc -xj days-between-dates.cob 07267 07267
D
<lang d>import std.datetime.date; import std.stdio;
void main() {
auto fromDate = Date.fromISOExtString("2019-01-01"); auto toDate = Date.fromISOExtString("2019-10-07"); auto diff = toDate - fromDate; writeln("Number of days between ", fromDate, " and ", toDate, ": ", diff.total!"days");
}</lang>
- Output:
Number of days between 2019-Jan-01 and 2019-Oct-07: 279
Erlang
<lang erlang>
-module(daysbetween). -export([between/2,dateToInts/2]).
% Return Year or Month or Date from datestring dateToInts(String, POS) ->
list_to_integer( lists:nth( POS, string:tokens(String, "-") ) ).
% Alternative form of above % dateToInts(String,POS) -> % list_to_integer( lists:nth( POS, re:split(String ,"-", [{return,list},trim]) ) ).
% Return the number of days between dates formatted "2019-09-30" between(DateOne,DateTwo) ->
L = [1,2,3], [Y1,M1,D1] = [ dateToInts(DateOne,X) || X <- L], [Y2,M2,D2] = [ dateToInts(DateTwo,X) || X <- L], GregOne = calendar:date_to_gregorian_days(Y1,M1,D1), GregTwo = calendar:date_to_gregorian_days(Y2,M2,D2), GregTwo - GregOne.
</lang>
- Output:
erlang shell:
30> c(daysbetween). c(daysbetween). {ok,daysbetween} 31> daysbetween:between("2019-01-01", "2019-09-30"). daysbetween:between("2019-01-01", "2019-09-30"). 272
Factor
Factor supports the addition and subtraction of timestamps and durations with the time+
and time-
words.
<lang factor>USING: calendar calendar.parser kernel math prettyprint ;
- days-between ( ymd-str ymd-str -- n )
[ ymd>timestamp ] bi@ time- duration>days abs ;
"2019-01-01" "2019-09-30" days-between . "2016-01-01" "2016-09-30" days-between . ! leap year</lang>
- Output:
272 273
Go
<lang go>package main
import (
"fmt" "log" "time"
)
const layout = "2006-01-02" // template for time.Parse
// Parameters assumed to be in YYYY-MM-DD format. func daysBetween(date1, date2 string) int {
t1, err := time.Parse(layout, date1) check(err) t2, err := time.Parse(layout, date2) check(err) days := int(t1.Sub(t2).Hours() / 24) if days < 0 { days = -days } return days
}
func check(err error) {
if err != nil { log.Fatal(err) }
}
func main() {
date1, date2 := "2019-01-01", "2019-09-30" days := daysBetween(date1, date2) fmt.Printf("There are %d days between %s and %s\n", days, date1, date2)
date1, date2 = "2015-12-31", "2016-09-30" days = daysBetween(date1, date2) fmt.Printf("There are %d days between %s and %s\n", days, date1, date2)
}</lang>
- Output:
There are 272 days between 2019-01-01 and 2019-09-30 There are 274 days between 2015-12-31 and 2016-09-30
Groovy
<lang groovy>import java.time.LocalDate
def fromDate = LocalDate.parse("2019-01-01") def toDate = LocalDate.parse("2019-10-19") def diff = fromDate - toDate println "Number of days between ${fromDate} and ${toDate}: ${diff}"</lang>
- Output:
Number of days between 2019-01-01 and 2019-10-19: 291
Haskell
<lang Haskell>import Data.Time (Day) import Data.Time.Calendar (diffDays) import Data.Time.Format (parseTimeM,defaultTimeLocale)
main = do
putStrLn $ task "2019-01-01" "2019-09-30" putStrLn $ task "2015-12-31" "2016-09-30"
task :: String -> String -> String task xs ys = "There are " ++ (show $ betweenDays xs ys) ++ " days between " ++ xs ++ " and " ++ ys ++ "."
betweenDays :: String -> String -> Integer betweenDays date1 date2 = go (stringToDay date1) (stringToDay date2)
where go (Just x) (Just y) = diffDays y x go Nothing _ = error "Exception: Bad format first date" go _ Nothing = error "Exception: Bad format second date"
stringToDay :: String -> Maybe Day stringToDay date = parseTimeM True defaultTimeLocale "%Y-%-m-%-d" date</lang>
- Output:
There are 272 days between 2019-01-01 and 2019-09-30. There are 274 days between 2015-12-31 and 2016-09-30.
Java
<lang java>import java.time.LocalDate; import java.time.temporal.ChronoUnit;
public class DaysBetweenDates {
public static void main(String[] args) { LocalDate fromDate = LocalDate.parse("2019-01-01"); LocalDate toDate = LocalDate.parse("2019-10-19"); long diff = ChronoUnit.DAYS.between(fromDate, toDate); System.out.printf("Number of days between %s and %s: %d\n", fromDate, toDate, diff); }
}</lang>
- Output:
Number of days between 2019-01-01 and 2019-10-19: 291
Julia
<lang julia>using Dates
@show Day(DateTime("2019-09-30") - DateTime("2019-01-01"))
@show Day(DateTime("2019-03-01") - DateTime("2019-02-01"))
@show Day(DateTime("2020-03-01") - DateTime("2020-02-01"))
@show Day(DateTime("2029-03-29") - DateTime("2019-03-29"))
</lang>
- Output:
Day(DateTime("2019-09-30") - DateTime("2019-01-01")) = 272 days Day(DateTime("2019-03-01") - DateTime("2019-02-01")) = 28 days Day(DateTime("2020-03-01") - DateTime("2020-02-01")) = 29 days Day(DateTime("2029-03-29") - DateTime("2019-03-29")) = 3653 days
Kotlin
<lang scala>import java.time.LocalDate import java.time.temporal.ChronoUnit
fun main() {
val fromDate = LocalDate.parse("2019-01-01") val toDate = LocalDate.parse("2019-10-19") val diff = ChronoUnit.DAYS.between(fromDate, toDate) println("Number of days between $fromDate and $toDate: $diff")
}</lang>
- Output:
Number of days between 2019-01-01 and 2019-10-19: 291
Lua
This uses os.difftime to compare two Epoch times. Not to be used with dates before 1970. <lang lua>SECONDS_IN_A_DAY = 60 * 60 * 24
-- Convert date string as YYYY-MM-DD to Epoch time. function parseDate (str)
local y, m, d = string.match(str, "(%d+)-(%d+)-(%d+)") return os.time({year = y, month = m, day = d})
end
-- Main procedure io.write("Enter date 1: ") local d1 = parseDate(io.read()) io.write("Enter date 2: ") local d2 = parseDate(io.read()) local diff = math.ceil(os.difftime(d2, d1) / SECONDS_IN_A_DAY) print("There are " .. diff .. " days between these dates.")</lang>
- Output:
Enter date 1: 1970-01-01 Enter date 2: 2019-10-02 There are 18171 days between these dates.
Perl
Would not reinvent this wheel. <lang perl>use feature 'say'; use Date::Calc qw(Delta_Days);
say Delta_Days(2018,7,13, 2019,9,13); # triskaidekaphobia say Delta_Days(1900,1,1, 2000,1,1); # a century say Delta_Days(2000,1,1, 2100,1,1); # another, with one extra leap day say Delta_Days(2020,1,1, 2019,10,1); # backwards in time say Delta_Days(2019,2,29, 2019,3,1); # croaks</lang>
- Output:
427 36524 36525 -92 Date::Calc::PP::Delta_Days(): Date::Calc::Delta_Days(): not a valid date at Days_between_dates line 10
Phix
<lang Phix>include builtins\timedate.e -- specify as many or as few permitted formats as you like: set_timedate_formats({"YYYY-MM-DD","DD/MM/YYYY","YYYY/MM/DD"})
constant SECONDS_TO_DAYS = 60*60*24
procedure test(string d1, d2, desc="")
timedate td1 = parse_date_string(d1), td2 = parse_date_string(d2) atom s = timedate_diff(td1,td2,DT_DAY), d = s/SECONDS_TO_DAYS string e = elapsed(s)&desc printf(1,"Days between %s and %s: %d [%s]\n",{d1,d2,d,e})
end procedure
test("1969-12-31","1970-01-01")
test("1995-11-21","1995-11-21",", same date")
test("2019-01-02","2019-01-01",", negative date")
test("2019-01-01","2019-03-01",", non-leap year")
test("2020-01-01","2020-03-01",", leap year")
test("1970-01-01", "2019/10/18")
test("1970-01-01", "18/10/2019")</lang>
As shown, timedate_diff() can optionally round to the nearest whole number of days [else omit DT_DAY].
Note that elapsed() assumes all years are exactly 365 days, and in no way takes leap years into consideration
(as opposed, of course, to timedate_diff() which handles them flawlessly), not that you sh/c/would ever use the
string output of elapsed() in any further calculations anyway.
- Output:
Days between 1969-12-31 and 1970-01-01: 1 [1 day] Days between 1995-11-21 and 1995-11-21: 0 [0s, same date] Days between 2019-01-02 and 2019-01-01: -1 [minus 1 day, negative date] Days between 2019-01-01 and 2019-03-01: 59 [59 days, non-leap year] Days between 2020-01-01 and 2020-03-01: 60 [60 days, leap year] Days between 1970-01-01 and 2019/10/18: 18187 [49 years, 302 days] Days between 1970-01-01 and 18/10/2019: 18187 [49 years, 302 days]
Python
<lang python>
- !/usr/bin/python
import sys
Difference between two dates = g(y2,m2,d2) - g(y1,m1,d1)
Where g() gives us the Gregorian Calendar Day Inspired by discussion at: https://stackoverflow.com/questions/12862226/the-implementation-of-calculating-the-number-of-days-between-2-dates
def days( y,m,d ):
input year and month are shifted to begin the year in march m = (m + 9) % 12 y = y - m/10
with (m*306 + 5)/10 the number of days from march 1 to the current 'm' month result = 365*y + y/4 - y/100 + y/400 + (m*306 + 5)/10 + ( d - 1 ) return result
def diff(one,two):
[y1,m1,d1] = one.split('-') [y2,m2,d2] = two.split('-') # strings to integers year2 = days( int(y2),int(m2),int(d2)) year1 = days( int(y1), int(m1), int(d1) ) return year2 - year1
if __name__ == "__main__":
one = sys.argv[1] two = sys.argv[2] print diff(one,two)
</lang>
- Output:
python days-between.py 2019-01-01 2019-09-30 272
Raku
(formerly Perl 6) Dates are first class objects in Raku and may have arithmetic in days done directly on them. <lang perl6>say Date.new('2019-09-30') - Date.new('2019-01-01');
say Date.new('2019-03-01') - Date.new('2019-02-01');
say Date.new('2020-03-01') - Date.new('2020-02-01');
say Date.new('2029-03-29') - Date.new('2019-03-29');
say Date.new('2019-01-01') + 90;
say Date.new('2020-01-01') + 90;
say Date.new('2019-02-29') + 30;
CATCH { default { .message.say; exit; } };</lang>
272 28 29 3653 2019-04-01 2020-03-31 Day out of range. Is: 29, should be in 1..28
REBOL
REBOL accepts a multitude of lexically recognized date! formats. date! is a builtin datatype. Slashes or dashes, year-month-day, day/month/year, or dd/monthname/year, etc. Math on dates defaults to days.
prompt$ rebol -q >> 2019-11-24 - 2000-01-01 == 7267 >> 2019-nov-24 - 01-jan-2000 == 7267
REXX
bare bones version
Programming note: the B (Base) an option for the date BIF which indicates to compute the number of
days since the beginning of the Gregorian calendar, and I which is the option that indicates the date is in
the ISO (International Standards Organization standard 8601:2004) format.
<lang rexx>/*REXX program computes the number of days between two dates in the form of YYYY-MM-DD */
parse arg $1 $2 . /*get 2 arguments (dates) from the C.L.*/
say abs( date('B',$1,"I") - date('B',$2,"I") ) ' days between ' $1 " and " $2
/*stick a fork in it, we're all done. */</lang>
- output when using the inputs of: 2019-10-02 2000-01-01
7214 days between 2019-10-02 and 2000-01-01
supports more variations
This REXX version supports more variations in the date format (allows a single digit month and/or day), as well as
allowing a single asterisk (*) to be used for a date (which signifies that the current date is to be used).
Commas (,) are inserted into numbers where appropriate.
Also, more informative error messages are generated. <lang rexx>/*REXX program computes the number of days between two dates in the form of YYYY-MM-DD */ parse arg $.1 $.2 _ . 1 . . xtra /*obtain two arguments from the C.L. */ if $.1=='*' then $.1= date("I") /*obtain current date if it's an *. */ if $.2=='*' then $.2= date("I") /* " " " " " " " */ parse var $.1 yr.1 '-' mon.1 "-" dd.1 /*obtain the constituents of 1st date. */ parse var $.2 yr.2 '-' mon.2 "-" dd.2 /* " " " " 2nd " */ ?.1= '1st' ?.2= '2nd' if _ \== then call err "too many arguments specified: " xtra dy.=31 /*default number of days for all months*/ parse value 30 with dy.4 1 dy.6 1 dy.9 1 dy.11 /*define 30─day months, Feb. is special*/ @notCorr= "isn't in the correct format: YYYY-MM-DD "
do j=1 for 2 /*examine both dates for correct format*/ if $.j == then call err ?.j "date was not specified." if length(yr.j)==0 then call err ?.j "year" @notCorr '(missing)' if isDec(yr.j) then call err ?.j "year" @notCorr '(has a non─decimal digit)' if yr.j<1 | yr.j>9999 then call err ?.j "year" @notCorr '(not in the range 1──►9999)' if length(mon.j)==0 then call err ?.j "month" @notCorr '(missing)' if isDec(mon.j) then call err ?.j "month" @notCorr '(has a non─decimal digit)' if mon.j<1 | mon.j>12 then call err ?.j "month" @notCorr '(not in the range 1──►12)' if length(dd.j)==0 then call err ?.j "day" @notCorr '(missing)' if isDec(dd.j) then call err ?.j "day" @notCorr '(has a non─decimal digit)' mo= mon.j if leapYr(yr.j) then dy.2= 29 /*Is it a leapyear? Use 29 days for Feb*/ else dy.2= 28 /*Isn't " " " 28 " " " */ if dd.j<1 | dd.j>dy.mo then call err ?.j "day" @notCorr '(day in month is invalid)'
yr.j= right( yr.j +0, 4, 0) /*force YYYY to be four decimal digits.*/ mon.j= right(mon.j +0, 2, 0) /* " MON " " two " " */ dd.j= right( dd.j +0, 2, 0) /* " DD " " " " " */ $.j= yr.j'-'mon.j"-"dd.j /*reconstitute a date from above parts.*/ end /*j*/
say commas(abs(date('B',$.1,"I") -date('B',$.2,"I"))) ' days between ' $.1 " and " $.2 exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ commas: parse arg _; do c_=length(_)-3 to 1 by -3; _=insert(',', _, c_); end; return _ err: say; say '***error*** ' arg(1); exit 13 /*issue an error message (with text) */ isDec: return verify( arg(1), 1234567890) \== 0 /*insure argument is just decimal digs.*/ leapYr: arg _; ly=_//4==0; if ly==0 then return 0; ly=((_//100\==0)|_//400==0); return ly</lang>
- output when using the inputs of: * 2000-1-1
Today (indicated by the asterisk) is 2019-10-2
7,214 days between 2019-10-02 and 2000-01-01
Ruby
<lang ruby>require "date"
d1, d2 = Date.parse("2019-1-1"), Date.parse("2019-10-19")
p (d1 - d2).to_i # => -291 p (d2 - d1).to_i # => 291 </lang>
Scala
<lang scala>object DaysBetweenDates {
/*Inspired by the Python version of the algorithm and the discussion here https://stackoverflow.com/questions/12862226/the-implementation-of-calculating-the-number-of-days-between-2-dates.*/
/**Transform a date into a day number in the Gregorian Calendar*/ def dateToDays(year : Int, month : Int, day : Int ) : Int = { val m = (month+ 9) % 12 val y = year - m/10 val d = day 365*y + y/4 - y/100 + y/400 + (m*306 + 5)/10 + (d - 1) }
/**Compute the difference of days between both input dates*/ def daysDifference(firstDate : String, secondDate : String) : Int = { val firstDateTuple = firstDate.split('-') match { case Array(a, b, c) => (a, b, c) } val secondDateTuple = secondDate.split('-') match { case Array(a, b, c) => (a, b, c) }
val firstYear = dateToDays( firstDateTuple._1.toInt, firstDateTuple._2.toInt, firstDateTuple._3.toInt) val secondYear = dateToDays( secondDateTuple._1.toInt, secondDateTuple._2.toInt, secondDateTuple._3.toInt )
return secondYear - firstYear }
def main(args: Array[String]): Unit = { println(daysDifference("2019-01-01", "2019-09-30")) println(daysDifference("1995-11-21", "1995-11-21")) println(daysDifference("2019-01-01", "2019-01-02")) println(daysDifference("2019-01-02", "2019-01-01")) println(daysDifference("2019-01-01", "2019-03-01")) println(daysDifference("2020-01-01", "2020-03-01")) println(daysDifference("1902-01-01", "1968-12-25")) println(daysDifference("2090-01-01", "2098-12-25")) println(daysDifference("1902-01-01", "2098-12-25")) }
} </lang>
- Output:
272 0 1 -1 59 60 24465 3280 71947
Sidef
<lang ruby>require('Date::Calc')
func days_diff(a,b) {
%S<Date::Calc>.Delta_Days(a.split('-')..., b.split('-')...)
}
var date1 = "1970-01-01" var date2 = "2019-10-02"
say "Date 1: #{date1}" say "Date 2: #{date2}"
var days = days_diff(date1, date2)
say "There are #{days} days between these dates"</lang>
- Output:
Date 1: 1970-01-01 Date 2: 2019-10-02 There are 18171 days between these dates
Swift
<lang swift>import Foundation
func daysFromTimeInterval(_ interval: Double) -> Int {
return Int(interval) / 86400
}
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
print("Enter date one (yyyy-MM-dd): ", terminator: "")
guard let date1Str = readLine(strippingNewline: true), let date1 = formatter.date(from: date1Str) else {
fatalError("Invalid date two")
}
print("Enter date two (yyyy-MM-dd): ", terminator: "")
guard let date2Str = readLine(strippingNewline: true), let date2 = formatter.date(from: date2Str) else {
fatalError("Invalid date two")
}
let (start, end) = date1 > date2 ? (date2, date1) : (date1, date2) let days = daysFromTimeInterval(DateInterval(start: start, end: end).duration)
print("There are \(days) days between \(start) and \(end)")</lang>
- Output:
Enter date one (yyyy-MM-dd): 2019-01-01 Enter date two (yyyy-MM-dd): 2019-12-02 There are 335 days between 2019-01-01 05:00:00 +0000 and 2019-12-02 05:00:00 +0000
UNIX Shell
<lang bash># return true if year is leap in Gregorian calendar leap() {
local -i year year=$1 if (( year % 4 )); then return 1; fi if (( year % 100 )); then return 0; fi ! (( year % 400 ))
}
- convert date to Gregorian day count (Rata Die), where RD 1 = January 1, 1 CE
rd() {
local year month day IFS=- read year month day <<<"$1" local -i elapsed_years=year-1 (( day += elapsed_years * 365 )) (( day += elapsed_years/4 )) (( day -= elapsed_years/100 )) (( day += elapsed_years/400 )) local month_lengths=(31 28 31 30 31 30 31 31 30 31 30 31) if leap "$year"; then let month_lengths[1]+=1; fi local m for (( m=0; m<month-1; ++m)); do (( day += month_lengths[m] )) done printf '%d\n' "$day"
}
days_between() {
local -i date1 date2 date1=$(rd "$1") date2=$(rd "$2") printf '%d\n' $(( date2 - date1 ))
}
days_between 1970-01-01 2019-12-04 </lang>
- Output:
18234
Visual Basic .NET
<lang vbnet>Imports System.Globalization
Module Module1
Function DateDiff(d1 As String, d2 As String) As Integer Dim a = DateTime.ParseExact(d1, "yyyy-MM-dd", CultureInfo.InvariantCulture) Dim b = DateTime.ParseExact(d2, "yyyy-MM-dd", CultureInfo.InvariantCulture) Return (b - a).TotalDays End Function
Sub Main() Console.WriteLine(DateDiff("1970-01-01", "2019-10-18")) End Sub
End Module</lang>
- Output:
18187
Wren
No built-in date support so we need to work from scratch. <lang javascript>var dayOfYear = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
var isLeapYear = Fn.new { |y|
return ((y % 4 == 0) && (y % 100!= 0)) || (y % 400 == 0)
}
// Parameters assumed to be in YYYY-MM-DD format. var daysBetween = Fn.new { |date1, date2|
date1 = date1.replace("-", "") date2 = date2.replace("-", "") var neg = false if (Num.fromString(date1) > Num.fromString(date2)) { var temp = date2 date2 = date1 date1 = temp neg = true } var y1 = Num.fromString(date1[0..3]) var m1 = Num.fromString(date1[4..5]) - 1 var d1 = Num.fromString(date1[6..7]) var y2 = Num.fromString(date2[0..3]) var m2 = Num.fromString(date2[4..5]) - 1 var d2 = Num.fromString(date2[6..7]) var days = 0 if (y1 == y2) { days = dayOfYear[m2] - dayOfYear[m1] + d2 - d1 if (isLeapYear.call(y1) && m1 < 2 && m2 >= 2) days = days + 1 } else { days = 365 - dayOfYear[m1] - d1 if (isLeapYear.call(y1) && m1 < 2) days = days + 1 days = days + dayOfYear[m2] + d2 if (isLeapYear.call(y2) && m2 > 1) days = days + 1 if (y1 < y2 - 1) { for (y in y1+1..y2-1) { days = days + 365 if (isLeapYear.call(y)) days = days + 1 } } } return (neg) ? -days : days
}
var datePairs = [
["1995-11-21", "1995-11-21"], ["2019-01-01", "2019-01-02"], ["2019-01-02", "2019-01-01"], ["2019-01-01", "2019-03-01"], ["2020-01-01", "2020-03-01"], ["1902-01-01", "1968-12-25"], ["2090-01-01", "2098-12-25"], ["1902-01-01", "2098-12-25"], ["1970-01-01", "2019-10-18"], ["2019-03-29", "2029-03-29"], ["2020-02-29", "2020-03-01"]
] for (dates in datePairs) {
var days = daysBetween.call(dates[0], dates[1]) System.print("Days between %(dates[0]) and %(dates[1]) = %(days)")
}</lang>
- Output:
Days between 1995-11-21 and 1995-11-21 = 0 Days between 2019-01-01 and 2019-01-02 = 1 Days between 2019-01-02 and 2019-01-01 = -1 Days between 2019-01-01 and 2019-03-01 = 59 Days between 2020-01-01 and 2020-03-01 = 60 Days between 1902-01-01 and 1968-12-25 = 24465 Days between 2090-01-01 and 2098-12-25 = 3280 Days between 1902-01-01 and 2098-12-25 = 71947 Days between 1970-01-01 and 2019-10-18 = 18187 Days between 2019-03-29 and 2029-03-29 = 3653 Days between 2020-02-29 and 2020-03-01 = 1
zkl
<lang zkl>var [const] TD=Time.Date; today:=TD.parseDate("--"); // "yyyy-mm-dd" and variations --> (y,m,d) // or Time.Clock.UTC --> (y,m,d,h,m,s) then:=TD.parseDate("2018-9-30"); diff:=TD.deltaDays(then,today.xplode()); // ( (y,m,d), y,m,d ) println("Number of days between %s and %s: %d".fmt(then,today,diff)); println("Number of days between %s and %s: %d".fmt(
TD.toYMDString(today.xplode()), // to(y,m,d) not to((y,m,d)) TD.toYMDString(then.xplode()), TD.deltaDays(today,then.xplode())));</lang>
- Output:
Number of days between L(2018,9,30) and L(2019,9,30): 365 Number of days between 2019-09-30 and 2018-09-30: -365