# HG changeset patch # User Bram Moolenaar # Date 1574347503 -3600 # Node ID 9007e9896303312cc6dfb4c54c9a963f1afeff85 # Parent ce55d198b1b51c26ba1d48b1398f4ac5f9556a00 patch 8.1.2326: cannot parse a date/time string Commit: https://github.com/vim/vim/commit/10455d43fef041309ce0613fa792c635dd71e3a8 Author: Bram Moolenaar Date: Thu Nov 21 15:36:18 2019 +0100 patch 8.1.2326: cannot parse a date/time string Problem: Cannot parse a date/time string. Solution: Add strptime(). (Stephen Wall, closes #) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 8.1. Last change: 2019 Nov 17 +*eval.txt* For Vim version 8.1. Last change: 2019 Nov 21 VIM REFERENCE MANUAL by Bram Moolenaar @@ -488,7 +488,7 @@ as a key. To avoid having to put quotes around every key the #{} form can be used. This does require the key to consist only of ASCII letters, digits, '-' and '_'. Example: > - let mydict = #{zero: 0, one_key: 1, two-key: 2, 333: 3} + :let mydict = #{zero: 0, one_key: 1, two-key: 2, 333: 3} Note that 333 here is the string "333". Empty keys are not possible with #{}. A value can be any expression. Using a Dictionary for a value creates a @@ -2667,7 +2667,7 @@ remote_read({serverid} [, {timeout}]) remote_send({server}, {string} [, {idvar}]) String send key sequence remote_startserver({name}) none become server {name} -remove({list}, {idx} [, {end}]) any/List +remove({list}, {idx} [, {end}]) any/List remove items {idx}-{end} from {list} remove({blob}, {idx} [, {end}]) Number/Blob remove bytes {idx}-{end} from {blob} @@ -2770,7 +2770,7 @@ strchars({expr} [, {skipcc}]) Number cha strcharpart({str}, {start} [, {len}]) String {len} characters of {str} at {start} strdisplaywidth({expr} [, {col}]) Number display length of the String {expr} -strftime({format} [, {time}]) String time in specified format +strftime({format} [, {time}]) String format time with a specified format strgetchar({str}, {index}) Number get char {index} from {str} stridx({haystack}, {needle} [, {start}]) Number index of {needle} in {haystack} @@ -2778,6 +2778,8 @@ string({expr}) String String represent strlen({expr}) Number length of the String {expr} strpart({str}, {start} [, {len}]) String {len} characters of {str} at {start} +strptime({format}, {timestring}) + Number Convert {timestring} to unix timestamp strridx({haystack}, {needle} [, {start}]) Number last index of {needle} in {haystack} strtrans({expr}) String translate string to make it printable @@ -5634,7 +5636,7 @@ getwininfo([{winid}]) *getwininfo()* terminal 1 if a terminal window {only with the +terminal feature} tabnr tab page number - topline first displayed buffer line + topline first displayed buffer line variables a reference to the dictionary with window-local variables width window width @@ -5652,7 +5654,7 @@ getwininfo([{winid}]) *getwininfo()* getwinpos([{timeout}]) *getwinpos()* The result is a list with two numbers, the result of - getwinposx() and getwinposy() combined: + |getwinposx()| and |getwinposy()| combined: [x-pos, y-pos] {timeout} can be used to specify how long to wait in msec for a response from the terminal. When omitted 100 msec is used. @@ -6614,7 +6616,7 @@ listener_remove({id}) *listener_remo localtime() *localtime()* Return the current time, measured as seconds since 1st Jan - 1970. See also |strftime()| and |getftime()|. + 1970. See also |strftime()|, |strptime()| and |getftime()|. log({expr}) *log()* @@ -7103,9 +7105,9 @@ mkdir({name} [, {path} [, {prot}]]) There is no error if the directory already exists and the "p" flag is passed (since patch 8.0.1708). However, without the - "p" option the call will fail. - - The function result is a Number, which is 1 if the call was + "p" option the call will fail. + + The function result is a Number, which is 1 if the call was successful or 0 if the directory creation failed or partly failed. @@ -9267,7 +9269,7 @@ strftime({format} [, {time}]) *strfti {format} depends on your system, thus this is not portable! See the manual page of the C function strftime() for the format. The maximum length of the result is 80 characters. - See also |localtime()| and |getftime()|. + See also |localtime()|, |getftime()| and |strptime()|. The language can be changed with the |:language| command. Examples: > :echo strftime("%c") Sun Apr 27 11:49:23 1997 @@ -9368,6 +9370,34 @@ strpart({src}, {start} [, {len}]) *str Can also be used as a |method|: > GetText()->strpart(5) +strptime({format}, {timestring}) *strptime()* + The result is a Number, which is a unix timestamp representing + the date and time in {timestring}, which is expected to match + the format specified in {format}. + + The accepted {format} depends on your system, thus this is not + portable! See the manual page of the C function strptime() + for the format. Especially avoid "%c". The value of $TZ also + matters. + + If the {timestring} cannot be parsed with {format} zero is + returned. If you do not know the format of {timestring} you + can try different {format} values until you get a non-zero + result. + + See also |strftime()|. + Examples: > + :echo strptime("%Y %b %d %X", "1997 Apr 27 11:49:23") +< 862156163 > + :echo strftime("%c", strptime("%y%m%d %T", "970427 11:53:55")) +< Sun Apr 27 11:53:55 1997 > + :echo strftime("%c", strptime("%Y%m%d%H%M%S", "19970427115355") + 3600) +< Sun Apr 27 12:53:55 1997 + + Not available on all systems. To check use: > + :if exists("*strptime") + + strridx({haystack}, {needle} [, {start}]) *strridx()* The result is a Number, which gives the byte index in {haystack} of the last occurrence of the String {needle}. diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -796,6 +796,7 @@ Date and Time: *date-functions* *time getftime() get last modification time of a file localtime() get current time in seconds strftime() convert time to a string + strptime() convert a date/time string to time reltime() get the current or elapsed time accurately reltimestr() convert reltime() result to a string reltimefloat() convert reltime() result to a Float diff --git a/src/auto/configure b/src/auto/configure --- a/src/auto/configure +++ b/src/auto/configure @@ -12572,8 +12572,8 @@ for ac_func in fchdir fchown fchmod fsyn memset mkdtemp nanosleep opendir putenv qsort readlink select setenv \ getpgid setpgid setsid sigaltstack sigstack sigset sigsetjmp sigaction \ sigprocmask sigvec strcasecmp strerror strftime stricmp strncasecmp \ - strnicmp strpbrk strtol tgetent towlower towupper iswupper tzset \ - usleep utime utimes mblen ftruncate unsetenv posix_openpt + strnicmp strpbrk strptime strtol tgetent towlower towupper iswupper \ + tzset usleep utime utimes mblen ftruncate unsetenv posix_openpt do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" diff --git a/src/config.h.in b/src/config.h.in --- a/src/config.h.in +++ b/src/config.h.in @@ -206,6 +206,7 @@ #undef HAVE_STRNCASECMP #undef HAVE_STRNICMP #undef HAVE_STRPBRK +#undef HAVE_STRPTIME #undef HAVE_STRTOL #undef HAVE_CANBERRA #undef HAVE_ST_BLKSIZE diff --git a/src/configure.ac b/src/configure.ac --- a/src/configure.ac +++ b/src/configure.ac @@ -3744,8 +3744,8 @@ AC_CHECK_FUNCS(fchdir fchown fchmod fsyn memset mkdtemp nanosleep opendir putenv qsort readlink select setenv \ getpgid setpgid setsid sigaltstack sigstack sigset sigsetjmp sigaction \ sigprocmask sigvec strcasecmp strerror strftime stricmp strncasecmp \ - strnicmp strpbrk strtol tgetent towlower towupper iswupper tzset \ - usleep utime utimes mblen ftruncate unsetenv posix_openpt) + strnicmp strpbrk strptime strtol tgetent towlower towupper iswupper \ + tzset usleep utime utimes mblen ftruncate unsetenv posix_openpt) AC_FUNC_SELECT_ARGTYPES AC_FUNC_FSEEKO diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -20,7 +20,7 @@ # include #endif -#ifdef MACOS_X +#if defined(MACOS_X) # include // for time_t #endif @@ -237,6 +237,9 @@ static void f_stridx(typval_T *argvars, static void f_strlen(typval_T *argvars, typval_T *rettv); static void f_strcharpart(typval_T *argvars, typval_T *rettv); static void f_strpart(typval_T *argvars, typval_T *rettv); +#ifdef HAVE_STRPTIME +static void f_strptime(typval_T *argvars, typval_T *rettv); +#endif static void f_strridx(typval_T *argvars, typval_T *rettv); static void f_strtrans(typval_T *argvars, typval_T *rettv); static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv); @@ -738,6 +741,9 @@ static funcentry_T global_functions[] = {"string", 1, 1, FEARG_1, f_string}, {"strlen", 1, 1, FEARG_1, f_strlen}, {"strpart", 2, 3, FEARG_1, f_strpart}, +#ifdef HAVE_STRPTIME + {"strptime", 2, 2, FEARG_1, f_strptime}, +#endif {"strridx", 2, 3, FEARG_1, f_strridx}, {"strtrans", 1, 1, FEARG_1, f_strtrans}, {"strwidth", 1, 1, FEARG_1, f_strwidth}, @@ -7412,6 +7418,40 @@ f_strpart(typval_T *argvars, typval_T *r rettv->vval.v_string = vim_strnsave(p + n, len); } +#ifdef HAVE_STRPTIME +/* + * "strptime({format}, {timestring})" function + */ + static void +f_strptime(typval_T *argvars, typval_T *rettv) +{ + struct tm tmval; + char_u *fmt; + char_u *str; + vimconv_T conv; + char_u *enc; + + vim_memset(&tmval, NUL, sizeof(tmval)); + fmt = tv_get_string(&argvars[0]); + str = tv_get_string(&argvars[1]); + + conv.vc_type = CONV_NONE; + enc = enc_locale(); + convert_setup(&conv, p_enc, enc); + if (conv.vc_type != CONV_NONE) + fmt = string_convert(&conv, fmt, NULL); + if (fmt == NULL + || strptime((char *)str, (char *)fmt, &tmval) == NULL + || (rettv->vval.v_number = mktime(&tmval)) == -1) + rettv->vval.v_number = 0; + + if (conv.vc_type != CONV_NONE) + vim_free(fmt); + convert_setup(&conv, NULL, NULL); + vim_free(enc); +} +#endif + /* * "strridx()" function */ diff --git a/src/os_unix.h b/src/os_unix.h --- a/src/os_unix.h +++ b/src/os_unix.h @@ -127,9 +127,16 @@ # endif #endif +// on some systems time.h should not be included together with sys/time.h #if !defined(HAVE_SYS_TIME_H) || defined(TIME_WITH_SYS_TIME) -# include /* on some systems time.h should not be - included together with sys/time.h */ +// Needed for strptime() +# ifndef _XOPEN_SOURCE +# define _XOPEN_SOURCE +# endif +# ifndef __USE_XOPEN +# define __USE_XOPEN +# endif +# include #endif #ifdef HAVE_SYS_TIME_H # include diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -181,9 +181,8 @@ func Test_str2nr() endfunc func Test_strftime() - if !exists('*strftime') - return - endif + CheckFunction strftime + " Format of strftime() depends on system. We assume " that basic formats tested here are available and " identical on all systems which support strftime(). @@ -222,7 +221,28 @@ func Test_strftime() else unlet $TZ endif +endfunc +func Test_strptime() + CheckFunction strptime + + if exists('$TZ') + let tz = $TZ + endif + let $TZ = 'UTC' + + call assert_equal(1484653763, strptime('%Y-%m-%d %X', '2017-01-17 11:49:23')) + + call assert_fails('call strptime()', 'E119:') + call assert_fails('call strptime("xxx")', 'E119:') + call assert_equal(0, strptime("%Y", '')) + call assert_equal(0, strptime("%Y", "xxx")) + + if exists('tz') + let $TZ = tz + else + unlet $TZ + endif endfunc func Test_resolve_unix() diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2326, +/**/ 2325, /**/ 2324,