@@ -134,6 +134,7 @@ PHPAPI time_t php_time(void)
134
134
* zone = (( "+" / "-" ) 4DIGIT)
135
135
*/
136
136
#define DATE_FORMAT_RFC2822 "D, d M Y H:i:s O"
137
+
137
138
/*
138
139
* RFC3339, Section 5.6: http://www.ietf.org/rfc/rfc3339.txt
139
140
* date-fullyear = 4DIGIT
@@ -156,8 +157,42 @@ PHPAPI time_t php_time(void)
156
157
*/
157
158
#define DATE_FORMAT_RFC3339 "Y-m-d\\TH:i:sP"
158
159
160
+ /*
161
+ * This format does not technically match the ISO 8601 standard, as it does not
162
+ * use : in the UTC offset format specifier. This is kept for BC reasons. The
163
+ * DATE_FORMAT_ISO8601_EXPANDED format does correct this, as well as adding
164
+ * support for years out side of the traditional 0000-9999 range.
165
+ */
159
166
#define DATE_FORMAT_ISO8601 "Y-m-d\\TH:i:sO"
160
167
168
+ /* ISO 8601:2004(E)
169
+ *
170
+ * Section 3.5 Expansion:
171
+ * By mutual agreement of the partners in information interchange, it is
172
+ * permitted to expand the component identifying the calendar year, which is
173
+ * otherwise limited to four digits. This enables reference to dates and times
174
+ * in calendar years outside the range supported by complete representations,
175
+ * i.e. before the start of the year [0000] or after the end of the year
176
+ * [9999]."
177
+ *
178
+ * Section 4.1.2.4 Expanded representations:
179
+ * If, by agreement, expanded representations are used, the formats shall be as
180
+ * specified below. The interchange parties shall agree the additional number of
181
+ * digits in the time element year. In the examples below it has been agreed to
182
+ * expand the time element year with two digits.
183
+ * Extended format: ±YYYYY-MM-DD
184
+ * Example: +001985-04-12
185
+ *
186
+ * PHP's year expansion digits are variable.
187
+ */
188
+ #define DATE_FORMAT_ISO8601_EXPANDED "X-m-d\\TH:i:sP"
189
+
190
+ /* Internal Only
191
+ * This format only extends the year when needed, keeping the 'P' format with
192
+ * colon for UTC offsets
193
+ */
194
+ #define DATE_FORMAT_ISO8601_LARGE_YEAR "x-m-d\\TH:i:sP"
195
+
161
196
/*
162
197
* RFC3339, Appendix A: http://www.ietf.org/rfc/rfc3339.txt
163
198
* ISO 8601 also requires (in section 5.3.1.3) that a decimal fraction
@@ -659,6 +694,8 @@ static zend_string *date_format(const char *format, size_t format_len, timelib_t
659
694
case 'L' : length = slprintf (buffer , sizeof (buffer ), "%d" , timelib_is_leap ((int ) t -> y )); break ;
660
695
case 'y' : length = slprintf (buffer , sizeof (buffer ), "%02d" , (int ) (t -> y % 100 )); break ;
661
696
case 'Y' : length = slprintf (buffer , sizeof (buffer ), "%s%04lld" , t -> y < 0 ? "-" : "" , php_date_llabs ((timelib_sll ) t -> y )); break ;
697
+ case 'x' : length = slprintf (buffer , sizeof (buffer ), "%s%04lld" , t -> y < 0 ? "-" : (t -> y >= 10000 ? "+" : "" ), php_date_llabs ((timelib_sll ) t -> y )); break ;
698
+ case 'X' : length = slprintf (buffer , sizeof (buffer ), "%s%04lld" , t -> y < 0 ? "-" : "+" , php_date_llabs ((timelib_sll ) t -> y )); break ;
662
699
663
700
/* time */
664
701
case 'a' : length = slprintf (buffer , sizeof (buffer ), "%s" , t -> h >= 12 ? "pm" : "am" ); break ;
@@ -1810,7 +1847,7 @@ static void date_object_to_hash(php_date_obj *dateobj, HashTable *props)
1810
1847
zval zv ;
1811
1848
1812
1849
/* first we add the date and time in ISO format */
1813
- ZVAL_STR (& zv , date_format ("Y -m-d H:i:s.u" , sizeof ("Y -m-d H:i:s.u" )- 1 , dateobj -> time , 1 ));
1850
+ ZVAL_STR (& zv , date_format ("x -m-d H:i:s.u" , sizeof ("x -m-d H:i:s.u" )- 1 , dateobj -> time , 1 ));
1814
1851
zend_hash_str_update (props , "date" , sizeof ("date" )- 1 , & zv );
1815
1852
1816
1853
/* then we add the timezone name (or similar) */
@@ -3800,7 +3837,7 @@ PHP_FUNCTION(timezone_transitions_get)
3800
3837
#define add_nominal () \
3801
3838
array_init(&element); \
3802
3839
add_assoc_long(&element, "ts", timestamp_begin); \
3803
- add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601 , 13, timestamp_begin, 0)); \
3840
+ add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601_LARGE_YEAR , 13, timestamp_begin, 0)); \
3804
3841
add_assoc_long(&element, "offset", tzobj->tzi.tz->type[0].offset); \
3805
3842
add_assoc_bool(&element, "isdst", tzobj->tzi.tz->type[0].isdst); \
3806
3843
add_assoc_string(&element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[0].abbr_idx]); \
@@ -3809,7 +3846,7 @@ PHP_FUNCTION(timezone_transitions_get)
3809
3846
#define add (i ,ts ) \
3810
3847
array_init(&element); \
3811
3848
add_assoc_long(&element, "ts", ts); \
3812
- add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601 , 13, ts, 0)); \
3849
+ add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601_LARGE_YEAR , 13, ts, 0)); \
3813
3850
add_assoc_long(&element, "offset", tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].offset); \
3814
3851
add_assoc_bool(&element, "isdst", tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].isdst); \
3815
3852
add_assoc_string(&element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].abbr_idx]); \
@@ -3818,7 +3855,7 @@ PHP_FUNCTION(timezone_transitions_get)
3818
3855
#define add_by_index (i ,ts ) \
3819
3856
array_init(&element); \
3820
3857
add_assoc_long(&element, "ts", ts); \
3821
- add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601 , 13, ts, 0)); \
3858
+ add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601_LARGE_YEAR , 13, ts, 0)); \
3822
3859
add_assoc_long(&element, "offset", tzobj->tzi.tz->type[i].offset); \
3823
3860
add_assoc_bool(&element, "isdst", tzobj->tzi.tz->type[i].isdst); \
3824
3861
add_assoc_string(&element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[i].abbr_idx]); \
@@ -3827,7 +3864,7 @@ PHP_FUNCTION(timezone_transitions_get)
3827
3864
#define add_from_tto (to ,ts ) \
3828
3865
array_init(&element); \
3829
3866
add_assoc_long(&element, "ts", ts); \
3830
- add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601 , 13, ts, 0)); \
3867
+ add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601_LARGE_YEAR , 13, ts, 0)); \
3831
3868
add_assoc_long(&element, "offset", (to)->offset); \
3832
3869
add_assoc_bool(&element, "isdst", (to)->is_dst); \
3833
3870
add_assoc_string(&element, "abbr", (to)->abbr); \
0 commit comments