Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

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

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

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.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                //- タイムゾーン
mixin timeOptions()
  option(value="14") UTC +14
  option(value="13") UTC +13
  option(value="12.75") UTC +12:45
  option(value="12") UTC +12
  option(value="11") UTC +11
  option(value="10.5") UTC +10:30
  option(value="10") UTC +10
  option(value="9.5") UTC +9:30
  option(value="9") UTC +9
  option(value="8.75") UTC +8:45
  option(value="8.5") UTC +8:30
  option(value="8") UTC +8
  option(value="7") UTC +7
  option(value="6.5") UTC +6:30
  option(value="6") UTC +6
  option(value="5:75") UTC +5:45
  option(value="5.5") UTC +5:30
  option(value="5") UTC +5
  option(value="4.5") UTC +4:30
  option(value="4") UTC +4
  option(value="3.5") UTC +3:30
  option(value="3") UTC +3
  option(value="2") UTC +2
  option(value="1") UTC +1
  option(value="0" selected) UTC +0
  option(value="-1") UTC -1
  option(value="-2") UTC -2
  option(value="-3") UTC -3
  option(value="-3.5") UTC -3:30
  option(value="-4") UTC -4
  option(value="-5") UTC -5
  option(value="-6") UTC -6
  option(value="-7") UTC -7
  option(value="-8") UTC -8
  option(value="-9") UTC -9
  option(value="-9.5") UTC -9:30
  option(value="-10") UTC -10
  option(value="-11") UTC -11
  option(value="-12") UTC -12


//- 時間のフォーマット
mixin timeFormatOptions
  option(value="Y/M/D H:mm:ss") 2008/5/1 2:00:00
  option(value="ddd, D MMM YYYY HH:mm:ss ZZ") Thu, 1 May 2008 02:00:00 +0900
  option(value="ddd MMM D HH:mm:ss ZZ YYYY") Thu May 1 02:00:00 +0900 2008
  option(value="ddd MMM DD YYYY HH:mm:ss [GMT]ZZ") Thu May 01 2008 02:00:00 GMT+0900
  option(value="X") Unix timestamp
  option(value="x") Unix ms timestamp


.wrapper

  //- 入力
  .cell.cell__input.has-background-white
    .cell__head
      label.label.has-text-grey.is-size-6.is-marginless Time
      
      .cell__action
            
    .field
      .control.has-icons-left
        input.input.is-medium#convert-from(type="text", placeholder="input time")
        span.icon.is-left
          i.far.fa-clock

    //- タイムゾーンの設定
    .options
      .select.is-small.is-rounded
        select.input-time-diff
          +timeOptions()
          
      a(href="#").now.button.is-small.is-outlined.is-rounded Now
      
    .help
      label.label.is-size-7 e.g.
      p.is-size-7 2016/12/31 1:23:45
      p.is-size-7 2016, 12, 31 1:23:45 PM
      p.is-size-7 Dec 31, 2016 1:23:45
      p.is-size-7 1483115025
      p.is-size-7 1483115025000
        
  //- 出力
  .cell.has-background-white-ter
    .cell__head
      label.label.has-text-grey.is-size-6.is-marginless Format changed

      .cell__action
        //- button.button.has-text-grey.is-light#result-copy(title="コピー")
        //-   span.icon
        //-     i.fas.fa-copy
  
    //- 結果の表示
    p.result.is-size-3
      //| 2016/11/18(Fri) 21:27:38
      
    .options
      .select.is-small.is-rounded
        select.output-time-diff.has-background-white-ter
          +timeOptions()

      .select.is-small.is-rounded
        select.output-time-format.has-background-white-ter
          +timeFormatOptions()
              
            
!

CSS

              
                // ブレイクポイント
$grid-breakpoints: (
  xs: 0,
  sm: 576px,
  md: 768px,
  lg: 992px,
  xl: 1200px
);


body {
  
  @media (min-width: map_get($grid-breakpoints, md) + 1px) {
    margin: 20px;
  }
}


// コンテナ
.wrapper {
  display: flex;
  
  @media (max-width: map_get($grid-breakpoints, md)) {
    flex-direction: column;
  }
  
  @media (min-width: map_get($grid-breakpoints, md) + 1px) {
    
  }
}

// セル
.cell {
  border: solid 1px #e0e0e0;
  padding: 1rem;
  
  @media (max-width: map_get($grid-breakpoints, md)) {
    width: 100%;
    
    & + .cell {
      border-top: none;
    }
  }
  
  @media (min-width: map_get($grid-breakpoints, md) + 1px) {
    width: 50%;
    
    & + .cell {
      border-left: none;
    }
  }
}


// 入力
.cell__input {
  background-color: #fff;
}


// セルのヘッダー
.cell__head {
  display: flex;
  min-height: 2.5rem; // 高さを揃えるため
  align-items: center;
  margin-bottom: 1rem;
}


// 結果
p.result {
  margin-bottom: 1rem;
}


// セルのアクション
.cell__action {
  margin-left: auto;
  
  .button.is-text {
    text-decoration: none;
  }
}


// オプション項目
.options {
  display: flex;
  flex-wrap: wrap;
  
  > * {
    &:not(:last-child) {
      margin-right: 0.5rem;
      margin-bottom: 0.5rem;
    }
  }
}
              
            
!

JS

              
                let mediator = _.extend({}, Backbone.Events);

namespace EVENT {
  SUBMIT: 'submit'
};


class TimeModel extends Backbone.Model {
  
  inputTime: string; // 入力された時間
  inputTimeDiff: number;  // 入力された時間の時差
  inputTimeMoment: Moment;  // inputTimeとinputTimeDiffから生成したMoment←CodePenだと型定義はこれで大丈夫なん??
  outputTime: number; // 出力する時間(ミリ秒)
  outputTimeDiff: number; // 出力する時間の時差
  outputTimeMoment: Moment; // outputTimeとoutputTimeDiffから生成したMoment←CodePenだと型定義はこれで大丈夫なん??
  outputTimeFormat: string|null; // 出力する時間のフォーマット
  isValid: boolean; // 入力された時間が日時のフォーマットになっているか
  
  initialize (attributes?: any, options?: any) {
    _.bindAll(this,
      "updateTime"
    );
    
    this.listenTo(this, "change:inputTime", this.updateTime);
    this.listenTo(this, "change:inputTimeDiff", this.updateTime);
    this.listenTo(this, "change:outputTimeDiff", this.updateTime);
    
    this.updateTime();
    
    // // TEST
    // this.listenTo(this, 'change', function () {
    //   console.log(this.get('inputTime'), this.get('inputTimeDiff'), this.get('outputTimeMoment').format('YYYY/MM/DD HH:mm:ss'), this.get('outputTime'), this.get('outputTimeDiff'));
    // });
  }
  
  defaults() {
    return {
      inputTime: 0,
      isValidDate: true
    }
  }
  
  updateTime(): void {
    const inputTime = this.get('inputTime').trim();
    const inputTimeDiff = this.get('inputTimeDiff');
    let format = '';  // 時刻フォーマット
    
    if (inputTime.match(/^(-?\d{0,11}|\d{0,11}\.\d{3})$/)) {
      // unix timestamp
      format = 'X';
    }
    else if (inputTime.match(/^\d{0,13}$/)) {
      // unix ms timestamp
      format = 'x';
    }
    
    // unix timestampの場合utcOffsetが効かない?
    const inputTimeMoment = moment(inputTime, format).utcOffset(inputTimeDiff, 'hours');
    const outputTimeDiff = this.get('outputTimeDiff');
    const outputTimeMoment = inputTimeMoment.utcOffset(outputTimeDiff * 60);
    
    this.set({
      outputTime: parseInt(outputTimeMoment.format('x')),
      outputTimeMoment,
      isValid: outputTimeMoment.isValid()
    });
  }
}



/**
 * 入力フォーム
 *
 */
class InputFormController extends Backbone.View<TimeModel> {

  initialize (options?: Backbone.ViewOptions<TimeModel>) {
    _.bindAll(this,
      'onSubmit'
    );
  }
  
  events(): Backbone.EventsHash {
    return {
      "submit form": this.onSubmit
    }
  }
  
  onSubmit (e: Event): void {
    e.preventDefault();
    mediator.trigger(EVENT.SUBMIT);
  }
  
}


class InputFieldView extends Backbone.View<TimeModel> {
  
  initialize (options?: Backbone.ViewOptions<TimeModel>) {
    _.bindAll(this,
      'onChangeValid'
    );
    
    this.listenTo(this.model, 'change:isValid', this.onChangeValid);
  }

  onChangeValid (): void {
    if (this.model.get('isValid') === true) {
      this.$el.removeClass('has-error');
    }
    else {
      this.$el.addClass('has-error');
    }
  }
}


/**
 * 時間入力フォーム
 *
 */
class InputTimeController extends Backbone.View<TimeModel> {

  initialize (options?: Backbone.ViewOptions<TimeModel>) {
    _.bindAll(this,
      'onChange'
    );
    
    this.listenTo(mediator, EVENT.SUBMIT, this.onChange); // 送信された
  }
  
  events(): Backbone.EventsHash {
    return {
      "change": this.onChange
    }
  }
  
  onChange (): void {
    this.model.set({
      inputTime: this.$el.val()
    });
  }
  
}

/**
 * 時間入力フォーム
 *
 */
class InputTimeView extends Backbone.View<TimeModel> {

  initialize (options?: Backbone.ViewOptions<TimeModel>) {
    _.bindAll(this,
      'onUpdate'
    );
    
    this.listenTo(this.model, 'change:inputTime', this.onUpdate);
  }
  
  onUpdate (): void {
    const inputTime = this.model.get('inputTime');
  
    // 入力値とmodelの値に差があった場合、modelの値に合わせる
    if (inputTime !== this.$el.val()) {
      this.$el.val(inputTime);
    }
  }
  
}


/**
 * 現在時刻を選択
 *
 */
class TimeNowController extends Backbone.View<TimeModel> {
  
  initialize (options?: Backbone.ViewOptions<TimeModel>) {
    _.bindAll(this,
      'onClick'
    );
  }
  
  events(): Backbone.EventsHash {
    return {
      "click": this.onClick
    }
  }
  
  onClick (e): void {
    e.preventDefault();
    const now = getNow();
    
    this.model.set({
      inputTime: now.time,
      inputTimeDiff: now.diff
    });
  }
}


/**
 * 時差の選択
 *
 */
class TimeDiffController extends Backbone.View<TimeModel> {
  
  target: string; // 'input' or 'output'
  
  initialize (options?: Backbone.ViewOptions<TimeModel>) {
    this.target = options.target;
  
    _.bindAll(this,
      'onChange'
    );
  
    if (this.target === 'input') {    
      this.listenTo(mediator, EVENT.SUBMIT, this.onChange); // 送信された
    }
  }
  
  
  events(): Backbone.EventsHash {
    return {
      "change": this.onChange
    }
  }
  
  onChange (): void {
    const value = parseFloat(this.$el.val());
    
    if (this.target === 'input') {
      this.model.set({
        inputTimeDiff: value
      });
    }
    else if (this.target === 'output') {
      this.model.set({
        outputTimeDiff: value
      });
    }
  }
  
}


/**
 * 時差の選択
 *
 */
class TimeDiffView extends Backbone.View<TimeModel> {
  
  target: string; // 'input' or 'output'
  param: string;  // 対象のmodelの値
  
  initialize (options?: Backbone.ViewOptions<TimeModel>) {
    this.target = options.target;
  
    _.bindAll(this,
      'onUpdate'
    );
  
    let param: string; // listenToする変数名
    if (this.target === 'input') {
      param = 'inputTimeDiff';
    }
    else {
      param = 'outputTimeDiff';
    }

    this.param = param;
    this.listenTo(this.model, `change:${this.param}`, this.onUpdate);
  }
  
  onUpdate (): void {
    const value = this.model.get(this.param);
    
    // 入力値とmodelの値に差があった場合、modelの値に合わせる
    if (parseFloat(this.$el.val()) !== value) {
      this.$el.val(value);
    }
  }
  
}


class OutputTimeView extends Backbone.View<TimeModel> {

  initialize (options?: Backbone.ViewOptions<TimeModel>) {
    _.bindAll(this,
      'render'
    );
    
    this.listenTo(this.model, 'change:outputTime', this.render);
    this.listenTo(this.model, 'change:outputTimeDiff', this.render);
    this.listenTo(this.model, 'change:outputTimeFormat', this.render);
    
    this.render();
  }
  
  render(): void {
    const time = this.model.get('outputTimeMoment')
    const format = this.model.get('outputTimeFormat');
  
    this.$el.html(time.format(format));
  }
  
}


/**
 * 出力時間の形式
 *
 */
class TimeFormatView extends Backbone.View<TimeModel> {
  
  initialize (options?: Backbone.ViewOptions<TimeModel>) {
    _.bindAll(this,
      'onChange'
    );
  }
  
  
  events(): Backbone.EventsHash {
    return {
      "change": this.onChange
    }
  }
  
  onChange (): void {
    this.model.set({
      outputTimeFormat: this.$el.val()
    });
  }
  
}


/**
 * 現在時間と時差を取得
 *
 * @return .time 現在時間
 * @return .diff 時差
 */
function getNow(): string[] {
  const now = new Date();
  const currentTimeZoneOffsetInHours = now.getTimezoneOffset() / -60; // 現在時間の時差
  
  return {
    time: now.toString().replace(/\sGMT[^$]+/i, ''),
    diff: currentTimeZoneOffsetInHours
  };
}


{
  const $convertFrom = $('#convert-from');  // 時間入力フォーム
  const $inputTimeDiff = $('.input-time-diff'); // 入力時間の時差
  const $outputTimDiff = $('.output-time-diff');  // 出力時間の時差
  const $outputTimeFormat = $('.output-time-format');  // 出力時間の形式
  const now = getNow(); // 現在時間と時差
  
  // 入力時間に現在の時間を設定
  $convertFrom.val(now.time);  // GMT〜を削除したローカル時間
  $inputTimeDiff.val(now.diff);
  
  // TODO 入力時間の時差を設定する

  const timeModel = new TimeModel({
    inputTime: $convertFrom.val(),
    inputTimeDiff: parseFloat($inputTimeDiff.val()),
    outputTimeDiff: parseFloat($outputTimDiff.val()),
    outputTimeFormat: $('.output-time-format').val()
  });

  // TODO cookieの設定で、時差、出力後のフォーマットを設定する

  new InputFormController({
    model: timeModel,
    el: '.input-field form'
  });
  
  new InputFieldView({
    model: timeModel,
    el: $('.input-field').find('.input-group').get(0)
  });
  
  new InputTimeController({
    model: timeModel,
    el: $convertFrom.get(0)
  });
  
  new InputTimeView({
    model: timeModel,
    el: $convertFrom.get(0)
  });

  new TimeDiffController({
    model: timeModel,
    el: $inputTimeDiff.get(0),
    target: 'input'
  });

  new TimeDiffView({
    model: timeModel,
    el: $inputTimeDiff.get(0),
    target: 'input'
  });
  
  new TimeNowController({
    model: timeModel,
    el: '.now'
  });

  new OutputTimeView({
    model: timeModel,
    el: '.result'
  });

  new TimeDiffController({
    model: timeModel,
    el: $outputTimDiff.get(0),
    target: 'output'
  });

  new TimeDiffView({
    model: timeModel,
    el: $outputTimDiff.get(0),
    target: 'output'
  });
  
  new TimeFormatView({
    model: timeModel,
    el: $outputTimeFormat.get(0)
  });


  // 入力エリアにフォーカスを設定する
  // document.getElementById('convert-from').focus();
}
              
            
!
999px

Console