Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(124)

Side by Side Diff: base/third_party/nspr/prtime.cc

Issue 266193002: Extend PR_ParseTimeString() to accept some ISO 8601 formats to fix timezone parsing in SyslogParser. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address wtc's comments. Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/third_party/nspr/prtime.h ('k') | base/time/pr_time_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* Portions are Copyright (C) 2011 Google Inc */ 1 /* Portions are Copyright (C) 2011 Google Inc */
2 /* ***** BEGIN LICENSE BLOCK ***** 2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * 4 *
5 * The contents of this file are subject to the Mozilla Public License Version 5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with 6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at 7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/ 8 * http://www.mozilla.org/MPL/
9 * 9 *
10 * Software distributed under the License is distributed on an "AS IS" basis, 10 * Software distributed under the License is distributed on an "AS IS" basis,
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
54 * PR_GMTParameters 54 * PR_GMTParameters
55 * PR_ImplodeTime 55 * PR_ImplodeTime
56 * This was modified to use the Win32 SYSTEMTIME/FILETIME structures 56 * This was modified to use the Win32 SYSTEMTIME/FILETIME structures
57 * and the timezone offsets are applied to the FILETIME structure. 57 * and the timezone offsets are applied to the FILETIME structure.
58 * All types and macros are defined in the base/third_party/prtime.h file. 58 * All types and macros are defined in the base/third_party/prtime.h file.
59 * These have been copied from the following nspr files. We have only copied 59 * These have been copied from the following nspr files. We have only copied
60 * over the types we need. 60 * over the types we need.
61 * 1. prtime.h 61 * 1. prtime.h
62 * 2. prtypes.h 62 * 2. prtypes.h
63 * 3. prlong.h 63 * 3. prlong.h
64 *
65 * Unit tests are in base/time/pr_time_unittest.cc.
64 */ 66 */
65 67
66 #include "base/logging.h" 68 #include "base/logging.h"
67 #include "base/third_party/nspr/prtime.h" 69 #include "base/third_party/nspr/prtime.h"
68 #include "build/build_config.h" 70 #include "build/build_config.h"
69 71
70 #if defined(OS_WIN) 72 #if defined(OS_WIN)
71 #include <windows.h> 73 #include <windows.h>
72 #elif defined(OS_MACOSX) 74 #elif defined(OS_MACOSX)
73 #include <CoreFoundation/CoreFoundation.h> 75 #include <CoreFoundation/CoreFoundation.h>
(...skipping 424 matching lines...) Expand 10 before | Expand all | Expand 10 after
498 * Mon Jan 16 16:12 +0130 1989 500 * Mon Jan 16 16:12 +0130 1989
499 * 6 May 1992 16:41-JST (Wednesday) 501 * 6 May 1992 16:41-JST (Wednesday)
500 * 22-AUG-1993 10:59:12.82 502 * 22-AUG-1993 10:59:12.82
501 * 22-AUG-1993 10:59pm 503 * 22-AUG-1993 10:59pm
502 * 22-AUG-1993 12:59am 504 * 22-AUG-1993 12:59am
503 * 22-AUG-1993 12:59 PM 505 * 22-AUG-1993 12:59 PM
504 * Friday, August 04, 1995 3:54 PM 506 * Friday, August 04, 1995 3:54 PM
505 * 06/21/95 04:24:34 PM 507 * 06/21/95 04:24:34 PM
506 * 20/06/95 21:07 508 * 20/06/95 21:07
507 * 95-06-08 19:32:48 EDT 509 * 95-06-08 19:32:48 EDT
510 * 1995-06-17T23:11:25.342156Z
508 * 511 *
509 * If the input string doesn't contain a description of the timezone, 512 * If the input string doesn't contain a description of the timezone,
510 * we consult the `default_to_gmt' to decide whether the string should 513 * we consult the `default_to_gmt' to decide whether the string should
511 * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE). 514 * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE).
512 * The correct value for this argument depends on what standard specified 515 * The correct value for this argument depends on what standard specified
513 * the time string which you are parsing. 516 * the time string which you are parsing.
514 */ 517 */
515 518
516 PRStatus 519 PRStatus
517 PR_ParseTimeString( 520 PR_ParseTimeString(
518 const char *string, 521 const char *string,
519 PRBool default_to_gmt, 522 PRBool default_to_gmt,
520 PRTime *result_imploded) 523 PRTime *result_imploded)
521 { 524 {
522 PRExplodedTime tm; 525 PRExplodedTime tm;
523 PRExplodedTime *result = &tm; 526 PRExplodedTime *result = &tm;
524 TIME_TOKEN dotw = TT_UNKNOWN; 527 TIME_TOKEN dotw = TT_UNKNOWN;
525 TIME_TOKEN month = TT_UNKNOWN; 528 TIME_TOKEN month = TT_UNKNOWN;
526 TIME_TOKEN zone = TT_UNKNOWN; 529 TIME_TOKEN zone = TT_UNKNOWN;
527 int zone_offset = -1; 530 int zone_offset = -1;
528 int dst_offset = 0; 531 int dst_offset = 0;
529 int date = -1; 532 int date = -1;
530 PRInt32 year = -1; 533 PRInt32 year = -1;
531 int hour = -1; 534 int hour = -1;
532 int min = -1; 535 int min = -1;
533 int sec = -1; 536 int sec = -1;
537 int usec = -1;
534 538
535 const char *rest = string; 539 const char *rest = string;
536 540
537 int iterations = 0; 541 int iterations = 0;
538 542
539 PR_ASSERT(string && result); 543 PR_ASSERT(string && result);
540 if (!string || !result) return PR_FAILURE; 544 if (!string || !result) return PR_FAILURE;
541 545
542 while (*rest) 546 while (*rest)
543 { 547 {
(...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after
767 zone = TT_GMT; 771 zone = TT_GMT;
768 break; 772 break;
769 } 773 }
770 774
771 case '0': case '1': case '2': case '3': case '4': 775 case '0': case '1': case '2': case '3': case '4':
772 case '5': case '6': case '7': case '8': case '9': 776 case '5': case '6': case '7': case '8': case '9':
773 { 777 {
774 int tmp_hour = -1; 778 int tmp_hour = -1;
775 int tmp_min = -1; 779 int tmp_min = -1;
776 int tmp_sec = -1; 780 int tmp_sec = -1;
781 int tmp_usec = -1;
777 const char *end = rest + 1; 782 const char *end = rest + 1;
778 while (*end >= '0' && *end <= '9') 783 while (*end >= '0' && *end <= '9')
779 end++; 784 end++;
780 785
781 /* end is now the first character after a range of digit s. */ 786 /* end is now the first character after a range of digit s. */
782 787
783 if (*end == ':') 788 if (*end == ':')
784 { 789 {
785 if (hour >= 0 && min >= 0) /* already got it */ 790 if (hour >= 0 && min >= 0) /* already got it */
786 break; 791 break;
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
826 ; 831 ;
827 else if ((end - rest) > 2) 832 else if ((end - rest) > 2)
828 /* it is [0-9][0-9][0-9]+: */ 833 /* it is [0-9][0-9][0-9]+: */
829 break; 834 break;
830 else if ((end - rest) == 2) 835 else if ((end - rest) == 2)
831 tmp_sec = ((rest[0]-'0')*10 + 836 tmp_sec = ((rest[0]-'0')*10 +
832 (rest[1]-'0')); 837 (rest[1]-'0'));
833 else 838 else
834 tmp_sec = (rest[0]-'0'); 839 tmp_sec = (rest[0]-'0');
835 840
836 /* If we made it here, we've parsed hour and min , 841 /* fractional second */
837 and possibly sec, so it worked as a unit. */ 842 rest = end;
843 if (*rest == '.') {
844 rest++;
845 end++;
846 tmp_usec = 0;
847 /* use up to 6 digits, skip over the rest */
848 while (*end >= '0' && *end <= '9') {
849 if (end - rest < 6) {
850 tmp_usec = tmp_usec * 10 + *end - '0';
851 }
852 end++;
853 }
854 int ndigits = end - rest;
855 while (ndigits++ < 6)
856 tmp_usec *= 10;
857 }
838 858
839 /* skip over whitespace and see if there's an AM or PM 859 if (*end == 'Z') {
840 directly following the time. 860 zone = TT_GMT;
wtc 2014/05/12 19:47:15 Should we do end++; here? Otherwise 'Z' may
Thiemo Nagel 2014/05/13 14:08:19 It's not strictly necessary because the break will
841 */ 861 }
wtc 2014/05/12 19:47:15 Nit: Lines 843-861 probably should follow the brac
Thiemo Nagel 2014/05/13 14:08:19 Thanks, I fully agree. Done.
842 if (tmp_hour <= 12) 862 else if (tmp_hour <= 12)
843 { 863 {
864 /* If we made it here, we've parsed hour and min,
865 and possibly sec, so it worked as a unit. */
wtc 2014/05/12 19:47:15 By "it worked as a unit", did you mean we have ful
Thiemo Nagel 2014/05/13 14:08:19 That's how I understand the original author. (I'v
866
867 /* skip over whitespace and see if there's a n AM or PM
868 directly following the time.
869 */
844 const char *s = end; 870 const char *s = end;
845 while (*s && (*s == ' ' || *s == '\t')) 871 while (*s && (*s == ' ' || *s == '\t'))
846 s++; 872 s++;
847 if ((s[0] == 'p' || s[0] == 'P') && 873 if ((s[0] == 'p' || s[0] == 'P') &&
848 (s[1] == 'm' || s[1] == 'M')) 874 (s[1] == 'm' || s[1] == 'M'))
849 /* 10:05pm == 22:05, and 12:05pm == 12 :05 */ 875 /* 10:05pm == 22:05, and 12:05pm == 12 :05 */
850 tmp_hour = (tmp_hour == 12 ? 12 : tmp_ hour + 12); 876 tmp_hour = (tmp_hour == 12 ? 12 : tmp_ hour + 12);
851 else if (tmp_hour == 12 && 877 else if (tmp_hour == 12 &&
852 (s[0] == 'a' || s[0] == 'A') && 878 (s[0] == 'a' || s[0] == 'A') &&
853 (s[1] == 'm' || s[1] == 'M')) 879 (s[1] == 'm' || s[1] == 'M'))
854 /* 12:05am == 00:05 */ 880 /* 12:05am == 00:05 */
855 tmp_hour = 0; 881 tmp_hour = 0;
856 } 882 }
857 883
858 hour = tmp_hour; 884 hour = tmp_hour;
859 min = tmp_min; 885 min = tmp_min;
860 sec = tmp_sec; 886 sec = tmp_sec;
887 usec = tmp_usec;
861 rest = end; 888 rest = end;
862 break; 889 break;
863 } 890 }
864 else if ((*end == '/' || *end == '-') && 891 else if ((*end == '/' || *end == '-') &&
865 end[1] >= '0' && end[1] <= '9') 892 end[1] >= '0' && end[1] <= '9')
866 { 893 {
867 /* Perhaps this is 6/16/95, 16/6/95, 6-16-95, or 16-6-95 894 /* Perhaps this is 6/16/95, 16/6/95, 6-16-95, or 16-6-95
868 or even 95-06-05... 895 or even 95-06-05 or 1995-06-22.
869 #### But it doesn't handle 1995-06-22.
870 */ 896 */
871 int n1, n2, n3; 897 int n1, n2, n3;
872 const char *s; 898 const char *s;
873 899
874 if (month != TT_UNKNOWN) 900 if (month != TT_UNKNOWN)
875 /* if we saw a month name, this can't be. */ 901 /* if we saw a month name, this can't be. */
876 break; 902 break;
877 903
878 s = rest; 904 s = rest;
879 905
880 n1 = (*s++ - '0'); /* first 1 or 2 digits */ 906 n1 = (*s++ - '0'); /* first 1, 2 or 4 digits */
881 if (*s >= '0' && *s <= '9') 907 if (*s >= '0' && *s <= '9')
882 n1 = n1*10 + (*s++ - '0'); 908 n1 = n1*10 + (*s++ - '0');
883 909
910 if (*s >= '0' && *s <= '9') /* option al digits 3 and 4 */
911 {
912 n1 = n1*10 + (*s++ - '0');
913 if (*s < '0' || *s > '9')
914 break;
wtc 2014/05/12 19:47:15 This break statement means you want to disallow a
Thiemo Nagel 2014/05/13 14:08:19 Yes, that's the intent.
915 n1 = n1*10 + (*s++ - '0');
916 }
wtc 2014/05/12 19:47:15 Nit: it seems that this new if block (lines 910-91
Thiemo Nagel 2014/05/13 14:08:19 Yes, good point, that's more readable.
917
884 if (*s != '/' && *s != '-') /* sl ash */ 918 if (*s != '/' && *s != '-') /* sl ash */
885 break; 919 break;
886 s++; 920 s++;
887 921
888 if (*s < '0' || *s > '9') /* seco nd 1 or 2 digits */ 922 if (*s < '0' || *s > '9') /* seco nd 1 or 2 digits */
889 break; 923 break;
890 n2 = (*s++ - '0'); 924 n2 = (*s++ - '0');
891 if (*s >= '0' && *s <= '9') 925 if (*s >= '0' && *s <= '9')
892 n2 = n2*10 + (*s++ - '0'); 926 n2 = n2*10 + (*s++ - '0');
893 927
(...skipping 10 matching lines...) Expand all
904 if (*s >= '0' && *s <= '9') /* option al digits 3, 4, and 5 */ 938 if (*s >= '0' && *s <= '9') /* option al digits 3, 4, and 5 */
905 { 939 {
906 n3 = n3*10 + (*s++ - '0'); 940 n3 = n3*10 + (*s++ - '0');
907 if (*s < '0' || *s > '9') 941 if (*s < '0' || *s > '9')
908 break; 942 break;
909 n3 = n3*10 + (*s++ - '0'); 943 n3 = n3*10 + (*s++ - '0');
910 if (*s >= '0' && *s <= '9') 944 if (*s >= '0' && *s <= '9')
911 n3 = n3*10 + (*s++ - '0'); 945 n3 = n3*10 + (*s++ - '0');
912 } 946 }
913 947
914 if ((*s >= '0' && *s <= '9') || /* follow ed by non-alphanum */ 948 if (*s == 'T' && s[1] >= '0' && s[1] <= '9')
915 (*s >= 'A' && *s <= 'Z') || 949 /* followed by ISO 8601 T delimiter and number is ok */
916 (*s >= 'a' && *s <= 'z')) 950 ;
951 else if ((*s >= '0' && *s <= '9') ||
952 (*s >= 'A' && *s <= 'Z') ||
953 (*s >= 'a' && *s <= 'z'))
954 /* but other alphanumerics are not ok */
917 break; 955 break;
918 956
919 /* Ok, we parsed three 1-2 digit numbers, with / or - 957 /* Ok, we parsed three multi-digit numbers, with / or -
920 between them. Now decide what the hell they are 958 between them. Now decide what the hell they are
921 (DD/MM/YY or MM/DD/YY or YY/MM/DD.) 959 (DD/MM/YY or MM/DD/YY or [YY]YY/MM/DD.)
922 */ 960 */
923 961
924 if (n1 > 31 || n1 == 0) /* must be YY/MM/DD */ 962 if (n1 > 31 || n1 == 0) /* must be [YY]YY/MM/DD */
925 { 963 {
926 if (n2 > 12) break; 964 if (n2 > 12) break;
927 if (n3 > 31) break; 965 if (n3 > 31) break;
928 year = n1; 966 year = n1;
929 if (year < 70) 967 if (year < 70)
930 year += 2000; 968 year += 2000;
931 else if (year < 100) 969 else if (year < 100)
932 year += 1900; 970 year += 1900;
933 month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1); 971 month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
934 date = n3; 972 date = n3;
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
1019 1057
1020 /* Skip to the end of this token, whether we parsed it or not. 1058 /* Skip to the end of this token, whether we parsed it or not.
1021 Tokens are delimited by whitespace, or ,;-/ 1059 Tokens are delimited by whitespace, or ,;-/
1022 But explicitly not :+-. 1060 But explicitly not :+-.
1023 */ 1061 */
1024 while (*rest && 1062 while (*rest &&
1025 *rest != ' ' && *rest != '\t' && 1063 *rest != ' ' && *rest != '\t' &&
1026 *rest != ',' && *rest != ';' && 1064 *rest != ',' && *rest != ';' &&
1027 *rest != '-' && *rest != '+' && 1065 *rest != '-' && *rest != '+' &&
1028 *rest != '/' && 1066 *rest != '/' &&
1029 *rest != '(' && *rest != ')' && *rest != '[' && *rest ! = ']') 1067 *rest != '(' && *rest != ')' && *rest != '[' && *rest ! = ']' &&
1068 !(*rest == 'T' && rest[1] >= '0' && rest[1] <= '9') /* T precedes time in ISO 8601 */
wtc 2014/05/12 19:47:15 Nit: maybe this comment should be moved to the com
Thiemo Nagel 2014/05/13 14:08:19 Done. At that occasion, I've updated the pre-exis
1069 )
1030 rest++; 1070 rest++;
1031 /* skip over uninteresting chars. */ 1071 /* skip over uninteresting chars. */
1032 SKIP_MORE: 1072 SKIP_MORE:
1033 while (*rest && 1073 while (*rest == ' ' || *rest == '\t' ||
Thiemo Nagel 2014/05/09 16:19:00 *rest is redundant here.
1034 (*rest == ' ' || *rest == '\t' || 1074 *rest == ',' || *rest == ';' || *rest == '/' ||
1035 *rest == ',' || *rest == ';' || *rest == '/' || 1075 *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']')
1036 *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']'))
1037 rest++; 1076 rest++;
1038 1077
1039 /* "-" is ignored at the beginning of a token if we have not yet 1078 /* "-" is ignored at the beginning of a token if we have not yet
1040 parsed a year (e.g., the second "-" in "30-AUG-1966"), or if 1079 parsed a year (e.g., the second "-" in "30-AUG-1966"), or if
1041 the character after the dash is not a digit. */ 1080 the character after the dash is not a digit. */
1042 if (*rest == '-' && ((rest > string && 1081 if (*rest == '-' && ((rest > string &&
1043 isalpha((unsigned char)rest[-1]) && year < 0) || 1082 isalpha((unsigned char)rest[-1]) && year < 0) ||
1044 rest[1] < '0' || rest[1] > '9')) 1083 rest[1] < '0' || rest[1] > '9'))
1045 { 1084 {
1046 rest++; 1085 rest++;
1047 goto SKIP_MORE; 1086 goto SKIP_MORE;
1048 } 1087 }
1049 1088
1050 } 1089 /* Skip T that may precede ISO 8601 time. */
1090 if (*rest == 'T' && rest[1] >= '0' && rest[1] <= '9')
1091 rest++;
1092
wtc 2014/05/12 19:47:15 Nit: delete this blank line.
Thiemo Nagel 2014/05/13 14:08:19 Done.
1093 } /* while */
1051 1094
1052 if (zone != TT_UNKNOWN && zone_offset == -1) 1095 if (zone != TT_UNKNOWN && zone_offset == -1)
1053 { 1096 {
1054 switch (zone) 1097 switch (zone)
1055 { 1098 {
1056 case TT_PST: zone_offset = -8 * 60; break; 1099 case TT_PST: zone_offset = -8 * 60; break;
1057 case TT_PDT: zone_offset = -8 * 60; dst_offset = 1 * 60; break; 1100 case TT_PDT: zone_offset = -8 * 60; dst_offset = 1 * 60; break;
1058 case TT_MST: zone_offset = -7 * 60; break; 1101 case TT_MST: zone_offset = -7 * 60; break;
1059 case TT_MDT: zone_offset = -7 * 60; dst_offset = 1 * 60; break; 1102 case TT_MDT: zone_offset = -7 * 60; dst_offset = 1 * 60; break;
1060 case TT_CST: zone_offset = -6 * 60; break; 1103 case TT_CST: zone_offset = -6 * 60; break;
(...skipping 14 matching lines...) Expand all
1075 } 1118 }
1076 1119
1077 /* If we didn't find a year, month, or day-of-the-month, we can't 1120 /* If we didn't find a year, month, or day-of-the-month, we can't
1078 possibly parse this, and in fact, mktime() will do something random 1121 possibly parse this, and in fact, mktime() will do something random
1079 (I'm seeing it return "Tue Feb 5 06:28:16 2036", which is no doubt 1122 (I'm seeing it return "Tue Feb 5 06:28:16 2036", which is no doubt
1080 a numerologically significant date... */ 1123 a numerologically significant date... */
1081 if (month == TT_UNKNOWN || date == -1 || year == -1 || year > PR_INT16_MAX) 1124 if (month == TT_UNKNOWN || date == -1 || year == -1 || year > PR_INT16_MAX)
1082 return PR_FAILURE; 1125 return PR_FAILURE;
1083 1126
1084 memset(result, 0, sizeof(*result)); 1127 memset(result, 0, sizeof(*result));
1128 if (usec != -1)
1129 result->tm_usec = usec;
1085 if (sec != -1) 1130 if (sec != -1)
1086 result->tm_sec = sec; 1131 result->tm_sec = sec;
1087 if (min != -1) 1132 if (min != -1)
1088 result->tm_min = min; 1133 result->tm_min = min;
1089 if (hour != -1) 1134 if (hour != -1)
1090 result->tm_hour = hour; 1135 result->tm_hour = hour;
1091 if (date != -1) 1136 if (date != -1)
1092 result->tm_mday = date; 1137 result->tm_mday = date;
1093 if (month != TT_UNKNOWN) 1138 if (month != TT_UNKNOWN)
1094 result->tm_month = (((int)month) - ((int)TT_JAN)); 1139 result->tm_month = (((int)month) - ((int)TT_JAN));
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
1197 + 60 * localTime.tm_hour 1242 + 60 * localTime.tm_hour
1198 + 1440 * (localTime.tm_mday - 2); 1243 + 1440 * (localTime.tm_mday - 2);
1199 } 1244 }
1200 1245
1201 result->tm_params.tp_gmt_offset = zone_offset * 60; 1246 result->tm_params.tp_gmt_offset = zone_offset * 60;
1202 result->tm_params.tp_dst_offset = dst_offset * 60; 1247 result->tm_params.tp_dst_offset = dst_offset * 60;
1203 1248
1204 *result_imploded = PR_ImplodeTime(result); 1249 *result_imploded = PR_ImplodeTime(result);
1205 return PR_SUCCESS; 1250 return PR_SUCCESS;
1206 } 1251 }
OLDNEW
« no previous file with comments | « base/third_party/nspr/prtime.h ('k') | base/time/pr_time_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698