Moment.js constructor in strict checking mode returns different types in different environments - momentjs

I'm using a function to format dates that arrive as different types and in different formats, the ultimate goal is to localize and format the result with moment.
To check for different formats in string inputs I try to create a moment object with each supported format and check if it's valid.
const DATE_FORMATS = ['DD/MM/YYYY', 'DD-MM-YYYY', 'YYYY/MM/DD', 'YYYY-MM-DD'];
export const dateFormat = (date, format = 'DD/MM/YYY', toBeMultiplied = true) => {
let formattedDate = date;
if (typeof (date) === 'string') {
let found = false;
for (const date_format of DATE_FORMATS) {
formattedDate = moment(date, date_format);
if (formattedDate.isValid()) { found = true; break; }
}
if (!found) return 'Invalid date string';
} else if (typeof (date) === 'number') {
formattedDate = toBeMultiplied ? date * 1000 : date;
}
return moment(formattedDate).format(format);
};
For some reason this works in my local environment, but when I release it and try the same thing from the server the moment constructor returns a string.
What is going on? Is this an expected behavior?

Since posting the original question I realized that this is just a quirk of the debugger, possibly due to the fact that momentjs is a promise based library and with different environments the resolution flow isn't 100% consistent.
Since this is just a hunch I'll leave the question unanswered for now.

Related

How can I determine if an Array is readonly using TS compiler-api?

I'd like to determine if an array type is readonly. This includes ReadonlyArray and readonly prefixed.
Examples:
type a = ReadonlyArray<string>
type b = readonly string[]
The relevant non-exposed TypeChecker code is:
let globalReadonlyArrayType = <GenericType>getGlobalTypeOrUndefined("ReadonlyArray" as __String, /*arity*/ 1) || globalArrayType;
function isReadonlyArrayType(type: Type): boolean {
return !!(getObjectFlags(type) & ObjectFlags.Reference) && (<TypeReference>type).target === globalReadonlyArrayType;
}
function getGlobalTypeOrUndefined(name: __String, arity = 0): ObjectType | undefined {
const symbol = getGlobalSymbol(name, SymbolFlags.Type, /*diagnostic*/ undefined);
return symbol && <GenericType>getTypeOfGlobalSymbol(symbol, arity);
}
function getTypeOfGlobalSymbol(symbol: Symbol | undefined, arity: number): ObjectType {
function getTypeDeclaration(symbol: Symbol): Declaration | undefined {
const declarations = symbol.declarations;
for (const declaration of declarations) {
switch (declaration.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
return declaration;
}
}
}
if (!symbol) {
return arity ? emptyGenericType : emptyObjectType;
}
const type = getDeclaredTypeOfSymbol(symbol);
if (!(type.flags & TypeFlags.Object)) {
error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_be_a_class_or_interface_type, symbolName(symbol));
return arity ? emptyGenericType : emptyObjectType;
}
if (length((<InterfaceType>type).typeParameters) !== arity) {
error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_have_1_type_parameter_s, symbolName(symbol), arity);
return arity ? emptyGenericType : emptyObjectType;
}
return <ObjectType>type;
}
TypeChecker Method
cspotcode pointed out that you can get IndexInfo via the TypeChecker.
const isReadonlyArrayType = (type: Type) =>
type.checker.isArrayLikeType(type) &&
!!type.checker.getIndexInfoOfType(type, IndexKind.Number)?.isReadonly
TS Compiler Method
The following matches the compiler's logic.
let globalReadonlyArrayType: Type;
export const isReadonlyArrayType = (type: Type): boolean => {
const { checker } = type;
if (!globalReadonlyArrayType) {
const symbol =
checker.resolveName('ReadonlyArray', /* location */ void 0, SymbolFlags.Type, /* excludeGlobals */ false)!;
globalReadonlyArrayType = checker.getDeclaredTypeOfSymbol(symbol);
}
return !!((type as ObjectType).objectFlags & ObjectFlags.Reference) &&
((<TypeReference>type).target === globalReadonlyArrayType);
};
Notes
It appears that there may be no immediate advantage of the TypeChecker method over using the Compiler method. The one concern that I had was that comparing target equality may fail if ReadonlyArray was extended, but it appears that this is currently not possible with TypeScript (v3.9.3)
Logic-wise, if performing isArrayLikeType first, the TypeChecker method would be performing a little more work, but likely not enough to worry about in terms of performance.
With that said, it seems that there may be advantage in the TypeChecker method over the second in the event that TS changes its readonly logic, allows extension of ReadonlyArray, etc.
For that reason, I'd recommend using the TypeChecker method.
If you're not using byots, you could probably replace the call to isArrayLikeType with !!((type as ObjectType).objectFlags & ObjectFlags.Reference)
Caveat: My understanding of ReadonlyArray is at a basic level, as of writing this, so if I'm wrong on any of this, please let me know!

Firebase function to timestamp last change runs multiple times

I have a firebase function to timestamp an item when it is updated. I have seen other examples where code gets stuck in an infinite loop because the timestamp for the latest update causes it to run again, but I thought I had that part figured out in my code below. I believe this was working fine at one point, now it seems to get stuck in an update loop. Am I missing something?
exports.itemUpdate = functions.database.ref('/items/{id}').onUpdate((event) => {
//dont mark updated timestamp if item is deleted
if (!event.data.exists()) {
return null;
}
//dont update if we are simply updating timestamp
if (event.data.child('lastUpdated').changed()) {
return null;
}else{
return admin.database().ref('/items/' + event.params.id + '/lastUpdated').set(admin.database.ServerValue.TIMESTAMP);
}
});
Please note, this is for pre v1.0 beta version of firebase functions.
I believe your code is not getting into the if block, as there's no method changed in the dataSnapshot.
You could try to check if lastUpdated changed with something like this:
const oldValue = event.data.previous.val();
const newValue = event.data.val();
if(oldValue.lastUpdated !== newValue.lastUpdated) {
return null;
} else {
return admin.database().ref('/items/' + event.params.id + '/lastUpdated').set(admin.database.ServerValue.TIMESTAMP);
}

How to make Flow understand dynamic code that uses lodash for runtime type-checking?

Flow's dynamic code example indicates that Flow can figure out runtime type-checking:
function foo(x) {
if (typeof x === 'string') {
return x.length; // flow is smart enough to see this is safe
} else {
return x;
}
}
var res = foo('Hello') + foo(42);
But in real life, typeof isn't good enough. I usually use lodash's type-checking functions (_.isFunction, _.isString etc), which handle a lot of edge cases.
The problem is, if we change the example to use lodash for the runtime type-checking, Flow no longer understands it:
function foo(x) {
if (_.isString(x)) {
return x.length; // warning: `length` property not found in Number
} else {
return x;
}
}
var res = foo('Hello') + foo(42);
I tried using iflow-lodash but it doesn't seem to make a difference here.
What's the best solution to make Flow understand code that uses lodash for runtime type-checking? I'm new to Flow btw.
This would depend on having predicate types in your lodash libdefs.
Predicate types have recently been added to Flow. Although they are still in an experimental state so I would recommend being careful about their usage for anything serious for now.
function isString(x): boolean %checks { // << declare that the method is a refinement
return typeof x === 'string';
}
function method(x: string | number): number {
if (isString(x)) { // << valid refinement
return x.charCodeAt(0); // << no errors
} else {
return x;
}
}
[try it out]
Note: This answer may quickly fall out of date in one of the next releases as this is a brand new feature. Check out Flow's changelog for the latest information.
The solution for now if possible is to use the built-in refinements.
function method(x: string | number): number {
if (typeof x === "string") { // << Inline the check
return x.charCodeAt(0);
} else {
return x;
}
}
The most obvious solution for this specific case is:
if (_.isString(x) && typeof x === 'string') {
In general, you might be able to overcome Flow errors with creative error suppression, like this:
if (_.isString(x)) {
// #ManuallyTyped
var xStr: string = x;
return xStr.length;
} else { ... }
Make sure to define // #ManuallyTyped as a custom suppress_comment in your flow config file for this to work. You might need an ugly regex for that, see flow docs.
It's been a while since I've last done this, but if I recall correctly Flow will assume that your xStr is a string, while the rest of type checking will work just fine.

Why does JsonConvert change time of DateTimes with DateTimeKind.Unspecified when using DateTimeStyles.AssumeUniversal?

I'm building a web API and am having trouble with the JSON serialization of DateTimes. After doing some tests I can only conclude that the behavior of Newtonsoft.Json.JsonConvert and/or the Newtonsoft IsoDateTimeConverter is not what I expected.
Consider this:
// Arrange
var noonUtc = new DateTime(2016, 05, 12, 12, 0, 0, DateTimeKind.Utc);
var noon = new DateTime(2016, 05, 12, 12, 0, 0, DateTimeKind.Unspecified);
var settings = new JsonSerializerSettings();
settings.Converters.Add(new IsoDateTimeConverter
{
Culture = CultureInfo.InvariantCulture,
DateTimeStyles = DateTimeStyles.AdjustToUniversal
});
// Act
var utcJson = JsonConvert.SerializeObject(noonUtc, settings); // "\"2016-05-12T12:00:00Z\""
var json = JsonConvert.SerializeObject(noon, settings); // "\"2016-05-12T10:00:00Z\""
... // Assertions
Okay, so the time for the DateTime with DateTimeKind.Unspecified has been adjusted from 12 o'clock to 10 o'clock. I'm in Stockholm which is currently two hours ahead of UTC, so fair enough.
However, let's change the serializer settings to use DateTimeStyles.AssumeUniversal, like so:
settings.Converters.Add(new IsoDateTimeConverter
{
Culture = CultureInfo.InvariantCulture,
DateTimeStyles = DateTimeStyles.AssumeUniversal
});
This results in the exact same strings and thus also adjusts the DateTime with DateTimeKind.Unspecified by two hours! Should it not assume the date time was already UTC time and leave the time as it was? What am I missing here?
I don't think you're missing anything; this looks like it might be a bug in the IsoDateTimeConverter. Here is the relevant code from the source:
if ((_dateTimeStyles & DateTimeStyles.AdjustToUniversal) == DateTimeStyles.AdjustToUniversal
|| (_dateTimeStyles & DateTimeStyles.AssumeUniversal) == DateTimeStyles.AssumeUniversal)
{
dateTime = dateTime.ToUniversalTime();
}
As you can see, it only looks at whether _dateTimeStyles is set to AdjustToUniversal or AssumeUniversal before calling ToUniversalTime(); it never checks the date's Kind property.
And the documentation for DateTime.ToUniversalTime() says this:
Starting with the .NET Framework version 2.0, the value returned by the ToUniversalTime method is determined by the Kind property of the current DateTime object. The following table describes the possible results.
Kind | Results
----------- | ----------------------------------------------------------
Utc | No conversion is performed.
Local | The current DateTime object is converted to UTC.
Unspecified | The current DateTime object is assumed to be a local time,
| and the conversion is performed as if Kind were Local.
So yeah, it looks like the converter should definitely not be calling ToUniversalTime in this situation. You might want to report an issue.
For now, you can work around this issue by implementing a replacement converter (derived from the original) with the correct behavior. This is probably closer to what you would want:
public class CorrectedIsoDateTimeConverter : IsoDateTimeConverter
{
private const string DefaultDateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK";
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is DateTime)
{
DateTime dateTime = (DateTime)value;
if (dateTime.Kind == DateTimeKind.Unspecified)
{
if (DateTimeStyles.HasFlag(DateTimeStyles.AssumeUniversal))
{
dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc);
}
else if (DateTimeStyles.HasFlag(DateTimeStyles.AssumeLocal))
{
dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Local);
}
}
if (DateTimeStyles.HasFlag(DateTimeStyles.AdjustToUniversal))
{
dateTime = dateTime.ToUniversalTime();
}
string format = string.IsNullOrEmpty(DateTimeFormat) ? DefaultDateTimeFormat : DateTimeFormat;
writer.WriteValue(dateTime.ToString(format, Culture));
}
else
{
base.WriteJson(writer, value, serializer);
}
}
}

Can we compare two java collections with dynamic equals method?

Lets say we have a 'Client' object:
(am just mentioning the attributes and the equals method alone of the 'Client' object below!!)
public class Client {
private Long clientId;
private String clientName;
private Integer status;
//getters and setters for above attributes
.....
...
//hashCode method
....
..
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Client other = (Client) obj;
if (clientId == null) {
if (other.clientId != null)
return false;
} else if (!clientId.equals(other.clientId))
return false;
if (clientName == null) {
if (other.clientName != null)
return false;
} else if (!clientName.equals(other.clientName))
return false;
if (status == null) {
if (other.status != null)
return false;
} else if (!status.equals(other.status))
return false;
return true;
}
}
From the above equals method itz clear that 'two' client objects are said to be equal if all the attributes of the two objects are identical.
Now assume a scenario where I need to compare two collections(named say incomingClients and existingClients) of Client objects.
The first collection(Collection incomingClients) was generated after reading the 'client' data from a csv/xls file.
The second collection(Collection existingClients) contains, all the existing clients currently in the system.
I can do the following code (using apache CollectionUtils)to get the 'common' clients.
Collection<Client> commonClients = (Collection<Client>)CollectionUtils.intersection(incomingClients,existingClients);
Now with the below code I can remove these commonClients from both the collections.
incomingClients.removeAll(commonClients);
existingClients.removeAll(commonClients);
The intention of removing the 'common clients objects' was that, we dont need to do 'any processing' for these records,
as we are really not at all interested in those records.
Now how can I figure out which are the entirely 'new clients' in the 'Collection incomingClients' collection?
(When I say 'new' it means a client having a new 'clientId' which doesnt exist in the 'Collection existingClients')
Also, how can I figure out which are the clients which needs 'modification'
(When I say 'modification' it means that the 'Collection incomingClients' and Collection existingClients'
have the same clientId, but, say, different 'clientName')
I know that we can do the normal 'for' loop('check below') to figure out the 'new'/'modification needed' clients.
But I thought of writing 'something new', whether we can achieve this using some classes/function in the 'Apache CollectionUtils' package.
Collection<Client> newClients = new ArrayList<Client>();
Collection<Client> toBeModifiedClients = new ArrayList<Client>();
boolean foundClient = false;
Client client = null;
for(Client incomingClient :incomingClients){
foundClient = false;
for(Client existingClient : existingClients){
if(existingClient.getClientId().equals(incomingClient.getClientId())){
client = existingClient;
foundClient = true;
break;
}
}
if(foundClient){
toBeModifiedClients.add(client);
}else{
//not found in existing. so this is completely new
newClients.add(incomingClient);
}
}
Am I 'complicating' a simple stuff??
Any thoughts??
First, yes, you are complicating "simple stuff". Your entire question could be summarized as follows:
Given collections A and B, how can I get the following using CollectionUtils:
A-B, using a particular function that determines equality
A∩B, using a particular function that determines equality
So, yes. CollectionUtils has what you need. Look at CollectionUtils.select().

Resources