Search code examples
cdate-conversion

How to convert a date format: 2019-08-22 16:16:08] to the format: aammjjhhmmss in C language


I need to convert this date : 2019-08-22 16:16:08 to this specific form: aammjjhhmmss (aa for 'année' or year; jj for 'jour' or day).

I'm working with C language. This is my code. I don't find it good because I need to call the function three times to get what I want:

#include <stdio.h>
#include <string.h>

void removeChar(char *s, int c){ 

    int j;
    int n = strlen(s); 
    for (int i=j=0; i<n; i++) 
       if (s[i] != c) 
          s[j++] = s[i]; 

    s[j] = '\0'; 
} 

int main() 
{ 
   char s[] = "2019-08-22 16:16:08"; 
   removeChar(s, '-'); 
   printf("test 1 without '-' %s \n",s); 
   removeChar(s, ':');
   printf("test 2 without ':' %s \n",s); 
   removeChar(s, ' ');
   printf("test 3 without ' ' %s \n",s); 
   return 0; 
} 

Results :

test 1 without '-' 20190822 16:16:08                                                                                                        
test 2 without ':' 20190822 161608                                                                                                          
test 3 without ' ' 20190822161608

And the format is: YYMMDDHHMMSS.


Solution

  • You can treat this as an exercise in date/time manipulation (hard), or as an exercise in string manipulation (easier).

    If you want to do date/time manipulation, then in a POSIX environment, strptime() and strftime() are your friends. (NB: strftime() is standard C; strptime() is part of POSIX.)

    Your code treats it as an exercise in string manipulation. Your result shows YYYYMMDD (4 digits for the year); your request shows AA or YY (2 digits) for the year. I wonder which is correct? If you have 4 input and 4 output digits life is simpler than if you have 4 input and 2 output. Assuming that it should be 4 digits in, 4 digits out, then all you need to do is copy the digits.

    There's a subtle bug in your code:

    int j;
    int n = strlen(s); 
    for (int i=j=0; i<n; i++) 
       if (s[i] != c) 
          s[j++] = s[i]; 
    
    s[j] = '\0'; 
    

    You have two different variables j, and the j used in the last assignment is uninitialized. If you use GCC, you could use -Wshadow as a compilation option to get warnings about such variable shadowing. You should use:

    int j = 0;
    int n = strlen(s); 
    for (int i = 0; i < n; i++) 
    {
       if (s[i] != c) 
          s[j++] = s[i];
    }
    s[j] = '\0'; 
    

    However, you legitimately worry about calling your function three times. You could write a variant that only needs to be called once, because it keeps only the digits in the input:

    #include <ctype.h>
    
    void keepDigits(char *s)
    {
        int j = 0;
        int n = strlen(s);
        for (int i = 0; i < n; i++)
        {
            if (isdigit((unsigned char)s[i]))
                s[j++] = s[i];
        }
        s[j] = '\0';
    }
    

    Indeed, you could also avoid pre-scanning the string with strlen() by using:

    void keepDigits(char *s)
    {
        int j = 0;
        for (int i = 0; s[i] != '\0'; i++)
        {
            if (isdigit((unsigned char)s[i]))
                s[j++] = s[i];
        }
        s[j] = '\0';
    }
    

    Now you scan the string once instead of 6 times (3 calls to your 'remove' function which scans the entire string, and each of which calls strlen() which also scans the entire string).

    Test code:

    int main(void) 
    { 
       char s[] = "2019-08-22 16:16:08"; 
       printf("Before: [%s]\n", s);
       keepDigits(s); 
       printf("After:  [%s]\n", s);
       return 0; 
    }
    

    Output:

    Before: [2019-08-22 16:16:08]
    After:  [20190822161608]
    

    If you only want 2 digits for the year, then you could use:

    int main(void) 
    { 
       char s[] = "2019-08-22 16:16:08"; 
       char *p = &s[2];
       printf("Before: [%s]\n", p);
       keepDigits(p); 
       printf("After:  [%s]\n", p);
       return 0; 
    }
    

    Output:

    Before: [19-08-22 16:16:08]
    After:  [190822161608]
    

    Note, however, that the leading two digits are still in s here.