css Audio - Active file-generic CSS - Active Generic - Active HTML - Active JS - Active SVG - Active Text - Active file-generic Video - Active header Love html icon-new-collection icon-person icon-team numbered-list123 pop-out spinner split-screen star tv

Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

You're using npm packages, so we've auto-selected Babel for you here, which we require to process imports and make it all work. If you need to use a different JavaScript preprocessor, remove the packages in the npm tab.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Use npm Packages

We can make npm packages available for you to use in your JavaScript. We use webpack to prepare them and make them available to import. We'll also process your JavaScript with Babel.

⚠️ This feature can only be used by logged in users.

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

HTML Settings

Here you can Sed posuere consectetur est at lobortis. Donec ullamcorper nulla non metus auctor fringilla. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.

            
              header
  h1 Generate date&time random

#app
  .results
    .section-header.section-header__results
      h2 Results
      
      //- コピー
      button.button.is-rounded.is-small.copy-results.is-text(:data-clipboard-text="copyAllResults", title="copy")
        span.icon.is-small
          i.fas.fa-copy
      
      //- 再生成
      button.button.is-rounded.is-small.copy-results.is-text(@click.prevent="generateTimes", :data-clipboard-text="copyAllResults", title="generate")
        span.icon.is-small
          i.fas.fa-sync-alt

      .section-header-menu
        .select.is-rounded.is-small
          select(v-model="options.sort")
            option(value="random") random
            option(value="newest") newest
            option(value="oldest") oldest
      
        .select.is-rounded.is-small
          select(v-model="options.num")
            option(value="10") 10
            option(value="25") 25
            option(value="50") 50
            option(value="100") 100
            option(value="250") 250
            option(value="500") 500
          
    table.table.is-fullwidth
      thead
        tr
          th #
          th time
      tbody
        tr(v-for="time, index in results")
          td.index {{ index + 1 }}
          td {{ time }}
      
      
  .options
    .section-header
      h2 Options
    
    //- 時間のフォーマット
    div.field
      label.label Format
      
      .options-item
        input.input(type="text", placeholder="YYYY/MM/DD HH:mm:ss", v-model="options.format")
      
        ul.help
          li: a.button.is-text.is-small(@click.prevent="changeFormat('YYYY/MM/DD HH:mm')") 2016/12/31 01:23
          li: a.button.is-text.is-small(@click.prevent="changeFormat('YYYY/MM/DD')") 2016/12/31
          li: a.button.is-text.is-small(@click.prevent="changeFormat('HH:mm')") 16:23
          li: a.button.is-text.is-small(@click.prevent="changeFormat('YYYY/MM/DD (ddd) HH:mm:ss')") 2016/12/31 (Feb) 01:23:45
          li: a.button.is-text.is-small(@click.prevent="changeFormat('dddd, MMMM Do YYYY, h:mm:ss a')") Feburary, December 31th 2016, 1:23:45 am
          li: a.button.is-text.is-small(@click.prevent="changeFormat('H:mm:ss.SSS')") 16:23:45.012
          
          li: a.button.is-text.is-small(@click.prevent="changeFormat('X')") Unix Timestamp
          li: a.button.is-text.is-small(@click.prevent="changeFormat('x')") Unix Millisecond Timestamp
          
          li: a.button.is-text.is-small(@click.prevent="changeFormat('Qo')") Quarter
          li: a.button.is-text.is-small(@click.prevent="changeFormat('DDDo')") Day of Year
          li: a.button.is-text.is-small(@click.prevent="changeFormat('Wo')") Week of Year
          
    //- 範囲
    div.field
      label.label Range
      
      .options-item
        .columns.is-mobile.is-variable.is-1
          .column
            .input-date
              label.label.is-small start
              input.input.is-small(placeholder="YYYY/MM/DD", v-model="options.range.start", :class="{ 'is-danger': range.start.isValid === false }")
          .column
            .input-date
              label.label.is-small end
              input.input.is-small(placeholder="YYYY/MM/DD", v-model="options.range.end", :class="{ 'is-danger': range.end.isValid === false }")
             
        //- プリセット
        ul.help
          li: a.button.is-text.is-small(@click.prevent="changeRange('today')") today
          li: a.button.is-text.is-small(@click.prevent="changeRange('this week')") this week
          li: a.button.is-text.is-small(@click.prevent="changeRange('this month')") this month
          li: a.button.is-text.is-small(@click.prevent="changeRange('this quarter')") this quarter
          li: a.button.is-text.is-small(@click.prevent="changeRange('this year')") this year
      
    //- 結果から除外する日時
    div.field
      label.label Not (date or time)
      
      .options-item
        .columns.is-mobile.is-variable.is-1(v-for="(item, index) in options.not")
          .column
            .input-date
              label.label.is-small start
              input.input.is-small(placeholder="YYYY/MM/DD or HH:mm", v-model=`item.start`, :class="{ 'is-danger': not[index].isInput && not[index].isValid === false }")
          .column
            .input-date
              label.label.is-small end
              input.input.is-small(placeholder="YYYY/MM/DD or HH:mm", v-model=`item.end`, :class="{ 'is-danger': not[index].isInput && not[index].isValid === false }")
              
script(defer, src="https://use.fontawesome.com/releases/v5.3.1/js/all.js")

            
          
!
            
              $sidebar-width: 260px;

html {
  background-color: #f5f5f5;
}

body {
  margin: 20px;
}

header {
  padding-top: 2rem;
  padding-bottom: 2rem;
  
  h1 {
    font-size: 1.6rem;
    font-weight: bold;
  }
}

#app {
  width: 100%;
  display: flex;
  align-items: flex-start;
}


// オプション設定
.options {
  display: flex;
  width: $sidebar-width;
  flex-wrap: wrap;
  flex-direction: column;
  border-radius: 6px;
  
  .field {
    &:not(:last-of-type) {
      margin-bottom: 2rem;
    }
  }
  
  .label.is-small {
    color: #757575;
  }
}

.options-item {
  padding: 10px;
  background-color: #e0e0e0;
  border-radius: 2px;
  
  &:not(:last-of-type) {
    margin-bottom: 1rem;
  }
}

// 結果
.results {
  width: calc(100% - #{$sidebar-width});
  box-sizing: border-box;
  margin-right: 20px;
  
  .index {
    width: 2rem;
    color: #aaaaaa;
  }
}


.section-header {
  display: flex;
  padding-top: 0.6rem;
  padding-bottom: 0.6rem;
  
  h2 {
    font-weight: bold;
  }
}


.section-header__results {
  position: sticky;
  top: 0;
  background-color: #f5f5f5;
  
  h2 {
    margin-right: 1rem;
  }
  
  * {
    &:nth-child(4) {
      // 右寄せ
      margin-left: auto;
    }
  }
}
            
          
!
            
              // デフォルトの範囲設定
const end = moment();
const start = end.subtract(1, 'years');

// 時間、日付の正規表現
const timeFormat = new RegExp(/^\s*\d{1,2}\s*:\s*\d{1,2}\s*[\.|:]?\d+$/);
const dateFormat = new RegExp(/^\s*\d{1,2}\s*\/\s*\d{1,2}$/);

// TODO optionsをcookieに保存する?
// TODO 含まない期間を指定できるようにする(複数のstart, end)
// 時間だけのとき、期間のときがある

const state = {
  times: [],  // 生成した年月日
  
  // 設定(入力 or 選択される)
  options: {
    num: 10,
    sort: 'random',
    format: 'YYYY/MM/DD HH:mm',
    range: {
      // とりあえず今年にしておく
      start: moment().startOf('year').format('YYYY/M/D'),
      end: moment().endOf('year').format('YYYY/M/D')
    },
    // 結果から除外する日時
    not: [
      { start: null, end: null },
      { start: null, end: null },
      { start: null, end: null }
    ]
  }
};


/**
 * 有効な範囲を取得する
 *
 * @param range { start: number, end: number } 日時の範囲
 * @param not { start: number, end: number }[] 含めない範囲
 * @return { start: number, end: number }[] 有効な範囲のリスト
 */
function effectiveRange (range, not) {
  let results = [
    { start: range.start, end: range.end }
  ];
    
  // notの範囲を削除していく
  // notは昇順になっているのが前提
  not.forEach((notItem) => {
    for (let i = 0, len = results.length; i < len; i++) {
      let result = results[i];
      
      if (result !== null) {
        if (notItem.start <= result.start && result.end <= notItem.end) {
          // すべての範囲がnot
          // 削除
          results[i] = null;
        }
        else if (notItem.start <= result.start && result.start <= notItem.end) {
          // 前半がnot
          // notで分割する
          results[i] = {
            start: notItem.end + 1,
            end: result.end
          };
        }
        else if (notItem.start <= result.end && result.end <= notItem.end) {
          // 後半がnot
          results[i] = {
            start: result.start,
            end: notItem.start - 1
          };
        }
        else if (result.start <= notItem.start && notItem.end <= result.end)  {
          // 途中がnot
          
          // 分割する
          results[i] = {
            start: result.start,
            end: notItem.start - 1
          };
          
          const div = {
            start: notItem.end + 1,
            end: result.end
          };
          
          results.splice(i + 1, 0, div);
        }
      }
    }
  });
  
  // console.log('effective range', results);
  
  // nullの要素を削除する
  return _.compact(results);
}


/**
 * 指定の時間がnotに含まれているかを判別
 *
 * @param time {number} 時間(ミリ秒)
 * @param notTimes {{ start: number, end: number, isCrossOverDay: boolean }} Notの時間(ミリ秒)
 * @param {boolean} Notに含まれているか
 */
function isNotTime (time, notTimes) {
  let result = false;
  const len = notTimes.length;
  
  for (let i = 0; i < len; i++) {
    const item = notTimes[i];
    
    if (item.isCrossOverDay === false) {
      if (item.start <= time && time <= item.end) {
        // Notに含まれている
        result = true;
        break;
      }
    }
    else {
      // 0:00をまたぐ場合
      if ((item.start <= time && time < 86400000) || time <= item.end) {
        // Notに含まれている
        result = true;
        break;
      }
    }
  }
  
  return result;
}


new Vue({
  el: '#app',
  data: state,
  
  created: function () {
    this.generateTimes();
  },
  
  methods: {
    
    /**
     * 年月日を生成する
     *
     */
    generateTimes: function () {
      const times = [];
      
      // console.log('start', this.range.start, moment(this.range.start.date, 'x').format('YYYY/M/D HH:mm:ss'), 'end', this.range.end, moment(this.range.end.date, 'x').format('YYYY/M/D HH:mm:ss'));
      
      // 有効なRange(Range内の日時からNotを取り除いた)をリストにする
      // Notの種類が期間('period')のものを省く
      
      // 含まない期間を選択
      const notDates = this.not.filter((item) => {
        return item.type === 'period' ? { start: item.start, end: item.end } : false;
      });
      
      // 含まない時間を抽出して、比較しやすいフォーマットに変換する
      const notTimes = this.not.filter((item) => item.type === 'interval' ).map((item) => {
          const startTime = item.start.split(':');
          const endTime = item.end.split(':');
          const weight = [60 * 60 * 1000, 60 * 1000, 1000]; // ミリ秒に変換するため時間
          
          // HH:mm:ddをミリ秒に変換
          const start = startTime.map((item, index) => item * weight[index] ).reduce((prev, current) => prev + current);
          const end = endTime.map((item, index) => item * weight[index] ).reduce((prev, current) => prev + current);
          
          return {
            start,
            end,
            isCrossOverDay: end < start // 日をまたぐか(23:00 - 5:00のような場合)
          };
      });
      
      // {
      //   type: "date"
      //   isInput: true
      //   isValid: true
      //   start: 1514736072
      //   end: 1514769000
      // }
      
      // Notを省いた期間のリストを生成
      const rangeGroup = effectiveRange({
        start: this.range.start.date,
        end: this.range.end.date
      }, notDates);
      const numRangeGroup = rangeGroup.length; // 期間のグループ数
      
      
      // 表示の最大件数分つくっておく
      // 件数やフォーマットを変えたときに再生成させないため     
      for (let i = 0; i < 500; i++) {
        const hit = _.random(numRangeGroup - 1);  // どのグループから選ぶか
        
        let generated = null;
        
        while (generated === null) {
        // for (let j = 0; j < 10; j++) {
          generated = moment(_.random(rangeGroup[hit].start, rangeGroup[hit].end, false), 'x');

          // Notの時間だったら作り直す
          const time = Number(generated.format('H')) * 60 * 60 * 1000
          + Number(generated.format('m')) * 60 * 1000
          + Number(generated.format('s')) * 1000;
          
          const result = isNotTime(time, notTimes);
          
          // console.log(time, generated.format('H:m:s'), result);
          
          // 含めない時間 or 不正な日付だったら作り直す
          // Notに日付をまたぐ時間を指定したときに、invalidな日時になることがある?
          if (result === true || generated.isValid() === false) {
            generated = null;
          }
        }
        
        times.push(generated);
      }
      
      this.times = times;
    },
    
    changeFormat: function (format) {
      this.options.format = format;
    },
    
    /**
     * 範囲プリセットから変更する
     *
     * @param label {string} プリセット名
     */
    changeRange: function (label) {
      switch (label) {
        case 'today': // 今日
          this.options.range.start = moment().startOf('date').format('YYYY/M/D');
          this.options.range.end = moment().endOf('date').format('YYYY/M/D');
          break;
          
        case 'this week': // 今週
          this.options.range.start = moment().startOf('week').format('YYYY/M/D');
          this.options.range.end = moment().endOf('week').format('YYYY/M/D');
          break;
          
        case 'this month':  // 今月
          this.options.range.start = moment().startOf('month').format('YYYY/M/D');
          this.options.range.end = moment().endOf('month').format('YYYY/M/D');
          break;
          
        case 'this quarter':  // 今期
          this.options.range.start = moment().startOf('quarter').format('YYYY/M/D');
          this.options.range.end = moment().endOf('quarter').format('YYYY/M/D');
          break;
          
        case 'this year':  // 今年
          this.options.range.start = moment().startOf('year').format('YYYY/M/D');
          this.options.range.end = moment().endOf('year').format('YYYY/M/D');
          break;
      }
    }
  },
  
  computed: {
    
    /**
     * 表示するための時間を生成
     *
     */
    results: function () {
      // 件数分を指定の時間フォーマットにする
      const items = this.times.filter( (time, index) => index < this.options.num ).sort( (a, b) => {
        if (this.options.sort === 'newest') {
          // 新しい順
          return a === b ? 0 : a < b;
        }
        else if (this.options.sort === 'oldest') {
          // 古い順
          return a === b ? 0 : a > b;
        }
        
        return 0;
      });
      
      // ソート順を反映する
      
      return items.map( time => moment(time).format(this.options.format) );
    },
    
    /**
     * 期間の範囲を指定する
     *
     */
    range: function () {
      const start = moment(this.options.range.start);
      const end = moment(this.options.range.end);
      
      // endは日付指定のみ(時間未指定)の場合、その日いっぱいを含む
      if (end.isValid() && this.options.range.end.match(/^(\d{2,4}\s*\/)?\s*\d{1,2}\s*\/\s*\d{1,2}$/)) {
        end.endOf('day');
      }
      
      // 入力された時間の範囲をミリ秒に変換する
      // エラーだった場合は、
      return {
        start: {
          date: start.isValid() ? Number(start.format('x')) : Number(moment().startOf('day').format('x')),
          isValid: start.isValid()
        },
        end: {
          date: end.isValid() ? Number(end.format('x')) : Number(moment().endOf('day').format('x')),
          isValid: end.isValid()
        }
      }
    },
    
    /**
     * 除外する期間、時間を計算する
     *
     */
    not: function () {
      const result = this.options.not.map((item) => {
        // 入力された日時を補完する
        // 文字列からフォーマットを推測する
        // 開始、終了のどちらかが未入力なら、それ以前、それ以降と判定する
        // 日時がおかしい or フォーマットが不明な場合は無効にする
        
        let start = item.start; // 除外開始日時
        let end = item.end; // 除外終了日時
        const startDate = moment(item.start);
        let endDate = moment(item.end);
        
        // 日時 or 時間のみを判定する
        
        // 書式から判定する
        let type = null;
        let isValid = true;
        let isInput = false;
        
        // console.log(startDate.format('YYYY/M/D H:mm:ss.SSS'));
        // console.log(endDate.format('YYYY/M/D H:mm:ss.SSS'));
        
        if (item.start !== null && item.start !== '' && item.end !== null && item.end !== '') {
          isInput = true;
          
          // console.log('---');
          // console.log(item.start.match(timeFormat), item.end.match(timeFormat));
          // console.log(startDate.isValid(), endDate.isValid());
          // console.log(startDate.isValid(), item.end.match(timeFormat));
          
          if (item.start.match(timeFormat) && item.end.match(timeFormat)) {
            // start, endが時間
            type = 'interval';
            start = item.start;
            end = item.end;
          }
          else if (startDate.isValid() && endDate.isValid()) {
            // start, endが日時
            type = 'period';
            
            // 開始と終了をミリ秒で保持
            start = Number(startDate.format('x'));
            end = Number(endDate.endOf('day').format('x'));  // その日いっぱいをNotにする
            
            if (start >= end) {
              isValid = false;
            }
          }
          else if (startDate.isValid() && item.end.match(timeFormat)) {
            // startが日時、endが時間
            // ex. 2018/1/1, 12:34
            type = 'period';
            
            // 開始と終了をミリ秒で保持
            start = Number(startDate.format('x'));
            // end = // 終了時間を計算する
            endDate = startDate.clone();
            
            // startDateとendを組み合わせて、終了時間を生成する
            // →時間だけ反映する
            const endTime = end.split(/:|\./i).map( (num) => Number(num));
            endDate.hours(endTime[0] | 0);
            endDate.minutes(endTime[1] | 0);
            endDate.seconds(endTime[2] | 0);
            endDate.milliseconds(endTime[3] | 0);
            
            end = Number(endDate.format('x'));
              
            if (start >= end) {
              isValid = false;
            }
          }
          else if (startDate.isValid() && item.end.match(dateFormat)) {
            // startが日時、endが日付
            // ex. 2018/1/1, 1/10
            type = 'period';
            
            // 開始と終了をミリ秒で保持
            start = Number(startDate.format('x'));
            // end = // 終了時間を計算する
            end = Number(moment(`${startDate.format('YYYY')}/${item.end}`).endOf('day').format('x')); // その日いっぱいをNotにする
              
            if (start >= end) {
              isValid = false;
            }
          }
          else {
            isValid = false;
          }
        }
        
        // console.log('type', type, 'isValid', isValid, 'start', start, 'end', end);
        
        return {
          start,
          end,
          type,
          isValid,
          isInput
        };
      });
      
      // console.log('not', result);
      
      return result;
    },
    
    /**
     * 結果をコピーできる文字列を取得
     *
     */
    copyAllResults: function () {
      return this.results.join('\n');
    }
  },
  
  watch: {
    // 範囲が変更されたら生成しなおす
    'range.start.date': function () {
      this.generateTimes();
    },
    
    // 範囲が変更されたら生成しなおす
    'range.end.date': function () {
      this.generateTimes();
    },
    
    // 除く日時が更新されたら生成しなおす
    'not': function () {
      this.generateTimes();
    }
  }
});


new ClipboardJS('.copy-results');
            
          
!
999px
🕑 One or more of the npm packages you are using needs to be built. You're the first person to ever need it! We're building it right now and your preview will start updating again when it's ready.
Loading ..................

Console