@@ -54,7 +54,10 @@ cdef dict _parse_code_table = {'y': 0,
54
54
' W' : 16 ,
55
55
' Z' : 17 ,
56
56
' p' : 18 , # an additional key, only with I
57
- ' z' : 19 }
57
+ ' z' : 19 ,
58
+ ' G' : 20 ,
59
+ ' V' : 21 ,
60
+ ' u' : 22 }
58
61
59
62
60
63
def array_strptime (object[:] values , object fmt ,
@@ -77,6 +80,7 @@ def array_strptime(object[:] values, object fmt,
77
80
object [:] result_timezone
78
81
int year, month, day, minute, hour, second, weekday, julian
79
82
int week_of_year, week_of_year_start, parse_code, ordinal
83
+ int iso_week, iso_year
80
84
int64_t us, ns
81
85
object val, group_key, ampm, found, timezone
82
86
dict found_key
@@ -169,13 +173,14 @@ def array_strptime(object[:] values, object fmt,
169
173
raise ValueError (" time data %r does not match format "
170
174
" %r (search)" % (values[i], fmt))
171
175
176
+ iso_year = - 1
172
177
year = 1900
173
178
month = day = 1
174
179
hour = minute = second = ns = us = 0
175
180
timezone = None
176
181
# Default to -1 to signify that values not known; not critical to have,
177
182
# though
178
- week_of_year = - 1
183
+ iso_week = week_of_year = - 1
179
184
week_of_year_start = - 1
180
185
# weekday and julian defaulted to -1 so as to signal need to calculate
181
186
# values
@@ -265,13 +270,44 @@ def array_strptime(object[:] values, object fmt,
265
270
timezone = pytz.timezone(found_dict[' Z' ])
266
271
elif parse_code == 19 :
267
272
timezone = parse_timezone_directive(found_dict[' z' ])
273
+ elif parse_code == 20 :
274
+ iso_year = int (found_dict[' G' ])
275
+ elif parse_code == 21 :
276
+ iso_week = int (found_dict[' V' ])
277
+ elif parse_code == 22 :
278
+ weekday = int (found_dict[' u' ])
279
+ weekday -= 1
280
+
281
+ # don't assume default values for ISO week/year
282
+ if iso_year != - 1 :
283
+ if iso_week == - 1 or weekday == - 1 :
284
+ raise ValueError (" ISO year directive '%G ' must be used with "
285
+ " the ISO week directive '%V ' and a weekday "
286
+ " directive '%A ', '%a ', '%w ', or '%u '." )
287
+ if julian != - 1 :
288
+ raise ValueError (" Day of the year directive '%j ' is not "
289
+ " compatible with ISO year directive '%G '. "
290
+ " Use '%Y ' instead." )
291
+ elif year != - 1 and week_of_year == - 1 and iso_week != - 1 :
292
+ if weekday == - 1 :
293
+ raise ValueError (" ISO week directive '%V ' must be used with "
294
+ " the ISO year directive '%G ' and a weekday "
295
+ " directive '%A ', '%a ', '%w ', or '%u '." )
296
+ else :
297
+ raise ValueError (" ISO week directive '%V ' is incompatible with"
298
+ " the year directive '%Y '. Use the ISO year "
299
+ " '%G ' instead." )
268
300
269
301
# If we know the wk of the year and what day of that wk, we can figure
270
302
# out the Julian day of the year.
271
- if julian == - 1 and week_of_year != - 1 and weekday != - 1 :
272
- week_starts_Mon = True if week_of_year_start == 0 else False
273
- julian = _calc_julian_from_U_or_W(year, week_of_year, weekday,
274
- week_starts_Mon)
303
+ if julian == - 1 and weekday != - 1 :
304
+ if week_of_year != - 1 :
305
+ week_starts_Mon = week_of_year_start == 0
306
+ julian = _calc_julian_from_U_or_W(year, week_of_year, weekday,
307
+ week_starts_Mon)
308
+ elif iso_year != - 1 and iso_week != - 1 :
309
+ year, julian = _calc_julian_from_V(iso_year, iso_week,
310
+ weekday + 1 )
275
311
# Cannot pre-calculate datetime_date() since can change in Julian
276
312
# calculation and thus could have different value for the day of the wk
277
313
# calculation.
@@ -511,14 +547,17 @@ class TimeRE(dict):
511
547
# The " \d" part of the regex is to make %c from ANSI C work
512
548
' d' : r " ( ?P<d> 3[0-1 ]| [1-2 ]\d | 0[1-9 ]| [1-9 ]| [1-9 ]) " ,
513
549
' f' : r " ( ?P<f> [0-9 ]{1,9} ) " ,
550
+ ' G' : r " ( ?P<G> \d\d\d\d ) " ,
514
551
' H' : r " ( ?P<H> 2[0-3 ]| [0-1 ]\d | \d ) " ,
515
552
' I' : r " ( ?P<I> 1[0-2 ]| 0[1-9 ]| [1-9 ]) " ,
516
553
' j' : (r " ( ?P<j> 36[0-6 ]| 3[0-5 ]\d | [1-2 ]\d\d | 0[1-9 ]\d | 00[1-9 ]| "
517
554
r"[1-9 ]\d | 0[1-9 ]| [1-9 ]) " ),
518
555
' m' : r " ( ?P<m> 1[0-2 ]| 0[1-9 ]| [1-9 ]) " ,
519
556
' M' : r " ( ?P<M> [0-5 ]\d | \d ) " ,
520
557
' S' : r " ( ?P<S> 6[0-1 ]| [0-5 ]\d | \d ) " ,
558
+ ' u' : r " ( ?P<u> [1-7 ]) " ,
521
559
' U' : r " ( ?P<U> 5[0-3 ]| [0-4 ]\d | \d ) " ,
560
+ ' V' : r " ( ?P<V> 5[0-3 ]| 0[1-9 ]| [1-4 ]\d | \d ) " ,
522
561
' w' : r " ( ?P<w> [0-6 ]) " ,
523
562
# W is set below by using 'U'
524
563
' y' : r " ( ?P<y> \d\d ) " ,
@@ -597,7 +636,23 @@ cdef _calc_julian_from_U_or_W(int year, int week_of_year,
597
636
int day_of_week, int week_starts_Mon):
598
637
""" Calculate the Julian day based on the year, week of the year, and day of
599
638
the week, with week_start_day representing whether the week of the year
600
- assumes the week starts on Sunday or Monday (6 or 0)."""
639
+ assumes the week starts on Sunday or Monday (6 or 0).
640
+
641
+ Parameters
642
+ ----------
643
+ year : int
644
+ the year
645
+ week_of_year : int
646
+ week taken from format U or W
647
+ week_starts_Mon : int
648
+ represents whether the week of the year
649
+ assumes the week starts on Sunday or Monday (6 or 0)
650
+
651
+ Returns
652
+ -------
653
+ int
654
+ converted julian day
655
+ """
601
656
602
657
cdef:
603
658
int first_weekday, week_0_length, days_to_week
@@ -620,6 +675,40 @@ cdef _calc_julian_from_U_or_W(int year, int week_of_year,
620
675
return 1 + days_to_week + day_of_week
621
676
622
677
678
+ cdef _calc_julian_from_V(int iso_year, int iso_week, int iso_weekday):
679
+ """ Calculate the Julian day based on the ISO 8601 year, week, and weekday.
680
+ ISO weeks start on Mondays, with week 01 being the week containing 4 Jan.
681
+ ISO week days range from 1 (Monday) to 7 (Sunday).
682
+
683
+ Parameters
684
+ ----------
685
+ iso_year : int
686
+ the year taken from format %G
687
+ iso_week : int
688
+ the week taken from format %V
689
+ iso_weekday : int
690
+ weekday taken from format %u
691
+
692
+ Returns
693
+ -------
694
+ (int, int)
695
+ the iso year and the Gregorian ordinal date / julian date
696
+ """
697
+
698
+ cdef:
699
+ int correction, ordinal
700
+
701
+ correction = datetime_date(iso_year, 1 , 4 ).isoweekday() + 3
702
+ ordinal = (iso_week * 7 ) + iso_weekday - correction
703
+ # ordinal may be negative or 0 now, which means the date is in the previous
704
+ # calendar year
705
+ if ordinal < 1 :
706
+ ordinal += datetime_date(iso_year, 1 , 1 ).toordinal()
707
+ iso_year -= 1
708
+ ordinal -= datetime_date(iso_year, 1 , 1 ).toordinal()
709
+ return iso_year, ordinal
710
+
711
+
623
712
cdef parse_timezone_directive(object z):
624
713
"""
625
714
Parse the '%z ' directive and return a pytz.FixedOffset
0 commit comments