Thought this was interesting. So I wrote something. If you do not have GNU awk or gawk(supports mktime, strptime, strftime)
then you have to resort to C
or perl
.
This C
code (see example usage in the code comments) does what you asked. But there is some weirdness in the way lpstat
handles older time/date formats. Don Cragun here on the Forums may have more information on that.
Plus, you may not understand how UNIX time as epoch seconds is used to compare dates and times. Please read all the comments. TLDR;
is not going to help you at all.
If you have gawk
you can parody a lot of the code in the C
module.
There is also the problem date/time change after six months. (Don Cragun) So this code has limits.
// timetest.c - free to copy and modify per: GNU General Public License
// https://www.gnu.org/licenses/licenses.en.html
// to compile with gcc
// gcc timetest.c -o timetest
// to compile with cc (Note HP-UX default K & R compile will NOT work)
// cc timetest.c -o timetest
// copy timetest to where it can be seen and executed by the correct set of users Ex:
// cp timetest /path/to/files/
// chmod timetest and directories as needed to allow access + execute
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
// ignore this, it is just for a quick compile on windows, some linux, too
#ifdef __CYGWIN__
#define _XOPEN_SOURCE
extern char *strptime( char *, const char *, struct tm *);
#endif
// *************************************************************************************
// IMPORTANT NOTE:
// The current POSIX (opengroup.org) specification for lpstat does not mention the lpstat -o output
// format for dates of events, nor does it appear to require dates to be output at all.
//
// So, as a guess, your system does what the UNIX/HP-UX command ls does with dates of varying ages:
// "young" file ls -l output:
// -rwxr-xr-x 1 Owner None 494 Sep 12 09:18 arr.c
// "old" file ls -l output:
// -rwxr-xr-x 1 Owner None 2233 Jul 28 2018 unscr.c
//
// The DATES ARE DIFFERENT:
// hours:minutes are dropped, replaced by year.
// lpstat is free to do the same. This code will not deal with old entries.
// The changeover in date formats happens at 6 months age AFAIK. So if you have ancient lp requests,
// they break this code.
// *****************************************************************************************
// timetest.c jmc 2/22/2019 19:17:23 MST
// *****************************************************************
// Function: check age of date string, return 0 if okay, 1 otherwise
// assumes the year value, handles issues in brand new year
// returns 2 on fatal error.
//
// example usage ----------------------------------------
// /usr/bin/lpstat -o |
// while read data
// do
// /path/to/testime $data
// status=$?
// if [ $status -eq 2 ]; then
// exit 1 # fatal error, quit shell script
// fi
// if [ $status -eq 0 ]; then
// echo "$data"
// fi
// done
// ---------------------------------------------
//
// example input data="653e-8022 prod priority 1 Feb 22 08:42"
// the 5th 6th 7th values: Feb 22 08:42, first assume this year (year the code was run)
//
// items you can et by editing a few line -------------------------------------------------
// the variable:
// allowed_secs
// needs to be set
// There are 86400 seconds in one day
#define SECS_PER_DAY 86400
// so we need a number of days , change this value to what is needed, we start with 3 days:
#define DAYS_ALLOWED 3
// the variables min_words and max_words allow you to skip over lines
// with too few words or too many words
// (note it has to be one more):
// Take the value you want, add one to it.
// Ex: You want 13 so enter 14.
// This is here because lpstat -o is somewhat freeform about what it writes to stdout.
#define MAX_ALLOWED_WORDS 11
#define MIN_ALLOWED_WORDS 8
// *******************************************************************
int allowed_secs=SECS_PER_DAY * DAYS_ALLOWED; // number of epoch seconds old
int min_words=MIN_ALLOWED_WORDS;
int max_words=MAX_ALLOWED_WORDS;
void barf(const int, const char *);
int parse(char *, char *, char *, char *);
char *trim(char *);
// fatal error exit for bad data - i.e., not valid data times
void barf(const int line, const char *input)
{
fprintf(stderr, "Content: %s\n", (input==NULL) ? "NULL" : input);
fprintf(stderr, "On line %d:\n Fatal Date/time format error %s\n", line, strerror(errno));
exit(2);
}
// *****************************************************
// parse takes command line data,
// guesses a year of the event
// figures out current epoch seconds based on guessing a year
// compare event date in epoch seconds with limit
// return 0 if all is okay, return 1 not okay, exit when we have unexpected problems
// *****************************************************
int parse(char *mon, char *day, char *HM, char *year)
{
int tmp_yr=0;
struct tm tstruct;
struct tm *tm=&tstruct; // tm is now the name of the struct w want to use
char working[80]={0x0};
time_t now=time(NULL);
time_t limit=now - allowed_secs; // oldest allowed epoch time we set above
time_t then=0; // epoch seconds from derived date
int use_prev_yr=(year[0]!=0); // tells us if we are running a second time
tm=localtime(&now); //assume no error return here
if(use_prev_yr != 0) // first time in the function,
// use the current year since year is blank
// we get here because the original guessed year put us in the future
// example: run on Jan 1 2019 with December 30th 2018 actual event date
// so we get epoch seconds for December 2019 - in future. Wrong year assumed.
// guess again....
{
tm->tm_year--; // use previous year, we guessed wrong the first time
}
tmp_yr=tm->tm_year + 1900; // set variable to guess year
sprintf(year, "%d", tmp_yr); // move value of guess to string
// set string "working" to values from command line
sprintf(working, "%s %s %s %s", mon, day, HM, year);
memset(tm, 0x0, sizeof(struct tm)); // clear tm from previous changes
if (strptime(working, "%b %d %H:%M %Y", tm)==NULL) //next get tm for guessed year/date
{
errno=EINVAL;
barf(__LINE__, working); // strptime failed
}
then=mktime(tm); // get our hopefully "older" value in seconds
if (then > now) // correct problem with assuming we are in the
// same year as the entry
{
if( use_prev_yr) // should not happen, but need to block infinite recursion
{
errno=EINVAL;
barf(__LINE__, "Invalid previous year setting");
}
else
{
return parse( mon, day, HM, year); // recursion: rerun so one year will be substracted
}
}
// we got here and now know
// dates were valid, as far as we know anyway, so:
// then == epoch time of event
// and now == current epoch time
// and limit == oldest allowed epoch time for event
if( then < limit) // too old
{
return 1; // reject event
}
return 0; // event is okay
}
// chop off too long values
char *trim(char *p)
{
if(strlen(p)>9) // try to preserve some junk text without flooding the screen
p[9]=0x0;
return p;
}
// driver
int main(int argc, char **argv)
{
char year[8]={0x0}; // year set to "empty"
char errline[128]={0x0};
// garbage checks -------------------------------------------
// 1. too many or too few words in a line
if(argc < min_words || argc > max_words ) // consider this record an airball(errant text)
exit(1); // do not print this record
// 2. no valid data because a string is too long or someone goofed
// Month is 3, day is 2, time (HH:MM) is 5
if( strnlen(argv[5],6) > 3 ||
strnlen(argv[6],6) > 2 ||
strnlen(argv[7],6) > 5 )
{
// we likely have junk data, so barf
sprintf(errline, "Garbage data(truncated): %s %s %s",
trim(argv[5]),
trim(argv[6]),
trim(argv[7])
);
errno=EINVAL;
barf(__LINE__, errline);
}
// end garbage checks ------------------------------------------
// set return code to 1 or zero (possibly 2 on major error) based on hopefully good data
// return 1 == entry too old
// return 0 == entry okay
return parse( argv[5], argv[6], argv[7], year );
}
// EOF 2/22/2019 23:09:00 MST