Altering Date Formatting


#1

I need to turn this value, Thu, May 09
Into this value: // -- parameter: yyyy-mm-dd
The result would look like this: 2019-05-09

This is for a JavaScript that tells me how many months and days an event occurs in the future. Putting in the current year at the beginning is good enough for now.

Is there a built in filter that already can parse the month value from abbreviation of month to numerical value of month in KM? Or, do I need to write out a table where it says, if Feb, 02, if Mar, 03, etc?

Thanks

Post Script: I would like to find a website where I can post further questions about using and understanding JavaScript. I don't mind asking such things here on the KM site but I don't think that learning JavaScript is the best use of this forum. If anyone can suggest a good place to learn JS, that would be appreciated.


#2

You source data does NOT include the year. Do you always want to assume the current year?

Not a KM tool, but this has been done many times in JavaScript:

getMonthNum("Sep");
 
 function getMonthNum(pMonthName){

   var d = Date.parse(pMonthName + " 1, 2012");
   if(!isNaN(d)){
      return new Date(d).getMonth() + 1;
   }
   return -1;
 }

Since you are already using JavaScript, you should be able to incorporate this script:

String.prototype.lpad = function(padString, length) {
    var str = this;
    while (str.length < length)
        str = padString + str;
    return str;
}

var dateSource = 'Thu, May 09'
var monthDayStr = dateSource.match(/^\w+, (.+)/)[1];


getISODate(monthDayStr);
//==>"2019-05-09"

 function getISODate(pMonthDay){

    var yearStr = (new Date()).getFullYear().toString()
    var dateUnix = Date.parse(pMonthDay + ", " + yearStr);
    var oDate = new Date(dateUnix);
    var isoDateStr = oDate.getFullYear() + '-' + (oDate.getMonth()+1).toString().lpad('0',2) + '-' + (oDate.getDate()).toString().lpad('0',2);
    return isoDateStr;
 }

Sorry for the lack of comments, but I'm short on time.
Please feel free to ask any questions.


#3

Thanks but ... I am breaking down a JavaScript someone else gave me and trying to make sense of it as I go. Which is to say, I really don't know much about JavaScript right now.

I understand the basics of programming but not much about the logic or the syntax.

For example, I understand that a variable needs to be named in JavaScript.

I have read enough to understand that a function is how JavaScript crowds a bunch of activities that a section of code will address into any one block. I am not sure yet how much I am supposed to cram into a single function or when it is best to use many function blocks.

I don't understand the string.prototype.1pad = function idea. I can see there is a thing called a string.prototype.1pad that is defined by its length in a function but, well ...

No worries. Thanks for the starting point. Remember, I really don't know much about JavaScript at this point, but I am not afraid to try. This seems like something I could figure out by trying a few times using a KM JavaScript component. I will test this out a few times that way and see if I can make some sense of it.

I will get back to you when I either succeed or hit a wall.

Thanks


#4

------------------------------------------
EDITING ADDED:

What follows, works, but I can't see a way to assign a variable inside a JavaScript from what is already on the KM clipboard or in a KM variable? So, I appear to have not been successful in passing the value in the clipboard to the variable used in the example JavaScript provided.

Also, the following works in a script editor but will cause an error when in a KM "Execute Shell Script".

    var VarName12 = 'Thu, Feb 14'
    String.prototype.lpad = function(padString, length) {
    				var str = this;
    				while (str.length < length)
    				str = padString + str;
    				return str;
    }
    var dateSource = VarName12
    var monthDayStr = dateSource.match(/^\w+, (.+)/)[1];
    getISODate(monthDayStr);
    //==>"2019-05-09"
    	function getISODate(pMonthDay){
    				var yearStr = (new Date()).getFullYear().toString()
    				var dateUnix = Date.parse(pMonthDay + ", " + yearStr);
    				var oDate = new Date(dateUnix);
    				var isoDateStr = oDate.getFullYear() + '-' + (oDate.getMonth()+1).toString().lpad('0',2) + '-' + (oDate.getDate()).toString().lpad('0',2);
    				return isoDateStr;
    	}

In another thread I saw some comments that there is a library that I should be using.

I am not at all experienced with JavaScript. I understand the bare and basic idea of what a library is, mostly that I would need to download it an install it somewhere for my KM macros to make use if the commands in that library. I would appreciate some more input on this.

I am experimenting with two JavaScript KM blocks to run scripts. I am trying with JavaScript as text and also, putting in actual JavaScript documents that will work in a script editor. All seem to be ineffective.

Execute “JavaScript KM Date Until script” Shell Script (external file)
Execute Shell Script (JavaScript in a KM script block)
Execute JavaScript For Automation (JavaScript in a KM script block)

Any further thoughts would be most appreciated.

Thanks.

PS: I have to wait for more input, I followed the KM scripting guide as best I could. I am still unsure if the kmvar dictionary is already built in or if I need to find it, download it, and install it.

Trying it's suggestion to put a name wrapper on the variable I am using, I am trying it but again, without feedback I am stalled a bit here. Naming the variable I have already in KM which is VarName12 and making it document.kmvar.VarName12 is still not 100% clear to me. I have tried it in the JavaScript text block and it seems to not have changed anything.

Remember that the value assigned to the variable VarName12 is the value that will continue to change as a new value is copied to clipboard and assigned to that value as the final script will loop until done. But it will always be some versions of; Thu, Feb 14.

Right now my goal is to test a single occurrence and to get this to work. Looping will come later and is not important right now.

Should document.kmvar.VarName12 be the actual name of the variable in KM as well? Or, should I redefine the variable in the JavaScript as VarName12 using the document.kmvar.VarName12 in the first instance of this? I have tried this in the example below with no luck at all.

Execute Shell Script
With input from Variable - VarName12
Execute text script - display results in a window

VarName12 = "'" + document.kmvar.VarName12 + "'"
String.prototype.lpad = function(padString, length) {
				var str = this;
				while (str.length < length)
				str = padString + str;
				return str;
}

var dateSource = VarName12
var monthDayStr = dateSource.match(/^\w+, (.+)/)[1];
getISODate(monthDayStr);
//==>"2019-05-09"
	function getISODate(pMonthDay){
				var yearStr = (new Date()).getFullYear().toString()
				var dateUnix = Date.parse(pMonthDay + ", " + yearStr);
				var oDate = new Date(dateUnix);
				var isoDateStr = oDate.getFullYear() + '-' + (oDate.getMonth()+1).toString().lpad('0',2) + '-' + (oDate.getDate()).toString().lpad('0',2);
				return isoDateStr;
	}

#5

If you are using Execute a JavaScript in Browser actions, it is very easy:

// Get Keyboard Maestro variable "My KM Data"
var myData = document.kmvar.My_KM_Data

If you are using JavaScript for Automation (JXA) in a Execute a JavaScript For Automation action, it is also fairly easy:

// --- SET KME APP VARIABLE NEEDED TO GET/SET KM VARIABLES ---
  var kme = Application("Keyboard Maestro Engine");
 
  //--- GET A KM VARIABLE --- 
  //    Returns empty string if it doesn't exist
  var someVarNameStr = kme.getvariable("KMVarNameToGet") || 'Default Value if NOT Found';

Both of those are shown in the KM Wiki links provided.

It is now clear how to get KM variables in JavaScript actions?


There is rarely any need to use Execute Shell Script to run JavaScript in a KM action.
You should use either:

  1. Execute a JavaScript in Browser action
    • IF you want to use the text of a web page
  2. Execute a JavaScript For Automation action
    • IF your script has nothing to do with content of web pages

I am not clear on what you are trying to accomplish.
Please post/upload your script/macro, both file and image along with clear description of what you want it to accomplish.


#6

First, this has nothing to do with a web browser. My solution ends with typing out the results in a text page.
Second, This involves rethinking how I am using scripts with Typinator and thinking how I can use KeyboardMaestro to run the same scripts in Typinator.
At the moment my solutions are still triggering a Typinator script as part of the KM solution.
I suppose it could be possible to do all of this in a KM macro but I have been breaking this down, one step at at time.

_______ STEP _______

This has to do with using a macro to read upcoming appointments in my calendar and gather the appointments in a specified date range for a specified calendar. The method I am using to gather the calendar appointments uses a free script called “icalbuddy”. The developer no longer supports icalbuddy. It still works, but the formatting it leaves me with formatting that I don't like.

With Typinator, this is the result of typing the trigger key combination, Typinator says to open a script named “Scripts/CalendarEvents_VA” and then to print or type out the text “endofhteline”. In this use of a bin script, the value is already set and cannot be passed into the script from a variable.

(What Typinator triggers)

{Scripts/CalendarEvents_VA}
 endoftheline

(Contents of the CalendarEvents_VA script)
------------------------
#!/bin/bash
#-- parameter: calendar
/usr/local/bin/icalBuddy -sd -nc -nrd -df " %a, %b %d" -eep "notes" -ic "VA" -ec "Repeating","US Holidays","Graphic","ProjectY", eventsFrom:Today to:Today+365 2>/dev/null
------------------------

This is what the results will look like.

Tue, Feb 19:

:white_check_mark: VA Pool Therapy Evaluation
2:30 PM - 3:30 PM

Wed, Feb 20:

:white_check_mark: ENT Appointment
2:30 PM - 3:30 PM

endoftheline

I put in the term “endoftheline” because I wanted something to trigger an if/then statement in KM later and stop the loop. The number of appointments that the script will pull in the next year will vary and this seemed an easy solution to have the script go until it is done.

_______ STEP _______

Now, I have a list of appointments in a formatting that I do not like. To fix this, I have a solution “IF” I can make a Typinator script available to my KM macro. This Typinator macro takes a known date in a specified format and passes it to a script. This is the reason I was asking how to set a variable in a KM macro and then pass that variable into a script.

To do this, I have to first alter the date format from this, “Tue, Feb 19”, to this, “2019-02-19”.

(The result of all this is going to change this;)

Tue, Feb 19:

:white_check_mark: VA Pool Therapy Evaluation
2:30 PM - 3:30 PM

Wed, Feb 20:

:white_check_mark: ENT Appointment
2:30 PM - 3:30 PM

endoftheline

(Into this)

02 days until • :white_check_mark: VA Pool Therapy Evaluation - Tue, Feb 19
03 days until • :white_check_mark: ENT Appointment - Wed, Feb 20

In Typinator, it looks like this:
(What Typinator triggers)

VA Mtng 02{tab}{tab}04/03/19:{tab}{tab}{Scripts/YMD-Diff.scpt 2019-04-03}

— { PLEASE NOTE} —, In the Typinator example, the date value is already set in the Typinator trigger event. This date value is then passed into the script. When I ask how to assign a value to a variable, I am trying to figure out a way to run this same script using KM alone as in KM I can assign a value from the clipboard to a variable.

(Contents of the Scripts/YMD-Diff.scpt 2019-04-03 script)
------------------------
function expand(dateParameter, context, testDate) { // -- parameter: yyyy-mm-dd
var refDate = "{{refDate=" + dateParameter + "}}"

	var today = new Date()
	if (testDate) {
		today = new Date(testDate)
	}

	today.setHours(0)
	today.setMinutes(0)
	today.setSeconds(0)

	var eventDate = new Date(dateParameter)
		
	var startDate = eventDate
	var endDate = today
	if (eventDate > today) { // future date; swap the dates
		var startDate = today
		var endDate = eventDate
	}
	
	var mSecPerDay=1000*60*60*24
	var diffDays = Math.round((endDate - startDate) / mSecPerDay)

	if (diffDays <= 31) {
		return refDate + multiple("day", diffDays)
	}
	
	var diffMonths = 1
	while (monthsInFuture(startDate, diffMonths) < endDate) {
		diffMonths ++
	}
	
	// go back one month, so we have a date in the past
	diffMonths--
	startDate = monthsInFuture(startDate, diffMonths)
	diffDays = Math.round((endDate - startDate) / mSecPerDay)
	
	// the calculations can cause subtle errors at the end of months.
	// if the remaining days are not more than 3, we omit them
	// if the remaining days are 28 or more, we assume that we have another month
	if (diffDays <= 3) {
		diffDays = 0
	} else if (diffDays >= 28) {
		diffMonths++
		diffDays = 0
	}
	
	var diffYears = 0
	if (diffMonths >= 12) {
		diffYears = Math.floor(diffMonths / 12)
		diffMonths = diffMonths % 12
	}
	
	var result = ""
	
	if (diffYears > 0) {
		result = result + multiple("year", diffYears)
	}

	if (diffMonths > 0) {
		if (result.length > 0) {
			if (diffDays == 0) {
				result = result + " and "
			} else { // "and" will be inserted before the days
				result = result + ", "
			}
		}
		result = result + multiple("month", diffMonths)
	}
	
	
	if (diffDays > 0) {
		if (result.length > 0) {
			result = result + " and "
		}
		result = result + multiple("day", diffDays)
	}
		
	return "{{refDate=" + dateParameter + "}}" + result
}

function multiple(unit, count)
{
	if (count == 1) {
		return "1 " + unit
	} else {
		return count + " " + unit + "s"
	}
}

function monthsInFuture(date, months)
{
	date = new Date(date.valueOf()) // create a copy of the date
	date.setFullYear(date.getFullYear(), date.getMonth() + months)
	return date
}

function testing(fromDate, nDays)
{
	var d = new Date(fromDate)
	for (var i=0; i<=nDays; i++) {
		var d = new Date(fromDate)
		d.setFullYear(d.getFullYear(), d.getMonth(), d.getDate()+i)
		console.log(d + " -- " + expand("2016-01-01", "", d.valueOf()))
	}
	return ""
}

// testing("2016-01-20", 90)

expand("2016-01-01") // for testing in Script Editor
------------------------

_______ STEP _______

I just showed the end results. To get there, there are two additional steps. Both are fairly straightforward, and both have already been resolved.

  1. you have to go through the list of icalbuddy output using keystrokes for option arrow end of word to copy each date then,
  2. convert the copied date and set it to a macro to be used in the since/until script.

And of course, you have to use this same select/copy/paste process to rearrange or delete all the lines of the output.

I hope this made some sense. Other than this explanation I can't see how offering a clear example at this point would help anyone understand this any better. I am still changing my macro flow as I go. The more I realize what I can do, the more my macro keeps changing. Keeping up with it so that I could explain the exact steps at this point seems counterproductive to how my already overheating brain is handling all of this.

Thanks


#7

I can appreciate that you want to try to use what you have already entered in Typinator, but, IMO, it would be best to NOT use Typinator in a KM Macro. I would just incorporate the text/expansion from Typinator directly into KM Macro, where you can have much better control over it.

Sorry, but your long post does not really help me understand what you are trying to do. Thus, I really can't help you much more. Maybe others can understand and help.

If you want my help, I will need, to start with, a simple real-world example of:

  1. Source text that you start with
  2. Criteria for changing the text
  3. Final resultant text
  4. Actual Macros/script that you currently use that you want help with.

#8

Yes, I agree, putting all of this in a single KM macro should be the goal. Typinator us using scripts that KM should also be able to use - and so far I have been doing this.

Scratching my head here, as showing you what I am trying to do ... is exactly what I just did. I just showed you the scripts. I just showed the before result. I just showed what it should end up doing.

I am trying to change this:
Tue, Feb 19:
• VA Pool Therapy Evaluation
2:30 PM - 3:30 PM

Into, This.
02 days until • VA Pool Therapy Evaluation - Tue, Feb 19

  1. bash script using icalbuddy produces calendar events in a certain format.
  2. Showed the result of the bash script
  3. Showed the result of the text after the second script
  4. Examples of both the bash script and the time/since script were provided.

I honestly do not understand, what you did not understand, in what I wrote?

OK then. I will get it eventually.


#9

Ignoring the Typinator part of your macroj/script, here's a macro/script that should help you get started:


Example Results

image

MACRO:   Extract Fields from Text [Example]

~~~ VER: 1.0    2019-02-17 ~~~

DOWNLOAD:

Extract Fields from Text [Example].kmmacros (6.3 KB)
Note: This Macro was uploaded in a DISABLED state. You must enable before it can be triggered.


image


JXA Script To Get Date Diff in Days

String.prototype.lpad = function(padString, length) {
    var str = this;
    while (str.length < length)
        str = padString + str;
    return str;
}

'use strict';

var app = Application.currentApplication()
app.includeStandardAdditions = true

var kmInst = app.systemAttribute("KMINSTANCE");
var kmeApp = Application("Keyboard Maestro Engine")
 
var dateSource = kmeApp.getvariable("Local__Date",  {instance: kmInst}) || 'Thu, Feb 19';


//--- Get First Capture Group ---
//var dateSource = 'Thu, Feb 19'
var monthDayStr = dateSource.match(/^\w+, (.+)/)[1];
//==>"May 09"

getDateDiff(monthDayStr).toString().lpad('0',2);
//==>"2019-05-09"


 function getDateDiff(pMonthDay){

    var yearStr = (new Date()).getFullYear().toString();
    var dateUnix = Date.parse(pMonthDay + ", " + yearStr);
    var oDate = new Date(dateUnix);
    var today = new Date()
    var dateDiff = oDate.getDate() - today.getDate();
    return dateDiff;
 }

 function getISODate(pMonthDay){

    var yearStr = (new Date()).getFullYear().toString();
    var dateUnix = Date.parse(pMonthDay + ", " + yearStr);
    var oDate = new Date(dateUnix);
    var isoDateStr = oDate.getFullYear() 
        + '-' + (oDate.getMonth()+1).toString().lpad('0',2) 
        + '-' + (oDate.getDate()).toString().lpad('0',2);
    return isoDateStr;
 }


Does this basicly do what you want?


#10

I changed the input for Local__SourceStr to %SystemClipboard%. Yes, thanks very much. I added an execute shell script to start the macro using the bash script to get the calendar list so I can drop Typinator completely from the solution. Set the results to the clipboard of course.

#!/bin/bash
#-- parameter: calendar
/usr/local/bin/icalBuddy -sd -nc -nrd -df " %a, %b %d" -eep "notes" -ic "VA" -ec "Repeating","US Holidays","Graphic","ProjectY", eventsFrom:Today-30 to:Today+365 2>/dev/null

This works great - with one caveat, this blows up midway through the list. I am not sure why yet.

Testing: Works with Feb dates. It blows up with March, April, May dates.
Result: Changing all months to Feb made no difference so - not the months blowing it up. What then?

Here are the last ones that are blowing up and what I mean by blowing up.

Try These:

 Tue, Feb 26:
------------------------
• ✅ VA Sleep Adjustment
    1:00 PM - 2:00 PM

 Fri, Mar 01:
------------------------
• ✅ VA PT
    2:00 PM - 3:00 PM

 Thu, Mar 07:
------------------------
• ✅ Pendleton VA
    1:00 PM - 2:00 PM

 Mon, Mar 18:
------------------------
• ✅ VA OT
    2:00 PM - 3:00 PM

 Mon, Apr 08:
------------------------
• ✅ VA Dr. follow up
    2:20 PM - 3:20 PM

These give The Following Result:

08 days until	• ✅ VA Sleep Adjustment -Tue, Feb 26
-17 
  0:-, 
  1:1, 
  2:7, 
  length:3 days until	• ✅ VA PT -Fri, Mar 01
-11 
  0:-, 
  1:1, 
  2:1, 
  length:3 days until	• ✅ Pendleton VA -Thu, Mar 07
00 days until	• ✅ VA OT -Mon, Mar 18
-10 
  0:-, 
  1:1, 
  2:0, 
  length:3 days until	• ✅ VA Dr. follow up -Mon, Apr 08

ADDED: I realize that in the example I provided, all the dates are going forward from today. But, the script I was showing at the start of the thread is Since/Until because it will differentiate if an event is so many days, weeks months ahead (xx days until) - or, so many days weeks or months behind (xx days since)

This will become very important when tracking a Project on a calendar. By focusing the bash script on just the Project's calendar (Project xyz), then this macro would show me how long it was since I sent in the proposal, had the meeting, did the workshop, etc.. I would also want to know how far ahead is my next milestone, meeting, etc.

In the example that you provided (which I am very grateful for), when I add past dates, it still looks up the past dates as future "until" dates. Of course, it is still blowing up, so, perhaps when this is all sorted out, this part will work too? Thought it was worth mentioning.

Your script Since/Until and the Typinator version of the script since/Until:

Your version of Since/Until in KM script now:
————————————

String.prototype.lpad = function(padString, length) {
    var str = this;
    while (str.length < length)
        str = padString + str;
    return str;
}

'use strict';

var app = Application.currentApplication()
app.includeStandardAdditions = true

var kmInst = app.systemAttribute("KMINSTANCE");
var kmeApp = Application("Keyboard Maestro Engine")
 
var dateSource = kmeApp.getvariable("Local__Date",  {instance: kmInst}) || 'Thu, Feb 19';


//--- Get First Capture Group ---
//var dateSource = 'Thu, Feb 19'
var monthDayStr = dateSource.match(/^\w+, (.+)/)[1];
//==>"May 09"

getDateDiff(monthDayStr).toString().lpad('0',2);
//==>"2019-05-09"


 function getDateDiff(pMonthDay){

		var yearStr = (new Date()).getFullYear().toString();
    var dateUnix = Date.parse(pMonthDay + ", " + yearStr);
		var oDate = new Date(dateUnix);
		var today = new Date()
		var dateDiff = oDate.getDate() - today.getDate();
		return dateDiff;
 }

 function getISODate(pMonthDay){

		var yearStr = (new Date()).getFullYear().toString();
    var dateUnix = Date.parse(pMonthDay + ", " + yearStr);
		var oDate = new Date(dateUnix);
		var isoDateStr = oDate.getFullYear() 
				+ '-' + (oDate.getMonth()+1).toString().lpad('0',2) 
				+ '-' + (oDate.getDate()).toString().lpad('0',2);
		return isoDateStr;
 }

Version of Since/Until in Typinator script now:
————————————

function expand(dateParameter, context, testDate) { // -- parameter: yyyy-mm-dd
	var refDate = "{{refDate=" + dateParameter + "}}"

	var today = new Date()
	if (testDate) {
		today = new Date(testDate)
	}

	today.setHours(0)
	today.setMinutes(0)
	today.setSeconds(0)

	var eventDate = new Date(dateParameter)
		
	var startDate = eventDate
	var endDate = today
	var sinceUntil = " since"
	if (eventDate > today) { // future date; swap the dates
		var startDate = today
		var endDate = eventDate
		var sinceUntil = " until"
	}
	
	var mSecPerDay=1000*60*60*24
	var diffDays = Math.round((endDate - startDate) / mSecPerDay)

	if (diffDays <= 31) {
		return refDate + multiple("day", diffDays) + sinceUntil
	}
	
	var diffMonths = 1
	while (monthsInFuture(startDate, diffMonths) < endDate) {
		diffMonths ++
	}
	
	// go back one month, so we have a date in the past
	diffMonths--
	startDate = monthsInFuture(startDate, diffMonths)
	diffDays = Math.round((endDate - startDate) / mSecPerDay)
	
	// the calculations can cause subtle errors at the end of months.
	// if the remaining days are not more than 3, we omit them
	// if the remaining days are 28 or more, we assume that we have another month
	if (diffDays <= 3) {
		diffDays = 0
	} else if (diffDays >= 28) {
		diffMonths++
		diffDays = 0
	}
	
	var diffYears = 0
	if (diffMonths >= 12) {
		diffYears = Math.floor(diffMonths / 12)
		diffMonths = diffMonths % 12
	}
	
	var result = ""
	
	if (diffYears > 0) {
		result = result + multiple("year", diffYears)
	}

	if (diffMonths > 0) {
		if (result.length > 0) {
			if (diffDays == 0) {
				result = result + " and "
			} else { // "and" will be inserted before the days
				result = result + ", "
			}
		}
		result = result + multiple("month", diffMonths)
	}
	
	
	if (diffDays > 0) {
		if (result.length > 0) {
			result = result + " and "
		}
		result = result + multiple("day", diffDays)
	}
		
	return "{{refDate=" + dateParameter + "}}" + result + sinceUntil
}

function multiple(unit, count)
{
	if (count == 1) {
		return "1 " + unit
	} else {
		return count + " " + unit + "s"
	}
}

function monthsInFuture(date, months)
{
	date = new Date(date.valueOf()) // create a copy of the date
	date.setFullYear(date.getFullYear(), date.getMonth() + months)
	return date
}

function testing(fromDate, nDays)
{
	var d = new Date(fromDate)
	for (var i=0; i<=nDays; i++) {
		var d = new Date(fromDate)
		d.setFullYear(d.getFullYear(), d.getMonth(), d.getDate()+i)
		console.log(d + " -- " + expand("2016-01-01", "", d.valueOf()))
	}
	return ""
}

// testing("2016-01-20", 90)

expand("2016-01-01") // for testing in Script Editor

Macro: Extract Event Data from Text; Calc Days Diff, Reformat Results [Example]
#11

OK, I think I've fixed the above issue with this Macro just posted:

Example Results

image

MACRO: Extract Event Data from Text; Calc Days Diff, Reformat Results [Example]

Please let us know if this is good enough to get you started.


#12

Fantastic!. Thanks very much for your assistance in this.

I added one important piece to it. I added an "Execute Shell Script" with Save Results to Clipboard. I used this bash script;

#!/bin/bash
#-- parameter: calendar
/usr/local/bin/icalBuddy -sd -nc -nrd -df " %a, %b %d" -eep "notes" -ic "VA", -ec "Repeating","US Holidays","Graphic","ProjectY", eventsFrom:Today-30 to:Today+365 2>/dev/null

The only reason I needed to copy the text at the beginning was to alter the formatting from a bash script that used icalbuddy to collect information from my calendar. Putting that script inside the macro saves running the bash script, copying the text the bash script put out, running the macro, etc.

Kudos for solving a problem I have been struggling with for a very long time.

I added the bash script so I don't have to start by copying and I have the results paste (to text). This is what it looks like now;
JMichaelTX Updated Calendar VA Since:Until .kmmacros (15.6 KB)

Just to mention for anyone who likes this, the icalbuddy script can be easily altered to include other calendars, all calendars, etc.

Also, the date range can be modified reference the icalbuddy pages.

Here I have changed the "365" days date range to the current calendar year;

#!/bin/bash
#-- parameter: calendar
/usr/local/bin/icalBuddy -sd -nc -nrd -df " %a, %b %d" -eep "notes" -ic "Birthdays", -ec "Repeating","US Holidays","Graphic","ProjectY", eventsFrom:'january 01, 2019' to:'december 31, 2019' 2>/dev/null

I found this to be necessary as when I started using the new KM macro for my birthday calendar I was getting duplicate entries for last Jan and first half of Feb dates. Duplicate of course as this is also showing past birthdays.

PS - I hate to mention another thing as you have already gone way above and beyond with helping me but, I am curious if it is at all easy to add in a single separation line between the past dates and the current date? I can figure this out eventually but, just wondering if there is a way to insert this into your script?

What I am asking would turn this:

-07 days since • VA Dr. - Thu, Feb 12
-06 days since • VA Dr. - Thu, Feb 13
-05 days since • VA Dr. - Thu, Feb 14
00 days until • :white_check_mark: VA Therapy Evaluation - Tue, Feb 19
01 days until • :white_check_mark: VA Pool Therapy - Tue, Feb 20
02 days until • :white_check_mark: VA Pool Therapy - Tue, Feb 21

Into, this:

-07 days since • VA Dr. - Thu, Feb 12
-06 days since • VA Dr. - Thu, Feb 13
-05 days since • VA Dr. - Thu, Feb 14

00 days until • :white_check_mark: VA Therapy Evaluation - Tue, Feb 19
01 days until • :white_check_mark: VA Pool Therapy - Tue, Feb 20
02 days until • :white_check_mark: VA Pool Therapy - Tue, Feb 21

I was able to do it by repeating the bash script (and the rest of your script), once for past dates, adding a return keystroke, and again for dates from today until December 31st. I was wondering if you knew how to do this more efficiently within the script?

Again, sincere thanks and appreciation for the help.

__________ Error Detected: __________

If more than one appointment occurs on the same calendar day, this script is only picking one - and ignoring the other. I have discovered this in past events (since) and also in future events (until).


#13

Here's the basic un-tested pseudo logic:

//--- Before Loop ---
Set Last_DaysDiff to 0

//--- Insert This Just Before Local__Results is set ---
IF (Last_DaysDiff < 0) & (Local__DaysDiff > 0) THEN
   Set Local__Results to Local__Results + CR
   Set Last_DaysDiff to Local__DaysDiff

That's probably not perfect, but should be enough to give you the right design idea.
I'll leave the implementation up to you, but please post when you have it.


#14

Here is my run twice example with if/then/else as I understood it from your last post. It doesn’t change anything - the added return is from my workaround of doing it twice with a return keystroke in between:

JMichaelTX Updated Calendar VA Since:Until rev 2.kmmacros (24.9 KB)

EDIT AND CLARIFICATION:

I tested by putting in some duplicate events on my calendar. Now I see the problem. If there are more than one events on a days date, then they are listed immediately after each other. This is different from the standard way that I told you it worked. My apologies.

In the script, there needs to be a test to see if the next line is blank or not. If not, the script continues. If a blank, then it is as before. I will work this out in a KM macro. I hope to understand the places in the script you provided that changes a date to a value.

This is what it looks like when there are more than one entries per date:

Fri, Feb 22:
(line) ------------------------
:white_check_mark: VA MRI Left Shoulder
1:00 PM - 2:00 PM
:white_check_mark: VA Test Duplicate Entry 1
1:00 PM - 2:00 PM
:white_check_mark: VA Test Duplicate Entry 2
1:00 PM - 2:00 PM

Mon, Feb 25:
(line) ------------------------
:white_check_mark: VA OT
2:00 PM - 3:00 PM

Tue, Feb 26:
(line) ------------------------
:white_check_mark: VA Sleep Adjustment
1:00 PM - 2:00 PM
:white_check_mark: VA Test Duplicate Entry 4
1:00 PM - 2:00 PM
:white_check_mark: VA Test Duplicate Entry 3
1:00 PM - 2:00 PM

Fri, Mar 01:
(line) ------------------------
:white_check_mark: VA Pool Therapy
10:30 AM - 11:30 AM
:white_check_mark: VA PT
2:00 PM - 3:00 PM


#15

So, it is now working OK for you? It works fine for me:

image


I think I've answered/solved well more than your original post:

And even your greatly expanded request here:

So I will leave any further fine-tuning "up to the student as an exercise". :wink:

If one of the above posts solves your problem/question as originally stated, please check the "Solved" checkbox (click for details) at the bottom of that post.

Otherwise, please post your remaining questions/issues about this problem.
If you have other questions, please start a new topic.


#16

It does, and it doesn’t. But, you have helped quite a bit with what you have provided just the same. I thank you for your assistance.


#17

It is a binary choice: either it does, OR, it does NOT, solve the question/problem you posed in your original post. It may not do everything you ultimately want it to do, but that is not the question.

This is why we strongly encourage everyone to post a new topic when they have a new question, even if it is related to the OP.


#18

Not to beat this into the ground because, if you are satisfied you can offer nothing more then I am grateful for what you have already offered.

However, my OP was about how to get a variable into a script. And, knowing very little about the actual structure of a javascript and being unsure about how to set a KM variable to a variable to use in a Javascript, that task is still ahead of me to learn. You provided an excellent example, and, I will say it once again, I am grateful for your assistance.

But, I still don't understand how to break into the script you provided to make it do what I want. And that, is what I have to learn to do.

I can make the KM script assess what is on a line and if the date variable is on the line, make sth script do a thing and return the results until a line no longer begins with a date structure.

I am used to seeing how this works on printed text, but applying this same logic to what is in a variable is a little different. Or perhaps it is just the same? I have only experience the programming language used by Panorama. In that language, a variable does not have to be declared. It is the same thing in AppleScript but I hate using AppleScript as it is too confusing for my tastes.

I will get there eventually but, as I said, it does, and it doesn’t answer my OP question at the start of this thread.


#19

Your question:

How does my post not answer your direct question:

It is a simple script, and all you need to do is call the getISODate() function from anywhere in your script.


#20

Thanks. It will take me a while to figure this out but, when I do I will post it in a new thread. I will mark this thread closed. Thanks again for the help.