changeset 18669:9007e9896303 v8.1.2326

patch 8.1.2326: cannot parse a date/time string Commit: https://github.com/vim/vim/commit/10455d43fef041309ce0613fa792c635dd71e3a8 Author: Bram Moolenaar <Bram@vim.org> 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 #)
author Bram Moolenaar <Bram@vim.org>
date Thu, 21 Nov 2019 15:45:03 +0100
parents ce55d198b1b5
children 4658aa8e09fc
files runtime/doc/eval.txt runtime/doc/usr_41.txt src/auto/configure src/config.h.in src/configure.ac src/evalfunc.c src/os_unix.h src/testdir/test_functions.vim src/version.c
diffstat 9 files changed, 122 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- 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}.
--- 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
--- 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"
--- 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
--- 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
 
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -20,7 +20,7 @@
 # include <float.h>
 #endif
 
-#ifdef MACOS_X
+#if defined(MACOS_X)
 # include <time.h>	// 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
  */
--- 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 <time.h>	    /* 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 <time.h>
 #endif
 #ifdef HAVE_SYS_TIME_H
 # include <sys/time.h>
--- 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()
--- 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,