Skip to content

Instantly share code, notes, and snippets.

@mndrix
Last active December 15, 2015 00:29
Show Gist options
  • Save mndrix/5173377 to your computer and use it in GitHub Desktop.
Save mndrix/5173377 to your computer and use it in GitHub Desktop.
Modified Julian Day calculations in Prolog. This experiment might form the basis of a date library someday.
:- module(julian, [ date_name/2
, day_of_week/2
, gregorian/3
]).
:- use_module(library(clpfd)).
%% gregorian(?Year, ?Month, ?Day) is det.
%
% True if Year, Month and Day form a valid date in the Gregorian
% calendar. For example, one could iterate all leap years with
%
% ==
% gregorian(Y, 2, 29), Y #> 1776, indomain(Y).
% ==
gregorian(Y,M,D) :-
Y in -4713..3267,
M in 1..12,
( (D in 1..28)
#\/ (M #\= 2 #/\ D in 29..30)
#\/ (M in 1 \/ 3 \/ 5 \/ 7 \/ 8 \/ 10 \/ 12 #/\ D #= 31)
#\/ (M #= 2 #/\ D #= 29 #/\ Y mod 400 #= 0)
#\/ (M #= 2 #/\ D #= 29 #/\ Y mod 4 #= 0 #/\ Y mod 100 #\= 0)
).
%% mjd(?MJD:integer) is det.
%
% True if MJD is a valid modified Julian day number.
mjd(MJD) :-
MJD in -2400328 .. 514671.
%% day_of_week(?Datetime, ?DayOfWeek:atom) is det.
%
% True Datetime occurs on the given day of the week.
day_of_week(datetime(MJD, _), DayOfWeek) :-
(MJD+2) mod 7 #= DayNumber,
day_name(DayNumber, DayOfWeek).
%% day_name(?Number:integer, ?DayOfWeek:atom) is semidet.
%
% True if Number is the ISO number for DayOfWeek.
% 0 is Monday, 6 is Sunday.
day_name(0, monday).
day_name(1, tuesday).
day_name(2, wednesday).
day_name(3, thursday).
day_name(4, friday).
day_name(5, saturday).
day_name(6, sunday).
:- multifile date_name/2.
date_name(Dt, today) :-
get_time(Now),
stamp_date_time(Now, date(Year, Month, Day, _,_,_,_,_,_), local),
date_name(Dt, gregorian(Year,Month,Day)),
!.
date_name(Dt, (First,Rest)) :- % constraint conjunction
date_name(Dt, First),
!,
date_name(Dt, Rest).
date_name(Dt, DayOfWeek) :- % day of week constraints
day_name(_, DayOfWeek),
!,
day_of_week(Dt, DayOfWeek).
date_name(datetime(MJD,_Nano), gregorian(Year, Month, Day)) :-
gregorian(Year, Month, Day),
mjd(MJD),
!,
Y = 4716,
V = 3,
J = 1401,
U = 5,
M = 2,
S = 153,
N = 12,
W = 2,
R = 4,
B = 274277,
P = 1461,
C = -38,
F0 #= JD + J,
F1 #= F0 + (((4 * JD + B)/146097) * 3)/4 + C,
E #= R * F1 + V,
G #= mod(E, P)/R,
H #= U * G + W,
Day #= (mod(H, S))/U + 1,
Month #= mod(H/S + M, N) + 1,
Year #= E/P - Y + (N + M - Month)/N,
MJD #= JD - 2400001,
( ground(MJD) ->
labeling([ff, up, bisect], [Year, Month, Day])
; ground(Year), ground(Month), ground(Day) ->
labeling([leftmost, up, bisect], [MJD])
; true
).
date_name(Datetime, july_fourth) :-
Y #>= 1776,
date_name(Datetime, gregorian(Y,7,4)).
:- begin_tests(acceptable_gregorian_dates).
test(march_16_2013) :-
gregorian(2013,3,16).
% leap year rules
test(1900, [fail]) :-
gregorian(1900,2,29).
test(2000) :-
gregorian(2012,2,29).
test(2012) :-
gregorian(2012,2,29).
test(2013, [fail]) :-
gregorian(2013,2,29).
test(2014, [fail]) :-
gregorian(2014,2,29).
test(2016) :-
gregorian(2016,2,29).
:- end_tests(acceptable_gregorian_dates).
:- begin_tests(gregorian_conversion).
test(march_16_2013) :-
date_name(datetime(56367, _), gregorian(2013,3,16)).
test(unix_epoch) :-
date_name(datetime(40587, _), gregorian(1970,1,1)).
:- end_tests(gregorian_conversion).
@mndrix
Copy link
Author

mndrix commented Mar 16, 2013

Find Fourth of July on a Sunday during Dwight Eisenhower's presidency:

?- use_module(library(clpfd)).
true.
?- Y in 1953..1961, date_name(Dt, (sunday,july_fourth,gregorian(Y,_,_))).
Y = 1954,
Dt = datetime(34927, _G44).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment