Repository: salsita/node-pg-migrate (ts)

Issue:
migration file use YYYYMMDDHHmmss instead timestamp
current create use timestamp for generated migration, should allowed customize or use `YYYYMMDDHHmmss`, comparable, readable, knex generated migration use this pattern too.

Pull request description:
Ability to use sort of UTC time in filename
Fixes #607

Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index aea6138..0468c3b 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -92,7 +92,7 @@ jobs:
       - <<: *node-image
         environment:
           - DATABASE_URL=postgres://ubuntu:ubuntu@localhost:5432/circle_test
-      - image: postgres:10.12-alpine
+      - image: postgres:10.13-alpine
         environment:
           - POSTGRES_USER=ubuntu
           - POSTGRES_PASSWORD=ubuntu
@@ -106,7 +106,7 @@ jobs:
       - <<: *node-image
         environment:
           - DATABASE_URL=postgres://ubuntu:ubuntu@localhost:5432/circle_test
-      - image: postgres:11.7-alpine
+      - image: postgres:11.8-alpine
         environment:
           - POSTGRES_USER=ubuntu
           - POSTGRES_PASSWORD=ubuntu
@@ -120,7 +120,7 @@ jobs:
       - <<: *node-image
         environment:
           - DATABASE_URL=postgres://ubuntu:ubuntu@localhost:5432/circle_test
-      - image: postgres:12.2-alpine
+      - image: postgres:12.3-alpine
         environment:
           - POSTGRES_USER=ubuntu
           - POSTGRES_PASSWORD=ubuntu
@@ -155,26 +155,12 @@ jobs:
       - run:
           name: test
           command: npm run migrate up -- -m test/cockroach --no-lock && npm run migrate down 0 -- -m test/cockroach --no-lock --timestamps
-  test-node-8:
-    docker:
-      - image: circleci/node:8
-        environment:
-          - DATABASE_URL=postgres://ubuntu:ubuntu@localhost:5432/circle_test
-      - image: postgres:12.2-alpine
-        environment:
-          - POSTGRES_USER=ubuntu
-          - POSTGRES_PASSWORD=ubuntu
-          - POSTGRES_DB=circle_test
-    steps:
-      - <<: *restore
-      - <<: *postgres-wait
-      - <<: *test-postgres
   test-node-10:
     docker:
       - image: circleci/node:10
         environment:
           - DATABASE_URL=postgres://ubuntu:ubuntu@localhost:5432/circle_test
-      - image: postgres:12.2-alpine
+      - image: postgres:12.3-alpine
         environment:
           - POSTGRES_USER=ubuntu
           - POSTGRES_PASSWORD=ubuntu
@@ -188,7 +174,7 @@ jobs:
       - image: circleci/node:14
         environment:
           - DATABASE_URL=postgres://ubuntu:ubuntu@localhost:5432/circle_test
-      - image: postgres:12.2-alpine
+      - image: postgres:12.3-alpine
         environment:
           - POSTGRES_USER=ubuntu
           - POSTGRES_PASSWORD=ubuntu
@@ -200,7 +186,7 @@ jobs:
   test-config:
     docker:
       - <<: *node-image
-      - image: postgres:12.2-alpine
+      - image: postgres:12.3-alpine
         environment:
           - POSTGRES_USER=ubuntu
           - POSTGRES_PASSWORD=ubuntu
@@ -230,7 +216,7 @@ jobs:
         environment:
           # can't use characters @#?/ in password - they have special meaning in url
           - DATABASE_URL=postgres://ubuntu:123456abcdefghABCDEFGH~\`\!$%^\&*-_=+{}[]\(\)\<\>,.\;:\"\'\|\\@localhost:5432/circle_test
-      - image: postgres:12.2-alpine
+      - image: postgres:12.3-alpine
         environment:
           - POSTGRES_USER=ubuntu
           - POSTGRES_PASSWORD=123456abcdefghABCDEFGH~\`\!$%^\&*-_=+{}[]\(\)\<\>,.\;:\"\'\|\\
@@ -242,7 +228,7 @@ jobs:
   test-password-2:
     docker:
       - <<: *node-image
-      - image: postgres:12.2-alpine
+      - image: postgres:12.3-alpine
         environment:
           - POSTGRES_USER=ubuntu
           - POSTGRES_PASSWORD=123456abcdefghABCDEFGH~\`\!@#$%^\&*-_=+{}[]\(\)\<\>,.\;:\"\'?\|/\\
@@ -273,7 +259,7 @@ jobs:
         - PGUSER=ubuntu
         - PGPASSWORD=123456abcdefghABCDEFGH~\`\!@#$%^\&*-_=+{}[]\(\)\<\>,.\;:\"\'?\|/\\
         - PGDATABASE=circle_test
-    - image: postgres:12.2-alpine
+    - image: postgres:12.3-alpine
       environment:
         - POSTGRES_USER=ubuntu
         - POSTGRES_PASSWORD=123456abcdefghABCDEFGH~\`\!@#$%^\&*-_=+{}[]\(\)\<\>,.\;:\"\'?\|/\\
@@ -289,7 +275,7 @@ jobs:
           - DATABASE_URL=postgres://ubuntu:ubuntu@localhost:5432/circle_test
           # used in tests
           - SCHEMA=myschema
-      - image: postgres:12.2-alpine
+      - image: postgres:12.3-alpine
         environment:
           - POSTGRES_USER=ubuntu
           - POSTGRES_PASSWORD=ubuntu
@@ -307,7 +293,7 @@ jobs:
           - DATABASE_URL=postgres://ubuntu:ubuntu@localhost:5432/circle_test
           # used in tests
           - SCHEMA=myschema
-      - image: postgres:12.2-alpine
+      - image: postgres:12.3-alpine
         environment:
           - POSTGRES_USER=ubuntu
           - POSTGRES_PASSWORD=ubuntu
@@ -323,7 +309,7 @@ jobs:
       - <<: *node-image
         environment:
           - DATABASE_URL=postgres://ubuntu:ubuntu@localhost:5432/circle_test
-      - image: postgres:12.2-alpine
+      - image: postgres:12.3-alpine
         environment:
           - POSTGRES_USER=ubuntu
           - POSTGRES_PASSWORD=ubuntu
@@ -339,7 +325,7 @@ jobs:
       - <<: *node-image
         environment:
           - DATABASE_URL=postgres://ubuntu:ubuntu@localhost:5432/circle_test
-      - image: postgres:12.2-alpine
+      - image: postgres:12.3-alpine
         environment:
           - POSTGRES_USER=ubuntu
           - POSTGRES_PASSWORD=ubuntu
@@ -380,9 +366,6 @@ workflows:
       - test-cockroach-2:
           requires:
             - install
-      - test-node-8:
-          requires:
-            - install
       - test-node-10:
           requires:
             - install
diff --git a/bin/node-pg-migrate b/bin/node-pg-migrate
index e12ec9f..434b076 100755
--- a/bin/node-pg-migrate
+++ b/bin/node-pg-migrate
@@ -31,6 +31,7 @@ const migrationsTableArg = 'migrations-table'
 const migrationsSchemaArg = 'migrations-schema'
 const createMigrationsSchemaArg = 'create-migrations-schema'
 const migrationFileLanguageArg = 'migration-file-language'
+const migrationFilenameFormatArg = 'migration-filename-format'
 const checkOrderArg = 'check-order'
 const configValueArg = 'config-value'
 const configFileArg = 'config-file'
@@ -133,12 +134,19 @@ const { argv } = yargs
 
   .option('j', {
     alias: migrationFileLanguageArg,
-    defaultDescription: 'last one used or js if there is no migration yet',
+    defaultDescription: 'last one used or "js" if there is no migration yet',
     choices: ['js', 'ts', 'sql'],
     describe: 'Language of the migration file (Only valid with the create action)',
     type: 'string',
   })
 
+  .option(migrationFilenameFormatArg, {
+    defaultDescription: '"timestamp"',
+    choices: ['timestamp', 'utc'],
+    describe: 'Prefix type of migration filename (Only valid with the create action)',
+    type: 'string',
+  })
+
   .option(tsconfigArg, {
     describe: 'path to tsconfig.json file',
     type: 'string',
@@ -209,6 +217,7 @@ let MIGRATIONS_SCHEMA = argv[migrationsSchemaArg]
 let CREATE_MIGRATIONS_SCHEMA = argv[createMigrationsSchemaArg]
 let MIGRATIONS_TABLE = argv[migrationsTableArg]
 let MIGRATIONS_FILE_LANGUAGE = argv[migrationFileLanguageArg]
+let MIGRATIONS_FILENAME_FORMAT = argv[migrationFilenameFormatArg]
 let CHECK_ORDER = argv[checkOrderArg]
 let VERBOSE = argv[verboseArg]
 let DECAMELIZE = argv[decamelizeArg]
@@ -254,6 +263,8 @@ function readJson(json) {
     MIGRATIONS_TABLE = typeof MIGRATIONS_TABLE !== 'undefined' ? MIGRATIONS_TABLE : json[migrationsTableArg]
     MIGRATIONS_FILE_LANGUAGE =
       typeof MIGRATIONS_FILE_LANGUAGE !== 'undefined' ? MIGRATIONS_FILE_LANGUAGE : json[migrationFileLanguageArg]
+    MIGRATIONS_FILENAME_FORMAT =
+      typeof MIGRATIONS_FILENAME_FORMAT !== 'undefined' ? MIGRATIONS_FILENAME_FORMAT : json[migrationFilenameFormatArg]
     IGNORE_PATTERN = typeof IGNORE_PATTERN !== 'undefined' ? IGNORE_PATTERN : json[ignorePatternArg]
     CHECK_ORDER = typeof CHECK_ORDER !== 'undefined' ? CHECK_ORDER : json[checkOrderArg]
     VERBOSE = typeof VERBOSE !== 'undefined' ? VERBOSE : json[verboseArg]
@@ -326,7 +337,13 @@ if (action === 'create') {
     process.exit(1)
   }
 
-  Migration.create(newMigrationName, MIGRATIONS_DIR, MIGRATIONS_FILE_LANGUAGE, IGNORE_PATTERN)
+  Migration.create(
+    newMigrationName,
+    MIGRATIONS_DIR,
+    MIGRATIONS_FILE_LANGUAGE,
+    IGNORE_PATTERN,
+    MIGRATIONS_FILENAME_FORMAT,
+  )
     .then((migrationPath) => {
       console.log(util.format('Created migration -- %s', migrationPath))
       process.exit(0)
diff --git a/package-lock.json b/package-lock.json
index 2f623fd..dd3de94 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -147,9 +147,9 @@
       "dev": true
     },
     "@types/lodash": {
-      "version": "4.14.150",
-      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.150.tgz",
-      "integrity": "sha512-kMNLM5JBcasgYscD9x/Gvr6lTAv2NVgsKtet/hm93qMyf/D1pt+7jeEZklKJKxMVmXjxbRVQQGfqDSfipYCO6w==",
+      "version": "4.14.151",
+      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.151.tgz",
+      "integrity": "sha512-Zst90IcBX5wnwSu7CAS0vvJkTjTELY4ssKbHiTnGcJgi170uiS8yQDdc3v6S77bRqYQIN1App5a1Pc2lceE5/g==",
       "dev": true
     },
     "@types/mkdirp": {
@@ -199,9 +199,9 @@
       "dev": true
     },
     "@types/sinon": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.0.tgz",
-      "integrity": "sha512-v2TkYHkts4VXshMkcmot/H+ERZ2SevKa10saGaJPGCJ8vh3lKrC4u663zYEeRZxep+VbG6YRDtQ6gVqw9dYzPA==",
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.1.tgz",
+      "integrity": "sha512-vqWk3K1HYJExooYgORUdiGX1EdCWQxPi7P/OEIetdaJn4jNvEYoRRGLG/HwomtbzZ4IP9Syz2k4N50CItv6w6g==",
       "dev": true,
       "requires": {
         "@types/sinonjs__fake-timers": "*"
@@ -224,45 +224,45 @@
       "dev": true
     },
     "@typescript-eslint/eslint-plugin": {
-      "version": "2.31.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.31.0.tgz",
-      "integrity": "sha512-iIC0Pb8qDaoit+m80Ln/aaeu9zKQdOLF4SHcGLarSeY1gurW6aU4JsOPMjKQwXlw70MvWKZQc6S2NamA8SJ/gg==",
+      "version": "2.33.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.33.0.tgz",
+      "integrity": "sha512-QV6P32Btu1sCI/kTqjTNI/8OpCYyvlGjW5vD8MpTIg+HGE5S88HtT1G+880M4bXlvXj/NjsJJG0aGcVh0DdbeQ==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/experimental-utils": "2.31.0",
+        "@typescript-eslint/experimental-utils": "2.33.0",
         "functional-red-black-tree": "^1.0.1",
         "regexpp": "^3.0.0",
         "tsutils": "^3.17.1"
       }
     },
     "@typescript-eslint/experimental-utils": {
-      "version": "2.31.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.31.0.tgz",
-      "integrity": "sha512-MI6IWkutLYQYTQgZ48IVnRXmLR/0Q6oAyJgiOror74arUMh7EWjJkADfirZhRsUMHeLJ85U2iySDwHTSnNi9vA==",
+      "version": "2.33.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.33.0.tgz",
+      "integrity": "sha512-qzPM2AuxtMrRq78LwyZa8Qn6gcY8obkIrBs1ehqmQADwkYzTE1Pb4y2W+U3rE/iFkSWcWHG2LS6MJfj6SmHApg==",
       "dev": true,
       "requires": {
         "@types/json-schema": "^7.0.3",
-        "@typescript-eslint/typescript-estree": "2.31.0",
+        "@typescript-eslint/typescript-estree": "2.33.0",
         "eslint-scope": "^5.0.0",
         "eslint-utils": "^2.0.0"
       }
     },
     "@typescript-eslint/parser": {
-      "version": "2.31.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.31.0.tgz",
-      "integrity": "sha512-uph+w6xUOlyV2DLSC6o+fBDzZ5i7+3/TxAsH4h3eC64tlga57oMb96vVlXoMwjR/nN+xyWlsnxtbDkB46M2EPQ==",
+      "version": "2.33.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.33.0.tgz",
+      "integrity": "sha512-AUtmwUUhJoH6yrtxZMHbRUEMsC2G6z5NSxg9KsROOGqNXasM71I8P2NihtumlWTUCRld70vqIZ6Pm4E5PAziEA==",
       "dev": true,
       "requires": {
         "@types/eslint-visitor-keys": "^1.0.0",
-        "@typescript-eslint/experimental-utils": "2.31.0",
-        "@typescript-eslint/typescript-estree": "2.31.0",
+        "@typescript-eslint/experimental-utils": "2.33.0",
+        "@typescript-eslint/typescript-estree": "2.33.0",
         "eslint-visitor-keys": "^1.1.0"
       }
     },
     "@typescript-eslint/typescript-estree": {
-      "version": "2.31.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.31.0.tgz",
-      "integrity": "sha512-vxW149bXFXXuBrAak0eKHOzbcu9cvi6iNcJDzEtOkRwGHxJG15chiAQAwhLOsk+86p9GTr/TziYvw+H9kMaIgA==",
+      "version": "2.33.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.33.0.tgz",
+      "integrity": "sha512-d8rY6/yUxb0+mEwTShCQF2zYQdLlqihukNfG9IUlLYz5y1CH6G/9XYbrxQLq3Z14RNvkCC6oe+OcFlyUpwUbkg==",
       "dev": true,
       "requires": {
         "debug": "^4.1.1",
@@ -270,8 +270,16 @@
         "glob": "^7.1.6",
         "is-glob": "^4.0.1",
         "lodash": "^4.17.15",
-        "semver": "^6.3.0",
+        "semver": "^7.3.2",
         "tsutils": "^3.17.1"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "7.3.2",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
+          "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
+          "dev": true
+        }
       }
     },
     "acorn": {
@@ -5092,16 +5100,16 @@
       "dev": true
     },
     "pg": {
-      "version": "8.1.0",
-      "resolved": "https://registry.npmjs.org/pg/-/pg-8.1.0.tgz",
-      "integrity": "sha512-Jp+XSNTGYDztc2FgIbmBXeeYMR7kKjfgnl3R+ioO6rkcxDmaea+YPp/gaxe13PBnJAFYyEGl0ixpwPm2gb6eUw==",
+      "version": "8.2.1",
+      "resolved": "https://registry.npmjs.org/pg/-/pg-8.2.1.tgz",
+      "integrity": "sha512-DKzffhpkWRr9jx7vKxA+ur79KG+SKw+PdjMb1IRhMiKI9zqYUGczwFprqy+5Veh/DCcFs1Y6V8lRLN5I1DlleQ==",
       "dev": true,
       "requires": {
         "buffer-writer": "2.0.0",
         "packet-reader": "1.0.0",
-        "pg-connection-string": "^2.2.2",
-        "pg-pool": "^3.2.0",
-        "pg-protocol": "^1.2.2",
+        "pg-connection-string": "^2.2.3",
+        "pg-pool": "^3.2.1",
+        "pg-protocol": "^1.2.4",
         "pg-types": "^2.1.0",
         "pgpass": "1.x",
         "semver": "4.3.2"
@@ -5116,9 +5124,9 @@
       }
     },
     "pg-connection-string": {
-      "version": "2.2.2",
-      "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.2.2.tgz",
-      "integrity": "sha512-+hel4DGuSZCjCZwglAuyi+XlodHnKmrbyTw0hVWlmGN2o4AfJDkDo5obAFzblS5M5PFBMx0uDt5Y1QjlNC+tqg==",
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.2.3.tgz",
+      "integrity": "sha512-I/KCSQGmOrZx6sMHXkOs2MjddrYcqpza3Dtsy0AjIgBr/bZiPJRK9WhABXN1Uy1UDazRbi9gZEzO2sAhL5EqiQ==",
       "dev": true
     },
     "pg-int8": {
@@ -5128,15 +5136,15 @@
       "dev": true
     },
     "pg-pool": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.0.tgz",
-      "integrity": "sha512-7BLwDNDEfPFjE9vmZLcJPLFwuDAVGZ5lIZo2MeQfwYG7EPGfdNVis/dz6obI/yKqvQIx2sf6QBKXMLB+y/ftgA==",
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.1.tgz",
+      "integrity": "sha512-BQDPWUeKenVrMMDN9opfns/kZo4lxmSWhIqo+cSAF7+lfi9ZclQbr9vfnlNaPr8wYF3UYjm5X0yPAhbcgqNOdA==",
       "dev": true
     },
     "pg-protocol": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.2.2.tgz",
-      "integrity": "sha512-r8hGxHOk3ccMjjmhFJ/QOSVW5A+PP84TeRlEwB/cQ9Zu+bvtZg8Z59Cx3AMfVQc9S0Z+EG+HKhicF1W1GN5Eqg==",
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.2.4.tgz",
+      "integrity": "sha512-/8L/G+vW/VhWjTGXpGh8XVkXOFx1ZDY+Yuz//Ab8CfjInzFkreI+fDG3WjCeSra7fIZwAFxzbGptNbm8xSXenw==",
       "dev": true
     },
     "pg-types": {
@@ -6507,9 +6515,9 @@
       "dev": true
     },
     "typescript": {
-      "version": "3.8.3",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
-      "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==",
+      "version": "3.9.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.2.tgz",
+      "integrity": "sha512-q2ktq4n/uLuNNShyayit+DTobV2ApPEo/6so68JaD5ojvc/6GClBipedB9zNWYxRSAlZXAe405Rlijzl6qDiSw==",
       "dev": true
     },
     "union-value": {
diff --git a/package.json b/package.json
index fc51957..5dde1ea 100644
--- a/package.json
+++ b/package.json
@@ -31,7 +31,7 @@
     "api"
   ],
   "engines": {
-    "node": ">=8.0.0"
+    "node": ">=10.0.0"
   },
   "bugs": {
     "url": "https://github.com/salsita/node-pg-migrate/issues"
@@ -51,14 +51,14 @@
   "devDependencies": {
     "@types/chai": "4.2.11",
     "@types/chai-as-promised": "7.1.2",
-    "@types/lodash": "4.14.150",
+    "@types/lodash": "4.14.151",
     "@types/mkdirp": "1.0.0",
     "@types/mocha": "7.0.2",
     "@types/proxyquire": "1.3.28",
-    "@types/sinon": "9.0.0",
+    "@types/sinon": "9.0.1",
     "@types/sinon-chai": "3.2.4",
-    "@typescript-eslint/eslint-plugin": "2.31.0",
-    "@typescript-eslint/parser": "2.31.0",
+    "@typescript-eslint/eslint-plugin": "2.33.0",
+    "@typescript-eslint/parser": "2.33.0",
     "chai": "4.2.0",
     "chai-as-promised": "7.1.1",
     "config": "3.3.1",
@@ -76,14 +76,14 @@
     "json5": "2.1.3",
     "lint-staged": "10.2.2",
     "mocha": "7.1.2",
-    "pg": "8.1.0",
+    "pg": "8.2.1",
     "prettier": "2.0.5",
     "proxyquire": "2.1.3",
     "rimraf": "3.0.2",
     "sinon": "9.0.2",
     "sinon-chai": "3.5.0",
     "ts-node": "8.10.1",
-    "typescript": "3.8.3"
+    "typescript": "3.9.2"
   },
   "peerDependencies": {
     "pg": ">=4.3.0 <9.0.0"
diff --git a/src/migration.ts b/src/migration.ts
index 98399fe..ba34e2b 100644
--- a/src/migration.ts
+++ b/src/migration.ts
@@ -9,28 +9,20 @@
 import fs from 'fs'
 import mkdirp from 'mkdirp'
 import path from 'path'
-import { promisify } from 'util'
 import { DBConnection } from './db'
 import MigrationBuilder from './migration-builder'
 import { MigrationAction, MigrationBuilderActions, MigrationDirection, RunnerOption, Logger } from './types'
 import { getMigrationTableSchema } from './utils'
 import { ColumnDefinitions } from './operations/tablesTypes'
 
-const readdir = promisify(fs.readdir) // eslint-disable-line security/detect-non-literal-fs-filename
-const lstat = promisify(fs.lstat) // eslint-disable-line security/detect-non-literal-fs-filename
+const { readdir } = fs.promises
 
 const SEPARATOR = '_'
 
 export const loadMigrationFiles = async (dir: string, ignorePattern?: string) => {
-  const dirContent = await readdir(`${dir}/`)
-  const files = (
-    await Promise.all(
-      dirContent.map(async (file) => {
-        const stats = await lstat(`${dir}/${file}`)
-        return stats.isFile() ? file : null
-      }),
-    )
-  )
+  const dirContent = await readdir(`${dir}/`, { withFileTypes: true })
+  const files = dirContent
+    .map((file) => (file.isFile() || file.isSymbolicLink() ? file.name : null))
     .filter((file): file is string => Boolean(file))
     .sort()
   const filter = new RegExp(`^(${ignorePattern})$`) // eslint-disable-line security/detect-non-literal-regexp
@@ -46,22 +38,58 @@ const getLastSuffix = async (dir: string, ignorePattern?: string) => {
   }
 }
 
+export const getTimestamp = (logger: Logger, filename: string): number => {
+  const prefix = filename.split(SEPARATOR)[0]
+  if (prefix && /^\d+$/.test(prefix)) {
+    if (prefix.length === 13) {
+      // timestamp: 1391877300255
+      return Number(prefix)
+    }
+    if (prefix && prefix.length === 17) {
+      // utc: 20200513070724505
+      const year = prefix.substr(0, 4)
+      const month = prefix.substr(4, 2)
+      const date = prefix.substr(6, 2)
+      const hours = prefix.substr(8, 2)
+      const minutes = prefix.substr(10, 2)
+      const seconds = prefix.substr(12, 2)
+      const ms = prefix.substr(14)
+      return new Date(`${year}-${month}-${date}T${hours}:${minutes}:${seconds}.${ms}Z`).valueOf()
+    }
+  }
+  logger.error(`Can't determine timestamp for ${prefix}`)
+  return Number(prefix) || 0
+}
+
 export interface RunMigration {
   readonly path: string
   readonly name: string
   readonly timestamp: number
 }
 
+export enum FilenameFormat {
+  timestamp = 'timestamp',
+  utc = 'utc',
+}
+
 export class Migration implements RunMigration {
   // class method that creates a new migration file by cloning the migration template
-  static async create(name: string, directory: string, language?: 'js' | 'ts' | 'sql', ignorePattern?: string) {
+  static async create(
+    name: string,
+    directory: string,
+    language?: 'js' | 'ts' | 'sql',
+    ignorePattern?: string,
+    filenameFormat = FilenameFormat.timestamp,
+  ) {
     // ensure the migrations directory exists
     mkdirp.sync(directory)
 
     const suffix = language || (await getLastSuffix(directory, ignorePattern)) || 'js'
 
+    const now = new Date()
+    const time = filenameFormat === FilenameFormat.utc ? now.toISOString().replace(/[^\d]/g, '') : now.valueOf()
     // file name looks like migrations/1391877300255_migration-title.js
-    const newFile = `${directory}/${Date.now()}${SEPARATOR}${name}.${suffix}`
+    const newFile = `${directory}/${time}${SEPARATOR}${name}.${suffix}`
 
     // copy the default migration template to the new file location
     await new Promise((resolve, reject) => {
@@ -105,7 +133,7 @@ export class Migration implements RunMigration {
     this.db = db
     this.path = migrationPath
     this.name = path.basename(migrationPath, path.extname(migrationPath))
-    this.timestamp = Number(this.name.split(SEPARATOR)[0]) || 0
+    this.timestamp = getTimestamp(logger, this.name)
     this.up = up
     this.down = down
     this.options = options
diff --git a/src/operations/tables.ts b/src/operations/tables.ts
index b4582a0..deb9553 100644
--- a/src/operations/tables.ts
+++ b/src/operations/tables.ts
@@ -289,7 +289,7 @@ export function createTable(mOptions: MigrationOptions) {
     const inheritsStr = inherits ? ` INHERITS (${mOptions.literal(inherits)})` : ''
     const tableNameStr = mOptions.literal(tableName)
 
-    const createTableQuery = `CREATE TABLE${temporaryStr}${ifNotExistsStr} ${tableNameStr} (
+    const createTableQuery = `CREATE${temporaryStr} TABLE${ifNotExistsStr} ${tableNameStr} (
 ${formatLines(tableDefinition)}
 )${inheritsStr};`
     const comments = [...columnComments, ...constraintComments]
diff --git a/src/runner.ts b/src/runner.ts
index 4a2baa0..daee8b8 100644
--- a/src/runner.ts
+++ b/src/runner.ts
@@ -24,27 +24,29 @@ const loadMigrations = async (db: DBConnection, options: RunnerOption, logger: L
   try {
     let shorthands: ColumnDefinitions = {}
     const files = await loadMigrationFiles(options.dir, options.ignorePattern)
-    return Promise.all(
-      files.map(async (file) => {
-        const filePath = `${options.dir}/${file}`
-        const actions: MigrationBuilderActions =
-          path.extname(filePath) === '.sql'
-            ? await migrateSqlFile(filePath)
-            : // eslint-disable-next-line global-require,import/no-dynamic-require,security/detect-non-literal-require
-              require(path.relative(__dirname, filePath))
-        shorthands = { ...shorthands, ...actions.shorthands }
-        return new Migration(
-          db,
-          filePath,
-          actions,
-          options,
-          {
-            ...shorthands,
-          },
-          logger,
-        )
-      }),
-    )
+    return (
+      await Promise.all(
+        files.map(async (file) => {
+          const filePath = `${options.dir}/${file}`
+          const actions: MigrationBuilderActions =
+            path.extname(filePath) === '.sql'
+              ? await migrateSqlFile(filePath)
+              : // eslint-disable-next-line global-require,import/no-dynamic-require,security/detect-non-literal-require
+                require(path.relative(__dirname, filePath))
+          shorthands = { ...shorthands, ...actions.shorthands }
+          return new Migration(
+            db,
+            filePath,
+            actions,
+            options,
+            {
+              ...shorthands,
+            },
+            logger,
+          )
+        }),
+      )
+    ).sort((m1, m2) => m1.timestamp - m2.timestamp)
   } catch (err) {
     throw new Error(`Can't get migration files: ${err.stack}`)
   }
diff --git a/src/sqlMigration.ts b/src/sqlMigration.ts
index f45543a..804604f 100644
--- a/src/sqlMigration.ts
+++ b/src/sqlMigration.ts
@@ -1,8 +1,7 @@
 import fs from 'fs'
-import { promisify } from 'util'
 import { MigrationBuilderActions } from './types'
 
-const readFile = promisify(fs.readFile) // eslint-disable-line security/detect-non-literal-fs-filename
+const { readFile } = fs.promises
 
 const createMigrationCommentRegex = (direction: 'up' | 'down') =>
   new RegExp(`^\\s*--[\\s-]*${direction}\\s+migration`, 'im') // eslint-disable-line security/detect-non-literal-regexp


Test patch:
diff --git a/test/migration-test.ts b/test/migration-test.ts
index 63018ed..9f7701f 100644
--- a/test/migration-test.ts
+++ b/test/migration-test.ts
@@ -2,7 +2,7 @@
 import sinon, { SinonSpy } from 'sinon'
 import { expect } from 'chai'
 import { RunnerOption, Logger } from '../src/types'
-import { Migration } from '../src/migration'
+import { Migration, getTimestamp } from '../src/migration'
 import { DBConnection } from '../src/db'
 
 const callbackMigration = '1414549381268_names.js'
@@ -24,6 +24,18 @@ describe('lib/migration', () => {
     dbMock.query = queryMock
   })
 
+  describe('getTimestamp', () => {
+    it('Should get timestamp for normal timestamp', () => {
+      const now = Date.now()
+      expect(getTimestamp(logger, String(now))).to.eql(now)
+    })
+
+    it('Should get timestamp for shortened iso format', () => {
+      const now = new Date()
+      expect(getTimestamp(logger, now.toISOString().replace(/[^\d]/g, ''))).to.eql(now.valueOf())
+    })
+  })
+
   describe('self.applyUp', () => {
     it('normal operations: db.query should be called', () => {
       migration = new Migration(dbMock, callbackMigration, actionsCallback, options, {}, logger)
diff --git a/test/migrations/081_temporary_table.js b/test/migrations/081_temporary_table.js
new file mode 100644
index 0000000..afd69cd
--- /dev/null
+++ b/test/migrations/081_temporary_table.js
@@ -0,0 +1,18 @@
+exports.comment = 'comment on table t2'
+
+exports.up = (pgm) => {
+  pgm.createTable('tmp', { id: 'id' }, { temporary: true })
+}
+
+exports.down = async (pgm) => {
+  await pgm.db.query('SAVEPOINT sp_temp_table;')
+  try {
+    await pgm.db.query('DROP TABLE "tmp"')
+    throw 1 // eslint-disable-line no-throw-literal
+  } catch (err) {
+    if (err === 1) {
+      throw new Error('Missing TEMPORARY clause')
+    }
+    await pgm.db.query('ROLLBACK TO SAVEPOINT sp_temp_table;')
+  }
+}