Skip to content

Instantly share code, notes, and snippets.

@marshall007
Last active December 20, 2015 00:09
Show Gist options
  • Save marshall007/6039852 to your computer and use it in GitHub Desktop.
Save marshall007/6039852 to your computer and use it in GitHub Desktop.
AngularJS "Time Ago" filter.
timeAgo = angular.module 'app'
# Filter: time
# Date.parse() is inconsistent across browsers
# this is a shim for parsing ISO-8601 date strings
timeAgo.filter 'time', ->
(time) ->
return if not time
return time if angular.isDate time
return new Date time if angular.isNumber time
time = time.replace /\.\d+/, ''
time = time.replace(/-/, '/').replace /-/, '/'
time = time.replace(/T/, ' ').replace /Z/, ' UTC'
time = time.replace /([\+\-]\d\d)\:?(\d\d)/, ' $1$2'
time = new Date time * 1000 or time
# Filter: timeAgo
# Allows you to override any of the default templates
timeAgo.constant 'timeAgoTemplates', {}
timeAgo.filter 'timeAgo', [
'timeFilter', 'timeAgoTemplates'
(normalize, config) ->
templates =
default: 'Never'
prefix: ''
suffix: 'ago'
seconds: 'less than a minute'
minute: 'a minute'
minutes: '%d minutes'
hour: 'an hour'
hours: '%d hours'
day: 'a day'
days: '%d days'
month: 'a month'
months: '%d months'
year: 'a year'
years: '%d years'
angular.extend templates, config
template = (t, n) ->
templates[t]?.replace /%d/i, Math.abs Math.round n
build = -> # (prefix, value, suffix) ->
args = Array.prototype.slice.call arguments, 0
return args.map (a) ->
a.trim()
.join(' ').trim()
(time, none, prefix) ->
none = none or templates.default
prefix = prefix or templates.prefix
time = normalize time
return none if not time
now = new Date
seconds = ((now.getTime() - time) * .001) >> 0
minutes = seconds / 60
hours = minutes / 60
days = hours / 24
years = days / 365
return build prefix, (
seconds < 45 and template('seconds', seconds) or
seconds < 90 and template('minute', 1) or
minutes < 45 and template('minutes', minutes) or
minutes < 90 and template('hour', 1) or
hours < 24 and template('hours', hours) or
hours < 42 and template('day', 1) or
days < 30 and template('days', days) or
days < 45 and template('month', 1) or
days < 365 and template('months', days / 30) or
years < 1.5 and template('year', 1) or
template 'years', years
), templates.suffix
]
# Directive: <time-ago>
timeAgo.directive 'timeAgo', [
'$interval', 'timeFilter',
($interval, normalize) ->
restrict: 'E'
replace: true
scope:
default: '@'
prefix: '@'
time: '='
template: """
<time datetime="{{ time }}"
title="{{ time | date:'short' }}">
{{ time | timeAgo:default:prefix }}
</time>
"""
link: (scope) ->
clear = null
scope.$watch 'time', (value) ->
$interval.cancel clear if clear
hours = (Date.now() - normalize value) / 3600000
# update every 30 seconds if less than an hour has passed
if hours < 1
clear = $interval angular.noop, 30000
]
@jtangelder
Copy link

awesome, modified it a bit to create an auto updating directive

<timeago time="post.created_on"></timeago>
angular.module('app').directive 'timeago', ->
  templates =
    seconds: 'a few seconds ago'
    minute: 'a minute ago'
    minutes: '%d minutes ago'
    hour: 'an hour ago'
    hours: '%d hours ago'
    day: 'a day ago'
    days: '%d days ago'
    month: 'a month ago'
    months: '%d months ago'
    year: 'a year ago'
    years: '%d years ago'

  # replace %d with a value
  template = (name, value) ->
    templates[name]?.replace /%d/i, Math.abs(Math.round(value))

  # generate time ago string
  getTimeAgo = (time) ->
    if not time then return 'Never'

    time = time.replace /\.\d+/, ''
    time = time.replace(/-/, '/').replace /-/, '/'
    time = time.replace(/T/, ' ').replace /Z/, ' UTC'
    time = time.replace /([\+\-]\d\d)\:?(\d\d)/, ' $1$2'
    time = new Date time * 1000 || time

    now = new Date
    seconds = ((now.getTime() - time) * .001) >> 0
    minutes = seconds / 60
    hours = minutes / 60
    days = hours / 24
    years = days / 365

    if seconds < 30 then return template 'seconds', seconds
    if seconds < 90 then return template 'minute', 1
    if minutes < 45 then return template 'minutes', minutes
    if minutes < 90 then return template 'hour', 1
    if hours < 24 then return template 'hours', hours
    if hours < 42 then return template 'day', 1
    if days < 30 then return template 'days', days
    if days < 45 then return template 'month', 1
    if days < 365 then return template 'months', days/30
    if years < 1.5 then return template 'year', 1
    return template 'years', years

  restrict: 'E'
  replace: true
  template: '<time datetime="{{time}}" title="{{time|date:\'medium\'}}">{{timeago}}</time>'
  scope:
    time: "="
  controller: ['$scope','$interval', ($scope, $interval)->
      $scope.timeago = getTimeAgo($scope.time)
      $interval(->
          $scope.timeago = getTimeAgo($scope.time)
      , 5000)
    ]

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