Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow specifying a timezone that :at parameters should be interpreted as #239

Closed
wants to merge 8 commits into from

Conversation

Projects
None yet
@twalpole
Copy link

commented Jul 26, 2012

Allows using set :timzone, '' in the configuration file to specify that times in :at should be interpreted as being in the specified timezone and then converted to localtime for writing to cron

@javan

This comment has been minimized.

Copy link
Owner

commented Aug 8, 2012

Say your server is on UTC time - many are - and you're on EDT (D for daylight savings time). You set your schedule.rb file to eastern time and deploy a job to run every day at 3pm. That will work fine until daylight savings time ends and then that job will be off by an hour unless you happen to deploy again. That's no good.

Here's some Ruby (and Rails) to demonstrate:

>> Time.zone = "US/Eastern"
=> "US/Eastern"
>> Time.zone.parse('3pm').utc.hour
=> 19
>> (Time.zone.parse('3pm') + 3.months).utc.hour
=> 20

I might be wrong. Time zones hurt my brain.

@twalpole

This comment has been minimized.

Copy link
Author

commented Aug 9, 2012

Correct - it will be off for one hour until you redeploy your app and whenever writes out a new cron fixing it. However this isnt limited to whenever, its a problem with cron for any server running in UTC. If I set any cron to run at 1am UTC and local time changes to DST then it will run 1 hour off (in local time). What this patch allows is to choose the time zone the times are interpreted as so that if/when deploying to multiple servers set to be in different time zones, each servers crontab will be adjusted correctly at deploy time.

@twalpole

This comment has been minimized.

Copy link
Author

commented Aug 9, 2012

@javan As a further comment, if you want timezones that dont vary with DST you can use the 'Etc/GMT-5' style zones instead of 'US/Eastern'

@javan

This comment has been minimized.

Copy link
Owner

commented Aug 30, 2012

I'm still torn on this one. Sure, it's nice to offer time zone support, but it's also your responsibility to understand what time zone your servers are set to.

@twalpole

This comment has been minimized.

Copy link
Author

commented Aug 30, 2012

@javan its optional and provides a convenient way to synchronize jobs if one has multiple servers set to different timezones - how would you do that without some form of timezone support?

@tomharrisonjr

This comment has been minimized.

Copy link

commented Oct 9, 2012

+1 for adding timezone support.

I agree that you should understand what timezone your server is running in (and most should be running UTC). But we have cases where we need to coordinate jobs with partners, or send emails at the "right" time of day, so having whenever do the work we all hate (timezone conversion) is a good thing.

Having the :timezone set as an option is just that, an option.

While I understand the objection regarding daylight-savings time shifts (and the need to redeploy), I think the suggestion of using GMT-5 style zone names is a solid solution.

(And hey, maybe we could make a cron task that re-runs whenever when there's a time change :-) )

Tom

twalpole and others added some commits Jul 26, 2012

Merge branch 'timezone' of github.com:twalpole/whenever into timezone
* 'timezone' of github.com:twalpole/whenever:
  Allow specifying a timezone that :at parameters should be interpreted as being in
Merge pull request #294 from t3hk0d3/master
Fixed used server connection credentials (also fixes 1.9 tests)
@JamesCropcho

This comment has been minimized.

Copy link

commented Feb 15, 2013

+1

@JamesCropcho

This comment has been minimized.

Copy link

commented Feb 15, 2013

As a not-horrible interim fix, this seems to work well:

Time.zone = "US/Eastern"

every 1.day, :at => Time.zone.parse('4:30 am').utc do
  command 'echo hello'
end

Yielding:

30 9 * * * /bin/bash -l -c 'echo hello >> /home/james/cool_app/log/cron_log.log 2>&1'

## [message] Above is your schedule file converted to cron syntax; your crontab file was not updated.
## [message] Run `whenever --help' for more options.

If any of you might be able to see an issue I don't let me know, kindly.

@javan

This comment has been minimized.

Copy link
Owner

commented Feb 20, 2013

@JamesCropcho I dig that!

@JamesCropcho

This comment has been minimized.

Copy link

commented Feb 20, 2013

Cool, thanks @javan. Happy to help.

@twalpole

This comment has been minimized.

Copy link
Author

commented Feb 20, 2013

@JamesCropcho I believe (could be wrong, only took a quick glance) that only works if your servers are set to UTC, which I wish was always the case - but isn't

@javan

This comment has been minimized.

Copy link
Owner

commented Feb 20, 2013

You could do Time.zone.parse('4:30 am').localtime instead.

@twalpole

This comment has been minimized.

Copy link
Author

commented Feb 21, 2013

@javan yup and then if you DRY it all up so schedule.rb isnt littered with Time.zone.parse(xxxx).locatime for each cron job - you basically end up with this pull request

@codenamev

This comment has been minimized.

Copy link

commented Jul 31, 2013

+1, @javan what's the holdup on this? Sure, we should know the server's time zone, but we should also have the option to use a different one if we would like without having to muck up the schedule file with repeated timezone parsing.

@javan

This comment has been minimized.

Copy link
Owner

commented Jul 31, 2013

The truth is, I don't have much time to dedicate to this project and I'm not sure I want to take on the responsibility of troubleshooting people's timezone issues. There are always timezone issues.

@griffithac

This comment has been minimized.

Copy link

commented Oct 9, 2013

Here is how I dealt with the issue. I created a subclass of Time and then overroad its class method parse in order to adjust for the time zone offset of my app. This requires that your server is set to UTC and you need to know your offset. Mine is PST -0700. This does not account for daylight savings time. I Also defined a method called local() that makes the tasks themselves easier to read.

# schedule.rb

set :output, "/path/to/log/cron.log"

# Requires that your production server is set to UTC time
# Subclass Time and override self.parse to adjust for timezone offset
class TimeInZone < Time
  def initialize
    super
  end

  def self.parse args, utc_offset = 0
    hours = 3600 # seconds in hour
    super(args) - (hours * utc_offset)
  end
end

LOCAL_TIME_ZONE_OFFSET = -7  # Set default offset

# Add helper method to DRY up the cron tasks
def local time, utc_offset = LOCAL_TIME_ZONE_OFFSET
  TimeInZone.parse(time, utc_offset)
end

# Cron tasks here
every 1.day, at: local('5:00 am') do
  rake "tasks:generate"
end

@javan javan closed this Dec 8, 2013

@javan javan referenced this pull request Oct 5, 2014

Closed

Time zone #481

@Tomtomgo

This comment has been minimized.

Copy link

commented Oct 9, 2014

FYI @JamesCropcho's solution didn't work for me as Rake wasn't somehow correctly using ActiveSupport's Time, so I used this:

require 'tzinfo'
every :day, at: TZInfo::Timezone.get("Europe/Amsterdam").local_to_utc(Time.parse("12:30")) do 
  run_once "hounds:release"
end
@dannyyu92

This comment has been minimized.

Copy link

commented Jun 11, 2015

require 'active_support/all'

Allows @JamesCropcho 's fix to work for me.

@flatrocks

This comment has been minimized.

Copy link

commented Jul 6, 2015

I agree that it makes better sense for scheduled jobs use server time zone, not Rails time zone.

However it's unnecessarily confusing since there's no mention of this in the README, and the examples use Rails time helpers every 1.day, :at => '4:30 am' do to describe scheduling (sort of implying the times will be interpreted per usual Rails implementation.).

I suggest adding a one-liner to the README to say this is how it works. (And an added benefit is that it might prevent more issues being opened on this same topic.)

@jankeesvw

This comment has been minimized.

Copy link

commented Jul 11, 2015

This is my schedule.rb which works around the timezone issue, my server is in UTC, my users in CET (with daylight saving time):

require "tzinfo"

env :PATH, ENV["PATH"]

def local(time)
  TZInfo::Timezone.get("Europe/Amsterdam").local_to_utc(Time.parse(time))
end

every :sunday, at: local("7:00 am") do
  rake "make:stuff:happen"
end

every 1.day, at: "1:00 am" do
  command "whenever --update-crontab"
end
@nathantsoi

This comment has been minimized.

Copy link

commented Sep 10, 2015

nice @jankeesvw, so basically put this somewhere in schedule.rb

require "tzinfo"
def local(time); TZInfo::Timezone.get("US/Pacific").local_to_utc(Time.parse(time)); end

then use:

every 1.hour, at: local('5:30 am') do
  # awesome things here
end
@jankeesvw

This comment has been minimized.

Copy link

commented Sep 14, 2015

Yes that should work @nathantsoi, but I would rather keep it readable though .

@iangreenleaf

This comment has been minimized.

Copy link

commented May 3, 2016

No disrespect to the folks who have kindly shared their solutions, they're certainly better than nothing, but they are really not sufficient for robust time zone support. Take, for example:

require "tzinfo"
def local(time); TZInfo::Timezone.get("US/Pacific").local_to_utc(Time.parse(time)); end
every :sunday, at: local("10 pm") do
  # Start the week
end

This adjusts the time but not the day in cron:

0 5 * * 0 /bin/bash ...

Meaning it now runs at 5am Sunday UTC, which is 10pm Saturday Pacfic. Oops.

Sane time zone support really needs this gem's cooperation, or else a giant ugly monkey-patch to compensate for it.

@wbotelhos

This comment has been minimized.

Copy link

commented Nov 24, 2016

Using Rails, you can load the environment:

require File.expand_path('../config/environment', __dir__)

And than get the Rails zone:

every :day, at: Time.zone.parse('04:56am').localtime do
  runner 'My::Job.method'
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.