iA


CoffeeScript Experiments: Magic Date

by Vijay Kiran

I started learning CoffeeScript recently and wanted to get my hands dirty. Here’s the first experiment.

##########################################################################################
#                                 Magic Date
#
# Author: Vijay Kiran<mail@vijaykiran.com>
#
# Allows conversion of natural date string to real date object.
# e.g. tomorrow, today, 2w etc.
#
#
# Implementation is shame-lessly based on MagicDate in Python. 
##########################################################################################
##                        Requirements/Specs                                            ##
##                                                                                      ##
#
# One liner - should work like OmniFocus date field
## 
#
# Details:
# • 2d, -3w, 1h, 1y1m, and so on — Relative dates and times put the date at
# a certain amount of time from right now. Negative numbers represent times in the past.
# 
# • 2 days, -3 weeks, 1 hour, 1 year 1 month, and so on — 
# You can use the full names of units too.
# 
# • yesterday, tomorrow, next thursday, last month, this friday, and so on — 
#   You can refer to relative dates using common words. 
#   “This”, “next”, and “last” have specific meanings: this friday always means 
#   the Friday in this week, next friday always means 
#   the Friday in the next week, and last friday always means 
#   the Friday in last week, regardless of what day today is. 
#   Other units work in the same way.
# 
# • september, thurs, 2019, and so on — If you enter the name of a specific time period, 
#   the date will be at its beginning. So september means September first.
# 
# • 5/23/08 10a, 9.30.09 2:00 PM, and so on — You can use the short date format as 
#   defined in your International system preferences. 
# 
# • 2w sat, 4d @ 5p, tues 6a, aug 6 tues 5p, and so on —
#    Mix the available formats however you like.
# 
# • now, 9, 14:00, tom, and so on — OmniFocus makes its best guess 
#   at things like bare numbers, times, and word fragments. 
#   If you think something might work, give it a try.
 
weekdays = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']
 
magicDate = (str) ->
 d = new Date
 regs = [
   # Today
   [ /^tod/i, Date ],
   # Tomorrow
   [ /^tom/i, -> d.setDate(d.getDate() + 1) ;  d ],
   # Now
   [ /^now/i, Date],
   # Yesterday
   [ /^yes/i, -> d.setDate(d.getDate() - 1) ; d ],
   [ /^(last|next|this)\s(week)/, 
     (result) ->
       switch result[1].toLowerCase()
         when "last" then d.setDate(d.getDate() - 7)
         when "this" then d.setDate(d.getDate() + 1)
         when "next" then d.setDate(d.getDate() + 7)
       d
   ],
    # <last> <this>/<next> <weekday>
    [
      /^(last|this|next)\s(\S+)/i,
      (result) ->
        rel = result[1].toLowerCase()
        weekday = result[2].toLowerCase()
        today = d.getDay()
        # Pick index + 1 because getDay is 1 = Monday, 2 = Tuesday and so on
        index = (i + 1 for item, i in weekdays when new RegExp(weekday).test(item))
 
        switch result[1].toLowerCase()
          when "last" then d.setDate(d.getDate() - (today - index + (if (index < today ) then 0 else 7)) )
          when "this" then  d.setDate(d.getDate() + index - today)
          when "next" then  d.setDate(d.getDate() + index + today - 1 )
        d
    ]
   ]
# 
 for x in regs
     re  = x[0] 
     fn  = x[1]
     if re.test str then return fn(str.match re)

And here’s how you can use it:

testStrings = [ "yes", "yesterda", "Yes", "ye", "-"
                "tod", "todd", "today", "TodaY", "-"
                "now", "nowww", "Now", "ANDNOW","-"
                "tom", "tomorrow", "Tomorrow", "to", "-"  
                "last mon", "last tuesday", "last wed", "last thurs",  "last fri", "last sat" , "LAst Sunda", "-"
                "last week", "tHIS Week", "next week", "-"
                "this mon", "this tuesday", "this weDNE", "this thursday", "THIS FRIDAY", "this sat", "this sun", "-"
                "next mon", "NExt tue", "nex wednesday", "next thursday", "Next Friday", "Next Satur", "next sun", "-"]
 
for str in testStrings
  if str is "-" 
    console.log "---------------------------------------------------"
  else
    console.log "#{str}           =     #{magicDate str}"