Open elSuperRiton opened 5 years ago
it is converted to a timestamp by the marshaller component - see https://github.com/awslabs/dynamodb-data-mapper-js/blob/master/packages/dynamodb-data-marshaller/src/marshallItem.ts#L118
Is there any reason the milliseconds are being truncated?
We can treat Typescript's Date
type as DynamoDB String
by specifying this in the @attribute
declaration:
@attribute({ type: "String"})
MyDate: Date
The obvious, universal way to store a date as a string is using the ISO-8601 format, which I'd expect the above to produce. However, it produces a more descriptive format, e.g. Mon Jul 26 2021 11:42:08 GMT+0100 (British Summer Time)
(not sure what this is called).
Is there any way we can configure the DataMapper
client to treat Date
types as ISO-8601 strings when reading from/writing to a table?
It's worth mentioning, Date
's are saved as ISO-8601 strings in some cases. For example, if we convert to JSON then convert back to an instance of our class, then save this instance to our table, the date is an ISO-8601 string.
@table("MyTable")
export class MyClass {
@hashKey()
Id!: number;
@attribute({type: "String"})
MyDate!: Date;
}
const tempObj = new MyClass();
Object.assign(tempObj, {
Id: 1,
MyDate: new Date()
});
mapper.put(tempObj );
This produces the following entry in the table:
{
"Id: {
"N": "1",
"S": "Mon Jul 26 2021 11:42:08 GMT+0100 (British Summer Time)"
}
}
However if we convert to JSON and back again, we get a different result.
const serialized = JSON.stringify(tempObj);
const deserialized = JSON.parse(serialized);
const myClass = new MyClass();
Object.assign(myClass, deserialized);
mapper.put(myClass);
This produces the following entry in the table
{
"Id: {
"N": "1",
"S": "2021-07-27T10:42:08.786Z"
}
}
So this proves that the DataMapper client does support writing Date
's as ISO-8601 strings. This inconsistency also creates a bit of a problem. Regardless of whether the source data came from JSON or from Typescript, we should be able to always have it saved to DynamoDB in the same format (IMO).
After all of this, I have one question:
How to configure the DataMapper client to treat Typescript's DateTime
type as a DynamoDB String
type formatted as an ISO-8601 string?
After playing around with this, I was able to store Date
's as ISO-8601 string's as by using the Custom
type and specifying custom logic on how to marshall/unmarshall the data:
@table("MyTable")
export class MyClass {
@hashKey()
Id!: number;
@attribute({ type: "Custom", marshall: (input) => {
return { S: new Date(input).toISOString() }
}, unmarshall: (input) => {
if (input.S) return new Date(input.S?.toString())
}})
MyDate!: Date;
}
It's a bit messy and it has to be done for every date you want to store as an ISO string. Would be nice if there was some global setting to default to ISO string's for Dates.
@kpenergy you can take advantage of the CustomType
interface for creating a reusable marshaller.
import { CustomType } from '@aws/dynamodb-data-marshaller';
import {
attribute,
hashKey,
rangeKey,
table,
} from '@aws/dynamodb-data-mapper-annotations';
...
// Create uniform ISO date strings for each date.
// Note that if you have a TTL attribute, this needs to be stored as a number
const ISOdateType: CustomType<Date> = {
type: 'Custom',
marshall: (input: Date): AttributeValue => ({ S: input.toISOString() }),
unmarshall: (persistedValue: AttributeValue): Date =>
new Date(persistedValue.S!),
};
@table(config.AWS_TABLE_NAME)
export class History {
@hashKey({ defaultProvider: () => 'history' })
type?: string;
// Use custom marshaller
@rangeKey(ISOdateType)
timestamp?: Date;
// Provide a default as well
@attribute({
...ISOdateType,
defaultProvider: () => new Date(),
})
created_at?: Date;
@attribute()
count?: number;
//... other attributes
}
Hello,
When creating a table with type "S" for a date ( as required per documentation ) and using the type Date with the data mapper annotation I get a mismatch type error. It appears that the Date type does not parses into ISO-8601 string before saving to ddb.