1818import array
1919import tempfile
2020import itertools
21+ from datetime import date
2122
2223#
2324# Constants for shape types
3637MULTIPOINTM = 28
3738MULTIPATCH = 31
3839
40+ MISSING = [None ,'' ]
41+
3942PYTHON3 = sys .version_info [0 ] == 3
4043
4144if PYTHON3 :
@@ -492,14 +495,15 @@ def __record(self):
492495 # deleted record
493496 return None
494497 record = []
495- for (name , typ , size , deci ), value in zip (self .fields ,
496- recordContents ):
498+ for (name , typ , size , deci ), value in zip (self .fields , recordContents ):
497499 if name == 'DeletionFlag' :
498500 continue
499501 elif not value .strip ():
502+ # why this? better to delete this and leave it up to the field type?
500503 record .append (value )
501504 continue
502- elif typ == "N" :
505+ elif typ in ("N" ,"F" ):
506+ # numeric or float: number stored as a string, right justified, and padded with blanks to the width of the field.
503507 value = value .replace (b ('\0 ' ), b ('' )).strip ()
504508 value = value .replace (b ('*' ), b ('' )) # QGIS NULL is all '*' chars
505509 if value == b ('' ):
@@ -517,18 +521,24 @@ def __record(self):
517521 #not parseable as int, set to None
518522 value = None
519523 elif typ == b ('D' ):
524+ # date: 8 bytes - date stored as a string in the format YYYYMMDD.
520525 if value .count (b ('0' )) == len (value ): # QGIS NULL is all '0' chars
521526 value = None
522527 else :
523528 try :
524529 y , m , d = int (value [:4 ]), int (value [4 :6 ]), int (value [6 :8 ])
525- value = [ y , m , d ]
530+ value = date ( y , m , d )
526531 except :
527532 value = value .strip ()
528533 elif typ == b ('L' ):
529- value = (value in b ('YyTt' ) and b ('T' )) or \
530- (value in b ('NnFf' ) and b ('F' )) or b ('?' )
534+ # logical: 1 byte - initialized to 0x20 (space) otherwise T or F.
535+ if value == " " :
536+ value = None # space means missing or not yet set
537+ else :
538+ value = (value in b ('YyTt' ) and b ('T' )) or \
539+ (value in b ('NnFf' ) and b ('F' )) or b ('?' )
531540 else :
541+ # anything else is forced to string/unicode
532542 value = u (value )
533543 value = value .strip ()
534544 record .append (value )
@@ -918,11 +928,30 @@ def __dbfRecords(self):
918928 for (fieldName , fieldType , size , dec ), value in zip (self .fields , record ):
919929 fieldType = fieldType .upper ()
920930 size = int (size )
921- if fieldType .upper () == "N" :
922- value = str (value ).rjust (size )
931+ if fieldType in ("N" ,"F" ):
932+ # numeric or float: number stored as a string, right justified, and padded with blanks to the width of the field.
933+ if value in MISSING :
934+ value = str ("*" * size ) # QGIS NULL
935+ else :
936+ value = str (value ).rjust (size )
937+ elif fieldType == "D" :
938+ # date: 8 bytes - date stored as a string in the format YYYYMMDD.
939+ if isinstance (value , date ):
940+ value = b"%s%s%s" % (value .year ,value .month ,value .day )
941+ elif isinstance (value , list ) and len (value ) == 3 :
942+ value = b"%s%s%s" % value
943+ elif value in MISSING :
944+ value = b ('0' ) * 8 # QGIS NULL for date type
945+ else :
946+ raise ShapefileException ("Date values must be either a datetime.date object, a list, or a missing value of None or ''." )
923947 elif fieldType == 'L' :
924- value = str (value )[0 ].upper ()
948+ # logical: 1 byte - initialized to 0x20 (space) otherwise T or F.
949+ if value in MISSING :
950+ value = str (' ' ) # missing is set to space
951+ else :
952+ value = str (value )[0 ].upper ()
925953 else :
954+ # anything else is forced to string
926955 value = str (value )[:size ].ljust (size )
927956 if len (value ) != size :
928957 raise ShapefileException (
0 commit comments