-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Allow passing YYYY-MM-DD
in native database date types to avoid timezone issues
#7490
Comments
After some research I understand the whole thing has to do with the fact that prisma always uses datetime with timezone support even if I want to store a date only. I think as MySQL (and PostgreSQL for that matter) support timezone-agnostic DATE datatypes it would be obvious that prisma supports these as well. I have seen recommendations like storing a date as a string, using middlewares to convert and such. Coming from Laravel/Eloquent and Sequelize I did not even expect that saving a date object can be a problem. I just want to store birthdays, start-of-work and end-of-work dates without thinking too much about that. |
You can try to use Native Types: https://www.prisma.io/docs/concepts/components/prisma-schema/data-model#native-types-mapping I had this issue since Prisma 1. I store hotel arrivals and departures as numbers, like: Relevant issue: #4355 |
I already use native types, at least npx prisma db pull seems to generate these for me. Even if this works in the sample, in my real app the insert into a Maybe I resort to a string based date format bit that just does not feel right. |
Oh sorry, I have just skimmed through your issue, didn't see you use native types. I have once researched the subject. I agree that the best would be if it was handled by Prisma. |
Thanks for the info. I feel that the whole date-time handling is surprisingly complex given the various variants how the Javascript Date() constructor handles timezones, console.log(date) handles timezones and there is no real Date datatype without timezones. The most robust solution is probably to store dates as strings (YYYY-MM-DD) or numbers a you did. At least this gives you no surprises. |
I believe it's still advisable to use the native Date type in database over String or Int, not sure though.
You should also know that there is a new spec coming to Javascript called Temporal: https://tc39.es/proposal-temporal/docs/ It's currently in Stage 3, there are some polyfills already and you can test it (https://dev.to/romulocintra/temporal-date-time-in-javascript-today-23cb), soon to be available in browsers. The main thing you are looking for is Temporal.PlainDate, which will handle your use case: |
Thanks for the update, Temporal seems to evolve into a great thing, will keep looking into it. |
Thanks for the discussion here. I also went through the issue and it seems to me this is not a serialization fault in Prisma as we just call We can potentially allow just passing |
YYYY-MM-DD
in native database date types to avoid timezone issues
This is an issue most Prisma users will eventually stumble upon. For example, I'm GMT-3 so if I do Prisma should not enforce JavaScript date type upon SQL Date fields exactly because of that. Date types should definitely accept |
First you need to edit the
to
and run |
is there any plan to support this issue. I think this is the only big critical issue left in your awesome library. |
If timezone offset not specified, UTC date is different from timezone date. new Date(Date.UTC(2022, 9, 19)) it's safely used if storing specific date even if using any timezone because it is converted date object adapts timezone and stored as utc time. (e.g. 1) 2022-10-19 if earlier X hours than utc time, the conversion new Date(Date.UTC(2022, 9, 19)) [js timezone Object]-> 2022-10-19 (+ X hours from UTC) [UTC conversion]-> 2022-10-19:00:00:00(UTC stored in DB) (e.g. 2) 2022-10-19 if later X hours than utc time, the conversion new Date(Date.UTC(2022, 9, 19)) [js timezone Object]-> 2022-10-18 (- X hours from UTC) [UTC conversion]-> 2022-10-19:00:00:00(UTC stored in DB) So, comparatively simple patches can be applied in javascript layer type DIGIT = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7| 8 | 9
// type YYYY = `${DIGIT}${DIGIT}${DIGIT}${DIGIT}` // too many literal types inferred error
type YYYY = number
type MM = `0${Exclude<DIGIT, 0>}` | `1${0 | 1 | 2}`
type DD = Exclude<`${0 | 1 | 2}${DIGIT}` | `3${0 | 1}`, '00'>
function parseUTCBasedDate(date: `${YYYY}-{MM}-${DD}` | Date): Date {
// if(typeof date === 'string') {
// const [yyyy, mm, dd] = date.split('-')
// return new Date(Date.UTC(parseInt(yyyy, 10), parseInt(mm, 10) - 1, parseInt(dd, 10)))
// }
// above code for arbitrary format
const _date = typeof date === 'string' ? new Date(date) : date;
// If you want to apply new Date(2022, 9, 19) as saved 2022-9-19 in your timezone by parse(new Date(2022, 9, 19) )
return new Date(Date.UTC(_date.getFullYear(), _date.getMonth(), _date.getDate()))
} The codes restricts stored date to UTC. But there is one more consideration. /* strict
export function getTZDateFromUTC(dateField: Date) {
return new Date(dateField.getFullYear(), dateField.getMonth(), dateField.getDate(), dateField.getHours() + (dateField.getTimezoneOffset() / 60))
}
*/
// better performance
export function getTZDateFromUTC(dateField: Date) {
return new Date(dateField.getUTCFullYear(), dateField.getUTCMonth(), dateField.getUTCDate())
} Now, I tested test code below. /* eslint-disable @typescript-eslint/no-var-requires */
const { TestEnvironment } = require('jest-environment-node')
module.exports = class TimeZoneEnv extends TestEnvironment {
constructor(config, context) {
super(config, context)
const timeZoneFromOptions =
config.projectConfig.testEnvironmentOptions.timezone
if (timeZoneFromOptions) {
process.env.TZ = timeZoneFromOptions
}
}
} /**
* @jest-environment ./jest/test-date-environment.js
* @jest-environment-options {"timezone": "Asia/Tokyo"}
*/
import { parseUTCBasedDate, getTZDateFromUTC } from './force-utc'
describe('conversion utc and tz', () => {
it('store as UTC date', () => {
const date = parseUTCBasedDate(new Date(2022, 9, 11))
const date2 = parseUTCBasedDate('2022-10-11')
expect(date.toUTCString()).toBe('Tue, 11 Oct 2022 00:00:00 GMT')
expect(date2.toUTCString()).toBe('Tue, 11 Oct 2022 00:00:00 GMT')
})
it('restore as TZ date', () => {
const tzdate = new Date(2022, 9, 11)
const date = parseUTCBasedDate(tzdate)
expect(getTZDateFromUTC(date).toUTCString()).toBe(tzdate.toUTCString())
})
}) Now, we can control date type in all timezone. Maybe, these codes can be patched to @prisma/client codebase, too. If more strictly date type wanted derived from some databases, prisma should make DBs enable to configure other timezone. As I thought it, date type should be stored yyyymmdd as short int or yyyymmdd as string as learning my experience like already mentioned when strict date type isn't needed. It makes code simple and have advantages about performance when indexing if it as numerical data. |
I created a column:
The generated types in the Prisma client are:
When I try to write the string there, I get a runtime error (and no type error):
At least the types should match what is actually allowed! Using UTC / specifying timezone is a huge over complication, its simpler to just store it as a string. We shouldn't have to though, because Postgres supports dates (with no timezone) natively. |
Any news on this one? :) |
Prisma has been a pain when dealing with date and datetime. The Prisma team has been ignoring these date / datetime timezone issues for a long time. Probably, should just go back to Sequelize. |
Fair but how hard is it to workaround? Can't you just use an |
i think if prisma detect the field is "Date" only, it should not convert it to utc, it does not make sense |
I am also having the same problem using sql server and SvelteKit. It seems to work fine when inserting the date into the database. When the date is retrieved on the server side it is correct, but once it hits the client side the date is incorrect. Only for the @db.Date not @db.DateTime |
Same with startDate DateTime @db.Date // yyyy-mm-dd
startTime DateTime @db.Time // hh:mm:ss
endDate DateTime @db.Date // yyyy-mm-dd
endTime DateTime @db.Time // hh:mm:ss Results in Argument startDate: Got invalid value '2023-04-01' on prisma.createOneEvent. Provided String, expected DateTime.
Argument startTime: Got invalid value '00:00:01' on prisma.createOneEvent. Provided String, expected DateTime.
Argument endDate: Got invalid value '2023-12-31' on prisma.createOneEvent. Provided String, expected DateTime.
Argument endTime: Got invalid value '23:59:59' on prisma.createOneEvent. Provided String, expected DateTime. Also
|
so, any suggestions on it? how you guys works with Date now? |
Timezones are difficult, maybe extra so in Javascript. An overview over what we (don't) do:
General tips
|
I have two functions that I use to fix the date issue with prisma. I am using these for a sql server database. These are just for saving dates when you are saving date/datetimes that are not in UTC. One is for saving the date to prisma and the other is for using a date that you get from prisma. fixedDateToPrisma uses date-fns to cut off any time/timezone that is on the date you are sending and saving the date in the format prisma requires. This should properly convert your date. I hope this helps someone! //fixed datetimes sent to database if database times are not in UTC //fix timezone issue from prisma if database times are not in utc And here is an example of creating a post: /** @type {import('./$types').RequestHandler} */
} And then this would be an example of using the date on a page somewhere. You would also need to import format from date-fns: import { format } from 'date-fns'; let commentDateToCompare = (date) => { If you are saving times in UTC as @tomfa mentioned then in your server.js file you need to make sure to set the timezone for your server to UTC // Set the TZ environment variable to "UTC" to fix timezone issues |
This one issue is why I won't be using Prisma for my current project. It's disturbing that it hasn't been addressed after more than 3 years. Issue #2783 dated 2020.06.17 was closed without the problem having been fixed. |
@alanpendleton what are you using instead of Prisma? I'd like to move to another ORM because of this issue too. |
Bug description
When I insert a date value, it gets stored in the database
date
attribute with a day off by one.How to reproduce
I create a date object via
new Date(1960, 8, 24)
and I insert it on both adate
and adatetime
attribute of a table record.In the
date
attributexdate
I find "1960-09-23" which to me is unexpected behaviour.In the
datetime
attributexdatetime
I find "1960-09-24" which I would expect.schema.prisma was generated by
npx prisma db pull
Expected behavior
I create a date object via
new Date(1960, 8, 24)
and I insert it on both adate
and adatetime
attribute of a table record.date
attributexdate
I find "1960-09-23" which to me is unexpected behaviour.datetime
attributexdatetime
I find "1960-09-24" which I would expect.Prisma information
Environment & setup
Prisma Version
The text was updated successfully, but these errors were encountered: