@type-r/models
Version:
The serializable type system for JS and TypeScript
109 lines (86 loc) • 3.63 kB
text/typescript
/**
* Date attribute type.
*
* Implements validation, cross-browser compatibility fixes, variety of Date serialization formats,
* and optimized update pipeline.
*/
import { TransactionOptions } from '../../transactions';
import { AnyType } from './any';
import { AttributesContainer } from '../updates';
// Date Attribute
/** @private */
export class DateType extends AnyType {
create(){
return new Date();
}
convert( next : any, a, record, options ){
if( next == null || next instanceof Date ) return next;
const date = new Date( next ),
timestamp = date.getTime();
if( timestamp !== timestamp ){
this._log( 'error', 'Type-R:InvalidDate', 'Date attribute assigned with invalid date', next, record, options.logger );
}
return date;
}
validate( model, value, name ) {
if( value != null ){
const timestamp = value.getTime();
if( timestamp !== timestamp ) return name + ' is Invalid Date';
}
}
toJSON( value ) { return value && value.toISOString(); }
isChanged( a, b ) { return ( a && a.getTime() ) !== ( b && b.getTime() ); }
doInit( value, record : AttributesContainer, options : TransactionOptions ){
// Date don't have handleChanges step.
return this.transform( value === void 0 ? this.defaultValue() : value, void 0, record, options );
}
doUpdate( value, record, options, nested ){
const { name } = this,
{ attributes } = record,
prev = attributes[ name ];
// Date don't have handleChanges step.
return this.isChanged( prev , attributes[ name ] = this.transform( value, prev, record, options ) );
}
clone( value ) { return value && new Date( value.getTime() ); }
dispose(){}
}
// If ISO date is not supported by date constructor (such as in Safari), polyfill it.
function supportsDate( date ){
return !isNaN( ( new Date( date ) ).getTime() );
}
if( !supportsDate('2011-11-29T15:52:30.5') ||
!supportsDate('2011-11-29T15:52:30.52') ||
!supportsDate('2011-11-29T15:52:18.867') ||
!supportsDate('2011-11-29T15:52:18.867Z') ||
!supportsDate('2011-11-29T15:52:18.867-03:30') ){
DateType.prototype.convert = function( value ){
return value == null || value instanceof Date ? value : new Date( safeParseDate( value ) );
}
}
const numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ],
isoDatePattern = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/;
function safeParseDate( date : string ) : number {
var timestamp, struct : any[], minutesOffset = 0;
if( ( struct = isoDatePattern.exec( date )) ) {
// avoid NaN timestamps caused by undefined values being passed to Date.UTC
for( var i = 0, k; ( k = numericKeys[ i ] ); ++i ) {
struct[ k ] = +struct[ k ] || 0;
}
// allow undefined days and months
struct[ 2 ] = (+struct[ 2 ] || 1) - 1;
struct[ 3 ] = +struct[ 3 ] || 1;
if( struct[ 8 ] !== 'Z' && struct[ 9 ] !== undefined ) {
minutesOffset = struct[ 10 ] * 60 + struct[ 11 ];
if( struct[ 9 ] === '+' ) {
minutesOffset = 0 - minutesOffset;
}
}
timestamp =
Date.UTC( struct[ 1 ], struct[ 2 ], struct[ 3 ], struct[ 4 ], struct[ 5 ] + minutesOffset, struct[ 6 ],
struct[ 7 ] );
}
else {
timestamp = Date.parse( date );
}
return timestamp;
}