Date In the Past

I am seeking a way to start with a date in a specific format, call it, 2017-02-08. I am not stuck on the year-month-day format at this point but it is similar to an example I have using Typinator and JavaScript. Typinator does a great job tracking dates for me. But those dates are preset. This one I want to start and end with a selected text date.

This date value will already be on a document. To begin the macro I will highlight the date and …

What I want is to calculate from that past (selected) date until today and have the results paste back as or similar to:
“From 2017-02-08 it has been 35 days as of Wed. March 15, 2017.” here assuming I did this on that later date.

This is for tracking progress notes in several files. I think it will be useful when I enter some notes on a date to later be able to review progress in my notes and have a macro tell me how long it has been sitting there - for whatever reason, closed, side tracked, etc.

I looked for a date item in the KM macros but did not find one. I looked and there are several date calculation threads on this forum, none of which appeared simple enough to easily grasp for this purpose.

The example I took from my Typinator macro looks like this:


function expand(dateParameter, context) { // – parameter: yyyy-mm-dd
_ var today = new Date()_
_ today.setHours(0)_
_ today.setMinutes(0)_
_ today.setSeconds(0)_

_ var eventDate = new Date(dateParameter)_

_ var sinceUntil = eventDate > today ? 'until ’ : ‘since’_

_ var mSecPerDay=10006060*24_
_ var diffMsec = Math.abs(today-eventDate); // difference in milliseconds_
_ var diffDays = Math.round(diffMsec/mSecPerDay)_
_ res1 = diffDays < 10 ? ‘0’ + diffDays : diffDays_

_ return “{{refDate=” + dateParameter + “}}” + res1 + " days " + sinceUntil_
}


I am not skilled enough to understand how to tweak this into what I am asking for. I looks like if I set the clipboard value (the date) to dateParameter as a variable and simply run this script, it should do. Will play with it a while. Still, it never hurts to ask in case I am wasting my time.

Thanks for any help.

Here is a acro that will do that. No scripting required since Keyboard Maestro already has a bunch of date functions.

If it will always be at least 2 days ago, you can remove that second last action (the If Then Else action), but I never like to say "1 days ago", the extranious s always bothers me.

Days Ago.kmmacros (4.0 KB)

The ROUND in the calculation should not be needed since the dates are always GMT there should be no daylight savings issues or the like, but I left it in anyway.

Excellent, thanks much. I was trying, not succeeding but, trying. I really appreciate the help.

Looking around this page for the “Solved” button now to close this topic.

Found it. :slight_smile:

Sorry, but you found the wrong “Solved” checkbox.
Every post in this topic has a “Solved” checkbox at the bottom of the post.
You checked the one at the bottom of your post.
I think you intended to check the one at the bottom of Peter’s post.

Sorry, I hadn’t figure that out yet. :grimacing:

Hey @levelbest,

Here’s how to do it with AppleScriptObjC:

------------------------------------------------------------------------------
# Auth: Christopher Stone { Heavy Lifting by Shane Stanley }
# dCre: 2016/03/17 03:45
# dMod: 2017/02/10 14:37
# Appl: AppleScriptObjC
# Task: Use Data-Detectors to extract a date from unstructured string input.
#     : Output number of days elapsed until today.
# Libs: None
# Osax: None
# Tags: @Applescript, @Script, @ASObjC, @Date, @String, @Data-Detectors
------------------------------------------------------------------------------
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
------------------------------------------------------------------------------

# Example Date String Inputs:
set inputStr to "2017/01/01 10:31:21"

# Extract the date
set startDate to my getDatesIn:inputStr
set startDate to first item of (startDate as list)

# Get the current date
set currentDate to current date

# Convert from ObjC-Date-Object to an AppleScript date:
considering numeric strings
   if AppleScript's version < "2.5" then
      set startDate to my makeASDateFrom:startDate
   else
      set startDate to startDate as date
   end if
end considering

# Zero-out the time
set hours of startDate to 0
set minutes of startDate to 0
set seconds of startDate to 0

set numberOfDays to (((get current date) - startDate - (1 * days)) / days) as integer

# Create formatted date strings for output:
set startDate to my formatDate:(startDate) usingFormat:"y-MM-dd"
set endDate to my formatDate:currentDate usingFormat:"eee. y-MM-dd"

# Assemble output string:
set outputString to "From " & startDate & " it has been " & numberOfDays & " days as of " & endDate

return outputString

------------------------------------------------------------------------------
--» HANDLERS
------------------------------------------------------------------------------
on formatDate:theDate usingFormat:formatString
   if class of theDate is date then set theDate to my makeNSDateFrom:theDate
   set theFormatter to current application's NSDateFormatter's new()
   theFormatter's setLocale:(current application's NSLocale's localeWithLocaleIdentifier:"en_US_POSIX")
   theFormatter's setDateFormat:formatString
   set theString to theFormatter's stringFromDate:theDate
   return theString as text
end formatDate:usingFormat:
------------------------------------------------------------------------------
on getDatesIn:aString
   # Convert string to Cocoa string
   set anNSString to current application's NSString's stringWithString:aString
   # Create data detector
   set theDetector to current application's NSDataDetector's dataDetectorWithTypes:(current application's NSTextCheckingTypeDate) |error|:(missing value)
   # Find first match in string; returns an NSTextCheckingResult object
   set theMatch to theDetector's firstMatchInString:anNSString options:0 range:{0, anNSString's |length|()}
   if theMatch = missing value then error "No date found"
   # Get the date property of the NSTextCheckingResult
   set theDate to theMatch's |date|()
   return theDate
end getDatesIn:
------------------------------------------------------------------------------
# Required before 10.11
on makeASDateFrom:theNSDate
   set theCalendar to current application's NSCalendar's currentCalendar()
   set comps to theCalendar's componentsInTimeZone:(missing value) fromDate:theNSDate # 'missing value' means current time zone
   tell (current date) to set {theASDate, year, day, its month, day, time} to ¬
      {it, comps's |year|(), 1, comps's |month|(), comps's |day|(), (comps's hour()) * hours + (comps's minute()) * minutes + (comps's |second|())}
   return theASDate
end makeASDateFrom:
------------------------------------------------------------------------------
on makeNSDateFrom:theASDate
   set {theYear, theMonth, theDay, theSeconds} to theASDate's {year, month, day, time}
   if theYear < 0 then
      set theYear to -theYear
      set theEra to 0
   else
      set theEra to 1
   end if
   set theCalendar to current application's NSCalendar's currentCalendar()
   set newDate to theCalendar's dateWithEra:theEra |year|:theYear |month|:(theMonth as integer) ¬
      |day|:theDay hour:0 minute:0 |second|:theSeconds nanosecond:0
   return newDate
end makeNSDateFrom:
------------------------------------------------------------------------------

This has only one real advantage over Peter’s method, in that it does NOT need to know the format of the date-input string (due to using Apple’s Data Detectors).

-Chris

1 Like

Just for fun and possible interest, here is a solution using JXA:
I think it may be the simplest solution.

Note that it tries to get the Start Date from a KM Variable "Start_Date".

2017-02-10 21:44 CT

###JavaScript Date Difference

(function run() {

var kmeApp = Application("Keyboard Maestro Engine");
var startDateStr = kmeApp.getvariable('GDD__Start_Date');

try {

  if (! startDateStr) {throw new Error("Invalid Date: >>>" + startDateStr + "<<<")}
  
  var startDate  = createDate(startDateStr);
  today = new Date();
    
  var day2Sec = 1000 * 3600 * 24;
  var daysDiff = Math.trunc((today.getTime() - startDate.getTime())/day2Sec);


  return daysDiff;
}
catch (oErr) {
  return "[ERROR]\n" + oErr.message
}

function createDate(pDateStr) {
  
  dateFromStr = new Date(pDateStr);
  
  if (!dateFromStr || isNaN(dateFromStr)) {throw new Error("Invalid Date: >>>" + pDateStr + "<<<")}
  
  //--- ADD TIME ZONE OFFSET IF DATE STRING IS ISO FORMAT ---
  
  if (RegExp(/^\d{4}[\/\.-]\d\d[\/\.-]\d\d/).test(pDateStr)) {
    dateFromStr.setMinutes(dateFromStr.getMinutes() + dateFromStr.getTimezoneOffset());
    }

  return dateFromStr;
}

})();

JavaScript Date() function is very accommodating for different string date formats. For example:

###JavaScript Date String to Date Script

var myDates = {};

//--- EXAMPLE OF THE SAME DATE FORMATTED IN 3 COMMON WAYS ---

myDates.iso = createDate("2017-02-08");
myDates.us  = createDate("2/08/2017");
myDates.us2  = createDate("Feb 8, 2017");

//--- CURRENT DATE/TIME ---
myDates.today = new Date();

logObj(myDates);

//~~~~~~~~~~~~~~~~ END OF MAIN SCRIPT ~~~~~~~~~~~~~~~~~~~~~~

function createDate(pDateStr) {
  
  dateFromStr = new Date(pDateStr);
  
  //--- ADD TIME ZONE OFFSET IF DATE STRING IS ISO FORMAT ---
  //    (like "2016-02-10")
  //    When a date object is created from a string in ISO format,
  //    JavaScript assumes you intend this to be in GMT time zone,
  //    and so subtracts the time zone offset for your locale.
  
  if (RegExp(/^\d{4}[\/\.-]\d\d[\/\.-]\d\d/).test(pDateStr)) {
    dateFromStr.setMinutes(dateFromStr.getMinutes() + dateFromStr.getTimezoneOffset());
    console.log("converted ISO Date: " + pDateStr);
    }

  return dateFromStr;
}

function logObj(pObject) {
  for (key in pObject){
    console.log(key + ": " + pObject[key]);
  }
}

// RESULTS:
/* converted ISO Date: 2017-02-08 */
/* Fri Feb 10 2017 21:39:58 GMT-0600 (CST) */
/* iso: Wed Feb 08 2017 00:00:00 GMT-0600 (CST) */
/* us: Wed Feb 08 2017 00:00:00 GMT-0600 (CST) */
/* us2: Wed Feb 08 2017 00:00:00 GMT-0600 (CST) */
/* today: Fri Feb 10 2017 21:39:58 GMT-0600 (CST) */


So here's my macro based on JavaScript:

###Example Results

<img src="/uploads/default/original/2X/4/45c607636845d64dd26b707289e63c10dc4b60ac.png" width="445" height="205">

###MACRO:&nbsp;&nbsp;&nbsp;Get Date Difference in Days @Example

~~~ VER: 1.0&nbsp;&nbsp;&nbsp;&nbsp;2017-02-10 ~~~

####DOWNLOAD:
<a class="attachment" href="/uploads/default/original/2X/e/e79864777739a2cae6779b92b797caf58ffc137d.kmmacros">Get Date Difference in Days @Example.kmmacros</a> (8.8 KB)

---

###ReleaseNotes

All date calculations are done in a JXA script.

---

<img src="/uploads/default/original/2X/2/229aecf7e2962b3a0aea6548fc00f6023abbb6ac.png" width="565" height="1037">
1 Like