Unix/Linux Go Back    


UNIX for Beginners Questions & Answers If you're not sure where to post a Unix or Linux question, post it here. All unix and Linux beginners welcome in this forum!

General Purpose Date Script

UNIX for Beginners Questions & Answers


Reply    
 
Thread Tools Search this Thread Display Modes
    #8  
Old Unix and Linux 01-16-2014   -   Original Discussion by Corona688
Corona688's Unix or Linux Image
Corona688 Corona688 is offline Forum Staff  
Mead Rotor
 
Join Date: Aug 2005
Last Activity: 13 December 2017, 10:16 AM EST
Location: Saskatchewan
Posts: 22,546
Thanks: 1,159
Thanked 4,286 Times in 3,955 Posts
Another update, with more bugfixes, some code simplifications, and a --help option. I'm keeping the most up-to-date code in the OP now.
Sponsored Links
    #9  
Old Unix and Linux 01-17-2014   -   Original Discussion by Corona688
drl's Unix or Linux Image
drl drl is offline Forum Advisor  
Registered Voter
 
Join Date: Apr 2007
Last Activity: 13 December 2017, 12:29 PM EST
Location: Saint Paul, MN USA / BSD, CentOS, Debian, OS X, Solaris
Posts: 2,212
Thanks: 250
Thanked 417 Times in 358 Posts
Hi.

Thanks for taking the time to do this.

I usually add warnings and strict to help avoid errors. Adding those yields:

Code:
Global symbol "$offset" requires explicit package name at ./date.pl line 99.  (about 10 times).

and

Code:
$ ./date.pl 
Use of uninitialized value $input in split at ./date.pl line 65.
Use of uninitialized value $offset in multiplication (*) at ./date.pl line 144.

The line numbers reflect the addition of lines for use strict; and use warnings;

Best wishes ... cheers, drl
The Following User Says Thank You to drl For This Useful Post:
Corona688 (01-17-2014)
Sponsored Links
    #10  
Old Unix and Linux 01-17-2014   -   Original Discussion by Corona688
Corona688's Unix or Linux Image
Corona688 Corona688 is offline Forum Staff  
Mead Rotor
 
Join Date: Aug 2005
Last Activity: 13 December 2017, 10:16 AM EST
Location: Saskatchewan
Posts: 22,546
Thanks: 1,159
Thanked 4,286 Times in 3,955 Posts
warnings and strict added, errors corrected. v0.0.6 now.

Last edited by Corona688; 01-17-2014 at 11:42 AM..
    #11  
Old Unix and Linux 05-04-2016   -   Original Discussion by Corona688
Corona688's Unix or Linux Image
Corona688 Corona688 is offline Forum Staff  
Mead Rotor
 
Join Date: Aug 2005
Last Activity: 13 December 2017, 10:16 AM EST
Location: Saskatchewan
Posts: 22,546
Thanks: 1,159
Thanked 4,286 Times in 3,955 Posts
Here is v0.0.7, with a little more refined input detection. It can handle more informal dates and time, like:


Code:
$ ./gdate.pl -d "Mar 10, 2016 1:51:09 PM CST"

2016-03-10 13:51:09

$ ./date.pl -d "mon 2"

2016-05-02 10:43:46

$

Unknown tokens in the -d string are no longer a fatal error, it skips over them and only prints warnings given -v.

It's possible -- even likely -- that loosening the syntax has introduced unintended bugs, so I'm not putting it in the OP just yet. Any comments or suggestions?


Code:
#!/usr/bin/perl

use POSIX;
use strict;
use warnings;

my $cmdstr="%Y-%m-%d %H:%M:%S";
my ($input, $arg, $sign, $file,$offset)=("", undef, 0, undef,0);
my $verbose=0;
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst);

my %days = ( "sun" => 0, "sunday" => 0,
        "mon" => 1, "monday" => 1,
        "tue" => 2, "tues" => 2, "tuesday" => 2,
        "wed" => 3, "wednesday" => 3,
        "thu" => 4, "thurs" => 4, "thursday" => 4,
        "fri" => 5, "friday" => 5,
        "sat" => 6, "saturday" => 6 );

my %month = (   "jan" => 0, "january" => 0,"feb" => 1, "february" => 1,
                "mar" => 2, "march" => 2,"apr" => 3, "april" => 3,
                "may" => 4,"jun" => 5, "june" => 5,
                "jul" => 6, "july" => 6,"aug" => 7, "august" => 7,
                "sep" => 8, "sept" => 8, "september" => 8,
                "oct" => 9, "october" => 9,"nov" => 10, "november" => 10,
                "dec" => 11, "december" => 11 );

sub set { # Sets the big mess of time variables from epoch input
        ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(shift);
}

set(time);

# Commandline parsing stuff
while(defined($arg=shift)) {
        if($arg =~ /^--date=/)  {       $input=substr($arg, 7);         }
        elsif($arg eq "-v")     {       $verbose=1;                     }
        elsif($arg eq "-d")   {       $input=shift;                   }
        elsif($arg eq "-r")   {       $file=shift;                    }
        elsif($arg =~ /^--reference=/) {$file=substr($arg, 12);         }
        elsif($arg =~/^\+/)     {       $cmdstr=substr($arg,1);         }
        elsif($arg =~/^-h|--help|--version$/) {
                print STDERR <<"EOT";
date.pl v0.0.7, Tyler Montbriand, 2016.  Free PERL date calc/converter.

Usage:
 -d "time string"       string like "YYYYMMDD", "YYYY/MM/DD",
                        "HHMMSS", "HH:MM:SS", "\@epoch", "- 3 days",
                        "Mar 3 2016 1:16:09 AM",
                        etc.  You can string them together, like
                        "\@1343322750 - 3 days".

 -r /path/to/file       Show mtime of the given file, not current time.

 +"formatstring"        Give strftime this format string instead of the
                        default "%Y-%m-%d %H:%M:%S".  See 'man strftime'

 -v                     Verbose, warn when input formatting is ignored.

Examples:
 date.pl                        # current time in YYYY-MM-DD HH:MM:SS
 TZ="UTC" ./date.pl             # Use an alternate time zone
 date.pl +"%a %b %d %Y %r"      # Like Thu Jan 16 2014 12:58:59 PM
 date.pl -d "+ 3 days"          # Current time plus three days
 date.pl -d "\@1343322750"      # exact time in epoch seconds
 date.pl -d "2013/01/02 12:00:00"# exact time in YYYYMMDD HHMMSS
 date.pl -r /etc/passwd         # display mtime of /etc/passwd
 date.pl -r /etc/passwd -d "12:00:00" # date of /etc/passwd, time of noon
EOT
                exit(1);
        }
        else {       die("unknown argument $arg, try --help");   }
}

if(defined($file)) { # stat file to get mtime
        my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
               $atime,$mtime,$ctime,$blksize,$blocks)
               = stat($file);

        defined($dev) || die("No such file $file");

        set($mtime);
}

# Put the date string back into argv, split on any whitespace or grammar
unshift(@ARGV, split(/[ \r\n\t,]+/, $input));

while(defined($arg=shift))
{
        # Need to split +1 into +1
        if($arg =~ /^[+]/) {    $sign=1;        $arg=substr($arg,1);    }
        elsif($arg =~ /^-/) {   $sign=-1;       $arg=substr($arg,1);    }

        ################## DATE FORMAT DETECTION ########################

        # Month and date
        if(exists($month{tolower($arg)}) && ($ARGV[0] =~ /^[0-9]+$/) )
        {
                $mon=$month{tolower($arg)};
                $mday=shift;
                $mday = $mday + 0;
                next;
        }

        # Things like "Mon 12" are day of month
        if(exists($days{tolower($arg)})) {
                if($ARGV[0] =~ /^[0-9]+$/)
                {
                        $mday=$ARGV[0] + 0;
                        shift;
                }
                next;
        }

        # A bare four digit number beginning with 19 or 20 is probably a year
        if($arg =~ /^(19[0-9][0-9])|(2[0-9][0-9][0-9])$/)
        {
                $year=$arg - 1900;
                next;
        }

        # @1234 means seconds in epoch time
        if($arg =~ /^@([0-9]+)$/)      {        set($1+0);  next;        }

        # Checks for YYYYMMDD or YYYY/MM/DD time
        # TODO:  Check for YYMMDD dates
        # TODO:  Check for YYYYDDMM dates (ugh)
        if($arg =~ /^([0-9]{4})([\/-]?)([0-9]{2})\2([0-9]{2})/)
        {
                ($year,$mon,$mday)=($1-1900,$3-1,$4+0);
                ($sec,$min,$hour)=(0,0,0);
                next;
        }

        # HH:MM:SS times
        if($arg =~ /([0-2]?[0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?$/)
        {
                ($sec,$min,$hour)=($3+0, $2+0, $1+0);

                # Handle time with PM in it
                if(tolower($ARGV[0]) eq "pm")
                {
                        shift;
                        if($hour >= 1) { $hour += 12; }
                }
                elsif(tolower($ARGV[0]) == "am")
                {
                        shift;
                        if($hour == 12) { $hour -= 12;  }
                }
                next;
        }

        # As last resort, assume its a pure number.
        if($arg =~ /^([0-9]+)$/) {
                ($sign != 0) ||
                die("offset without unit -- probably unrecognized format");

                $offset=$1+0;
                next;
        }

        if($arg =~ /^seconds?$/)   { } # Just take seconds at face value
        elsif($arg =~ /^minutes?$/)     {       $offset *= 60;          }
        elsif($arg =~ /^hours?/)        {       $offset *= 60*60;       }
        elsif($arg =~ /^days?/)         {       $offset *= 60*60*24;    }
        elsif($arg =~ /^weeks?/)        {       $offset *= 60*60*24*7;  }
        elsif($arg =~ /^months?$/)      {

                                        $mon += ($offset*$sign);

                                        while($mon > 12)
                                        {
                                                $mon-=12;
                                                $year++;
                                        }

                                        while($mon < 0)
                                        {
                                                $mon+=12;
                                                $year--;
                                        }

                                        $sign=0;
                                        $offset=0;
                                }
        elsif ($arg =~ /^years?$/) {
                                        $year += ($offset*$sign);
                                        $sign=0;
                                        $offset=0;
                                }
        elsif($arg =~ /^now$/)  {       set(time);      }
        elsif(length($arg) == 0){       } # Empty string?  Ignore
        elsif($verbose) {
                print STDERR "Unknown argument $arg\n";
        }
}

# Convert the altered year, month, etc back into epoch time.
my $ref=mktime($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst);

# Print the calculated time plus offset
print strftime($cmdstr."\n", localtime($ref + ($sign*$offset)));
exit(0);

The Following User Says Thank You to Corona688 For This Useful Post:
RavinderSingh13 (05-06-2016)
Sponsored Links
    #12  
Old Unix and Linux 05-04-2016   -   Original Discussion by Corona688
RudiC's Unix or Linux Image
RudiC RudiC is online now Forum Staff  
Moderator
 
Join Date: Jul 2012
Last Activity: 13 December 2017, 1:42 PM EST
Location: Aachen, Germany
Posts: 11,794
Thanks: 339
Thanked 3,659 Times in 3,357 Posts
Brilliant and versatile!

One thought - how about considering/including the locales? Like
Code:
locale mon
Januar;Februar;März;April;Mai;Juni;Juli;August;September;Oktober;November;Dezember
locale abmon
Jan;Feb;Mär;Apr;Mai;Jun;Jul;Aug;Sep;Okt;Nov;Dez

?
Sponsored Links
    #13  
Old Unix and Linux 05-05-2016   -   Original Discussion by Corona688
Corona688's Unix or Linux Image
Corona688 Corona688 is offline Forum Staff  
Mead Rotor
 
Join Date: Aug 2005
Last Activity: 13 December 2017, 10:16 AM EST
Location: Saskatchewan
Posts: 22,546
Thanks: 1,159
Thanked 4,286 Times in 3,955 Posts
I'm not certain whether I want to depend on outside locale or even the Perl locale module; this script sees the most use as a kludge on old systems, and the only time locale generally comes up on unix.com is when people are fighting its problems. Parsing lists is a lot shorter than the hash syntax either way, though, so I'm happy to leave that choice to the user.


Code:
# loadhash(\%hash, "A;B", "a;b")
# sets $hash{A}=0, $hash{B}=1, $hash{a}=0, $hash{b}=1
sub loadhash {
        my ($h,$n)=(shift,0);
        foreach(@_)     { $n=0; foreach(split('[;\n]', lc($_))) { ${$h}{$_}=$n++; } }
}

#loadhash(\%month, `locale mon`, `locale abmon`);
loadhash(\%month, "January;February;March;April;May;June;July;August;September;October;November;December",
        "Jan;Feb;Mar;Apr;May;Jun;Jul;Aug;Sep;Oct;Nov;Dec");
#loadhash(\%days, `locale day`, `locale abday`);
loadhash(\%days, "Sunday;Monday;Tuesday;Wednesday;Thursday;Friday;Saturday",
        "Sun;Mon;Tues;Wed;Thurs;Fri;Sat");

Beginning to understand why people play 'perl golf' now, if the goal is to write a subroutine shorter than the 10 lines of repetition its to replace...!
Sponsored Links
    #14  
Old Unix and Linux 05-05-2016   -   Original Discussion by Corona688
drl's Unix or Linux Image
drl drl is offline Forum Advisor  
Registered Voter
 
Join Date: Apr 2007
Last Activity: 13 December 2017, 12:29 PM EST
Location: Saint Paul, MN USA / BSD, CentOS, Debian, OS X, Solaris
Posts: 2,212
Thanks: 250
Thanked 417 Times in 358 Posts
Hi.
Quote:
Originally Posted by Corona688 View Post
I'm not certain whether I want to depend on outside locale or even the Perl locale module; this script sees the most use as a kludge on old systems ...
I think Corona688 and I have had a discussion about perl modules -- I tend to prefer to use them to make code shorter, and I think Corona688 likes his code to be as independent as possible.

I usually recommend this perl date code when the user (typically on older Solaris or AIX) cannot use GNU date or the dateuitls suite, so I definitely agree with Corona688's position in such situations.

Thanks again to Corona688 for taking the time to provide this.

Best wishes ... cheers, drl
Sponsored Links
Reply

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Linux More UNIX and Linux Forum Topics You Might Find Helpful
Thread Thread Starter Forum Replies Last Post
Purpose of - (hypen) in script or command line gwgreen1 UNIX for Dummies Questions & Answers 1 07-25-2011 03:24 AM
whats the purpose of the following script? vrn UNIX for Dummies Questions & Answers 1 03-20-2006 09:15 PM
Looking for a general purpose System Monitor darthur UNIX for Dummies Questions & Answers 5 04-09-2002 04:12 PM



All times are GMT -4. The time now is 02:48 PM.