Add typed vCard 4 reduced-precision date constructors
This commit is contained in:
@@ -405,6 +405,80 @@ func normalizeStructuredParamValues(values: seq[string]): seq[string] =
|
||||
else:
|
||||
values
|
||||
|
||||
func padInt(value, width: int): string =
|
||||
($value).align(width, '0')
|
||||
|
||||
func formatTypedDateAndOrTimeValue(
|
||||
year: Option[int] = none[int](),
|
||||
month: Option[int] = none[int](),
|
||||
day: Option[int] = none[int](),
|
||||
hour: Option[int] = none[int](),
|
||||
minute: Option[int] = none[int](),
|
||||
second: Option[int] = none[int](),
|
||||
timezone: Option[string] = none[string]()
|
||||
): string =
|
||||
|
||||
if year.isSome and year.get < 0:
|
||||
raise newException(ValueError, "year must be non-negative")
|
||||
if month.isSome and (month.get < 1 or month.get > 12):
|
||||
raise newException(ValueError, "month must be between 1 and 12")
|
||||
if day.isSome and (day.get < 1 or day.get > 31):
|
||||
raise newException(ValueError, "day must be between 1 and 31")
|
||||
if hour.isSome and (hour.get < 0 or hour.get > 23):
|
||||
raise newException(ValueError, "hour must be between 0 and 23")
|
||||
if minute.isSome and (minute.get < 0 or minute.get > 59):
|
||||
raise newException(ValueError, "minute must be between 0 and 59")
|
||||
if second.isSome and (second.get < 0 or second.get > 59):
|
||||
raise newException(ValueError, "second must be between 0 and 59")
|
||||
if timezone.isSome and
|
||||
not (timezone.get == "Z" or
|
||||
(timezone.get.len == 5 and
|
||||
{'+', '-'}.contains(timezone.get[0]) and
|
||||
timezone.get[1..^1].allCharsInSet(DIGIT))):
|
||||
raise newException(ValueError,
|
||||
"timezone must be 'Z' or an RFC 6350 numeric UTC offset")
|
||||
|
||||
let hasDate = year.isSome or month.isSome or day.isSome
|
||||
let hasTime = hour.isSome or minute.isSome or second.isSome or timezone.isSome
|
||||
if not hasDate and not hasTime:
|
||||
raise newException(ValueError,
|
||||
"at least one date-and-or-time component must be provided")
|
||||
if timezone.isSome and second.isNone:
|
||||
raise newException(ValueError,
|
||||
"timezone requires a second component for RFC 6350 date-and-or-time values")
|
||||
|
||||
if year.isSome:
|
||||
result &= padInt(year.get, 4)
|
||||
elif month.isSome or day.isSome:
|
||||
result &= "--"
|
||||
|
||||
if month.isSome:
|
||||
result &= padInt(month.get, 2)
|
||||
elif day.isSome:
|
||||
result &= "-"
|
||||
|
||||
if day.isSome:
|
||||
result &= padInt(day.get, 2)
|
||||
|
||||
if hasTime:
|
||||
result &= "T"
|
||||
|
||||
if hour.isSome:
|
||||
result &= padInt(hour.get, 2)
|
||||
elif minute.isSome or second.isSome or timezone.isSome:
|
||||
result &= "-"
|
||||
|
||||
if minute.isSome:
|
||||
result &= padInt(minute.get, 2)
|
||||
elif second.isSome or timezone.isSome:
|
||||
result &= "-"
|
||||
|
||||
if second.isSome:
|
||||
result &= padInt(second.get, 2)
|
||||
|
||||
if timezone.isSome:
|
||||
result &= timezone.get
|
||||
|
||||
proc parseDateAndOrTime[T](
|
||||
prop: var T,
|
||||
value: string
|
||||
@@ -777,6 +851,60 @@ genTextOrUriPropInitializers(fixedValueTypeProperties -->
|
||||
genUriPropInitializers(fixedValueTypeProperties -->
|
||||
filter(it[1] == vtUri).map(it[0]))
|
||||
|
||||
proc newVC4_Bday*(
|
||||
year: Option[int] = none[int](),
|
||||
month: Option[int] = none[int](),
|
||||
day: Option[int] = none[int](),
|
||||
hour: Option[int] = none[int](),
|
||||
minute: Option[int] = none[int](),
|
||||
second: Option[int] = none[int](),
|
||||
timezone: Option[string] = none[string](),
|
||||
calscale: Option[string] = none[string](),
|
||||
altId: Option[string] = none[string](),
|
||||
group: Option[string] = none[string](),
|
||||
params: seq[VC_Param] = @[]): VC4_Bday =
|
||||
|
||||
return newVC4_Bday(
|
||||
value = formatTypedDateAndOrTimeValue(
|
||||
year = year,
|
||||
month = month,
|
||||
day = day,
|
||||
hour = hour,
|
||||
minute = minute,
|
||||
second = second,
|
||||
timezone = timezone),
|
||||
calscale = calscale,
|
||||
altId = altId,
|
||||
group = group,
|
||||
params = params)
|
||||
|
||||
proc newVC4_Anniversary*(
|
||||
year: Option[int] = none[int](),
|
||||
month: Option[int] = none[int](),
|
||||
day: Option[int] = none[int](),
|
||||
hour: Option[int] = none[int](),
|
||||
minute: Option[int] = none[int](),
|
||||
second: Option[int] = none[int](),
|
||||
timezone: Option[string] = none[string](),
|
||||
calscale: Option[string] = none[string](),
|
||||
altId: Option[string] = none[string](),
|
||||
group: Option[string] = none[string](),
|
||||
params: seq[VC_Param] = @[]): VC4_Anniversary =
|
||||
|
||||
return newVC4_Anniversary(
|
||||
value = formatTypedDateAndOrTimeValue(
|
||||
year = year,
|
||||
month = month,
|
||||
day = day,
|
||||
hour = hour,
|
||||
minute = minute,
|
||||
second = second,
|
||||
timezone = timezone),
|
||||
calscale = calscale,
|
||||
altId = altId,
|
||||
group = group,
|
||||
params = params)
|
||||
|
||||
func newVC4_N*(
|
||||
family: seq[string] = @[],
|
||||
given: seq[string] = @[],
|
||||
|
||||
@@ -325,6 +325,21 @@ suite "vcard/vcard4":
|
||||
parsed.anniversary.isSome
|
||||
parsed.anniversary.get.calscale == some("gregorian")
|
||||
|
||||
test "spec: typed BDAY and ANNIVERSARY constructors support reduced-precision values":
|
||||
let bday = newVC4_Bday(month = some(12), day = some(24))
|
||||
let anniversary = newVC4_Anniversary(year = some(2014), month = some(6))
|
||||
check:
|
||||
bday.value == "--1224"
|
||||
bday.year.isNone
|
||||
bday.month == some(12)
|
||||
bday.day == some(24)
|
||||
serialize(bday) == "BDAY:--1224"
|
||||
anniversary.value == "201406"
|
||||
anniversary.year == some(2014)
|
||||
anniversary.month == some(6)
|
||||
anniversary.day.isNone
|
||||
serialize(anniversary) == "ANNIVERSARY:201406"
|
||||
|
||||
test "spec: unsupported standard parameters are rejected on known properties":
|
||||
expect(VCardParsingError):
|
||||
discard parseSingleVCard4(vcard4Doc(
|
||||
|
||||
Reference in New Issue
Block a user