| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- /*
- Tideland Common Go Library - Time and Crontab
- Copyright (C) 2009-2011 Frank Mueller / Oldenburg / Germany
- Redistribution and use in source and binary forms, with or
- modification, are permitted provided that the following conditions are
- met:
- Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- Neither the name of Tideland nor the names of its contributors may be
- used to endorse or promote products derived from this software without
- specific prior written permission.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- THE POSSIBILITY OF SUCH DAMAGE.
- */
- package cgl
- //--------------------
- // IMPORTS
- //--------------------
- import (
- "log"
- "time"
- )
- //--------------------
- // DATE AND TIME
- //--------------------
- // Calc nanoseconds from microseconds.
- func NsMicroseconds(count int64) int64 { return count * 1e3 }
- // Calc nanoseconds from milliseconds.
- func NsMilliseconds(count int64) int64 { return NsMicroseconds(count * 1e3) }
- // Calc nanoseconds from seconds.
- func NsSeconds(count int64) int64 { return NsMilliseconds(count * 1e3) }
- // Calc nanoseconds from minutes.
- func NsMinutes(count int64) int64 { return NsSeconds(count * 60) }
- // Calc nanoseconds from hours.
- func NsHours(count int64) int64 { return NsMinutes(count * 60) }
- // Calc nanoseconds from days.
- func NsDays(count int64) int64 { return NsHours(count * 24) }
- // Calc nanoseconds from weeks.
- func NsWeeks(count int64) int64 { return NsDays(count * 7) }
- // Test if the year of a time is in a given list.
- func YearInList(time *time.Time, years []int64) bool {
- for _, year := range years {
- if time.Year == year {
- return true
- }
- }
- return false
- }
- // Test if a year of a time is in a given range.
- func YearInRange(time *time.Time, minYear, maxYear int64) bool {
- return (minYear <= time.Year) && (time.Year <= maxYear)
- }
- // Test if the month of a time is in a given list.
- func MonthInList(time *time.Time, months []int) bool {
- return fieldInList(time.Month, months)
- }
- // Test if a month of a time is in a given range.
- func MonthInRange(time *time.Time, minMonth, maxMonth int) bool {
- return fieldInRange(time.Month, minMonth, maxMonth)
- }
- // Test if the day of a time is in a given list.
- func DayInList(time *time.Time, days []int) bool {
- return fieldInList(time.Day, days)
- }
- // Test if a day of a time is in a given range.
- func DayInRange(time *time.Time, minDay, maxDay int) bool {
- return fieldInRange(time.Day, minDay, maxDay)
- }
- // Test if the hour of a time is in a given list.
- func HourInList(time *time.Time, hours []int) bool {
- return fieldInList(time.Hour, hours)
- }
- // Test if a hour of a time is in a given range.
- func HourInRange(time *time.Time, minHour, maxHour int) bool {
- return fieldInRange(time.Hour, minHour, maxHour)
- }
- // Test if the minute of a time is in a given list.
- func MinuteInList(time *time.Time, minutes []int) bool {
- return fieldInList(time.Minute, minutes)
- }
- // Test if a minute of a time is in a given range.
- func MinuteInRange(time *time.Time, minMinute, maxMinute int) bool {
- return fieldInRange(time.Minute, minMinute, maxMinute)
- }
- // Test if the second of a time is in a given list.
- func SecondInList(time *time.Time, seconds []int) bool {
- return fieldInList(time.Second, seconds)
- }
- // Test if a second of a time is in a given range.
- func SecondInRange(time *time.Time, minSecond, maxSecond int) bool {
- return fieldInRange(time.Second, minSecond, maxSecond)
- }
- // Test if the weekday of a time is in a given list.
- func WeekdayInList(time *time.Time, weekdays []int) bool {
- return fieldInList(time.Weekday, weekdays)
- }
- // Test if a weekday of a time is in a given range.
- func WeekdayInRange(time *time.Time, minWeekday, maxWeekday int) bool {
- return fieldInRange(time.Weekday, minWeekday, maxWeekday)
- }
- //--------------------
- // JOB
- //--------------------
- // Check function type.
- type CheckFunc func(*time.Time) (bool, bool)
- // Tast function type.
- type TaskFunc func(string)
- // Job type.
- type Job struct {
- id string
- check CheckFunc
- task TaskFunc
- }
- // Create a new job.
- func NewJob(id string, check CheckFunc, task TaskFunc) *Job {
- return &Job{id, check, task}
- }
- // Check if the job has to be performed at a given time.
- func (job *Job) checkAndPerform(time *time.Time) bool {
- perform, delete := job.check(time)
- if perform {
- go job.task(job.id)
- }
- return perform && delete
- }
- //--------------------
- // CRONTAB
- //--------------------
- const (
- opJobAdd = iota
- opJobDel
- opCrontabStop
- )
- // Crontab control type.
- type crontabControl struct {
- opCode int
- args interface{}
- }
- // Crontab.
- type Crontab struct {
- jobs map[string]*Job
- control chan *crontabControl
- ticker *time.Ticker
- }
- // Start a crontab server.
- func NewCrontab() *Crontab {
- ctb := &Crontab{
- jobs: make(map[string]*Job),
- control: make(chan *crontabControl),
- ticker: time.NewTicker(1e9),
- }
- go ctb.backend()
- return ctb
- }
- // Stop the server.
- func (ctb *Crontab) Stop() {
- ctb.control <- &crontabControl{opCrontabStop, nil}
- }
- // Add a job to the server.
- func (ctb *Crontab) AddJob(job *Job) {
- ctb.control <- &crontabControl{opJobAdd, job}
- }
- // Delete a job from the server.
- func (ctb *Crontab) DeleteJob(id string) {
- ctb.control <- &crontabControl{opJobDel, id}
- }
- // Return the supervisor.
- func (src *Crontab) Supervisor() *Supervisor {
- return GlobalSupervisor()
- }
- // Recover after an error.
- func (ctb *Crontab) Recover(recoverable Recoverable, err interface{}) {
- log.Printf("Recovering crontab backend after error '%v'!", err)
- go ctb.backend()
- }
- // Crontab backend.
- func (ctb *Crontab) backend() {
- defer func() {
- HelpIfNeeded(ctb, recover())
- }()
- for {
- select {
- case sc := <-ctb.control:
- // Control the server.
- switch sc.opCode {
- case opJobAdd:
- job := sc.args.(*Job)
- ctb.jobs[job.id] = job
- case opJobDel:
- id := sc.args.(string)
- job, _ := ctb.jobs[id]
- ctb.jobs[id] = job, false
- case opCrontabStop:
- ctb.ticker.Stop()
- }
- case <-ctb.ticker.C:
- // One tick every second.
- ctb.tick()
- }
- }
- }
- // Handle one server tick.
- func (ctb *Crontab) tick() {
- now := time.UTC()
- deletes := make(map[string]*Job)
- // Check and perform jobs.
- for id, job := range ctb.jobs {
- delete := job.checkAndPerform(now)
- if delete {
- deletes[id] = job
- }
- }
- // Delete those marked for deletion.
- for id, job := range deletes {
- ctb.jobs[id] = job, false
- }
- }
- //--------------------
- // HELPERS
- //--------------------
- // Test if an int is in a list of ints.
- func fieldInList(field int, list []int) bool {
- for _, item := range list {
- if field == item {
- return true
- }
- }
- return false
- }
- // Test if an int is in a given range.
- func fieldInRange(field int, min, max int) bool {
- return (min <= field) && (field <= max)
- }
- /*
- EOF
- */
|