简体   繁体   中英

Formatting date/time in the same way as “ls -l” command

I am implementing a version of ls -la command in OS X. I happened to notice that ls command lists years for some files and times for others as shown below:

-rwxr-xr-x 1 Jenna 197609     3584 Mar 13  2015 untitled1.exe*
drwxr-xr-x 1 Jenna 197609        0 Mar  2 07:48 Videos/

After some research, I found out that files older than 6 months gets a year while those that are not get the actual time. How do I get the date and times of these files to correctly display either the year or time ? Currently I have the following code which always displays the time:

int main()
{
   struct stat *fileInfo;
   char dir[] = "~/Desktop/file.txt";
   stat(dir, &fileInfo); 
   printf("%.12s ", 4+ctime(&fileInfo->st_mtimespec));

   return 0;

}

strftime is your friend here. Formatting it like ls is not that straight forward. The two formats that ls in coreutils is using are

"%b %e  %Y"
"%b %e %H:%M"

Heres one paragraph from the docs of the ls in coreutils

/* TRANSLATORS: ls output needs to be aligned for ease of reading, so be wary of using variable width fields from the locale. Note %b is handled specially by ls and aligned correctly. Note also that specifying a width as in %5b is erroneous as strftime will count bytes rather than characters in multibyte locales. */ N_("%b %e %H:%M")

They're taking into consideration things like Locale ie multibyte characters etc. They also discuss things like when a file is older than N months they change how it's displayed etc. You can find a lot of information here...

http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/ls.c

For your needs something simpler might be fine, heres some code that might point you in the correct direction. You can change the format to whatever you want by changing the third argument to strftime .

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int main(void) {
  time_t t;
  srand((unsigned) time(&t));
  size_t i = 0;
  struct timespec ttime;
  while(i < 0xffffffff) {\
    ttime.tv_sec  = i;
    i += 3600;
    struct tm time_struct;
    localtime_r(&ttime.tv_sec, &time_struct);
    char time_str[1024];
    if(rand() > RAND_MAX/2) {
      strftime(time_str, sizeof(time_str), "a == %b %e  %Y", &time_struct);
    }
    else {
      strftime(time_str, sizeof(time_str), "b == %b %e %H:%M", &time_struct);
    }
    printf("%s\n", time_str);
  }
  exit(0);
}

Try it and see what it does. From the man page, tweaking it will get you really close to what you need.

size_t strftime(char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict timeptr);

The strftime() function formats the information from timeptr into the buffer s, according to the string pointed to by format.

If I recall correctly, the determining factor used by ls -l to output a date/time or output a date/year is whether the mtime year returned by stat matches the current year. If the file mtime year matches the current year, then date/time information is provided, if the mtime year is not the current year, then date/year information is provided.

In order to make the comparison, you need to fill a struct tm for both the file mtime and now . The following example uses localtime_r to fill each struct tm , makes the comparison, and then outputs the file details accordingly.

(the code will read/compare mtimes s and output the time format for all filenames provided as arguments. (a placeholder string is used to provide basic formatting for the file permissions, number, user, group, size, etc.)

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>

#define MAXC 64

void dump_tm (struct tm *t);

int main(int argc, char **argv)
{
    int i = 0;
    char time_str[MAXC] = "";
    time_t now = time (NULL);
    struct stat sb;
    struct tm tmfile, tmnow;

    if (argc < 2) {     /* validate sufficient arguments provided */
        fprintf (stderr, "error: insufficient input, usage: %s <pathname>\n",
                argv[0]);
        return 1;
    }

    for (i = 1; i < argc; i++) {    /* for each file given as arg */
        if (stat(argv[i], &sb) == -1) {  /* validate stat of file */
            perror("stat");
            return 1;
        }

        localtime_r (&sb.st_mtime, &tmfile);    /* get struct tm for file */
        localtime_r (&now, &tmnow);             /* and now */

        if (tmfile.tm_year == tmnow.tm_year) {    /* compare year values  */
            strftime (time_str, sizeof (time_str), "%b %e %H:%M",
                      &tmfile);   /* if year is current output date/time  */
            printf ("permission 1 user group 12345 %s %s\n",
                    time_str, argv[i]);
        }
        else { /* if year is not current, output time/year */
            strftime (time_str, sizeof (time_str), "%b %e  %Y",
                      &tmfile);
            printf ("permission 1 user group 12345 %s %s\n",
                    time_str, argv[i]);
        }
    }

    return 0;
}

Example ls -l Output

$ ls -l tmp.c ls_time_date_fmt.c
-rw-r--r-- 1 david david 1312 Apr  6 03:15 ls_time_date_fmt.c
-rw-r--r-- 1 david david 2615 May 23  2014 tmp.c

Program Use/Output

$ ./bin/ls_time_date_fmt ls_time_date_fmt.c tmp.c
permission 1 user group 12345 Apr  6 03:15 ls_time_date_fmt.c
permission 1 user group 12345 May 23  2014 tmp.c

Let me know if you have any questions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM