/* createIsoString UDF by James Moberg / SunStar Media 2023-07-16 Gist: https://gist.github.com/JamoCA/3e825f773d3bbb45f5c36ee85793e10e Blog: https://dev.to/gamesover/createisostring-a-coldfusion-user-defined-function-udf-to-replace-datetimeformatiso-2p15 Tweet: https://twitter.com/gamesover/status/1680711586946891776 */ public string function createIsoString(string date="", string timezone="local", string truncatedTo="MILLIS", string format="utc", boolean throwOnError=true) output=false hint="Converts date object/string into a UTC, ISO8601, RFC 339, ATOM or W3C string to a timezone with offset and optional millisecond precision" { if (!len(arguments.date) || arguments.date eq "now"){ arguments.date = now(); }; if(!isvalid("date", arguments.date)){ if (arguments.throwOnError) { throw(message="createIsoString: Can't convert value [#encodeforhtml(arguments.date)#] to a datetime value"); } return ""; } if (arguments.throwOnError && len(arguments.truncatedTo) && !listfindnocase("DAYS,HALF_DAYS,HOURS,MINUTES,SECONDS,MILLIS", arguments.truncatedTo)){ throw(message="createIsoString: Can't truncate date using value [#encodeforhtml(arguments.truncatedTo)#]"); } // cache java classes within current request if (!structkeyexists(request, "createIsoStringJava")){ request.createIsoStringJava = [ "offsetDateTime": createobject("java", "java.time.OffsetDateTime") ,"dateTimeFormatter": createobject("java", "java.time.format.DateTimeFormatter") ,"chronoUnit": createobject("java", "java.time.temporal.ChronoUnit") ,"zoneId": createobject("java", "java.time.ZoneId") ]; request.createIsoStringJava["zoneIdList"] = "," & request.createIsoStringJava.zoneId.getAvailableZoneIds().toString().replaceAll("[\[\] ]","") & ","; request.createIsoStringJava["units"] = [ "DAYS": request.createIsoStringJava.ChronoUnit.DAYS ,"HALF_DAYS": request.createIsoStringJava.ChronoUnit.HALF_DAYS ,"HOURS": request.createIsoStringJava.ChronoUnit.HOURS ,"MILLIS": request.createIsoStringJava.ChronoUnit.MILLIS ,"MINUTES": request.createIsoStringJava.ChronoUnit.MINUTES ,"SECONDS": request.createIsoStringJava.ChronoUnit.SECONDS ]; } // validate timezone local.tzRegex = (refind("[^a-zA-Z0-9\/\_]", arguments.timezone)) ? rereplace(arguments.timezone, "(?=[\[\]\\^$.|?*+()])", "\", "all") : arguments.timezone; local.zoneIdPos = refindnocase(",#local.tzRegex#,", request.createIsoStringJava.zoneIdList); if (arguments.throwOnError && len(arguments.timezone) && !listfindnocase("local,server", arguments.timezone) && !local.zoneIdPos){ throw(message="createIsoString: timezone value [#encodeforhtml(arguments.truncatedTo)#] is not valid."); } local.isoDateRegex = "^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)((-(\d{2}):(\d{2})|Z)?)$"; if (refind(local.isoDateRegex, arguments.date)){ local.date = request.createIsoStringJava.OffsetDateTime.parse(arguments.date); } else { local.timeString = javacast("string", datetimeformat(arguments.date, "yyyy-mm-dd'T'HH:nn:ss.lllZ")); local.date = request.createIsoStringJava.OffsetDateTime.parse(local.timeString, request.createIsoStringJava.dateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX")); } // timezone is valid, use the correct capitalization if (local.zoneIdPos gt 0){ local.date = local.date.atZoneSameInstant(request.createIsoStringJava.zoneId.of( mid(request.createIsoStringJava.zoneIdList, local.zoneIdPos+1, len(arguments.timezone)) )); } // perform optional truncation if (structkeyexists(request.createIsoStringJava.units, arguments.truncatedTo)){ local.date = local.date.truncatedTo( request.createIsoStringJava.units[arguments.truncatedTo] ); } // format the date to ISO 8601 w/offset local.isoFormat = request.createIsoStringJava.dateTimeFormatter.ISO_OFFSET_DATE_TIME; local.formattedDate = local.isoFormat.format(local.date).toString(); if (find("8601", arguments.format) || findnocase("iso", arguments.format)){ return local.formattedDate.replaceAll("Z$", "\+0000"); // ISO-8601 = +0000 } else if (find("3339", arguments.format) || findnocase("atom", arguments.format) || findnocase("w3c", arguments.format)){ return local.formattedDate.replaceAll("Z$", "\+00\:00"); // RFC 3339, ATOM or W3C = +00:00 } return local.formattedDate; }