2026年1月30日金曜日

日時のシリアル値をタイムゾーンのオフセットありの書式へ変換するマクロ

タイムゾーンを指定して日時の書式を設定

タイムゾーンを指定して E8601DXの書式でフォーマットするマクロです。入力は日時のシリアル値、結果は macvar で指定した変数に返ります。

  • 入力のパラメータ DT=2085402932.92693, TZ='America/New_York'
  • 結果 2026-01-30T00:35:33-05:00
SASシステムオプションでTZ変えて書式整えれば良いのですが、システム的に変更を禁止されている場合があります。あと、なんかテストしていて正しく書式が定義されないので自分でロジックを組んでしまったのです。
最近この日時の書式を使うようにしています。理由は海外のエンジニアにログとかメトリクスの抽出を依頼するときに、タイムゾーンがはっきりと分かる方が便利だからです。

作成したマクロ  


%macro formatSerialDateTime(dt=, tz=, macvar=, debug=0);
  %let &macvar=;
  %local notes tmp;

  /* Suppress NOTE logs */ 
  %let notes=%sysfunc(getoption(notes));
  options nonotes;
  
  /* Set default value */
  %if %length(&dt)=0 %then %let dt=%sysfunc(datetime());
  
  %let tmp=; 
  data _null_;
    attrib result length=$64;
    attrib dt length=8 label='datetime';
    attrib tz length=$32 label='timezone';
    attrib offset1 length=8 label='Default offset';
    attrib offset2 length=8 label='Time zone offset specified by the parameter';

    /* Remove quotation marks from parameters */    
    dt = dequote(symget('dt'));
    tz = dequote(symget('tz'));
 
    /* Calculate the offset time according to the time zone */
    offset1 = tzoneoff();
    if missing(tz) then offset2 = offset2;
    else offset2 = tzoneoff(tz);
    
    /* Once the time zone is correctly obtained, set the date and time. */
    if not missing(offset2) then do;
    
      dt = dt + (offset2 - offset1);
  
      /* Set the offset sign */    
      if offset2<0 then sign= "-";
      else sign="+";
      
      /* Construct the result string and set it to the macro variable. */
      length year month day hour minute second hh mm 8;
      length offset $8;
      year = year(datepart(dt));
      month = month(datepart(dt));
      day = day(datepart(dt));
      hour = hour(dt);
      minute = minute(dt);
      second = second(dt);
      hh = hour(abs(offset2));
      mm = minute(abs(offset2));
      
      ymd = cats(put(year,z4.), '-', put(month, z2.), '-', put(day,z2.));
      hms = cats(put(hour,z2.), ':', put(minute,z2.), ':', put(second,z2.));
      offset = cats(sign, put(hh,z2.), ':', put(mm,z2.));
      
      result = cats(ymd, 'T', hms, offset);
    end;
    
    call symputx('tmp', compress(result));
  run;  
  %if &syserr=0 %then %let &macvar=&tmp;

  options ¬es;
  %if &debug=1 %then %put DEBUG: formatSerialDateTime(&=dt, &=tz, &=macvar, &=debug) &macvar=&&&macvar;
%mend;

%macro test_foo;
  %local now;
  %let now=%sysfunc(datetime());

  %let result=;
  %formatSerialDateTime(dt=&now, tz="Asia/Tokyo", macvar=result, debug=1);
  %put &=result;
  
  %let result=;
  %formatSerialDateTime(dt=&now, tz='America/New_York', macvar=result, debug=1);
  %put &=result;

  %let result=;
  %formatSerialDateTime(dt=&now, tz='Europe/Istanbul', macvar=result, debug=1);
  %put &=result;

  %let result=;
  %formatSerialDateTime(dt=&now, tz='Asia/Kolkata', macvar=result, debug=1);
  %put &=result;

  %let result=;
  %formatSerialDateTime(dt=&now, tz='UTC', macvar=result, debug=1);
  %put &=result;
%mend;

options nomprint nosource nomlogic nosymbolgen;
%test_foo;




2026年1月17日土曜日

よく使うコードの書き方をスニペットに登録して時間節約

何故 Snippet を使うのか?

よく使う割に書き方を忘れてしまうことがあります。そんなときにSAS Studioのスニペットが役立つということを遅ればせながら実感しました。スニペットは昔からある機能ですが、何故か敬遠というか存在を無視して使っていませんでした。

しかし、頻発する「あれはどうやって書くのか?」という度に検索したり、自分のBlogを読み返したりするのは時間が惜しくなってきました。で、典型例を探して自分のスニペットに登録し始めました。

いまボランティアワークでPrometheusからデータを取得する仕組みを作っています。SAS Viya Monitoring for Kubernetes からメトリクスを取得してシステム管理に役立つ何かを得ようとしています。そんな中で開発をちょっと効率化したいと思ったわけです。

私が登録しているSnippet

参考までに私が登録しているマクロを示します。
  • マクロ変数の値から引用符を取り除く
  • マクロのパラメータで値が無い項目をチェックしてエラーメッセージ出力
  • テンポラリのファイル参照名を定義
  • ファイル参照名が定義されているか判定してクリア
  • その他、PROC HTTPの例とか追加する予定

感じるメリット

書いてみると分かるのですが、諳んじて書くのと注意してコメントまで入れてロジックの穴を点検するのでは気づきがあります。例えばエラー処理で %goto でマクロの終端にジャンプするというのは統一していると方がより良いです。

また分かりやすいコメントも含めておくとより良いです。最近はCopilotに相談しながら英語圏の人でも分かりやすいコメントを入れるようにしています。急いで書くと不自然なコメントになってしまいます。

コードの例  

/* Remove quotation marks from parameter values */
%let macvar=%sysfunc(dequote(%superq(macvar)));

/* Check missing parameter */
%local /readonly list=from to step query out macvar debug;
%local i n item;
%let n = %sysfunc(countw(&list));
%do i = 1 %to &n;
  %let item = %scan(&list, &i);
  %if %length(&&&item)=0 %then %do;
    %put ERROR: Missing parameter &item in %sysfunc(lowcase(&sysmacroname));
    %goto exit;
  %end;
%end;
  
/* Assign a temprary file reference name */
%let macvar=_RF%sysfunc(putn(%sysfunc(monotonic()), z5.));
  
/* Clear the file reference name */
%if %sysfunc(fileref(&macvar))<=0 %then %do;
  filename &macvar clear;
%end;