G-gen ã®é«å®®ã§ããGoogle Apps ScriptïŒGASïŒãçšãã Google ã«ã¬ã³ããŒã®èªååã«ã€ããŠè§£èª¬ããŸããç¹ã«ãåæ¥ç¥æ¥ãäŒç€ŸäŒæ¥ãèæ
®ããæã®ç¬¬1å¶æ¥æ¥ãæçµå¶æ¥æ¥ãšãã£ããå€åçãªæ¥ä»ãžã®å®æçãªäºå®è¿œå ã«çŠç¹ãåœãŠãŸãã ã¯ããã« æŠèŠ Google Apps ScriptïŒGASïŒãšã¯ æ°ãã GAS ãããžã§ã¯ãã®äœæ å¶æ¥æ¥èšç®åŠç å®è£
åäœç¢ºèª 第1å¶æ¥æ¥ã®ã€ãã³ãç»é²åŠç å®è£
åäœç¢ºèª æçµå¶æ¥æ¥ã®ã€ãã³ãç»é²åŠç ã¯ããã« æŠèŠ Google ã«ã¬ã³ã㌠ã§ã¯æããšã®ç¹°ãè¿ãã®äºå®ãããæ¯æ25æ¥ãããæ¯æç¬¬3éææ¥ããªã©ã®åœ¢åŒã§äœæããããšã¯ã§ããŸããããããåæ¥ç¥æ¥ã»äŒç€ŸäŒæ¥ãªã©ãèæ
®ããäŒç€Ÿå¶æ¥æ¥ã§ã®å®æçãªã¹ã±ãžã¥ãŒã«äœæãç°¡åã«è¡ãããšã¯ã§ããŸããã äŒç€Ÿã®æ¥åã§ã¯ãæã®ç¬¬1å¶æ¥æ¥ã«åæã®å€æ ã®ç· ãäœæ¥ãäŸé Œããããæã®æçµå¶æ¥æ¥ã«çµè²»ç²Ÿç®ã®ç³è«ããããªã©ã®ã±ãŒã¹ããããŸãããã®ãããªå Žåã«ãæéããããã« Google ã«ã¬ã³ããŒã«å®æçãªäºå®ãäœæãããã§ãã åœèšäºã§ã¯ãGAS ã䜿çšããŠãåæ¥ç¥æ¥ã»äŒç€ŸäŒæ¥ãèæ
®ããæã®ç¬¬1å¶æ¥æ¥ãæçµå¶æ¥æ¥ã« Google ã«ã¬ã³ããŒã«å®æçãªäºå®ã远å ããå
·äœçãªæ¹æ³ã解説ããŸãã Google Apps ScriptïŒGASïŒãšã¯ Google Apps Script ïŒGASïŒã¯ãGoogle Workspace ãšçµ±åãããã¢ããªã±ãŒã·ã§ã³ãç°¡åã«äœæã§ããã¢ããªã±ãŒã·ã§ã³éçºãã©ãããã©ãŒã ã§ããJavaScript ã§ã³ãŒããèšè¿°ããGmailãã«ã¬ã³ããŒãªã©ã® Google Workspace ã¢ããªçšã®çµã¿èŸŒã¿ã©ã€ãã©ãªã䜿çšã§ããŸãã åè : Google Apps Script ã®æŠèŠ åè : Google Apps Script (GAS) ã«ããŽãªãŒã®èšäºäžèЧ - G-gen Tech Blog æ°ãã GAS ãããžã§ã¯ãã®äœæ 以äžã® URL ã«ã¢ã¯ã»ã¹ãããšã Apps Script ããã·ã¥ããŒãã衚瀺ãããŸãã https://script.google.com/ æ°ãããããžã§ã¯ã äœæãã¿ã³ãæŒäžãããšãæ°ãã GAS ãããžã§ã¯ããäœæã§ããŸãã å¶æ¥æ¥èšç®åŠç å®è£
ãããžã§ã¯ãã«ãã¡ã€ã« date_utils.gs ãäœæãã第1å¶æ¥æ¥ãšæçµå¶æ¥æ¥ãèšç®ããåŠçãå®è£
ããŸããä»åã®äŸã§ã¯ã忥ãç¥æ¥ãšå¹Žæ«å¹Žå§ã«äŒæãããäŒç€Ÿãæ³å®ããŠããŸãã /** * äŒç€ŸäŒæ¥ïŒæ/æ¥åœ¢åŒã®æååé
åïŒ * ãã®é
åã«ãåºå®ã®äŒç€ŸäŒæ¥ã远å ã§ããŸãã */ const COMPANY_HOLIDAYS = [ '12/30' , '12/31' , '1/1' , '1/2' , '1/3' , '1/4' ] ; /** * æ¥æ¬ã®ç¥æ¥ã«ã¬ã³ããŒãååŸããŸãã * ç¹°ãè¿ãåŒã³åºãããã®ãé²ããããäžåºŠã ãååŸããŠãã£ãã·ã¥ããŸãã */ const JAPANESE_HOLIDAY_CALENDAR = CalendarApp . getCalendarById ( 'ja.japanese#holiday@group.v.calendar.google.com' ) ; /** * æå®ãããæ¥ä»ãäŒæ¥ãã©ãããå€å®ããŸãã * @param {Date} date - å€å®å¯Ÿè±¡ã®æ¥ä»ãªããžã§ã¯ãã * @return {boolean} - äŒæ¥ã®å Žå㯠trueãå¶æ¥æ¥ã®å Žå㯠false ãè¿ããŸãã */ function isHoliday ( date ) { // 1. åææ¥ïŒ6ïŒãŸãã¯æ¥ææ¥ïŒ0ïŒãå€å® const day = date . getDay () ; if ( day === 0 || day === 6 ) { return true ; } // 2. æ¥æ¬ã®ç¥æ¥ãå€å® const events = JAPANESE_HOLIDAY_CALENDAR . getEventsForDay ( date ) ; if ( events . length > 0 ) { return true ; } // 3. äŒç€ŸäŒæ¥ãå€å® const month = date . getMonth () + 1 ; // getMonth()ã¯0å§ãŸãã®ãã+1 const dateOfMonth = date . getDate () ; const dateString = ` ${ month } / ${ dateOfMonth } ` ; if ( COMPANY_HOLIDAYS . includes ( dateString )) { return true ; } // äžèšã®ãããã«ã該åœããªãå Žåã¯å¶æ¥æ¥ return false ; } /** * æå®ããã幎æã®ç¬¬1å¶æ¥æ¥ãååŸããŸãã * @param {number} year - 幎ïŒäŸ: 2025ïŒã * @param {number} month - æïŒ1ã12ïŒã * @return {Date} - 第1å¶æ¥æ¥ã®æ¥ä»ãªããžã§ã¯ãã */ function getFirstBusinessDay ( year , month ) { // æå®ãããæã®1æ¥ãéå§æ¥ãšããŠèšå® const date = new Date ( year , month - 1 , 1 ) ; // æã¯0å§ãŸãã®ãã-1 // äŒæ¥ã§ãªããªããŸã§æ¥ä»ã1æ¥ãã€é²ãã while ( isHoliday ( date )) { date . setDate ( date . getDate () + 1 ) ; } return date ; } /** * æå®ããã幎æã®æçµå¶æ¥æ¥ãååŸããŸãã * @param {number} year - 幎ïŒäŸ: 2025ïŒã * @param {number} month - æïŒ1ã12ïŒã * @return {Date} - æçµå¶æ¥æ¥ã®æ¥ä»ãªããžã§ã¯ãã */ function getLastBusinessDay ( year , month ) { // æå®ãããæã®æ«æ¥ãéå§æ¥ãšããŠèšå® const date = new Date ( year , month , 0 ) ; // ç¿æã®0æ¥ç®ãæå®ãããšããã®æã®æ«æ¥ãååŸã§ãã // äŒæ¥ã§ãªããªããŸã§æ¥ä»ã1æ¥ãã€é¡ã while ( isHoliday ( date )) { date . setDate ( date . getDate () - 1 ) ; } return date ; } ããã§ã¯ãGoogle ã«ã¬ã³ããŒã§æäŸãããŠãããæ¥æ¬ã®ç¥æ¥ãã«ã¬ã³ããŒããç¥æ¥ã®æ
å ±ãååŸããããã CalendarApp ã¯ã©ã¹ã䜿çšããŠããŸãã åè : Class CalendarApp åäœç¢ºèª æ°ãããã¡ã€ã« ã³ãŒã.gs ã«ä»¥äžã®åŠçãå®è£
ãã main 颿°ãå®è¡ãå¶æ¥æ¥ã®èšç®ãæ£åžžã«å®è¡ã§ããŠããã確èªããŸãã /** * ãã¹ãçšã®å¯Ÿè±¡å¹Žæã®ãªã¹ã */ const TARGET_YEAR_MONTH_LIST = [ { // 2024幎12æ TARGET_YEAR : 2024 , TARGET_MONTH : 12 , } , { // 2025幎1æ TARGET_YEAR : 2025 , TARGET_MONTH : 1 , } , ] /** * å®è¡ãã¹ãçšã®ã¡ã€ã³é¢æ° */ function main () { TARGET_YEAR_MONTH_LIST . forEach ( targetYearMonth => { const firstDay = getFirstBusinessDay ( targetYearMonth . TARGET_YEAR , targetYearMonth . TARGET_MONTH ) ; const lastDay = getLastBusinessDay ( targetYearMonth . TARGET_YEAR , targetYearMonth . TARGET_MONTH ) ; // çµæããã°ã«åºå console . log ( ` ${ targetYearMonth . TARGET_YEAR } 幎 ${ targetYearMonth . TARGET_MONTH } æã®ç¬¬1å¶æ¥æ¥: ${ Utilities . formatDate ( firstDay , 'JST' , 'yyyy-MM-dd (E)' )} ` ) ; console . log ( ` ${ targetYearMonth . TARGET_YEAR } 幎 ${ targetYearMonth . TARGET_MONTH } æã®æçµå¶æ¥æ¥: ${ Utilities . formatDate ( lastDay , 'JST' , 'yyyy-MM-dd (E)' )} ` ) ; }) ; } ååå®è¡æã«ã¯ãGoogle ã¢ã«ãŠã³ããéžæããæš©éãä»äžããå¿
èŠããããŸãã æ£åžžçµäºãããšã以äžã®ãããªãã°ãåºåãããŸãã 第1å¶æ¥æ¥ã®ã€ãã³ãç»é²åŠç å®è£
æ°ãããã¡ã€ã« create_event.gs ãäœæãã以äžã®åŠçãå®è£
ããŸãã /** * ã€ãã³ããäœæããã«ã¬ã³ããŒãæå®ããŸãã * ããã©ã«ãã«ã¬ã³ããŒä»¥å€ã䜿ãããå Žåã¯ã'xxxx@group.calendar.google.com' ã®ãããªã«ã¬ã³ããŒIDã«æžãæããŠãã ããã */ const TARGET_CALENDAR_ID = 'primary' ; // 'primary' ã¯ããã©ã«ãã«ã¬ã³ããŒãæå³ããŸã /** * ã«ã¬ã³ããŒã«äœæããã€ãã³ãã®å
容ãããã§å®çŸ©ããŸãã */ const EVENT_TITLE = 'ãææ¬¡ã倿 ç· ãäœæ¥ãäžé·ç³è«' ; const EVENT_DESCRIPTION = 'åæåã®å€æ ã®ç· ãäœæ¥ãã·ã¹ãã ã䜿çšããŠäžé·ã«ç³è«ããã' ; /** * ã€ãã³ããäœæããéå§å¹Žææ¥ãšçµäºå¹Žææ¥ãå®çŸ©ããŸãã */ const START_DATE = new Date ( 2025 , 6 , 25 , 0 , 0 , 0 , 0 ) ; // 2025/07/25 const END_DATE = new Date ( 2026 , 1 , 28 , 0 , 0 , 0 , 0 ) ; // 2026/02/28 /** * ã€ãã³ããäœæããæã®éå§æéãšçµäºæéãå®çŸ©ããŸãã */ const START_TIME = new Date ( 0 , 0 , 0 , 9 , 0 , 0 , 0 ) ; // 9:00 const END_TIME = new Date ( 0 , 0 , 0 , 9 , 30 , 0 , 0 ) ; // 9:30 /** * æå®ãããæéå
ã®å¶æ¥æ¥ã®å®äŸã€ãã³ããGoogleã«ã¬ã³ããŒã«äœæããŸãã * @param {function} getBusinessDay - å¶æ¥æ¥ãèšç®ãã颿°(第1åŒæ°ïŒå¹Žã第2åŒæ°ïŒæ) */ const createBusinessDayEvents_ = function ( getBusinessDay ) { // 1. èšå®ã®ãã§ã㯠if ( START_DATE > END_DATE ) { console . error ( 'ãšã©ãŒ: éå§å¹Žææ¥ã¯çµäºå¹Žææ¥ä»¥åã®æ¥ä»ã«ããŠãã ããã' ) ; return; } if ( START_TIME > END_TIME ) { console . error ( 'ãšã©ãŒ: éå§æéã¯çµäºæé以åã®æéã«ããŠãã ããã' ) ; return; } if ( typeof getBusinessDay ! = 'function' ) { console . error ( 'ãšã©ãŒ: åŒæ°ã«ã¯å¶æ¥æ¥ãèšç®ãã颿°ãèšå®ããŠãã ããã' ) ; return; } const calendar = CalendarApp . getCalendarById ( TARGET_CALENDAR_ID ) ; if ( ! calendar ) { console . error ( `ãšã©ãŒ: æå®ãããã«ã¬ã³ããŒID ( ${ TARGET_CALENDAR_ID } ) ãèŠã€ãããŸããã` ) ; return; } console . log ( 'åŠçãéå§ããŸã...' ) ; console . log ( `察象æé: ${ Utilities . formatDate ( START_DATE , 'JST' , 'yyyy-MM-dd' )} ~ ${ Utilities . formatDate ( END_DATE , 'JST' , 'yyyy-MM-dd' )} ` ) ; // 2. æå®æéå
ã®å¶æ¥æ¥ãèšç® // æããšã«ã«ãŒã const recurrence = CalendarApp . newRecurrence () ; const recurrenceDaysList = [] ; let currentDate = new Date ( START_DATE ) ; while ( currentDate <= END_DATE ) { const year = currentDate . getFullYear () ; const month = currentDate . getMonth () + 1 ; // æã¯1å§ãŸãã«è£æ£ // å¶æ¥æ¥ãèšç® const businessDay = getBusinessDay ( year , month ) ; const formattedDate = Utilities . formatDate ( businessDay , 'JST' , 'yyyy-MM-dd' ) ; // èšç®ããå¶æ¥æ¥ãæå®æéå
ã«ããå Žåã®ã¿ã¿ã¹ã¯ãäœæ if ( businessDay >= START_DATE && businessDay <= END_DATE ) { recurrence . addDate ( businessDay ) ; recurrenceDaysList . push ( businessDay ) ; console . log ( `察象ããªã¹ãã«è¿œå ããŸãã: ${ formattedDate } ` ) ; } else { console . log ( `æå®æéå€ã®ããã¹ãããããŸãã: ${ formattedDate } ` ) ; } // æ¬¡ã®æãž currentDate . setMonth ( currentDate . getMonth () + 1 ) ; currentDate . setDate ( 1 ) ; // æ¥ã1æ¥ã«ãªã»ãã } // 3. 宿çãªäºå®ã®äœæ if ( recurrenceDaysList . length > 0 ) { const initialDay = recurrenceDaysList [ 0 ] ; const startDate = new Date ( initialDay ) ; startDate . setHours ( START_TIME . getHours ()) ; startDate . setMinutes ( START_TIME . getMinutes ()) ; const endDate = new Date ( initialDay ) ; endDate . setHours ( END_TIME . getHours ()) ; endDate . setMinutes ( END_TIME . getMinutes ()) ; calendar . createEventSeries ( EVENT_TITLE , startDate , endDate , recurrence , { description : EVENT_DESCRIPTION } , ) ; console . log ( '宿çãªäºå®ãäœæããŸããã' ) ; } else { console . log ( 'æå®æéã«å¯Ÿè±¡ã®æ¥ä»ããªãããã宿çãªäºå®ãäœæããŠããŸããã' ) ; } } ; /** * æå®ãããæéå
ã®åæã®ç¬¬1å¶æ¥æ¥ã®å®äŸã€ãã³ããGoogleã«ã¬ã³ããŒã«äœæããŸãã */ function createFirstBusinessDayEvents () { createBusinessDayEvents_ ( getFirstBusinessDay ) ; } createBusinessDayEvents_ ã§ã¯ãŸãã CalendarApp ã¯ã©ã¹ã䜿çšããèªèº«ã® Google ã«ã¬ã³ããŒã®æ
å ±ãååŸããŸãã æ¬¡ã«ã RecurrenceRule ã¯ã©ã¹ã䜿çšããŠã宿çãªäºå®ã®ã«ãŒã«ãäœæããŠãããŸããåŒæ°ã®å¶æ¥æ¥èšç®çšã®é¢æ°ã䜿çšããŠå¶æ¥æ¥ã®èšç®ãè¡ãã addDate ã¡ãœããã䜿çšããŠå®æçãªäºå®ã«èšå®ããå¶æ¥æ¥ã远å ããŸãã æåŸã«ã Calendar ã¯ã©ã¹ã® createEventSeries ã¡ãœããã䜿çšããŠå®æçãªäºå®ãç»é²ããŸãã åè : Class Calendar åè : Class RecurrenceRule åäœç¢ºèª createFirstBusinessDayEvents 颿°ãå®è¡ãããšãã«ã¬ã³ããŒã«å®æçãªäºå®ãç»é²ãããŸãã以äžã®ããã«ã Google ã«ã¬ã³ããŒã«æå®ããæéã®äºå®ãç»é²ãããŠããããšã確èªã§ããŸãã Google ã«ã¬ã³ããŒäžã§ã¯ç»é²ãããäºå®ã«ã¯ç¹°ãè¿ãèšå®ããªããšè¡šç€ºãããŠããŸããã ã¹ã±ãžã¥ãŒã«ã®åé€ãå®è¡ãããšã宿çãªäºå®ã®åé€ã®ãã€ã¢ãã°ã衚瀺ããã宿çãªäºå®ãšãªã£ãŠããããšã確èªã§ããŸãã ãŸãã2025幎11æã2026幎1æã確èªãããšãåæ¥ç¥æ¥ãäŒç€ŸäŒæ¥ã®èšå®ãèæ
®ãããã第1å¶æ¥æ¥ãç»é²ãããŠããããšã確èªã§ããŸãã æçµå¶æ¥æ¥ã®ã€ãã³ãç»é²åŠç æçµå¶æ¥æ¥ã«ã¿ã¹ã¯ãäœæãããå Žåã¯ã create_event.gs ã«ä»¥äžã®ã³ãŒããå®è£
ããŸãã createBusinessDayEvents_ 颿°ã«æçµå¶æ¥æ¥èšç®çšã®é¢æ°ãåŒæ°ãšããŠæž¡ãããšã§å®è£
ã§ããŸãã createLastBusinessDayEvents 颿°ãå®è¡ãããšãæçµå¶æ¥æ¥ã«å®æçãªäºå®ãç»é²ã§ããŸãã /** * æå®ãããæéå
ã®åæã®æçµå¶æ¥æ¥ã®å®äŸã€ãã³ããGoogleã«ã¬ã³ããŒã«äœæããŸãã */ function createLastBusinessDayEvents () { createBusinessDayEvents_ ( getLastBusinessDay ) ; } é«å®® æ (èšäºäžèЧ) ã¯ã©ãŠããœãªã¥ãŒã·ã§ã³éšã¯ã©ãŠããšã¯ã¹ãããŒã©èª² 2025幎6æãããG-genã«ãžã§ã€ã³ãåè·ã¯ååœã®SIerã§é»åãè£œé æ¥ç³»ã®ã客æ§ã«å¯ŸããŠãPMïŒAPãšã³ãžãã¢ãšããŠãèŠä»¶å®çŸ©ããéçšä¿å®ãŸã§å
šå·¥çšãæ
åœãçŸåšã¯Google CloudãåŠã³ãªããããã«ã¹ã¿ãã¯ãšã³ãžãã¢ãç®æããŠã¯ã©ãŠããšã³ãžãã¢ãšããŠã®ã¹ãã«ãç¿åŸäžã Follow @Ggen_RTakamiya