Crontab timing working not as expected - cron

I have a backup script syncing some files every 3rd day.
# m h dom mon dow
0 5 */3 * * backup /home/backup/scripts/system_backup.sh
Today I was checking the backup and there was none.
I expected, because today is the 27th, 27 / 3 is an even number, it would execute.
The timestamps of the other backups contain the days 19, 22, 25
Shouldn't it execute like on the days 18, 21, 24, 27 ?
A timestamp of the server right now is Fri Jun 27 08:52:00 UTC 2014.

From the man page for crontab(5):
Step values can be used in conjunction with ranges. Following a range with ``/'' specifies skips of the number's value through the range.
Basically, what this means is that values are used at 0-based indexes that are divisible by <number>.
For dom, * is the same as 1-31. So */3 is 1-31/3. Which means it'll start at 1 (index 0) then add 3 to get to the next one (4, at index 3) and so on.
If you want the cron to run on days that are divisible by three, you can use 3-31/3 instead.

Related

Numerical month to character month in excel

Is there a pre-installed function that directly converts 08 to Aug, 10 to Oct?
Currently I use text(date(0,have,1),"mmm").
You could simplify like this: =TEXT("8/0","mmm").
Update
I've come up with a new technique: =TEXT(number * 30,"mmm"):
How it works
Dates are stored as numbers in Excel. The number 1 is the date Jan 1, 1900; 2 is Jan 2, 1900; etc.
The 30th day in 1900 is in January; the 60th day is in February; the 90th day is in March.
Every multiple of 30 between 30 and 360 is in a different month. So we can simply multiply 30 by a number between 1 and 12, and the TEXT function will give us the month.
You can use
MonthName(yourmonthNumber)
or
MonthName(yourmonthNumber, True) 'to abbreviate the name
Or did you mean a worksheet function?

run every monday at 5am

I generated a cron to run every Monday at 5am.
0 5 1 * 1
The third number, 1, for day of month, has it set to run on the first of every month as well as Monday.
Do I change that 1 to 0 so it ignores the day of month? Otherwise it will run every Monday as well as run on the 1st of the month.
Have two entries:
to run every Monday at 5 AM : 0 5 * * 1
to run on 1st of every month at 5 AM : 0 5 1 * *
OR, if you want a single entry, the you may have to do something like: https://github.com/xr09/cron-last-sunday/blob/master/run-if-today
To avoid day confusion we can give like this as well -
to run every Monday at 5 AM : 0 5 * * MON

Sum dynamic range in pivot table but certain row(s)

I'm not quite at the level of using VBA or an issue like this, though I'm open to trying, but I am looking for a formula (or other method) that will allow me to sum the values in a dynamic range while excluding certain rows.
I have many pivot tables set up in the following fashion:
2013
Oct 10
Nov 20
Dec 10
2014 40
Jan 20
Feb 30
Mar 20
Grand Total 110
I have been trying to create a formula that sums the last 6 months, but excludes the subtotal (as seen next to 2014), so I only sum Mar-2014, Feb-2014, Jan-2014, Dec-2013, Nov-2013, Oct-2013. When we add to the source data, we will see Apr-2014, May-2014, etc, so I am trying to create a dynamic range that only counts the last six months. So far, I have used:
=OFFSET($Grand Total$110, -7, 0, 6, 0)
to try to capture the six rows leading up to the grand total row. I have tried to create an if function that says:
"if(OFFSET($Grand Total$110, -7, 0, 6, 0)=[cell with subtotal for 2014],sum(OFFSET($Grand Total$110, -7, 0, 6, 0))-[cell with subtotal for 2014],sum(OFFSET($Grand Total$110, -7, 0, 6, 0))"
but the offset function doesn't appear to act as a "range."
Is there a way to sum an offset function but exclude the subtotal row from the calculation? The problem with doing:
sum(OFFSET($Grand Total$110, -7, 0, 6, 0))-[row with subtotals]
is that once you get to Jun-2014, you will be subtracting the subtotal even though it isn't in the last 6-month range, e.g.,
2014 [Subtotal]
Jan (6 months ago)
Feb (5 months ago)
Mar (4 months ago)
Apr (3 months ago)
May (2 months ago)
June (last month)
I hope my issue makes sense and that someone might have a useful, formula-driven solution for this.

Rounding Up Minutes above 8, 23, 38, 53 to the nearest quarter hour

Here’s a challenge for someone. I’m trying to round session times up to the nearest quarter hour (I report my total client hours for license credentialing)
8 minutes or above: round up to 15
23 minutes or above: round up to 30
38 minutes or above: round up to 45
53 minutes or above: round up to 60
Ex: in the first quarter hour, minutes below 8 will be their exact value: 1=1, 2=2, 3=3.
When 8 is entered, it is automatically rounded up to 15 (the same holds true for the rest of the hour: ex: 16=16, 17=17, 18=18, 19=19, 20=20, 21=21, 22=22. But 23 through 29 are all rounded up to 30.
Ideally, I could enter both hours and minutes in a single column, ex: 1.54
However, I realize that it may be necessary to create a separate column for hours and minutes in order to make this work (i.e., so that my formula is only concerned with rounding up minutes. I can add my hours and minutes together after the minutes are rounded.) Thus:
Column A = Hours (3 hours maximum)
Column B = Minutes
Column C = Minutes Rounded up to nearest ¼ hour
Column D = Col A + Col C
In column B I would like to enter minutes as 1 through 60 (no decimal- i.e., in General, not Time format)
38 minutes in column B would automatically be rounded up to 45 minutes in column C
Does anyone have any ideas? How can I do this using the fewest number of columns?
[A Previously posted question - "Round up to nearest quarter" - introduces the concept of Math.Ceiling. Is this something I should use? I couldn't wrap my head around the answer).
With Grateful Thanks,
~ Jay
How's this go?
DECLARE #time DATETIME = '2014-03-19T09:59:00'
SELECT CASE
WHEN DATEPART(mi, #time) BETWEEN 8 AND 15 THEN DATEADD(mi, 15-DATEPART(mi, #time), #time)
WHEN DATEPART(mi, #time) BETWEEN 23 AND 30 THEN DATEADD(mi, 30-DATEPART(mi, #time), #time)
WHEN DATEPART(mi, #time) BETWEEN 38 AND 45 THEN DATEADD(mi, 45-DATEPART(mi, #time), #time)
WHEN DATEPART(mi, #time) BETWEEN 53 AND 59 THEN DATEADD(mi, 60-DATEPART(mi, #time), #time)
ELSE #time
END
Assume "sessions" is your table (CTE below contains 2 sample records), with session start time & end time stored (as noted in comments above, just store these data points, don't store the calculated values). You might be able to do the rounding as below. (not sure if this is what you want, since it either rounds up or down... do you not want to round down?)
;WITH sessions AS (
SELECT CAST('20140317 12:00' AS DATETIME) AS session_start, CAST('20140317 12:38' AS DATETIME) AS session_end
UNION ALL
SELECT CAST('20140317 12:00' AS DATETIME), CAST('20140317 12:37:59' AS DATETIME) AS session_end
)
SELECT *, DATEDIFF(MINUTE, session_start, session_end) AS session_time
, ROUND(DATEDIFF(MINUTE, session_start, session_end)/15.0, 0) * 15.0 AS bill_time
FROM sessions;
EDIT:
Hi Jay, I don't think you mentioned it is an Excel problem! I was assuming SQL. As Stuart suggested in a comment above, it would be helpful if you modified your question to indicate it is for Excel, so that others can possibly get help from this dialog in the future.
With Excel, you can do it with two columns that contain the session start date and time (column A) and session end date and time (column B), plus two formulas:
Column C (Actual Minutes) = ROUND((B1-A1) * 1440,0)
Column D (Billing Minutes) = (FLOOR(C1/15, 1) * 15) + IF(MOD(C1,15) >= 8, 15, MOD(C1,15))
This is what my table looks like:
3/18/2014 12:00 3/18/2014 12:38 38 45
3/18/2014 14:00 3/18/2014 14:37 37 37

How to instruct cron to execute a job every second week?

I would like to run a job through cron that will be executed every second Tuesday at given time of day. For every Tuesday is easy:
0 6 * * Tue
But how to make it on "every second Tuesday" (or if you prefer - every second week)?
I would not like to implement any logic in the script it self, but keep the definition only in cron.
Answer
Modify your Tuesday cron logic to execute every other week since the epoch.
Knowing that there are 604800 seconds in a week (ignoring DST changes and leap seconds, thank you), and using GNU date:
0 6 * * Tue expr `date +\%s` / 604800 \% 2 >/dev/null || /scripts/fortnightly.sh
Aside
Calendar arithmetic is frustrating.
#xahtep's answer is terrific but, as #Doppelganger noted in comments, it will fail on certain year boundaries. None of the date utility's "week of year" specifiers can help here. Some Tuesday in early January will inevitably repeat the week parity of the final Tuesday in the preceding year: 2016-01-05 (%V), 2018-01-02 (%U), and 2019-01-01 (%W).
How about this, it does keep it in the crontab even if it isn't exactly defined in the first five fields:
0 6 * * Tue expr `date +\%W` \% 2 > /dev/null || /scripts/fortnightly.sh
pilcrow's answer is great. However, it results in the fortnightly.sh script running every even week (since the epoch). If you need the script to run on odd weeks, you can tweak his answer a little:
0 6 * * Tue expr \( `date +\%s` / 604800 + 1 \) \% 2 > /dev/null || /scripts/fortnightly.sh
Changing the 1 to a 0 will move it back to even weeks.
Something like
0 0 1-7,15-21 * 2
Would hit the first and third Tuesday of the month.
Note: Don't use this with vixie cron (included in RedHat and SLES distros), as it makes an or between the day-of-month and day-of-week fields instead of an and.
Maybe a little dumb, but one could also create two cronjobs, one for every first tuesday and one for every third.
First cronjob:
0 0 8 ? 1/1 TUE#1 *
Second cronjob:
0 0 8 ? 1/1 TUE#3 *
Not sure about the syntax here, I used http://www.cronmaker.com/ to make these.
If you want to do it based on a given start date:
0 6 * * 1 expr \( `date +\%s` / 86400 - `date --date='2018-03-19' +\%s` / 86400 \) \% 14 == 0 > /dev/null && /scripts/fortnightly.sh
Should fire every other Monday beginning with 2018-03-19
Expression reads: Run at 6am on Mondays if ...
1 - Get today's date, in seconds, divided by the number of seconds in a day to convert to days sice epoch
2 - Do the same for the starting date, converting it to the number of days since epoch
3 - Get the difference between the two
4 - divide by 14 and check the remainder
5- If the remainder is zero you are on the two-week cycle
I discovered some additional limitations of above approaches that can fail in some edge cases. For instance, consider:
#xahtep and #Doppelganger discussed issues using %W on certain year boundaries above.
#pilcrow's answer gets around this to some degree, however it too will fail on certain boundaries. Answers in this and or other related topics use the number of seconds in a day (vs. week), which also fail on certain boundaries for the same reasons.
This is because these approaches rely on UTC time (date +%s). Consider a case where we're running a job at 1am and 10pm every 2nd Tuesday.
Suppose GMT-2:
1am local time = 11pm UTC yesterday
10pm local time = 8pm UTC today
If we are only checking a single time each day, this will not be an issue, but if we are checking multiple times -- or if we are close to UTC time and daylight savings occurs, the script wouldn't consider these to be the same day.
To get around this, we need to calculate an offset from UTC based on our local timezone not UTC. I couldn't find a simple way to do this in BASH, so I developed a solution that uses a quick one liner in Perl to compute the offset from UTC in seconds.
This script takes advantage of date +%z, which outputs the local timezone.
Bash script:
TZ_OFFSET=$( date +%z | perl -ne '$_ =~ /([+-])(\d{2})(\d{2})/; print eval($1."60**2") * ($2 + $3/60);' )
DAY_PARITY=$(( ( `date +%s` + ${TZ_OFFSET} ) / 86400 % 2 ))
then, to determine whether the day is even or odd:
if [ ${DAY_PARITY} -eq 1 ]; then
...
else
...
fi
There are many good answers here. Based upon the comments, I see quite a bit of confusion and frustration. My intention with this answer is to not only answer the OPs question How to instruct cron to execute a job every second week?, but also clear up some confusion for folks who may read this in the future.
TL;DR:
The crontab entry look like this:
\<minute\> \<hour\> * * \<Day of Week\> expr \\( $( date+\\%s ) \\/ 604800 \\% 2 \\) > /dev/null && \<command to run on odd weeks\> || \<command to run on even weeks\>
Crontab Entries:
All crontab entries [made with crontab -e] have this format, per the man page:
1: The Minute of the hour to execute [command]
Range: 0-59
2: The Hour of the Day to execute [command]
Range: 0-23
3: The Day of the Month to execute [command]
Range: 1-31
4: The Month to execute [command]
Range: 1-12
5: The Day of Week to execute [command]
Range: Check your man page, could be 0-6, 1-7 or 0-7
6: command to execute
Note, there are also # values, which I will not discuss here.
Some versions of *nix allow for expressions:
*/2 = every even number in range
1 + */2 = every odd number in range
1,15 = First & 15th
2-8 = Second through the Eighth (inclusive)
Some versions allow for the Human Readable words also, such as February or Wednesday.
For Month, */2 will execute on Feb, Apr, Jun, Aug, Oct, Dec.
For Days of Week, */2 will run every even day -- check your man page to see how the Days of Week are numbered. Tue/2 is NOT a valid expression.
Human Readable (Calendar) Woes
Some constants you need to know:
There are 365.2464 days in a year (For convenience, we'll round to 365).
There are 12 months in a year.
There are 7 days in a week.
There 86,400 seconds in a day.
Therefore,
1 month is 4-1/3 weeks
[i.e. 3 months is 13 weeks]
1 year is 52-1/7 weeks
The bane of calendar math:
Semi-Monthly = every half month = 2x/month = 24 times per year.
Bi-weekly = every other week (fortnightly) = 26 times per year.
Note: These terms are often mis-used.
Some years have 51 weeks, some have 53, most have 52. If My cron runs every odd week ( date +%W mod 2), and the year has 51 or 53 weeks, it will also run the following week, which is week 1 of the new year. Conversely, if my cron runs every even week, it will skip 2 weeks. Not what I want.
CRON can support semi-monthly, it cannot support bi-weekly!
Semi-monthly:
The first of the month will always fall between the 1st & 7th. The second half of the week will always occur between the 15th and the 21st.
Semi-monthly would have 2 values, one in the first half and the other in the second half of the month. Such as:
2,16
Unix Time
At a very high level, *nix time is 2 values:
date +%s = Number of Seconds since Epoch (01/01/1970 00:00:00)
date +%N = fractional seconds (in Nano seconds)
Therefore, time in *nix is date +%s.%N
*Nix uses epoch time. The /etc/shadow file contains the date of last password change. It is the integer portion of %s divided by 86,400.
Factino
The GPS satellites measure time as "Number of weeks since epoch time" and "(fractional) seconds into the week.
Note: Epoch weeks are independent of years. It does not matter if the year has 51, 52, or 53 weeks. Epoch weeks never roll over.
Bi-Weekly Time Algorithm
In *Nix date +%W is week number of the year, not epoch week. *nix does not have an epoch week value, but it can be computed.
Epoch Week = Integer( Epoch Seconds / Seconds_per_Day / Days_per_Week )
Fortnightly = Epoch_Week Modulo 2
The Fortnightly value will always be 0 or 1, thus, running the command when Fortnightly = 0 runs on every even week, and 1 = every odd week.
Computing Fortnightly)
The first way (bc):
date +"%s / 604800 % 2" | bc
This will generate "[Epoch Seconds] / 604800 % 2"
That answer is then sent to bc, a basic calculator, which does the math and echoes the answer to STDOUT and any errors to STDERR.
The return code is 0.
The second way (expr):
In this case, send the expression to expr and let it do the math
expr $( date +%s ) / 604800 % 2
The expr command does the math, echoes the answer to STDOUT, errors to STDERR.
The return code is 1 if the answer is 0, otherwise, the return code is 0.
In this manner, I don't care what the answer is, only the return code. Consider this:
$ expr 1 % 2 && echo Odd || echo Even
1
Odd
$ expr 2 % 2 && echo Odd || echo Even
0
Even
Since the result doesn't matter, I can re-direct STDOUT to /dev/null. In this case, I'll leave STDERR incase something unexpected happens, I'll see the error message.
Crontab Entry
In crontab, the percent sign and the parenthesis have special meanings, so they need to be escaped with a backslash (\).
First, I need to decide if I want to run on Even weeks or odd weeks. Or, perhaps, run Command_O on Odd weeks and command_E on even weeks. Then, I need to escape all the special characters.
Since the expr only evaluates WEEKS, it is necessary to specify a particular day of the week (and time) to evaluate, such as every Tuesday at 3:15pm. Thus, the first 5 files (the Time Entry) of my crontab entry will be:
15 3 * * TUE
The first part of my cron command will be the expr command, with STDOUT sent to null:
expr \\( $( date+\\%s ) \\/ 604800 \\% 2 \\) > /dev/null
The second part will be the evaluator, && or ||
The Third part will be the command (or script) I want to run. Which looks like this:
expr \\( $( date+\\%s ) \\/ 604800 \\% 2 \\) > /dev/null && \<command_O\> || \<command_E\>
Hopefully, that will clarify some of the confusion
Try this
0 0 1-7,15-21 * 2
This is run 2 times a month, both on Tuesdays and at least a week apart.
If you want every tuesday of second week only:
00 06 * * 2#2
Cron provides an 'every other' syntax "/2". Just follow the appropriate time unit field with "/2" and it will execute the cronjob 'every other time'. In your case...
0 6 * * Tue/2
The above should execute every other Tuesday.
Syntax "/2" is not goes along with weekday. So my added to the smart above is simply use 1,15 on MonthDay filed.
0 6 1,15 * * /scripts/fornightly.sh > /dev/null 2>&1
0 0 */14 * *
Runs At 00:00 on every 14th day-of-month
Why not something like
0 0 1-7,15-21,29-31 * 5
Is that appropriate?

Resources