cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
JMP is taking Discovery online, April 16 and 18. Register today and join us for interactive sessions featuring popular presentation topics, networking, and discussions with the experts.
Choose Language Hide Translation Bar
Craige_Hales
Super User
Select Date Range with Calendar Boxes

This JSL function displays a modal dialog containing two calendar boxes. The boxes are linked with JSL so picking a minimum date in the left box grays unavailable dates in the right box, and similar for the other direction.

Linked calendar boxesLinked calendar boxes

The JSL is written as a function you can call with an optional limit on the range of dates (shown in the title bar). The function call returns a list:

{21Aug2019, 26Aug2019}

 

promptDateRange = Function( {minAllowed = 1jan1001, maxAllowed = 31dec2999},
	{roundMidnight, result, startDate, endDate}, // local variables
	
	roundMidnight = Function( {datetime}, // return the date, without the time...which is midnight at the start of a day
		As Date( Date MDY( Month( datetime ), Day( datetime ), Year( datetime ) ) )
	);
	minAllowed = roundMidnight( minAllowed );
	maxAllowed = roundMidnight( maxAllowed );
	result = New Window( "Select date range between " || Char( minAllowed ) || " and " || Char( maxAllowed ),
		modal, // modal: the window must be closed before JMP can continue
		// do the validation in OnValidate, NOT OnClose! Doing it in OnClose will prevent canceling, which is Bad!
		<<OnValidate( (startDate << getDate) <= (endDate << getDate) ), // validate the range makes sense
		// capture answers when the window is closed
		<<OnClose( // convert the variables holding the calendar boxes into the value in the calendar box
			startDate = roundMidnight( startDate << getDate ); // throw away time part; this rounding
			endDate = roundMidnight( endDate << getDate ); // is needed if the default date is unchanged.
			1; // return 1: the window will close. Validation was already done.
		), 
		// when the window first opens, remove the Today buttons.
		<<OnOpen( // They don't run callback or round to midnight.
			(((startDate)[Button Box( 3 )])) << delete;
			(((endDate)[Button Box( 3 )])) << delete;
		),
		H List Box( // make a left/right pair of calendars in a horizontal list box
			V List Box(
				startDate = Calendar Box(
					<<min date( minAllowed ),
					<<max date( maxAllowed ),
					<<showTime( 0 ), // don't show the time
					// make the left (smaller date) box adjust the upper date's lowest possible value
					<<setfunction(
						Function( {this, date},
							{},
							endDate << mindate( roundMidnight( startDate << getDate ) );
							If( roundMidnight( endDate << getDate ) < (endDate << getMinDate),
								endDate << date( endDate << getMinDate )
							);
							startDate << maxdate( roundMidnight( endDate << getDate ) );
						)
					)
				),
				H Center Box( Text Box( "begin", <<setFontSize( 20 ) ) )
			),
			Spacer Box( size( 50, 1 ) ), // add a bit of space between the calendars
			V List Box(
				endDate = Calendar Box(
					<<min date( minAllowed ),
					<<max date( maxAllowed ),
					<<showtime( 0 ),  // don't show time
					// make the right (bigger date) box adjust the lower date's biggest possible value
					<<setfunction(
						Function( {this, date},
							{},
							endDate << mindate( roundMidnight( startDate << getDate ) );
							startDate << maxdate( roundMidnight( endDate << getDate ) );
							If( roundMidnight( startDate << getDate ) > (startDate << getMaxDate),
								startDate << date( startDate << getMaxDate )
							);
						)
					)
				),
				H Center Box( Text Box( "end", <<setFontSize( 20 ) ) )
			)
		)
	);

	If( result["Button"] == 1, // result == {Button( 1 )} if OK is pressed
		Eval List( {startDate, endDate} ) // ok
	,
		{., .} // cancel
	);

);

Write( promptDateRange( Today() - In Days( 5 ), Today() + In Days( 5 ) ) );

This JSL turns off the time display in the calendar control and removes any seconds after midnight whenever they appear. The JSL also sets a valid range of dates for the left and right controls; picking a date in the left control changes the minimum date in the right control, and similar in the other direction. The today buttons are removed as well. If you try to modify this JSL to support time you'll discover the today buttons don't make the callback and the time they pick is not midnight-rounded like the minimum and maximum date values.


See Also

https://www.jmp.com/support/help/14-2/date-time-functions.shtml

Last Modified: Aug 24, 2019 11:03 PM
Comments
Françoise
Level V

Hi,

 

I would like to use this script to replace a local data filter applied for a graph.

 

the local data filter has 3 choices: year, month and date.

 

with 2 calendars, it will be more simple.

 

is it possible to include it in a local data filter?

 

best regards

Craige_Hales
Super User

I don't think so. User defined selectors might be a feature request.

You should be able to get the range slider, but it won't give single-day accuracy over a large date range.

drag the two selector controlsdrag the two selector controls

The calendars are already there, one at a time, and very hard to discover. If you hover over the blue line, the icon sometimes shows where you need to click. When you move to click the icon, it vanishes, but you can still click where it appeared! The cursor also changes to a pointing finger when you move it over the left part of the date. (JMP 15. 16 may be better.)  @XanGregg @danschikore @Audrey_Shull 

Françoise
Level V
too bad, I thought I could use the calendar boxes as a filter instead of having a filter for every type of duration (month, week, year, ..).
thank you for your reply.
 
all the best !
Craige_Hales
Super User

Try something like this if you want to make your own. In addition to calling a function you supply, I also made it initialize the two calendar dates to the range, rather than today, which seems more useful. You could embed the calendars with the report instead of the modal window. 

As written below, you can see how setrange(...) just hides and excludes the data you don't want to see. It is not integrated with the local data filter, but works in a similar way.

dt = New Table( "Untitled 3",
	Add Rows( 367 ),
	New Column( "date", Numeric, "Continuous", Format( "ddMonyyyy", 10 ), Input Format( "ddMonyyyy" ), Formula( 01Jan2020 + In Days( Row() - 1 ) ) ),
	New Column( "value", Numeric, "Continuous", Format( "Best", 12 ), Formula( Random Uniform() ) )
);

gb = dt << Graph Builder(
	Size( 531, 456 ),
	Show Control Panel( 0 ),
	Variables( X( :date ), Y( :value ) ),
	Elements( Points( X, Y, Legend( 3 ) ), Line Of Fit( X, Y, Legend( 5 ) ) )
);

wait(0);

setrange=function({low,high},
	dt<<select hidden; dt<<hide(0); 
	dt<<select excluded; dt<<exclude(0); 
	dt<<selectwhere(!(low <= dt:date <= high));
	dt<<hide<<exclude;
);

promptDateRange = Function( {minAllowed = 1jan1001, maxAllowed = 31dec2999},
	{roundMidnight, result, startDate, endDate}, // local variables
	
	roundMidnight = Function( {datetime}, // return the date, without the time...which is midnight at the start of a day
		As Date( Date MDY( Month( datetime ), Day( datetime ), Year( datetime ) ) )
	);
	minAllowed = roundMidnight( minAllowed );
	maxAllowed = roundMidnight( maxAllowed );
	result = New Window( "Select date range between " || Char( minAllowed ) || " and " || Char( maxAllowed ),
		modal, // modal: the window must be closed before JMP can continue
		// do the validation in OnValidate, NOT OnClose! Doing it in OnClose will prevent canceling, which is Bad!
		<<OnValidate( (startDate << getDate) <= (endDate << getDate) ), // validate the range makes sense
		// capture answers when the window is closed
		<<OnClose( // convert the variables holding the calendar boxes into the value in the calendar box
			startDate = roundMidnight( startDate << getDate ); // throw away time part; this rounding
			endDate = roundMidnight( endDate << getDate ); // is needed if the default date is unchanged.
			1; // return 1: the window will close. Validation was already done.
		), 
		// when the window first opens, remove the Today buttons.
		<<OnOpen( // They don't run callback or round to midnight.
			(((startDate)[Button Box( 3 )])) << delete;
			(((endDate)[Button Box( 3 )])) << delete;
		),
		H List Box( // make a left/right pair of calendars in a horizontal list box
			V List Box(
				startDate = Calendar Box(
					<<date(minAllowed), // added this too
					<<min date( minAllowed ),
					<<max date( maxAllowed ),
					<<showTime( 0 ), // don't show the time
					// make the left (smaller date) box adjust the upper date's lowest possible value
					<<setfunction(
						Function( {this, date},
							{},
							endDate << mindate( roundMidnight( startDate << getDate ) );
							If( roundMidnight( endDate << getDate ) < (endDate << getMinDate),
								endDate << date( endDate << getMinDate )
							);
							startDate << maxdate( roundMidnight( endDate << getDate ) );
							
							setrange(roundMidnight( startDate << getDate ),roundMidnight( endDate << getDate )); // callback added
						)
					)
				),
				H Center Box( Text Box( "begin", <<setFontSize( 20 ) ) )
			),
			Spacer Box( size( 50, 1 ) ), // add a bit of space between the calendars
			V List Box(
				endDate = Calendar Box(
					<<date(maxAllowed), // added this too
					<<min date( minAllowed ),
					<<max date( maxAllowed ),
					<<showtime( 0 ),  // don't show time
					// make the right (bigger date) box adjust the lower date's biggest possible value
					<<setfunction(
						Function( {this, date},
							{},
							endDate << mindate( roundMidnight( startDate << getDate ) );
							startDate << maxdate( roundMidnight( endDate << getDate ) );
							If( roundMidnight( startDate << getDate ) > (startDate << getMaxDate),
								startDate << date( startDate << getMaxDate )
							);

							setrange(roundMidnight( startDate << getDate ),roundMidnight( endDate << getDate )); // callback added
						)
					)
				),
				H Center Box( Text Box( "end", <<setFontSize( 20 ) ) )
			)
		)
	);

	If( result["Button"] == 1, // result == {Button( 1 )} if OK is pressed
		Eval List( {startDate, endDate} ) // ok
	,
		{., .} // cancel
	);

);

promptDateRange( Col Min( dt:date ), Col Max( dt:date ) );
danschikore
Staff

Thank you @Craige_Hales for pointing out the issue with the calendar icons in the filter.  The icon should appear any time you hover over the numeric text, giving you the option to choose between the textual-edit or the calendar-edit.  This has been fixed for a future release.  As you point out, the calendars still work in the current release, but in some cases it does not show the calendar icon to hint at the functionality.

Françoise
Level V

Thank you @Craige_Hales for your answers.

 

best regards