-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
to-offset-date-time #27087
to-offset-date-time #27087
Conversation
describe("work in +08:00 timezone", function () { | ||
it("converts a base64 string to bytes", function () { | ||
const date = new Date("2022-01-05T19:08:10Z"); | ||
assert.strictEqual(toOffsetDateTime(date), "2022-01-05T19:08:10.000+08:00"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xirzec I don't think the test case is correct for all timezones, but have no idea how to set a specific timezone, so that it works
API change check APIView has identified API level changes in this PR and created following API reviews. |
const date = new Date("2022-01-05T19:08:10.000Z"); | ||
assert.strictEqual( | ||
Date.parse(toOffsetDateTime(date)) - Date.parse(date.toISOString()), | ||
60 * date.getTimezoneOffset() * 1000 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A little unsure whether the difference should be 0 ?
if it's 2022-01-05T19:08:10.000Z
In UTC time, then it should be 2022-01-06T03:08:10.000+08:00
in the +08:00 timezone. so maybe purely use something like the below in toOffsetDateTime is incorrect?
date.toISOString().replace(/Z$/, "") + diff + pad(tzOffset / 60) + ":" + pad(tzOffset % 60)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the difference should be 0 here, no matter the ISO datetime or offset datetime they are just format differences and the underlying number should be the one.
Not sure it's easy to convert them. If not, we may take the offsetDateTime
as utcDatetime
in typespec.
I notice that we didn't import any library to handle date and time. Not sure this is mainly for performance consideration.
I know with momentjs we could easily format any date (docs).
> moment.defaultFormat
'YYYY-MM-DDTHH:mm:ssZ'
> moment.defaultFormatUtc
'YYYY-MM-DDTHH:mm:ss[Z]'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that moment is an extremely heavy dependency and something that will eventually be replaced by an ecmascript standard /cc @bterlson
import { toOffsetDateTime } from "../../src/datetime"; | ||
import { assert } from "chai"; | ||
|
||
describe("utcDateTime with timezone offset", function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we add more cases to cover corner situations e.g +0, -11?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As stated here #27087 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if you can figure out a way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do some self reviews.
}); | ||
it("the to offset datetime should be correct in 0 milliseconds precision", function () { | ||
const date = new Date(); | ||
assert.strictEqual(toOffsetDateTime(date, 0), moment(date).format("YYYY-MM-DDTHH:mm:ssZ")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I am not sure how to set the timezone, I realize we can think of the result of moment format as the source of truth to test the converter helper. if this tests can pass, then at least, the converter should be able to work in my local timezone and the one that the CI runs.
}); | ||
}); | ||
|
||
describe("work in 2022-01-05T19:08:10.000Z", function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add this one because of the milliseconds is 0, and we might miss to pad 0s to the end.
// Licensed under the MIT license. | ||
export type OffsetMillisecondsPrecision = 0 | 1 | 2 | 3; | ||
|
||
export function toOffsetDateTime(date: Date, precision: OffsetMillisecondsPrecision = 0): string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add an option as the precision of milliseconds we want to keep. by default it's 0 now, but let me know if there;s other opppinion.
pad(date.getMinutes()) + | ||
":" + | ||
pad(date.getSeconds()) + | ||
(precision !== 0 ? `.${precisedMilliseconds}`.padEnd(precision + 1, "0") : "") + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
padEnd first parameter is precison + 1 because there's a dot in the string.
const date = new Date(); | ||
assert.strictEqual( | ||
Math.floor( | ||
Math.abs(Date.parse(toOffsetDateTime(date)) - Date.parse(date.toISOString())) / 1000 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here as the toOffsetDateTime will lost the miliiseconds of three digit, and we have no way to assert the real diff, so I think we can assume the quotient of the diff divided by 1000 is zero.
const date = new Date("2022-01-05T19:08:10.000Z"); | ||
// assert.strictEqual(moment(date).format(), "2022-01-06T03:08:10+08:00"); | ||
assert.strictEqual(toOffsetDateTime(date), moment(date).format()); | ||
assert.strictEqual(Date.parse(toOffsetDateTime(date)) - Date.parse(date.toISOString()), 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As the milliseconds is zero, we don't have unixtime diff loss. so the diff should be zero for all precisions.
@@ -105,7 +105,8 @@ | |||
"sinon": "^15.0.0", | |||
"typescript": "~5.0.0", | |||
"util": "^0.12.1", | |||
"ts-node": "^10.0.0" | |||
"ts-node": "^10.0.0", | |||
"moment": "^2.29.4" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the dev dependency, so I guess it's okay ?
const pad = (n: number) => `${Math.floor(Math.abs(n))}`.padStart(2, "0"); | ||
const precisedMilliseconds = getPrecisedMilliseconds(date, precision); | ||
return ( | ||
date.getFullYear() + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was wondering if this implementation would work if timezone is crossed year for example utc is in 2023, but with offset timezone it may 2022. The same question for crossed month and day.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just realize these interfaces use the local timezone
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getFullYear
Found this for mocking the timezone in Node-only tests at least: https://www.npmjs.com/package/timezone-mock To step back for a bit, I'm not sure I understand what this datatype means from a TypeSpec perspective, especially for roundtripping. Like when a client retrieves a value from the service, is the offset based on the service's timezone? Or based on the last client that updated that field? If the timezone in the local client is different, do we change the value when doing a PUT even if the developer didn't touch that field? This seems like an extremely error-prone feature of the language. @bterlson can you explain why TypeSpec has this type? |
Talked to Brian offline, for now we can be lossy here - clients won't know the original offset that came from the service (we convert to Date) and we'll always serialize back to the local TZ. Eventually with Temporal we can do better, but not holding my breath for runtime support. |
@xirzec yeah, we can totally do that, and it makes much more sense to me. Because I also thougtht of that when client side get the response with a date time which is a offsetDateTime, customers will probably have to convert it by themselves, then I thought maybe they could leverage this helper function or other util whatever they like. Should I just close this PR ? but I want to confirm with @bterlson, do you think we should make this cross language consistent ? |
close it as offline confirmed with Brian, the current behavior of other language treating offsetDateTime exactly the same as utcDateTime is incorrect, but they can handle this as appropriate. |
Packages impacted by this PR
@azure/core-util
Issues associated with this PR
Some background microsoft/typespec#1899
Describe the problem that is addressed by this PR
In TypeSpec, we have a offsetDateTime type, which is suppose to provide the timezone difference when the format is rfc3339, like
2023-09-11T18:45:20.988+08:00
but typescript date.toISOString() doesn't have such info. as there's no easy way to convert it, I think it might be appropriate to add the convert function into core-util.
What are the possible designs available to address the problem? If there are more than one possible design, why was the one in this PR chosen?
Are there test cases added in this PR? (If not, why?)
Provide a list of related PRs (if any)
Command used to generate this PR:**(Applicable only to SDK release request PRs)
Checklists