[{"data":1,"prerenderedAt":503},["ShallowReactive",2],{"post:lucee-datetimeoffset":3},{"id":4,"title":5,"body":6,"date":488,"description":489,"draft":490,"extension":491,"meta":492,"navigation":493,"path":494,"seo":495,"stem":496,"tags":497,"__hash__":502},"posts\u002Fposts\u002Flucee-datetimeoffset.md","Storing and Using DateTimeOffsets in Lucee",{"type":7,"value":8,"toc":483},"minimark",[9,32,35,40,51,62,77,84,189,202,206,214,217,271,274,281,288,330,337,391,398,402,405,411,420,423,445,463,466,479],[10,11,12,13,20,21,26,27,31],"p",{},"Recently at ",[14,15,19],"a",{"href":16,"rel":17},"https:\u002F\u002Fimpelos.com\u002F",[18],"nofollow","Impelos"," I began storing dates using SQL Server's ",[14,22,25],{"href":23,"rel":24},"https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fsql\u002Ft-sql\u002Fdata-types\u002Fdatetimeoffset-transact-sql?view=sql-server-2017",[18],"DateTimeOffset"," field, which allows us to store a date and time ",[28,29,30],"strong",{},"with embedded UTC offset",". Unfortunately, I ran into several issues with Lucee's support for the data type.",[10,33,34],{},"This post details what I did to work around the issues and even details how to format the dates to an ISO 8601 JS-safe format.",[36,37,39],"h2",{"id":38},"problem-one-utc-offset-truncation","Problem One: UTC Offset Truncation",[10,41,42,43,46,47,50],{},"The first issue I ran into with the ",[44,45,25],"code",{}," field had to do with losing the UTC offset on insert\u002Fupdate. When using the ",[44,48,49],{},"datetime"," cfsqltype, the UTC offset is truncated.",[10,52,53,54,57,58,61],{},"So instead of ",[44,55,56],{},"2019-07-07 13:22:01 -5:00",", we get ",[44,59,60],{},"2019-07-07 13:22:01 +0:00",". Notice the UTC offset is changed to UTC +0, but the date is never converted to UTC +0, or GMT. This causes the inserted time to be off by the full value of the UTC offset. Insert a datetime at 7PM UTC +1, and it indicates 7PM GMT, which is actually 8PM at UTC +1.",[10,63,64,65,68,69,72,73,76],{},"The workaround I settled on was to use a ",[44,66,67],{},"varchar"," cfsqltype and manually format the value to fit MSSQL's ",[44,70,71],{},"datetimeoffset"," format: ",[44,74,75],{},"YYYY-MM-DD hh:mm:ss[.nnnnnnn] [{+|-}hh:mm]",".",[10,78,79,80,83],{},"Here's a quick example using ",[44,81,82],{},"QueryExecute()",":",[85,86,91],"pre",{"className":87,"code":88,"language":89,"meta":90,"style":90},"language-js shiki shiki-themes github-light github-dark","var params = [\n    datesaved: {\n        value: dateTimeFormat(now(), \"YYYY-mm-dd HH:nn:ss XXX\"),\n        cfsqltype: \"varchar\"\n    },\n    title: \"My crazy title\"\n];\nqueryExecute( \"UPDATE blogposts SET title=:title, datesaved=:datesaved\", params );\n","js","",[44,92,93,112,118,144,153,159,168,174],{"__ignoreMap":90},[94,95,98,102,106,109],"span",{"class":96,"line":97},"line",1,[94,99,101],{"class":100},"szBVR","var",[94,103,105],{"class":104},"sVt8B"," params ",[94,107,108],{"class":100},"=",[94,110,111],{"class":104}," [\n",[94,113,115],{"class":96,"line":114},2,[94,116,117],{"class":104},"    datesaved: {\n",[94,119,121,124,128,131,134,137,141],{"class":96,"line":120},3,[94,122,123],{"class":104},"        value: ",[94,125,127],{"class":126},"sScJk","dateTimeFormat",[94,129,130],{"class":104},"(",[94,132,133],{"class":126},"now",[94,135,136],{"class":104},"(), ",[94,138,140],{"class":139},"sZZnC","\"YYYY-mm-dd HH:nn:ss XXX\"",[94,142,143],{"class":104},"),\n",[94,145,147,150],{"class":96,"line":146},4,[94,148,149],{"class":104},"        cfsqltype: ",[94,151,152],{"class":139},"\"varchar\"\n",[94,154,156],{"class":96,"line":155},5,[94,157,158],{"class":104},"    },\n",[94,160,162,165],{"class":96,"line":161},6,[94,163,164],{"class":104},"    title: ",[94,166,167],{"class":139},"\"My crazy title\"\n",[94,169,171],{"class":96,"line":170},7,[94,172,173],{"class":104},"];\n",[94,175,177,180,183,186],{"class":96,"line":176},8,[94,178,179],{"class":126},"queryExecute",[94,181,182],{"class":104},"( ",[94,184,185],{"class":139},"\"UPDATE blogposts SET title=:title, datesaved=:datesaved\"",[94,187,188],{"class":104},", params );\n",[10,190,191,192,195,196,201],{},"This worked pretty well for me. Note that ",[44,193,194],{},"XXX"," is a mask sequence currently not documented on CFDocs.org. I found this on the documentation page for the ",[14,197,200],{"href":198,"rel":199},"https:\u002F\u002Fdocs.oracle.com\u002Fjavase\u002F7\u002Fdocs\u002Fapi\u002Fjava\u002Ftext\u002FSimpleDateFormat.html",[18],"Java SimpleDateFormat"," class, which Lucee uses under the hood. I'm planning to document that on CFDocs soon!",[36,203,205],{"id":204},"problem-two-datetimeoffset-retrieved-as-java-object","Problem Two: DateTimeOffset Retrieved as Java Object",[10,207,208,209,213],{},"Here's the second issue. Querying for a datetimeoffset field or value actually returns a ... ",[210,211,212],"em",{},"wait for it","... Java object. Yep.",[10,215,216],{},"A simple query like this:",[85,218,220],{"className":87,"code":219,"language":89,"meta":90,"style":90},"var query = queryExecute(\"\n    SELECT getUTCDate()\n    AT TIME ZONE 'Central Standard Time'\n\");\n",[44,221,222,239,248,263],{"__ignoreMap":90},[94,223,224,226,229,231,234,236],{"class":96,"line":97},[94,225,101],{"class":100},[94,227,228],{"class":104}," query ",[94,230,108],{"class":100},[94,232,233],{"class":126}," queryExecute",[94,235,130],{"class":104},[94,237,238],{"class":139},"\"\n",[94,240,241,244],{"class":96,"line":114},[94,242,243],{"class":139},"    SELECT getUTCDate(",[94,245,247],{"class":246},"s7hpK",")\n",[94,249,250,254,257,260],{"class":96,"line":120},[94,251,253],{"class":252},"sj4cs","    AT",[94,255,256],{"class":252}," TIME",[94,258,259],{"class":252}," ZONE",[94,261,262],{"class":139}," 'Central Standard Time'\n",[94,264,265,268],{"class":96,"line":146},[94,266,267],{"class":139},"\")",[94,269,270],{"class":246},";\n",[10,272,273],{},"returns the wonderful little object shown below:",[10,275,276],{},[277,278],"img",{"alt":279,"src":280},"image of SQL query: \"Select getutcdate() at time zone 'Central Standard Time' rendering as java object","https:\u002F\u002Fthepracticaldev.s3.amazonaws.com\u002Fi\u002Fwvwy9y3xlbno2pixrstd.png",[10,282,283,284,287],{},"In case you're wondering, serializing this as JSON does not explode spectacularly as I assumed it would (at least in ColdBox. I admit I did not try this with the native ",[44,285,286],{},"serializeJSON()",".) This Datetime java object renders as the following struct:",[85,289,291],{"className":87,"code":290,"language":89,"meta":90,"style":90},"\"column_0\": {\n    \"Timestamp\": \"June, 13 2019 07:55:00 -0400\",\n    \"MinutesOffset\": 0\n}\n",[44,292,293,301,315,325],{"__ignoreMap":90},[94,294,295,298],{"class":96,"line":97},[94,296,297],{"class":139},"\"column_0\"",[94,299,300],{"class":104},": {\n",[94,302,303,306,309,312],{"class":96,"line":114},[94,304,305],{"class":139},"    \"Timestamp\"",[94,307,308],{"class":104},": ",[94,310,311],{"class":139},"\"June, 13 2019 07:55:00 -0400\"",[94,313,314],{"class":104},",\n",[94,316,317,320,322],{"class":96,"line":120},[94,318,319],{"class":139},"    \"MinutesOffset\"",[94,321,308],{"class":104},[94,323,324],{"class":252},"0\n",[94,326,327],{"class":96,"line":146},[94,328,329],{"class":104},"}\n",[10,331,332,333,336],{},"I tried to force this to a somewhat useable date format using the ",[44,334,335],{},"toString"," method:",[85,338,340],{"className":87,"code":339,"language":89,"meta":90,"style":90},"for ( var row in query ) {\n    row[\"datesaved\"] = row[\"datesaved\"].toString()\n}\n",[44,341,342,361,387],{"__ignoreMap":90},[94,343,344,347,350,352,355,358],{"class":96,"line":97},[94,345,346],{"class":100},"for",[94,348,349],{"class":104}," ( ",[94,351,101],{"class":100},[94,353,354],{"class":104}," row ",[94,356,357],{"class":100},"in",[94,359,360],{"class":104}," query ) {\n",[94,362,363,366,369,372,374,377,379,382,384],{"class":96,"line":114},[94,364,365],{"class":104},"    row[",[94,367,368],{"class":139},"\"datesaved\"",[94,370,371],{"class":104},"] ",[94,373,108],{"class":100},[94,375,376],{"class":104}," row[",[94,378,368],{"class":139},[94,380,381],{"class":104},"].",[94,383,335],{"class":126},[94,385,386],{"class":104},"()\n",[94,388,389],{"class":96,"line":120},[94,390,329],{"class":104},[10,392,393,394,397],{},"but I soon received complaints that the frontend couldn't parse my date strings. In other words ",[44,395,396],{},"row[\"datesaved\"].toString()"," did not output a JS-safe date.",[36,399,401],{"id":400},"formatting-dates-in-iso-8601-format","Formatting Dates in ISO 8601 Format",[10,403,404],{},"Just what date format does JS require? According to Stack Overflow:",[406,407,408],"blockquote",{},[10,409,410],{},"JavaScript officially supports a simplification of the ISO 8601 Extended Format. The format is as follows: YYYY-MM-DDTHH:mm:ss.sssZ. The letter T is the date\u002Ftime separator and Z is the time zone offset specified as Z (for UTC) or either + or - followed by a time expression HH:mm. Some parts (e.g. the time) of that format can be omitted.",[10,412,413],{},[210,414,415],{},[14,416,419],{"href":417,"rel":418},"https:\u002F\u002Fstackoverflow.com\u002Fa\u002F51715260\u002F1525594",[18],"Answer to \"What are valid Date Time Strings in JavaScript?\"",[10,421,422],{},"The easy way to marshall a date into proper ISO 8601 format would be the following:",[85,424,426],{"className":87,"code":425,"language":89,"meta":90,"style":90},"DateTimeFormat(now(), \"YYYY-mm-dd'T'HH:nn:ss.SSSXXX\");\n",[44,427,428],{"__ignoreMap":90},[94,429,430,433,435,437,439,442],{"class":96,"line":97},[94,431,432],{"class":126},"DateTimeFormat",[94,434,130],{"class":104},[94,436,133],{"class":126},[94,438,136],{"class":104},[94,440,441],{"class":139},"\"YYYY-mm-dd'T'HH:nn:ss.SSSXXX\"",[94,443,444],{"class":104},");\n",[10,446,447,448,450,451,454,455,458,459,462],{},"Again, we're using the ",[44,449,194],{}," mask to output a UTC offset like ",[44,452,453],{},"-5:00",", and using single quotes to escape the ",[44,456,457],{},"T"," in the middle of the date string. This should put out a proper ISO 8601 date like so: ",[44,460,461],{},"2019-07-05T17:16:29.029Z",". I'm still a little unsure on the milliseconds, as they don't look right to me, but I don't consider millisecond precision as super important, at least right now.",[10,464,465],{},"With that, my date woes are over for now. It’s been helpful to find extra date formatting masks I didn’t know about, and slightly frustrating to find two year-old bugs in the Lucee DateTimeOffset support.",[10,467,468],{},[210,469,470,473,474,76],{},[28,471,472],{},"Note:"," Both issues are mentioned in the ",[14,475,478],{"href":476,"rel":477},"https:\u002F\u002Fluceeserver.atlassian.net\u002Fbrowse\u002FLDEV-1680",[18],"Lucee Jira ticket #1680",[480,481,482],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":90,"searchDepth":114,"depth":114,"links":484},[485,486,487],{"id":38,"depth":114,"text":39},{"id":204,"depth":114,"text":205},{"id":400,"depth":114,"text":401},"2019-07-08T16:13:57-04:00","Recently at Impelos I began storing dates using SQL Server's DateTimeOffset field, which allows us to store a date and time with embedded UTC offset. Unfortunately, I ran into several issues with Lucee's support for the data type.",false,"md",{},true,"\u002Fposts\u002Flucee-datetimeoffset",{"title":5,"description":489},"posts\u002Flucee-datetimeoffset",[498,499,500,501],"coldfusion","lucee","cfml","sql","TvbwrK973RAMeLepS2YWGvf5-k0HmLTBqplHo3B5Krw",1790471527795]